Skip to content

Commit b611f6c

Browse files
authored
update k8s 1.25 validation logic (#270)
* update k8s 1.25 validation logic to look for deprecated k8s APIs in various CSV fields Signed-off-by: Bryce Palmer <[email protected]> * update validation logic Signed-off-by: Bryce Palmer <[email protected]> * remove debug statements Signed-off-by: Bryce Palmer <[email protected]> Signed-off-by: Bryce Palmer <[email protected]>
1 parent b527a19 commit b611f6c

File tree

4 files changed

+200
-48
lines changed

4 files changed

+200
-48
lines changed

pkg/validation/internal/removed_apis.go

Lines changed: 129 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ package internal
33
import (
44
"fmt"
55
"sort"
6+
"strings"
67

78
"github.com/blang/semver/v4"
89
"github.com/operator-framework/api/pkg/manifests"
10+
"github.com/operator-framework/api/pkg/operators/v1alpha1"
911
"github.com/operator-framework/api/pkg/validation/errors"
1012
interfaces "github.com/operator-framework/api/pkg/validation/interfaces"
1113
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14+
"k8s.io/apimachinery/pkg/runtime"
1215
)
1316

1417
// k8sVersionKey defines the key which can be used by its consumers
@@ -148,11 +151,12 @@ func checkRemovedAPIsForVersion(
148151
errs []error, warns []error) ([]error, []error) {
149152

150153
found := map[string][]string{}
154+
warnsFound := map[string][]string{}
151155
switch k8sVersionToCheck.String() {
152156
case "1.22.0":
153157
found = getRemovedAPIsOn1_22From(bundle)
154158
case "1.25.0":
155-
found = getRemovedAPIsOn1_25From(bundle)
159+
found, warnsFound = getRemovedAPIsOn1_25From(bundle)
156160
case "1.26.0":
157161
found = getRemovedAPIsOn1_26From(bundle)
158162
default:
@@ -173,6 +177,16 @@ func checkRemovedAPIsForVersion(
173177
warns = append(warns, msg)
174178
}
175179
}
180+
181+
if len(warnsFound) > 0 {
182+
deprecatedAPIsMessage := generateMessageWithDeprecatedAPIs(warnsFound)
183+
msg := fmt.Errorf(DeprecateMessage,
184+
k8sVersionToCheck.Major, k8sVersionToCheck.Minor,
185+
k8sVersionToCheck.Major, k8sVersionToCheck.Minor,
186+
deprecatedAPIsMessage)
187+
warns = append(warns, msg)
188+
}
189+
176190
return errs, warns
177191
}
178192

@@ -288,40 +302,129 @@ func getRemovedAPIsOn1_22From(bundle *manifests.Bundle) map[string][]string {
288302
// add manifests on the bundle using these APIs. On top of that some Kinds such as the CronJob
289303
// are not currently a valid/supported by OLM and never would to be added to bundle.
290304
// See: https://github.com/operator-framework/operator-registry/blob/v1.19.5/pkg/lib/bundle/supported_resources.go#L3-L23
291-
func getRemovedAPIsOn1_25From(bundle *manifests.Bundle) map[string][]string {
305+
func getRemovedAPIsOn1_25From(bundle *manifests.Bundle) (map[string][]string, map[string][]string) {
292306
deprecatedAPIs := make(map[string][]string)
307+
warnDeprecatedAPIs := make(map[string][]string)
308+
309+
addIfDeprecated := func(u *unstructured.Unstructured) {
310+
switch u.GetAPIVersion() {
311+
case "batch/v1beta1":
312+
if u.GetKind() == "CronJob" {
313+
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], u.GetName())
314+
}
315+
case "discovery.k8s.io/v1beta1":
316+
if u.GetKind() == "EndpointSlice" {
317+
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], u.GetName())
318+
}
319+
case "events.k8s.io/v1beta1":
320+
if u.GetKind() == "Event" {
321+
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], u.GetName())
322+
}
323+
case "autoscaling/v2beta1":
324+
if u.GetKind() == "HorizontalPodAutoscaler" {
325+
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], u.GetName())
326+
}
327+
case "policy/v1beta1":
328+
if u.GetKind() == "PodDisruptionBudget" || u.GetKind() == "PodSecurityPolicy" {
329+
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], u.GetName())
330+
}
331+
case "node.k8s.io/v1beta1":
332+
if u.GetKind() == "RuntimeClass" {
333+
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], u.GetName())
334+
}
335+
}
336+
}
337+
338+
warnIfDeprecated := func(res string, msg string) {
339+
switch res {
340+
case "cronjobs":
341+
warnDeprecatedAPIs[res] = append(warnDeprecatedAPIs[res], msg)
342+
case "endpointslices":
343+
warnDeprecatedAPIs[res] = append(warnDeprecatedAPIs[res], msg)
344+
case "events":
345+
warnDeprecatedAPIs[res] = append(warnDeprecatedAPIs[res], msg)
346+
case "horizontalpodautoscalers":
347+
warnDeprecatedAPIs[res] = append(warnDeprecatedAPIs[res], msg)
348+
case "poddisruptionbudgets":
349+
warnDeprecatedAPIs[res] = append(warnDeprecatedAPIs[res], msg)
350+
case "podsecuritypolicies":
351+
warnDeprecatedAPIs[res] = append(warnDeprecatedAPIs[res], msg)
352+
case "runtimeclasses":
353+
warnDeprecatedAPIs[res] = append(warnDeprecatedAPIs[res], msg)
354+
}
355+
}
356+
293357
for _, obj := range bundle.Objects {
294358
switch u := obj.GetObjectKind().(type) {
295359
case *unstructured.Unstructured:
296360
switch u.GetAPIVersion() {
297-
case "batch/v1beta1":
298-
if u.GetKind() == "CronJob" {
299-
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], obj.GetName())
300-
}
301-
case "discovery.k8s.io/v1beta1":
302-
if u.GetKind() == "EndpointSlice" {
303-
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], obj.GetName())
304-
}
305-
case "events.k8s.io/v1beta1":
306-
if u.GetKind() == "Event" {
307-
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], obj.GetName())
308-
}
309-
case "autoscaling/v2beta1":
310-
if u.GetKind() == "HorizontalPodAutoscaler" {
311-
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], obj.GetName())
312-
}
313-
case "policy/v1beta1":
314-
if u.GetKind() == "PodDisruptionBudget" || u.GetKind() == "PodSecurityPolicy" {
315-
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], obj.GetName())
316-
}
317-
case "node.k8s.io/v1beta1":
318-
if u.GetKind() == "RuntimeClass" {
319-
deprecatedAPIs[u.GetKind()] = append(deprecatedAPIs[u.GetKind()], obj.GetName())
361+
case "operators.coreos.com/v1alpha1":
362+
// Check a couple CSV fields for references to deprecated APIs
363+
if u.GetKind() == "ClusterServiceVersion" {
364+
resInCsvCrds := make(map[string]struct{})
365+
csv := &v1alpha1.ClusterServiceVersion{}
366+
err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, csv)
367+
if err != nil {
368+
fmt.Println("failed to convert unstructured.Unstructed to v1alpha1.ClusterServiceVersion:", err)
369+
}
370+
371+
// Loop through all the CRDDescriptions to see
372+
// if there is any with an API Version & Kind that is deprecated
373+
crdCheck := func(crdsField string, crdDescriptions []v1alpha1.CRDDescription) {
374+
for i, desc := range crdDescriptions {
375+
for j, res := range desc.Resources {
376+
resFromKind := fmt.Sprintf("%ss", strings.ToLower(res.Kind))
377+
resInCsvCrds[resFromKind] = struct{}{}
378+
unstruct := &unstructured.Unstructured{
379+
Object: map[string]interface{}{
380+
"apiVersion": res.Version,
381+
"kind": res.Kind,
382+
"metadata": map[string]interface{}{
383+
"name": fmt.Sprintf("ClusterServiceVersion.Spec.CustomResourceDefinitions.%s[%d].Resource[%d]", crdsField, i, j),
384+
},
385+
},
386+
}
387+
addIfDeprecated(unstruct)
388+
}
389+
}
390+
}
391+
392+
// Check the Owned Resources
393+
crdCheck("Owned", csv.Spec.CustomResourceDefinitions.Owned)
394+
395+
// Check the Required Resources
396+
crdCheck("Required", csv.Spec.CustomResourceDefinitions.Required)
397+
398+
// Loop through all the StrategyDeploymentPermissions to see
399+
// if the rbacv1.PolicyRule that is defined specifies a resource that
400+
// *may* have a deprecated API then add it to the warnings.
401+
// Only present a warning if the resource was NOT found as a resource
402+
// in the ClusterServiceVersion.Spec.CustomResourceDefinitions fields
403+
permCheck := func(permField string, perms []v1alpha1.StrategyDeploymentPermissions) {
404+
for i, perm := range perms {
405+
for j, rule := range perm.Rules {
406+
for _, res := range rule.Resources {
407+
if _, ok := resInCsvCrds[res]; ok {
408+
continue
409+
}
410+
warnIfDeprecated(res, fmt.Sprintf("ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.%s[%d].Rules[%d]", permField, i, j))
411+
}
412+
}
413+
}
414+
}
415+
416+
// Check the ClusterPermissions
417+
permCheck("ClusterPermissions", csv.Spec.InstallStrategy.StrategySpec.ClusterPermissions)
418+
419+
// Check the Permissions
420+
permCheck("Permissions", csv.Spec.InstallStrategy.StrategySpec.Permissions)
320421
}
422+
default:
423+
addIfDeprecated(u)
321424
}
322425
}
323426
}
324-
return deprecatedAPIs
427+
return deprecatedAPIs, warnDeprecatedAPIs
325428
}
326429

