Skip to content

Commit

Permalink
Add "health last changed" column to argocd app wait --watch output
Browse files Browse the repository at this point in the history
Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com>
  • Loading branch information
schakrad committed Oct 23, 2023
1 parent 15cbeeb commit 5a48c66
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 6 deletions.
49 changes: 43 additions & 6 deletions cmd/argocd/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ import (
"sigs.k8s.io/yaml"
)

type healthLastChangedWithTimeStamp struct {
healthStatus health.HealthStatusCode
time string
}

// NewApplicationCommand returns a new instance of an `argocd app` command
func NewApplicationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Expand Down Expand Up @@ -1589,6 +1594,24 @@ func printTreeViewDetailed(nodeMapping map[string]argoappv1.ResourceNode, parent
_ = w.Flush()
}

func printTreeViewWaitWatch(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState, prevHealthStates map[string]healthLastChangedWithTimeStamp) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
_, _ = fmt.Fprintf(w, "KIND/NAME\tSTATUS\tHEALTH\tHEALTH LAST CHANGED\tMESSAGE\n")
for uid := range parentNodes {
treeViewAppWaitWatch("", nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState, prevHealthStates, w)
}
_ = w.Flush()
}

func printTreeViewDetailedWaitWatch(nodeMapping map[string]argoappv1.ResourceNode, parentChildMapping map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState, prevHealthStates map[string]healthLastChangedWithTimeStamp) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, "KIND/NAME\tSTATUS\tHEALTH\tHEALTH LAST CHANGED\tAGE\tMESSAGE\tREASON\n")
for uid := range parentNodes {
detailedTreeViewWaitWatch("", nodeMapping, parentChildMapping, nodeMapping[uid], mapNodeNameToResourceState, prevHealthStates, w)
}
_ = w.Flush()
}

