Skip to content

Commit 0212980

Browse files
authored
Add SupportedFeatures to GatewayClassStatus (#4236)
* Add SupportedFeatures to GatewayClassStatus
1 parent aa0f45c commit 0212980

15 files changed

+346
-61
lines changed

internal/controller/manager.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
3737
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
3838
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
39+
"sigs.k8s.io/gateway-api/pkg/consts"
3940

4041
ngfAPIv1alpha1 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1"
4142
ngfAPIv1alpha2 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha2"
@@ -493,7 +494,7 @@ func registerControllers(
493494
options: []controller.Option{
494495
controller.WithOnlyMetadata(),
495496
controller.WithK8sPredicate(
496-
predicate.AnnotationPredicate{Annotation: graph.BundleVersionAnnotation},
497+
predicate.AnnotationPredicate{Annotation: consts.BundleVersionAnnotation},
497498
),
498499
},
499500
},

internal/controller/state/change_processor.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
v1 "sigs.k8s.io/gateway-api/apis/v1"
1616
"sigs.k8s.io/gateway-api/apis/v1alpha2"
1717
"sigs.k8s.io/gateway-api/apis/v1beta1"
18+
"sigs.k8s.io/gateway-api/pkg/consts"
1819

1920
ngfAPIv1alpha1 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1"
2021
ngfAPIv1alpha2 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha2"
@@ -63,6 +64,8 @@ type ChangeProcessorConfig struct {
6364
GatewayCtlrName string
6465
// GatewayClassName is the name of the GatewayClass resource.
6566
GatewayClassName string
67+
// ExperimentalFeatures indicates if experimental features are enabled.
68+
ExperimentalFeatures bool
6669
}
6770

6871
// ChangeProcessorImpl is an implementation of ChangeProcessor.
@@ -190,7 +193,7 @@ func NewChangeProcessorImpl(cfg ChangeProcessorConfig) *ChangeProcessorImpl {
190193
{
191194
gvk: cfg.MustExtractGVK(&apiext.CustomResourceDefinition{}),
192195
store: newObjectStoreMapAdapter(clusterStore.CRDMetadata),
193-
predicate: annotationChangedPredicate{annotation: graph.BundleVersionAnnotation},
196+
predicate: annotationChangedPredicate{annotation: consts.BundleVersionAnnotation},
194197
},
195198
{
196199
gvk: cfg.MustExtractGVK(&ngfAPIv1alpha2.NginxProxy{}),
@@ -275,6 +278,7 @@ func (c *ChangeProcessorImpl) Process() *graph.Graph {
275278
c.cfg.PlusSecrets,
276279
c.cfg.Validators,
277280
c.cfg.Logger,
281+
c.cfg.ExperimentalFeatures,
278282
)
279283

280284
return c.latestGraph

internal/controller/state/change_processor_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
v1 "sigs.k8s.io/gateway-api/apis/v1"
1919
"sigs.k8s.io/gateway-api/apis/v1alpha2"
2020
"sigs.k8s.io/gateway-api/apis/v1beta1"
21+
"sigs.k8s.io/gateway-api/pkg/consts"
2122

2223
ngfAPIv1alpha1 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1"
2324
ngfAPIv1alpha2 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha2"
@@ -666,13 +667,13 @@ var _ = Describe("ChangeProcessor", func() {
666667
ObjectMeta: metav1.ObjectMeta{
667668
Name: "gatewayclasses.gateway.networking.k8s.io",
668669
Annotations: map[string]string{
669-
graph.BundleVersionAnnotation: graph.SupportedVersion,
670+
consts.BundleVersionAnnotation: consts.BundleVersion,
670671
},
671672
},
672673
}
673674

674675
gatewayAPICRDUpdated = gatewayAPICRD.DeepCopy()
675-
gatewayAPICRDUpdated.Annotations[graph.BundleVersionAnnotation] = "v1.99.0"
676+
gatewayAPICRDUpdated.Annotations[consts.BundleVersionAnnotation] = "v1.99.0"
676677
})
677678
BeforeEach(func() {
678679
expRouteHR1 = &graph.L7Route{
@@ -1556,7 +1557,7 @@ var _ = Describe("ChangeProcessor", func() {
15561557
}
15571558

15581559
expGraph.GatewayClass.Conditions = conditions.NewGatewayClassSupportedVersionBestEffort(
1559-
graph.SupportedVersion,
1560+
consts.BundleVersion,
15601561
)
15611562

15621563
processAndValidateGraph(expGraph)
@@ -1574,7 +1575,7 @@ var _ = Describe("ChangeProcessor", func() {
15741575
}
15751576

15761577
expGraph.GatewayClass.Conditions = conditions.NewGatewayClassSupportedVersionBestEffort(
1577-
graph.SupportedVersion,
1578+
consts.BundleVersion,
15781579
)
15791580

15801581
graphCfg := processor.Process()

internal/controller/state/graph/gatewayclass.go

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,14 @@ import (
88
"k8s.io/apimachinery/pkg/util/validation/field"
99
"sigs.k8s.io/controller-runtime/pkg/client"
1010
v1 "sigs.k8s.io/gateway-api/apis/v1"
11+
"sigs.k8s.io/gateway-api/pkg/consts"
12+
"sigs.k8s.io/gateway-api/pkg/features"
1113

1214
"github.com/nginx/nginx-gateway-fabric/v2/internal/controller/state/conditions"
1315
"github.com/nginx/nginx-gateway-fabric/v2/internal/framework/helpers"
1416
"github.com/nginx/nginx-gateway-fabric/v2/internal/framework/kinds"
1517
)
1618

17-
const (
18-
// BundleVersionAnnotation is the annotation on Gateway API CRDs that contains the installed version.
19-
BundleVersionAnnotation = "gateway.networking.k8s.io/bundle-version"
20-
// SupportedVersion is the supported version of the Gateway API CRDs.
21-
SupportedVersion = "v1.4.0"
22-
)
23-
2419
var gatewayCRDs = map[string]apiVersion{
2520
"gatewayclasses.gateway.networking.k8s.io": {},
2621
"gateways.gateway.networking.k8s.io": {},
@@ -41,6 +36,8 @@ type GatewayClass struct {
4136
Conditions []conditions.Condition
4237
// Valid shows whether the GatewayClass is valid.
4338
Valid bool
39+
// ExperimentalSupported indicates whether experimental features are supported.
40+
ExperimentalSupported bool
4441
}
4542

4643
// processedGatewayClasses holds the resources that belong to NGF.
@@ -83,6 +80,7 @@ func buildGatewayClass(
8380
gc *v1.GatewayClass,
8481
nps map[types.NamespacedName]*NginxProxy,
8582
crdVersions map[types.NamespacedName]*metav1.PartialObjectMetadata,
83+
experimentalEnabled bool,
8684
) *GatewayClass {
8785
if gc == nil {
8886
return nil
@@ -93,13 +91,17 @@ func buildGatewayClass(
9391
np = getNginxProxyForGatewayClass(*gc.Spec.ParametersRef, nps)
9492
}
9593

96-
conds, valid := validateGatewayClass(gc, np, crdVersions)
94+
conds, valid, crdExperimental := validateGatewayClass(gc, np, crdVersions)
95+
96+
// Experimental features are supported only if both the config flag AND CRD channel are experimental
97+
experimental := experimentalEnabled && crdExperimental
9798

9899
return &GatewayClass{
99-
Source: gc,
100-
NginxProxy: np,
101-
Valid: valid,
102-
Conditions: conds,
100+
Source: gc,
101+
NginxProxy: np,
102+
Valid: valid,
103+
Conditions: conds,
104+
ExperimentalSupported: experimental,
103105
}
104106
}
105107

@@ -145,14 +147,14 @@ func validateGatewayClass(
145147
gc *v1.GatewayClass,
146148
npCfg *NginxProxy,
147149
crdVersions map[types.NamespacedName]*metav1.PartialObjectMetadata,
148-
) ([]conditions.Condition, bool) {
150+
) ([]conditions.Condition, bool, bool) {
149151
var conds []conditions.Condition
150152

151-
supportedVersionConds, versionsValid := validateCRDVersions(crdVersions)
153+
supportedVersionConds, versionsValid, experimental := validateCRDVersions(crdVersions)
152154
conds = append(conds, supportedVersionConds...)
153155

154156
if gc.Spec.ParametersRef == nil {
155-
return conds, versionsValid
157+
return conds, versionsValid, experimental
156158
}
157159

158160
path := field.NewPath("spec").Child("parametersRef")
@@ -161,7 +163,7 @@ func validateGatewayClass(
161163
// return early since parametersRef isn't valid
162164
if len(refConds) > 0 {
163165
conds = append(conds, refConds...)
164-
return conds, versionsValid
166+
return conds, versionsValid, experimental
165167
}
166168

167169
if npCfg == nil {
@@ -172,7 +174,7 @@ func validateGatewayClass(
172174
field.NotFound(path.Child("name"), gc.Spec.ParametersRef.Name).Error(),
173175
),
174176
)
175-
return conds, versionsValid
177+
return conds, versionsValid, experimental
176178
}
177179

178180
if !npCfg.Valid {
@@ -182,10 +184,10 @@ func validateGatewayClass(
182184
conditions.NewGatewayClassRefInvalid(msg),
183185
conditions.NewGatewayClassInvalidParameters(msg),
184186
)
185-
return conds, versionsValid
187+
return conds, versionsValid, experimental
186188
}
187189

188-
return append(conds, conditions.NewGatewayClassResolvedRefs()), versionsValid
190+
return append(conds, conditions.NewGatewayClassResolvedRefs()), versionsValid, experimental
189191
}
190192

191193
var supportedParamKinds = map[string]struct{}{
@@ -199,9 +201,9 @@ type apiVersion struct {
199201

200202
func validateCRDVersions(
201203
crdMetadata map[types.NamespacedName]*metav1.PartialObjectMetadata,
202-
) (conds []conditions.Condition, valid bool) {
203-
installedAPIVersions := getBundleVersions(crdMetadata)
204-
supportedAPIVersion := parseVersionString(SupportedVersion)
204+
) (conds []conditions.Condition, valid bool, experimental bool) {
205+
installedAPIVersions, channels := getBundleVersions(crdMetadata)
206+
supportedAPIVersion := parseVersionString(consts.BundleVersion)
205207

206208
var unsupported, bestEffort bool
207209

@@ -213,15 +215,23 @@ func validateCRDVersions(
213215
}
214216
}
215217

218+
// Check if any CRD is using experimental channel
219+
for _, ch := range channels {
220+
if ch == features.FeatureChannelExperimental {
221+
experimental = true
222+
break
223+
}
224+
}
225+
216226
if unsupported {
217-
return conditions.NewGatewayClassUnsupportedVersion(SupportedVersion), false
227+
return conditions.NewGatewayClassUnsupportedVersion(consts.BundleVersion), false, experimental
218228
}
219229

220230
if bestEffort {
221-
return conditions.NewGatewayClassSupportedVersionBestEffort(SupportedVersion), true
231+
return conditions.NewGatewayClassSupportedVersionBestEffort(consts.BundleVersion), true, experimental
222232
}
223233

224-
return nil, true
234+
return nil, true, experimental
225235
}
226236

227237
func parseVersionString(version string) apiVersion {
@@ -246,15 +256,25 @@ func parseVersionString(version string) apiVersion {
246256
}
247257
}
248258

249-
func getBundleVersions(crdMetadata map[types.NamespacedName]*metav1.PartialObjectMetadata) []apiVersion {
259+
func getBundleVersions(
260+
crdMetadata map[types.NamespacedName]*metav1.PartialObjectMetadata,
261+
) ([]apiVersion, []features.FeatureChannel) {
250262
versions := make([]apiVersion, 0, len(gatewayCRDs))
263+
channels := make([]features.FeatureChannel, 0, len(gatewayCRDs))
251264

252265
for nsname, md := range crdMetadata {
253266
if _, ok := gatewayCRDs[nsname.Name]; ok {
254-
bundleVersion := md.Annotations[BundleVersionAnnotation]
267+
bundleVersion := md.Annotations[consts.BundleVersionAnnotation]
255268
versions = append(versions, parseVersionString(bundleVersion))
269+
270+
// Default to standard channel if annotation is missing
271+
ch := md.Annotations[consts.ChannelAnnotation]
272+
if ch == "" {
273+
ch = string(features.FeatureChannelStandard)
274+
}
275+
channels = append(channels, features.FeatureChannel(ch))
256276
}
257277
}
258278

259-
return versions
279+
return versions, channels
260280
}

0 commit comments

Comments
 (0)