Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions conformance/conformance.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ func DefaultOptions(t *testing.T) suite.ConformanceOptions {
ExemptFeatures: exemptFeatures,
ManifestFS: []fs.FS{&Manifests},
GatewayClassName: *flags.GatewayClassName,
MeshName: *flags.MeshName,
Implementation: implementation,
Mode: *flags.Mode,
NamespaceAnnotations: namespaceAnnotations,
Expand Down
1 change: 1 addition & 0 deletions conformance/utils/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const (

var (
GatewayClassName = flag.String("gateway-class", "gateway-conformance", "Name of GatewayClass to use for tests")
MeshName = flag.String("mesh-name", "", "Name of Mesh to use for tests")
ShowDebug = flag.Bool("debug", false, "Whether to print debug logs")
CleanupBaseResources = flag.Bool("cleanup-base-resources", true, "Whether to cleanup base test resources after the run")
SupportedFeatures = flag.String("supported-features", "", "Supported features included in conformance tests suites")
Expand Down
63 changes: 52 additions & 11 deletions conformance/utils/suite/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (

gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
"sigs.k8s.io/gateway-api/apis/v1beta1"
xmeshv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1"
confv1 "sigs.k8s.io/gateway-api/conformance/apis/v1"
"sigs.k8s.io/gateway-api/conformance/utils/config"
"sigs.k8s.io/gateway-api/conformance/utils/flags"
Expand All @@ -65,6 +66,7 @@ type ConformanceTestSuite struct {
RoundTripper roundtripper.RoundTripper
GRPCClient grpc.Client
GatewayClassName string
MeshName string
ControllerName string
Debug bool
Cleanup bool
Expand Down Expand Up @@ -136,6 +138,7 @@ type ConformanceOptions struct {
Clientset clientset.Interface
RestConfig *rest.Config
GatewayClassName string
MeshName string
AddressType string
Debug bool
RoundTripper roundtripper.RoundTripper
Expand Down Expand Up @@ -206,20 +209,23 @@ func NewConformanceTestSuite(options ConformanceOptions) (*ConformanceTestSuite,
supportedFeatures = features.SetsToNamesSet(features.AllFeatures)
} else if shouldInferSupportedFeatures(&options) {
var err error
supportedFeatures, err = fetchSupportedFeatures(options.Client, options.GatewayClassName)
if err != nil {
return nil, fmt.Errorf("cannot infer supported features: %w", err)
if options.GatewayClassName != "" {
supportedFeatures, err = fetchGatewayClassSupportedFeatures(options.Client, options.GatewayClassName)
if err != nil {
return nil, fmt.Errorf("cannot infer supported features from GWC: %w", err)
}
}

// If Mesh features are populated in the GatewayClass we remove them from the supported features set.
meshFeatureNames := features.SetsToNamesSet(features.MeshCoreFeatures, features.MeshExtendedFeatures)
for _, f := range supportedFeatures.UnsortedList() {
if meshFeatureNames.Has(f) {
supportedFeatures.Delete(f)
fmt.Printf("WARNING: Mesh feature %q should not be populated in GatewayClass, skipping...", f)
supportedMeshFeatures := FeaturesSet{}
if options.MeshName != "" {
supportedMeshFeatures, err = fetchMeshSupportedFeatures(options.Client, options.MeshName)
if err != nil {
return nil, fmt.Errorf("cannot infer supported features from XMesh: %w", err)
}
}

source = supportedFeaturesSourceInferred
supportedFeatures = supportedFeatures.Union(supportedMeshFeatures)
}

// If features were not inferred from Status, it's a GWC issue.
Expand Down Expand Up @@ -591,24 +597,59 @@ func ParseConformanceProfiles(p string) sets.Set[ConformanceProfileName] {
return res
}

func fetchSupportedFeatures(client client.Client, gatewayClassName string) (FeaturesSet, error) {
func fetchGatewayClassSupportedFeatures(client client.Client, gatewayClassName string) (FeaturesSet, error) {
if gatewayClassName == "" {
return nil, fmt.Errorf("GatewayClass name must be provided to fetch supported features")
}
gwc := &gatewayv1.GatewayClass{}
err := client.Get(context.TODO(), types.NamespacedName{Name: gatewayClassName}, gwc)
if err != nil {
return nil, fmt.Errorf("fetchSupportedFeatures(): %w", err)
return nil, fmt.Errorf("fetchGatewayClassSupportedFeatures(): %w", err)
}

fs := FeaturesSet{}
for _, feature := range gwc.Status.SupportedFeatures {
fs.Insert(features.FeatureName(feature.Name))
}

// If Mesh features are populated in the GatewayClass we remove them from the supported features set.
meshFeatureNames := features.SetsToNamesSet(features.MeshCoreFeatures, features.MeshExtendedFeatures)
for _, f := range fs.UnsortedList() {
if meshFeatureNames.Has(f) {
fs.Delete(f)
fmt.Printf("WARNING: Mesh feature %q should not be populated in GatewayClass, skipping...", f)
}
}
fmt.Printf("Supported features for GatewayClass %s: %v\n", gatewayClassName, fs.UnsortedList())
return fs, nil
}

func fetchMeshSupportedFeatures(client client.Client, meshName string) (FeaturesSet, error) {
if meshName == "" {
return nil, fmt.Errorf("mesh name must be provided to fetch supported features")
}
xmesh := &xmeshv1alpha1.XMesh{}
err := client.Get(context.TODO(), types.NamespacedName{Name: meshName}, xmesh)
if err != nil {
return nil, fmt.Errorf("fetchMeshSupportedFeatures(): %w", err)
}

fs := FeaturesSet{}
for _, feature := range xmesh.Status.SupportedFeatures {
fs.Insert(features.FeatureName(feature.Name))
}

gwcFeatureNames := features.SetsToNamesSet(features.GatewayCoreFeatures, features.GatewayExtendedFeatures)
for _, f := range fs.UnsortedList() {
if gwcFeatureNames.Has(f) {
fs.Delete(f)
fmt.Printf("WARNING: Mesh feature %q should not be populated in XMesh.Status, skipping...", f)
}
}
fmt.Printf("Supported features for XMesh%s: %v\n", meshName, fs.UnsortedList())
return fs, nil
}

// shouldInferSupportedFeatures checks if any flags were supplied for manually
// picking what to test. Inferred supported features are only used when no flags
// are set.
Expand Down
118 changes: 113 additions & 5 deletions conformance/utils/suite/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/fake"

gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
xmeshv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1"
confv1 "sigs.k8s.io/gateway-api/conformance/apis/v1"
"sigs.k8s.io/gateway-api/pkg/consts"
"sigs.k8s.io/gateway-api/pkg/features"
Expand Down Expand Up @@ -411,7 +412,7 @@ func TestSuiteReport(t *testing.T) {
}
}

var statusFeatureNames = []string{
var gwcStatusFeatureNames = []string{
"Gateway",
"GatewayPort8080",
"HTTPRoute",
Expand All @@ -423,7 +424,7 @@ var statusFeatureNames = []string{
"ReferenceGrant",
}

func TestInferSupportedFeatures(t *testing.T) {
func TestInferGWCSupportedFeatures(t *testing.T) {
testCases := []struct {
name string
allowAllFeatures bool
Expand All @@ -436,7 +437,7 @@ func TestInferSupportedFeatures(t *testing.T) {
}{
{
name: "properly infer supported features",
expectedFeatures: namesToFeatureSet(statusFeatureNames),
expectedFeatures: namesToFeatureSet(gwcStatusFeatureNames),
expectedSource: supportedFeaturesSourceInferred,
},
{
Expand Down Expand Up @@ -473,7 +474,7 @@ func TestInferSupportedFeatures(t *testing.T) {
{
name: "supports conformance profile core with inferred extended features",
ConformanceProfile: sets.New(GatewayHTTPConformanceProfileName),
expectedFeatures: namesToFeatureSet(statusFeatureNames),
expectedFeatures: namesToFeatureSet(gwcStatusFeatureNames),
expectedSource: supportedFeaturesSourceInferred,
expectedExtendedFeatures: map[ConformanceProfileName]sets.Set[features.FeatureName]{
GatewayHTTPConformanceProfileName: namesToFeatureSet([]string{
Expand Down Expand Up @@ -505,7 +506,7 @@ func TestInferSupportedFeatures(t *testing.T) {
Message: "GatewayClass is accepted and ready for use",
},
},
SupportedFeatures: featureNamesToSet(statusFeatureNames),
SupportedFeatures: featureNamesToSet(gwcStatusFeatureNames),
},
}
scheme := runtime.NewScheme()
Expand Down Expand Up @@ -552,6 +553,113 @@ func TestInferSupportedFeatures(t *testing.T) {
}
}

var meshStatusFeatureNames = []string{
"Mesh",
"MeshClusterIPMatching",
"MeshNamespaceSelector",
"MeshServiceAccountSelector",
"MeshTLS",
"MeshTLSClientCert",
"MeshTrafficSplit",
"MeshAccessControl",
"HTTPRoute",
}

func TestXMeshInferSupportedFeatures(t *testing.T) {
testCases := []struct {
name string
allowAllFeatures bool
supportedFeatures FeaturesSet
exemptFeatures FeaturesSet
ConformanceProfile sets.Set[ConformanceProfileName]
expectedFeatures FeaturesSet
expectedSource supportedFeaturesSource
}{
{
name: "properly infer mesh supported features",
expectedFeatures: namesToFeatureSet(meshStatusFeatureNames),
expectedSource: supportedFeaturesSourceInferred,
},
{
name: "no features",
supportedFeatures: sets.New[features.FeatureName]("Mesh"),
expectedFeatures: sets.New[features.FeatureName]("Mesh"),
expectedSource: supportedFeaturesSourceManual,
},
{
name: "remove exempt features",
supportedFeatures: sets.New[features.FeatureName]("MeshTLS", "MeshAccessControl"),
exemptFeatures: sets.New[features.FeatureName]("MeshAccessControl"),
expectedFeatures: sets.New[features.FeatureName]("MeshTLS"),
expectedSource: supportedFeaturesSourceManual,
},
{
name: "supports conformance profile - core",
ConformanceProfile: sets.New(MeshHTTPConformanceProfileName),
expectedFeatures: namesToFeatureSet(meshStatusFeatureNames),
expectedSource: supportedFeaturesSourceInferred,
},
}

meshName := "xochopintre"
xmesh := &xmeshv1alpha1.XMesh{
ObjectMeta: metav1.ObjectMeta{
Name: meshName,
},
Spec: xmeshv1alpha1.MeshSpec{
ControllerName: "example.com/mesh-controller",
},
Status: xmeshv1alpha1.MeshStatus{
Conditions: []metav1.Condition{
{
Type: string(xmeshv1alpha1.MeshConditionAccepted),
Status: metav1.ConditionTrue,
Reason: "Accepted",
Message: "XMesh is accepted and ready for use",
},
},
SupportedFeatures: featureNamesToSet(meshStatusFeatureNames),
},
}
scheme := runtime.NewScheme()
scheme.AddKnownTypes(xmeshv1alpha1.SchemeGroupVersion, &xmeshv1alpha1.XMesh{})
fakeClient := fake.NewClientBuilder().
WithScheme(scheme).
WithObjects(xmesh).
WithLists(&apiextensionsv1.CustomResourceDefinitionList{}).
Build()

xmeshv1alpha1.Install(fakeClient.Scheme())
apiextensionsv1.AddToScheme(fakeClient.Scheme())

for _, tc := range testCases {
options := ConformanceOptions{
AllowCRDsMismatch: true,
MeshName: meshName,
EnableAllSupportedFeatures: tc.allowAllFeatures,
SupportedFeatures: tc.supportedFeatures,
ExemptFeatures: tc.exemptFeatures,
ConformanceProfiles: tc.ConformanceProfile,
Client: fakeClient,
}

t.Run(tc.name, func(t *testing.T) {
cSuite, err := NewConformanceTestSuite(options)
if err != nil {
t.Fatalf("error initializing conformance suite: %v", err)
}

if cSuite.supportedFeaturesSource != tc.expectedSource {
t.Errorf("InferredSupportedFeatures mismatch: got %v, want %v", cSuite.supportedFeaturesSource, tc.expectedSource)
}

if equal := cSuite.SupportedFeatures.Equal(tc.expectedFeatures); !equal {
t.Errorf("SupportedFeatures mismatch: got %v, want %v", cSuite.SupportedFeatures.UnsortedList(), tc.expectedFeatures.UnsortedList())
}
})
}
}

func TestGWCPublishedMeshFeatures(t *testing.T) {
gwcName := "ochopintre"
gwc := &gatewayv1.GatewayClass{
Expand Down