// NewApplicationSyncCommand returns a new instance of an `argocd app sync` command
func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
Expand Down Expand Up @@ -2308,6 +2331,7 @@ func resourceTreeWatch(appIf application.ApplicationServiceClient, ctx context.C
mapNodeNameToResourceState := make(map[string]*resourceState)
gitResources := make(chan v1alpha1.Application)
appResources := make(chan []v1alpha1.ResourceNode)
prevHealthStates := make(map[string]healthLastChangedWithTimeStamp)
var app *v1alpha1.Application
var wg sync.WaitGroup
wg.Add(2)
Expand Down Expand Up @@ -2339,7 +2363,6 @@ func resourceTreeWatch(appIf application.ApplicationServiceClient, ctx context.C
log.Printf("appResourceStream read failed: %v", err)
return
}
fmt.Println("")
appResources <- msg.Nodes
}
}
Expand Down Expand Up @@ -2389,13 +2412,27 @@ func resourceTreeWatch(appIf application.ApplicationServiceClient, ctx context.C
return
case appTreeNodes := <-appResources:
mapUidToNode, mapParentToChild, parentNodes = parentChildInfo(appTreeNodes)
printAppResourceStatus(mapUidToNode, mapParentToChild, parentNodes, mapNodeNameToResourceState, output, app)
for _, node := range appTreeNodes {
val, ok := prevHealthStates[node.UID]
if ok {
if node.Health != nil {
if val.healthStatus != node.Health.Status {
prevHealthStates[node.UID] = healthLastChangedWithTimeStamp{node.Health.Status, time.Now().Format("2006-01-02T15:04:05-07:00")}
}
}
} else {
if node.Health != nil {
prevHealthStates[node.UID] = healthLastChangedWithTimeStamp{node.Health.Status, time.Now().Format("2006-01-02T15:04:05-07:00")}
}
}
}
printAppResourceStatus(mapUidToNode, mapParentToChild, parentNodes, mapNodeNameToResourceState, output, app, prevHealthStates)
case gitResourceApp := <-gitResources:
app = &gitResourceApp
for _, res := range getResourceStates(&gitResourceApp, nil) {
mapNodeNameToResourceState[res.Kind+"/"+res.Name] = res
}
printAppResourceStatus(mapUidToNode, mapParentToChild, parentNodes, mapNodeNameToResourceState, output, app)
printAppResourceStatus(mapUidToNode, mapParentToChild, parentNodes, mapNodeNameToResourceState, output, app, prevHealthStates)
case <-done:
wg.Wait()
once.Do(func() {
Expand All @@ -2409,7 +2446,7 @@ func resourceTreeWatch(appIf application.ApplicationServiceClient, ctx context.C
}
}

func printAppResourceStatus(mapUidToNode map[string]v1alpha1.ResourceNode, mapParentToChild map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState, output string, app *v1alpha1.Application) {
func printAppResourceStatus(mapUidToNode map[string]v1alpha1.ResourceNode, mapParentToChild map[string][]string, parentNodes map[string]struct{}, mapNodeNameToResourceState map[string]*resourceState, output string, app *v1alpha1.Application, prevHealthStates map[string]healthLastChangedWithTimeStamp) {
fmt.Print("\033[2J\033[H\033[3J")
switch output {
case "yaml", "json":
Expand All @@ -2425,12 +2462,12 @@ func printAppResourceStatus(mapUidToNode map[string]v1alpha1.ResourceNode, mapPa
case "tree":
if len(mapUidToNode) > 0 {
fmt.Println()
printTreeView(mapUidToNode, mapParentToChild, parentNodes, mapNodeNameToResourceState)
printTreeViewWaitWatch(mapUidToNode, mapParentToChild, parentNodes, mapNodeNameToResourceState, prevHealthStates)
}
case "tree=detailed":
if len(mapUidToNode) > 0 {
fmt.Println()
printTreeViewDetailed(mapUidToNode, mapParentToChild, parentNodes, mapNodeNameToResourceState)
printTreeViewDetailedWaitWatch(mapUidToNode, mapParentToChild, parentNodes, mapNodeNameToResourceState, prevHealthStates)
}
default:
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
Expand Down
57 changes: 57 additions & 0 deletions cmd/argocd/commands/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,64 @@ func treeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode
}
treeViewAppGet(p, uidToNodeMap, parentToChildMap, uidToNodeMap[childUid], mapNodeNameToResourceState, w)
}
}

func treeViewAppWaitWatch(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentToChildMap map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState, prevHealthStates map[string]healthLastChangedWithTimeStamp, w *tabwriter.Writer) {
healthStatus, _ := extractHealthStatusAndReason(parent)
healthLastChanged := ""
val, ok := prevHealthStates[parent.UID]
if ok {
healthLastChanged = val.time
}
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\n", printPrefix(prefix), parent.Kind+"/"+value.Name, value.Status, healthStatus, healthLastChanged, value.Message)
} else {
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+parent.Name, "", healthStatus, healthLastChanged, "")
}
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
}
treeViewAppWaitWatch(p, uidToNodeMap, parentToChildMap, uidToNodeMap[childUid], mapNodeNameToResourceState, prevHealthStates, w)
}
}

func detailedTreeViewWaitWatch(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState, prevHealthStates map[string]healthLastChangedWithTimeStamp, w *tabwriter.Writer) {
healthStatus, reason := extractHealthStatusAndReason(parent)
healthLastChanged := ""
val, ok := prevHealthStates[parent.UID]
if ok {
healthLastChanged = val.time
}
var age = "<unknown>"
if parent.CreatedAt != nil {
age = duration.HumanDuration(time.Since(parent.CreatedAt.Time))
}

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\t%s\n", printPrefix(prefix), parent.Kind+"/"+value.Name, value.Status, healthStatus, healthLastChanged, age, value.Message, reason)
} else {
_, _ = fmt.Fprintf(w, "%s%s\t%s\t%s\t%s\t%s\t%s\t%s\n", printPrefix(prefix), parent.Kind+"/"+parent.Name, "", healthStatus, healthLastChanged, 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
}
detailedTreeViewWaitWatch(p, uidToNodeMap, parentChildMap, uidToNodeMap[child], mapNodeNameToResourceState, prevHealthStates, w)
}
}

func detailedTreeViewAppGet(prefix string, uidToNodeMap map[string]v1alpha1.ResourceNode, parentChildMap map[string][]string, parent v1alpha1.ResourceNode, mapNodeNameToResourceState map[string]*resourceState, w *tabwriter.Writer) {
Expand Down

0 comments on commit 5a48c66

Please sign in to comment.