From 2ae3ea8d611c4a2d4baa1e2414ea63d238cc2f99 Mon Sep 17 00:00:00 2001
From: lonelyCZ <531187475@qq.com>
Date: Tue, 18 Jan 2022 11:19:00 +0800
Subject: [PATCH] Optimize karmadactl get to output more information

Signed-off-by: lonelyCZ <531187475@qq.com>
---
 pkg/karmadactl/get.go | 205 +++++++++++++++++++++++++++---------------
 1 file changed, 135 insertions(+), 70 deletions(-)

diff --git a/pkg/karmadactl/get.go b/pkg/karmadactl/get.go
index 0d9d4455853a..4b2c8a30f4d4 100644
--- a/pkg/karmadactl/get.go
+++ b/pkg/karmadactl/get.go
@@ -6,6 +6,7 @@ import (
 	"io"
 	"os"
 	"path/filepath"
+	"sort"
 	"strings"
 	"sync"
 
@@ -15,7 +16,6 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
-	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	utilerrors "k8s.io/apimachinery/pkg/util/errors"
@@ -30,10 +30,10 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
-	policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
 	workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
 	"github.com/karmada-io/karmada/pkg/karmadactl/options"
 	"github.com/karmada-io/karmada/pkg/util/gclient"
+	"github.com/karmada-io/karmada/pkg/util/names"
 )
 
 const printColumnClusterNum = 1
@@ -44,7 +44,7 @@ var (
 	getErr = os.Stderr
 
 	podColumns = []metav1.TableColumnDefinition{
-		{Name: "Cluster", Type: "string", Format: "", Priority: 0},
+		{Name: "CLUSTER", Type: "string", Format: "", Priority: 0},
 		{Name: "ADOPTION", Type: "string", Format: "", Priority: 0},
 	}
 
@@ -73,12 +73,16 @@ func NewCmdGet(out io.Writer, karmadaConfig KarmadaConfig, parentCommand string)
 			return nil
 		},
 	}
+
+	o.GlobalCommandOptions.AddFlags(cmd.Flags())
+	o.PrintFlags.AddFlags(cmd)
+
 	cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", "default", "-n=namespace or -n namespace")
 	cmd.Flags().StringVarP(&o.LabelSelector, "labels", "l", "", "-l=label or -l label")
 	cmd.Flags().StringSliceVarP(&o.Clusters, "clusters", "C", []string{}, "-C=member1,member2")
 	cmd.Flags().StringVar(&o.ClusterNamespace, "cluster-namespace", options.DefaultKarmadaClusterNamespace, "Namespace in the control plane where member cluster are stored.")
+	cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
 
-	o.GlobalCommandOptions.AddFlags(cmd.Flags())
 	return cmd
 }
 
@@ -145,10 +149,18 @@ func (g *CommandGetOptions) Complete(cmd *cobra.Command, args []string) error {
 		return ErrEmptyConfig
 	}
 
