Skip to content

Commit df8c96c

Browse files
authored
Added flag for running mesh conformance suite and automatically inferring supported features from Mesh.Status (#4097)
* Read SupportedFeatures from XMesh status for conformance test. * Defined Mesh flag for testing. * Renamed mesh flag. * renamed fetched mesh features set. * suite_test formatting. * Set mesh flag empty default value
1 parent 6b6e608 commit df8c96c

File tree

4 files changed

+167
-16
lines changed

4 files changed

+167
-16
lines changed

conformance/conformance.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func DefaultOptions(t *testing.T) suite.ConformanceOptions {
9393
ExemptFeatures: exemptFeatures,
9494
ManifestFS: []fs.FS{&Manifests},
9595
GatewayClassName: *flags.GatewayClassName,
96+
MeshName: *flags.MeshName,
9697
Implementation: implementation,
9798
Mode: *flags.Mode,
9899
NamespaceAnnotations: namespaceAnnotations,

conformance/utils/flags/flags.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030

3131
var (
3232
GatewayClassName = flag.String("gateway-class", "gateway-conformance", "Name of GatewayClass to use for tests")
33+
MeshName = flag.String("mesh-name", "", "Name of Mesh to use for tests")
3334
ShowDebug = flag.Bool("debug", false, "Whether to print debug logs")
3435
CleanupBaseResources = flag.Bool("cleanup-base-resources", true, "Whether to cleanup base test resources after the run")
3536
SupportedFeatures = flag.String("supported-features", "", "Supported features included in conformance tests suites")

conformance/utils/suite/suite.go

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939

4040
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
4141
"sigs.k8s.io/gateway-api/apis/v1beta1"
42+
xmeshv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1"
4243
confv1 "sigs.k8s.io/gateway-api/conformance/apis/v1"
4344
"sigs.k8s.io/gateway-api/conformance/utils/config"
4445
"sigs.k8s.io/gateway-api/conformance/utils/flags"
@@ -65,6 +66,7 @@ type ConformanceTestSuite struct {
6566
RoundTripper roundtripper.RoundTripper
6667
GRPCClient grpc.Client
6768
GatewayClassName string
69+
MeshName string
6870
ControllerName string
6971
Debug bool
7072
Cleanup bool
@@ -136,6 +138,7 @@ type ConformanceOptions struct {
136138
Clientset clientset.Interface
137139
RestConfig *rest.Config
138140
GatewayClassName string
141+
MeshName string
139142
AddressType string
140143
Debug bool
141144
RoundTripper roundtripper.RoundTripper
@@ -206,20 +209,23 @@ func NewConformanceTestSuite(options ConformanceOptions) (*ConformanceTestSuite,
206209
supportedFeatures = features.SetsToNamesSet(features.AllFeatures)
207210
} else if shouldInferSupportedFeatures(&options) {
208211
var err error
209-
supportedFeatures, err = fetchSupportedFeatures(options.Client, options.GatewayClassName)
210-
if err != nil {
211-
return nil, fmt.Errorf("cannot infer supported features: %w", err)
212+
if options.GatewayClassName != "" {
213+
supportedFeatures, err = fetchGatewayClassSupportedFeatures(options.Client, options.GatewayClassName)
214+
if err != nil {
215+
return nil, fmt.Errorf("cannot infer supported features from GWC: %w", err)
216+
}
212217
}
213218

214-
// If Mesh features are populated in the GatewayClass we remove them from the supported features set.
215-
meshFeatureNames := features.SetsToNamesSet(features.MeshCoreFeatures, features.MeshExtendedFeatures)
216-
for _, f := range supportedFeatures.UnsortedList() {
217-
if meshFeatureNames.Has(f) {
218-
supportedFeatures.Delete(f)
219-
fmt.Printf("WARNING: Mesh feature %q should not be populated in GatewayClass, skipping...", f)
219+
supportedMeshFeatures := FeaturesSet{}
220+
if options.MeshName != "" {
221+
supportedMeshFeatures, err = fetchMeshSupportedFeatures(options.Client, options.MeshName)
222+
if err != nil {
223+
return nil, fmt.Errorf("cannot infer supported features from XMesh: %w", err)
220224
}
221225
}
226+
222227
source = supportedFeaturesSourceInferred
228+
supportedFeatures = supportedFeatures.Union(supportedMeshFeatures)
223229
}
224230

225231
// If features were not inferred from Status, it's a GWC issue.
@@ -606,24 +612,59 @@ func ParseConformanceProfiles(p string) sets.Set[ConformanceProfileName] {
606612
return res
607613
}
608614

609-
func fetchSupportedFeatures(client client.Client, gatewayClassName string) (FeaturesSet, error) {
615+
func fetchGatewayClassSupportedFeatures(client client.Client, gatewayClassName string) (FeaturesSet, error) {
610616
if gatewayClassName == "" {
611617
return nil, fmt.Errorf("GatewayClass name must be provided to fetch supported features")
612618
}
613619
gwc := &gatewayv1.GatewayClass{}
614620
err := client.Get(context.TODO(), types.NamespacedName{Name: gatewayClassName}, gwc)
615621
if err != nil {
616-
return nil, fmt.Errorf("fetchSupportedFeatures(): %w", err)
622+
return nil, fmt.Errorf("fetchGatewayClassSupportedFeatures(): %w", err)
617623
}
618624

619625
fs := FeaturesSet{}
620626
for _, feature := range gwc.Status.SupportedFeatures {
621627
fs.Insert(features.FeatureName(feature.Name))
622628
}
629+
630+
// If Mesh features are populated in the GatewayClass we remove them from the supported features set.
631+
meshFeatureNames := features.SetsToNamesSet(features.MeshCoreFeatures, features.MeshExtendedFeatures)
632+
for _, f := range fs.UnsortedList() {
633+
if meshFeatureNames.Has(f) {
634+
fs.Delete(f)
635+
fmt.Printf("WARNING: Mesh feature %q should not be populated in GatewayClass, skipping...", f)
636+
}
637+
}
623638
fmt.Printf("Supported features for GatewayClass %s: %v\n", gatewayClassName, fs.UnsortedList())
624639
return fs, nil
625640
}
626641

642+
func fetchMeshSupportedFeatures(client client.Client, meshName string) (FeaturesSet, error) {
643+
if meshName == "" {
644+
return nil, fmt.Errorf("mesh name must be provided to fetch supported features")
645+
}
646+
xmesh := &xmeshv1alpha1.XMesh{}
647+
err := client.Get(context.TODO(), types.NamespacedName{Name: meshName}, xmesh)
648+
if err != nil {
649+
return nil, fmt.Errorf("fetchMeshSupportedFeatures(): %w", err)
650+
}
651+
652+
fs := FeaturesSet{}
653+
for _, feature := range xmesh.Status.SupportedFeatures {
654+
fs.Insert(features.FeatureName(feature.Name))
655+
}
656+
657+
gwcFeatureNames := features.SetsToNamesSet(features.GatewayCoreFeatures, features.GatewayExtendedFeatures)
658+
for _, f := range fs.UnsortedList() {
659+
if gwcFeatureNames.Has(f) {
660+
fs.Delete(f)
661+
fmt.Printf("WARNING: Mesh feature %q should not be populated in XMesh.Status, skipping...", f)
662+
}
663+
}
664+
fmt.Printf("Supported features for XMesh%s: %v\n", meshName, fs.UnsortedList())
665+
return fs, nil
666+
}
667+
627668
// shouldInferSupportedFeatures checks if any flags were supplied for manually
628669
// picking what to test. Inferred supported features are only used when no flags
629670
// are set.

conformance/utils/suite/suite_test.go

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"sigs.k8s.io/controller-runtime/pkg/client/fake"
2929

3030
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
31+
xmeshv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1"
3132
confv1 "sigs.k8s.io/gateway-api/conformance/apis/v1"
3233
"sigs.k8s.io/gateway-api/pkg/consts"
3334
"sigs.k8s.io/gateway-api/pkg/features"
@@ -411,7 +412,7 @@ func TestSuiteReport(t *testing.T) {
411412
}
412413
}
413414

414-
var statusFeatureNames = []string{
415+
var gwcStatusFeatureNames = []string{
415416
"Gateway",
416417
"GatewayPort8080",
417418
"HTTPRoute",
@@ -423,7 +424,7 @@ var statusFeatureNames = []string{
423424
"ReferenceGrant",
424425
}
425426

426-
func TestInferSupportedFeatures(t *testing.T) {
427+
func TestInferGWCSupportedFeatures(t *testing.T) {
427428
testCases := []struct {
428429
name string
429430
allowAllFeatures bool
@@ -435,7 +436,7 @@ func TestInferSupportedFeatures(t *testing.T) {
435436
}{
436437
{
437438
name: "properly infer supported features",
438-
expectedFeatures: namesToFeatureSet(statusFeatureNames),
439+
expectedFeatures: namesToFeatureSet(gwcStatusFeatureNames),
439440
expectedSource: supportedFeaturesSourceInferred,
440441
},
441442
{
@@ -460,7 +461,7 @@ func TestInferSupportedFeatures(t *testing.T) {
460461
{
461462
name: "supports conformance profile - core",
462463
ConformanceProfile: sets.New(GatewayHTTPConformanceProfileName),
463-
expectedFeatures: namesToFeatureSet(statusFeatureNames),
464+
expectedFeatures: namesToFeatureSet(gwcStatusFeatureNames),
464465
expectedSource: supportedFeaturesSourceInferred,
465466
},
466467
}
@@ -482,7 +483,7 @@ func TestInferSupportedFeatures(t *testing.T) {
482483
Message: "GatewayClass is accepted and ready for use",
483484
},
484485
},
485-
SupportedFeatures: featureNamesToSet(statusFeatureNames),
486+
SupportedFeatures: featureNamesToSet(gwcStatusFeatureNames),
486487
},
487488
}
488489
scheme := runtime.NewScheme()
@@ -524,6 +525,113 @@ func TestInferSupportedFeatures(t *testing.T) {
524525
}
525526
}
526527

528+
var meshStatusFeatureNames = []string{
529+
"Mesh",
530+
"MeshClusterIPMatching",
531+
"MeshNamespaceSelector",
532+
"MeshServiceAccountSelector",
533+
"MeshTLS",
534+
"MeshTLSClientCert",
535+
"MeshTrafficSplit",
536+
"MeshAccessControl",
537+
"HTTPRoute",
538+
}
539+
540+
func TestXMeshInferSupportedFeatures(t *testing.T) {
541+
testCases := []struct {
542+
name string
543+
allowAllFeatures bool
544+
supportedFeatures FeaturesSet
545+
exemptFeatures FeaturesSet
546+
ConformanceProfile sets.Set[ConformanceProfileName]
547+
expectedFeatures FeaturesSet
548+
expectedSource supportedFeaturesSource
549+
}{
550+
{
551+
name: "properly infer mesh supported features",
552+
expectedFeatures: namesToFeatureSet(meshStatusFeatureNames),
553+
expectedSource: supportedFeaturesSourceInferred,
554+
},
555+
{
556+
name: "no features",
557+
supportedFeatures: sets.New[features.FeatureName]("Mesh"),
558+
expectedFeatures: sets.New[features.FeatureName]("Mesh"),
559+
expectedSource: supportedFeaturesSourceManual,
560+
},
561+
{
562+
name: "remove exempt features",
563+
supportedFeatures: sets.New[features.FeatureName]("MeshTLS", "MeshAccessControl"),
564+
exemptFeatures: sets.New[features.FeatureName]("MeshAccessControl"),
565+
expectedFeatures: sets.New[features.FeatureName]("MeshTLS"),
566+
expectedSource: supportedFeaturesSourceManual,
567+
},
568+
{
569+
name: "supports conformance profile - core",
570+
ConformanceProfile: sets.New(MeshHTTPConformanceProfileName),
571+
expectedFeatures: namesToFeatureSet(meshStatusFeatureNames),
572+
expectedSource: supportedFeaturesSourceInferred,
573+
},
574+
}
575+
576+
meshName := "xochopintre"
577+
xmesh := &xmeshv1alpha1.XMesh{
578+
ObjectMeta: metav1.ObjectMeta{
579+
Name: meshName,
580+
},
581+
Spec: xmeshv1alpha1.MeshSpec{
582+
ControllerName: "example.com/mesh-controller",
583+
},
584+
Status: xmeshv1alpha1.MeshStatus{
585+
Conditions: []metav1.Condition{
586+
{
587+
Type: string(xmeshv1alpha1.MeshConditionAccepted),
588+
Status: metav1.ConditionTrue,
589+
Reason: "Accepted",
590+
Message: "XMesh is accepted and ready for use",
591+
},
592+
},
593+
SupportedFeatures: featureNamesToSet(meshStatusFeatureNames),
594+
},
595+
}
596+
scheme := runtime.NewScheme()
597+
scheme.AddKnownTypes(xmeshv1alpha1.SchemeGroupVersion, &xmeshv1alpha1.XMesh{})
598+
fakeClient := fake.NewClientBuilder().
599+
WithScheme(scheme).
600+
WithObjects(xmesh).
601+
WithLists(&apiextensionsv1.CustomResourceDefinitionList{}).
602+
Build()
603+
604+
xmeshv1alpha1.Install(fakeClient.Scheme())
605+
apiextensionsv1.AddToScheme(fakeClient.Scheme())
606+
607+
for _, tc := range testCases {
608+
options := ConformanceOptions{
609+
AllowCRDsMismatch: true,
610+
MeshName: meshName,
611+
EnableAllSupportedFeatures: tc.allowAllFeatures,
612+
SupportedFeatures: tc.supportedFeatures,
613+
ExemptFeatures: tc.exemptFeatures,
614+
ConformanceProfiles: tc.ConformanceProfile,
615+
Client: fakeClient,
616+
}
617+
618+
t.Run(tc.name, func(t *testing.T) {
619+
cSuite, err := NewConformanceTestSuite(options)
620+
if err != nil {
621+
t.Fatalf("error initializing conformance suite: %v", err)
622+
}
623+
624+
if cSuite.supportedFeaturesSource != tc.expectedSource {
625+
t.Errorf("InferredSupportedFeatures mismatch: got %v, want %v", cSuite.supportedFeaturesSource, tc.expectedSource)
626+
}
627+
628+
if equal := cSuite.SupportedFeatures.Equal(tc.expectedFeatures); !equal {
629+
t.Errorf("SupportedFeatures mismatch: got %v, want %v", cSuite.SupportedFeatures.UnsortedList(), tc.expectedFeatures.UnsortedList())
630+
}
631+
})
632+
}
633+
}
634+
527635
func TestGWCPublishedMeshFeatures(t *testing.T) {
528636
gwcName := "ochopintre"
529637
gwc := &gatewayv1.GatewayClass{

0 commit comments

Comments
 (0)