@@ -8,18 +8,13 @@ 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/kinds"
1416)
1517
16- const (
17- // BundleVersionAnnotation is the annotation on Gateway API CRDs that contains the installed version.
18- BundleVersionAnnotation = "gateway.networking.k8s.io/bundle-version"
19- // SupportedVersion is the supported version of the Gateway API CRDs.
20- SupportedVersion = "v1.4.0"
21- )
22-
2318var gatewayCRDs = map [string ]apiVersion {
2419 "gatewayclasses.gateway.networking.k8s.io" : {},
2520 "gateways.gateway.networking.k8s.io" : {},
@@ -40,6 +35,8 @@ type GatewayClass struct {
4035 Conditions []conditions.Condition
4136 // Valid shows whether the GatewayClass is valid.
4237 Valid bool
38+ // ExperimentalSupported indicates whether experimental features are supported.
39+ ExperimentalSupported bool
4340}
4441
4542// processedGatewayClasses holds the resources that belong to NGF.
@@ -82,6 +79,7 @@ func buildGatewayClass(
8279 gc * v1.GatewayClass ,
8380 nps map [types.NamespacedName ]* NginxProxy ,
8481 crdVersions map [types.NamespacedName ]* metav1.PartialObjectMetadata ,
82+ experimentalEnabled bool ,
8583) * GatewayClass {
8684 if gc == nil {
8785 return nil
@@ -92,13 +90,17 @@ func buildGatewayClass(
9290 np = getNginxProxyForGatewayClass (* gc .Spec .ParametersRef , nps )
9391 }
9492
95- conds , valid := validateGatewayClass (gc , np , crdVersions )
93+ conds , valid , crdExperimental := validateGatewayClass (gc , np , crdVersions )
94+
95+ // Experimental features are supported only if both the config flag AND CRD channel are experimental
96+ experimental := experimentalEnabled && crdExperimental
9697
9798 return & GatewayClass {
98- Source : gc ,
99- NginxProxy : np ,
100- Valid : valid ,
101- Conditions : conds ,
99+ Source : gc ,
100+ NginxProxy : np ,
101+ Valid : valid ,
102+ Conditions : conds ,
103+ ExperimentalSupported : experimental ,
102104 }
103105}
104106
@@ -144,14 +146,14 @@ func validateGatewayClass(
144146 gc * v1.GatewayClass ,
145147 npCfg * NginxProxy ,
146148 crdVersions map [types.NamespacedName ]* metav1.PartialObjectMetadata ,
147- ) ([]conditions.Condition , bool ) {
149+ ) ([]conditions.Condition , bool , bool ) {
148150 var conds []conditions.Condition
149151
150- supportedVersionConds , versionsValid := validateCRDVersions (crdVersions )
152+ supportedVersionConds , versionsValid , experimental := validateCRDVersions (crdVersions )
151153 conds = append (conds , supportedVersionConds ... )
152154
153155 if gc .Spec .ParametersRef == nil {
154- return conds , versionsValid
156+ return conds , versionsValid , experimental
155157 }
156158
157159 path := field .NewPath ("spec" ).Child ("parametersRef" )
@@ -160,7 +162,7 @@ func validateGatewayClass(
160162 // return early since parametersRef isn't valid
161163 if len (refConds ) > 0 {
162164 conds = append (conds , refConds ... )
163- return conds , versionsValid
165+ return conds , versionsValid , experimental
164166 }
165167
166168 if npCfg == nil {
@@ -171,7 +173,7 @@ func validateGatewayClass(
171173 field .NotFound (path .Child ("name" ), gc .Spec .ParametersRef .Name ).Error (),
172174 ),
173175 )
174- return conds , versionsValid
176+ return conds , versionsValid , experimental
175177 }
176178
177179 if ! npCfg .Valid {
@@ -181,10 +183,10 @@ func validateGatewayClass(
181183 conditions .NewGatewayClassRefInvalid (msg ),
182184 conditions .NewGatewayClassInvalidParameters (msg ),
183185 )
184- return conds , versionsValid
186+ return conds , versionsValid , experimental
185187 }
186188
187- return append (conds , conditions .NewGatewayClassResolvedRefs ()), versionsValid
189+ return append (conds , conditions .NewGatewayClassResolvedRefs ()), versionsValid , experimental
188190}
189191
190192var supportedParamKinds = map [string ]struct {}{
@@ -198,9 +200,9 @@ type apiVersion struct {
198200
199201func validateCRDVersions (
200202 crdMetadata map [types.NamespacedName ]* metav1.PartialObjectMetadata ,
201- ) (conds []conditions.Condition , valid bool ) {
202- installedAPIVersions := getBundleVersions (crdMetadata )
203- supportedAPIVersion := parseVersionString (SupportedVersion )
203+ ) (conds []conditions.Condition , valid bool , experimental bool ) {
204+ installedAPIVersions , channels := getBundleVersions (crdMetadata )
205+ supportedAPIVersion := parseVersionString (consts . BundleVersion )
204206
205207 var unsupported , bestEffort bool
206208
@@ -212,15 +214,23 @@ func validateCRDVersions(
212214 }
213215 }
214216
217+ // Check if any CRD is using experimental channel
218+ for _ , ch := range channels {
219+ if ch == features .FeatureChannelExperimental {
220+ experimental = true
221+ break
222+ }
223+ }
224+
215225 if unsupported {
216- return conditions .NewGatewayClassUnsupportedVersion (SupportedVersion ), false
226+ return conditions .NewGatewayClassUnsupportedVersion (consts . BundleVersion ), false , experimental
217227 }
218228
219229 if bestEffort {
220- return conditions .NewGatewayClassSupportedVersionBestEffort (SupportedVersion ), true
230+ return conditions .NewGatewayClassSupportedVersionBestEffort (consts . BundleVersion ), true , experimental
221231 }
222232
223- return nil , true
233+ return nil , true , experimental
224234}
225235
226236func parseVersionString (version string ) apiVersion {
@@ -245,15 +255,25 @@ func parseVersionString(version string) apiVersion {
245255 }
246256}
247257
248- func getBundleVersions (crdMetadata map [types.NamespacedName ]* metav1.PartialObjectMetadata ) []apiVersion {
258+ func getBundleVersions (
259+ crdMetadata map [types.NamespacedName ]* metav1.PartialObjectMetadata ,
260+ ) ([]apiVersion , []features.FeatureChannel ) {
249261 versions := make ([]apiVersion , 0 , len (gatewayCRDs ))
262+ channels := make ([]features.FeatureChannel , 0 , len (gatewayCRDs ))
250263
251264 for nsname , md := range crdMetadata {
252265 if _ , ok := gatewayCRDs [nsname .Name ]; ok {
253- bundleVersion := md .Annotations [BundleVersionAnnotation ]
266+ bundleVersion := md .Annotations [consts . BundleVersionAnnotation ]
254267 versions = append (versions , parseVersionString (bundleVersion ))
268+
269+ // Default to standard channel if annotation is missing
270+ ch := md .Annotations [consts .ChannelAnnotation ]
271+ if ch == "" {
272+ ch = string (features .FeatureChannelStandard )
273+ }
274+ channels = append (channels , features .FeatureChannel (ch ))
255275 }
256276 }
257277
258- return versions
278+ return versions , channels
259279}
0 commit comments