From acbb6c2de71c2e121c6df32bf73fc35ef40db8fb Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Wed, 6 Sep 2023 14:53:43 -0700 Subject: [PATCH 01/11] tree view feature for app get cli comand Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- cmd/argocd/commands/app.go | 56 ++++++++++++++++++++++++- cmd/argocd/commands/tree.go | 83 +++++++++++++++++++++++++++++++++++++ go.mod | 3 ++ go.sum | 6 +++ 4 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 cmd/argocd/commands/tree.go diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index d0da5e1781e76..6720f563b011f 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -19,6 +19,8 @@ import ( "github.com/argoproj/gitops-engine/pkg/sync/hook" "github.com/argoproj/gitops-engine/pkg/sync/ignore" "github.com/argoproj/gitops-engine/pkg/utils/kube" + "github.com/fatih/color" + "github.com/gosuri/uitable" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" "github.com/mattn/go-isatty" log "github.com/sirupsen/logrus" @@ -53,6 +55,8 @@ import ( "github.com/argoproj/argo-cd/v2/util/text/label" ) +type void struct{} + // NewApplicationCommand returns a new instance of an `argocd app` command func NewApplicationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ @@ -259,6 +263,33 @@ func hasAppChanged(appReq, appRes *argoappv1.Application, upsert bool) bool { return true } +func parentChildDetails(appIf application.ApplicationServiceClient, ctx context.Context, appName string, appNs string) (mapUidToNode map[string]argoappv1.ResourceNode, mapParentToChild map[string][]string, parentNode map[string]void) { + + mapUidToNode = make(map[string]argoappv1.ResourceNode) + mapParentToChild = make(map[string][]string) + parentNode = make(map[string]void) + var member void + resourceTree, err := appIf.ResourceTree(ctx, &application.ResourcesQuery{Name: &appName, AppNamespace: &appNs, ApplicationName: &appName}) + errors.CheckError(err) + + for _, node := range resourceTree.Nodes { + mapUidToNode[node.UID] = node + + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp + } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = member + } + + } + return +} + // NewApplicationGetCommand returns a new instance of an `argocd app get` command func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( @@ -268,12 +299,13 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com showParams bool showOperation bool ) + var command = &cobra.Command{ Use: "get APPNAME", Short: "Get application details", Run: func(c *cobra.Command, args []string) { ctx := c.Context() - + output, _ = c.Flags().GetString("output") if len(args) == 0 { c.HelpFunc()(c, args) os.Exit(1) @@ -283,11 +315,13 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com defer argoio.Close(conn) appName, appNs := argo.ParseFromQualifiedName(args[0], "") + app, err := appIf.Get(ctx, &application.ApplicationQuery{ Name: &appName, Refresh: getRefreshType(refresh, hardRefresh), AppNamespace: &appNs, }) + errors.CheckError(err) pConn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie() @@ -319,18 +353,28 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com if showParams { printParams(app) } + if len(app.Status.Resources) > 0 { fmt.Println() w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) printAppResources(w, app) _ = w.Flush() } + case "tree": + mapUidToNode, mapParentToChild, parentNode := parentChildDetails(appIf, ctx, appName, appNs) + mapNodeNameToResourceState := make(map[string]*resourceState) + for _, res := range getResourceStates(app, nil) { + mapNodeNameToResourceState[res.Kind+"/"+res.Name] = res + } + if len(mapUidToNode) > 0 { + printTreeView(os.Stderr, mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) + } default: errors.CheckError(fmt.Errorf("unknown output format: %s", output)) } }, } - command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide") + command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|tree") command.Flags().BoolVar(&showOperation, "show-operation", false, "Show application operation") command.Flags().BoolVar(&showParams, "show-params", false, "Show application parameters and overrides") command.Flags().BoolVar(&refresh, "refresh", false, "Refresh application data when retrieving") @@ -1521,6 +1565,14 @@ func printAppResources(w io.Writer, app *argoappv1.Application) { } } +func printTreeView(w io.Writer, nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parent map[string]void, mapNodeNameToResourceState map[string]*resourceState) { + tbl := uitable.New() + tbl.Separator = " " + tbl.AddRow("GROUP", "NAMESPACE", "KIND", "NAME", "STATUS", "HEALTH", "HOOK", "MESSAGE") + treeView(tbl, nodeMapping, parentChildMapping, parent, mapNodeNameToResourceState) + fmt.Fprintln(color.Output, tbl) +} + // NewApplicationSyncCommand returns a new instance of an `argocd app sync` command func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var ( diff --git a/cmd/argocd/commands/tree.go b/cmd/argocd/commands/tree.go new file mode 100644 index 0000000000000..27933c7c972a7 --- /dev/null +++ b/cmd/argocd/commands/tree.go @@ -0,0 +1,83 @@ +package commands + +import ( + "fmt" + "strings" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/fatih/color" + "github.com/gosuri/uitable" +) + +const ( + firstElemPrefix = `├─` + lastElemPrefix = `└─` + indent = " " + pipe = `│ ` +) + +var ( + gray = color.New(color.FgHiBlack) +) + +// treeView prints object hierarchy to out stream. +func treeView(tbl *uitable.Table, objs map[string]v1alpha1.ResourceNode, obj map[string][]string, parentNodes map[string]void, mapNodeNameToResourceState map[string]*resourceState) { + for uid := range parentNodes { + treeViewInner("", tbl, objs, obj, objs[uid], mapNodeNameToResourceState) + } + +} + +func treeViewInner(prefix string, tbl *uitable.Table, objs map[string]v1alpha1.ResourceNode, obj map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState) { + if mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] != nil { + value := mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] + + tbl.AddRow(value.Group, value.Namespace, value.Kind, fmt.Sprintf("%s%s/%s", + gray.Sprint(printPrefix(prefix)), + parent.Kind, + color.New(color.Bold).Sprint(parent.Name)), + value.Status, + value.Health, + value.Hook, + value.Message) + } else { + tbl.AddRow(parent.Group, parent.Namespace, parent.Kind, fmt.Sprintf("%s%s/%s", + gray.Sprint(printPrefix(prefix)), + parent.Kind, + color.New(color.Bold).Sprint(parent.Name)), + "", + "", + "", + "") + + } + + chs := obj[parent.UID] + for i, child := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + treeViewInner(p, tbl, objs, obj, objs[child], mapNodeNameToResourceState) + } + +} + +func printPrefix(p string) string { + // this part is hacky af + if strings.HasSuffix(p, firstElemPrefix) { + p = strings.Replace(p, firstElemPrefix, pipe, strings.Count(p, firstElemPrefix)-1) + } else { + p = strings.ReplaceAll(p, firstElemPrefix, pipe) + } + + if strings.HasSuffix(p, lastElemPrefix) { + p = strings.Replace(p, lastElemPrefix, strings.Repeat(" ", len([]rune(lastElemPrefix))), strings.Count(p, lastElemPrefix)-1) + } else { + p = strings.ReplaceAll(p, lastElemPrefix, strings.Repeat(" ", len([]rune(lastElemPrefix)))) + } + return p +} diff --git a/go.mod b/go.mod index 2600734150ad1..720a624fc0812 100644 --- a/go.mod +++ b/go.mod @@ -124,8 +124,11 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.18.0 // indirect github.com/aws/smithy-go v1.13.5 // indirect + github.com/fatih/color v1.12.0 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/gosuri/uitable v0.0.4 // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect diff --git a/go.sum b/go.sum index 84c5a4b6e0ca6..7551efdb2c4ce 100644 --- a/go.sum +++ b/go.sum @@ -950,6 +950,8 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+ne github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -1288,6 +1290,8 @@ github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregdel/pushover v1.2.1 h1:IPPJCdzXz60gMqnlzS0ZAW5z5aS1gI4nU+YM0Pe+ssA= github.com/gregdel/pushover v1.2.1/go.mod h1:EcaO66Nn1StkpEm1iKtBTV3d2A16SoMsVER1PthX7to= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -1473,6 +1477,8 @@ github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= From 3bda357aa021402dfefb11d2404250ade7c25a4f Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Wed, 6 Sep 2023 16:38:16 -0700 Subject: [PATCH 02/11] including application details Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- cmd/argocd/commands/app.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index 6720f563b011f..f6fc695f6220f 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -361,6 +361,23 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com _ = w.Flush() } case "tree": + aURL := appURL(ctx, acdClient, app.Name) + printAppSummaryTable(app, aURL, windows) + + if len(app.Status.Conditions) > 0 { + fmt.Println() + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + printAppConditions(w, app) + _ = w.Flush() + fmt.Println() + } + if showOperation && app.Status.OperationState != nil { + fmt.Println() + printOperationResult(app.Status.OperationState) + } + if showParams { + printParams(app) + } mapUidToNode, mapParentToChild, parentNode := parentChildDetails(appIf, ctx, appName, appNs) mapNodeNameToResourceState := make(map[string]*resourceState) for _, res := range getResourceStates(app, nil) { From aea9ee3b0484b92ec4c657de070e944064e0e124 Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Thu, 7 Sep 2023 01:37:24 -0700 Subject: [PATCH 03/11] testcase included for printTreeView Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- cmd/argocd/commands/app.go | 5 ++-- cmd/argocd/commands/app_test.go | 42 +++++++++++++++++++++++++++++++++ go.mod | 4 ++-- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index f6fc695f6220f..a3e1b8282fcd8 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -19,7 +19,6 @@ import ( "github.com/argoproj/gitops-engine/pkg/sync/hook" "github.com/argoproj/gitops-engine/pkg/sync/ignore" "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/fatih/color" "github.com/gosuri/uitable" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" "github.com/mattn/go-isatty" @@ -273,6 +272,7 @@ func parentChildDetails(appIf application.ApplicationServiceClient, ctx context. errors.CheckError(err) for _, node := range resourceTree.Nodes { + mapUidToNode[node.UID] = node if len(node.ParentRefs) > 0 { @@ -1583,11 +1583,12 @@ func printAppResources(w io.Writer, app *argoappv1.Application) { } func printTreeView(w io.Writer, nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parent map[string]void, mapNodeNameToResourceState map[string]*resourceState) { + w = os.Stdout tbl := uitable.New() tbl.Separator = " " tbl.AddRow("GROUP", "NAMESPACE", "KIND", "NAME", "STATUS", "HEALTH", "HOOK", "MESSAGE") treeView(tbl, nodeMapping, parentChildMapping, parent, mapNodeNameToResourceState) - fmt.Fprintln(color.Output, tbl) + fmt.Fprintln(w, tbl) } // NewApplicationSyncCommand returns a new instance of an `argocd app sync` command diff --git a/cmd/argocd/commands/app_test.go b/cmd/argocd/commands/app_test.go index 0880fdc1c1ae5..838df70a90277 100644 --- a/cmd/argocd/commands/app_test.go +++ b/cmd/argocd/commands/app_test.go @@ -1,6 +1,7 @@ package commands import ( + "bytes" "fmt" "os" "testing" @@ -115,6 +116,47 @@ func TestFindRevisionHistoryWithoutPassedId(t *testing.T) { } +func TestPrintTreeView(t *testing.T) { + var buf bytes.Buffer + var nodes [3]v1alpha1.ResourceNode + nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} + nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} + nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + var member void + var nodeMapping = make(map[string]v1alpha1.ResourceNode) + var mapParentToChild = make(map[string][]string) + var parentNode = make(map[string]void) + + for _, node := range nodes { + nodeMapping[node.UID] = node + + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp + } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = member + } + } + + output, _ := captureOutput(func() error { + printTreeView(&buf, nodeMapping, mapParentToChild, parentNode, nil) + return nil + }) + + assert.Contains(t, output, "Pod") + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout-demo-5dcd5457d5-6trpt") + assert.Contains(t, output, "argoproj.io") + +} + func TestDefaultWaitOptions(t *testing.T) { watch := watchOpts{ sync: false, diff --git a/go.mod b/go.mod index 720a624fc0812..99c4326cc311f 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/cyphar/filepath-securejoin v0.2.3 github.com/dustin/go-humanize v1.0.1 github.com/evanphx/json-patch v5.6.0+incompatible + github.com/fatih/color v1.12.0 github.com/fsnotify/fsnotify v1.6.0 github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e github.com/go-git/go-git/v5 v5.8.1 @@ -45,6 +46,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 github.com/gosimple/slug v1.13.1 + github.com/gosuri/uitable v0.0.4 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -124,9 +126,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.18.0 // indirect github.com/aws/smithy-go v1.13.5 // indirect - github.com/fatih/color v1.12.0 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/gosuri/uitable v0.0.4 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.8 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect From e3abe5e643df8d6f892d751f08ea99a98b3bca10 Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Thu, 7 Sep 2023 02:26:24 -0700 Subject: [PATCH 04/11] removed the unnecessary variables Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- cmd/argocd/commands/app.go | 10 ++++++---- cmd/argocd/commands/app_test.go | 4 +--- cmd/argocd/commands/tree.go | 12 ++---------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index a3e1b8282fcd8..19853c8b1096f 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -384,7 +384,7 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com mapNodeNameToResourceState[res.Kind+"/"+res.Name] = res } if len(mapUidToNode) > 0 { - printTreeView(os.Stderr, mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) + printTreeView(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) } default: errors.CheckError(fmt.Errorf("unknown output format: %s", output)) @@ -1582,12 +1582,14 @@ func printAppResources(w io.Writer, app *argoappv1.Application) { } } -func printTreeView(w io.Writer, nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parent map[string]void, mapNodeNameToResourceState map[string]*resourceState) { - w = os.Stdout +func printTreeView(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]void, mapNodeNameToResourceState map[string]*resourceState) { + w := os.Stdout tbl := uitable.New() tbl.Separator = " " tbl.AddRow("GROUP", "NAMESPACE", "KIND", "NAME", "STATUS", "HEALTH", "HOOK", "MESSAGE") - treeView(tbl, nodeMapping, parentChildMapping, parent, mapNodeNameToResourceState) + for uid := range parentNodes { + treeViewInnerGet("", tbl, nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState) + } fmt.Fprintln(w, tbl) } diff --git a/cmd/argocd/commands/app_test.go b/cmd/argocd/commands/app_test.go index 838df70a90277..ad0ff87101ca3 100644 --- a/cmd/argocd/commands/app_test.go +++ b/cmd/argocd/commands/app_test.go @@ -1,7 +1,6 @@ package commands import ( - "bytes" "fmt" "os" "testing" @@ -117,7 +116,6 @@ func TestFindRevisionHistoryWithoutPassedId(t *testing.T) { } func TestPrintTreeView(t *testing.T) { - var buf bytes.Buffer var nodes [3]v1alpha1.ResourceNode nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} @@ -145,7 +143,7 @@ func TestPrintTreeView(t *testing.T) { } output, _ := captureOutput(func() error { - printTreeView(&buf, nodeMapping, mapParentToChild, parentNode, nil) + printTreeView(nodeMapping, mapParentToChild, parentNode, nil) return nil }) diff --git a/cmd/argocd/commands/tree.go b/cmd/argocd/commands/tree.go index 27933c7c972a7..f255dfe719f12 100644 --- a/cmd/argocd/commands/tree.go +++ b/cmd/argocd/commands/tree.go @@ -20,15 +20,7 @@ var ( gray = color.New(color.FgHiBlack) ) -// treeView prints object hierarchy to out stream. -func treeView(tbl *uitable.Table, objs map[string]v1alpha1.ResourceNode, obj map[string][]string, parentNodes map[string]void, mapNodeNameToResourceState map[string]*resourceState) { - for uid := range parentNodes { - treeViewInner("", tbl, objs, obj, objs[uid], mapNodeNameToResourceState) - } - -} - -func treeViewInner(prefix string, tbl *uitable.Table, objs map[string]v1alpha1.ResourceNode, obj map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState) { +func treeViewInnerGet(prefix string, tbl *uitable.Table, objs map[string]v1alpha1.ResourceNode, obj map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState) { if mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] != nil { value := mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] @@ -61,7 +53,7 @@ func treeViewInner(prefix string, tbl *uitable.Table, objs map[string]v1alpha1.R default: p = prefix + firstElemPrefix } - treeViewInner(p, tbl, objs, obj, objs[child], mapNodeNameToResourceState) + treeViewInnerGet(p, tbl, objs, obj, objs[child], mapNodeNameToResourceState) } } From 1537d7355b0f899b4304025c53ab43b866e60430 Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Thu, 7 Sep 2023 02:43:57 -0700 Subject: [PATCH 05/11] Adding changes to the documentation Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- docs/user-guide/commands/argocd_app_get.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/user-guide/commands/argocd_app_get.md b/docs/user-guide/commands/argocd_app_get.md index ac027526072bd..cb5ec781a5286 100644 --- a/docs/user-guide/commands/argocd_app_get.md +++ b/docs/user-guide/commands/argocd_app_get.md @@ -11,7 +11,7 @@ argocd app get APPNAME [flags] ``` --hard-refresh Refresh application data as well as target manifests cache -h, --help help for get - -o, --output string Output format. One of: json|yaml|wide (default "wide") + -o, --output string Output format. One of: json|yaml|wide|tree (default "wide") --refresh Refresh application data when retrieving --show-operation Show application operation --show-params Show application parameters and overrides @@ -47,5 +47,4 @@ argocd app get APPNAME [flags] ### SEE ALSO -* [argocd app](argocd_app.md) - Manage applications - +- [argocd app](argocd_app.md) - Manage applications From b5c2833c948ad76816e02cb44f742c2fb4f7998d Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Thu, 7 Sep 2023 13:27:29 -0700 Subject: [PATCH 06/11] change in the argocd doc Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- docs/user-guide/commands/argocd_app_get.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/user-guide/commands/argocd_app_get.md b/docs/user-guide/commands/argocd_app_get.md index cb5ec781a5286..1269386486294 100644 --- a/docs/user-guide/commands/argocd_app_get.md +++ b/docs/user-guide/commands/argocd_app_get.md @@ -47,4 +47,5 @@ argocd app get APPNAME [flags] ### SEE ALSO -- [argocd app](argocd_app.md) - Manage applications +* [argocd app](argocd_app.md) - Manage applications + From e4c54ed59db684e9fabbb6058223328c92c42fff Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Sat, 9 Sep 2023 20:02:22 -0700 Subject: [PATCH 07/11] included tee_test.go Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- cmd/argocd/commands/app.go | 23 +++++---- cmd/argocd/commands/app_test.go | 6 +-- cmd/argocd/commands/tree.go | 7 ++- cmd/argocd/commands/tree_test.go | 89 ++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 cmd/argocd/commands/tree_test.go diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index 19853c8b1096f..be08ec7c79045 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -54,8 +54,6 @@ import ( "github.com/argoproj/argo-cd/v2/util/text/label" ) -type void struct{} - // NewApplicationCommand returns a new instance of an `argocd app` command func NewApplicationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var command = &cobra.Command{ @@ -262,12 +260,12 @@ func hasAppChanged(appReq, appRes *argoappv1.Application, upsert bool) bool { return true } -func parentChildDetails(appIf application.ApplicationServiceClient, ctx context.Context, appName string, appNs string) (mapUidToNode map[string]argoappv1.ResourceNode, mapParentToChild map[string][]string, parentNode map[string]void) { +func parentChildDetails(appIf application.ApplicationServiceClient, ctx context.Context, appName string, appNs string) (mapUidToNode map[string]argoappv1.ResourceNode, mapParentToChild map[string][]string, parentNode map[string]struct{}) { mapUidToNode = make(map[string]argoappv1.ResourceNode) mapParentToChild = make(map[string][]string) - parentNode = make(map[string]void) - var member void + parentNode = make(map[string]struct{}) + resourceTree, err := appIf.ResourceTree(ctx, &application.ResourcesQuery{Name: &appName, AppNamespace: &appNs, ApplicationName: &appName}) errors.CheckError(err) @@ -283,7 +281,7 @@ func parentChildDetails(appIf application.ApplicationServiceClient, ctx context. } mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) } else { - parentNode[node.UID] = member + parentNode[node.UID] = struct{}{} } } @@ -1582,13 +1580,18 @@ func printAppResources(w io.Writer, app *argoappv1.Application) { } } -func printTreeView(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]void, mapNodeNameToResourceState map[string]*resourceState) { +func tableConfig() (tbl *uitable.Table) { + tbl = uitable.New() + tbl.Separator = " " + return +} + +func printTreeView(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState) { w := os.Stdout - tbl := uitable.New() - tbl.Separator = " " + tbl := tableConfig() tbl.AddRow("GROUP", "NAMESPACE", "KIND", "NAME", "STATUS", "HEALTH", "HOOK", "MESSAGE") for uid := range parentNodes { - treeViewInnerGet("", tbl, nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState) + treeViewAppGet("", tbl, nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState) } fmt.Fprintln(w, tbl) } diff --git a/cmd/argocd/commands/app_test.go b/cmd/argocd/commands/app_test.go index ad0ff87101ca3..a67543125e187 100644 --- a/cmd/argocd/commands/app_test.go +++ b/cmd/argocd/commands/app_test.go @@ -122,10 +122,10 @@ func TestPrintTreeView(t *testing.T) { nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} - var member void + var nodeMapping = make(map[string]v1alpha1.ResourceNode) var mapParentToChild = make(map[string][]string) - var parentNode = make(map[string]void) + var parentNode = make(map[string]struct{}) for _, node := range nodes { nodeMapping[node.UID] = node @@ -138,7 +138,7 @@ func TestPrintTreeView(t *testing.T) { } mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) } else { - parentNode[node.UID] = member + parentNode[node.UID] = struct{}{} } } diff --git a/cmd/argocd/commands/tree.go b/cmd/argocd/commands/tree.go index f255dfe719f12..a6d4051e8b7f6 100644 --- a/cmd/argocd/commands/tree.go +++ b/cmd/argocd/commands/tree.go @@ -20,7 +20,7 @@ var ( gray = color.New(color.FgHiBlack) ) -func treeViewInnerGet(prefix string, tbl *uitable.Table, objs map[string]v1alpha1.ResourceNode, obj map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState) { +func treeViewAppGet(prefix string, tbl *uitable.Table, objs map[string]v1alpha1.ResourceNode, obj map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState) { if mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] != nil { value := mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] @@ -43,7 +43,6 @@ func treeViewInnerGet(prefix string, tbl *uitable.Table, objs map[string]v1alpha "") } - chs := obj[parent.UID] for i, child := range chs { var p string @@ -53,13 +52,13 @@ func treeViewInnerGet(prefix string, tbl *uitable.Table, objs map[string]v1alpha default: p = prefix + firstElemPrefix } - treeViewInnerGet(p, tbl, objs, obj, objs[child], mapNodeNameToResourceState) + treeViewAppGet(p, tbl, objs, obj, objs[child], mapNodeNameToResourceState) } } func printPrefix(p string) string { - // this part is hacky af + if strings.HasSuffix(p, firstElemPrefix) { p = strings.Replace(p, firstElemPrefix, pipe, strings.Count(p, firstElemPrefix)-1) } else { diff --git a/cmd/argocd/commands/tree_test.go b/cmd/argocd/commands/tree_test.go new file mode 100644 index 0000000000000..14ac00e6563dd --- /dev/null +++ b/cmd/argocd/commands/tree_test.go @@ -0,0 +1,89 @@ +package commands + +import ( + "testing" + + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" + "github.com/gosuri/uitable" + "github.com/stretchr/testify/assert" +) + +func TestTreeViewAppGet(t *testing.T) { + var parent v1alpha1.ResourceNode + parent.ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + objs := make(map[string]v1alpha1.ResourceNode) + objs["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = parent + var child v1alpha1.ResourceNode + child.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + child.ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + + objs["75c30dce-1b66-414f-a86c-573a74be0f40"] = child + + childMapping := make(map[string][]string) + childMapping["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = []string{"75c30dce-1b66-414f-a86c-573a74be0f40"} + + stateMap := make(map[string]*resourceState) + stateMap["Rollout/numalogic-rollout-demo"] = &resourceState{ + Status: "Running", + Health: "Healthy", + Hook: "", + Message: "No Issues", + Name: "sandbox-rollout-numalogic-demo", + Kind: "Rollout", + Group: "argoproj.io", + } + + tbl := uitable.New() + + treeViewAppGet("", tbl, objs, childMapping, parent, stateMap) + + output := tbl.String() + + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout") + assert.Contains(t, output, "Healthy") + assert.Contains(t, output, "No Issues") + assert.Contains(t, output, "argoproj.io") +} + +func TestPrintPrefix(t *testing.T) { + tests := []struct { + input string + expected string + name string + }{ + { + input: "", + expected: "", + name: "empty string", + }, + { + input: firstElemPrefix, + expected: firstElemPrefix, + name: "only first element prefix", + }, + { + input: lastElemPrefix, + expected: lastElemPrefix, + name: "only last element prefix", + }, + { + input: firstElemPrefix + firstElemPrefix, + expected: pipe + firstElemPrefix, + name: "double first element prefix", + }, + { + input: firstElemPrefix + lastElemPrefix, + expected: pipe + lastElemPrefix, + name: "first then last element prefix", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := printPrefix(test.input) + assert.Equal(t, test.expected, got) + }) + } +} From 9b5d9eeb01b415db20605fef2f26d30c49769f43 Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Tue, 12 Sep 2023 14:55:45 -0700 Subject: [PATCH 08/11] tree view changes for app get and app resources Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- cmd/argocd/commands/app.go | 54 +++++-- cmd/argocd/commands/app_resource_test.go | 82 +++++++++- cmd/argocd/commands/app_resources.go | 111 ++++++++++++-- cmd/argocd/commands/app_test.go | 44 +++++- cmd/argocd/commands/tree.go | 141 +++++++++++++++--- cmd/argocd/commands/tree_test.go | 135 ++++++++++++++++- docs/user-guide/commands/argocd_app_logs.md | 4 +- .../commands/argocd_app_resources.md | 5 +- 8 files changed, 512 insertions(+), 64 deletions(-) diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index 5f6c4570989fa..7cb2a1dd5b726 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -19,7 +19,6 @@ import ( "github.com/argoproj/gitops-engine/pkg/sync/hook" "github.com/argoproj/gitops-engine/pkg/sync/ignore" "github.com/argoproj/gitops-engine/pkg/utils/kube" - "github.com/gosuri/uitable" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" "github.com/mattn/go-isatty" log "github.com/sirupsen/logrus" @@ -384,6 +383,33 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com if len(mapUidToNode) > 0 { printTreeView(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) } + case "tree=detailed": + aURL := appURL(ctx, acdClient, app.Name) + printAppSummaryTable(app, aURL, windows) + + if len(app.Status.Conditions) > 0 { + fmt.Println() + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + printAppConditions(w, app) + _ = w.Flush() + fmt.Println() + } + if showOperation && app.Status.OperationState != nil { + fmt.Println() + printOperationResult(app.Status.OperationState) + } + if showParams { + printParams(app) + } + mapUidToNode, mapParentToChild, parentNode := parentChildDetails(appIf, ctx, appName, appNs) + mapNodeNameToResourceState := make(map[string]*resourceState) + for _, res := range getResourceStates(app, nil) { + mapNodeNameToResourceState[res.Kind+"/"+res.Name] = res + } + if len(mapUidToNode) > 0 { + fmt.Println() + printTreeViewDetailed(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) + } default: errors.CheckError(fmt.Errorf("unknown output format: %s", output)) } @@ -479,12 +505,12 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co command.Flags().StringVar(&kind, "kind", "", "Resource kind") command.Flags().StringVar(&namespace, "namespace", "", "Resource namespace") command.Flags().StringVar(&resourceName, "name", "", "Resource name") - command.Flags().BoolVarP(&follow, "follow", "f", false, "Specify if the logs should be streamed") + command.Flags().BoolVar(&follow, "follow", false, "Specify if the logs should be streamed") command.Flags().Int64Var(&tail, "tail", 0, "The number of lines from the end of the logs to show") command.Flags().Int64Var(&sinceSeconds, "since-seconds", 0, "A relative time in seconds before the current time from which to show logs") command.Flags().StringVar(&untilTime, "until-time", "", "Show logs until this time") command.Flags().StringVar(&filter, "filter", "", "Show logs contain this string") - command.Flags().StringVarP(&container, "container", "c", "", "Optional container name") + command.Flags().StringVar(&container, "container", "", "Optional container name") command.Flags().BoolVarP(&previous, "previous", "p", false, "Specify if the previously terminated container logs should be returned") return command @@ -1580,20 +1606,22 @@ func printAppResources(w io.Writer, app *argoappv1.Application) { } } -func tableConfig() (tbl *uitable.Table) { - tbl = uitable.New() - tbl.Separator = " " - return +func printTreeView(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState) { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + _, _ = fmt.Fprintf(w, "KIND/NAME\tSTATUS\tHEALTH\tMESSAGE\n") + for uid := range parentNodes { + treeViewAppGet("", nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState, w) + } + _ = w.Flush() } -func printTreeView(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState) { - w := os.Stdout - tbl := tableConfig() - tbl.AddRow("GROUP", "NAMESPACE", "KIND", "NAME", "STATUS", "HEALTH", "HOOK", "MESSAGE") +func printTreeViewDetailed(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState) { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + fmt.Fprintf(w, "KIND/NAME\tSTATUS\tHEALTH\tAGE\tMESSAGE\tREASON\n") for uid := range parentNodes { - treeViewAppGet("", tbl, nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState) + detailedTreeViewAppGet("", nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState, w) } - fmt.Fprintln(w, tbl) + _ = w.Flush() } // NewApplicationSyncCommand returns a new instance of an `argocd app sync` command diff --git a/cmd/argocd/commands/app_resource_test.go b/cmd/argocd/commands/app_resource_test.go index 2c94ad7a0f418..5846065141e15 100644 --- a/cmd/argocd/commands/app_resource_test.go +++ b/cmd/argocd/commands/app_resource_test.go @@ -1,13 +1,93 @@ package commands import ( + "bytes" "testing" + "text/tabwriter" "github.com/stretchr/testify/assert" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" ) +func TestPrintTreeViewAppResources(t *testing.T) { + var nodes [3]v1alpha1.ResourceNode + nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} + nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} + nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + var nodeMapping = make(map[string]v1alpha1.ResourceNode) + var mapParentToChild = make(map[string][]string) + var parentNode = make(map[string]struct{}) + for _, node := range nodes { + nodeMapping[node.UID] = node + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp + } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = struct{}{} + } + } + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + + printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + output := buf.String() + + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "argoproj.io") +} + +func TestPrintTreeViewDetailedAppResources(t *testing.T) { + var nodes [3]v1alpha1.ResourceNode + nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} + nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} + nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + nodes[2].Health = &v1alpha1.HealthStatus{ + Status: "Degraded", + Message: "Readiness Gate failed", + } + + var nodeMapping = make(map[string]v1alpha1.ResourceNode) + var mapParentToChild = make(map[string][]string) + var parentNode = make(map[string]struct{}) + for _, node := range nodes { + nodeMapping[node.UID] = node + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp + } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = struct{}{} + } + } + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + + printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, false, false, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + output := buf.String() + + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "Degraded") + assert.Contains(t, output, "Readiness Gate failed") +} + func TestPrintResourcesTree(t *testing.T) { tree := v1alpha1.ApplicationTree{ Nodes: []v1alpha1.ResourceNode{ @@ -32,7 +112,7 @@ func TestPrintResourcesTree(t *testing.T) { }, } output, _ := captureOutput(func() error { - printResources(true, false, &tree) + printResources(true, false, &tree, "") return nil }) diff --git a/cmd/argocd/commands/app_resources.go b/cmd/argocd/commands/app_resources.go index 60ba6efff406e..1d16e1f514a64 100644 --- a/cmd/argocd/commands/app_resources.go +++ b/cmd/argocd/commands/app_resources.go @@ -4,9 +4,8 @@ import ( "fmt" "os" - "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - "github.com/argoproj/argo-cd/v2/cmd/util" + "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -148,35 +147,114 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) return command } +func parentChildInfo(nodes []v1alpha1.ResourceNode) (mapUidToNode map[string]v1alpha1.ResourceNode, mapParentToChild map[string][]string, parentNode map[string]struct{}) { + mapUidToNode = make(map[string]v1alpha1.ResourceNode) + mapParentToChild = make(map[string][]string) + parentNode = make(map[string]struct{}) -func printResources(listAll bool, orphaned bool, appResourceTree *v1alpha1.ApplicationTree) { - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - headers := []interface{}{"GROUP", "KIND", "NAMESPACE", "NAME", "ORPHANED"} - fmtStr := "%s\t%s\t%s\t%s\t%s\n" - _, _ = fmt.Fprintf(w, fmtStr, headers...) - if !orphaned || listAll { - for _, res := range appResourceTree.Nodes { - if len(res.ParentRefs) == 0 { - _, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "No") + for _, node := range nodes { + mapUidToNode[node.UID] = node + + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = struct{}{} } } - if orphaned || listAll { - for _, res := range appResourceTree.OrphanedNodes { - _, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "Yes") + return +} + +func printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) { + for uid := range parentNodes { + detailedTreeViewAppResourcesNotOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w) + } + +} + +func printDetailedTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) { + for uid := range parentNodes { + detailedTreeViewAppResourcesOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w) + } +} + +func printTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) { + for uid := range parentNodes { + treeViewAppResourcesNotOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w) + } + +} + +func printTreeViewAppResourcesOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) { + for uid := range parentNodes { + treeViewAppResourcesOrphaned("", nodeMapping, parentChildMapping, nodeMapping[uid], w) + } +} + +func printResources(listAll bool, orphaned bool, appResourceTree *v1alpha1.ApplicationTree, output string) { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + if output == "tree=detailed" { + fmt.Fprintf(w, "GROUP\tKIND\tNAMESPACE\tNAME\tORPHANED\tAGE\tHEALTH\tREASON\n") + + if !orphaned || listAll { + mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.Nodes) + printDetailedTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w) + } + + if orphaned || listAll { + mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes) + printDetailedTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w) + } + + } else if output == "tree" { + fmt.Fprintf(w, "GROUP\tKIND\tNAMESPACE\tNAME\tORPHANED\n") + + if !orphaned || listAll { + mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.Nodes) + printTreeViewAppResourcesNotOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w) + } + + if orphaned || listAll { + mapUidToNode, mapParentToChild, parentNode := parentChildInfo(appResourceTree.OrphanedNodes) + printTreeViewAppResourcesOrphaned(mapUidToNode, mapParentToChild, parentNode, orphaned, listAll, w) } + + } else { + + headers := []interface{}{"GROUP", "KIND", "NAMESPACE", "NAME", "ORPHANED"} + fmtStr := "%s\t%s\t%s\t%s\t%s\n" + _, _ = fmt.Fprintf(w, fmtStr, headers...) + if !orphaned || listAll { + for _, res := range appResourceTree.Nodes { + if len(res.ParentRefs) == 0 { + _, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "No") + } + } + } + if orphaned || listAll { + for _, res := range appResourceTree.OrphanedNodes { + _, _ = fmt.Fprintf(w, fmtStr, res.Group, res.Kind, res.Namespace, res.Name, "Yes") + } + } + } _ = w.Flush() + } func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command { var orphaned bool + var output string var command = &cobra.Command{ Use: "resources APPNAME", Short: "List resource of application", Run: func(c *cobra.Command, args []string) { ctx := c.Context() - + output, _ = c.Flags().GetString("output") if len(args) != 1 { c.HelpFunc()(c, args) os.Exit(1) @@ -190,9 +268,10 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions) AppNamespace: &appNs, }) errors.CheckError(err) - printResources(listAll, orphaned, appResourceTree) + printResources(listAll, orphaned, appResourceTree, output) }, } command.Flags().BoolVar(&orphaned, "orphaned", false, "Lists only orphaned resources") + command.Flags().StringVar(&output, "output", "", "Provides the tree view of the resources") return command } diff --git a/cmd/argocd/commands/app_test.go b/cmd/argocd/commands/app_test.go index a67543125e187..68983560999c8 100644 --- a/cmd/argocd/commands/app_test.go +++ b/cmd/argocd/commands/app_test.go @@ -115,7 +115,7 @@ func TestFindRevisionHistoryWithoutPassedId(t *testing.T) { } -func TestPrintTreeView(t *testing.T) { +func TestPrintTreeViewAppGet(t *testing.T) { var nodes [3]v1alpha1.ResourceNode nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} @@ -151,7 +151,47 @@ func TestPrintTreeView(t *testing.T) { assert.Contains(t, output, "ReplicaSet") assert.Contains(t, output, "Rollout") assert.Contains(t, output, "numalogic-rollout-demo-5dcd5457d5-6trpt") - assert.Contains(t, output, "argoproj.io") +} + +func TestPrintTreeViewDetailedAppGet(t *testing.T) { + var nodes [3]v1alpha1.ResourceNode + nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "", Version: "v1", Kind: "Pod", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5-6trpt", UID: "92c3a5fe-d13e-4ae2-b8ec-c10dd3543b28"} + nodes[0].Health = &v1alpha1.HealthStatus{Status: "Degraded", Message: "Readiness Gate failed"} + nodes[0].ParentRefs = []v1alpha1.ResourceRef{{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"}} + nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + nodes[1].ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + + var nodeMapping = make(map[string]v1alpha1.ResourceNode) + var mapParentToChild = make(map[string][]string) + var parentNode = make(map[string]struct{}) + + for _, node := range nodes { + nodeMapping[node.UID] = node + + if len(node.ParentRefs) > 0 { + _, ok := mapParentToChild[node.ParentRefs[0].UID] + if !ok { + var temp []string + mapParentToChild[node.ParentRefs[0].UID] = temp + } + mapParentToChild[node.ParentRefs[0].UID] = append(mapParentToChild[node.ParentRefs[0].UID], node.UID) + } else { + parentNode[node.UID] = struct{}{} + } + } + + output, _ := captureOutput(func() error { + printTreeViewDetailed(nodeMapping, mapParentToChild, parentNode, nil) + return nil + }) + + assert.Contains(t, output, "Pod") + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout-demo-5dcd5457d5-6trpt") + assert.Contains(t, output, "Degraded") + assert.Contains(t, output, "Readiness Gate failed") } diff --git a/cmd/argocd/commands/tree.go b/cmd/argocd/commands/tree.go index a6d4051e8b7f6..d46e5375df130 100644 --- a/cmd/argocd/commands/tree.go +++ b/cmd/argocd/commands/tree.go @@ -3,10 +3,12 @@ package commands import ( "fmt" "strings" + "text/tabwriter" + "time" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - "github.com/fatih/color" - "github.com/gosuri/uitable" + "github.com/argoproj/gitops-engine/pkg/health" + "k8s.io/apimachinery/pkg/util/duration" ) const ( @@ -16,34 +18,105 @@ const ( pipe = `│ ` ) -var ( - gray = color.New(color.FgHiBlack) -) +func extractHealthStatusAndReason(node v1alpha1.ResourceNode) (healthStatus health.HealthStatusCode, reason string) { + if node.Health != nil { + healthStatus = node.Health.Status + reason = node.Health.Message + } + return +} -func treeViewAppGet(prefix string, tbl *uitable.Table, objs map[string]v1alpha1.ResourceNode, obj map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState) { +func treeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentToChildMap map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState, w *tabwriter.Writer) { if mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] != nil { value := mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] + _, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+value.Name, value.Status, value.Health, value.Message) + } else { + _, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+parent.Name, "", "", "") + } + chs := parentToChildMap[parent.UID] + for i, childUid := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + treeViewAppGet(p, uidToNodeMap, parentToChildMap, uidToNodeMap[childUid], mapNodeNameToResourceState, w) + } + +} + +func detailedTreeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState, w *tabwriter.Writer) { + healthStatus, reason := extractHealthStatusAndReason(parent) + var age = "" + if parent.CreatedAt != nil { + age = duration.HumanDuration(time.Since(parent.CreatedAt.Time)) + } - tbl.AddRow(value.Group, value.Namespace, value.Kind, fmt.Sprintf("%s%s/%s", - gray.Sprint(printPrefix(prefix)), - parent.Kind, - color.New(color.Bold).Sprint(parent.Name)), - value.Status, - value.Health, - value.Hook, - value.Message) + if mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] != nil { + value := mapNodeNameToResourceState[parent.Kind+"/"+parent.Name] + _, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+value.Name, value.Status, value.Health, age, value.Message, reason) } else { - tbl.AddRow(parent.Group, parent.Namespace, parent.Kind, fmt.Sprintf("%s%s/%s", - gray.Sprint(printPrefix(prefix)), - parent.Kind, - color.New(color.Bold).Sprint(parent.Name)), - "", - "", - "", - "") + _, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+parent.Name, "", healthStatus, age, "", reason) + + } + chs := parentChildMap[parent.UID] + for i, child := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + detailedTreeViewAppGet(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], mapNodeNameToResourceState, w) + } +} + +func treeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) { + if len(parent.ParentRefs) == 0 { + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "No") + } + chs := parentChildMap[parent.UID] + for i, child := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + treeViewAppResourcesNotOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w) + } +} + +func treeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) { + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes") + chs := parentChildMap[parent.UID] + for i, child := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + treeViewAppResourcesOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w) + } +} +func detailedTreeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) { + + if len(parent.ParentRefs) == 0 { + healthStatus, reason := extractHealthStatusAndReason(parent) + var age = "" + if parent.CreatedAt != nil { + age = duration.HumanDuration(time.Since(parent.CreatedAt.Time)) + } + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "No", age, healthStatus, reason) } - chs := obj[parent.UID] + chs := parentChildMap[parent.UID] for i, child := range chs { var p string switch i { @@ -52,9 +125,29 @@ func treeViewAppGet(prefix string, tbl *uitable.Table, objs map[string]v1alpha1. default: p = prefix + firstElemPrefix } - treeViewAppGet(p, tbl, objs, obj, objs[child], mapNodeNameToResourceState) + detailedTreeViewAppResourcesNotOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w) } +} + +func detailedTreeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, w *tabwriter.Writer) { + healthStatus, reason := extractHealthStatusAndReason(parent) + var age = "" + if parent.CreatedAt != nil { + age = duration.HumanDuration(time.Since(parent.CreatedAt.Time)) + } + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes", age, healthStatus, reason) + chs := parentChildMap[parent.UID] + for i, child := range chs { + var p string + switch i { + case len(chs) - 1: + p = prefix + lastElemPrefix + default: + p = prefix + firstElemPrefix + } + detailedTreeViewAppResourcesOrphaned(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], w) + } } func printPrefix(p string) string { diff --git a/cmd/argocd/commands/tree_test.go b/cmd/argocd/commands/tree_test.go index 14ac00e6563dd..91ffb9b963d01 100644 --- a/cmd/argocd/commands/tree_test.go +++ b/cmd/argocd/commands/tree_test.go @@ -1,10 +1,11 @@ package commands import ( + "bytes" "testing" + "text/tabwriter" "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" - "github.com/gosuri/uitable" "github.com/stretchr/testify/assert" ) @@ -33,18 +34,144 @@ func TestTreeViewAppGet(t *testing.T) { Group: "argoproj.io", } - tbl := uitable.New() + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + treeViewAppGet("", objs, childMapping, parent, stateMap, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + output := buf.String() + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout") + assert.Contains(t, output, "Healthy") + assert.Contains(t, output, "No Issues") +} - treeViewAppGet("", tbl, objs, childMapping, parent, stateMap) +func TestTreeViewDetailedAppGet(t *testing.T) { + var parent v1alpha1.ResourceNode + parent.ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + objs := make(map[string]v1alpha1.ResourceNode) + objs["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = parent + var child v1alpha1.ResourceNode + child.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + child.ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + child.Health = &v1alpha1.HealthStatus{Status: "Degraded", Message: "Readiness Gate failed"} + objs["75c30dce-1b66-414f-a86c-573a74be0f40"] = child - output := tbl.String() + childMapping := make(map[string][]string) + childMapping["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = []string{"75c30dce-1b66-414f-a86c-573a74be0f40"} + + stateMap := make(map[string]*resourceState) + stateMap["Rollout/numalogic-rollout-demo"] = &resourceState{ + Status: "Running", + Health: "Healthy", + Hook: "", + Message: "No Issues", + Name: "sandbox-rollout-numalogic-demo", + Kind: "Rollout", + Group: "argoproj.io", + } + + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + detailedTreeViewAppGet("", objs, childMapping, parent, stateMap, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + + output := buf.String() assert.Contains(t, output, "ReplicaSet") assert.Contains(t, output, "Rollout") assert.Contains(t, output, "numalogic-rollout") assert.Contains(t, output, "Healthy") assert.Contains(t, output, "No Issues") + assert.Contains(t, output, "Degraded") + assert.Contains(t, output, "Readiness Gate failed") +} + +func TestTreeViewAppResources(t *testing.T) { + var parent v1alpha1.ResourceNode + parent.ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + objs := make(map[string]v1alpha1.ResourceNode) + objs["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = parent + var child v1alpha1.ResourceNode + child.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + child.ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + + objs["75c30dce-1b66-414f-a86c-573a74be0f40"] = child + + childMapping := make(map[string][]string) + childMapping["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = []string{"75c30dce-1b66-414f-a86c-573a74be0f40"} + + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + + treeViewAppResourcesNotOrphaned("", objs, childMapping, parent, w) + + var orphan v1alpha1.ResourceNode + orphan.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcdnk457d5", UID: "75c30dce-1b66-41hf-a86c-573a74be0f40"} + objsOrphan := make(map[string]v1alpha1.ResourceNode) + objsOrphan["75c30dce-1b66-41hf-a86c-573a74be0f40"] = orphan + orphanchildMapping := make(map[string][]string) + orphanParent := orphan + + treeViewAppResourcesOrphaned("", objsOrphan, orphanchildMapping, orphanParent, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + output := buf.String() + + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout") + assert.Contains(t, output, "argoproj.io") + assert.Contains(t, output, "No") + assert.Contains(t, output, "Yes") + assert.Contains(t, output, "numalogic-rollout-demo-5dcdnk457d5") +} + +func TestTreeViewDetailedAppResources(t *testing.T) { + var parent v1alpha1.ResourceNode + parent.ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"} + objs := make(map[string]v1alpha1.ResourceNode) + objs["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = parent + var child v1alpha1.ResourceNode + child.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcd5457d5", UID: "75c30dce-1b66-414f-a86c-573a74be0f40"} + child.ParentRefs = []v1alpha1.ResourceRef{{Group: "argoproj.io", Version: "", Kind: "Rollout", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo", UID: "87f3aab0-f634-4b2c-959a-7ddd30675ed0"}} + objs["75c30dce-1b66-414f-a86c-573a74be0f40"] = child + childMapping := make(map[string][]string) + childMapping["87f3aab0-f634-4b2c-959a-7ddd30675ed0"] = []string{"75c30dce-1b66-414f-a86c-573a74be0f40"} + buf := &bytes.Buffer{} + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + detailedTreeViewAppResourcesNotOrphaned("", objs, childMapping, parent, w) + var orphan v1alpha1.ResourceNode + orphan.ResourceRef = v1alpha1.ResourceRef{Group: "apps", Version: "v1", Kind: "ReplicaSet", Namespace: "sandbox-rollout-numalogic-demo", Name: "numalogic-rollout-demo-5dcdnk457d5", UID: "75c30dce-1b66-41hf-a86c-573a74be0f40"} + orphan.Health = &v1alpha1.HealthStatus{ + Status: "Degraded", + Message: "Readiness Gate failed", + } + objsOrphan := make(map[string]v1alpha1.ResourceNode) + objsOrphan["75c30dce-1b66-41hf-a86c-573a74be0f40"] = orphan + + orphanchildMapping := make(map[string][]string) + orphanParent := orphan + detailedTreeViewAppResourcesOrphaned("", objsOrphan, orphanchildMapping, orphanParent, w) + if err := w.Flush(); err != nil { + t.Fatal(err) + } + output := buf.String() + + assert.Contains(t, output, "ReplicaSet") + assert.Contains(t, output, "Rollout") + assert.Contains(t, output, "numalogic-rollout") assert.Contains(t, output, "argoproj.io") + assert.Contains(t, output, "No") + assert.Contains(t, output, "Yes") + assert.Contains(t, output, "numalogic-rollout-demo-5dcdnk457d5") + assert.Contains(t, output, "Degraded") + assert.Contains(t, output, "Readiness Gate failed") } func TestPrintPrefix(t *testing.T) { diff --git a/docs/user-guide/commands/argocd_app_logs.md b/docs/user-guide/commands/argocd_app_logs.md index 1ed3d9db46baa..ae2c2d5ff0286 100644 --- a/docs/user-guide/commands/argocd_app_logs.md +++ b/docs/user-guide/commands/argocd_app_logs.md @@ -9,9 +9,9 @@ argocd app logs APPNAME [flags] ### Options ``` - -c, --container string Optional container name + --container string Optional container name --filter string Show logs contain this string - -f, --follow Specify if the logs should be streamed + --follow Specify if the logs should be streamed --group string Resource group -h, --help help for logs --kind string Resource kind diff --git a/docs/user-guide/commands/argocd_app_resources.md b/docs/user-guide/commands/argocd_app_resources.md index 936fe61229897..bfb698178f1ea 100644 --- a/docs/user-guide/commands/argocd_app_resources.md +++ b/docs/user-guide/commands/argocd_app_resources.md @@ -9,8 +9,9 @@ argocd app resources APPNAME [flags] ### Options ``` - -h, --help help for resources - --orphaned Lists only orphaned resources + -h, --help help for resources + --orphaned Lists only orphaned resources + --output string Provides the tree view of the resources ``` ### Options inherited from parent commands From f6f97a71bef3008fef90b85d8c20c62535da7e30 Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:59:55 -0700 Subject: [PATCH 09/11] go.mod and go.sum by running go mod tidy Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- go.mod | 3 --- go.sum | 6 ------ 2 files changed, 9 deletions(-) diff --git a/go.mod b/go.mod index 99c4326cc311f..2600734150ad1 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,6 @@ require ( github.com/cyphar/filepath-securejoin v0.2.3 github.com/dustin/go-humanize v1.0.1 github.com/evanphx/json-patch v5.6.0+incompatible - github.com/fatih/color v1.12.0 github.com/fsnotify/fsnotify v1.6.0 github.com/gfleury/go-bitbucket-v1 v0.0.0-20220301131131-8e7ed04b843e github.com/go-git/go-git/v5 v5.8.1 @@ -46,7 +45,6 @@ require ( github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 github.com/gosimple/slug v1.13.1 - github.com/gosuri/uitable v0.0.4 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -128,7 +126,6 @@ require ( github.com/aws/smithy-go v1.13.5 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect diff --git a/go.sum b/go.sum index 7551efdb2c4ce..84c5a4b6e0ca6 100644 --- a/go.sum +++ b/go.sum @@ -950,8 +950,6 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+ne github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -1290,8 +1288,6 @@ github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= -github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= -github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregdel/pushover v1.2.1 h1:IPPJCdzXz60gMqnlzS0ZAW5z5aS1gI4nU+YM0Pe+ssA= github.com/gregdel/pushover v1.2.1/go.mod h1:EcaO66Nn1StkpEm1iKtBTV3d2A16SoMsVER1PthX7to= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -1477,8 +1473,6 @@ github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= From 0468990b9aa18f3904ba8be6a64ddee627615699 Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:23:26 -0700 Subject: [PATCH 10/11] changes after review Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- cmd/argocd/commands/app.go | 82 +++++++-------------- cmd/argocd/commands/app_resources.go | 2 +- docs/user-guide/commands/argocd_app_logs.md | 4 +- 3 files changed, 30 insertions(+), 58 deletions(-) diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index 7cb2a1dd5b726..799f51272bc30 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -284,7 +284,27 @@ func parentChildDetails(appIf application.ApplicationServiceClient, ctx context. } } - return + return mapUidToNode, mapParentToChild, parentNode +} + +func commonOutputFunctionality(acdClient argocdclient.Client, app *argoappv1.Application, ctx context.Context, windows *argoappv1.SyncWindows, showOperation bool, showParams bool) { + aURL := appURL(ctx, acdClient, app.Name) + printAppSummaryTable(app, aURL, windows) + + if len(app.Status.Conditions) > 0 { + fmt.Println() + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + printAppConditions(w, app) + _ = w.Flush() + fmt.Println() + } + if showOperation && app.Status.OperationState != nil { + fmt.Println() + printOperationResult(app.Status.OperationState) + } + if showParams { + printParams(app) + } } // NewApplicationGetCommand returns a new instance of an `argocd app get` command @@ -333,24 +353,7 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com err := PrintResource(app, output) errors.CheckError(err) case "wide", "": - aURL := appURL(ctx, acdClient, app.Name) - printAppSummaryTable(app, aURL, windows) - - if len(app.Status.Conditions) > 0 { - fmt.Println() - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - printAppConditions(w, app) - _ = w.Flush() - fmt.Println() - } - if showOperation && app.Status.OperationState != nil { - fmt.Println() - printOperationResult(app.Status.OperationState) - } - if showParams { - printParams(app) - } - + commonOutputFunctionality(acdClient, app, ctx, windows, showOperation, showParams) if len(app.Status.Resources) > 0 { fmt.Println() w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) @@ -358,49 +361,18 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com _ = w.Flush() } case "tree": - aURL := appURL(ctx, acdClient, app.Name) - printAppSummaryTable(app, aURL, windows) - - if len(app.Status.Conditions) > 0 { - fmt.Println() - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - printAppConditions(w, app) - _ = w.Flush() - fmt.Println() - } - if showOperation && app.Status.OperationState != nil { - fmt.Println() - printOperationResult(app.Status.OperationState) - } - if showParams { - printParams(app) - } + commonOutputFunctionality(acdClient, app, ctx, windows, showOperation, showParams) mapUidToNode, mapParentToChild, parentNode := parentChildDetails(appIf, ctx, appName, appNs) mapNodeNameToResourceState := make(map[string]*resourceState) for _, res := range getResourceStates(app, nil) { mapNodeNameToResourceState[res.Kind+"/"+res.Name] = res } if len(mapUidToNode) > 0 { + fmt.Println() printTreeView(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) } case "tree=detailed": - aURL := appURL(ctx, acdClient, app.Name) - printAppSummaryTable(app, aURL, windows) - - if len(app.Status.Conditions) > 0 { - fmt.Println() - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - printAppConditions(w, app) - _ = w.Flush() - fmt.Println() - } - if showOperation && app.Status.OperationState != nil { - fmt.Println() - printOperationResult(app.Status.OperationState) - } - if showParams { - printParams(app) - } + commonOutputFunctionality(acdClient, app, ctx, windows, showOperation, showParams) mapUidToNode, mapParentToChild, parentNode := parentChildDetails(appIf, ctx, appName, appNs) mapNodeNameToResourceState := make(map[string]*resourceState) for _, res := range getResourceStates(app, nil) { @@ -505,12 +477,12 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co command.Flags().StringVar(&kind, "kind", "", "Resource kind") command.Flags().StringVar(&namespace, "namespace", "", "Resource namespace") command.Flags().StringVar(&resourceName, "name", "", "Resource name") - command.Flags().BoolVar(&follow, "follow", false, "Specify if the logs should be streamed") + command.Flags().BoolVarP(&follow, "follow", "f", false, "Specify if the logs should be streamed") command.Flags().Int64Var(&tail, "tail", 0, "The number of lines from the end of the logs to show") command.Flags().Int64Var(&sinceSeconds, "since-seconds", 0, "A relative time in seconds before the current time from which to show logs") command.Flags().StringVar(&untilTime, "until-time", "", "Show logs until this time") command.Flags().StringVar(&filter, "filter", "", "Show logs contain this string") - command.Flags().StringVar(&container, "container", "", "Optional container name") + command.Flags().StringVarP(&container, "container", "c", "", "Optional container name") command.Flags().BoolVarP(&previous, "previous", "p", false, "Specify if the previously terminated container logs should be returned") return command diff --git a/cmd/argocd/commands/app_resources.go b/cmd/argocd/commands/app_resources.go index 1d16e1f514a64..71e738808173f 100644 --- a/cmd/argocd/commands/app_resources.go +++ b/cmd/argocd/commands/app_resources.go @@ -166,7 +166,7 @@ func parentChildInfo(nodes []v1alpha1.ResourceNode) (mapUidToNode map[string]v1a parentNode[node.UID] = struct{}{} } } - return + return mapUidToNode, mapParentToChild, parentNode } func printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping map[string]v1alpha1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, orphaned bool, listAll bool, w *tabwriter.Writer) { diff --git a/docs/user-guide/commands/argocd_app_logs.md b/docs/user-guide/commands/argocd_app_logs.md index ae2c2d5ff0286..1ed3d9db46baa 100644 --- a/docs/user-guide/commands/argocd_app_logs.md +++ b/docs/user-guide/commands/argocd_app_logs.md @@ -9,9 +9,9 @@ argocd app logs APPNAME [flags] ### Options ``` - --container string Optional container name + -c, --container string Optional container name --filter string Show logs contain this string - --follow Specify if the logs should be streamed + -f, --follow Specify if the logs should be streamed --group string Resource group -h, --help help for logs --kind string Resource kind From 8c81989aeef7bdab03f84e8518868262bf1fe571 Mon Sep 17 00:00:00 2001 From: schakrad <58915923+schakrad@users.noreply.github.com> Date: Fri, 15 Sep 2023 09:41:16 -0700 Subject: [PATCH 11/11] changes after review Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com> --- cmd/argocd/commands/app.go | 16 ++++++++-------- cmd/argocd/commands/app_resources.go | 9 +++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index 799f51272bc30..2559c702b5ddc 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -259,11 +259,11 @@ func hasAppChanged(appReq, appRes *argoappv1.Application, upsert bool) bool { return true } -func parentChildDetails(appIf application.ApplicationServiceClient, ctx context.Context, appName string, appNs string) (mapUidToNode map[string]argoappv1.ResourceNode, mapParentToChild map[string][]string, parentNode map[string]struct{}) { +func parentChildDetails(appIf application.ApplicationServiceClient, ctx context.Context, appName string, appNs string) (map[string]argoappv1.ResourceNode, map[string][]string, map[string]struct{}) { - mapUidToNode = make(map[string]argoappv1.ResourceNode) - mapParentToChild = make(map[string][]string) - parentNode = make(map[string]struct{}) + mapUidToNode := make(map[string]argoappv1.ResourceNode) + mapParentToChild := make(map[string][]string) + parentNode := make(map[string]struct{}) resourceTree, err := appIf.ResourceTree(ctx, &application.ResourcesQuery{Name: &appName, AppNamespace: &appNs, ApplicationName: &appName}) errors.CheckError(err) @@ -287,7 +287,7 @@ func parentChildDetails(appIf application.ApplicationServiceClient, ctx context. return mapUidToNode, mapParentToChild, parentNode } -func commonOutputFunctionality(acdClient argocdclient.Client, app *argoappv1.Application, ctx context.Context, windows *argoappv1.SyncWindows, showOperation bool, showParams bool) { +func printHeader(acdClient argocdclient.Client, app *argoappv1.Application, ctx context.Context, windows *argoappv1.SyncWindows, showOperation bool, showParams bool) { aURL := appURL(ctx, acdClient, app.Name) printAppSummaryTable(app, aURL, windows) @@ -353,7 +353,7 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com err := PrintResource(app, output) errors.CheckError(err) case "wide", "": - commonOutputFunctionality(acdClient, app, ctx, windows, showOperation, showParams) + printHeader(acdClient, app, ctx, windows, showOperation, showParams) if len(app.Status.Resources) > 0 { fmt.Println() w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) @@ -361,7 +361,7 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com _ = w.Flush() } case "tree": - commonOutputFunctionality(acdClient, app, ctx, windows, showOperation, showParams) + printHeader(acdClient, app, ctx, windows, showOperation, showParams) mapUidToNode, mapParentToChild, parentNode := parentChildDetails(appIf, ctx, appName, appNs) mapNodeNameToResourceState := make(map[string]*resourceState) for _, res := range getResourceStates(app, nil) { @@ -372,7 +372,7 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com printTreeView(mapUidToNode, mapParentToChild, parentNode, mapNodeNameToResourceState) } case "tree=detailed": - commonOutputFunctionality(acdClient, app, ctx, windows, showOperation, showParams) + printHeader(acdClient, app, ctx, windows, showOperation, showParams) mapUidToNode, mapParentToChild, parentNode := parentChildDetails(appIf, ctx, appName, appNs) mapNodeNameToResourceState := make(map[string]*resourceState) for _, res := range getResourceStates(app, nil) { diff --git a/cmd/argocd/commands/app_resources.go b/cmd/argocd/commands/app_resources.go index 71e738808173f..f7c8ecdbe12bf 100644 --- a/cmd/argocd/commands/app_resources.go +++ b/cmd/argocd/commands/app_resources.go @@ -147,10 +147,11 @@ func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) return command } -func parentChildInfo(nodes []v1alpha1.ResourceNode) (mapUidToNode map[string]v1alpha1.ResourceNode, mapParentToChild map[string][]string, parentNode map[string]struct{}) { - mapUidToNode = make(map[string]v1alpha1.ResourceNode) - mapParentToChild = make(map[string][]string) - parentNode = make(map[string]struct{}) + +func parentChildInfo(nodes []v1alpha1.ResourceNode) (map[string]v1alpha1.ResourceNode, map[string][]string, map[string]struct{}) { + mapUidToNode := make(map[string]v1alpha1.ResourceNode) + mapParentToChild := make(map[string][]string) + parentNode := make(map[string]struct{}) for _, node := range nodes { mapUidToNode[node.UID] = node