+	if g.AllNamespaces {
+		g.ExplicitNamespace = false
+	}
+
 	g.ToPrinter = func(mapping *meta.RESTMapping, outputObjects *bool, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
 		// make a new copy of current flags / opts before mutating
 		printFlags := g.PrintFlags.Copy()
 
+		if mapping != nil {
+			printFlags.SetKind(mapping.GroupVersionKind.GroupKind())
+		}
+
 		if withNamespace {
 			_ = printFlags.EnsureWithNamespace()
 		}
@@ -188,6 +200,7 @@ type OtherPrint struct {
 }
 
 // Run performs the get operation.
+//nolint:gocyclo
 func (g *CommandGetOptions) Run(karmadaConfig KarmadaConfig, cmd *cobra.Command, args []string) error {
 	mux := sync.Mutex{}
 	var wg sync.WaitGroup
@@ -224,26 +237,12 @@ func (g *CommandGetOptions) Run(karmadaConfig KarmadaConfig, cmd *cobra.Command,
 		fmt.Println(fmt.Sprintf(noPushModeMessage, strings.Join(noPushModeCluster, ",")))
 	}
 
-	table := &metav1.Table{}
-	allTableRows, mapping, err := g.reconstructionRow(objs, table)
-	if err != nil {
-		return err
-	}
-	table.Rows = allTableRows
+	printWithKind := multipleGVKsRequested(objs)
 
-	setNoAdoption(mapping)
-	setColumnDefinition(table)
-
-	if len(table.Rows) == 0 {
-		msg := fmt.Sprintf("%v from server (NotFound)", args)
-		fmt.Println(msg)
-		return nil
-	}
-	printObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(table)
-	if err != nil {
-		return err
-	}
-	newPrintObj := &unstructured.Unstructured{Object: printObj}
+	// sort objects by resource kind to classify them
+	sort.Slice(objs, func(i, j int) bool {
+		return objs[i].Mapping.Resource.String() < objs[j].Mapping.Resource.String()
+	})
 
 	var printer printers.ResourcePrinter
 	var lastMapping *meta.RESTMapping
@@ -254,34 +253,72 @@ func (g *CommandGetOptions) Run(karmadaConfig KarmadaConfig, cmd *cobra.Command,
 	separatorWriter := &separatorWriterWrapper{Delegate: trackingWriter}
 
 	w := printers.GetNewTabWriter(separatorWriter)
-	if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
-		w.Flush()
-		w.SetRememberedWidths(nil)
-
-		// add linebreaks between resource groups (if there is more than one)
-		// when it satisfies all following 3 conditions:
-		// 1) it's not the first resource group
-		// 2) it has row header
-		// 3) we've written output since the last time we started a new set of headers
-		if !g.NoHeaders && trackingWriter.Written > 0 {
-			separatorWriter.SetReady(true)
+	sameKind := make([]Obj, 0)
+	for ix := range objs {
+		mapping := objs[ix].Mapping
+		sameKind = append(sameKind, objs[ix])
+
+		printWithNamespace := g.AllNamespaces
+
+		if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
+			printWithNamespace = false
 		}
 
-		printer, err = g.ToPrinter(mapping, nil, false, false)
-		if err != nil {
-			if !errs.Has(err.Error()) {
-				errs.Insert(err.Error())
-				allErrs = append(allErrs, err)
+		if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
+			w.Flush()
+			w.SetRememberedWidths(nil)
+
+			// add linebreaks between resource groups (if there is more than one)
+			// when it satisfies all following 3 conditions:
+			// 1) it's not the first resource group
+			// 2) it has row header
+			// 3) we've written output since the last time we started a new set of headers
+			if lastMapping != nil && !g.NoHeaders && trackingWriter.Written > 0 {
+				separatorWriter.SetReady(true)
 			}
-			return err
+
+			printer, err = g.ToPrinter(mapping, nil, printWithNamespace, printWithKind)
+			if err != nil {
+				if !errs.Has(err.Error()) {
+					errs.Insert(err.Error())
+					allErrs = append(allErrs, err)
+				}
+				continue
+			}
+			lastMapping = mapping
+		}
+
+		if ix == len(objs)-1 || ix < len(objs)-1 && objs[ix].Mapping.Resource != objs[ix+1].Mapping.Resource {
+			table := &metav1.Table{}
+			allTableRows, mapping, err := g.reconstructionRow(sameKind, table)
+			if err != nil {
+				return err
+			}
+			table.Rows = allTableRows
+
+			setNoAdoption(mapping)
+			setColumnDefinition(table)
+
+			printObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(table)
+			if err != nil {
+				return err
+			}
+			newPrintObj := &unstructured.Unstructured{Object: printObj}
+
+			err = printer.PrintObj(newPrintObj, w)
+			if err != nil {
+				return err
+			}
+
+			sameKind = make([]Obj, 0)
 		}
-		//lastMapping = mapping
-	}
-	err = printer.PrintObj(newPrintObj, w)
-	if err != nil {
-		return err
 	}
+
 	w.Flush()
+	if trackingWriter.Written == 0 && len(allErrs) == 0 {
+		msg := fmt.Sprintf("%v from server (NotFound)", args)
+		fmt.Println(msg)
+	}
 
 	return utilerrors.NewAggregate(allErrs)
 }
@@ -339,7 +376,8 @@ func (g *CommandGetOptions) reconstructionRow(objs []Obj, table *metav1.Table) (
 		}
 		for rowIdx := range table.Rows {
 			var tempRow metav1.TableRow
-			rbKey := getRBKey(mapping.Resource, table.Rows[rowIdx], objs[ix].Cluster)
+			rbKey := getRBKey(mapping.GroupVersionKind, table.Rows[rowIdx], objs[ix].Cluster)
+
 			tempRow.Cells = append(append(tempRow.Cells, table.Rows[rowIdx].Cells[0], objs[ix].Cluster), table.Rows[rowIdx].Cells[1:]...)
 			if _, ok := RBInfo[rbKey]; ok {
 				tempRow.Cells = append(tempRow.Cells, "Y")
@@ -404,7 +442,7 @@ func clusterInfoInit(g *CommandGetOptions, karmadaConfig KarmadaConfig, clusterI
 		return nil, fmt.Errorf("method getClusterInKarmada get cluster info in karmada failed, err is: %w", err)
 	}
 
-	if err := getRBInKarmada(g.Namespace, karmadaclient); err != nil {
+	if err := g.getRBInKarmada(g.Namespace, karmadaclient); err != nil {
 		return nil, err
 	}
 
@@ -434,22 +472,45 @@ func (g *CommandGetOptions) transformRequests(req *rest.Request) {
 	}, ","))
 }
 
-func getRBInKarmada(namespace string, config *rest.Config) error {
-	resourceList := &workv1alpha2.ResourceBindingList{}
+func (g *CommandGetOptions) getRBInKarmada(namespace string, config *rest.Config) error {
+	rbList := &workv1alpha2.ResourceBindingList{}
+	crbList := &workv1alpha2.ClusterResourceBindingList{}
+
 	gClient, err := gclient.NewForConfig(config)
 	if err != nil {
 		return err
 	}
-	if err = gClient.List(context.TODO(), resourceList, &client.ListOptions{
-		LabelSelector: labels.SelectorFromSet(labels.Set{
-			policyv1alpha1.PropagationPolicyNamespaceLabel: namespace,
-		})}); err != nil {
+
+	if !g.AllNamespaces {
+		err = gClient.List(context.TODO(), rbList, &client.ListOptions{
+			Namespace: namespace,
+		})
+	} else {
+		err = gClient.List(context.TODO(), rbList, &client.ListOptions{})
+	}
+	if err != nil {
 		return err
 	}
 
-	for idx := range resourceList.Items {
-		rbKey := resourceList.Items[idx].GetName()
-		val := resourceList.Items[idx].Status.AggregatedStatus
+	if err = gClient.List(context.TODO(), crbList, &client.ListOptions{}); err != nil {
+		return err
+	}
+
+	for idx := range rbList.Items {
+		rbKey := rbList.Items[idx].GetName()
+		val := rbList.Items[idx].Status.AggregatedStatus
+		for i := range val {
+			if val[i].Applied && val[i].ClusterName != "" {
+				newRBKey := fmt.Sprintf("%s-%s", val[i].ClusterName, rbKey)
+				RBInfo[newRBKey] = &OtherPrint{
+					Applied: val[i].Applied,
+				}
+			}
+		}
+	}
+	for idx := range crbList.Items {
+		rbKey := crbList.Items[idx].GetName()
+		val := crbList.Items[idx].Status.AggregatedStatus
 		for i := range val {
 			if val[i].Applied && val[i].ClusterName != "" {
 				newRBKey := fmt.Sprintf("%s-%s", val[i].ClusterName, rbKey)
@@ -498,20 +559,24 @@ func getClusterInKarmada(client *rest.Config, clusterInfos map[string]*ClusterIn
 	return nil
 }
 
-func getRBKey(groupResource schema.GroupVersionResource, row metav1.TableRow, cluster string) string {
-	rbKey, _ := row.Cells[0].(string)
-	var suffix string
-	switch groupResource.Resource {
-	case "deployments":
-		suffix = "deployment"
-	case "services":
-		suffix = "service"
-	case "daemonsets":
-		suffix = "daemonset"
-	default:
-		suffix = groupResource.Resource
-	}
-	return fmt.Sprintf("%s-%s-%s", cluster, rbKey, suffix)
+func getRBKey(gvk schema.GroupVersionKind, row metav1.TableRow, cluster string) string {
+	resourceName, _ := row.Cells[0].(string)
+	rbKey := names.GenerateBindingName(gvk.Kind, resourceName)
+
+	return fmt.Sprintf("%s-%s", cluster, rbKey)
+}
+
+func multipleGVKsRequested(objs []Obj) bool {
+	if len(objs) < 2 {
+		return false
+	}
+	gvk := objs[0].Mapping.GroupVersionKind
+	for _, obj := range objs {
+		if obj.Mapping.GroupVersionKind != gvk {
+			return true
+		}
+	}
+	return false
 }
 
 // setNoAdoption set pod no print adoption