@@ -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 ]
@@ -89,57 +102,69 @@ type ExperimentalConformanceOptions struct {
89
102
}
90
103
91
104
// NewExperimentalConformanceTestSuite is a helper to use for creating a new ExperimentalConformanceTestSuite.
92
- func NewExperimentalConformanceTestSuite (s ExperimentalConformanceOptions ) (* ExperimentalConformanceTestSuite , error ) {
93
- config .SetupTimeoutConfig (& s .TimeoutConfig )
105
+ func NewExperimentalConformanceTestSuite (options ExperimentalConformanceOptions ) (* ExperimentalConformanceTestSuite , error ) {
106
+ config .SetupTimeoutConfig (& options .TimeoutConfig )
94
107
95
- roundTripper := s .RoundTripper
108
+ roundTripper := options .RoundTripper
96
109
if roundTripper == nil {
97
- roundTripper = & roundtripper.DefaultRoundTripper {Debug : s .Debug , TimeoutConfig : s .TimeoutConfig }
110
+ roundTripper = & roundtripper.DefaultRoundTripper {Debug : options .Debug , TimeoutConfig : options .TimeoutConfig }
111
+ }
112
+
113
+ installedCRDs := & apiextensionsv1.CustomResourceDefinitionList {}
114
+ err := options .Client .List (context .TODO (), installedCRDs )
115
+ if err != nil {
116
+ return nil , err
117
+ }
118
+ apiVersion , apiChannel , err := getAPIVersionAndChannel (installedCRDs .Items )
119
+ if err != nil {
120
+ return nil , err
98
121
}
99
122
100
123
mode := flags .DefaultMode
101
- if s .Mode != "" {
102
- mode = s .Mode
124
+ if options .Mode != "" {
125
+ mode = options .Mode
103
126
}
104
127
105
128
suite := & ExperimentalConformanceTestSuite {
106
129
results : make (map [string ]testResult ),
107
130
extendedUnsupportedFeatures : make (map [ConformanceProfileName ]sets.Set [SupportedFeature ]),
108
131
extendedSupportedFeatures : make (map [ConformanceProfileName ]sets.Set [SupportedFeature ]),
109
- conformanceProfiles : s .ConformanceProfiles ,
110
- implementation : s .Implementation ,
132
+ conformanceProfiles : options .ConformanceProfiles ,
133
+ implementation : options .Implementation ,
111
134
mode : mode ,
135
+ apiVersion : apiVersion ,
136
+ apiChannel : apiChannel ,
112
137
}
113
138
114
139
// test suite callers are required to provide a conformance profile OR at
115
140
// minimum a list of features which they support.
116
- if s .SupportedFeatures == nil && s .ConformanceProfiles .Len () == 0 && ! s .EnableAllSupportedFeatures {
141
+ if options .SupportedFeatures == nil && options .ConformanceProfiles .Len () == 0 && ! options .EnableAllSupportedFeatures {
117
142
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
143
}
119
144
120
145
// test suite callers can potentially just run all tests by saying they
121
146
// cover all features, if they don't they'll need to have provided a
122
147
// 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 ]()
148
+ if options .EnableAllSupportedFeatures {
149
+ options .SupportedFeatures = AllFeatures
150
+ } else if options .SupportedFeatures == nil {
151
+ options .SupportedFeatures = sets .New [SupportedFeature ]()
127
152
}
128
153
129
- for _ , conformanceProfileName := range s .ConformanceProfiles .UnsortedList () {
154
+ for _ , conformanceProfileName := range options .ConformanceProfiles .UnsortedList () {
130
155
conformanceProfile , err := getConformanceProfileForName (conformanceProfileName )
131
156
if err != nil {
132
157
return nil , fmt .Errorf ("failed to retrieve conformance profile: %w" , err )
133
158
}
134
159
// the use of a conformance profile implicitly enables any features of
135
160
// that profile which are supported at a Core level of support.
136
161
for _ , f := range conformanceProfile .CoreFeatures .UnsortedList () {
137
- if ! s .SupportedFeatures .Has (f ) {
138
- s .SupportedFeatures .Insert (f )
162
+ if ! options .SupportedFeatures .Has (f ) {
163
+ options .SupportedFeatures .Insert (f )
139
164
}
140
165
}
141
166
for _ , f := range conformanceProfile .ExtendedFeatures .UnsortedList () {
142
- if s .SupportedFeatures .Has (f ) {
167
+ if options .SupportedFeatures .Has (f ) {
143
168
if suite .extendedSupportedFeatures [conformanceProfileName ] == nil {
144
169
suite .extendedSupportedFeatures [conformanceProfileName ] = sets .New [SupportedFeature ]()
145
170
}
@@ -151,40 +176,40 @@ func NewExperimentalConformanceTestSuite(s ExperimentalConformanceOptions) (*Exp
151
176
suite .extendedUnsupportedFeatures [conformanceProfileName ].Insert (f )
152
177
}
153
178
// Add Exempt Features into unsupported features list
154
- if s .ExemptFeatures .Has (f ) {
179
+ if options .ExemptFeatures .Has (f ) {
155
180
suite .extendedUnsupportedFeatures [conformanceProfileName ].Insert (f )
156
181
}
157
182
}
158
183
}
159
184
160
- for feature := range s .ExemptFeatures {
161
- s .SupportedFeatures .Delete (feature )
185
+ for feature := range options .ExemptFeatures {
186
+ options .SupportedFeatures .Delete (feature )
162
187
}
163
188
164
- if s .FS == nil {
165
- s .FS = & conformance .Manifests
189
+ if options .FS == nil {
190
+ options .FS = & conformance .Manifests
166
191
}
167
192
168
193
suite .ConformanceTestSuite = ConformanceTestSuite {
169
- Client : s .Client ,
170
- Clientset : s .Clientset ,
171
- RestConfig : s .RestConfig ,
194
+ Client : options .Client ,
195
+ Clientset : options .Clientset ,
196
+ RestConfig : options .RestConfig ,
172
197
RoundTripper : roundTripper ,
173
- GatewayClassName : s .GatewayClassName ,
174
- Debug : s .Debug ,
175
- Cleanup : s .CleanupBaseResources ,
176
- BaseManifests : s .BaseManifests ,
177
- MeshManifests : s .MeshManifests ,
198
+ GatewayClassName : options .GatewayClassName ,
199
+ Debug : options .Debug ,
200
+ Cleanup : options .CleanupBaseResources ,
201
+ BaseManifests : options .BaseManifests ,
202
+ MeshManifests : options .MeshManifests ,
178
203
Applier : kubernetes.Applier {
179
- NamespaceLabels : s .NamespaceLabels ,
180
- NamespaceAnnotations : s .NamespaceAnnotations ,
204
+ NamespaceLabels : options .NamespaceLabels ,
205
+ NamespaceAnnotations : options .NamespaceAnnotations ,
181
206
},
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 ,
207
+ SupportedFeatures : options .SupportedFeatures ,
208
+ TimeoutConfig : options .TimeoutConfig ,
209
+ SkipTests : sets .New (options .SkipTests ... ),
210
+ FS : * options .FS ,
211
+ UsableNetworkAddresses : options .UsableNetworkAddresses ,
212
+ UnusableNetworkAddresses : options .UnusableNetworkAddresses ,
188
213
}
189
214
190
215
// apply defaults
@@ -295,7 +320,8 @@ func (suite *ExperimentalConformanceTestSuite) Report() (*confv1a1.ConformanceRe
295
320
Date : time .Now ().Format (time .RFC3339 ),
296
321
Mode : suite .mode ,
297
322
Implementation : suite .implementation ,
298
- GatewayAPIVersion : "TODO" ,
323
+ GatewayAPIVersion : suite .apiVersion ,
324
+ GatewayAPIChannel : suite .apiChannel ,
299
325
ProfileReports : profileReports .list (),
300
326
}, nil
301
327
}
@@ -344,3 +370,30 @@ func ParseConformanceProfiles(p string) sets.Set[ConformanceProfileName] {
344
370
}
345
371
return res
346
372
}
373
+
374
+ // getAPIVersionAndChannel iterates over all the crds installed in the cluster and check the version and channel annotations.
375
+ // In case the annotations are not found or there are crds with different versions or channels, an error is returned.
376
+ func getAPIVersionAndChannel (crds []apiextensionsv1.CustomResourceDefinition ) (version string , channel string , err error ) {
377
+ for _ , crd := range crds {
378
+ v , okv := crd .Annotations [consts .BundleVersionAnnotation ]
379
+ c , okc := crd .Annotations [consts .ChannelAnnotation ]
380
+ if ! okv && ! okc {
381
+ continue
382
+ }
383
+ if ! okv || ! okc {
384
+ return "" , "" , errors .New ("detected CRDs with partial version and channel annotations" )
385
+ }
386
+ if version != "" && v != version {
387
+ return "" , "" , errors .New ("multiple gateway API CRDs versions detected" )
388
+ }
389
+ if channel != "" && c != channel {
390
+ return "" , "" , errors .New ("multiple gateway API CRDs channels detected" )
391
+ }
392
+ version = v
393
+ channel = c
394
+ }
395
+ if version == "" || channel == "" {
396
+ return "" , "" , errors .New ("no Gateway API CRDs with the proper annotations found in the cluster" )
397
+ }
398
+ return version , channel , nil
399
+ }
0 commit comments