Skip to content

Commit 771394e

Browse files
committed
fix(ci): align gatewayclass features with runtime config
1 parent 5aa2d74 commit 771394e

4 files changed

Lines changed: 177 additions & 3 deletions

File tree

internal/gatewayapi/supported_features.go

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,21 @@ const SupportedTCPRoute gatewayfeatures.FeatureName = "TCPRoute"
1515
// supports session persistence configuration in BackendLBPolicy.
1616
const SupportedBackendLBSessionPersistence gatewayfeatures.FeatureName = "BackendLBSessionPersistence"
1717

18+
// FeatureOptions controls which supported features are advertised for a
19+
// particular control-plane runtime configuration.
20+
type FeatureOptions struct {
21+
EnableExperimentalGateway bool
22+
}
23+
24+
var experimentalGatewayFeatures = []gatewayfeatures.FeatureName{
25+
gatewayfeatures.SupportListenerSet,
26+
SupportedTCPRoute,
27+
gatewayfeatures.SupportUDPRoute,
28+
gatewayfeatures.SupportTLSRoute,
29+
gatewayfeatures.SupportTLSRouteModeTerminate,
30+
gatewayfeatures.SupportTLSRouteModeMixed,
31+
}
32+
1833
// SupportedFeatureNameSet returns the feature-name set this repository
1934
// advertises through the in-repo conformance profile.
2035
func SupportedFeatureNameSet() sets.Set[gatewayfeatures.FeatureName] {
@@ -31,7 +46,7 @@ func SupportedFeatureNameSet() sets.Set[gatewayfeatures.FeatureName] {
3146
gatewayfeatures.SupportGatewayFrontendClientCertificateValidationInsecureFallback,
3247
gatewayfeatures.SupportGatewayHTTPSListenerDetectMisdirectedRequests,
3348
gatewayfeatures.SupportGatewayHTTPListenerIsolation,
34-
gatewayfeatures.SupportGatewayInfrastructurePropagation,
49+
gatewayfeatures.SupportGatewayInfrastructurePropagation,
3550
gatewayfeatures.SupportGatewayPort8080,
3651
gatewayfeatures.SupportGatewayStaticAddresses,
3752
gatewayfeatures.SupportHTTPRoute,
@@ -78,10 +93,32 @@ func SupportedFeatureNameSet() sets.Set[gatewayfeatures.FeatureName] {
7893
)
7994
}
8095

96+
// SupportedFeatureNameSetForOptions returns the feature-name set advertised by
97+
// a control-plane instance with the provided runtime feature options.
98+
func SupportedFeatureNameSetForOptions(options FeatureOptions) sets.Set[gatewayfeatures.FeatureName] {
99+
out := SupportedFeatureNameSet()
100+
if !options.EnableExperimentalGateway {
101+
for _, name := range experimentalGatewayFeatures {
102+
out.Delete(name)
103+
}
104+
}
105+
return out
106+
}
107+
81108
// SupportedFeatureNames returns the feature-name set this repository currently
82109
// advertises through the in-repo conformance profile.
83110
func SupportedFeatureNames() []gatewayfeatures.FeatureName {
84-
out := SupportedFeatureNameSet().UnsortedList()
111+
return sortedFeatureNamesFromSet(SupportedFeatureNameSet())
112+
}
113+
114+
// SupportedFeatureNamesForOptions returns the sorted feature names advertised by
115+
// a control-plane instance with the provided runtime feature options.
116+
func SupportedFeatureNamesForOptions(options FeatureOptions) []gatewayfeatures.FeatureName {
117+
return sortedFeatureNamesFromSet(SupportedFeatureNameSetForOptions(options))
118+
}
119+
120+
func sortedFeatureNamesFromSet(items sets.Set[gatewayfeatures.FeatureName]) []gatewayfeatures.FeatureName {
121+
out := items.UnsortedList()
85122
sort.Slice(out, func(i, j int) bool {
86123
return out[i] < out[j]
87124
})
@@ -92,6 +129,17 @@ func SupportedFeatureNames() []gatewayfeatures.FeatureName {
92129
// GatewayClass status shape required by the Gateway API.
93130
func SupportedFeatures() []gatewayv1.SupportedFeature {
94131
names := SupportedFeatureNames()
132+
return supportedFeaturesFromNames(names)
133+
}
134+
135+
// SupportedFeaturesForOptions converts the runtime feature set into the
136+
// GatewayClass status shape required by the Gateway API.
137+
func SupportedFeaturesForOptions(options FeatureOptions) []gatewayv1.SupportedFeature {
138+
names := SupportedFeatureNamesForOptions(options)
139+
return supportedFeaturesFromNames(names)
140+
}
141+
142+
func supportedFeaturesFromNames(names []gatewayfeatures.FeatureName) []gatewayv1.SupportedFeature {
95143
out := make([]gatewayv1.SupportedFeature, 0, len(names))
96144
for _, name := range names {
97145
out = append(out, gatewayv1.SupportedFeature{

internal/gatewayapi/supported_features_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,58 @@ func TestSupportedFeatureNamesAreSortedAndComplete(t *testing.T) {
8080
}
8181
}
8282

83+
func TestSupportedFeatureNamesForOptionsExcludesExperimentalGatewayFeaturesWhenDisabled(t *testing.T) {
84+
got := SupportedFeatureNamesForOptions(FeatureOptions{EnableExperimentalGateway: false})
85+
names := featureNameSet(got)
86+
87+
for _, name := range []gatewayfeatures.FeatureName{
88+
gatewayfeatures.SupportListenerSet,
89+
SupportedTCPRoute,
90+
gatewayfeatures.SupportUDPRoute,
91+
gatewayfeatures.SupportTLSRoute,
92+
gatewayfeatures.SupportTLSRouteModeTerminate,
93+
gatewayfeatures.SupportTLSRouteModeMixed,
94+
} {
95+
if names[name] {
96+
t.Fatalf("feature %s should not be advertised when experimental Gateway support is disabled: %#v", name, got)
97+
}
98+
}
99+
100+
for _, name := range []gatewayfeatures.FeatureName{
101+
gatewayfeatures.SupportGateway,
102+
gatewayfeatures.SupportHTTPRoute,
103+
gatewayfeatures.SupportGRPCRoute,
104+
gatewayfeatures.SupportReferenceGrant,
105+
} {
106+
if !names[name] {
107+
t.Fatalf("feature %s should remain advertised when experimental Gateway support is disabled: %#v", name, got)
108+
}
109+
}
110+
}
111+
112+
func TestSupportedFeatureNamesForOptionsIncludesExperimentalGatewayFeaturesWhenEnabled(t *testing.T) {
113+
got := SupportedFeatureNamesForOptions(FeatureOptions{EnableExperimentalGateway: true})
114+
names := featureNameSet(got)
115+
116+
for _, name := range []gatewayfeatures.FeatureName{
117+
gatewayfeatures.SupportListenerSet,
118+
SupportedTCPRoute,
119+
gatewayfeatures.SupportUDPRoute,
120+
gatewayfeatures.SupportTLSRoute,
121+
gatewayfeatures.SupportTLSRouteModeTerminate,
122+
gatewayfeatures.SupportTLSRouteModeMixed,
123+
} {
124+
if !names[name] {
125+
t.Fatalf("feature %s should be advertised when experimental Gateway support is enabled: %#v", name, got)
126+
}
127+
}
128+
129+
want := SupportedFeatureNames()
130+
if !reflect.DeepEqual(got, want) {
131+
t.Fatalf("enabled runtime feature names = %#v, want complete supported set %#v", got, want)
132+
}
133+
}
134+
83135
func TestSupportedFeaturesExposeSortedGatewayClassStatusShape(t *testing.T) {
84136
got := SupportedFeatures()
85137
names := make([]gatewayfeatures.FeatureName, 0, len(got))
@@ -115,3 +167,11 @@ func sortedFeatureNames(items []gatewayfeatures.FeatureName) []gatewayfeatures.F
115167
})
116168
return out
117169
}
170+
171+
func featureNameSet(items []gatewayfeatures.FeatureName) map[gatewayfeatures.FeatureName]bool {
172+
out := make(map[gatewayfeatures.FeatureName]bool, len(items))
173+
for _, item := range items {
174+
out[item] = true
175+
}
176+
return out
177+
}

internal/status/gatewayclass_status.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ func (r *gatewayClassStatusSupportResolver) resolve(
6565
r.err = err
6666
} else {
6767
r.crds = crds.Items
68-
r.features = gatewayapi.SupportedFeatures()
68+
r.features = gatewayapi.SupportedFeaturesForOptions(gatewayapi.FeatureOptions{
69+
EnableExperimentalGateway: r.reconciler.experimentalGatewayEnabled(),
70+
})
6971
}
7072
}
7173
if r.err != nil {

internal/status/reconciler_gatewayclass_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"sigs.k8s.io/controller-runtime/pkg/client"
1212
"sigs.k8s.io/controller-runtime/pkg/client/fake"
1313
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
14+
gatewayfeatures "sigs.k8s.io/gateway-api/pkg/features"
1415

1516
"github.com/nantian-gw/gateway/internal/gatewayapi"
1617
)
@@ -98,6 +99,61 @@ func TestReconcileGatewayClassObjectPublishesSupportedVersionAndFeatures(t *test
9899
}
99100
}
100101

102+
func TestReconcileGatewayClassObjectFiltersExperimentalGatewayFeaturesWhenDisabled(t *testing.T) {
103+
scheme := newScheme(t)
104+
controllerName := gatewayv1.GatewayController("gateway.networking.k8s.io/nantian-gw")
105+
106+
k8sClient := fake.NewClientBuilder().
107+
WithScheme(scheme).
108+
WithStatusSubresource(&gatewayv1.GatewayClass{}).
109+
WithObjects(
110+
&gatewayv1.GatewayClass{
111+
ObjectMeta: metav1.ObjectMeta{Name: "nantian-gw", Generation: 3},
112+
Spec: gatewayv1.GatewayClassSpec{
113+
ControllerName: controllerName,
114+
},
115+
},
116+
gatewayAPICRD("gatewayclasses.gateway.networking.k8s.io", "v1.5.1"),
117+
).
118+
Build()
119+
120+
reconciler := NewWithAddressesAndReaderOptions(
121+
k8sClient,
122+
k8sClient,
123+
string(controllerName),
124+
[]string{"127.0.0.1"},
125+
discardLogger(),
126+
Options{EnableExperimentalGateway: false},
127+
)
128+
if err := reconciler.ReconcileGatewayClassObject(context.Background(), "nantian-gw"); err != nil {
129+
t.Fatalf("ReconcileGatewayClassObject returned error: %v", err)
130+
}
131+
132+
var gatewayClass gatewayv1.GatewayClass
133+
if err := k8sClient.Get(context.Background(), client.ObjectKey{Name: "nantian-gw"}, &gatewayClass); err != nil {
134+
t.Fatalf("Get GatewayClass returned error: %v", err)
135+
}
136+
137+
want := gatewayapi.SupportedFeaturesForOptions(gatewayapi.FeatureOptions{EnableExperimentalGateway: false})
138+
if !reflect.DeepEqual(gatewayClass.Status.SupportedFeatures, want) {
139+
t.Fatalf("supported features with experimental Gateway disabled = %#v, want %#v", gatewayClass.Status.SupportedFeatures, want)
140+
}
141+
142+
names := supportedFeatureStatusNameSet(gatewayClass.Status.SupportedFeatures)
143+
for _, name := range []gatewayv1.FeatureName{
144+
gatewayv1.FeatureName(gatewayapi.SupportedTCPRoute),
145+
gatewayv1.FeatureName(gatewayfeatures.SupportListenerSet),
146+
gatewayv1.FeatureName(gatewayfeatures.SupportUDPRoute),
147+
gatewayv1.FeatureName(gatewayfeatures.SupportTLSRoute),
148+
gatewayv1.FeatureName(gatewayfeatures.SupportTLSRouteModeTerminate),
149+
gatewayv1.FeatureName(gatewayfeatures.SupportTLSRouteModeMixed),
150+
} {
151+
if names[name] {
152+
t.Fatalf("feature %s should not be advertised when experimental Gateway support is disabled: %#v", name, gatewayClass.Status.SupportedFeatures)
153+
}
154+
}
155+
}
156+
101157
func TestReconcileGatewayClassObjectRejectsMissingBundleVersionAnnotations(t *testing.T) {
102158
scheme := newScheme(t)
103159
controllerName := gatewayv1.GatewayController("gateway.networking.k8s.io/nantian-gw")
@@ -380,3 +436,11 @@ func TestReconcileLoadsGatewayAPICRDsOncePerFullReconcile(t *testing.T) {
380436
t.Fatalf("gateway API CRD list count = %d, want 1", crdLists)
381437
}
382438
}
439+
440+
func supportedFeatureStatusNameSet(items []gatewayv1.SupportedFeature) map[gatewayv1.FeatureName]bool {
441+
out := make(map[gatewayv1.FeatureName]bool, len(items))
442+
for _, item := range items {
443+
out[item.Name] = true
444+
}
445+
return out
446+
}

0 commit comments

Comments
 (0)