Skip to content

Commit b1f0239

Browse files
committed
feat: CRD version and channel added to tests
Signed-off-by: Mattia Lavacca <[email protected]>
1 parent 9fb8dc6 commit b1f0239

File tree

6 files changed

+307
-44
lines changed

6 files changed

+307
-44
lines changed

conformance/apis/v1alpha1/conformancereport.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ type ConformanceReport struct {
3737
// Mode is the operating mode the implementation used to run conformance tests.
3838
Mode string `json:"mode"`
3939

40+
// GatewayAPIChannel indicates which release channel of Gateway API this
41+
// test report was made for.
42+
GatewayAPIChannel string `json:"gatewayAPIChannel"`
43+
4044
// ProfileReports is a list of the individual reports for each conformance
4145
// profile that was enabled for a test run.
4246
ProfileReports []ProfileReport `json:"profiles"`

conformance/experimental_conformance_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"os"
2121
"testing"
2222

23+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2324
"k8s.io/apimachinery/pkg/util/sets"
2425
"k8s.io/client-go/kubernetes"
2526
"k8s.io/client-go/rest"
@@ -68,6 +69,7 @@ func TestExperimentalConformance(t *testing.T) {
6869
v1alpha2.AddToScheme(mgrClient.Scheme())
6970
v1beta1.AddToScheme(mgrClient.Scheme())
7071
gatewayv1.AddToScheme(mgrClient.Scheme())
72+
apiextensionsv1.AddToScheme(mgrClient.Scheme())
7173

7274
// standard conformance flags
7375
supportedFeatures = suite.ParseSupportedFeatures(*flags.SupportedFeatures)

conformance/utils/suite/experimental_suite.go

Lines changed: 92 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package suite
1818

1919
import (
20+
"context"
2021
"errors"
2122
"fmt"
2223
"sort"
@@ -25,6 +26,7 @@ import (
2526
"testing"
2627
"time"
2728

29+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2830
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2931
"k8s.io/apimachinery/pkg/util/sets"
3032

@@ -34,6 +36,7 @@ import (
3436
"sigs.k8s.io/gateway-api/conformance/utils/flags"
3537
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
3638
"sigs.k8s.io/gateway-api/conformance/utils/roundtripper"
39+
"sigs.k8s.io/gateway-api/pkg/consts"
3740
)
3841

3942
// -----------------------------------------------------------------------------
@@ -56,6 +59,16 @@ type ExperimentalConformanceTestSuite struct {
5659
// organization, project, etc.
5760
implementation confv1a1.Implementation
5861

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+
5972
// conformanceProfiles is a compiled list of profiles to check
6073
// conformance against.
6174
conformanceProfiles sets.Set[ConformanceProfileName]
@@ -89,57 +102,69 @@ type ExperimentalConformanceOptions struct {
89102
}
90103

91104
// 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)
94107

95-
roundTripper := s.RoundTripper
108+
roundTripper := options.RoundTripper
96109
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
98121
}
99122

100123
mode := flags.DefaultMode
101-
if s.Mode != "" {
102-
mode = s.Mode
124+
if options.Mode != "" {
125+
mode = options.Mode
103126
}
104127

105128
suite := &ExperimentalConformanceTestSuite{
106129
results: make(map[string]testResult),
107130
extendedUnsupportedFeatures: make(map[ConformanceProfileName]sets.Set[SupportedFeature]),
108131
extendedSupportedFeatures: make(map[ConformanceProfileName]sets.Set[SupportedFeature]),
109-
conformanceProfiles: s.ConformanceProfiles,
110-
implementation: s.Implementation,
132+
conformanceProfiles: options.ConformanceProfiles,
133+
implementation: options.Implementation,
111134
mode: mode,
135+
apiVersion: apiVersion,
136+
apiChannel: apiChannel,
112137
}
113138

114139
// test suite callers are required to provide a conformance profile OR at
115140
// 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 {
117142
return nil, fmt.Errorf("no conformance profile was selected for test run, and no supported features were provided so no tests could be selected")
118143
}
119144

120145
// test suite callers can potentially just run all tests by saying they
121146
// cover all features, if they don't they'll need to have provided a
122147
// 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]()
127152
}
128153

129-
for _, conformanceProfileName := range s.ConformanceProfiles.UnsortedList() {
154+
for _, conformanceProfileName := range options.ConformanceProfiles.UnsortedList() {
130155
conformanceProfile, err := getConformanceProfileForName(conformanceProfileName)
131156
if err != nil {
132157
return nil, fmt.Errorf("failed to retrieve conformance profile: %w", err)
133158
}
134159
// the use of a conformance profile implicitly enables any features of
135160
// that profile which are supported at a Core level of support.
136161
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)
139164
}
140165
}
141166
for _, f := range conformanceProfile.ExtendedFeatures.UnsortedList() {
142-
if s.SupportedFeatures.Has(f) {
167+
if options.SupportedFeatures.Has(f) {
143168
if suite.extendedSupportedFeatures[conformanceProfileName] == nil {
144169
suite.extendedSupportedFeatures[conformanceProfileName] = sets.New[SupportedFeature]()
145170
}
@@ -151,40 +176,40 @@ func NewExperimentalConformanceTestSuite(s ExperimentalConformanceOptions) (*Exp
151176
suite.extendedUnsupportedFeatures[conformanceProfileName].Insert(f)
152177
}
153178
// Add Exempt Features into unsupported features list
154-
if s.ExemptFeatures.Has(f) {
179+
if options.ExemptFeatures.Has(f) {
155180
suite.extendedUnsupportedFeatures[conformanceProfileName].Insert(f)
156181
}
157182
}
158183
}
159184

160-
for feature := range s.ExemptFeatures {
161-
s.SupportedFeatures.Delete(feature)
185+
for feature := range options.ExemptFeatures {
186+
options.SupportedFeatures.Delete(feature)
162187
}
163188

164-
if s.FS == nil {
165-
s.FS = &conformance.Manifests
189+
if options.FS == nil {
190+
options.FS = &conformance.Manifests
166191
}
167192

168193
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,
172197
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,
178203
Applier: kubernetes.Applier{
179-
NamespaceLabels: s.NamespaceLabels,
180-
NamespaceAnnotations: s.NamespaceAnnotations,
204+
NamespaceLabels: options.NamespaceLabels,
205+
NamespaceAnnotations: options.NamespaceAnnotations,
181206
},
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,
188213
}
189214

190215
// apply defaults
@@ -295,7 +320,8 @@ func (suite *ExperimentalConformanceTestSuite) Report() (*confv1a1.ConformanceRe
295320
Date: time.Now().Format(time.RFC3339),
296321
Mode: suite.mode,
297322
Implementation: suite.implementation,
298-
GatewayAPIVersion: "TODO",
323+
GatewayAPIVersion: suite.apiVersion,
324+
GatewayAPIChannel: suite.apiChannel,
299325
ProfileReports: profileReports.list(),
300326
}, nil
301327
}
@@ -344,3 +370,30 @@ func ParseConformanceProfiles(p string) sets.Set[ConformanceProfileName] {
344370
}
345371
return res
346372
}
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

Comments
 (0)