From a7c00e461f86bc489da7742f766e5c2f5d6aa373 Mon Sep 17 00:00:00 2001 From: Nathan Coleman Date: Tue, 26 Nov 2024 20:38:57 +0000 Subject: [PATCH] backport of commit 2775ebb9c01bea958f8825d1db9159e81bdc0398 --- cli/cmd/gateway/read/command.go | 15 ++- cli/cmd/gateway/read/command_test.go | 157 +++++++++++++++++++++++++++ cli/go.mod | 7 +- cli/go.sum | 6 + 4 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 cli/cmd/gateway/read/command_test.go diff --git a/cli/cmd/gateway/read/command.go b/cli/cmd/gateway/read/command.go index 604715bdc7..f7ec42a6b0 100644 --- a/cli/cmd/gateway/read/command.go +++ b/cli/cmd/gateway/read/command.go @@ -131,7 +131,8 @@ func (c *Command) fetchCRDs() error { return fmt.Errorf("error fetching GatewayClass CRD: %w", err) } - //// Fetch GatewayClassConfig referenced by GatewayClass + // FUTURE Fetch GatewayClassConfig referenced by GatewayClass + // This import requires resolving hairy dependency discrepancies between modules in this repo //var gatewayClassConfig v1alpha1.GatewayClassConfig //if err := c.kubernetes.Get(context.Background(), client.ObjectKey{Namespace: "", Name: gatewayClass.Spec.ParametersRef.Name}, &gatewayClassConfig); err != nil { // return fmt.Errorf("error fetching GatewayClassConfig CRD: %w", err) @@ -149,8 +150,8 @@ func (c *Command) fetchCRDs() error { return fmt.Errorf("error fetching TCPRoute CRDs: %w", err) } - // Fetch MeshServices referenced by HTTPRoutes or TCPRoutes - // FUTURE Filter to those referenced by HTTPRoutes or TCPRoutes instead of listing all + // FUTURE Fetch MeshServices referenced by HTTPRoutes or TCPRoutes + // This import requires resolving hairy dependency discrepancies between modules in this repo // var meshServices v1alpha1.MeshServiceList // if err := c.kubernetes.List(context.Background(), &meshServices); err != nil { // return fmt.Errorf("error fetching MeshService CRDs: %w", err) @@ -280,15 +281,17 @@ func (c *Command) initKubernetes() (err error) { if c.kubernetes, err = client.New(c.restConfig, client.Options{}); err != nil { return fmt.Errorf("error creating controller-runtime client: %w", err) } - // TODO Fix dependency issues introduced by registering v1alpha1 - // _ = v1alpha1.AddToScheme(c.kubernetes.Scheme()) + // FUTURE Fix dependency discrepancies between modules in this repo so that this scheme can be added (see above) + //_ = v1alpha1.AddToScheme(c.kubernetes.Scheme()) _ = gwv1alpha2.AddToScheme(c.kubernetes.Scheme()) _ = gwv1beta1.AddToScheme(c.kubernetes.Scheme()) } // If no namespace was specified, use the one from the kube context if c.flagGatewayNamespace == "" { - c.UI.Output("No namespace specified, using current kube context namespace: %s", settings.Namespace()) + if c.flagOutput != "json" { + c.UI.Output("No namespace specified, using current kube context namespace: %s", settings.Namespace()) + } c.flagGatewayNamespace = settings.Namespace() } diff --git a/cli/cmd/gateway/read/command_test.go b/cli/cmd/gateway/read/command_test.go new file mode 100644 index 0000000000..9c8ab59feb --- /dev/null +++ b/cli/cmd/gateway/read/command_test.go @@ -0,0 +1,157 @@ +package read + +import ( + "bytes" + "context" + "encoding/json" + "io" + "os" + "testing" + + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + gwv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/terminal" +) + +func TestFlagParsing(t *testing.T) { + cases := map[string]struct { + args []string + out int + }{ + "No args": { + args: []string{}, + out: 1, + }, + "Multiple gateway names passed": { + args: []string{"gateway-1", "gateway-2"}, + out: 1, + }, + "Nonexistent flag passed, -foo bar": { + args: []string{"gateway-1", "-foo", "bar"}, + out: 1, + }, + "Invalid argument passed, -namespace YOLO": { + args: []string{"gateway-1", "-namespace", "YOLO"}, + out: 1, + }, + "User passed incorrect output": { + args: []string{"gateway-1", "-output", "image"}, + out: 1, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + c := setupCommand(new(bytes.Buffer)) + c.kubernetes = fake.NewClientBuilder().WithObjectTracker(nil).Build() + + out := c.Run(tc.args) + require.Equal(t, tc.out, out) + }) + } +} + +func TestReadCommandOutput(t *testing.T) { + gatewayClassName := "gateway-class-1" + gatewayName := "gateway-1" + routeName := "route-1" + + fakeGatewayClass := &gwv1beta1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: gatewayClassName, + }, + } + + fakeGateway := &gwv1beta1.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: gatewayName, + }, + Spec: gwv1beta1.GatewaySpec{ + GatewayClassName: gwv1beta1.ObjectName(gatewayClassName), + }, + } + + fakeHTTPRoute := &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: routeName, + }, + Spec: gwv1beta1.HTTPRouteSpec{ + CommonRouteSpec: gwv1beta1.CommonRouteSpec{ + ParentRefs: []gwv1beta1.ParentReference{ + { + Name: gwv1beta1.ObjectName(fakeGateway.Name), + }, + }, + }, + }, + } + + fakeUnattachedHTTPRoute := &gwv1beta1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "route-2", + }, + } + + buf := new(bytes.Buffer) + c := setupCommand(buf) + + scheme := scheme.Scheme + gwv1beta1.AddToScheme(scheme) + gwv1alpha2.AddToScheme(scheme) + + c.kubernetes = fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(fakeGatewayClass, fakeGateway, fakeHTTPRoute, fakeUnattachedHTTPRoute). + Build() + + out := c.Run([]string{gatewayName, "-output", "json"}) + require.Equal(t, 0, out) + + gatewayWithRoutes := struct { + Gateway gwv1beta1.Gateway `json:"Gateway"` + GatewayClass gwv1beta1.GatewayClass `json:"GatewayClass"` + HTTPRoutes []gwv1beta1.HTTPRoute `json:"HTTPRoutes"` + }{} + require.NoErrorf(t, json.Unmarshal(buf.Bytes(), &gatewayWithRoutes), "failed to parse JSON output %s", buf.String()) + + // Make gateway assertions + assert.Equal(t, gatewayName, gatewayWithRoutes.Gateway.Name) + + // Make gateway class assertions + assert.Equal(t, gatewayClassName, gatewayWithRoutes.GatewayClass.Name) + + // Make http route assertions + require.Len(t, gatewayWithRoutes.HTTPRoutes, 1) + assert.Equal(t, routeName, gatewayWithRoutes.HTTPRoutes[0].Name) +} + +func setupCommand(buf io.Writer) *Command { + // Log at a test level to standard out. + log := hclog.New(&hclog.LoggerOptions{ + Name: "test", + Level: hclog.Debug, + Output: os.Stdout, + }) + + // Setup and initialize the command struct + command := &Command{ + BaseCommand: &common.BaseCommand{ + Log: log, + UI: terminal.NewUI(context.Background(), buf), + }, + } + command.init() + + return command +} diff --git a/cli/go.mod b/cli/go.mod index 8964ed12fc..91b2a41253 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -26,12 +26,12 @@ require ( golang.org/x/text v0.17.0 helm.sh/helm/v3 v3.14.4 k8s.io/api v0.31.0 - k8s.io/apiextensions-apiserver v0.29.0 + k8s.io/apiextensions-apiserver v0.31.0 k8s.io/apimachinery v0.31.0 k8s.io/cli-runtime v0.29.0 k8s.io/client-go v0.31.0 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 - sigs.k8s.io/controller-runtime v0.16.5 + sigs.k8s.io/controller-runtime v0.19.2 sigs.k8s.io/gateway-api v0.7.1 sigs.k8s.io/yaml v1.4.0 ) @@ -74,6 +74,7 @@ require ( github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect @@ -92,6 +93,7 @@ require ( github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect @@ -179,6 +181,7 @@ require ( golang.org/x/sys v0.24.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/time v0.5.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.65.0 // indirect diff --git a/cli/go.sum b/cli/go.sum index 5a6a1d4bf7..43fb076286 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -835,6 +835,8 @@ k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= +k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= +k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= @@ -857,6 +859,10 @@ oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= sigs.k8s.io/controller-runtime v0.16.5 h1:yr1cEJbX08xsTW6XEIzT13KHHmIyX8Umvme2cULvFZw= sigs.k8s.io/controller-runtime v0.16.5/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/controller-runtime v0.17.6 h1:12IXsozEsIXWAMRpgRlYS1jjAHQXHtWEOMdULh3DbEw= +sigs.k8s.io/controller-runtime v0.17.6/go.mod h1:N0jpP5Lo7lMTF9aL56Z/B2oWBJjey6StQM0jRbKQXtY= +sigs.k8s.io/controller-runtime v0.19.2 h1:3sPrF58XQEPzbE8T81TN6selQIMGbtYwuaJ6eDssDF8= +sigs.k8s.io/controller-runtime v0.19.2/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/gateway-api v0.7.1 h1:Tts2jeepVkPA5rVG/iO+S43s9n7Vp7jCDhZDQYtPigQ= sigs.k8s.io/gateway-api v0.7.1/go.mod h1:Xv0+ZMxX0lu1nSSDIIPEfbVztgNZ+3cfiYrJsa2Ooso= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=