From 7857cfbe192b8e1ccbd800c38beea3a767e6d225 Mon Sep 17 00:00:00 2001 From: Dennis Zhou Date: Thu, 4 Apr 2024 02:39:44 +0800 Subject: [PATCH 1/5] Add label filtering (-l) for describe commands --- gwctl/cmd/describe.go | 48 ++++++++- gwctl/pkg/printer/gatewayclasses_test.go | 99 ++++++++++++++++++ gwctl/pkg/printer/gateways_test.go | 128 +++++++++++++++++++++++ gwctl/pkg/printer/httproutes_test.go | 89 ++++++++++++++++ gwctl/pkg/printer/namespace_test.go | 59 +++++++++++ 5 files changed, 419 insertions(+), 4 deletions(-) diff --git a/gwctl/cmd/describe.go b/gwctl/cmd/describe.go index 14a500f643..3c6fb8d9e0 100644 --- a/gwctl/cmd/describe.go +++ b/gwctl/cmd/describe.go @@ -28,11 +28,13 @@ import ( "sigs.k8s.io/gateway-api/gwctl/pkg/utils" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" ) func NewDescribeCommand() *cobra.Command { var namespaceFlag string var allNamespacesFlag bool + var labelSelector string cmd := &cobra.Command{ Use: "describe {policies|httproutes|gateways|gatewayclasses|backends|namespace} RESOURCE_NAME", @@ -45,6 +47,7 @@ func NewDescribeCommand() *cobra.Command { } cmd.Flags().StringVarP(&namespaceFlag, "namespace", "n", "default", "") cmd.Flags().BoolVarP(&allNamespacesFlag, "all-namespaces", "A", false, "If present, list requested resources from all namespaces.") + cmd.Flags().StringVarP(&labelSelector, "selector", "l", "", "Label selector.") return cmd } @@ -64,6 +67,12 @@ func runDescribe(cmd *cobra.Command, args []string, params *utils.CmdParams) { os.Exit(1) } + labelSelector, err := cmd.Flags().GetString("selector") + if err != nil { + fmt.Fprintf(os.Stderr, "failed to read flag \"selector\": %v\n", err) + os.Exit(1) + } + if allNs { ns = metav1.NamespaceAll } @@ -97,7 +106,15 @@ func runDescribe(cmd *cobra.Command, args []string, params *utils.CmdParams) { policiesPrinter.PrintDescribeView(policyList) case "httproute", "httproutes": - filter := resourcediscovery.Filter{Namespace: ns} + selector, err := labels.Parse(labelSelector) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + os.Exit(1) + } + filter := resourcediscovery.Filter{ + Namespace: ns, + Labels: selector, + } if len(args) > 1 { filter.Name = args[1] } @@ -109,7 +126,15 @@ func runDescribe(cmd *cobra.Command, args []string, params *utils.CmdParams) { httpRoutesPrinter.PrintDescribeView(resourceModel) case "gateway", "gateways": - filter := resourcediscovery.Filter{Namespace: ns} + selector, err := labels.Parse(labelSelector) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + os.Exit(1) + } + filter := resourcediscovery.Filter{ + Namespace: ns, + Labels: selector, + } if len(args) > 1 { filter.Name = args[1] } @@ -121,7 +146,15 @@ func runDescribe(cmd *cobra.Command, args []string, params *utils.CmdParams) { gwPrinter.PrintDescribeView(resourceModel) case "gatewayclass", "gatewayclasses": - filter := resourcediscovery.Filter{} + selector, err := labels.Parse(labelSelector) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + os.Exit(1) + } + filter := resourcediscovery.Filter{ + Namespace: ns, + Labels: selector, + } if len(args) > 1 { filter.Name = args[1] } @@ -147,7 +180,14 @@ func runDescribe(cmd *cobra.Command, args []string, params *utils.CmdParams) { backendsPrinter.PrintDescribeView(resourceModel) case "namespace", "namespaces", "ns": - filter := resourcediscovery.Filter{} + selector, err := labels.Parse(labelSelector) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + os.Exit(1) + } + filter := resourcediscovery.Filter{ + Labels: selector, + } if len(args) > 1 { filter.Name = args[1] } diff --git a/gwctl/pkg/printer/gatewayclasses_test.go b/gwctl/pkg/printer/gatewayclasses_test.go index fbf52abec1..e3ee9ed7a0 100644 --- a/gwctl/pkg/printer/gatewayclasses_test.go +++ b/gwctl/pkg/printer/gatewayclasses_test.go @@ -296,3 +296,102 @@ foo-com-internal-gateway-class foo-com-internal-gateway-class/controller True t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) } } + +// TestGatewayClassesPrinterDescribe_LabelSelector Tests label selector filtering for GatewayClasses in 'describe' command. +func TestGatewayClassesPrinterDescribe_LabelSelector(t *testing.T) { + fakeClock := testingclock.NewFakeClock(time.Now()) + + gatewayClass := func(name string, labels map[string]string) *gatewayv1.GatewayClass { + return &gatewayv1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + CreationTimestamp: metav1.Time{ + Time: fakeClock.Now().Add(-365 * 24 * time.Hour), + }, + }, + Spec: gatewayv1.GatewayClassSpec{ + ControllerName: gatewayv1.GatewayController(name + "/controller"), + Description: common.PtrTo("random"), + }, + Status: gatewayv1.GatewayClassStatus{ + Conditions: []metav1.Condition{ + { + Type: "Accepted", + Status: metav1.ConditionTrue, + }, + }, + }, + } + } + objects := []runtime.Object{ + &apiextensionsv1.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "healthcheckpolicies.foo.com", + Labels: map[string]string{ + gatewayv1alpha2.PolicyLabelKey: "true", + }, + }, + Spec: apiextensionsv1.CustomResourceDefinitionSpec{ + Group: "foo.com", + Versions: []apiextensionsv1.CustomResourceDefinitionVersion{{Name: "v1"}}, + Names: apiextensionsv1.CustomResourceDefinitionNames{ + Plural: "healthcheckpolicies", + Kind: "HealthCheckPolicy", + }, + }, + }, + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "foo.com/v1", + "kind": "HealthCheckPolicy", + "metadata": map[string]interface{}{ + "name": "policy-name", + }, + "spec": map[string]interface{}{ + "targetRef": map[string]interface{}{ + "group": "gateway.networking.k8s.io", + "kind": "GatewayClass", + "name": "foo-com-internal-gateway-class", + }, + }, + }, + }, + gatewayClass("foo-com-external-gateway-class", map[string]string{"app": "foo"}), + gatewayClass("foo-com-internal-gateway-class", map[string]string{"app": "foo", "env": "internal"}), + } + params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) + discoverer := resourcediscovery.Discoverer{ + K8sClients: params.K8sClients, + PolicyManager: params.PolicyManager, + } + labelSelector := "env=internal" + selector, err := labels.Parse(labelSelector) + if err != nil { + t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + } + resourceModel, err := discoverer.DiscoverResourcesForGatewayClass(resourcediscovery.Filter{Labels: selector}) + if err != nil { + t.Fatalf("Failed to construct resourceModel: %v", resourceModel) + } + + gcp := &GatewayClassesPrinter{ + Out: params.Out, + Clock: fakeClock, + } + gcp.PrintDescribeView(resourceModel) + + got := params.Out.(*bytes.Buffer).String() + want := ` +Name: foo-com-internal-gateway-class +ControllerName: foo-com-internal-gateway-class/controller +Description: random +DirectlyAttachedPolicies: +- Group: foo.com + Kind: HealthCheckPolicy + Name: policy-name +` + if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { + t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) + } +} diff --git a/gwctl/pkg/printer/gateways_test.go b/gwctl/pkg/printer/gateways_test.go index 8d1eed9f3f..33d4583bf9 100644 --- a/gwctl/pkg/printer/gateways_test.go +++ b/gwctl/pkg/printer/gateways_test.go @@ -449,3 +449,131 @@ gateway-2 gatewayclass-1 192.168.100.5 8080 False 5d t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) } } + +// TestGatewaysPrinterDescribe_LabelSelector tests label selector filtering for Gateways in 'describe' command. +func TestGatewaysPrinterDescribe_LabelSelector(t *testing.T) { + fakeClock := testingclock.NewFakeClock(time.Now()) + gateway := func(name string, labels map[string]string) *gatewayv1.Gateway { + return &gatewayv1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + CreationTimestamp: metav1.Time{ + Time: fakeClock.Now().Add(-5 * 24 * time.Hour), + }, + }, + Spec: gatewayv1.GatewaySpec{ + GatewayClassName: "gatewayclass-1", + Listeners: []gatewayv1.Listener{ + { + Name: "http-8080", + Protocol: gatewayv1.HTTPProtocolType, + Port: gatewayv1.PortNumber(8080), + }, + }, + }, + Status: gatewayv1.GatewayStatus{ + Addresses: []gatewayv1.GatewayStatusAddress{ + { + Value: "192.168.100.5", + }, + }, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: "False", + }, + }, + }, + } + } + + objects := []runtime.Object{ + &gatewayv1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gatewayclass-1", + }, + Spec: gatewayv1.GatewayClassSpec{ + ControllerName: "example.net/gateway-controller", + Description: common.PtrTo("random"), + }, + }, + + &apiextensionsv1.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "healthcheckpolicies.foo.com", + Labels: map[string]string{ + gatewayv1alpha2.PolicyLabelKey: "inherited", + }, + }, + Spec: apiextensionsv1.CustomResourceDefinitionSpec{ + Scope: apiextensionsv1.ClusterScoped, + Group: "foo.com", + Versions: []apiextensionsv1.CustomResourceDefinitionVersion{{Name: "v1"}}, + Names: apiextensionsv1.CustomResourceDefinitionNames{ + Plural: "healthcheckpolicies", + Kind: "HealthCheckPolicy", + }, + }, + }, + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "foo.com/v1", + "kind": "HealthCheckPolicy", + "metadata": map[string]interface{}{ + "name": "health-check-gatewayclass", + }, + "spec": map[string]interface{}{ + "override": map[string]interface{}{ + "key1": "value-parent-1", + }, + "default": map[string]interface{}{ + "key2": "value-parent-2", + }, + "targetRef": map[string]interface{}{ + "group": "gateway.networking.k8s.io", + "kind": "GatewayClass", + "name": "gatewayclass-1", + }, + }, + }, + }, + gateway("gateway-1", map[string]string{"app": "foo"}), + gateway("gateway-2", map[string]string{"app": "foo", "env": "internal"}), + } + + params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) + discoverer := resourcediscovery.Discoverer{ + K8sClients: params.K8sClients, + PolicyManager: params.PolicyManager, + } + labelSelector := "env=internal" + selector, err := labels.Parse(labelSelector) + if err != nil { + t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + } + resourceModel, err := discoverer.DiscoverResourcesForGateway(resourcediscovery.Filter{Labels: selector}) + if err != nil { + t.Fatalf("Failed to construct resourceModel: %v", resourceModel) + } + + nsp := &GatewaysPrinter{ + Out: params.Out, + Clock: fakeClock, + } + nsp.PrintDescribeView(resourceModel) + + got := params.Out.(*bytes.Buffer).String() + want := ` +Name: gateway-2 +GatewayClass: gatewayclass-1 +EffectivePolicies: + HealthCheckPolicy.foo.com: + key1: value-parent-1 + key2: value-parent-2 +` + + if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { + t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) + } +} diff --git a/gwctl/pkg/printer/httproutes_test.go b/gwctl/pkg/printer/httproutes_test.go index be1397a0f7..3a413ed1ea 100644 --- a/gwctl/pkg/printer/httproutes_test.go +++ b/gwctl/pkg/printer/httproutes_test.go @@ -519,3 +519,92 @@ default httproute-2 example.com 1 24h t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) } } + +// TestHTTPRoutesPrinterDescribe_LabelSelector tests label selector filtering for HTTPRoute in 'describe' command. +func TestHTTPRoutesPrinterDescribe_LabelSelector(t *testing.T) { + fakeClock := testingclock.NewFakeClock(time.Now()) + httpRoute := func(name string, labels map[string]string) *gatewayv1.HTTPRoute { + return &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "default", + CreationTimestamp: metav1.Time{ + Time: fakeClock.Now().Add(-24 * time.Hour), + }, + Labels: labels, + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"example.com"}, + CommonRouteSpec: gatewayv1.CommonRouteSpec{ + ParentRefs: []gatewayv1.ParentReference{ + { + Name: "gateway-1", + }, + }, + }, + }, + } + } + + objects := []runtime.Object{ + &gatewayv1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gatewayclass-1", + }, + Spec: gatewayv1.GatewayClassSpec{ + ControllerName: "example.net/gateway-controller", + Description: common.PtrTo("random"), + }, + }, + + &gatewayv1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway-1", + Namespace: "default", + }, + Spec: gatewayv1.GatewaySpec{ + GatewayClassName: "gatewayclass-1", + }, + }, + httpRoute("httproute-1", map[string]string{"app": "foo"}), + httpRoute("httproute-2", map[string]string{"app": "foo", "env": "internal"}), + } + + params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) + discoverer := resourcediscovery.Discoverer{ + K8sClients: params.K8sClients, + PolicyManager: params.PolicyManager, + } + + labelSelector := "env=internal" + selector, err := labels.Parse(labelSelector) + if err != nil { + t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + } + resourceModel, err := discoverer.DiscoverResourcesForHTTPRoute(resourcediscovery.Filter{Labels: selector}) + if err != nil { + t.Fatalf("Failed to discover resources: %v", err) + } + + nsp := &HTTPRoutesPrinter{ + Out: params.Out, + Clock: fakeClock, + } + nsp.PrintDescribeView(resourceModel) + + got := params.Out.(*bytes.Buffer).String() + want := ` +Name: httproute-2 +Namespace: default +Hostnames: +- example.com +ParentRefs: +- name: gateway-1 +EffectivePolicies: + default/gateway-1: {} + +` + if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { + t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) + } +} diff --git a/gwctl/pkg/printer/namespace_test.go b/gwctl/pkg/printer/namespace_test.go index 16681c6ffa..24ff208192 100644 --- a/gwctl/pkg/printer/namespace_test.go +++ b/gwctl/pkg/printer/namespace_test.go @@ -313,3 +313,62 @@ namespace-2 Active 46d t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) } } + +// TestNamespacesPrinterDescribe_LabelSelector tests label selector filtering for Namespaces in 'describe' command. +func TestNamespacesPrinterDescribe_LabelSelector(t *testing.T) { + fakeClock := testingclock.NewFakeClock(time.Now()) + namespace := func(name string, labels map[string]string) *corev1.Namespace { + return &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + CreationTimestamp: metav1.Time{ + Time: fakeClock.Now().Add(-46 * 24 * time.Hour), + }, + Labels: labels, + }, + Status: corev1.NamespaceStatus{ + Phase: corev1.NamespaceActive, + }, + } + } + + objects := []runtime.Object{ + namespace("namespace-1", map[string]string{"app": "foo"}), + namespace("namespace-2", map[string]string{"app": "foo", "env": "internal"}), + } + + params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) + discoverer := resourcediscovery.Discoverer{ + K8sClients: params.K8sClients, + PolicyManager: params.PolicyManager, + } + labelSelector := "env=internal" + selector, err := labels.Parse(labelSelector) + if err != nil { + t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + } + resourceModel, err := discoverer.DiscoverResourcesForNamespace(resourcediscovery.Filter{Labels: selector}) + if err != nil { + t.Fatalf("Failed to construct resourceModel: %v", resourceModel) + } + + nsp := &NamespacesPrinter{ + Out: params.Out, + Clock: fakeClock, + } + + nsp.PrintDescribeView(resourceModel) + + got := params.Out.(*bytes.Buffer).String() + want := ` +Name: namespace-2 +Labels: + app: foo + env: internal +Status: Active +` + + if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { + t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) + } +} From 3c7b3e5a920edd2639b2d3bdafc80f4a84b7a07f Mon Sep 17 00:00:00 2001 From: Dennis Zhou Date: Thu, 4 Apr 2024 14:02:10 +0800 Subject: [PATCH 2/5] fix nits --- gwctl/cmd/describe.go | 5 ++--- gwctl/cmd/get.go | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gwctl/cmd/describe.go b/gwctl/cmd/describe.go index 3c6fb8d9e0..8469c28137 100644 --- a/gwctl/cmd/describe.go +++ b/gwctl/cmd/describe.go @@ -47,7 +47,7 @@ func NewDescribeCommand() *cobra.Command { } cmd.Flags().StringVarP(&namespaceFlag, "namespace", "n", "default", "") cmd.Flags().BoolVarP(&allNamespacesFlag, "all-namespaces", "A", false, "If present, list requested resources from all namespaces.") - cmd.Flags().StringVarP(&labelSelector, "selector", "l", "", "Label selector.") + cmd.Flags().StringVarP(&labelSelector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints.") return cmd } @@ -152,8 +152,7 @@ func runDescribe(cmd *cobra.Command, args []string, params *utils.CmdParams) { os.Exit(1) } filter := resourcediscovery.Filter{ - Namespace: ns, - Labels: selector, + Labels: selector, } if len(args) > 1 { filter.Name = args[1] diff --git a/gwctl/cmd/get.go b/gwctl/cmd/get.go index 330356ccde..75fe481871 100644 --- a/gwctl/cmd/get.go +++ b/gwctl/cmd/get.go @@ -46,7 +46,7 @@ func NewGetCommand() *cobra.Command { } cmd.Flags().StringVarP(&namespaceFlag, "namespace", "n", "default", "") cmd.Flags().BoolVarP(&allNamespacesFlag, "all-namespaces", "A", false, "If present, list requested resources from all namespaces.") - cmd.Flags().StringVarP(&labelSelector, "selector", "l", "", "Label selector.") + cmd.Flags().StringVarP(&labelSelector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints.") return cmd } From bc3262eaf7825c64d79e1d7bc91bbde9824bf45d Mon Sep 17 00:00:00 2001 From: Dennis Zhou Date: Wed, 10 Apr 2024 10:12:33 +0800 Subject: [PATCH 3/5] fix test --- gwctl/pkg/printer/gateways_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gwctl/pkg/printer/gateways_test.go b/gwctl/pkg/printer/gateways_test.go index 33d4583bf9..e7d3a35115 100644 --- a/gwctl/pkg/printer/gateways_test.go +++ b/gwctl/pkg/printer/gateways_test.go @@ -569,8 +569,8 @@ Name: gateway-2 GatewayClass: gatewayclass-1 EffectivePolicies: HealthCheckPolicy.foo.com: - key1: value-parent-1 - key2: value-parent-2 + key1: value-parent-1 + key2: value-parent-2 ` if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { From f69b12551417fc3eccd5238b9bec4190355611d2 Mon Sep 17 00:00:00 2001 From: Dennis Zhou Date: Wed, 17 Apr 2024 19:43:19 +0800 Subject: [PATCH 4/5] refactor test --- gwctl/go.mod | 2 + gwctl/pkg/printer/gatewayclasses_test.go | 119 ++---------------- gwctl/pkg/printer/gateways_test.go | 150 ++--------------------- gwctl/pkg/printer/httproutes_test.go | 116 ++---------------- gwctl/pkg/printer/namespace_test.go | 80 ++---------- 5 files changed, 34 insertions(+), 433 deletions(-) diff --git a/gwctl/go.mod b/gwctl/go.mod index a214da76fd..adf94e7d42 100644 --- a/gwctl/go.mod +++ b/gwctl/go.mod @@ -6,6 +6,7 @@ require ( github.com/evanphx/json-patch v5.9.0+incompatible github.com/google/go-cmp v0.6.0 github.com/spf13/cobra v1.8.0 + github.com/stretchr/testify v1.9.0 k8s.io/api v0.29.3 k8s.io/apiextensions-apiserver v0.29.3 k8s.io/apimachinery v0.29.3 @@ -41,6 +42,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.20.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect diff --git a/gwctl/pkg/printer/gatewayclasses_test.go b/gwctl/pkg/printer/gatewayclasses_test.go index e3ee9ed7a0..68157b11a1 100644 --- a/gwctl/pkg/printer/gatewayclasses_test.go +++ b/gwctl/pkg/printer/gatewayclasses_test.go @@ -22,6 +22,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -236,7 +237,7 @@ ControllerName: example.net/gateway-controller } } -// TestGatewayClassesPrinter_LabelSelector Tests label selector filtering for GatewayClasses in 'get' command. +// TestGatewayClassesPrinter_LabelSelector Tests label selector filtering for GatewayClasses. func TestGatewayClassesPrinter_LabelSelector(t *testing.T) { fakeClock := testingclock.NewFakeClock(time.Now()) @@ -281,117 +282,11 @@ func TestGatewayClassesPrinter_LabelSelector(t *testing.T) { t.Fatalf("Failed to construct resourceModel: %v", resourceModel) } - gcp := &GatewayClassesPrinter{ - Out: params.Out, - Clock: fakeClock, - } - gcp.Print(resourceModel) - - got := params.Out.(*bytes.Buffer).String() - want := ` -NAME CONTROLLER ACCEPTED AGE -foo-com-internal-gateway-class foo-com-internal-gateway-class/controller True 365d -` - if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { - t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) - } -} - -// TestGatewayClassesPrinterDescribe_LabelSelector Tests label selector filtering for GatewayClasses in 'describe' command. -func TestGatewayClassesPrinterDescribe_LabelSelector(t *testing.T) { - fakeClock := testingclock.NewFakeClock(time.Now()) - - gatewayClass := func(name string, labels map[string]string) *gatewayv1.GatewayClass { - return &gatewayv1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: labels, - CreationTimestamp: metav1.Time{ - Time: fakeClock.Now().Add(-365 * 24 * time.Hour), - }, - }, - Spec: gatewayv1.GatewayClassSpec{ - ControllerName: gatewayv1.GatewayController(name + "/controller"), - Description: common.PtrTo("random"), - }, - Status: gatewayv1.GatewayClassStatus{ - Conditions: []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - }, - }, - }, - } - } - objects := []runtime.Object{ - &apiextensionsv1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: "healthcheckpolicies.foo.com", - Labels: map[string]string{ - gatewayv1alpha2.PolicyLabelKey: "true", - }, - }, - Spec: apiextensionsv1.CustomResourceDefinitionSpec{ - Group: "foo.com", - Versions: []apiextensionsv1.CustomResourceDefinitionVersion{{Name: "v1"}}, - Names: apiextensionsv1.CustomResourceDefinitionNames{ - Plural: "healthcheckpolicies", - Kind: "HealthCheckPolicy", - }, - }, - }, - &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "foo.com/v1", - "kind": "HealthCheckPolicy", - "metadata": map[string]interface{}{ - "name": "policy-name", - }, - "spec": map[string]interface{}{ - "targetRef": map[string]interface{}{ - "group": "gateway.networking.k8s.io", - "kind": "GatewayClass", - "name": "foo-com-internal-gateway-class", - }, - }, - }, - }, - gatewayClass("foo-com-external-gateway-class", map[string]string{"app": "foo"}), - gatewayClass("foo-com-internal-gateway-class", map[string]string{"app": "foo", "env": "internal"}), - } - params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) - discoverer := resourcediscovery.Discoverer{ - K8sClients: params.K8sClients, - PolicyManager: params.PolicyManager, - } - labelSelector := "env=internal" - selector, err := labels.Parse(labelSelector) - if err != nil { - t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + expectedGatewayClassNames := []string{"foo-com-internal-gateway-class"} + gatewayClassNames := make([]string, 0, len(resourceModel.GatewayClasses)) + for _, gatewayClassNode := range resourceModel.GatewayClasses { + gatewayClassNames = append(gatewayClassNames, gatewayClassNode.GatewayClass.GetName()) } - resourceModel, err := discoverer.DiscoverResourcesForGatewayClass(resourcediscovery.Filter{Labels: selector}) - if err != nil { - t.Fatalf("Failed to construct resourceModel: %v", resourceModel) - } - - gcp := &GatewayClassesPrinter{ - Out: params.Out, - Clock: fakeClock, - } - gcp.PrintDescribeView(resourceModel) - got := params.Out.(*bytes.Buffer).String() - want := ` -Name: foo-com-internal-gateway-class -ControllerName: foo-com-internal-gateway-class/controller -Description: random -DirectlyAttachedPolicies: -- Group: foo.com - Kind: HealthCheckPolicy - Name: policy-name -` - if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { - t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) - } + assert.Equal(t, expectedGatewayClassNames, gatewayClassNames, "Expected GatewayClass name does not match the found one") } diff --git a/gwctl/pkg/printer/gateways_test.go b/gwctl/pkg/printer/gateways_test.go index e7d3a35115..538ac333e5 100644 --- a/gwctl/pkg/printer/gateways_test.go +++ b/gwctl/pkg/printer/gateways_test.go @@ -22,6 +22,8 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -366,7 +368,7 @@ EffectivePolicies: } } -// TestGatewaysPrinter_LabelSelector tests label selector filtering for Gateways in 'get' command. +// TestGatewaysPrinter_LabelSelector tests label selector filtering for Gateways. func TestGatewaysPrinter_LabelSelector(t *testing.T) { fakeClock := testingclock.NewFakeClock(time.Now()) gateway := func(name string, labels map[string]string) *gatewayv1.Gateway { @@ -433,147 +435,11 @@ func TestGatewaysPrinter_LabelSelector(t *testing.T) { t.Fatalf("Failed to construct resourceModel: %v", resourceModel) } - gp := &GatewaysPrinter{ - Out: params.Out, - Clock: fakeClock, - } - gp.Print(resourceModel) - - got := params.Out.(*bytes.Buffer).String() - want := ` -NAME CLASS ADDRESSES PORTS PROGRAMMED AGE -gateway-2 gatewayclass-1 192.168.100.5 8080 False 5d -` - - if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { - t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) - } -} - -// TestGatewaysPrinterDescribe_LabelSelector tests label selector filtering for Gateways in 'describe' command. -func TestGatewaysPrinterDescribe_LabelSelector(t *testing.T) { - fakeClock := testingclock.NewFakeClock(time.Now()) - gateway := func(name string, labels map[string]string) *gatewayv1.Gateway { - return &gatewayv1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: labels, - CreationTimestamp: metav1.Time{ - Time: fakeClock.Now().Add(-5 * 24 * time.Hour), - }, - }, - Spec: gatewayv1.GatewaySpec{ - GatewayClassName: "gatewayclass-1", - Listeners: []gatewayv1.Listener{ - { - Name: "http-8080", - Protocol: gatewayv1.HTTPProtocolType, - Port: gatewayv1.PortNumber(8080), - }, - }, - }, - Status: gatewayv1.GatewayStatus{ - Addresses: []gatewayv1.GatewayStatusAddress{ - { - Value: "192.168.100.5", - }, - }, - Conditions: []metav1.Condition{ - { - Type: "Programmed", - Status: "False", - }, - }, - }, - } - } - - objects := []runtime.Object{ - &gatewayv1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewayclass-1", - }, - Spec: gatewayv1.GatewayClassSpec{ - ControllerName: "example.net/gateway-controller", - Description: common.PtrTo("random"), - }, - }, - - &apiextensionsv1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: "healthcheckpolicies.foo.com", - Labels: map[string]string{ - gatewayv1alpha2.PolicyLabelKey: "inherited", - }, - }, - Spec: apiextensionsv1.CustomResourceDefinitionSpec{ - Scope: apiextensionsv1.ClusterScoped, - Group: "foo.com", - Versions: []apiextensionsv1.CustomResourceDefinitionVersion{{Name: "v1"}}, - Names: apiextensionsv1.CustomResourceDefinitionNames{ - Plural: "healthcheckpolicies", - Kind: "HealthCheckPolicy", - }, - }, - }, - &unstructured.Unstructured{ - Object: map[string]interface{}{ - "apiVersion": "foo.com/v1", - "kind": "HealthCheckPolicy", - "metadata": map[string]interface{}{ - "name": "health-check-gatewayclass", - }, - "spec": map[string]interface{}{ - "override": map[string]interface{}{ - "key1": "value-parent-1", - }, - "default": map[string]interface{}{ - "key2": "value-parent-2", - }, - "targetRef": map[string]interface{}{ - "group": "gateway.networking.k8s.io", - "kind": "GatewayClass", - "name": "gatewayclass-1", - }, - }, - }, - }, - gateway("gateway-1", map[string]string{"app": "foo"}), - gateway("gateway-2", map[string]string{"app": "foo", "env": "internal"}), - } - - params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) - discoverer := resourcediscovery.Discoverer{ - K8sClients: params.K8sClients, - PolicyManager: params.PolicyManager, - } - labelSelector := "env=internal" - selector, err := labels.Parse(labelSelector) - if err != nil { - t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) - } - resourceModel, err := discoverer.DiscoverResourcesForGateway(resourcediscovery.Filter{Labels: selector}) - if err != nil { - t.Fatalf("Failed to construct resourceModel: %v", resourceModel) - } - - nsp := &GatewaysPrinter{ - Out: params.Out, - Clock: fakeClock, + expectedGatewayNames := []string{"gateway-2"} + gatewayNames := make([]string, 0, len(resourceModel.Gateways)) + for _, gatewayNode := range resourceModel.Gateways { + gatewayNames = append(gatewayNames, gatewayNode.Gateway.GetName()) } - nsp.PrintDescribeView(resourceModel) - - got := params.Out.(*bytes.Buffer).String() - want := ` -Name: gateway-2 -GatewayClass: gatewayclass-1 -EffectivePolicies: - HealthCheckPolicy.foo.com: - key1: value-parent-1 - key2: value-parent-2 -` - if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { - t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) - } + assert.Equal(t, expectedGatewayNames, gatewayNames, "Expected Gateway name does not match the found one") } diff --git a/gwctl/pkg/printer/httproutes_test.go b/gwctl/pkg/printer/httproutes_test.go index 3a413ed1ea..82d5c42693 100644 --- a/gwctl/pkg/printer/httproutes_test.go +++ b/gwctl/pkg/printer/httproutes_test.go @@ -21,10 +21,9 @@ import ( "testing" "time" - gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" - gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -33,6 +32,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" testingclock "k8s.io/utils/clock/testing" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" "sigs.k8s.io/gateway-api/gwctl/pkg/common" "sigs.k8s.io/gateway-api/gwctl/pkg/resourcediscovery" "sigs.k8s.io/gateway-api/gwctl/pkg/utils" @@ -437,7 +438,7 @@ EffectivePolicies: } } -// TestHTTPRoutesPrinter_LabelSelector tests label selector filtering for HTTPRoute in 'get' command. +// TestHTTPRoutesPrinter_LabelSelector tests label selector filtering for HTTPRoute. func TestHTTPRoutesPrinter_LabelSelector(t *testing.T) { fakeClock := testingclock.NewFakeClock(time.Now()) httpRoute := func(name string, labels map[string]string) *gatewayv1.HTTPRoute { @@ -503,108 +504,11 @@ func TestHTTPRoutesPrinter_LabelSelector(t *testing.T) { t.Fatalf("Failed to discover resources: %v", err) } - hp := &HTTPRoutesPrinter{ - Out: params.Out, - Clock: fakeClock, - } - hp.Print(resourceModel) - - got := params.Out.(*bytes.Buffer).String() - want := ` -NAMESPACE NAME HOSTNAMES PARENT REFS AGE -default httproute-2 example.com 1 24h - -` - if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { - t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) - } -} - -// TestHTTPRoutesPrinterDescribe_LabelSelector tests label selector filtering for HTTPRoute in 'describe' command. -func TestHTTPRoutesPrinterDescribe_LabelSelector(t *testing.T) { - fakeClock := testingclock.NewFakeClock(time.Now()) - httpRoute := func(name string, labels map[string]string) *gatewayv1.HTTPRoute { - return &gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: "default", - CreationTimestamp: metav1.Time{ - Time: fakeClock.Now().Add(-24 * time.Hour), - }, - Labels: labels, - }, - Spec: gatewayv1.HTTPRouteSpec{ - Hostnames: []gatewayv1.Hostname{"example.com"}, - CommonRouteSpec: gatewayv1.CommonRouteSpec{ - ParentRefs: []gatewayv1.ParentReference{ - { - Name: "gateway-1", - }, - }, - }, - }, - } - } - - objects := []runtime.Object{ - &gatewayv1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewayclass-1", - }, - Spec: gatewayv1.GatewayClassSpec{ - ControllerName: "example.net/gateway-controller", - Description: common.PtrTo("random"), - }, - }, - - &gatewayv1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-1", - Namespace: "default", - }, - Spec: gatewayv1.GatewaySpec{ - GatewayClassName: "gatewayclass-1", - }, - }, - httpRoute("httproute-1", map[string]string{"app": "foo"}), - httpRoute("httproute-2", map[string]string{"app": "foo", "env": "internal"}), - } - - params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) - discoverer := resourcediscovery.Discoverer{ - K8sClients: params.K8sClients, - PolicyManager: params.PolicyManager, - } - - labelSelector := "env=internal" - selector, err := labels.Parse(labelSelector) - if err != nil { - t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) - } - resourceModel, err := discoverer.DiscoverResourcesForHTTPRoute(resourcediscovery.Filter{Labels: selector}) - if err != nil { - t.Fatalf("Failed to discover resources: %v", err) - } - - nsp := &HTTPRoutesPrinter{ - Out: params.Out, - Clock: fakeClock, + expectedHTTPRouteNames := []string{"httproute-2"} + HTTPRouteNames := make([]string, 0, len(resourceModel.HTTPRoutes)) + for _, HTTPRouteNode := range resourceModel.HTTPRoutes { + HTTPRouteNames = append(HTTPRouteNames, HTTPRouteNode.HTTPRoute.GetName()) } - nsp.PrintDescribeView(resourceModel) - - got := params.Out.(*bytes.Buffer).String() - want := ` -Name: httproute-2 -Namespace: default -Hostnames: -- example.com -ParentRefs: -- name: gateway-1 -EffectivePolicies: - default/gateway-1: {} -` - if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { - t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) - } + assert.Equal(t, expectedHTTPRouteNames, HTTPRouteNames, "Expected HTTPRoute name does not match the found one") } diff --git a/gwctl/pkg/printer/namespace_test.go b/gwctl/pkg/printer/namespace_test.go index 24ff208192..409dbabd0d 100644 --- a/gwctl/pkg/printer/namespace_test.go +++ b/gwctl/pkg/printer/namespace_test.go @@ -22,6 +22,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -259,7 +260,7 @@ DirectlyAttachedPolicies: } } -// TestNamespacesPrinter_LabelSelector tests label selector filtering for Namespaces in 'get' command. +// TestNamespacesPrinter_LabelSelector tests label selector filtering for Namespaces. func TestNamespacesPrinter_LabelSelector(t *testing.T) { fakeClock := testingclock.NewFakeClock(time.Now()) namespace := func(name string, labels map[string]string) *corev1.Namespace { @@ -297,78 +298,11 @@ func TestNamespacesPrinter_LabelSelector(t *testing.T) { t.Fatalf("Failed to construct resourceModel: %v", resourceModel) } - nsp := &NamespacesPrinter{ - Out: params.Out, - Clock: fakeClock, + expectedNamespaceNames := []string{"namespace-2"} + namespaceNames := make([]string, 0, len(resourceModel.Namespaces)) + for _, namespaceNode := range resourceModel.Namespaces { + namespaceNames = append(namespaceNames, namespaceNode.Namespace.Name) } - nsp.Print(resourceModel) - got := params.Out.(*bytes.Buffer).String() - want := ` -NAME STATUS AGE -namespace-2 Active 46d -` - - if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { - t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) - } -} - -// TestNamespacesPrinterDescribe_LabelSelector tests label selector filtering for Namespaces in 'describe' command. -func TestNamespacesPrinterDescribe_LabelSelector(t *testing.T) { - fakeClock := testingclock.NewFakeClock(time.Now()) - namespace := func(name string, labels map[string]string) *corev1.Namespace { - return &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - CreationTimestamp: metav1.Time{ - Time: fakeClock.Now().Add(-46 * 24 * time.Hour), - }, - Labels: labels, - }, - Status: corev1.NamespaceStatus{ - Phase: corev1.NamespaceActive, - }, - } - } - - objects := []runtime.Object{ - namespace("namespace-1", map[string]string{"app": "foo"}), - namespace("namespace-2", map[string]string{"app": "foo", "env": "internal"}), - } - - params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) - discoverer := resourcediscovery.Discoverer{ - K8sClients: params.K8sClients, - PolicyManager: params.PolicyManager, - } - labelSelector := "env=internal" - selector, err := labels.Parse(labelSelector) - if err != nil { - t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) - } - resourceModel, err := discoverer.DiscoverResourcesForNamespace(resourcediscovery.Filter{Labels: selector}) - if err != nil { - t.Fatalf("Failed to construct resourceModel: %v", resourceModel) - } - - nsp := &NamespacesPrinter{ - Out: params.Out, - Clock: fakeClock, - } - - nsp.PrintDescribeView(resourceModel) - - got := params.Out.(*bytes.Buffer).String() - want := ` -Name: namespace-2 -Labels: - app: foo - env: internal -Status: Active -` - - if diff := cmp.Diff(common.YamlString(want), common.YamlString(got), common.YamlStringTransformer); diff != "" { - t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) - } + assert.Equal(t, expectedNamespaceNames, namespaceNames, "Expected Namespace name does not match the found one") } From c7c406d14098c5358af0f2bf3e3a8773bd68abf6 Mon Sep 17 00:00:00 2001 From: Dennis Zhou Date: Thu, 18 Apr 2024 16:41:51 +0800 Subject: [PATCH 5/5] move tests to discoverer_test.go --- gwctl/go.mod | 2 - gwctl/pkg/printer/gatewayclasses_test.go | 56 ---- gwctl/pkg/printer/gateways_test.go | 78 ----- gwctl/pkg/printer/httproutes_test.go | 77 ----- gwctl/pkg/printer/namespace_test.go | 49 --- .../pkg/resourcediscovery/discoverer_test.go | 293 ++++++++++++++++++ 6 files changed, 293 insertions(+), 262 deletions(-) create mode 100644 gwctl/pkg/resourcediscovery/discoverer_test.go diff --git a/gwctl/go.mod b/gwctl/go.mod index adf94e7d42..a214da76fd 100644 --- a/gwctl/go.mod +++ b/gwctl/go.mod @@ -6,7 +6,6 @@ require ( github.com/evanphx/json-patch v5.9.0+incompatible github.com/google/go-cmp v0.6.0 github.com/spf13/cobra v1.8.0 - github.com/stretchr/testify v1.9.0 k8s.io/api v0.29.3 k8s.io/apiextensions-apiserver v0.29.3 k8s.io/apimachinery v0.29.3 @@ -42,7 +41,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.20.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect diff --git a/gwctl/pkg/printer/gatewayclasses_test.go b/gwctl/pkg/printer/gatewayclasses_test.go index 68157b11a1..cc06e6dc30 100644 --- a/gwctl/pkg/printer/gatewayclasses_test.go +++ b/gwctl/pkg/printer/gatewayclasses_test.go @@ -22,12 +22,10 @@ import ( "time" "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/assert" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" testingclock "k8s.io/utils/clock/testing" @@ -236,57 +234,3 @@ ControllerName: example.net/gateway-controller }) } } - -// TestGatewayClassesPrinter_LabelSelector Tests label selector filtering for GatewayClasses. -func TestGatewayClassesPrinter_LabelSelector(t *testing.T) { - fakeClock := testingclock.NewFakeClock(time.Now()) - - gatewayClass := func(name string, labels map[string]string) *gatewayv1.GatewayClass { - return &gatewayv1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: labels, - CreationTimestamp: metav1.Time{ - Time: fakeClock.Now().Add(-365 * 24 * time.Hour), - }, - }, - Spec: gatewayv1.GatewayClassSpec{ - ControllerName: gatewayv1.GatewayController(name + "/controller"), - }, - Status: gatewayv1.GatewayClassStatus{ - Conditions: []metav1.Condition{ - { - Type: "Accepted", - Status: metav1.ConditionTrue, - }, - }, - }, - } - } - objects := []runtime.Object{ - gatewayClass("foo-com-external-gateway-class", map[string]string{"app": "foo"}), - gatewayClass("foo-com-internal-gateway-class", map[string]string{"app": "foo", "env": "internal"}), - } - params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) - discoverer := resourcediscovery.Discoverer{ - K8sClients: params.K8sClients, - PolicyManager: params.PolicyManager, - } - labelSelector := "env=internal" - selector, err := labels.Parse(labelSelector) - if err != nil { - t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) - } - resourceModel, err := discoverer.DiscoverResourcesForGatewayClass(resourcediscovery.Filter{Labels: selector}) - if err != nil { - t.Fatalf("Failed to construct resourceModel: %v", resourceModel) - } - - expectedGatewayClassNames := []string{"foo-com-internal-gateway-class"} - gatewayClassNames := make([]string, 0, len(resourceModel.GatewayClasses)) - for _, gatewayClassNode := range resourceModel.GatewayClasses { - gatewayClassNames = append(gatewayClassNames, gatewayClassNode.GatewayClass.GetName()) - } - - assert.Equal(t, expectedGatewayClassNames, gatewayClassNames, "Expected GatewayClass name does not match the found one") -} diff --git a/gwctl/pkg/printer/gateways_test.go b/gwctl/pkg/printer/gateways_test.go index 538ac333e5..d34f106e4d 100644 --- a/gwctl/pkg/printer/gateways_test.go +++ b/gwctl/pkg/printer/gateways_test.go @@ -22,12 +22,10 @@ import ( "time" "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/assert" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" testingclock "k8s.io/utils/clock/testing" @@ -367,79 +365,3 @@ EffectivePolicies: t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) } } - -// TestGatewaysPrinter_LabelSelector tests label selector filtering for Gateways. -func TestGatewaysPrinter_LabelSelector(t *testing.T) { - fakeClock := testingclock.NewFakeClock(time.Now()) - gateway := func(name string, labels map[string]string) *gatewayv1.Gateway { - return &gatewayv1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: labels, - CreationTimestamp: metav1.Time{ - Time: fakeClock.Now().Add(-5 * 24 * time.Hour), - }, - }, - Spec: gatewayv1.GatewaySpec{ - GatewayClassName: "gatewayclass-1", - Listeners: []gatewayv1.Listener{ - { - Name: "http-8080", - Protocol: gatewayv1.HTTPProtocolType, - Port: gatewayv1.PortNumber(8080), - }, - }, - }, - Status: gatewayv1.GatewayStatus{ - Addresses: []gatewayv1.GatewayStatusAddress{ - { - Value: "192.168.100.5", - }, - }, - Conditions: []metav1.Condition{ - { - Type: "Programmed", - Status: "False", - }, - }, - }, - } - } - - objects := []runtime.Object{ - &gatewayv1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewayclass-1", - }, - Spec: gatewayv1.GatewayClassSpec{ - ControllerName: "example.net/gateway-controller", - Description: common.PtrTo("random"), - }, - }, - gateway("gateway-1", map[string]string{"app": "foo"}), - gateway("gateway-2", map[string]string{"app": "foo", "env": "internal"}), - } - - params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) - discoverer := resourcediscovery.Discoverer{ - K8sClients: params.K8sClients, - PolicyManager: params.PolicyManager, - } - labelSelector := "env=internal" - selector, err := labels.Parse(labelSelector) - if err != nil { - t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) - } - resourceModel, err := discoverer.DiscoverResourcesForGateway(resourcediscovery.Filter{Labels: selector}) - if err != nil { - t.Fatalf("Failed to construct resourceModel: %v", resourceModel) - } - - expectedGatewayNames := []string{"gateway-2"} - gatewayNames := make([]string, 0, len(resourceModel.Gateways)) - for _, gatewayNode := range resourceModel.Gateways { - gatewayNames = append(gatewayNames, gatewayNode.Gateway.GetName()) - } - - assert.Equal(t, expectedGatewayNames, gatewayNames, "Expected Gateway name does not match the found one") -} diff --git a/gwctl/pkg/printer/httproutes_test.go b/gwctl/pkg/printer/httproutes_test.go index 82d5c42693..e35ced4966 100644 --- a/gwctl/pkg/printer/httproutes_test.go +++ b/gwctl/pkg/printer/httproutes_test.go @@ -22,13 +22,11 @@ import ( "time" "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" testingclock "k8s.io/utils/clock/testing" @@ -437,78 +435,3 @@ EffectivePolicies: t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) } } - -// TestHTTPRoutesPrinter_LabelSelector tests label selector filtering for HTTPRoute. -func TestHTTPRoutesPrinter_LabelSelector(t *testing.T) { - fakeClock := testingclock.NewFakeClock(time.Now()) - httpRoute := func(name string, labels map[string]string) *gatewayv1.HTTPRoute { - return &gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: "default", - CreationTimestamp: metav1.Time{ - Time: fakeClock.Now().Add(-24 * time.Hour), - }, - Labels: labels, - }, - Spec: gatewayv1.HTTPRouteSpec{ - Hostnames: []gatewayv1.Hostname{"example.com"}, - CommonRouteSpec: gatewayv1.CommonRouteSpec{ - ParentRefs: []gatewayv1.ParentReference{ - { - Name: "gateway-1", - }, - }, - }, - }, - } - } - - objects := []runtime.Object{ - &gatewayv1.GatewayClass{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gatewayclass-1", - }, - Spec: gatewayv1.GatewayClassSpec{ - ControllerName: "example.net/gateway-controller", - Description: common.PtrTo("random"), - }, - }, - - &gatewayv1.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway-1", - Namespace: "default", - }, - Spec: gatewayv1.GatewaySpec{ - GatewayClassName: "gatewayclass-1", - }, - }, - httpRoute("httproute-1", map[string]string{"app": "foo"}), - httpRoute("httproute-2", map[string]string{"app": "foo", "env": "internal"}), - } - - params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) - discoverer := resourcediscovery.Discoverer{ - K8sClients: params.K8sClients, - PolicyManager: params.PolicyManager, - } - - labelSelector := "env=internal" - selector, err := labels.Parse(labelSelector) - if err != nil { - t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) - } - resourceModel, err := discoverer.DiscoverResourcesForHTTPRoute(resourcediscovery.Filter{Labels: selector}) - if err != nil { - t.Fatalf("Failed to discover resources: %v", err) - } - - expectedHTTPRouteNames := []string{"httproute-2"} - HTTPRouteNames := make([]string, 0, len(resourceModel.HTTPRoutes)) - for _, HTTPRouteNode := range resourceModel.HTTPRoutes { - HTTPRouteNames = append(HTTPRouteNames, HTTPRouteNode.HTTPRoute.GetName()) - } - - assert.Equal(t, expectedHTTPRouteNames, HTTPRouteNames, "Expected HTTPRoute name does not match the found one") -} diff --git a/gwctl/pkg/printer/namespace_test.go b/gwctl/pkg/printer/namespace_test.go index 409dbabd0d..6e8d7e56ee 100644 --- a/gwctl/pkg/printer/namespace_test.go +++ b/gwctl/pkg/printer/namespace_test.go @@ -22,13 +22,11 @@ import ( "time" "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" testingclock "k8s.io/utils/clock/testing" @@ -259,50 +257,3 @@ DirectlyAttachedPolicies: t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", got, want, diff) } } - -// TestNamespacesPrinter_LabelSelector tests label selector filtering for Namespaces. -func TestNamespacesPrinter_LabelSelector(t *testing.T) { - fakeClock := testingclock.NewFakeClock(time.Now()) - namespace := func(name string, labels map[string]string) *corev1.Namespace { - return &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - CreationTimestamp: metav1.Time{ - Time: fakeClock.Now().Add(-46 * 24 * time.Hour), - }, - Labels: labels, - }, - Status: corev1.NamespaceStatus{ - Phase: corev1.NamespaceActive, - }, - } - } - - objects := []runtime.Object{ - namespace("namespace-1", map[string]string{"app": "foo"}), - namespace("namespace-2", map[string]string{"app": "foo", "env": "internal"}), - } - - params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) - discoverer := resourcediscovery.Discoverer{ - K8sClients: params.K8sClients, - PolicyManager: params.PolicyManager, - } - labelSelector := "env=internal" - selector, err := labels.Parse(labelSelector) - if err != nil { - t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) - } - resourceModel, err := discoverer.DiscoverResourcesForNamespace(resourcediscovery.Filter{Labels: selector}) - if err != nil { - t.Fatalf("Failed to construct resourceModel: %v", resourceModel) - } - - expectedNamespaceNames := []string{"namespace-2"} - namespaceNames := make([]string, 0, len(resourceModel.Namespaces)) - for _, namespaceNode := range resourceModel.Namespaces { - namespaceNames = append(namespaceNames, namespaceNode.Namespace.Name) - } - - assert.Equal(t, expectedNamespaceNames, namespaceNames, "Expected Namespace name does not match the found one") -} diff --git a/gwctl/pkg/resourcediscovery/discoverer_test.go b/gwctl/pkg/resourcediscovery/discoverer_test.go new file mode 100644 index 0000000000..8499092390 --- /dev/null +++ b/gwctl/pkg/resourcediscovery/discoverer_test.go @@ -0,0 +1,293 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resourcediscovery + +import ( + corev1 "k8s.io/api/core/v1" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + testingclock "k8s.io/utils/clock/testing" + + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/gwctl/pkg/common" + "sigs.k8s.io/gateway-api/gwctl/pkg/utils" +) + +// TestDiscoverResourcesForGatewayClass_LabelSelector Tests label selector filtering for GatewayClasses. +func TestDiscoverResourcesForGatewayClass_LabelSelector(t *testing.T) { + fakeClock := testingclock.NewFakeClock(time.Now()) + + gatewayClass := func(name string, labels map[string]string) *gatewayv1.GatewayClass { + return &gatewayv1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + CreationTimestamp: metav1.Time{ + Time: fakeClock.Now().Add(-365 * 24 * time.Hour), + }, + }, + Spec: gatewayv1.GatewayClassSpec{ + ControllerName: gatewayv1.GatewayController(name + "/controller"), + }, + Status: gatewayv1.GatewayClassStatus{ + Conditions: []metav1.Condition{ + { + Type: "Accepted", + Status: metav1.ConditionTrue, + }, + }, + }, + } + } + objects := []runtime.Object{ + gatewayClass("foo-com-external-gateway-class", map[string]string{"app": "foo"}), + gatewayClass("foo-com-internal-gateway-class", map[string]string{"app": "foo", "env": "internal"}), + } + params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) + discoverer := Discoverer{ + K8sClients: params.K8sClients, + PolicyManager: params.PolicyManager, + } + labelSelector := "env=internal" + selector, err := labels.Parse(labelSelector) + if err != nil { + t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + } + resourceModel, err := discoverer.DiscoverResourcesForGatewayClass(Filter{Labels: selector}) + if err != nil { + t.Fatalf("Failed to construct resourceModel: %v", resourceModel) + } + + expectedGatewayClassNames := []string{"foo-com-internal-gateway-class"} + gatewayClassNames := make([]string, 0, len(resourceModel.GatewayClasses)) + for _, gatewayClassNode := range resourceModel.GatewayClasses { + gatewayClassNames = append(gatewayClassNames, gatewayClassNode.GatewayClass.GetName()) + } + if diff := cmp.Diff(expectedGatewayClassNames, gatewayClassNames); diff != "" { + t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", gatewayClassNames, expectedGatewayClassNames, diff) + } +} + +// TestDiscoverResourcesForGateway_LabelSelector tests label selector filtering for Gateways. +func TestDiscoverResourcesForGateway_LabelSelector(t *testing.T) { + fakeClock := testingclock.NewFakeClock(time.Now()) + gateway := func(name string, labels map[string]string) *gatewayv1.Gateway { + return &gatewayv1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + CreationTimestamp: metav1.Time{ + Time: fakeClock.Now().Add(-5 * 24 * time.Hour), + }, + }, + Spec: gatewayv1.GatewaySpec{ + GatewayClassName: "gatewayclass-1", + Listeners: []gatewayv1.Listener{ + { + Name: "http-8080", + Protocol: gatewayv1.HTTPProtocolType, + Port: gatewayv1.PortNumber(8080), + }, + }, + }, + Status: gatewayv1.GatewayStatus{ + Addresses: []gatewayv1.GatewayStatusAddress{ + { + Value: "192.168.100.5", + }, + }, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: "False", + }, + }, + }, + } + } + + objects := []runtime.Object{ + &gatewayv1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gatewayclass-1", + }, + Spec: gatewayv1.GatewayClassSpec{ + ControllerName: "example.net/gateway-controller", + Description: common.PtrTo("random"), + }, + }, + gateway("gateway-1", map[string]string{"app": "foo"}), + gateway("gateway-2", map[string]string{"app": "foo", "env": "internal"}), + } + + params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) + discoverer := Discoverer{ + K8sClients: params.K8sClients, + PolicyManager: params.PolicyManager, + } + labelSelector := "env=internal" + selector, err := labels.Parse(labelSelector) + if err != nil { + t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + } + resourceModel, err := discoverer.DiscoverResourcesForGateway(Filter{Labels: selector}) + if err != nil { + t.Fatalf("Failed to construct resourceModel: %v", resourceModel) + } + + expectedGatewayNames := []string{"gateway-2"} + gatewayNames := make([]string, 0, len(resourceModel.Gateways)) + for _, gatewayNode := range resourceModel.Gateways { + gatewayNames = append(gatewayNames, gatewayNode.Gateway.GetName()) + } + + if diff := cmp.Diff(expectedGatewayNames, gatewayNames); diff != "" { + t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", gatewayNames, expectedGatewayNames, diff) + } +} + +// TestDiscoverResourcesForHTTPRoute_LabelSelector tests label selector filtering for HTTPRoute. +func TestDiscoverResourcesForHTTPRoute_LabelSelector(t *testing.T) { + fakeClock := testingclock.NewFakeClock(time.Now()) + httpRoute := func(name string, labels map[string]string) *gatewayv1.HTTPRoute { + return &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "default", + CreationTimestamp: metav1.Time{ + Time: fakeClock.Now().Add(-24 * time.Hour), + }, + Labels: labels, + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"example.com"}, + CommonRouteSpec: gatewayv1.CommonRouteSpec{ + ParentRefs: []gatewayv1.ParentReference{ + { + Name: "gateway-1", + }, + }, + }, + }, + } + } + + objects := []runtime.Object{ + &gatewayv1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gatewayclass-1", + }, + Spec: gatewayv1.GatewayClassSpec{ + ControllerName: "example.net/gateway-controller", + Description: common.PtrTo("random"), + }, + }, + + &gatewayv1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway-1", + Namespace: "default", + }, + Spec: gatewayv1.GatewaySpec{ + GatewayClassName: "gatewayclass-1", + }, + }, + httpRoute("httproute-1", map[string]string{"app": "foo"}), + httpRoute("httproute-2", map[string]string{"app": "foo", "env": "internal"}), + } + + params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) + discoverer := Discoverer{ + K8sClients: params.K8sClients, + PolicyManager: params.PolicyManager, + } + + labelSelector := "env=internal" + selector, err := labels.Parse(labelSelector) + if err != nil { + t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + } + resourceModel, err := discoverer.DiscoverResourcesForHTTPRoute(Filter{Labels: selector}) + if err != nil { + t.Fatalf("Failed to discover resources: %v", err) + } + + expectedHTTPRouteNames := []string{"httproute-2"} + HTTPRouteNames := make([]string, 0, len(resourceModel.HTTPRoutes)) + for _, HTTPRouteNode := range resourceModel.HTTPRoutes { + HTTPRouteNames = append(HTTPRouteNames, HTTPRouteNode.HTTPRoute.GetName()) + } + + if diff := cmp.Diff(expectedHTTPRouteNames, HTTPRouteNames); diff != "" { + t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", expectedHTTPRouteNames, HTTPRouteNames, diff) + } +} + +// TestDiscoverResourcesForNamespace_LabelSelector tests label selector filtering for Namespaces. +func TestDiscoverResourcesForNamespace_LabelSelector(t *testing.T) { + fakeClock := testingclock.NewFakeClock(time.Now()) + namespace := func(name string, labels map[string]string) *corev1.Namespace { + return &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + CreationTimestamp: metav1.Time{ + Time: fakeClock.Now().Add(-46 * 24 * time.Hour), + }, + Labels: labels, + }, + Status: corev1.NamespaceStatus{ + Phase: corev1.NamespaceActive, + }, + } + } + + objects := []runtime.Object{ + namespace("namespace-1", map[string]string{"app": "foo"}), + namespace("namespace-2", map[string]string{"app": "foo", "env": "internal"}), + } + + params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...)) + discoverer := Discoverer{ + K8sClients: params.K8sClients, + PolicyManager: params.PolicyManager, + } + labelSelector := "env=internal" + selector, err := labels.Parse(labelSelector) + if err != nil { + t.Errorf("Unable to find resources that match the label selector \"%s\": %v\n", labelSelector, err) + } + resourceModel, err := discoverer.DiscoverResourcesForNamespace(Filter{Labels: selector}) + if err != nil { + t.Fatalf("Failed to construct resourceModel: %v", resourceModel) + } + + expectedNamespaceNames := []string{"namespace-2"} + namespaceNames := make([]string, 0, len(resourceModel.Namespaces)) + for _, namespaceNode := range resourceModel.Namespaces { + namespaceNames = append(namespaceNames, namespaceNode.Namespace.Name) + } + + if diff := cmp.Diff(expectedNamespaceNames, namespaceNames); diff != "" { + t.Errorf("Unexpected diff\ngot=\n%v\nwant=\n%v\ndiff (-want +got)=\n%v", expectedNamespaceNames, namespaceNames, diff) + } +}