Skip to content

Commit 84f7ca5

Browse files
camilamacedo86timflannagan
authored andcommitted
feat: add new validator to only check the usage of deprecate apis (openshift#156)
* feat: add new validator to only check the usage of deprecate apis * applying review sugesstions Upstream-repository: api Upstream-commit: e2541569a535e6421048bfaee00b781bb3dc092f
1 parent d1d3813 commit 84f7ca5

File tree

10 files changed

+409
-283
lines changed

10 files changed

+409
-283
lines changed

staging/api/pkg/validation/internal/community.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ const IndexImagePathKey = "index-path"
2121
// where the bundle will be distributed
2222
const ocpLabelindex = "com.redhat.openshift.versions"
2323

24+
// OCP version where the apis v1beta1 is no longer supported
25+
const ocpVerV1beta1Unsupported = "4.9"
26+
2427
// CommunityOperatorValidator validates the bundle manifests against the required criteria to publish
2528
// the projects on the community operators
2629
//

staging/api/pkg/validation/internal/operatorhub.go

Lines changed: 1 addition & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,6 @@ import (
1818
interfaces "github.com/operator-framework/api/pkg/validation/interfaces"
1919
)
2020

21-
// k8sVersionKey defines the key which can be used by its consumers
22-
// to inform what is the K8S version that should be used to do the tests against.
23-
const k8sVersionKey = "k8s-version"
24-
25-
const minKubeVersionWarnMessage = "csv.Spec.minKubeVersion is not informed. It is recommended you provide this information. " +
26-
"Otherwise, it would mean that your operator project can be distributed and installed in any cluster version " +
27-
"available, which is not necessarily the case for all projects."
28-
2921
// OperatorHubValidator validates the bundle manifests against the required criteria to publish
3022
// the projects on OperatorHub.io.
3123
//
@@ -111,7 +103,7 @@ func validateBundleOperatorHub(bundle *manifests.Bundle, k8sVersion string) erro
111103
result.Add(errors.WarnInvalidCSV(warn.Error(), bundle.CSV.GetName()))
112104
}
113105

114-
errs, warns := validateHubDeprecatedAPIS(bundle, k8sVersion)
106+
errs, warns := validateDeprecatedAPIS(bundle, k8sVersion)
115107
for _, err := range errs {
116108
result.Add(errors.ErrFailedValidation(err.Error(), bundle.CSV.GetName()))
117109
}
@@ -153,77 +145,6 @@ func validateHubChannels(channels []string) error {
153145
return nil
154146
}
155147

