Skip to content

Commit e6d8b77

Browse files
[release-1.4] Added flag for running mesh conformance suite and automatically inferring supported features from Mesh.Status (#4124)
* 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 --------- Co-authored-by: Beka Modebadze <[email protected]>
1 parent e8a5a5f commit e6d8b77

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.
@@ -591,24 +597,59 @@ func ParseConformanceProfiles(p string) sets.Set[ConformanceProfileName] {
591597
return res
592598
}
593599

594-
func fetchSupportedFeatures(client client.Client, gatewayClassName string) (FeaturesSet, error) {
600+
func fetchGatewayClassSupportedFeatures(client client.Client, gatewayClassName string) (FeaturesSet, error) {
595601
if gatewayClassName == "" {
596602
return nil, fmt.Errorf("GatewayClass name must be provided to fetch supported features")
597603
}
598604
gwc := &gatewayv1.GatewayClass{}
599605
err := client.Get(context.TODO(), types.NamespacedName{Name: gatewayClassName}, gwc)
600606
if err != nil {
601-
return nil, fmt.Errorf("fetchSupportedFeatures(): %w", err)
607+
return nil, fmt.Errorf("fetchGatewayClassSupportedFeatures(): %w", err)
602608
}
603609

604610
fs := FeaturesSet{}
605611
for _, feature := range gwc.Status.SupportedFeatures {
606612
fs.Insert(features.FeatureName(feature.Name))
607613
}
614+
615+
// If Mesh features are populated in the GatewayClass we remove them from the supported features set.
616+
meshFeatureNames := features.SetsToNamesSet(features.MeshCoreFeatures, features.MeshExtendedFeatures)
617+
for _, f := range fs.UnsortedList() {
618+
if meshFeatureNames.Has(f) {
619+
fs.Delete(f)
620+
fmt.Printf("WARNING: Mesh feature %q should not be populated in GatewayClass, skipping...", f)
621+
}
622+
}
608623
fmt.Printf("Supported features for GatewayClass %s: %v\n", gatewayClassName, fs.UnsortedList())
609624
return fs, nil
610625
}
611626

627+
func fetchMeshSupportedFeatures(client client.Client, meshName string) (FeaturesSet, error) {
628+
if meshName == "" {
629+
return nil, fmt.Errorf("mesh name must be provided to fetch supported features")
630+
}
631+
xmesh := &xmeshv1alpha1.XMesh{}
632+
err := client.Get(context.TODO(), types.NamespacedName{Name: meshName}, xmesh)
633+
if err != nil {
634+
return nil, fmt.Errorf("fetchMeshSupportedFeatures(): %w", err)
635+
}
636+
637+
fs := FeaturesSet{}
638+
for _, feature := range xmesh.Status.SupportedFeatures {
639+
fs.Insert(features.FeatureName(feature.Name))
640+
}
641+
642+
gwcFeatureNames := features.SetsToNamesSet(features.GatewayCoreFeatures, features.GatewayExtendedFeatures)
643+
for _, f := range fs.UnsortedList() {
644+
if gwcFeatureNames.Has(f) {
645+
fs.Delete(f)
646+
fmt.Printf("WARNING: Mesh feature %q should not be populated in XMesh.Status, skipping...", f)
647+
}
648+
}
649+
fmt.Printf("Supported features for XMesh%s: %v\n", meshName, fs.UnsortedList())
650+
return fs, nil
651+
}
652+
612653
// shouldInferSupportedFeatures checks if any flags were supplied for manually
613654
// picking what to test. Inferred supported features are only used when no flags
614655
// 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
@@ -436,7 +437,7 @@ func TestInferSupportedFeatures(t *testing.T) {
436437
}{
437438
{
438439
name: "properly infer supported features",
439-
expectedFeatures: namesToFeatureSet(statusFeatureNames),
440+
expectedFeatures: namesToFeatureSet(gwcStatusFeatureNames),
440441
expectedSource: supportedFeaturesSourceInferred,
441442
},
442443
{
@@ -473,7 +474,7 @@ func TestInferSupportedFeatures(t *testing.T) {
473474
{
474475
name: "supports conformance profile core with inferred extended features",
475476
ConformanceProfile: sets.New(GatewayHTTPConformanceProfileName),
476-
expectedFeatures: namesToFeatureSet(statusFeatureNames),
477+
expectedFeatures: namesToFeatureSet(gwcStatusFeatureNames),
477478
expectedSource: supportedFeaturesSourceInferred,
478479
expectedExtendedFeatures: map[ConformanceProfileName]sets.Set[features.FeatureName]{
479480
GatewayHTTPConformanceProfileName: namesToFeatureSet([]string{
@@ -505,7 +506,7 @@ func TestInferSupportedFeatures(t *testing.T) {
505506
Message: "GatewayClass is accepted and ready for use",
506507
},
507508
},
508-
SupportedFeatures: featureNamesToSet(statusFeatureNames),
509+
SupportedFeatures: featureNamesToSet(gwcStatusFeatureNames),
509510
},
510511
}
511512
scheme := runtime.NewScheme()
@@ -552,6 +553,113 @@ func TestInferSupportedFeatures(t *testing.T) {
552553
}
553554
}
554555

556+
var meshStatusFeatureNames = []string{
557+
"Mesh",
558+
"MeshClusterIPMatching",
559+
"MeshNamespaceSelector",
560+
"MeshServiceAccountSelector",
561+
"MeshTLS",
562+
"MeshTLSClientCert",
563+
"MeshTrafficSplit",
564+
"MeshAccessControl",
565+
"HTTPRoute",
566+
}
567+
568+
func TestXMeshInferSupportedFeatures(t *testing.T) {
569+
testCases := []struct {
570+
name string
571+
allowAllFeatures bool
572+
supportedFeatures FeaturesSet
573+
exemptFeatures FeaturesSet
574+
ConformanceProfile sets.Set[ConformanceProfileName]
575+
expectedFeatures FeaturesSet
576+
expectedSource supportedFeaturesSource
577+
}{
578+
{
579+
name: "properly infer mesh supported features",
580+
expectedFeatures: namesToFeatureSet(meshStatusFeatureNames),
581+
expectedSource: supportedFeaturesSourceInferred,
582+
},
583+
{
584+
name: "no features",
585+
supportedFeatures: sets.New[features.FeatureName]("Mesh"),
586+
expectedFeatures: sets.New[features.FeatureName]("Mesh"),
587+
expectedSource: supportedFeaturesSourceManual,
588+
},
589+
{
590+
name: "remove exempt features",
591+
supportedFeatures: sets.New[features.FeatureName]("MeshTLS", "MeshAccessControl"),
592+
exemptFeatures: sets.New[features.FeatureName]("MeshAccessControl"),
593+
expectedFeatures: sets.New[features.FeatureName]("MeshTLS"),
594+
expectedSource: supportedFeaturesSourceManual,
595+
},
596+
{
597+
name: "supports conformance profile - core",
598+
ConformanceProfile: sets.New(MeshHTTPConformanceProfileName),
599+
expectedFeatures: namesToFeatureSet(meshStatusFeatureNames),
600+
expectedSource: supportedFeaturesSourceInferred,
601+
},
602+
}
603+
604+
meshName := "xochopintre"
605+
xmesh := &xmeshv1alpha1.XMesh{
606+
ObjectMeta: metav1.ObjectMeta{
607+
Name: meshName,
608+
},
609+
Spec: xmeshv1alpha1.MeshSpec{
610+
ControllerName: "example.com/mesh-controller",
611+
},
612+
Status: xmeshv1alpha1.MeshStatus{
613+
Conditions: []metav1.Condition{
614+
{
615+
Type: string(xmeshv1alpha1.MeshConditionAccepted),
616+
Status: metav1.ConditionTrue,
617+
Reason: "Accepted",
618+
Message: "XMesh is accepted and ready for use",
619+
},
620+
},
621+
SupportedFeatures: featureNamesToSet(meshStatusFeatureNames),
622+
},
623+
}
624+
scheme := runtime.NewScheme()
625+
scheme.AddKnownTypes(xmeshv1alpha1.SchemeGroupVersion, &xmeshv1alpha1.XMesh{})
626+
fakeClient := fake.NewClientBuilder().
627+
WithScheme(scheme).
628+
WithObjects(xmesh).
629+
WithLists(&apiextensionsv1.CustomResourceDefinitionList{}).
630+
Build()
631+
632+
xmeshv1alpha1.Install(fakeClient.Scheme())
633+
apiextensionsv1.AddToScheme(fakeClient.Scheme())
634+
635+
for _, tc := range testCases {
636+
options := ConformanceOptions{
637+
AllowCRDsMismatch: true,
638+
MeshName: meshName,
639+
EnableAllSupportedFeatures: tc.allowAllFeatures,
640+
SupportedFeatures: tc.supportedFeatures,
641+
ExemptFeatures: tc.exemptFeatures,
642+
ConformanceProfiles: tc.ConformanceProfile,
643+
Client: fakeClient,
644+
}
645+
646+
t.Run(tc.name, func(t *testing.T) {
647+
cSuite, err := NewConformanceTestSuite(options)
648+
if err != nil {
649+
t.Fatalf("error initializing conformance suite: %v", err)
650+
}
651+
652+
if cSuite.supportedFeaturesSource != tc.expectedSource {
653+
t.Errorf("InferredSupportedFeatures mismatch: got %v, want %v", cSuite.supportedFeaturesSource, tc.expectedSource)
654+
}
655+
656+
if equal := cSuite.SupportedFeatures.Equal(tc.expectedFeatures); !equal {
657+
t.Errorf("SupportedFeatures mismatch: got %v, want %v", cSuite.SupportedFeatures.UnsortedList(), tc.expectedFeatures.UnsortedList())
658+
}
659+
})
660+
}
661+
}
662+
555663
func TestGWCPublishedMeshFeatures(t *testing.T) {
556664
gwcName := "ochopintre"
557665
gwc := &gatewayv1.GatewayClass{

0 commit comments

Comments
 (0)