Skip to content

Commit

Permalink
feat: Add CLI flags to configure table print options
Browse files Browse the repository at this point in the history
Signed-off-by: Justin Toh <tohjustin@hotmail.com>
  • Loading branch information
tohjustin committed Sep 8, 2021
1 parent d4a7c29 commit 0e9d4cf
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 49 deletions.
87 changes: 87 additions & 0 deletions pkg/cmd/lineage/humanreadable_flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package lineage

import (
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/printers"
)

// HumanPrintFlags provides default flags necessary for printing. Given the
// following flag values, a printer can be requested that knows how to handle
// printing based on these values.
type HumanPrintFlags struct {
NoHeaders *bool
ShowGroup *bool
ShowLabels *bool
WithNamespace bool
}

// EnsureWithGroup sets the "ShowGroup" human-readable option to true.
func (f *HumanPrintFlags) EnsureWithGroup() error {
showGroup := true
f.ShowGroup = &showGroup
return nil
}

// EnsureWithNamespace sets the "WithNamespace" human-readable option to true.
func (f *HumanPrintFlags) EnsureWithNamespace() error {
f.WithNamespace = true
return nil
}

// AllowedFormats returns more customized formating options
func (f *HumanPrintFlags) AllowedFormats() []string {
return []string{"wide"}
}

// ToPrinter receives an outputFormat and returns a printer capable of handling
// human-readable output.
func (f *HumanPrintFlags) ToPrinter(outputFormat string) (printers.ResourcePrinter, error) {
if len(outputFormat) > 0 && outputFormat != "wide" {
return nil, genericclioptions.NoCompatiblePrinterError{Options: f, AllowedFormats: f.AllowedFormats()}
}
noHeaders := false
if f.NoHeaders != nil {
noHeaders = *f.NoHeaders
}
showLabels := false
if f.ShowLabels != nil {
showLabels = *f.ShowLabels
}
p := printers.NewTablePrinter(printers.PrintOptions{
NoHeaders: noHeaders,
ShowLabels: showLabels,
Wide: outputFormat == "wide",
WithNamespace: f.WithNamespace,
})
return p, nil
}

// AddFlags receives a *cobra.Command reference and binds flags related to
// human-readable printing to it
func (f *HumanPrintFlags) AddFlags(c *cobra.Command) {
if f.NoHeaders != nil {
c.Flags().BoolVar(f.NoHeaders, "no-headers", *f.NoHeaders, "When using the default output format, don't print headers (default print headers).")
}
if f.ShowLabels != nil {
c.Flags().BoolVar(f.ShowLabels, "show-labels", *f.ShowLabels, "When printing, show all labels as the last column (default hide labels column)")
}
if f.ShowGroup != nil {
c.Flags().BoolVar(f.ShowGroup, "show-group", *f.ShowGroup, "If present, include the resource group for the requested object(s).")
}
}

