Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

detect cycle in cli custom commands #765

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
29 changes: 29 additions & 0 deletions cmd/cmd_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path"
"strconv"
"strings"

"github.com/fatih/color"
Expand All @@ -18,6 +19,11 @@ import (
u "github.com/cloudposse/atmos/pkg/utils"
)

const (
AtmosCommandMaxDepthEnv = "ATMOS_COMMAND_MAX_DEPTH"
AtmosCommandDefaultDepth = 10
)

// ValidateConfig holds configuration options for Atmos validation.
// CheckStack determines whether stack configuration validation should be performed.
type ValidateConfig struct {
Expand All @@ -33,6 +39,17 @@ func WithStackValidation(check bool) AtmosValidateOption {
}
}

func getAtmosCommandMaxDepth() int {
maxDepth := os.Getenv(AtmosCommandMaxDepthEnv)
if maxDepth == "" {
return AtmosCommandDefaultDepth
}
if val, err := strconv.Atoi(maxDepth); err == nil {
return val
}
return AtmosCommandDefaultDepth
}

// processCustomCommands processes and executes custom commands
func processCustomCommands(
cliConfig schema.CliConfiguration,
Expand All @@ -47,6 +64,10 @@ func processCustomCommands(
existingTopLevelCommands = getTopLevelCommands()
}

// Track the execution count for each command
visitCount := make(map[string]int)
maxIterations := getAtmosCommandMaxDepth() // Default to 10 or from environment

for _, commandCfg := range commands {
// Clone the 'commandCfg' struct into a local variable because of the automatic closure in the `Run` function of the Cobra command.
// Cloning will make a closure over the local variable 'commandConfig' which is different in each iteration.
Expand All @@ -67,6 +88,14 @@ func processCustomCommands(
preCustomCommand(cmd, args, parentCommand, commandConfig)
},
Run: func(cmd *cobra.Command, args []string) {
// Increment the visit count for the command
visitCount[commandConfig.Name]++
if visitCount[commandConfig.Name] > maxIterations {
u.LogWarning(cliConfig, fmt.Sprintf("Command '%s' reached max iteration limit (%d). Skipping further execution.\n", commandConfig.Name, maxIterations))
return
}

// Execute the command if under the limit
executeCustomCommand(cliConfig, cmd, args, parentCommand, commandConfig)
},
}
Expand Down