156-
// validateHubDeprecatedAPIS will check if the operator bundle is using a deprecated or no longer supported k8s api
157-
// Note if the k8s was informed via "k8s=1.22" it will be used. Otherwise, we will use the minKubeVersion in
158-
// the CSV to do the checks. So, the criteria is >=minKubeVersion. By last, if the minKubeVersion is not provided
159-
// then, we should consider the operator bundle is intend to work well in any Kubernetes version.
160-
// Then, it means that:
161-
//--optional-values="k8s-version=value" flag with a value => 1.16 <= 1.22 the validator will return result as warning.
162-
//--optional-values="k8s-version=value" flag with a value => 1.22 the validator will return result as error.
163-
//minKubeVersion >= 1.22 return the error result.
164-
//minKubeVersion empty returns a warning since it would mean the same of allow install in any supported version
165-
func validateHubDeprecatedAPIS(bundle *manifests.Bundle, versionProvided string) (errs, warns []error) {
166-
// K8s version where the apis v1betav1 is no longer supported
167-
const k8sVerV1betav1Unsupported = "1.22.0"
168-
// K8s version where the apis v1betav1 was deprecated
169-
const k8sVerV1betav1Deprecated = "1.16.0"
170-
// semver of the K8s version where the apis v1betav1 is no longer supported to allow us compare
171-
semVerK8sVerV1betav1Unsupported := semver.MustParse(k8sVerV1betav1Unsupported)
172-
// semver of the K8s version where the apis v1betav1 is deprecated to allow us compare
173-
semVerk8sVerV1betav1Deprecated := semver.MustParse(k8sVerV1betav1Deprecated)
174-
// isVersionProvided defines if the k8s version to test against was or not informed
175-
isVersionProvided := len(versionProvided) > 0
176-
177-
// Transform the key/option versionProvided in semver Version to compare
178-
var semVerVersionProvided semver.Version
179-
if isVersionProvided {
180-
var err error
181-
semVerVersionProvided, err = semver.ParseTolerant(versionProvided)
182-
if err != nil {
183-
errs = append(errs, fmt.Errorf("invalid value informed via the k8s key option : %s", versionProvided))
184-
} else {
185-
// we might want to return it as info instead of warning in the future.
186-
warns = append(warns, fmt.Errorf("checking APIs against Kubernetes version : %s", versionProvided))
187-
}
188-
}
189-
190-
// Transform the spec minKubeVersion in semver Version to compare
191-
var semverMinKube semver.Version
192-
if len(bundle.CSV.Spec.MinKubeVersion) > 0 {
193-
var err error
194-
if semverMinKube, err = semver.ParseTolerant(bundle.CSV.Spec.MinKubeVersion); err != nil {
195-
errs = append(errs, fmt.Errorf("unable to use csv.Spec.MinKubeVersion to verify the CRD/Webhook apis "+
196-
"because it has an invalid value: %s", bundle.CSV.Spec.MinKubeVersion))
197-
}
198-
}
199-
200-
// if the k8s value was informed and it is >=1.16 we should check
201-
// if the k8s value was not informed we also should check since the
202-
// check should occurs with any minKubeVersion value:
203-
// - if minKubeVersion empty then means that the project can be installed in any version
204-
// - if minKubeVersion any version defined it means that we are considering install
205-
// in any upper version from that where the check is always applied
206-
if !isVersionProvided || semVerVersionProvided.GE(semVerk8sVerV1betav1Deprecated) {
207-
deprecatedAPIs := getRemovedAPIsOn1_22From(bundle)
208-
if len(deprecatedAPIs) > 0 {
209-
deprecatedAPIsMessage := generateMessageWithDeprecatedAPIs(deprecatedAPIs)
210-
// isUnsupported is true only if the key/value OR minKubeVersion were informed and are >= 1.22
211-
isUnsupported := semVerVersionProvided.GE(semVerK8sVerV1betav1Unsupported) ||
212-
semverMinKube.GE(semVerK8sVerV1betav1Unsupported)
213-
// We only raise an error when the version >= 1.22 was informed via
214-
// the k8s key/value option or is specifically defined in the CSV
215-
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)
216-
if isUnsupported {
217-
errs = append(errs, msg)
218-
} else {
219-
warns = append(warns, msg)
220-
}
221-
}
222-
}
223-
224-
return errs, warns
225-
}
226-
227148
// validateHubCSVSpec will check the CSV against the criteria to publish an
228149
// operator bundle in the OperatorHub.io
229150
func validateHubCSVSpec(csv v1alpha1.ClusterServiceVersion) CSVChecks {

staging/api/pkg/validation/internal/operatorhub_test.go

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -343,125 +343,6 @@ func TestCheckSpecMinKubeVersion(t *testing.T) {
343343
}
344344
}
345345

