@@ -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-
2419var 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
191193var supportedParamKinds = map [string ]struct {}{
@@ -199,9 +201,9 @@ type apiVersion struct {
199201
200202func 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
227237func 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