@@ -3,30 +3,43 @@ package internal
3
3
import (
4
4
"fmt"
5
5
"github.com/blang/semver"
6
-
7
6
"github.com/operator-framework/api/pkg/manifests"
8
- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
9
-
10
7
"github.com/operator-framework/api/pkg/validation/errors"
11
8
interfaces "github.com/operator-framework/api/pkg/validation/interfaces"
9
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
10
+ "sort"
12
11
)
13
12
14
13
// k8sVersionKey defines the key which can be used by its consumers
15
14
// to inform what is the K8S version that should be used to do the tests against.
16
15
const k8sVersionKey = "k8s-version"
17
16
18
- const minKubeVersionWarnMessage = "csv.Spec.minKubeVersion is not informed. It is recommended you provide this information. " +
19
- "Otherwise, it would mean that your operator project can be distributed and installed in any cluster version " +
20
- "available, which is not necessarily the case for all projects."
21
-
22
- // K8s version where the apis v1betav1 is no longer supported
23
- const k8sVerV1betav1Unsupported = "1.22.0"
17
+ // DeprecateMessage defines the content of the message that will be raised as an error or warning
18
+ // when the removed apis are found
19
+ const DeprecateMessage = "this bundle is using APIs which were deprecated and removed in v%v.%v. " +
20
+ "More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v%v-%v. " +
21
+ "Migrate the API(s) for %s"
24
22
25
- // K8s version where the apis v1betav1 was deprecated
26
- const k8sVerV1betav1Deprecated = "1.16.0"
23
+ // K8sVersionsSupportedByValidator defines the k8s versions which this validator is implemented to
24
+ // perform the checks
25
+ var K8sVersionsSupportedByValidator = []string {"1.22.0" , "1.25.0" , "1.26.0" }
27
26
28
- // AlphaDeprecatedAPIsValidator validates if the bundles is using versions API version which are deprecate or
29
- // removed in specific Kubernetes versions informed via optional key value `k8s-version`.
27
+ // AlphaDeprecatedAPIsValidator implements Validator to validate bundle objects
28
+ // for API deprecation requirements.
29
+ //
30
+ // Note that this validator looks at the manifests. If any removed APIs for the mapped k8s versions are found,
31
+ // it raises a warning.
32
+ //
33
+ // This validator only raises an error when the deprecated API found is removed in the specified k8s
34
+ // version informed via the optional key `k8s-version`.
35
+ //
36
+ // The K8s versions supported and checks are:
37
+ //
38
+ // - 1.22 : https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22
39
+ //
40
+ // - 1.25 : https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25
41
+ //
42
+ // - 1.26 : https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-26
30
43
var AlphaDeprecatedAPIsValidator interfaces.Validator = interfaces .ValidatorFunc (validateDeprecatedAPIsValidator )
31
44
32
45
func validateDeprecatedAPIsValidator (objs ... interface {}) (results []errors.ManifestResult ) {
@@ -54,13 +67,14 @@ func validateDeprecatedAPIsValidator(objs ...interface{}) (results []errors.Mani
54
67
}
55
68
56
69
func validateDeprecatedAPIs (bundle * manifests.Bundle , k8sVersion string ) errors.ManifestResult {
57
- result := errors.ManifestResult {Name : bundle .Name }
58
-
70
+ result := errors.ManifestResult {}
59
71
if bundle == nil {
60
72
result .Add (errors .ErrInvalidBundle ("Bundle is nil" , nil ))
61
73
return result
62
74
}
63
75
76
+ result .Name = bundle .Name
77
+
64
78
if bundle .CSV == nil {
65
79
result .Add (errors .ErrInvalidBundle ("Bundle csv is nil" , bundle .Name ))
66
80
return result
@@ -78,34 +92,22 @@ func validateDeprecatedAPIs(bundle *manifests.Bundle, k8sVersion string) errors.
78
92
}
79
93
80
94
// validateDeprecatedAPIS will check if the operator bundle is using a deprecated or no longer supported k8s api
81
- // Note if the k8s was informed via "k8s=1.22" it will be used. Otherwise, we will use the minKubeVersion in
82
- // the CSV to do the checks. So, the criteria is >=minKubeVersion. By last , if the minKubeVersion is not provided
95
+ // Note if the k8s version was informed via "k8s-version" optional key it will be used. Otherwise, we will use the minKubeVersion in
96
+ // the CSV to do the checks. So, the criteria is >=minKubeVersion. Lastly , if the minKubeVersion is not provided
83
97
// then, we should consider the operator bundle is intend to work well in any Kubernetes version.
84
98
// Then, it means that:
85
- //-- optional-values="k8s-version=value" flag with a value => 1.16 <= 1.22 the validator will return result as warning.
86
- //-- optional-values="k8s-version=value" flag with a value => 1.22 the validator will return result as error.
87
- //minKubeVersion >= 1.22 return the error result.
88
- //minKubeVersion empty returns a warning since it would mean the same of allow install in any supported version
99
+ // - -- optional-values="k8s-version=value" flag with a value <= unsupportedAPIVersion the validator will return result as warning.
100
+ // - -- optional-values="k8s-version=value" flag with a value => unsupportedAPIVersion the validator will return result as error.
101
+ // - minKubeVersion >= unsupportedAPIVersion return the error result.
102
+ // - minKubeVersion empty returns a warning since it would mean the same of allow in any supported version
89
103
func validateDeprecatedAPIS (bundle * manifests.Bundle , versionProvided string ) (errs , warns []error ) {
90
-
91
- // semver of the K8s version where the apis v1betav1 is no longer supported to allow us compare
92
- semVerK8sVerV1betav1Unsupported := semver .MustParse (k8sVerV1betav1Unsupported )
93
- // semver of the K8s version where the apis v1betav1 is deprecated to allow us compare
94
- semVerk8sVerV1betav1Deprecated := semver .MustParse (k8sVerV1betav1Deprecated )
95
104
// isVersionProvided defines if the k8s version to test against was or not informed
96
105
isVersionProvided := len (versionProvided ) > 0
106
+ // semVerVersionProvided -- converts the k8s version informed in semver
107
+ semVerVersionProvided , _ := semver .ParseTolerant (versionProvided )
97
108
98
- // Transform the key/option versionProvided in semver Version to compare
99
- var semVerVersionProvided semver.Version
100
- if isVersionProvided {
101
- var err error
102
- semVerVersionProvided , err = semver .ParseTolerant (versionProvided )
103
- if err != nil {
104
- errs = append (errs , fmt .Errorf ("invalid value informed via the k8s key option : %s" , versionProvided ))
105
- } else {
106
- // we might want to return it as info instead of warning in the future.
107
- warns = append (warns , fmt .Errorf ("checking APIs against Kubernetes version : %s" , versionProvided ))
108
- }
109
+ if err := verifyK8sVersionInformed (versionProvided ); err != nil && isVersionProvided {
110
+ errs = append (errs , err )
109
111
}
110
112
111
113
// Transform the spec minKubeVersion in semver Version to compare
@@ -118,39 +120,86 @@ func validateDeprecatedAPIS(bundle *manifests.Bundle, versionProvided string) (e
118
120
}
119
121
}
120
122
121
- // if the k8s value was informed and it is >=1.16 we should check
122
- // if the k8s value was not informed we also should check since the
123
- // check should occurs with any minKubeVersion value:
124
- // - if minKubeVersion empty then means that the project can be installed in any version
125
- // - if minKubeVersion any version defined it means that we are considering install
126
- // in any upper version from that where the check is always applied
127
- if ! isVersionProvided || semVerVersionProvided .GE (semVerk8sVerV1betav1Deprecated ) {
128
- deprecatedAPIs := getRemovedAPIsOn1_22From (bundle )
129
- if len (deprecatedAPIs ) > 0 {
130
- deprecatedAPIsMessage := generateMessageWithDeprecatedAPIs (deprecatedAPIs )
131
- // isUnsupported is true only if the key/value OR minKubeVersion were informed and are >= 1.22
132
- isUnsupported := semVerVersionProvided .GE (semVerK8sVerV1betav1Unsupported ) ||
133
- semverMinKube .GE (semVerK8sVerV1betav1Unsupported )
134
- // We only raise an error when the version >= 1.22 was informed via
123
+ // Check the bundle with all k8s versions implemented
124
+ for _ , v := range K8sVersionsSupportedByValidator {
125
+ k8sVersionToCheck := semver .MustParse (v )
126
+ errs , warns = checkRemovedAPIsForVersion (bundle ,
127
+ k8sVersionToCheck ,
128
+ semVerVersionProvided ,
129
+ semverMinKube ,
130
+ errs ,
131
+ warns )
132
+ }
133
+
134
+ return errs , warns
135
+ }
136
+
137
+ // checkRemovedAPIsForVersion will check if the bundle is using the removed APIs
138
+ // for the version informed (k8sVersionToCheck)
139
+ func checkRemovedAPIsForVersion (
140
+ bundle * manifests.Bundle ,
141
+ k8sVersionToCheck , semVerVersionProvided , semverMinKube semver.Version ,
142
+ errs []error , warns []error ) ([]error , []error ) {
143
+
144
+ found := map [string ][]string {}
145
+ switch k8sVersionToCheck .String () {
146
+ case "1.22.0" :
147
+ found = getRemovedAPIsOn1_22From (bundle )
148
+ case "1.25.0" :
149
+ found = getRemovedAPIsOn1_25From (bundle )
150
+ case "1.26.0" :
151
+ found = getRemovedAPIsOn1_26From (bundle )
152
+ default :
153
+ panic (fmt .Errorf ("invalid internal call to check the removed apis with the version (%s) which is not supported" , k8sVersionToCheck .String ()))
154
+ }
155
+
156
+ if len (found ) > 0 {
157
+ deprecatedAPIsMessage := generateMessageWithDeprecatedAPIs (found )
158
+ msg := fmt .Errorf (DeprecateMessage ,
159
+ k8sVersionToCheck .Major , k8sVersionToCheck .Minor ,
160
+ k8sVersionToCheck .Major , k8sVersionToCheck .Minor ,
161
+ deprecatedAPIsMessage )
162
+ if isK8sVersionInformedEQ (semVerVersionProvided , k8sVersionToCheck , semverMinKube ) {
163
+ // We only raise an error when the version >= 1.26 was informed via
135
164
// the k8s key/value option or is specifically defined in the CSV
136
- msg := fmt .Errorf ("this bundle is using APIs which were deprecated and removed in v1.22. More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22. Migrate the API(s) for %s" , deprecatedAPIsMessage )
137
- if isUnsupported {
138
- errs = append (errs , msg )
139
- } else {
140
- warns = append (warns , msg )
141
- }
165
+ errs = append (errs , msg )
166
+ } else {
167
+ warns = append (warns , msg )
142
168
}
143
169
}
144
-
145
170
return errs , warns
146
171
}
147
172
173
+ // isK8sVersionInformedEQ returns true only if the key/value OR minKubeVersion were informed and are >= semVerAPIUnsupported
174
+ func isK8sVersionInformedEQ (semVerVersionProvided semver.Version , semVerAPIUnsupported semver.Version , semverMinKube semver.Version ) bool {
175
+ return semVerVersionProvided .GE (semVerAPIUnsupported ) || semverMinKube .GE (semVerAPIUnsupported )
176
+ }
177
+
178
+ func verifyK8sVersionInformed (versionProvided string ) error {
179
+ if _ , err := semver .ParseTolerant (versionProvided ); err != nil {
180
+ return fmt .Errorf ("invalid value informed via the k8s key option : %s" , versionProvided )
181
+ }
182
+ return nil
183
+ }
184
+
148
185
// generateMessageWithDeprecatedAPIs will return a list with the kind and the name
149
186
// of the resource which were found and required to be upgraded
150
187
func generateMessageWithDeprecatedAPIs (deprecatedAPIs map [string ][]string ) string {
151
188
msg := ""
152
189
count := 0
153
- for k , v := range deprecatedAPIs {
190
+
191
+ keys := make ([]string , 0 , len (deprecatedAPIs ))
192
+ for k := range deprecatedAPIs {
193
+ keys = append (keys , k )
194
+ }
195
+ sort .Strings (keys )
196
+
197
+ deprecatedAPIsSorted := make (map [string ][]string )
198
+ for _ , key := range keys {
199
+ deprecatedAPIsSorted [key ] = deprecatedAPIs [key ]
200
+ }
201
+
202
+ for k , v := range deprecatedAPIsSorted {
154
203
if count == len (deprecatedAPIs )- 1 {
155
204
msg = msg + fmt .Sprintf ("%s: (%+q)" , k , v )
156
205
} else {
@@ -160,9 +209,6 @@ func generateMessageWithDeprecatedAPIs(deprecatedAPIs map[string][]string) strin
160
209
return msg
161
210
}
162
211
163
- // todo: we need to improve this code since we ought to map the kinds, apis and ocp/k8s versions
164
- // where them are no longer supported ( removed ) instead of have this fixed in this way.
165
-
166
212
// getRemovedAPIsOn1_22From return the list of resources which were deprecated
167
213
// and are no longer be supported in 1.22. More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22
168
214
func getRemovedAPIsOn1_22From (bundle * manifests.Bundle ) map [string ][]string {
@@ -228,3 +274,59 @@ func getRemovedAPIsOn1_22From(bundle *manifests.Bundle) map[string][]string {
228
274
}
229
275
return deprecatedAPIs
230
276
}
277
+
278
+ // getRemovedAPIsOn1_25From return the list of resources which were deprecated
279
+ // and are no longer be supported in 1.25. More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-25
280
+ func getRemovedAPIsOn1_25From (bundle * manifests.Bundle ) map [string ][]string {
281
+ deprecatedAPIs := make (map [string ][]string )
282
+ for _ , obj := range bundle .Objects {
283
+ switch u := obj .GetObjectKind ().(type ) {
284
+ case * unstructured.Unstructured :
285
+ switch u .GetAPIVersion () {
286
+ case "batch/v1beta1" :
287
+ if u .GetKind () == "CronJob" {
288
+ deprecatedAPIs [u .GetKind ()] = append (deprecatedAPIs [u .GetKind ()], obj .GetName ())
289
+ }
290
+ case "discovery.k8s.io/v1beta1" :
291
+ if u .GetKind () == "EndpointSlice" {
292
+ deprecatedAPIs [u .GetKind ()] = append (deprecatedAPIs [u .GetKind ()], obj .GetName ())
293
+ }
294
+ case "events.k8s.io/v1beta1" :
295
+ if u .GetKind () == "Event" {
296
+ deprecatedAPIs [u .GetKind ()] = append (deprecatedAPIs [u .GetKind ()], obj .GetName ())
297
+ }
298
+ case "autoscaling/v2beta1" :
299
+ if u .GetKind () == "HorizontalPodAutoscaler" {
300
+ deprecatedAPIs [u .GetKind ()] = append (deprecatedAPIs [u .GetKind ()], obj .GetName ())
301
+ }
302
+ case "policy/v1beta1" :
303
+ if u .GetKind () == "PodDisruptionBudget" || u .GetKind () == "PodSecurityPolicy" {
304
+ deprecatedAPIs [u .GetKind ()] = append (deprecatedAPIs [u .GetKind ()], obj .GetName ())
305
+ }
306
+ case "node.k8s.io/v1beta1" :
307
+ if u .GetKind () == "RuntimeClass" {
308
+ deprecatedAPIs [u .GetKind ()] = append (deprecatedAPIs [u .GetKind ()], obj .GetName ())
309
+ }
310
+ }
311
+ }
312
+ }
313
+ return deprecatedAPIs
314
+ }
315
+
316
+ // getRemovedAPIsOn1_26From return the list of resources which were deprecated
317
+ // and are no longer be supported in 1.26. More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-26
318
+ func getRemovedAPIsOn1_26From (bundle * manifests.Bundle ) map [string ][]string {
319
+ deprecatedAPIs := make (map [string ][]string )
320
+ for _ , obj := range bundle .Objects {
321
+ switch u := obj .GetObjectKind ().(type ) {
322
+ case * unstructured.Unstructured :
323
+ switch u .GetAPIVersion () {
324
+ case "autoscaling/v2beta2" :
325
+ if u .GetKind () == "HorizontalPodAutoscaler" {
326
+ deprecatedAPIs [u .GetKind ()] = append (deprecatedAPIs [u .GetKind ()], obj .GetName ())
327
+ }
328
+ }
329
+ }
330
+ }
331
+ return deprecatedAPIs
332
+ }
0 commit comments