// NewHumanPrintFlags returns flags associated with human-readable printing,
// with default values set.
func NewHumanPrintFlags() *HumanPrintFlags {
noHeaders := false
showGroup := false
showLabels := false

return &HumanPrintFlags{
NoHeaders: &noHeaders,
ShowGroup: &showGroup,
ShowLabels: &showLabels,
WithNamespace: false,
}
}
59 changes: 55 additions & 4 deletions pkg/cmd/lineage/lineage.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
unstructuredv1 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
Expand All @@ -38,7 +40,10 @@ var (

// CmdOptions contains all the options for running the lineage command.
type CmdOptions struct {
ConfigFlags *genericclioptions.ConfigFlags
ConfigFlags *genericclioptions.ConfigFlags
PrintFlags *PrintFlags
ToPrinter func(withGroup bool, withNamespace bool) (printers.ResourcePrinterFunc, error)

ClientConfig *rest.Config
DynamicClient dynamic.Interface
DiscoveryClient discovery.DiscoveryInterface
Expand All @@ -63,6 +68,7 @@ type Resource struct {
func New(streams genericclioptions.IOStreams) *cobra.Command {
o := &CmdOptions{
ConfigFlags: genericclioptions.NewConfigFlags(true),
PrintFlags: NewLineagePrintFlags(),
IOStreams: streams,
}

Expand All @@ -81,6 +87,7 @@ func New(streams genericclioptions.IOStreams) *cobra.Command {
}

o.ConfigFlags.AddFlags(cmd.Flags())
o.PrintFlags.AddFlags(cmd)

return cmd
}
Expand Down Expand Up @@ -136,6 +143,26 @@ func (o *CmdOptions) Complete(cmd *cobra.Command, args []string) error {
return err
}

o.ToPrinter = func(withGroup bool, withNamespace bool) (printers.ResourcePrinterFunc, error) {
printFlags := o.PrintFlags.Copy()
if withGroup {
if err := printFlags.EnsureWithGroup(); err != nil {
return nil, err
}
}
if withNamespace {
if err := printFlags.EnsureWithNamespace(); err != nil {
return nil, err
}
}
printer, err := printFlags.ToPrinter()
if err != nil {
return nil, err
}

return printer.PrintObj, nil
}

return nil
}

Expand Down Expand Up @@ -180,9 +207,7 @@ func (o *CmdOptions) Run() error {
graph := buildDependencyGraph(objects, *rootObject)

// Print table
// TODO: Add CLI flags for print options
// TODO: Hide namespace column if all printed objects are in the same namespace
err = printGraph(graph, rootObject.GetUID(), printOptions{WithNamespace: true})
err = o.printGraph(graph, rootObject.GetUID())
if err != nil {
return err
}
Expand Down Expand Up @@ -293,6 +318,32 @@ func (o *CmdOptions) getObjectsByResource(api Resource) ([]unstructuredv1.Unstru
return result, nil
}

func (o *CmdOptions) printGraph(objects Graph, uid types.UID) error {
// TODO: Auto-show group if all objects contains different resources with the same kind
withGroup := false
if o.PrintFlags.HumanReadableFlags.ShowGroup != nil {
withGroup = *o.PrintFlags.HumanReadableFlags.ShowGroup
}
// TODO: Auto-hide namespace column if all objects are in the same namespace
withNamespace := true
printer, err := o.ToPrinter(withGroup, withNamespace)
if err != nil {
return err
}

// TODO: Sort graph before printing
rows, err := printTableRows(objects, uid, "", withGroup)
if err != nil {
return err
}

table := &metav1.Table{
ColumnDefinitions: objectColumnDefinitions,
Rows: rows,
}
return printer.PrintObj(table, o.Out)
}

func resourceFor(mapper meta.RESTMapper, resourceArg string) (schema.GroupVersionResource, error) {
fullySpecifiedGVR, Resource := schema.ParseResourceArg(strings.ToLower(resourceArg))
gvr := schema.GroupVersionResource{}
Expand Down
81 changes: 81 additions & 0 deletions pkg/cmd/lineage/lineage_flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package lineage

import (
"fmt"
"strings"

"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/printers"
)

// PrintFlags composes common printer flag structs used in the lineage command.
type PrintFlags struct {
HumanReadableFlags *HumanPrintFlags
OutputFormat *string
}

// AllowedFormats is the list of formats in which data can be displayed
func (f *PrintFlags) AllowedFormats() []string {
formats := []string{}
formats = append(formats, f.HumanReadableFlags.AllowedFormats()...)
return formats
}

// Copy returns a copy of PrintFlags for mutation
func (f *PrintFlags) Copy() PrintFlags {
printFlags := *f
return printFlags
}

// EnsureWithGroup ensures that human-readable flags return a printer capable of
// including resource kinds.
func (f *PrintFlags) EnsureWithGroup() error {
return f.HumanReadableFlags.EnsureWithGroup()
}

// EnsureWithNamespace ensures that human-readable flags return a printer capable
// of printing with a "namespace" column.
func (f *PrintFlags) EnsureWithNamespace() error {
return f.HumanReadableFlags.EnsureWithNamespace()
}

// ToPrinter attempts to find a composed set of PrintFlags suitable for
// returning a printer based on current flag values.
func (f *PrintFlags) ToPrinter() (printers.ResourcePrinter, error) {
outputFormat := ""
if f.OutputFormat != nil {
outputFormat = *f.OutputFormat
}

p, err := f.HumanReadableFlags.ToPrinter(outputFormat)
if !genericclioptions.IsNoCompatiblePrinterError(err) {
return p, err
}

return nil, genericclioptions.NoCompatiblePrinterError{
AllowedFormats: f.AllowedFormats(),
OutputFormat: &outputFormat,
}
}

// AddFlags receives a *cobra.Command reference and binds flags related to
// human-readable printing to it
func (f *PrintFlags) AddFlags(c *cobra.Command) {
f.HumanReadableFlags.AddFlags(c)

if f.OutputFormat != nil {
c.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, fmt.Sprintf("Output format. One of: %s.", strings.Join(f.AllowedFormats(), "|")))
}
}

// NewLineagePrintFlags returns flags associated with human-readable printing,
// with default values set.
func NewLineagePrintFlags() *PrintFlags {
outputFormat := ""

return &PrintFlags{
OutputFormat: &outputFormat,
HumanReadableFlags: NewHumanPrintFlags(),
}
}
Loading

0 comments on commit 0e9d4cf

Please sign in to comment.