Skip to content

Commit

Permalink
Add label filtering (-l) for describe commands (#2934)
Browse files Browse the repository at this point in the history
* Add label filtering (-l) for describe commands

* fix nits

* fix test

* refactor test

* move tests to discoverer_test.go
  • Loading branch information
deszhou authored Apr 18, 2024
1 parent 90e22fe commit 6b93e6e
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 295 deletions.
47 changes: 43 additions & 4 deletions gwctl/cmd/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -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|policycrd} RESOURCE_NAME",
Expand All @@ -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", "", "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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -112,7 +121,15 @@ func runDescribe(cmd *cobra.Command, args []string, params *utils.CmdParams) {
policiesPrinter.PrintPolicyCRDsDescribeView(policyCrdList)

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]
}
Expand All @@ -124,7 +141,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]
}
Expand All @@ -136,7 +161,14 @@ 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{
Labels: selector,
}
if len(args) > 1 {
filter.Name = args[1]
}
Expand All @@ -162,7 +194,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]
}
Expand Down
2 changes: 1 addition & 1 deletion gwctl/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
62 changes: 0 additions & 62 deletions gwctl/pkg/printer/gatewayclasses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
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"

Expand Down Expand Up @@ -251,64 +250,3 @@ Status: {}
})
}
}

// TestGatewayClassesPrinter_LabelSelector Tests label selector filtering for GatewayClasses in 'get' command.
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)
}

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)
}
}
86 changes: 1 addition & 85 deletions gwctl/pkg/printer/gateways_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ import (
"time"

"github.com/google/go-cmp/cmp"

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"

Expand Down Expand Up @@ -365,87 +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 in 'get' command.
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)
}

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)
}
}
90 changes: 3 additions & 87 deletions gwctl/pkg/printer/httproutes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@ 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"

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"

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"
Expand Down Expand Up @@ -436,86 +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 in 'get' command.
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)
}

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)
}
}
Loading

0 comments on commit 6b93e6e

Please sign in to comment.