Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gwctl: Show Events, Analysis and few other fields for various resources. #3051

Merged
2 changes: 1 addition & 1 deletion gwctl/cmd/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func runDescribe(cmd *cobra.Command, args []string, params *utils.CmdParams) {
httpRoutesPrinter := &printer.HTTPRoutesPrinter{Writer: params.Out, Clock: clock.RealClock{}}
gwPrinter := &printer.GatewaysPrinter{Writer: params.Out, Clock: clock.RealClock{}}
gwcPrinter := &printer.GatewayClassesPrinter{Writer: params.Out, Clock: clock.RealClock{}}
backendsPrinter := &printer.BackendsPrinter{Writer: params.Out}
backendsPrinter := &printer.BackendsPrinter{Writer: params.Out, Clock: clock.RealClock{}}
namespacesPrinter := &printer.NamespacesPrinter{Writer: params.Out, Clock: clock.RealClock{}}

switch kind {
Expand Down
8 changes: 5 additions & 3 deletions gwctl/pkg/policymanager/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ limitations under the License.

package policymanager

import "sigs.k8s.io/gateway-api/gwctl/pkg/common"

// ToPolicyRefs returns the Object references of all given policies. Note that
// these are not the value of targetRef within the Policies but rather the
// reference to the Policy object itself.
func ToPolicyRefs(policies []Policy) []ObjRef {
var result []ObjRef
func ToPolicyRefs(policies []Policy) []common.ObjRef {
var result []common.ObjRef
for _, policy := range policies {
result = append(result, ObjRef{
result = append(result, common.ObjRef{
Group: policy.Unstructured().GroupVersionKind().Group,
Kind: policy.Unstructured().GroupVersionKind().Kind,
Name: policy.Unstructured().GetName(),
Expand Down
18 changes: 6 additions & 12 deletions gwctl/pkg/policymanager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"k8s.io/client-go/dynamic"

gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
"sigs.k8s.io/gateway-api/gwctl/pkg/common"
)

type PolicyManager struct {
Expand Down Expand Up @@ -78,7 +79,7 @@ func (p *PolicyManager) Init(ctx context.Context) error {
return nil
}

func (p *PolicyManager) PoliciesAttachedTo(objRef ObjRef) []Policy {
func (p *PolicyManager) PoliciesAttachedTo(objRef common.ObjRef) []Policy {
var result []Policy
for _, policy := range p.policies {
if policy.IsAttachedTo(objRef) {
Expand Down Expand Up @@ -218,21 +219,14 @@ type Policy struct {
// targetRef references the target object this policy is attached to. This
// only makes sense in case of a directly-attached-policy, or an
// unmerged-inherited-policy.
targetRef ObjRef
targetRef common.ObjRef
// Indicates whether the policy is supposed to be "inherited" (as opposed to
// "direct").
inherited bool
}

func (p Policy) ClientObject() client.Object { return p.Unstructured() }

type ObjRef struct {
Group string `json:",omitempty"`
Kind string `json:",omitempty"`
Name string `json:",omitempty"`
Namespace string `json:",omitempty"`
}

func PolicyFromUnstructured(u unstructured.Unstructured, policyCRDs map[PolicyCrdID]PolicyCRD) (Policy, error) {
result := Policy{u: u}

Expand All @@ -248,7 +242,7 @@ func PolicyFromUnstructured(u unstructured.Unstructured, policyCRDs map[PolicyCr
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), structuredPolicy); err != nil {
return Policy{}, fmt.Errorf("failed to convert unstructured policy resource to structured: %v", err)
}
result.targetRef = ObjRef{
result.targetRef = common.ObjRef{
Group: string(structuredPolicy.Spec.TargetRef.Group),
Kind: string(structuredPolicy.Spec.TargetRef.Kind),
Name: string(structuredPolicy.Spec.TargetRef.Name),
Expand Down Expand Up @@ -280,7 +274,7 @@ func (p Policy) PolicyCrdID() PolicyCrdID {
return PolicyCrdID(p.u.GetObjectKind().GroupVersionKind().Kind + "." + p.u.GetObjectKind().GroupVersionKind().Group)
}

func (p Policy) TargetRef() ObjRef {
func (p Policy) TargetRef() common.ObjRef {
return p.targetRef
}

Expand All @@ -292,7 +286,7 @@ func (p Policy) IsDirect() bool {
return !p.inherited
}

func (p Policy) IsAttachedTo(objRef ObjRef) bool {
func (p Policy) IsAttachedTo(objRef common.ObjRef) bool {
if p.targetRef.Kind == "Namespace" && p.targetRef.Name == "" {
p.targetRef.Name = "default"
}
Expand Down
4 changes: 3 additions & 1 deletion gwctl/pkg/policymanager/merger.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (

jsonpatch "github.com/evanphx/json-patch"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

"sigs.k8s.io/gateway-api/gwctl/pkg/common"
)

// MergePoliciesOfSimilarKind will convert a slice a policies to a map of
Expand Down Expand Up @@ -147,7 +149,7 @@ func mergePolicy(parent, child Policy) (Policy, error) {
result.u.SetUnstructuredContent(resultUnstructured)
// Merging two policies means the targetRef no longer makes any sense since
// since they can be conflicting. So we unset the targetRef.
result.targetRef = ObjRef{}
result.targetRef = common.ObjRef{}
return result, nil
}

Expand Down
96 changes: 51 additions & 45 deletions gwctl/pkg/printer/backends.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,13 @@ package printer
import (
"fmt"
"io"
"os"
"strings"
"text/tabwriter"

"golang.org/x/exp/maps"
"k8s.io/apimachinery/pkg/util/duration"
"k8s.io/utils/clock"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"

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

Expand All @@ -39,12 +35,9 @@ type BackendsPrinter struct {
}

func (bp *BackendsPrinter) Print(resourceModel *resourcediscovery.ResourceModel) {
tw := tabwriter.NewWriter(bp, 0, 0, 2, ' ', 0)
row := []string{"NAMESPACE", "NAME", "TYPE", "REFERRED BY ROUTES", "AGE", "POLICIES"}
_, err := tw.Write([]byte(strings.Join(row, "\t") + "\n"))
if err != nil {
fmt.Fprintf(os.Stderr, "failed to write to the tab writer: %v\n", err)
os.Exit(1)
table := &Table{
ColumnNames: []string{"NAMESPACE", "NAME", "TYPE", "REFERRED BY ROUTES", "AGE", "POLICIES"},
UseSeparator: false,
}

backends := maps.Values(resourceModel.Backends)
Expand Down Expand Up @@ -90,58 +83,71 @@ func (bp *BackendsPrinter) Print(resourceModel *resourcediscovery.ResourceModel)
age,
policiesCount,
}
_, err := tw.Write([]byte(strings.Join(row, "\t") + "\n"))
if err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
table.Rows = append(table.Rows, row)
}

tw.Flush()
}

type backendDescribeView struct {
Group string `json:",omitempty"`
Kind string `json:",omitempty"`
Name string `json:",omitempty"`
Namespace string `json:",omitempty"`
DirectlyAttachedPolicies []policymanager.ObjRef `json:",omitempty"`
EffectivePolicies any `json:",omitempty"`
table.Write(bp, 0)
}

func (bp *BackendsPrinter) PrintDescribeView(resourceModel *resourcediscovery.ResourceModel) {
index := 0
for _, backendNode := range resourceModel.Backends {
index++

views := []backendDescribeView{
{
Group: backendNode.Backend.GroupVersionKind().Group,
Kind: backendNode.Backend.GroupVersionKind().Kind,
Name: backendNode.Backend.GetName(),
Namespace: backendNode.Backend.GetNamespace(),
},
backend := backendNode.Backend.DeepCopy()
backend.SetLabels(nil)
backend.SetAnnotations(nil)

pairs := []*DescriberKV{
{Key: "Name", Value: backendNode.Backend.GetName()},
{Key: "Namespace", Value: backendNode.Backend.GetNamespace()},
{Key: "Labels", Value: backendNode.Backend.GetLabels()},
{Key: "Annotations", Value: backendNode.Backend.GetAnnotations()},
{Key: "Backend", Value: backend},
}
if policyRefs := resourcediscovery.ConvertPoliciesMapToPolicyRefs(backendNode.Policies); len(policyRefs) != 0 {
views = append(views, backendDescribeView{
DirectlyAttachedPolicies: policyRefs,
})

// ReferencedByRoutes
routes := &Table{
ColumnNames: []string{"Kind", "Name"},
UseSeparator: true,
}
for _, httpRouteNode := range backendNode.HTTPRoutes {
row := []string{
httpRouteNode.HTTPRoute.Kind, // Kind
fmt.Sprintf("%v/%v", httpRouteNode.HTTPRoute.Namespace, httpRouteNode.HTTPRoute.Name), // Name
}
routes.Rows = append(routes.Rows, row)
}
pairs = append(pairs, &DescriberKV{Key: "ReferencedByRoutes", Value: routes})

// DirectlyAttachedPolicies
policyRefs := resourcediscovery.ConvertPoliciesMapToPolicyRefs(backendNode.Policies)
pairs = append(pairs, &DescriberKV{Key: "DirectlyAttachedPolicies", Value: convertPolicyRefsToTable(policyRefs)})

// EffectivePolicies
if len(backendNode.EffectivePolicies) != 0 {
views = append(views, backendDescribeView{
EffectivePolicies: backendNode.EffectivePolicies,
})
pairs = append(pairs, &DescriberKV{Key: "EffectivePolicies", Value: backendNode.EffectivePolicies})
}

for _, view := range views {
b, err := yaml.Marshal(view)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to marshal to yaml: %v\n", err)
os.Exit(1)
// ReferenceGrants
if len(backendNode.ReferenceGrants) != 0 {
var names []string
for _, refGrantNode := range backendNode.ReferenceGrants {
names = append(names, refGrantNode.ReferenceGrant.Name)
}
fmt.Fprint(bp, string(b))
pairs = append(pairs, &DescriberKV{Key: "ReferenceGrants", Value: names})
}

// Analysis
if len(backendNode.Errors) != 0 {
pairs = append(pairs, &DescriberKV{Key: "Analysis", Value: convertErrorsToString(backendNode.Errors)})
}

// Events
pairs = append(pairs, &DescriberKV{Key: "Events", Value: convertEventsSliceToTable(backendNode.Events, bp.Clock)})

Describe(bp, pairs)

if index+1 <= len(resourceModel.Backends) {
fmt.Fprintf(bp, "\n\n")
}
Expand Down
35 changes: 32 additions & 3 deletions gwctl/pkg/printer/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import (
"k8s.io/utils/clock"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"

"sigs.k8s.io/gateway-api/gwctl/pkg/common"
)

// DescriberKV stores key-value pairs that are used with Describing a resource.
Expand All @@ -52,7 +54,7 @@ func Describe(w io.Writer, pairs []*DescriberKV) {
fmt.Fprintf(w, "%v: <none>\n", pair.Key)
} else {
fmt.Fprintf(w, "%v:\n", pair.Key)
table.writeTable(w, defaultDescribeTableIndentSpaces)
table.Write(w, defaultDescribeTableIndentSpaces)
}
continue
}
Expand All @@ -76,9 +78,9 @@ type Table struct {
UseSeparator bool
}

// writeTable will write a formatted table to the writer. indent controls the
// Write will write a formatted table to the writer. indent controls the
// number of spaces at the beginning of each row.
func (t *Table) writeTable(w io.Writer, indent int) {
func (t *Table) Write(w io.Writer, indent int) {
tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0)

// Print column names.
Expand Down Expand Up @@ -151,6 +153,33 @@ func convertEventsSliceToTable(events []corev1.Event, clock clock.Clock) *Table
return table
}

func convertPolicyRefsToTable(policyRefs []common.ObjRef) *Table {
table := &Table{
ColumnNames: []string{"Type", "Name"},
UseSeparator: true,
}
for _, policyRef := range policyRefs {
name := policyRef.Name
if policyRef.Namespace != "" {
name = fmt.Sprintf("%v/%v", policyRef.Namespace, name)
}
row := []string{
fmt.Sprintf("%v.%v", policyRef.Kind, policyRef.Group), // Type
name, // Name
}
table.Rows = append(table.Rows, row)
}
return table
}

func convertErrorsToString(errors []error) []string {
var result []string
for _, err := range errors {
result = append(result, err.Error())
}
return result
}

type NodeResource interface {
ClientObject() client.Object
}
Expand Down
2 changes: 1 addition & 1 deletion gwctl/pkg/printer/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ TCPRoute ns2/my-tcproute
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
writable := &bytes.Buffer{}
tc.table.writeTable(writable, tc.indent)
tc.table.Write(writable, tc.indent)

got := writable.String()
if diff := cmp.Diff(common.YamlString(tc.want), common.YamlString(got), common.YamlStringTransformer); diff != "" {
Expand Down
Loading