func stackResourceStatuses()

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
}