327430
// getRemovedAPIsOn1_26From return the list of resources which were deprecated

pkg/validation/internal/removed_apis_test.go

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,27 +71,34 @@ func Test_GetRemovedAPIsOn1_25From(t *testing.T) {
7171
mock["HorizontalPodAutoscaler"] = []string{"memcached-operator-hpa"}
7272
mock["PodDisruptionBudget"] = []string{"memcached-operator-policy-manager"}
7373

74+
warnMock := make(map[string][]string)
75+
warnMock["cronjobs"] = []string{"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.ClusterPermissions[0].Rules[7]"}
76+
warnMock["events"] = []string{"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.Permissions[0].Rules[2]"}
77+
7478
type args struct {
7579
bundleDir string
7680
}
7781
tests := []struct {
78-
name string
79-
args args
80-
want map[string][]string
82+
name string
83+
args args
84+
errWant map[string][]string
85+
warnWant map[string][]string
8186
}{
8287
{
8388
name: "should return an empty map when no deprecated apis are found",
8489
args: args{
8590
bundleDir: "./testdata/valid_bundle_v1",
8691
},
87-
want: map[string][]string{},
92+
errWant: map[string][]string{},
93+
warnWant: map[string][]string{},
8894
},
8995
{
9096
name: "should fail return the removed APIs in 1.25",
9197
args: args{
9298
bundleDir: "./testdata/removed_api_1_25",
9399
},
94-
want: mock,
100+
errWant: mock,
101+
warnWant: warnMock,
95102
},
96103
}
97104
for _, tt := range tests {
@@ -101,8 +108,14 @@ func Test_GetRemovedAPIsOn1_25From(t *testing.T) {
101108
bundle, err := manifests.GetBundleFromDir(tt.args.bundleDir)
102109
require.NoError(t, err)
103110

104-
if got := getRemovedAPIsOn1_25From(bundle); !reflect.DeepEqual(got, tt.want) {
105-
t.Errorf("getRemovedAPIsOn1_25From() = %v, want %v", got, tt.want)
111+
errGot, warnGot := getRemovedAPIsOn1_25From(bundle)
112+
113+
if !reflect.DeepEqual(errGot, tt.errWant) {
114+
t.Errorf("getRemovedAPIsOn1_25From() = %v, want %v", errGot, tt.errWant)
115+
}
116+
117+
if !reflect.DeepEqual(warnGot, tt.warnWant) {
118+
t.Errorf("getRemovedAPIsOn1_25From() = %v, want %v", warnGot, tt.warnWant)
106119
}
107120
})
108121
}
@@ -174,7 +187,11 @@ func TestValidateDeprecatedAPIS(t *testing.T) {
174187
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.22. " +
175188
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22. " +
176189
"Migrate the API(s) for CRD: ([\"etcdbackups.etcd.database.coreos.com\" " +
177-
"\"etcdclusters.etcd.database.coreos.com\" \"etcdrestores.etcd.database.coreos.com\"])"},
190+
"\"etcdclusters.etcd.database.coreos.com\" \"etcdrestores.etcd.database.coreos.com\"])",
191+
"this bundle is using APIs which were deprecated and removed in v1.25. " +
192+
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25. " +
193+
"Migrate the API(s) for events: " +
194+
"([\"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.Permissions[0].Rules[1]\"])"},
178195
},
179196
{
180197
name: "should return an error when the k8sVersion is >= 1.22 and has the deprecated API",
@@ -188,6 +205,11 @@ func TestValidateDeprecatedAPIS(t *testing.T) {
188205
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22. " +
189206
"Migrate the API(s) for CRD: ([\"etcdbackups.etcd.database.coreos.com\"" +
190207
" \"etcdclusters.etcd.database.coreos.com\" \"etcdrestores.etcd.database.coreos.com\"])"},
208+
wantWarning: true,
209+
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.25. " +
210+
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25. " +
211+
"Migrate the API(s) for events: " +
212+
"([\"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.Permissions[0].Rules[1]\"])"},
191213
},
192214
{
193215
name: "should return an error when the k8sVersion is >= 1.25 and found removed APIs on 1.25",
@@ -201,6 +223,12 @@ func TestValidateDeprecatedAPIS(t *testing.T) {
201223
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25. " +
202224
"Migrate the API(s) for HorizontalPodAutoscaler: ([\"memcached-operator-hpa\"])," +
203225
"PodDisruptionBudget: ([\"memcached-operator-policy-manager\"]),"},
226+
wantWarning: true,
227+
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.25. " +
228+
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25. " +
229+
"Migrate the API(s) for cronjobs: " +
230+
"([\"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.ClusterPermissions[0].Rules[7]\"])" +
231+
",events: ([\"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.Permissions[0].Rules[2]\"]),"},
204232
},
205233
{
206234
name: "should return a warning if the k8sVersion is empty and found removed APIs on 1.25",
@@ -213,7 +241,12 @@ func TestValidateDeprecatedAPIS(t *testing.T) {
213241
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.25. " +
214242
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25. " +
215243
"Migrate the API(s) for HorizontalPodAutoscaler: ([\"memcached-operator-hpa\"])," +
216-
"PodDisruptionBudget: ([\"memcached-operator-policy-manager\"]),"},
244+
"PodDisruptionBudget: ([\"memcached-operator-policy-manager\"]),",
245+
"this bundle is using APIs which were deprecated and removed in v1.25. " +
246+
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25. " +
247+
"Migrate the API(s) for cronjobs: " +
248+
"([\"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.ClusterPermissions[0].Rules[7]\"])" +
249+
",events: ([\"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.Permissions[0].Rules[2]\"]),"},
217250
},
218251
{
219252
name: "should return an error when the k8sVersion is >= 1.26 and found removed APIs on 1.26",
@@ -226,6 +259,11 @@ func TestValidateDeprecatedAPIS(t *testing.T) {
226259
errStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.26. " +
227260
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-26. " +
228261
"Migrate the API(s) for HorizontalPodAutoscaler: ([\"memcached-operator-hpa\"])"},
262+
wantWarning: true,
263+
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.25. " +
264+
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25. " +
265+
"Migrate the API(s) for events: " +
266+
"([\"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.Permissions[0].Rules[2]\"])"},
229267
},
230268
{
231269
name: "should return a warning when the k8sVersion is empty and found removed APIs on 1.26",
@@ -235,9 +273,13 @@ func TestValidateDeprecatedAPIS(t *testing.T) {
235273
directory: "./testdata/removed_api_1_26",
236274
},
237275
wantWarning: true,
238-
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.26. " +
239-
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-26. " +
240-
"Migrate the API(s) for HorizontalPodAutoscaler: ([\"memcached-operator-hpa\"])"},
276+
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.25. " +
277+
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25. " +
278+
"Migrate the API(s) for events: " +
279+
"([\"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.Permissions[0].Rules[2]\"])",
280+
"this bundle is using APIs which were deprecated and removed in v1.26. " +
281+
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-26. " +
282+
"Migrate the API(s) for HorizontalPodAutoscaler: ([\"memcached-operator-hpa\"])"},
241283
},
242284
{
243285
name: "should return an error when the k8sVersion informed is invalid",
@@ -251,8 +293,12 @@ func TestValidateDeprecatedAPIS(t *testing.T) {
251293
wantWarning: true,
252294
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.22. " +
253295
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22. " +
254-
"Migrate the API(s) for CRD: ([\"etcdbackups.etcd.database.coreos.com\"" +
255-
" \"etcdclusters.etcd.database.coreos.com\" \"etcdrestores.etcd.database.coreos.com\"])"},
296+
"Migrate the API(s) for CRD: ([\"etcdbackups.etcd.database.coreos.com\" " +
297+
"\"etcdclusters.etcd.database.coreos.com\" \"etcdrestores.etcd.database.coreos.com\"])",
298+
"this bundle is using APIs which were deprecated and removed in v1.25. " +
299+
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25. " +
300+
"Migrate the API(s) for events: " +
301+
"([\"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.Permissions[0].Rules[1]\"])"},
256302
},
257303
{
258304
name: "should return an error when the csv.spec.minKubeVersion informed is invalid",
@@ -267,7 +313,11 @@ func TestValidateDeprecatedAPIS(t *testing.T) {
267313
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.22. " +
268314
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22. " +
269315
"Migrate the API(s) for CRD: ([\"etcdbackups.etcd.database.coreos.com\" " +
270-
"\"etcdclusters.etcd.database.coreos.com\" \"etcdrestores.etcd.database.coreos.com\"])"},
316+
"\"etcdclusters.etcd.database.coreos.com\" \"etcdrestores.etcd.database.coreos.com\"])",
317+
"this bundle is using APIs which were deprecated and removed in v1.25. " +
318+
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25. " +
319+
"Migrate the API(s) for events: " +
320+
"([\"ClusterServiceVersion.Spec.InstallStrategy.StrategySpec.Permissions[0].Rules[1]\"])"},
271321
},
272322
}
273323
for _, tt := range tests {

pkg/validation/internal/testdata/removed_api_1_25/memcached-operator.clusterserviceversion.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ spec:
9494
- subjectaccessreviews
9595
verbs:
9696
- create
97+
- apiGroups:
98+
- batch
99+
resources:
100+
- cronjobs
101+
verbs:
102+
- get
97103
serviceAccountName: memcached-operator-controller-manager
98104
deployments:
99105
- name: memcached-operator-controller-manager

pkg/validation/internal/testdata/valid_bundle_v1/memcached-operator.clusterserviceversion.yaml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,6 @@ spec:
176176
- update
177177
- patch
178178
- delete
179-
- apiGroups:
180-
- ""
181-
resources:
182-
- events
183-
verbs:
184-
- create
185-
- patch
186179
serviceAccountName: memcached-operator-controller-manager
187180
strategy: deployment
188181
installModes:

0 commit comments

Comments
 (0)