in internal/ui/stack.go [34:184]
func stackResourceStatuses(stack types.Stack) (string, []string) {
stackName := ptr.ToString(stack.StackName)
statuses := make(map[string]string)
messages := make([]string, 0)
nested := make(map[string]string)
// Get changeset details if possible
changeset, err := cfn.GetChangeSet(stackName, ptr.ToString(stack.ChangeSetId))
if err == nil {
for _, change := range changeset.Changes {
resourceID := ptr.ToString(change.ResourceChange.LogicalResourceId)
statuses[resourceID] = "REVIEW_IN_PROGRESS"
// Store nested stacks
if ptr.ToString(change.ResourceChange.ResourceType) == "AWS::CloudFormation::Stack" {
nested[resourceID] = fmt.Sprintf("%s: %s", console.Yellow(fmt.Sprintf("Stack %s", resourceID)), console.Grey("PENDING"))
}
}
}
// We ignore errors because it just means we'll list no resources
resources, _ := cfn.GetStackResources(stackName)
for _, resource := range resources {
resourceID := ptr.ToString(resource.LogicalResourceId)
status := string(resource.ResourceStatus)
rep := mapStatus(status)
statuses[resourceID] = status
// Store messages
if resource.ResourceStatusReason != nil && rep.category == failed {
msg := ptr.ToString(resource.ResourceStatusReason)
colour := statusColour[rep.category]
if msg != "Resource creation cancelled" {
id := resourceID
if resource.PhysicalResourceId != nil {
id += " - " + ptr.ToString(resource.PhysicalResourceId)
}
messages = append(messages, fmt.Sprintf("%s %s", console.Yellow(fmt.Sprintf("%s:", id)), colour(msg)))
}
}
// Store nested stacks
if ptr.ToString(resource.ResourceType) == "AWS::CloudFormation::Stack" {
stack, err := cfn.GetStack(ptr.ToString(resource.PhysicalResourceId))
if err == nil {
rs, rMessages := GetStackOutput(stack)
nested[resourceID] = rs
for _, rMessage := range rMessages {
messages = append(messages, fmt.Sprintf("%s%s", console.Yellow(fmt.Sprintf("%s/", resourceID)), rMessage))
}
}
}
}
// Build the output
out := strings.Builder{}
stackStatus := string(stack.StackStatus)
if strings.HasSuffix(stackStatus, "_IN_PROGRESS") {
total := len(statuses)
complete := 0
inProgress := 0
for _, status := range statuses {
switch stackStatus {
case
"CREATE_IN_PROGRESS",
"UPDATE_IN_PROGRESS",
"UPDATE_COMPLETE_CLEANUP_IN_PROGRESS",
"REVIEW_IN_PROGRESS",
"IMPORT_IN_PROGRESS":
switch status {
case "CREATE_COMPLETE", "CREATE_FAILED", "UPDATE_COMPLETE", "UPDATE_FAILED", "IMPORT_COMPLETE", "IMPORT_FAILED":
complete++
case "CREATE_IN_PROGRESS", "UPDATE_IN_PROGRESS", "IMPORT_IN_PROGRESS":
inProgress++
}
case
"DELETE_IN_PROGRESS":
switch status {
case "DELETE_COMPLETE", "DELETE_FAILED", "DELETE_SKIPPED":
complete++
case "DELETE_IN_PROGRESS":
inProgress++
}
case
"ROLLBACK_IN_PROGRESS",
"UPDATE_ROLLBACK_IN_PROGRESS",
"UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS",
"IMPORT_ROLLBACK_IN_PROGRESS":
switch status {
case "DELETE_COMPLETE", "DELETE_FAILED", "DELETE_SKIPPED", "IMPORT_ROLLBACK_COMPLETE", "IMPORT_ROLLBACK_FAILED":
complete++
case "DELETE_IN_PROGRESS", "IMPORT_ROLLBACK_IN_PROGRESS":
inProgress++
}
}
}
pending := total - complete - inProgress
parts := make([]string, 0)
if pending > 0 {
word := "resources"
if pending == 1 {
word = "resource"
}
parts = append(parts, console.Grey(fmt.Sprintf("%d %s pending", pending, word)))
}
if inProgress > 0 {
word := "resources"
if inProgress == 1 {
word = "resource"
}
parts = append(parts, console.Blue(fmt.Sprintf("%d %s in progress", inProgress, word)))
}
if len(parts) > 0 {
out.WriteString("- ")
out.WriteString(strings.Join(parts, ", "))
}
}
out.WriteString("\n")
// Append nested stacks to the output
names := make([]string, 0)
for name := range nested {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
other := nested[name]
parts := strings.Split(strings.TrimSpace(other), "\n")
for _, part := range parts {
out.WriteString(fmt.Sprintf(" - %s\n", part))
}
}
return out.String(), messages
}