@@ -17,6 +17,7 @@ limitations under the License.
17
17
package suite
18
18
19
19
import (
20
+ "context"
20
21
"errors"
21
22
"fmt"
22
23
"sort"
@@ -25,6 +26,7 @@ import (
25
26
"testing"
26
27
"time"
27
28
29
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
28
30
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
31
"k8s.io/apimachinery/pkg/util/sets"
30
32
@@ -34,6 +36,7 @@ import (
34
36
"sigs.k8s.io/gateway-api/conformance/utils/flags"
35
37
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
36
38
"sigs.k8s.io/gateway-api/conformance/utils/roundtripper"
39
+ "sigs.k8s.io/gateway-api/pkg/consts"
37
40
)
38
41
39
42
// -----------------------------------------------------------------------------
@@ -56,6 +59,16 @@ type ExperimentalConformanceTestSuite struct {
56
59
// organization, project, etc.
57
60
implementation confv1a1.Implementation
58
61
62
+ // apiVersion is the version of the Gateway API installed in the cluster
63
+ // and is extracted by the annotation gateway.networking.k8s.io/bundle-version
64
+ // in the Gateway API CRDs.
65
+ apiVersion string
66
+
67
+ // apiChannel is the channel of the Gateway API installed in the cluster
68
+ // and is extracted by the annotation gateway.networking.k8s.io/channel
69
+ // in the Gateway API CRDs.
70
+ apiChannel string
71
+
59
72
// conformanceProfiles is a compiled list of profiles to check
60
73
// conformance against.
61
74
conformanceProfiles sets.Set [ConformanceProfileName ]
@@ -84,62 +97,86 @@ type ExperimentalConformanceOptions struct {
84
97
Options
85
98
86
99
Mode string
100
+ AllowCRDsMismatch bool
87
101
Implementation confv1a1.Implementation
88
102
ConformanceProfiles sets.Set [ConformanceProfileName ]
89
103
}
90
104
105
+ const (
106
+ undefinedKeyword = "UNDEFINED"
107
+ )
108
+
91
109
// NewExperimentalConformanceTestSuite is a helper to use for creating a new ExperimentalConformanceTestSuite.
92
- func NewExperimentalConformanceTestSuite (s ExperimentalConformanceOptions ) (* ExperimentalConformanceTestSuite , error ) {
93
- config .SetupTimeoutConfig (& s .TimeoutConfig )
110
+ func NewExperimentalConformanceTestSuite (options ExperimentalConformanceOptions ) (* ExperimentalConformanceTestSuite , error ) {
111
+ config .SetupTimeoutConfig (& options .TimeoutConfig )
94
112
95
- roundTripper := s .RoundTripper
113
+ roundTripper := options .RoundTripper
96
114
if roundTripper == nil {
97
- roundTripper = & roundtripper.DefaultRoundTripper {Debug : s .Debug , TimeoutConfig : s .TimeoutConfig }
115
+ roundTripper = & roundtripper.DefaultRoundTripper {Debug : options .Debug , TimeoutConfig : options .TimeoutConfig }
116
+ }
117
+
118
+ installedCRDs := & apiextensionsv1.CustomResourceDefinitionList {}
119
+ err := options .Client .List (context .TODO (), installedCRDs )
120
+ if err != nil {
121
+ return nil , err
122
+ }
123
+ apiVersion , apiChannel , err := getAPIVersionAndChannel (installedCRDs .Items )
124
+ if err != nil {
125
+ // in case an error is returned and the AllowCRDsMismatch flag is false, the suite fails.
126
+ // This is the default behavior but can be customized in case one wants to experiment
127
+ // with mixed versions/channels of the API.
128
+ if ! options .AllowCRDsMismatch {
129
+ return nil , err
130
+ }
131
+ apiVersion = undefinedKeyword
132
+ apiChannel = undefinedKeyword
98
133
}
99
134
100
135
mode := flags .DefaultMode
101
- if s .Mode != "" {
102
- mode = s .Mode
136
+ if options .Mode != "" {
137
+ mode = options .Mode
103
138
}
104
139
105
140
suite := & ExperimentalConformanceTestSuite {
106
141
results : make (map [string ]testResult ),
107
142
extendedUnsupportedFeatures : make (map [ConformanceProfileName ]sets.Set [SupportedFeature ]),
108
143
extendedSupportedFeatures : make (map [ConformanceProfileName ]sets.Set [SupportedFeature ]),
109
- conformanceProfiles : s .ConformanceProfiles ,
110
- implementation : s .Implementation ,
144
+ conformanceProfiles : options .ConformanceProfiles ,
145
+ implementation : options .Implementation ,
111
146
mode : mode ,
147
+ apiVersion : apiVersion ,
148
+ apiChannel : apiChannel ,
112
149
}
113
150
114
151
// test suite callers are required to provide a conformance profile OR at
115
152
// minimum a list of features which they support.
116
- if s .SupportedFeatures == nil && s .ConformanceProfiles .Len () == 0 && ! s .EnableAllSupportedFeatures {
153
+ if options .SupportedFeatures == nil && options .ConformanceProfiles .Len () == 0 && ! options .EnableAllSupportedFeatures {
117
154
return nil , fmt .Errorf ("no conformance profile was selected for test run, and no supported features were provided so no tests could be selected" )
118
155
}
119
156
120
157
// test suite callers can potentially just run all tests by saying they
121
158
// cover all features, if they don't they'll need to have provided a
122
159
// conformance profile or at least some specific features they support.
123
- if s .EnableAllSupportedFeatures {
124
- s .SupportedFeatures = AllFeatures
125
- } else if s .SupportedFeatures == nil {
126
- s .SupportedFeatures = sets .New [SupportedFeature ]()
160
+ if options .EnableAllSupportedFeatures {
161
+ options .SupportedFeatures = AllFeatures
162
+ } else if options .SupportedFeatures == nil {
163
+ options .SupportedFeatures = sets .New [SupportedFeature ]()
127
164
}
128
165
129
- for _ , conformanceProfileName := range s .ConformanceProfiles .UnsortedList () {
166
+ for _ , conformanceProfileName := range options .ConformanceProfiles .UnsortedList () {
130
167
conformanceProfile , err := getConformanceProfileForName (conformanceProfileName )
131
168
if err != nil {
132
169
return nil , fmt .Errorf ("failed to retrieve conformance profile: %w" , err )
133
170
}
134
171
// the use of a conformance profile implicitly enables any features of
135
172
// that profile which are supported at a Core level of support.
136
173
for _ , f := range conformanceProfile .CoreFeatures .UnsortedList () {
137
- if ! s .SupportedFeatures .Has (f ) {
138
- s .SupportedFeatures .Insert (f )
174
+ if ! options .SupportedFeatures .Has (f ) {
175
+ options .SupportedFeatures .Insert (f )
139
176
}
140
177
}
141
178
for _ , f := range conformanceProfile .ExtendedFeatures .UnsortedList () {
142
- if s .SupportedFeatures .Has (f ) {
179
+ if options .SupportedFeatures .Has (f ) {
143
180
if suite .extendedSupportedFeatures [conformanceProfileName ] == nil {
144
181
suite .extendedSupportedFeatures [conformanceProfileName ] = sets .New [SupportedFeature ]()
145
182
}
@@ -151,40 +188,40 @@ func NewExperimentalConformanceTestSuite(s ExperimentalConformanceOptions) (*Exp
151
188
suite .extendedUnsupportedFeatures [conformanceProfileName ].Insert (f )
152
189
}
153
190
// Add Exempt Features into unsupported features list
154
- if s .ExemptFeatures .Has (f ) {
191
+ if options .ExemptFeatures .Has (f ) {
155
192
suite .extendedUnsupportedFeatures [conformanceProfileName ].Insert (f )
156
193
}
157
194
}
158
195
}
159
196
160
- for feature := range s .ExemptFeatures {
161
- s .SupportedFeatures .Delete (feature )
197
+ for feature := range options .ExemptFeatures {
198
+ options .SupportedFeatures .Delete (feature )
162
199
}
163
200
164
- if s .FS == nil {
165
- s .FS = & conformance .Manifests
201
+ if options .FS == nil {
202
+ options .FS = & conformance .Manifests
166
203
}
167
204
168
205
suite .ConformanceTestSuite = ConformanceTestSuite {
169
- Client : s .Client ,
170
- Clientset : s .Clientset ,
171
- RestConfig : s .RestConfig ,
206
+ Client : options .Client ,
207
+ Clientset : options .Clientset ,
208
+ RestConfig : options .RestConfig ,
172
209
RoundTripper : roundTripper ,
173
- GatewayClassName : s .GatewayClassName ,
174
- Debug : s .Debug ,
175
- Cleanup : s .CleanupBaseResources ,
176
- BaseManifests : s .BaseManifests ,
177
- MeshManifests : s .MeshManifests ,
210
+ GatewayClassName : options .GatewayClassName ,
211
+ Debug : options .Debug ,
212
+ Cleanup : options .CleanupBaseResources ,
213
+ BaseManifests : options .BaseManifests ,
214
+ MeshManifests : options .MeshManifests ,
178
215
Applier : kubernetes.Applier {
179
- NamespaceLabels : s .NamespaceLabels ,
180
- NamespaceAnnotations : s .NamespaceAnnotations ,
216
+ NamespaceLabels : options .NamespaceLabels ,
217
+ NamespaceAnnotations : options .NamespaceAnnotations ,
181
218
},
182
- SupportedFeatures : s .SupportedFeatures ,
183
- TimeoutConfig : s .TimeoutConfig ,
184
- SkipTests : sets .New (s .SkipTests ... ),
185
- FS : * s .FS ,
186
- UsableNetworkAddresses : s .UsableNetworkAddresses ,
187
- UnusableNetworkAddresses : s .UnusableNetworkAddresses ,
219
+ SupportedFeatures : options .SupportedFeatures ,
220
+ TimeoutConfig : options .TimeoutConfig ,
221
+ SkipTests : sets .New (options .SkipTests ... ),
222
+ FS : * options .FS ,
223
+ UsableNetworkAddresses : options .UsableNetworkAddresses ,
224
+ UnusableNetworkAddresses : options .UnusableNetworkAddresses ,
188
225
}
189
226
190
227
// apply defaults
@@ -295,7 +332,8 @@ func (suite *ExperimentalConformanceTestSuite) Report() (*confv1a1.ConformanceRe
295
332
Date : time .Now ().Format (time .RFC3339 ),
296
333
Mode : suite .mode ,
297
334
Implementation : suite .implementation ,
298
- GatewayAPIVersion : "TODO" ,
335
+ GatewayAPIVersion : suite .apiVersion ,
336
+ GatewayAPIChannel : suite .apiChannel ,
299
337
ProfileReports : profileReports .list (),
300
338
}, nil
301
339
}
@@ -344,3 +382,34 @@ func ParseConformanceProfiles(p string) sets.Set[ConformanceProfileName] {
344
382
}
345
383
return res
346
384
}
385
+
386
+ // getAPIVersionAndChannel iterates over all the crds installed in the cluster and check the version and channel annotations.
387
+ // In case the annotations are not found or there are crds with different versions or channels, an error is returned.
388
+ func getAPIVersionAndChannel (crds []apiextensionsv1.CustomResourceDefinition ) (version string , channel string , err error ) {
389
+ for _ , crd := range crds {
390
+ v , okv := crd .Annotations [consts .BundleVersionAnnotation ]
391
+ c , okc := crd .Annotations [consts .ChannelAnnotation ]
392
+ if ! okv && ! okc {
393
+ continue
394
+ }
395
+ if ! okv || ! okc {
396
+ return "" , "" , errors .New ("detected CRDs with partial version and channel annotations" )
397
+ }
398
+ if version != "" && v != version {
399
+ return "" , "" , errors .New ("multiple gateway API CRDs versions detected" )
400
+ }
401
+ if channel != "" && c != channel {
402
+ return "" , "" , errors .New ("multiple gateway API CRDs channels detected" )
403
+ }
404
+ version = v
405
+ channel = c
406
+ }
407
+ if version == "" || channel == "" {
408
+ return "" , "" , errors .New ("no Gateway API CRDs with the proper annotations found in the cluster" )
409
+ }
410
+ if version != consts .BundleVersion {
411
+ return "" , "" , errors .New ("the installed CRDs version is different from the suite version" )
412
+ }
413
+
414
+ return version , channel , nil
415
+ }
0 commit comments