346-
func TestValidateHubDeprecatedAPIS(t *testing.T) {
347-
type args struct {
348-
minKubeVersion string
349-
k8sVersion string
350-
directory string
351-
}
352-
tests := []struct {
353-
name string
354-
args args
355-
wantError bool
356-
wantWarning bool
357-
errStrings []string
358-
warnStrings []string
359-
}{
360-
{
361-
name: "should not return error or warning when the k8sVersion is <= 1.15",
362-
args: args{
363-
k8sVersion: "1.15",
364-
minKubeVersion: "",
365-
directory: "./testdata/valid_bundle_v1beta1",
366-
},
367-
wantWarning: true,
368-
warnStrings: []string{"checking APIs against Kubernetes version : 1.15"},
369-
},
370-
{
371-
name: "should return a warning when has the CRD v1beta1 and minKubeVersion is informed",
372-
args: args{
373-
k8sVersion: "",
374-
minKubeVersion: "1.11.3",
375-
directory: "./testdata/valid_bundle_v1beta1",
376-
},
377-
wantWarning: true,
378-
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.22. " +
379-
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22. " +
380-
"Migrate the API(s) for CRD: ([\"etcdbackups.etcd.database.coreos.com\" " +
381-
"\"etcdclusters.etcd.database.coreos.com\" \"etcdrestores.etcd.database.coreos.com\"])"},
382-
},
383-
{
384-
name: "should not return a warning or error when has minKubeVersion but the k8sVersion informed is <= 1.15",
385-
args: args{
386-
k8sVersion: "1.15",
387-
minKubeVersion: "1.11.3",
388-
directory: "./testdata/valid_bundle_v1beta1",
389-
},
390-
wantWarning: true,
391-
warnStrings: []string{"checking APIs against Kubernetes version : 1.15"},
392-
},
393-
{
394-
name: "should return an error when the k8sVersion is >= 1.22 and has the deprecated API",
395-
args: args{
396-
k8sVersion: "1.22",
397-
minKubeVersion: "",
398-
directory: "./testdata/valid_bundle_v1beta1",
399-
},
400-
wantError: true,
401-
errStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.22. " +
402-
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22. " +
403-
"Migrate the API(s) for CRD: ([\"etcdbackups.etcd.database.coreos.com\"" +
404-
" \"etcdclusters.etcd.database.coreos.com\" \"etcdrestores.etcd.database.coreos.com\"])"},
405-
wantWarning: true,
406-
warnStrings: []string{"checking APIs against Kubernetes version : 1.22"},
407-
},
408-
{
409-
name: "should return an error when the k8sVersion informed is invalid",
410-
args: args{
411-
k8sVersion: "invalid",
412-
minKubeVersion: "",
413-
directory: "./testdata/valid_bundle_v1beta1",
414-
},
415-
wantError: true,
416-
errStrings: []string{"invalid value informed via the k8s key option : invalid"},
417-
},
418-
{
419-
name: "should return an error when the csv.spec.minKubeVersion informed is invalid",
420-
args: args{
421-
minKubeVersion: "invalid",
422-
directory: "./testdata/valid_bundle_v1beta1",
423-
},
424-
wantError: true,
425-
wantWarning: true,
426-
errStrings: []string{"unable to use csv.Spec.MinKubeVersion to verify the CRD/Webhook apis because it " +
427-
"has an invalid value: invalid"},
428-
warnStrings: []string{"this bundle is using APIs which were deprecated and removed in v1.22. " +
429-
"More info: https://kubernetes.io/docs/reference/using-api/deprecation-guide/#v1-22. " +
430-
"Migrate the API(s) for CRD: ([\"etcdbackups.etcd.database.coreos.com\" " +
431-
"\"etcdclusters.etcd.database.coreos.com\" \"etcdrestores.etcd.database.coreos.com\"])"},
432-
},
433-
}
434-
for _, tt := range tests {
435-
t.Run(tt.name, func(t *testing.T) {
436-
// Validate the bundle object
437-
bundle, err := manifests.GetBundleFromDir(tt.args.directory)
438-
require.NoError(t, err)
439-
440-
bundle.CSV.Spec.MinKubeVersion = tt.args.minKubeVersion
441-
442-
errsResult, warnsResult := validateHubDeprecatedAPIS(bundle, tt.args.k8sVersion)
443-
444-
require.Equal(t, tt.wantWarning, len(warnsResult) > 0)
445-
if tt.wantWarning {
446-
require.Equal(t, len(tt.warnStrings), len(warnsResult))
447-
for _, w := range warnsResult {
448-
wString := w.Error()
449-
require.Contains(t, tt.warnStrings, wString)
450-
}
451-
}
452-
453-
require.Equal(t, tt.wantError, len(errsResult) > 0)
454-
if tt.wantError {
455-
require.Equal(t, len(tt.errStrings), len(errsResult))
456-
for _, err := range errsResult {
457-
errString := err.Error()
458-
require.Contains(t, tt.errStrings, errString)
459-
}
460-
}
461-
})
462-
}
463-
}
464-
465346
func TestValidateHubChannels(t *testing.T) {
466347
type args struct {
467348
channels []string

staging/api/pkg/validation/internal/removed_apis.go

Lines changed: 136 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,147 @@ package internal
22

33
import (
44
"fmt"
5+
"github.com/blang/semver"
56

67
"github.com/operator-framework/api/pkg/manifests"
78
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
9+
10+
"github.com/operator-framework/api/pkg/validation/errors"
11+
interfaces "github.com/operator-framework/api/pkg/validation/interfaces"
812
)
913

10-
// OCP version where the apis v1beta1 is no longer supported
11-
const ocpVerV1beta1Unsupported = "4.9"
14+
// k8sVersionKey defines the key which can be used by its consumers
15+
// to inform what is the K8S version that should be used to do the tests against.
16+
const k8sVersionKey = "k8s-version"
17+
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"
24+
// K8s version where the apis v1betav1 was deprecated
25+
const k8sVerV1betav1Deprecated = "1.16.0"
26+
27+
// AlphaDeprecatedAPIsValidator validates if the bundles is using versions API version which are deprecate or
28+
// removed in specific Kubernetes versions informed via optional key value `k8s-version`.
29+
var AlphaDeprecatedAPIsValidator interfaces.Validator = interfaces.ValidatorFunc(validateDeprecatedAPIsValidator)
30+
31+
func validateDeprecatedAPIsValidator(objs ...interface{}) (results []errors.ManifestResult) {
32+
33+
// Obtain the k8s version if informed via the objects an optional
34+
k8sVersion := ""
35+
for _, obj := range objs {
36+
switch obj.(type) {
37+
case map[string]string:
38+
k8sVersion = obj.(map[string]string)[k8sVersionKey]
39+
if len(k8sVersion) > 0 {
40+
break
41+
}
42+
}
43+
}
44+
45+
for _, obj := range objs {
46+
switch v := obj.(type) {
47+
case *manifests.Bundle:
48+
results = append(results, validateDeprecatedAPIs(v, k8sVersion))
49+
}
50+
}
51+
52+
return results
53+
}
54+
55+
func validateDeprecatedAPIs(bundle *manifests.Bundle, k8sVersion string) errors.ManifestResult {
56+
result := errors.ManifestResult{Name: bundle.Name}
57+
58+
if bundle == nil {
59+
result.Add(errors.ErrInvalidBundle("Bundle is nil", nil))
60+
return result
61+
}
62+
63+
if bundle.CSV == nil {
64+
result.Add(errors.ErrInvalidBundle("Bundle csv is nil", bundle.Name))
65+
return result
66+
}
67+
68+
errs, warns := validateDeprecatedAPIS(bundle, k8sVersion)
69+
for _, err := range errs {
70+
result.Add(errors.ErrFailedValidation(err.Error(), bundle.CSV.GetName()))
71+
}
72+
for _, warn := range warns {
73+
result.Add(errors.WarnFailedValidation(warn.Error(), bundle.CSV.GetName()))
74+
}
75+
76+
return result
77+
}
78+
79+
// validateDeprecatedAPIS will check if the operator bundle is using a deprecated or no longer supported k8s api
80+
// Note if the k8s was informed via "k8s=1.22" it will be used. Otherwise, we will use the minKubeVersion in
81+
// the CSV to do the checks. So, the criteria is >=minKubeVersion. By last, if the minKubeVersion is not provided
82+
// then, we should consider the operator bundle is intend to work well in any Kubernetes version.
83+
// Then, it means that:
84+
//--optional-values="k8s-version=value" flag with a value => 1.16 <= 1.22 the validator will return result as warning.
85+
//--optional-values="k8s-version=value" flag with a value => 1.22 the validator will return result as error.
86+
//minKubeVersion >= 1.22 return the error result.
87+
//minKubeVersion empty returns a warning since it would mean the same of allow install in any supported version
88+
func validateDeprecatedAPIS(bundle *manifests.Bundle, versionProvided string) (errs, warns []error) {
89+
90+
// semver of the K8s version where the apis v1betav1 is no longer supported to allow us compare
91+
semVerK8sVerV1betav1Unsupported := semver.MustParse(k8sVerV1betav1Unsupported)
92+
// semver of the K8s version where the apis v1betav1 is deprecated to allow us compare
93+
semVerk8sVerV1betav1Deprecated := semver.MustParse(k8sVerV1betav1Deprecated)
94+
// isVersionProvided defines if the k8s version to test against was or not informed
95+
isVersionProvided := len(versionProvided) > 0
96+
97+
// Transform the key/option versionProvided in semver Version to compare
98+
var semVerVersionProvided semver.Version
99+
if isVersionProvided {
100+
var err error
101+
semVerVersionProvided, err = semver.ParseTolerant(versionProvided)
102+
if err != nil {
103+
errs = append(errs, fmt.Errorf("invalid value informed via the k8s key option : %s", versionProvided))
104+
} else {
105+
// we might want to return it as info instead of warning in the future.
106+
warns = append(warns, fmt.Errorf("checking APIs against Kubernetes version : %s", versionProvided))
107+
}
108+
}
109+
110+
// Transform the spec minKubeVersion in semver Version to compare
111+
var semverMinKube semver.Version
112+
if len(bundle.CSV.Spec.MinKubeVersion) > 0 {
113+
var err error
114+
if semverMinKube, err = semver.ParseTolerant(bundle.CSV.Spec.MinKubeVersion); err != nil {
115+
errs = append(errs, fmt.Errorf("unable to use csv.Spec.MinKubeVersion to verify the CRD/Webhook apis "+
116+
"because it has an invalid value: %s", bundle.CSV.Spec.MinKubeVersion))
117+
}
118+
}
119+
120+
// if the k8s value was informed and it is >=1.16 we should check
121+
// if the k8s value was not informed we also should check since the
122+
// check should occurs with any minKubeVersion value:
123+
// - if minKubeVersion empty then means that the project can be installed in any version
124+
// - if minKubeVersion any version defined it means that we are considering install
125+
// in any upper version from that where the check is always applied
126+
if !isVersionProvided || semVerVersionProvided.GE(semVerk8sVerV1betav1Deprecated) {
127+
deprecatedAPIs := getRemovedAPIsOn1_22From(bundle)
128+
if len(deprecatedAPIs) > 0 {
129+
deprecatedAPIsMessage := generateMessageWithDeprecatedAPIs(deprecatedAPIs)
130+
// isUnsupported is true only if the key/value OR minKubeVersion were informed and are >= 1.22
131+
isUnsupported := semVerVersionProvided.GE(semVerK8sVerV1betav1Unsupported) ||
132+
semverMinKube.GE(semVerK8sVerV1betav1Unsupported)
133+
// We only raise an error when the version >= 1.22 was informed via
134+
// the k8s key/value option or is specifically defined in the CSV
135+
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)
136+
if isUnsupported {
137+
errs = append(errs, msg)
138+
} else {
139+
warns = append(warns, msg)
140+
}
141+
}
142+
}
143+
144+
return errs, warns
145+
}
12146

13147
// generateMessageWithDeprecatedAPIs will return a list with the kind and the name
14148
// of the resource which were found and required to be upgraded

0 commit comments

Comments
 (0)