Skip to content

Commit

Permalink
Implement gwctl get gatewayclass (kubernetes-sigs#2847)
Browse files Browse the repository at this point in the history
  • Loading branch information
jongwooo authored and hanxiaop committed Mar 13, 2024
1 parent 13ec455 commit d0cb3fc
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 7 deletions.
17 changes: 15 additions & 2 deletions gwctl/pkg/cmd/get/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func NewGetCommand(params *utils.CmdParams) *cobra.Command {
flags := &getFlags{}

cmd := &cobra.Command{
Use: "get {gateways|policies|policycrds|httproutes}",
Use: "get {gateways|gatewayclasses|policies|policycrds|httproutes}",
Short: "Display one or many resources",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
Expand All @@ -64,7 +64,8 @@ func runGet(args []string, params *utils.CmdParams, flags *getFlags) {
}
realClock := clock.RealClock{}
gwPrinter := &printer.GatewaysPrinter{Out: params.Out, Clock: realClock}
policiesPrinter := &printer.PoliciesPrinter{Out: params.Out, Clock: realClock}
gwcPrinter := &printer.GatewayClassesPrinter{Out: params.Out, Clock: realClock}
policiesPrinter := &printer.PoliciesPrinter{Out: params.Out}
httpRoutesPrinter := &printer.HTTPRoutesPrinter{Out: params.Out, Clock: realClock}

switch kind {
Expand All @@ -79,6 +80,18 @@ func runGet(args []string, params *utils.CmdParams, flags *getFlags) {
}
gwPrinter.Print(resourceModel)

case "gatewayclass", "gatewayclasses":
filter := resourcediscovery.Filter{Namespace: ns}
if len(args) > 1 {
filter.Name = args[1]
}
resourceModel, err := discoverer.DiscoverResourcesForGatewayClass(filter)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to discover GatewayClass resources: %v\n", err)
os.Exit(1)
}
gwcPrinter.Print(resourceModel)

case "policy", "policies":
list := params.PolicyManager.GetPolicies()
policiesPrinter.Print(list)
Expand Down
50 changes: 47 additions & 3 deletions gwctl/pkg/printer/gatewayclasses.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,21 @@ package printer
import (
"fmt"
"io"

"sigs.k8s.io/yaml"
"sort"
"strings"
"text/tabwriter"

"sigs.k8s.io/gateway-api/gwctl/pkg/policymanager"
"sigs.k8s.io/gateway-api/gwctl/pkg/resourcediscovery"
"sigs.k8s.io/yaml"

"k8s.io/apimachinery/pkg/util/duration"
"k8s.io/utils/clock"
)

type GatewayClassesPrinter struct {
Out io.Writer
Out io.Writer
Clock clock.Clock
}

type gatewayClassDescribeView struct {
Expand All @@ -39,6 +45,44 @@ type gatewayClassDescribeView struct {
DirectlyAttachedPolicies []policymanager.ObjRef `json:",omitempty"`
}

func (gcp *GatewayClassesPrinter) Print(model *resourcediscovery.ResourceModel) {
tw := tabwriter.NewWriter(gcp.Out, 0, 0, 2, ' ', 0)
row := []string{"NAME", "CONTROLLER", "ACCEPTED", "AGE"}
tw.Write([]byte(strings.Join(row, "\t") + "\n"))

gatewayClassNodes := make([]*resourcediscovery.GatewayClassNode, 0, len(model.GatewayClasses))
for _, gatewayClassNode := range model.GatewayClasses {
gatewayClassNodes = append(gatewayClassNodes, gatewayClassNode)
}

sort.Slice(gatewayClassNodes, func(i, j int) bool {
if gatewayClassNodes[i].GatewayClass.GetName() != gatewayClassNodes[j].GatewayClass.GetName() {
return gatewayClassNodes[i].GatewayClass.GetName() < gatewayClassNodes[j].GatewayClass.GetName()
}
return string(gatewayClassNodes[i].GatewayClass.Spec.ControllerName) < string(gatewayClassNodes[j].GatewayClass.Spec.ControllerName)
})

for _, gatewayClassNode := range gatewayClassNodes {
accepted := "Unknown"
for _, condition := range gatewayClassNode.GatewayClass.Status.Conditions {
if condition.Type == "Accepted" {
accepted = string(condition.Status)
}
}

age := duration.HumanDuration(gcp.Clock.Since(gatewayClassNode.GatewayClass.GetCreationTimestamp().Time))

row := []string{
gatewayClassNode.GatewayClass.GetName(),
string(gatewayClassNode.GatewayClass.Spec.ControllerName),
accepted,
age,
}
tw.Write([]byte(strings.Join(row, "\t") + "\n"))
}
tw.Flush()
}

func (gcp *GatewayClassesPrinter) PrintDescribeView(resourceModel *resourcediscovery.ResourceModel) {
index := 0
for _, gatewayClassNode := range resourceModel.GatewayClasses {
Expand Down
99 changes: 97 additions & 2 deletions gwctl/pkg/printer/gatewayclasses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,115 @@ package printer
import (
"bytes"
"testing"
"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/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/cmd/utils"
"sigs.k8s.io/gateway-api/gwctl/pkg/common"
"sigs.k8s.io/gateway-api/gwctl/pkg/resourcediscovery"
)

func TestGatewayClassesPrinter_Print(t *testing.T) {
fakeClock := testingclock.NewFakeClock(time.Now())
objects := []runtime.Object{
&gatewayv1.GatewayClass{
ObjectMeta: metav1.ObjectMeta{
Name: "bar-com-internal-gateway-class",
CreationTimestamp: metav1.Time{
Time: fakeClock.Now().Add(-365 * 24 * time.Hour),
},
},
Spec: gatewayv1.GatewayClassSpec{
ControllerName: "bar.baz/internal-gateway-class",
},
Status: gatewayv1.GatewayClassStatus{
Conditions: []metav1.Condition{
{
Type: "Accepted",
Status: "True",
},
},
},
},
&gatewayv1.GatewayClass{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-com-external-gateway-class",
CreationTimestamp: metav1.Time{
Time: fakeClock.Now().Add(-100 * 24 * time.Hour),
},
},
Spec: gatewayv1.GatewayClassSpec{
ControllerName: "foo.com/external-gateway-class",
},
Status: gatewayv1.GatewayClassStatus{
Conditions: []metav1.Condition{
{
Type: "Accepted",
Status: "False",
},
},
},
},
&gatewayv1.GatewayClass{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-com-internal-gateway-class",
CreationTimestamp: metav1.Time{
Time: fakeClock.Now().Add(-24 * time.Minute),
},
},
Spec: gatewayv1.GatewayClassSpec{
ControllerName: "foo.com/internal-gateway-class",
},
Status: gatewayv1.GatewayClassStatus{
Conditions: []metav1.Condition{
{
Type: "Accepted",
Status: "Unknown",
},
},
},
},
}

params := utils.MustParamsForTest(t, common.MustClientsForTest(t, objects...))
discoverer := resourcediscovery.Discoverer{
K8sClients: params.K8sClients,
PolicyManager: params.PolicyManager,
}
resourceModel, err := discoverer.DiscoverResourcesForGatewayClass(resourcediscovery.Filter{})
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
bar-com-internal-gateway-class bar.baz/internal-gateway-class True 365d
foo-com-external-gateway-class foo.com/external-gateway-class False 100d
foo-com-internal-gateway-class foo.com/internal-gateway-class Unknown 24m
`
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)
}
}

func TestGatewayClassesPrinter_PrintDescribeView(t *testing.T) {
fakeClock := testingclock.NewFakeClock(time.Now())
objects := []runtime.Object{
&gatewayv1.GatewayClass{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -89,7 +183,8 @@ func TestGatewayClassesPrinter_PrintDescribeView(t *testing.T) {
}

gcp := &GatewayClassesPrinter{
Out: params.Out,
Out: params.Out,
Clock: fakeClock,
}
gcp.PrintDescribeView(resourceModel)

Expand Down

0 comments on commit d0cb3fc

Please sign in to comment.