mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 01:28:45 +01:00
fix: get app resources tree view (#26166)
Signed-off-by: Patroklos Papapetrou <ppapapetrou76@gmail.com>
This commit is contained in:
committed by
GitHub
parent
ae34305d18
commit
bf1f836ece
@@ -43,9 +43,49 @@ func TestPrintTreeViewAppResources(t *testing.T) {
|
||||
printTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, w)
|
||||
require.NoError(t, w.Flush())
|
||||
output := buf.String()
|
||||
|
||||
t.Logf("Output:\n%s", output)
|
||||
assert.Contains(t, output, "Rollout")
|
||||
assert.Contains(t, output, "argoproj.io")
|
||||
assert.Contains(t, output, "└─apps ReplicaSet sandbox-rollout-numalogic-demo numalogic-rollout-demo-5dcd5457d5 No")
|
||||
assert.Contains(t, output, " └─ Pod sandbox-rollout-numalogic-demo numalogic-rollout-demo-5dcd5457d5-6trpt No")
|
||||
}
|
||||
|
||||
func TestPrintTreeViewAppResourcesWithMultipleChildren(t *testing.T) {
|
||||
var nodes [4]v1alpha1.ResourceNode
|
||||
// Parent
|
||||
nodes[0].ResourceRef = v1alpha1.ResourceRef{Group: "argoproj.io", Kind: "Rollout", Namespace: "ns", Name: "rollout", UID: "root"}
|
||||
// Child 1
|
||||
nodes[1].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Kind: "ReplicaSet", Namespace: "ns", Name: "rs1", UID: "rs1"}
|
||||
nodes[1].ParentRefs = []v1alpha1.ResourceRef{{UID: "root"}}
|
||||
// Child 2
|
||||
nodes[2].ResourceRef = v1alpha1.ResourceRef{Group: "apps", Kind: "ReplicaSet", Namespace: "ns", Name: "rs2", UID: "rs2"}
|
||||
nodes[2].ParentRefs = []v1alpha1.ResourceRef{{UID: "root"}}
|
||||
// Grandchild
|
||||
nodes[3].ResourceRef = v1alpha1.ResourceRef{Group: "", Kind: "Pod", Namespace: "ns", Name: "pod1", UID: "pod1"}
|
||||
nodes[3].ParentRefs = []v1alpha1.ResourceRef{{UID: "rs1"}}
|
||||
|
||||
nodeMapping := make(map[string]v1alpha1.ResourceNode)
|
||||
mapParentToChild := make(map[string][]string)
|
||||
parentNode := make(map[string]struct{})
|
||||
for _, node := range nodes {
|
||||
nodeMapping[node.UID] = node
|
||||
if len(node.ParentRefs) > 0 {
|
||||
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, w)
|
||||
require.NoError(t, w.Flush())
|
||||
output := buf.String()
|
||||
t.Logf("Output:\n%s", output)
|
||||
|
||||
assert.Contains(t, output, "├─apps ReplicaSet ns rs1")
|
||||
assert.Contains(t, output, "│ └─ Pod ns pod1")
|
||||
assert.Contains(t, output, "└─apps ReplicaSet ns rs2")
|
||||
}
|
||||
|
||||
func TestPrintTreeViewDetailedAppResources(t *testing.T) {
|
||||
@@ -82,10 +122,11 @@ func TestPrintTreeViewDetailedAppResources(t *testing.T) {
|
||||
printDetailedTreeViewAppResourcesNotOrphaned(nodeMapping, mapParentToChild, parentNode, w)
|
||||
require.NoError(t, w.Flush())
|
||||
output := buf.String()
|
||||
t.Logf("Output:\n%s", output)
|
||||
|
||||
assert.Contains(t, output, "Rollout")
|
||||
assert.Contains(t, output, "Degraded")
|
||||
assert.Contains(t, output, "Readiness Gate failed")
|
||||
assert.Contains(t, output, "argoproj.io Rollout sandbox-rollout-numalogic-demo numalogic-rollout-demo No <unknown> Degraded Readiness Gate failed")
|
||||
assert.Contains(t, output, "└─apps ReplicaSet sandbox-rollout-numalogic-demo numalogic-rollout-demo-5dcd5457d5 No")
|
||||
assert.Contains(t, output, " └─ Pod sandbox-rollout-numalogic-demo numalogic-rollout-demo-5dcd5457d5-6trpt No")
|
||||
}
|
||||
|
||||
func TestPrintResourcesTree(t *testing.T) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/util/templates"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/cmd/argocd/commands/utils"
|
||||
"github.com/argoproj/argo-cd/v3/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
@@ -533,7 +535,20 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions)
|
||||
)
|
||||
command := &cobra.Command{
|
||||
Use: "resources APPNAME",
|
||||
Short: "List resource of application",
|
||||
Short: "List resources of application",
|
||||
Example: templates.Examples(`
|
||||
# List first-level resources of application
|
||||
argocd app resources my-app --refresh
|
||||
|
||||
# List only the orphaned resources of application
|
||||
argocd app resources my-app --orphaned
|
||||
|
||||
# Shows resource hierarchy with parent-child relationships
|
||||
argocd app resources my-app --output tree
|
||||
|
||||
# Shows resource hierarchy with parent-child relationships including information about age, health and reason
|
||||
argocd app resources my-app --output tree=detailed
|
||||
`),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
ctx := c.Context()
|
||||
if len(args) != 1 {
|
||||
@@ -554,7 +569,9 @@ func NewApplicationListResourcesCommand(clientOpts *argocdclient.ClientOptions)
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVar(&orphaned, "orphaned", false, "Lists only orphaned resources")
|
||||
command.Flags().StringVar(&output, "output", "", "Provides the tree view of the resources")
|
||||
command.Flags().StringVar(&output, "output", "", `Output format. One of: tree|tree=detailed.
|
||||
tree: Shows resource hierarchy with parent-child relationships
|
||||
tree=detailed: Same as tree, but includes AGE, HEALTH, and REASON columns`)
|
||||
command.Flags().StringVar(&project, "project", "", `The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist`)
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
const (
|
||||
firstElemPrefix = `├─`
|
||||
lastElemPrefix = `└─`
|
||||
indent = " "
|
||||
pipe = `│ `
|
||||
)
|
||||
|
||||
@@ -75,9 +74,7 @@ func detailedTreeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.Reso
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Group, parent.Kind, parent.Namespace, parent.Name, "No")
|
||||
chs := parentChildMap[parent.UID]
|
||||
for i, child := range chs {
|
||||
var p string
|
||||
@@ -92,7 +89,7 @@ func treeViewAppResourcesNotOrphaned(prefix string, uidToNodeMap map[string]v1al
|
||||
}
|
||||
|
||||
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")
|
||||
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes")
|
||||
chs := parentChildMap[parent.UID]
|
||||
for i, child := range chs {
|
||||
var p string
|
||||
@@ -107,14 +104,12 @@ func treeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string]v1alpha
|
||||
}
|
||||
|
||||
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)
|
||||
age := "<unknown>"
|
||||
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)
|
||||
healthStatus, reason := extractHealthStatusAndReason(parent)
|
||||
age := "<unknown>"
|
||||
if parent.CreatedAt != nil {
|
||||
age = duration.HumanDuration(time.Since(parent.CreatedAt.Time))
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Group, parent.Kind, parent.Namespace, parent.Name, "No", age, healthStatus, reason)
|
||||
chs := parentChildMap[parent.UID]
|
||||
for i, child := range chs {
|
||||
var p string
|
||||
@@ -134,7 +129,7 @@ func detailedTreeViewAppResourcesOrphaned(prefix string, uidToNodeMap map[string
|
||||
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)
|
||||
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Group, parent.Kind, parent.Namespace, parent.Name, "Yes", age, healthStatus, reason)
|
||||
|
||||
chs := parentChildMap[parent.UID]
|
||||
for i, child := range chs {
|
||||
|
||||
2
docs/user-guide/commands/argocd_app.md
generated
2
docs/user-guide/commands/argocd_app.md
generated
@@ -97,7 +97,7 @@ argocd app [flags]
|
||||
* [argocd app patch](argocd_app_patch.md) - Patch application
|
||||
* [argocd app patch-resource](argocd_app_patch-resource.md) - Patch resource in an application
|
||||
* [argocd app remove-source](argocd_app_remove-source.md) - Remove a source from multiple sources application.
|
||||
* [argocd app resources](argocd_app_resources.md) - List resource of application
|
||||
* [argocd app resources](argocd_app_resources.md) - List resources of application
|
||||
* [argocd app rollback](argocd_app_rollback.md) - Rollback application to a previous deployed version by History ID, omitted will Rollback to the previous version
|
||||
* [argocd app set](argocd_app_set.md) - Set application parameters
|
||||
* [argocd app sync](argocd_app_sync.md) - Sync an application to its target state
|
||||
|
||||
22
docs/user-guide/commands/argocd_app_resources.md
generated
22
docs/user-guide/commands/argocd_app_resources.md
generated
@@ -2,18 +2,36 @@
|
||||
|
||||
## argocd app resources
|
||||
|
||||
List resource of application
|
||||
List resources of application
|
||||
|
||||
```
|
||||
argocd app resources APPNAME [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
# List first-level resources of application
|
||||
argocd app resources my-app --refresh
|
||||
|
||||
# List only the orphaned resources of application
|
||||
argocd app resources my-app --orphaned
|
||||
|
||||
# Shows resource hierarchy with parent-child relationships
|
||||
argocd app resources my-app --output tree
|
||||
|
||||
# Shows resource hierarchy with parent-child relationships including information about age, health and reason
|
||||
argocd app resources my-app --output tree=detailed
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for resources
|
||||
--orphaned Lists only orphaned resources
|
||||
--output string Provides the tree view of the resources
|
||||
--output string Output format. One of: tree|tree=detailed.
|
||||
tree: Shows resource hierarchy with parent-child relationships
|
||||
tree=detailed: Same as tree, but includes AGE, HEALTH, and REASON columns
|
||||
--project string The name of the application's project - specifying this allows the command to report "not found" instead of "permission denied" if the app does not exist
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user