@@ -2,13 +2,147 @@ package internal
2
2
3
3
import (
4
4
"fmt"
5
+ "github.com/blang/semver"
5
6
6
7
"github.com/operator-framework/api/pkg/manifests"
7
8
"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"
8
12
)
9
13
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
+ }
12
146
13
147
// generateMessageWithDeprecatedAPIs will return a list with the kind and the name
14
148
// of the resource which were found and required to be upgraded
0 commit comments