From 6df8731a13c810ab34986ba7905db502a4329cb6 Mon Sep 17 00:00:00 2001 From: ycyaoxdu Date: Thu, 29 Sep 2022 03:47:23 +0000 Subject: [PATCH 1/2] feature: format printer Signed-off-by: ycyaoxdu --- pkg/helpers/printer/option.go | 82 +++++++++++++++++++++++++ pkg/helpers/printer/treeprinter.go | 68 +++++++++++++++++++++ pkg/helpers/printer/trie.go | 98 ++++++++++++++++++++++++++++++ 3 files changed, 248 insertions(+) create mode 100644 pkg/helpers/printer/option.go create mode 100644 pkg/helpers/printer/treeprinter.go create mode 100644 pkg/helpers/printer/trie.go diff --git a/pkg/helpers/printer/option.go b/pkg/helpers/printer/option.go new file mode 100644 index 000000000..e8b379302 --- /dev/null +++ b/pkg/helpers/printer/option.go @@ -0,0 +1,82 @@ +// Copyright Contributors to the Open Cluster Management project +package printer + +import ( + "fmt" + + "github.com/spf13/pflag" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/cli-runtime/pkg/printers" +) + +type PrinterOption struct { + Options printers.PrintOptions + Format string + tree TreePrinter + table printers.ResourcePrinter + yaml printers.YAMLPrinter + + treeConverter func(runtime.Object, *TreePrinter) *TreePrinter + tableConverter func(runtime.Object) *metav1.Table +} + +func NewPrinterOption(o printers.PrintOptions) *PrinterOption { + return &PrinterOption{ + Options: o, + } +} + +func (p *PrinterOption) AddFlag(fs *pflag.FlagSet) { + fs.StringVarP(&p.Format, "output", "o", "tree", "output format can be tree, table or yaml") +} + +func (p *PrinterOption) Competele() { + p.tree = NewTreePrinter(p.Options.Kind.Kind) + p.table = printers.NewTablePrinter(p.Options) + p.yaml = printers.YAMLPrinter{} +} + +func (p *PrinterOption) Validate() error { + if p.Format != "tree" && p.Format != "table" && p.Format != "yaml" { + return fmt.Errorf("invalid output format") + } + return nil +} + +func (p *PrinterOption) WithTreeConverter(f func(runtime.Object, *TreePrinter) *TreePrinter) *PrinterOption { + p.treeConverter = f + return p +} +func (p *PrinterOption) WithTableConverter(f func(runtime.Object) *metav1.Table) *PrinterOption { + p.tableConverter = f + return p +} + +func (p *PrinterOption) Print(stream genericclioptions.IOStreams, obj runtime.Object) error { + switch p.Format { + case "tree": + p.tree = *p.treeConverter(obj, &p.tree) + return p.tree.Print(stream.Out) + case "table": + return p.table.PrintObj(p.tableConverter(obj), stream.Out) + case "yaml": + objs, err := meta.ExtractList(obj) + if err != nil { + return err + } + + for _, item := range objs { + err := p.yaml.PrintObj(item, stream.Out) + if err != nil { + return err + } + } + + return nil + default: + return fmt.Errorf("invalid output format") + } +} diff --git a/pkg/helpers/printer/treeprinter.go b/pkg/helpers/printer/treeprinter.go new file mode 100644 index 000000000..7452dc4f3 --- /dev/null +++ b/pkg/helpers/printer/treeprinter.go @@ -0,0 +1,68 @@ +// Copyright Contributors to the Open Cluster Management project +package printer + +import ( + "fmt" + "io" + "strings" + + "github.com/disiqueira/gotree" +) + +type TreePrinter struct { + name string + tree *Trie +} + +func NewTreePrinter(name string) TreePrinter { + return TreePrinter{ + name: name, + tree: NewTrie(DefaultSegmenter), + } +} + +// AddFileds add the fileds need to print to TreePrinter. +// @param name : the name of object to be print +// @param mp : key is a string represents level and path, value is the value will be printed. +func (t *TreePrinter) AddFileds(name string, mp *map[string]interface{}) { + if mp == nil { + return + } + t.add(name, "") + for key, value := range *mp { + t.add(name+key, value) + } +} + +// add the key value to TreePrinter. +func (t *TreePrinter) add(key string, value interface{}) { + t.tree.Put(key, value) +} + +func (t *TreePrinter) Print(outstream io.Writer) error { + _, err := fmt.Fprint(outstream, t.build().Print()) + return err +} + +func (t *TreePrinter) build() gotree.Tree { + var dfs func(part string, root *Trie) gotree.Tree + + dfs = func(part string, root *Trie) gotree.Tree { + if root.value == nil { + root.value = "" + } + line := fmt.Sprintf("<%s> %v", part, root.value) + level0 := gotree.New(line) + + if root.isLeaf() { + return level0 + } + + for key, value := range root.children { + level0.AddTree(dfs(strings.TrimPrefix(key, "."), value)) + } + return level0 + } + + return dfs(t.name, t.tree) +} diff --git a/pkg/helpers/printer/trie.go b/pkg/helpers/printer/trie.go new file mode 100644 index 000000000..3efeb3c11 --- /dev/null +++ b/pkg/helpers/printer/trie.go @@ -0,0 +1,98 @@ +// Copyright Contributors to the Open Cluster Management project +package printer + +import "strings" + +type IterFunc func(key string, value interface{}) error + +func DefaultIterFunc(key string, value interface{}) error { + return nil +} + +type Segmenter func(key string, start int) (segment string, nextIndex int) + +func DefaultSegmenter(path string, start int) (segment string, next int) { + if len(path) == 0 || start < 0 || start > len(path)-1 { + return "", -1 + } + end := strings.IndexRune(path[start+1:], '.') + if end == -1 { + return path[start:], -1 + } + return path[start : start+end+1], start + end + 1 +} + +type Trier interface { + Get(key string) interface{} + Put(key string, value interface{}) bool + Iter(it IterFunc) error +} + +type Trie struct { + segmenter Segmenter // key segmenter, must not cause heap allocs + value interface{} + children map[string]*Trie +} + +func NewTrie(sgmt Segmenter) *Trie { + return &Trie{ + segmenter: sgmt, + } +} + +func (trie *Trie) Get(key string) interface{} { + node := trie + for part, i := trie.segmenter(key, 0); part != ""; part, i = trie.segmenter(key, i) { + node = node.children[part] + if node == nil { + return nil + } + } + return node.value +} + +func (trie *Trie) newTrie() *Trie { + return &Trie{ + segmenter: trie.segmenter, + } +} +func (trie *Trie) Put(key string, value interface{}) bool { + node := trie + for part, i := trie.segmenter(key, 0); part != ""; part, i = trie.segmenter(key, i) { + child := node.children[part] + if child == nil { + if node.children == nil { + node.children = map[string]*Trie{} + } + child = trie.newTrie() + node.children[part] = child + } + node = child + } + + isNewVal := node.value == nil + node.value = value + return isNewVal +} + +func (trie *Trie) Iter(it IterFunc) error { + return trie.iter("", it) +} + +func (trie *Trie) iter(key string, it IterFunc) error { + if trie.value != nil { + if err := it(key, trie.value); err != nil { + return err + } + } + for part, child := range trie.children { + if err := child.iter(key+part, it); err != nil { + return err + } + } + return nil +} + +func (trie *Trie) isLeaf() bool { + return len(trie.children) == 0 +} From f71419960d63a234f78b39e3cf80e85ddbd1e10a Mon Sep 17 00:00:00 2001 From: ycyaoxdu Date: Thu, 29 Sep 2022 03:54:15 +0000 Subject: [PATCH 2/2] add new print format for get cluster/clusterset/work Signed-off-by: ycyaoxdu --- pkg/cmd/get/cluster/cmd.go | 2 + pkg/cmd/get/cluster/exec.go | 58 +++++++++++++++----- pkg/cmd/get/cluster/options.go | 33 ++++++------ pkg/cmd/get/clusterset/cmd.go | 2 + pkg/cmd/get/clusterset/exec.go | 89 ++++++++++++++++++++++--------- pkg/cmd/get/clusterset/options.go | 36 +++++++------ pkg/cmd/get/work/cmd.go | 4 +- pkg/cmd/get/work/exec.go | 72 ++++++++++++++++--------- pkg/cmd/get/work/options.go | 33 ++++++------ 9 files changed, 220 insertions(+), 109 deletions(-) diff --git a/pkg/cmd/get/cluster/cmd.go b/pkg/cmd/get/cluster/cmd.go index ddcf83f23..b19fa098e 100644 --- a/pkg/cmd/get/cluster/cmd.go +++ b/pkg/cmd/get/cluster/cmd.go @@ -50,5 +50,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream cmd.Flags().StringVar(&o.Clusterset, "clusterset", "", "ClusterSet of the clusters") + o.printer.AddFlag(cmd.Flags()) + return cmd } diff --git a/pkg/cmd/get/cluster/exec.go b/pkg/cmd/get/cluster/exec.go index 544176a9e..2a7dbe203 100644 --- a/pkg/cmd/get/cluster/exec.go +++ b/pkg/cmd/get/cluster/exec.go @@ -11,9 +11,12 @@ import ( "k8s.io/apimachinery/pkg/runtime" clusterclientset "open-cluster-management.io/api/client/cluster/clientset/versioned" clusterapiv1 "open-cluster-management.io/api/cluster/v1" + "open-cluster-management.io/clusteradm/pkg/helpers/printer" ) func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { + o.printer.Competele() + return nil } @@ -21,6 +24,12 @@ func (o *Options) validate(args []string) (err error) { if len(args) != 0 { return fmt.Errorf("there should be no argument") } + + err = o.printer.Validate() + if err != nil { + return err + } + return nil } @@ -49,12 +58,30 @@ func (o *Options) run() (err error) { return err } - table := converToTable(clusters) + o.printer.WithTreeConverter(o.convertToTree).WithTableConverter(o.converToTable) - return o.printer.PrintObj(table, o.Streams.Out) + return o.printer.Print(o.Streams, clusters) } -func converToTable(clusters *clusterapiv1.ManagedClusterList) *metav1.Table { +func (o *Options) convertToTree(obj runtime.Object, tree *printer.TreePrinter) *printer.TreePrinter { + if mclList, ok := obj.(*clusterapiv1.ManagedClusterList); ok { + for _, cluster := range mclList.Items { + accepted, available, version, cpu, memory, clusterset := getFileds(cluster) + mp := make(map[string]interface{}) + mp[".Accepted"] = accepted + mp[".Available"] = available + mp[".ClusterSet"] = clusterset + mp[".KubernetesVersion"] = version + mp[".Capacity.Cpu"] = cpu + mp[".Capacity.Memory"] = memory + + tree.AddFileds(cluster.Name, &mp) + } + } + return tree +} + +func (o *Options) converToTable(obj runtime.Object) *metav1.Table { table := &metav1.Table{ ColumnDefinitions: []metav1.TableColumnDefinition{ {Name: "Name", Type: "string"}, @@ -68,16 +95,24 @@ func converToTable(clusters *clusterapiv1.ManagedClusterList) *metav1.Table { Rows: []metav1.TableRow{}, } - for _, cluster := range clusters.Items { - row := convertRow(cluster) - table.Rows = append(table.Rows, row) - } + if mclList, ok := obj.(*clusterapiv1.ManagedClusterList); ok { + for _, cluster := range mclList.Items { + accepted, available, version, cpu, memory, clusterset := getFileds(cluster) + row := metav1.TableRow{ + Cells: []interface{}{cluster.Name, accepted, available, clusterset, cpu, memory, version}, + Object: runtime.RawExtension{Object: &cluster}, + } + table.Rows = append(table.Rows, row) + } + } return table } -func convertRow(cluster clusterapiv1.ManagedCluster) metav1.TableRow { - var available, cpu, memory, clusterset string +func getFileds(cluster clusterapiv1.ManagedCluster) (accepted bool, available, version, cpu, memory, clusterset string) { + accepted = cluster.Spec.HubAcceptsClient + + version = cluster.Status.Version.Kubernetes availableCond := meta.FindStatusCondition(cluster.Status.Conditions, clusterapiv1.ManagedClusterConditionAvailable) if availableCond != nil { @@ -96,8 +131,5 @@ func convertRow(cluster clusterapiv1.ManagedCluster) metav1.TableRow { clusterset = cluster.Labels["cluster.open-cluster-management.io/clusterset"] } - return metav1.TableRow{ - Cells: []interface{}{cluster.Name, cluster.Spec.HubAcceptsClient, available, clusterset, cpu, memory, cluster.Status.Version.Kubernetes}, - Object: runtime.RawExtension{Object: &cluster}, - } + return } diff --git a/pkg/cmd/get/cluster/options.go b/pkg/cmd/get/cluster/options.go index 428bd6996..6cfab6634 100644 --- a/pkg/cmd/get/cluster/options.go +++ b/pkg/cmd/get/cluster/options.go @@ -6,6 +6,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" + "open-cluster-management.io/clusteradm/pkg/helpers/printer" ) type Options struct { @@ -16,26 +17,28 @@ type Options struct { Streams genericclioptions.IOStreams - printer printers.ResourcePrinter + printer *printer.PrinterOption } func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { return &Options{ ClusteradmFlags: clusteradmFlags, Streams: streams, - printer: printers.NewTablePrinter(printers.PrintOptions{ - NoHeaders: false, - WithNamespace: false, - WithKind: false, - Wide: false, - ShowLabels: false, - Kind: schema.GroupKind{ - Group: "cluster.open-cluster-management.io", - Kind: "ManagedCluster", - }, - ColumnLabels: []string{}, - SortBy: "", - AllowMissingKeys: true, - }), + printer: printer.NewPrinterOption(pntOpt), } } + +var pntOpt = printers.PrintOptions{ + NoHeaders: false, + WithNamespace: false, + WithKind: false, + Wide: false, + ShowLabels: false, + Kind: schema.GroupKind{ + Group: "cluster.open-cluster-management.io", + Kind: "ManagedCluster", + }, + ColumnLabels: []string{}, + SortBy: "", + AllowMissingKeys: true, +} diff --git a/pkg/cmd/get/clusterset/cmd.go b/pkg/cmd/get/clusterset/cmd.go index 2723394b4..6295ab024 100644 --- a/pkg/cmd/get/clusterset/cmd.go +++ b/pkg/cmd/get/clusterset/cmd.go @@ -45,5 +45,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream }, } + o.printer.AddFlag(cmd.Flags()) + return cmd } diff --git a/pkg/cmd/get/clusterset/exec.go b/pkg/cmd/get/clusterset/exec.go index 03584c2f0..d686fef69 100644 --- a/pkg/cmd/get/clusterset/exec.go +++ b/pkg/cmd/get/clusterset/exec.go @@ -12,9 +12,22 @@ import ( "k8s.io/apimachinery/pkg/runtime" clusterclientset "open-cluster-management.io/api/client/cluster/clientset/versioned" clusterapiv1beta1 "open-cluster-management.io/api/cluster/v1beta1" + "open-cluster-management.io/clusteradm/pkg/helpers/printer" ) func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { + restConfig, err := o.ClusteradmFlags.KubectlFactory.ToRESTConfig() + if err != nil { + return err + } + clusterClient, err := clusterclientset.NewForConfig(restConfig) + if err != nil { + return err + } + o.Client = clusterClient + + o.printer.Competele() + return nil } @@ -22,29 +35,31 @@ func (o *Options) validate(args []string) (err error) { if len(args) != 0 { return fmt.Errorf("there should be no argument") } - return nil -} -func (o *Options) run() (err error) { - restConfig, err := o.ClusteradmFlags.KubectlFactory.ToRESTConfig() - if err != nil { - return err - } - clusterClient, err := clusterclientset.NewForConfig(restConfig) + err = o.printer.Validate() if err != nil { return err } - clustersets, err := clusterClient.ClusterV1beta1().ManagedClusterSets().List(context.TODO(), metav1.ListOptions{}) + return nil +} + +func (o *Options) run() (err error) { + clustersets, err := o.Client.ClusterV1beta1().ManagedClusterSets().List(context.TODO(), metav1.ListOptions{}) if err != nil { return err } - bindingMap := map[string][]string{} + o.printer.WithTreeConverter(o.convertToTree).WithTableConverter(o.converToTable) + + return o.printer.Print(o.Streams, clustersets) +} - bindings, err := clusterClient.ClusterV1beta1().ManagedClusterSetBindings(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{}) +func (o *Options) convertToTree(obj runtime.Object, tree *printer.TreePrinter) *printer.TreePrinter { + bindingMap := map[string][]string{} + bindings, err := o.Client.ClusterV1beta1().ManagedClusterSetBindings(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{}) if err != nil { - return err + panic(err) } for _, binding := range bindings.Items { if _, ok := bindingMap[binding.Spec.ClusterSet]; !ok { @@ -54,12 +69,33 @@ func (o *Options) run() (err error) { bindingMap[binding.Spec.ClusterSet] = append(bindingMap[binding.Spec.ClusterSet], binding.Namespace) } - table := converToTable(clustersets, bindingMap) + if csList, ok := obj.(*clusterapiv1beta1.ManagedClusterSetList); ok { + for _, clusterset := range csList.Items { + boundNs, status := getFileds(clusterset, bindingMap[clusterset.Name]) + mp := make(map[string]interface{}) + mp[".BoundNamespace"] = boundNs + mp[".Status"] = status + tree.AddFileds(clusterset.Name, &mp) + } + } - return o.printer.PrintObj(table, o.Streams.Out) + return tree } -func converToTable(clustersets *clusterapiv1beta1.ManagedClusterSetList, bindingMap map[string][]string) *metav1.Table { +func (o *Options) converToTable(obj runtime.Object) *metav1.Table { + bindingMap := map[string][]string{} + bindings, err := o.Client.ClusterV1beta1().ManagedClusterSetBindings(metav1.NamespaceAll).List(context.TODO(), metav1.ListOptions{}) + if err != nil { + panic(err) + } + for _, binding := range bindings.Items { + if _, ok := bindingMap[binding.Spec.ClusterSet]; !ok { + bindingMap[binding.Spec.ClusterSet] = []string{} + } + + bindingMap[binding.Spec.ClusterSet] = append(bindingMap[binding.Spec.ClusterSet], binding.Namespace) + } + table := &metav1.Table{ ColumnDefinitions: []metav1.TableColumnDefinition{ {Name: "Name", Type: "string"}, @@ -69,25 +105,28 @@ func converToTable(clustersets *clusterapiv1beta1.ManagedClusterSetList, binding Rows: []metav1.TableRow{}, } - for _, cluster := range clustersets.Items { - bindings := bindingMap[cluster.Name] - row := convertRow(cluster, bindings) - table.Rows = append(table.Rows, row) + if csList, ok := obj.(*clusterapiv1beta1.ManagedClusterSetList); ok { + for _, clusterset := range csList.Items { + boundNs, status := getFileds(clusterset, bindingMap[clusterset.Name]) + row := metav1.TableRow{ + Cells: []interface{}{clusterset.Name, boundNs, status}, + Object: runtime.RawExtension{Object: &clusterset}, + } + + table.Rows = append(table.Rows, row) + } } return table } -func convertRow(clusterset clusterapiv1beta1.ManagedClusterSet, bindings []string) metav1.TableRow { - var status string +func getFileds(clusterset clusterapiv1beta1.ManagedClusterSet, bindings []string) (boundNs, status string) { + boundNs = strings.Join(bindings, ",") emptyCond := meta.FindStatusCondition(clusterset.Status.Conditions, clusterapiv1beta1.ManagedClusterSetConditionEmpty) if emptyCond != nil { status = string(emptyCond.Message) } - return metav1.TableRow{ - Cells: []interface{}{clusterset.Name, strings.Join(bindings, ","), status}, - Object: runtime.RawExtension{Object: &clusterset}, - } + return } diff --git a/pkg/cmd/get/clusterset/options.go b/pkg/cmd/get/clusterset/options.go index a7cb312e3..18c11c686 100644 --- a/pkg/cmd/get/clusterset/options.go +++ b/pkg/cmd/get/clusterset/options.go @@ -5,7 +5,9 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" + clusterclientset "open-cluster-management.io/api/client/cluster/clientset/versioned" genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" + "open-cluster-management.io/clusteradm/pkg/helpers/printer" ) type Options struct { @@ -14,26 +16,30 @@ type Options struct { Streams genericclioptions.IOStreams - printer printers.ResourcePrinter + printer *printer.PrinterOption + + Client *clusterclientset.Clientset } func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { return &Options{ ClusteradmFlags: clusteradmFlags, Streams: streams, - printer: printers.NewTablePrinter(printers.PrintOptions{ - NoHeaders: false, - WithNamespace: false, - WithKind: false, - Wide: false, - ShowLabels: false, - Kind: schema.GroupKind{ - Group: "cluster.open-cluster-management.io", - Kind: "ManagedCluster", - }, - ColumnLabels: []string{}, - SortBy: "", - AllowMissingKeys: true, - }), + printer: printer.NewPrinterOption(pntOpt), } } + +var pntOpt = printers.PrintOptions{ + NoHeaders: false, + WithNamespace: false, + WithKind: false, + Wide: false, + ShowLabels: false, + Kind: schema.GroupKind{ + Group: "cluster.open-cluster-management.io", + Kind: "ManagedClusterSet", + }, + ColumnLabels: []string{}, + SortBy: "", + AllowMissingKeys: true, +} diff --git a/pkg/cmd/get/work/cmd.go b/pkg/cmd/get/work/cmd.go index 168dd9337..bcedc0d7f 100644 --- a/pkg/cmd/get/work/cmd.go +++ b/pkg/cmd/get/work/cmd.go @@ -47,7 +47,9 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream }, } - cmd.Flags().StringVar(&o.cluster, "cluster", "", "Name of the managed cluster") + cmd.Flags().StringVar(&o.cluster, "cluster", "", "Names of the managed cluster") + + o.printer.AddFlag(cmd.Flags()) return cmd } diff --git a/pkg/cmd/get/work/exec.go b/pkg/cmd/get/work/exec.go index c4fcc76ee..477989902 100644 --- a/pkg/cmd/get/work/exec.go +++ b/pkg/cmd/get/work/exec.go @@ -5,7 +5,6 @@ import ( "context" "fmt" - "github.com/disiqueira/gotree" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -17,7 +16,6 @@ import ( ) func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { - if len(args) > 1 { return fmt.Errorf("can only specify one manifestwork") } @@ -26,6 +24,8 @@ func (o *Options) complete(cmd *cobra.Command, args []string) (err error) { o.workName = args[0] } + o.printer.Competele() + return nil } @@ -33,6 +33,12 @@ func (o *Options) validate() (err error) { if len(o.cluster) == 0 { return fmt.Errorf("cluster name must be specified") } + + err = o.printer.Validate() + if err != nil { + return err + } + return nil } @@ -55,29 +61,40 @@ func (o *Options) run() (err error) { return err } + var workList *workapiv1.ManifestWorkList if len(o.workName) == 0 { - workList, err := workClient.WorkV1().ManifestWorks(o.cluster).List(context.TODO(), metav1.ListOptions{}) - if err != nil { - return err - } - - table := converToTable(workList) - return o.printer.PrintObj(table, o.Streams.Out) + workList, err = workClient.WorkV1().ManifestWorks(o.cluster).List(context.TODO(), metav1.ListOptions{}) + } else { + workList, err = workClient.WorkV1().ManifestWorks(o.cluster).List(context.TODO(), metav1.ListOptions{ + FieldSelector: fmt.Sprintf("name=%s", o.workName), + }) } - - work, err := workClient.WorkV1().ManifestWorks(o.cluster).Get(context.TODO(), o.workName, metav1.GetOptions{}) if err != nil { return err } - root := gotree.New("") - printer.PrintWorkDetail(root, work) + o.printer.WithTreeConverter(o.convertToTree).WithTableConverter(o.converToTable) - fmt.Fprint(o.Streams.Out, root.Print()) - return nil + return o.printer.Print(o.Streams, workList) } -func converToTable(works *workapiv1.ManifestWorkList) *metav1.Table { +func (o *Options) convertToTree(obj runtime.Object, tree *printer.TreePrinter) *printer.TreePrinter { + if workList, ok := obj.(*workapiv1.ManifestWorkList); ok { + for _, work := range workList.Items { + cluster, number, applied, available := getFileds(work) + mp := make(map[string]interface{}) + mp[".Cluster"] = cluster + mp[".Number of Manifests"] = number + mp[".Applied"] = applied + mp[".Available"] = available + + tree.AddFileds(work.Name, &mp) + } + } + return tree +} + +func (o *Options) converToTable(obj runtime.Object) *metav1.Table { table := &metav1.Table{ ColumnDefinitions: []metav1.TableColumnDefinition{ {Name: "Name", Type: "string"}, @@ -89,16 +106,24 @@ func converToTable(works *workapiv1.ManifestWorkList) *metav1.Table { Rows: []metav1.TableRow{}, } - for _, work := range works.Items { - row := convertRow(work) - table.Rows = append(table.Rows, row) + if workList, ok := obj.(*workapiv1.ManifestWorkList); ok { + for _, work := range workList.Items { + cluster, number, applied, available := getFileds(work) + row := metav1.TableRow{ + Cells: []interface{}{work.Name, cluster, number, applied, available}, + Object: runtime.RawExtension{Object: &work}, + } + + table.Rows = append(table.Rows, row) + } } return table } -func convertRow(work workapiv1.ManifestWork) metav1.TableRow { - var applied, available string +func getFileds(work workapiv1.ManifestWork) (cluster string, number int, applied, available string) { + cluster = work.Namespace + number = len(work.Spec.Workload.Manifests) appliedCond := meta.FindStatusCondition(work.Status.Conditions, workapiv1.WorkApplied) if appliedCond != nil { @@ -110,8 +135,5 @@ func convertRow(work workapiv1.ManifestWork) metav1.TableRow { available = string(availableCond.Status) } - return metav1.TableRow{ - Cells: []interface{}{work.Name, work.Namespace, len(work.Spec.Workload.Manifests), applied, available}, - Object: runtime.RawExtension{Object: &work}, - } + return } diff --git a/pkg/cmd/get/work/options.go b/pkg/cmd/get/work/options.go index eeb311ea5..22d52c83f 100644 --- a/pkg/cmd/get/work/options.go +++ b/pkg/cmd/get/work/options.go @@ -6,6 +6,7 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/printers" genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions" + "open-cluster-management.io/clusteradm/pkg/helpers/printer" ) type Options struct { @@ -18,26 +19,28 @@ type Options struct { Streams genericclioptions.IOStreams - printer printers.ResourcePrinter + printer *printer.PrinterOption } func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options { return &Options{ ClusteradmFlags: clusteradmFlags, Streams: streams, - printer: printers.NewTablePrinter(printers.PrintOptions{ - NoHeaders: false, - WithNamespace: false, - WithKind: false, - Wide: false, - ShowLabels: false, - Kind: schema.GroupKind{ - Group: "work.open-cluster-management.io", - Kind: "ManifestWork", - }, - ColumnLabels: []string{}, - SortBy: "", - AllowMissingKeys: true, - }), + printer: printer.NewPrinterOption(pntOpt), } } + +var pntOpt = printers.PrintOptions{ + NoHeaders: false, + WithNamespace: false, + WithKind: false, + Wide: false, + ShowLabels: false, + Kind: schema.GroupKind{ + Group: "work.open-cluster-management.io", + Kind: "ManifestWork", + }, + ColumnLabels: []string{}, + SortBy: "", + AllowMissingKeys: true, +}