From 6832ddd061c664ae51b6d8e43e5a7caf7b1513e7 Mon Sep 17 00:00:00 2001 From: haitham911 Date: Thu, 19 Dec 2024 04:50:48 +0200 Subject: [PATCH 01/24] fix clean cmd --- internal/exec/terraform.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/internal/exec/terraform.go b/internal/exec/terraform.go index fcf65bdb3..0690964f7 100644 --- a/internal/exec/terraform.go +++ b/internal/exec/terraform.go @@ -75,13 +75,10 @@ func ExecuteTerraform(info schema.ConfigAndStacksInfo) error { shouldCheckStack := true // Skip stack processing when cleaning with --everything or --force flags to allow // cleaning without requiring stack configuration - if info.SubCommand == "clean" && - (u.SliceContainsString(info.AdditionalArgsAndFlags, everythingFlag) || - u.SliceContainsString(info.AdditionalArgsAndFlags, forceFlag)) { + if info.SubCommand == "clean" { if info.ComponentFromArg == "" { shouldProcessStacks = false } - shouldCheckStack = info.Stack != "" } @@ -96,7 +93,7 @@ func ExecuteTerraform(info schema.ConfigAndStacksInfo) error { } } - if !info.ComponentIsEnabled { + if !info.ComponentIsEnabled && info.SubCommand != "clean" { u.LogInfo(atmosConfig, fmt.Sprintf("component '%s' is not enabled and skipped", info.ComponentFromArg)) return nil } From 0c90df55ef0c8180fbbf59aad107825b9e18ff61 Mon Sep 17 00:00:00 2001 From: haitham911 Date: Thu, 19 Dec 2024 14:37:39 +0200 Subject: [PATCH 02/24] feat --everything is the default behavior --- internal/exec/help.go | 2 +- internal/exec/utils.go | 8 +++++++- website/docs/cli/commands/terraform/terraform-clean.mdx | 2 +- website/docs/cli/commands/terraform/usage.mdx | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/internal/exec/help.go b/internal/exec/help.go index b90c8ce64..e5c8257ef 100644 --- a/internal/exec/help.go +++ b/internal/exec/help.go @@ -79,7 +79,7 @@ func processHelp( " - generated 'backend.tf.json' file\n" + " - 'terraform.tfstate.d' folder (if '--everything' flag is used)\n\n" + "Usage: atmos terraform clean -s \n\n" + - "Use '--everything' flag to also delete the Terraform state files and and directories with confirm message.\n\n" + + "Use '--everything' flag —everything is the default behavior to delete the Terraform state files and and directories with confirm message.\n\n" + "Use --force to forcefully delete Terraform state files and directories for the component.\n\n" + "- If no component is specified, the command will apply to all components and stacks.\n" + "- If no stack is specified, the command will apply to all stacks for the specified component.\n" + diff --git a/internal/exec/utils.go b/internal/exec/utils.go index a3a7c9cc5..d052ae4e8 100644 --- a/internal/exec/utils.go +++ b/internal/exec/utils.go @@ -657,8 +657,14 @@ func processArgsAndFlags(componentType string, inputArgsAndFlags []string) (sche var additionalArgsAndFlags []string var globalOptions []string var indexesToRemove []int + if len(inputArgsAndFlags) > 1 && inputArgsAndFlags[0] == "clean" { + if !u.SliceContainsString(inputArgsAndFlags, everythingFlag) { + inputArgsAndFlags = append(inputArgsAndFlags, everythingFlag) + } + } + fmt.Println(inputArgsAndFlags) - // For commands like `atmos terraform clean` and `atmos terraform plan`, show the command help + // For commands like `atmos terraform plan`, show the command help if len(inputArgsAndFlags) == 1 && inputArgsAndFlags[0] != "version" { info.SubCommand = inputArgsAndFlags[0] info.NeedHelp = true diff --git a/website/docs/cli/commands/terraform/terraform-clean.mdx b/website/docs/cli/commands/terraform/terraform-clean.mdx index 3ddc4b341..766fea226 100644 --- a/website/docs/cli/commands/terraform/terraform-clean.mdx +++ b/website/docs/cli/commands/terraform/terraform-clean.mdx @@ -23,7 +23,7 @@ Execute the `terraform clean` command like this: atmos terraform clean -s [--skip-lock-file] [--everything] [--force] :::warning -The `--everything` flag will delete all Terraform-related files including state files. The `--force` flag will bypass confirmation prompts. +The `--everything` flag is the default behavior and will delete all Terraform-related files including state files. The `--force` flag will bypass confirmation prompts. Use these flags with extreme caution as they can lead to irreversible data loss. ::: ``` diff --git a/website/docs/cli/commands/terraform/usage.mdx b/website/docs/cli/commands/terraform/usage.mdx index ce60dbee6..9054a5b16 100644 --- a/website/docs/cli/commands/terraform/usage.mdx +++ b/website/docs/cli/commands/terraform/usage.mdx @@ -59,7 +59,7 @@ HCL-based domain-specific language and its interpreter. Atmos works with [OpenTo - `atmos terraform clean` command deletes the `.terraform` folder, `.terraform.lock.hcl` lock file, and the previously generated `planfile` and `varfile` for the specified component and stack. Use the `--skip-lock-file` flag to skip deleting the `.terraform.lock.hcl` file. - Use the `--everything` flag to delete all the local Terraform state files and directories (including `terraform.tfstate.d`) for all components and stacks. + Use the `--everything` flag is the default behavior to delete all the local Terraform state files and directories (including `terraform.tfstate.d`) for all components and stacks. Use the `--force` flag to bypass the safety confirmation prompt and force the deletion (use with caution). :::warning From a2b820e59d56bdb26018bdd6b0e30d7377f2a25f Mon Sep 17 00:00:00 2001 From: haitham911 Date: Thu, 19 Dec 2024 15:54:44 +0200 Subject: [PATCH 03/24] remove debug print --- internal/exec/utils.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/exec/utils.go b/internal/exec/utils.go index d052ae4e8..3e11d9ab2 100644 --- a/internal/exec/utils.go +++ b/internal/exec/utils.go @@ -662,7 +662,6 @@ func processArgsAndFlags(componentType string, inputArgsAndFlags []string) (sche inputArgsAndFlags = append(inputArgsAndFlags, everythingFlag) } } - fmt.Println(inputArgsAndFlags) // For commands like `atmos terraform plan`, show the command help if len(inputArgsAndFlags) == 1 && inputArgsAndFlags[0] != "version" { From 4a9bde6ee3e741757d29390ab3f55506fa269449 Mon Sep 17 00:00:00 2001 From: "Erik Osterman (CEO @ Cloud Posse)" Date: Thu, 26 Dec 2024 10:46:12 -0600 Subject: [PATCH 04/24] Update website/docs/cli/commands/terraform/usage.mdx --- website/docs/cli/commands/terraform/usage.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/cli/commands/terraform/usage.mdx b/website/docs/cli/commands/terraform/usage.mdx index 9054a5b16..173165522 100644 --- a/website/docs/cli/commands/terraform/usage.mdx +++ b/website/docs/cli/commands/terraform/usage.mdx @@ -59,7 +59,7 @@ HCL-based domain-specific language and its interpreter. Atmos works with [OpenTo - `atmos terraform clean` command deletes the `.terraform` folder, `.terraform.lock.hcl` lock file, and the previously generated `planfile` and `varfile` for the specified component and stack. Use the `--skip-lock-file` flag to skip deleting the `.terraform.lock.hcl` file. - Use the `--everything` flag is the default behavior to delete all the local Terraform state files and directories (including `terraform.tfstate.d`) for all components and stacks. + The `--everything` flag is the default behavior. It deletes all local Terraform state files and directories (including `terraform.tfstate.d/`) for all components and stacks. Use the `--force` flag to bypass the safety confirmation prompt and force the deletion (use with caution). :::warning From 755e5c670cd6368fe548b15ca9ae8eb0ad8ae6ad Mon Sep 17 00:00:00 2001 From: "Erik Osterman (CEO @ Cloud Posse)" Date: Thu, 26 Dec 2024 10:46:39 -0600 Subject: [PATCH 05/24] Update website/docs/cli/commands/terraform/usage.mdx --- website/docs/cli/commands/terraform/usage.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/cli/commands/terraform/usage.mdx b/website/docs/cli/commands/terraform/usage.mdx index 173165522..a9c32f7f2 100644 --- a/website/docs/cli/commands/terraform/usage.mdx +++ b/website/docs/cli/commands/terraform/usage.mdx @@ -60,7 +60,7 @@ HCL-based domain-specific language and its interpreter. Atmos works with [OpenTo - `atmos terraform clean` command deletes the `.terraform` folder, `.terraform.lock.hcl` lock file, and the previously generated `planfile` and `varfile` for the specified component and stack. Use the `--skip-lock-file` flag to skip deleting the `.terraform.lock.hcl` file. The `--everything` flag is the default behavior. It deletes all local Terraform state files and directories (including `terraform.tfstate.d/`) for all components and stacks. - Use the `--force` flag to bypass the safety confirmation prompt and force the deletion (use with caution). + The `--force` flag bypasses the safety confirmation prompt and forces the deletion. Use with caution. :::warning The `--everything` flag performs destructive operations that can lead to permanent state loss. Always ensure you have remote state configured in your components before proceeding. From 13126b280f0263e999315c47c617310d50e75341 Mon Sep 17 00:00:00 2001 From: "Erik Osterman (CEO @ Cloud Posse)" Date: Thu, 26 Dec 2024 10:47:09 -0600 Subject: [PATCH 06/24] Apply suggestions from code review --- website/docs/cli/commands/terraform/terraform-clean.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/cli/commands/terraform/terraform-clean.mdx b/website/docs/cli/commands/terraform/terraform-clean.mdx index 766fea226..9bd0bdb66 100644 --- a/website/docs/cli/commands/terraform/terraform-clean.mdx +++ b/website/docs/cli/commands/terraform/terraform-clean.mdx @@ -23,7 +23,7 @@ Execute the `terraform clean` command like this: atmos terraform clean -s [--skip-lock-file] [--everything] [--force] :::warning -The `--everything` flag is the default behavior and will delete all Terraform-related files including state files. The `--force` flag will bypass confirmation prompts. +The `--everything` flag is the default behavior and will delete all Terraform-related files including state files, with a confirmation prompt before proceeding. The `--force` flag will bypass confirmation prompts. Use these flags with extreme caution as they can lead to irreversible data loss. ::: ``` From 7e2f054aa5552cf3787e0ca2a97bebeb7970fb69 Mon Sep 17 00:00:00 2001 From: "Erik Osterman (CEO @ Cloud Posse)" Date: Thu, 26 Dec 2024 10:48:38 -0600 Subject: [PATCH 07/24] Update internal/exec/help.go --- internal/exec/help.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/exec/help.go b/internal/exec/help.go index e5c8257ef..30355197e 100644 --- a/internal/exec/help.go +++ b/internal/exec/help.go @@ -79,7 +79,7 @@ func processHelp( " - generated 'backend.tf.json' file\n" + " - 'terraform.tfstate.d' folder (if '--everything' flag is used)\n\n" + "Usage: atmos terraform clean -s \n\n" + - "Use '--everything' flag —everything is the default behavior to delete the Terraform state files and and directories with confirm message.\n\n" + + "Use the `--everything` flag to remove all Terraform state files and directories, prompting you for confirmation before proceeding. This is the default behavior.\n\n" + "Use --force to forcefully delete Terraform state files and directories for the component.\n\n" + "- If no component is specified, the command will apply to all components and stacks.\n" + "- If no stack is specified, the command will apply to all stacks for the specified component.\n" + From 1acd1d0dfae264e83fd9d64e1065df1244d38673 Mon Sep 17 00:00:00 2001 From: haitham911 Date: Sun, 5 Jan 2025 00:37:17 +0200 Subject: [PATCH 08/24] remove --everything --- internal/exec/help.go | 7 ++----- internal/exec/terraform.go | 1 - internal/exec/terraform_clean.go | 9 ++++----- internal/exec/utils.go | 8 +++----- .../docs/cli/commands/terraform/terraform-clean.mdx | 7 +++---- website/docs/cli/commands/terraform/usage.mdx | 12 ++++++------ .../Screengrabs/atmos-terraform--help.html | 4 ++-- .../Screengrabs/atmos-terraform-clean--help.html | 4 ++-- 8 files changed, 22 insertions(+), 30 deletions(-) diff --git a/internal/exec/help.go b/internal/exec/help.go index 47e724037..1fe7abdc7 100644 --- a/internal/exec/help.go +++ b/internal/exec/help.go @@ -32,9 +32,8 @@ func processHelp( "the commands will use the planfile previously generated by 'atmos terraform plan' command instead of generating a new planfile") u.PrintMessage(" - 'atmos terraform apply' and 'atmos terraform deploy' commands commands support '--planfile' flag to specify the path " + "to a planfile. The '--planfile' flag should be used instead of the planfile argument in the native 'terraform apply ' command") - u.PrintMessage(" - 'atmos terraform clean' command deletes the '.terraform' folder, '.terraform.lock.hcl' lock file, " + + u.PrintMessage(" - 'atmos terraform clean' command delete the Terraform state files and directories for the component and stack., '.terraform.lock.hcl' lock file, " + "and the previously generated 'planfile', 'varfile', and 'backend.tf.json' file for the specified component and stack. " + - "Use the --everything flag to also delete the Terraform state files and directories for the component. " + "Note: State files store the local state of your infrastructure and should be handled with care, if not using a remote backend.\n\n" + "Additional flags:\n" + " --force Forcefully delete Terraform state files and directories without interaction\n" + @@ -70,16 +69,14 @@ func processHelp( "native arguments and flags for the 'helmfile' commands") } } else if componentType == "terraform" && command == "clean" { - u.PrintMessage("\n'atmos terraform clean' command deletes the following folders and files from the component's directory:\n\n" + + u.PrintMessage("\n'atmos terraform clean' command delete all the Terraform state files and directories for all components and stacks . deletes the following folders and files from the component's directory:\n\n" + " - '.terraform' folder\n" + " - folder that the 'TF_DATA_DIR' ENV var points to\n" + " - '.terraform.lock.hcl' file\n" + " - generated varfile for the component in the stack\n" + " - generated planfile for the component in the stack\n" + " - generated 'backend.tf.json' file\n" + - " - 'terraform.tfstate.d' folder (if '--everything' flag is used)\n\n" + "Usage: atmos terraform clean -s \n\n" + - "Use the `--everything` flag to remove all Terraform state files and directories, prompting you for confirmation before proceeding. This is the default behavior.\n\n" + "Use --force to forcefully delete Terraform state files and directories for the component.\n\n" + "- If no component is specified, the command will apply to all components and stacks.\n" + "- If no stack is specified, the command will apply to all stacks for the specified component.\n" + diff --git a/internal/exec/terraform.go b/internal/exec/terraform.go index 0690964f7..130baee7b 100644 --- a/internal/exec/terraform.go +++ b/internal/exec/terraform.go @@ -21,7 +21,6 @@ const ( outFlag = "-out" varFileFlag = "-var-file" skipTerraformLockFileFlag = "--skip-lock-file" - everythingFlag = "--everything" forceFlag = "--force" ) diff --git a/internal/exec/terraform_clean.go b/internal/exec/terraform_clean.go index e3b190e01..275d6b905 100644 --- a/internal/exec/terraform_clean.go +++ b/internal/exec/terraform_clean.go @@ -346,8 +346,8 @@ func handleTFDataDir(componentPath string, relativePath string, atmosConfig sche } } -func initializeFilesToClear(info schema.ConfigAndStacksInfo, atmosConfig schema.AtmosConfiguration, everything bool) []string { - if everything && info.Stack == "" { +func initializeFilesToClear(info schema.ConfigAndStacksInfo, atmosConfig schema.AtmosConfiguration) []string { + if info.ComponentFromArg == "" { return []string{".terraform", ".terraform.lock.hcl", "*.tfvar.json", "terraform.tfstate.d"} } varFile := constructTerraformComponentVarfileName(info) @@ -406,8 +406,7 @@ func handleCleanSubCommand(info schema.ConfigAndStacksInfo, componentPath string } force := u.SliceContainsString(info.AdditionalArgsAndFlags, forceFlag) - everything := u.SliceContainsString(info.AdditionalArgsAndFlags, everythingFlag) - filesToClear := initializeFilesToClear(info, atmosConfig, everything) + filesToClear := initializeFilesToClear(info, atmosConfig) folders, err := CollectDirectoryObjects(cleanPath, filesToClear) if err != nil { u.LogTrace(atmosConfig, fmt.Errorf("error collecting folders and files: %v", err).Error()) @@ -455,7 +454,7 @@ func handleCleanSubCommand(info schema.ConfigAndStacksInfo, componentPath string u.PrintMessage(fmt.Sprintf("Do you want to delete the folder '%s'? ", tfDataDir)) } var message string - if everything && info.ComponentFromArg == "" { + if info.ComponentFromArg == "" { message = fmt.Sprintf("This will delete %v local terraform state files affecting all components", total) } else if info.Component != "" && info.Stack != "" { message = fmt.Sprintf("This will delete %v local terraform state files for component '%s' in stack '%s'", total, info.Component, info.Stack) diff --git a/internal/exec/utils.go b/internal/exec/utils.go index 81a9cf518..9ea63313c 100644 --- a/internal/exec/utils.go +++ b/internal/exec/utils.go @@ -665,14 +665,12 @@ func processArgsAndFlags( var additionalArgsAndFlags []string var globalOptions []string var indexesToRemove []int - if len(inputArgsAndFlags) > 1 && inputArgsAndFlags[0] == "clean" { - if !u.SliceContainsString(inputArgsAndFlags, everythingFlag) { - inputArgsAndFlags = append(inputArgsAndFlags, everythingFlag) - } + if len(inputArgsAndFlags) == 1 && inputArgsAndFlags[0] == "clean" { + info.SubCommand = inputArgsAndFlags[0] } // For commands like `atmos terraform plan`, show the command help - if len(inputArgsAndFlags) == 1 && inputArgsAndFlags[0] != "version" { + if len(inputArgsAndFlags) == 1 && inputArgsAndFlags[0] != "version" && info.SubCommand == "" { info.SubCommand = inputArgsAndFlags[0] info.NeedHelp = true return info, nil diff --git a/website/docs/cli/commands/terraform/terraform-clean.mdx b/website/docs/cli/commands/terraform/terraform-clean.mdx index 9bd0bdb66..4834df177 100644 --- a/website/docs/cli/commands/terraform/terraform-clean.mdx +++ b/website/docs/cli/commands/terraform/terraform-clean.mdx @@ -23,7 +23,7 @@ Execute the `terraform clean` command like this: atmos terraform clean -s [--skip-lock-file] [--everything] [--force] :::warning -The `--everything` flag is the default behavior and will delete all Terraform-related files including state files, with a confirmation prompt before proceeding. The `--force` flag will bypass confirmation prompts. +The `clean` default behavior and will delete all Terraform-related files including state files, with a confirmation prompt before proceeding. The `--force` flag will bypass confirmation prompts. Use these flags with extreme caution as they can lead to irreversible data loss. ::: ``` @@ -36,10 +36,9 @@ Run `atmos terraform clean --help` to see all the available options ```shell # Delete all Terraform-related files for all components (with confirmation) -atmos terraform clean --everything - +atmos terraform clean # Force delete all Terraform-related files for all components (no confirmation) -atmos terraform clean --everything --force +atmos terraform clean --force atmos terraform clean top-level-component1 -s tenant1-ue2-dev atmos terraform clean infra/vpc -s tenant1-ue2-staging atmos terraform clean infra/vpc -s tenant1-ue2-staging --skip-lock-file diff --git a/website/docs/cli/commands/terraform/usage.mdx b/website/docs/cli/commands/terraform/usage.mdx index a9c32f7f2..3a28d2ce2 100644 --- a/website/docs/cli/commands/terraform/usage.mdx +++ b/website/docs/cli/commands/terraform/usage.mdx @@ -59,11 +59,11 @@ HCL-based domain-specific language and its interpreter. Atmos works with [OpenTo - `atmos terraform clean` command deletes the `.terraform` folder, `.terraform.lock.hcl` lock file, and the previously generated `planfile` and `varfile` for the specified component and stack. Use the `--skip-lock-file` flag to skip deleting the `.terraform.lock.hcl` file. - The `--everything` flag is the default behavior. It deletes all local Terraform state files and directories (including `terraform.tfstate.d/`) for all components and stacks. + It deletes all local Terraform state files and directories (including `terraform.tfstate.d/`) for all components and stacks. The `--force` flag bypasses the safety confirmation prompt and forces the deletion. Use with caution. :::warning - The `--everything` flag performs destructive operations that can lead to permanent state loss. Always ensure you have remote state configured in your components before proceeding. + The `clean` performs destructive operations that can lead to permanent state loss. Always ensure you have remote state configured in your components before proceeding. ::: - `atmos terraform workspace` command first runs `terraform init -reconfigure`, then `terraform workspace select`, and if the workspace was not @@ -113,16 +113,16 @@ atmos terraform destroy test/test-component-override -s tenant1-ue2-dev --redire atmos terraform init test/test-component-override-3 -s tenant1-ue2-dev # Clean all components (with confirmation) -atmos terraform clean --everything +atmos terraform clean # Clean a specific component -atmos terraform clean vpc --everything +atmos terraform clean vpc # Clean a specific component in a stack -atmos terraform clean vpc --stack dev --everything +atmos terraform clean vpc --stack dev # Clean without confirmation prompt -atmos terraform clean --everything --force +atmos terraform clean --force atmos terraform clean test/test-component-override-3 -s tenant1-ue2-dev atmos terraform workspace test/test-component-override-3 -s tenant1-ue2-dev diff --git a/website/src/components/Screengrabs/atmos-terraform--help.html b/website/src/components/Screengrabs/atmos-terraform--help.html index 5142abd4e..af2cfee27 100644 --- a/website/src/components/Screengrabs/atmos-terraform--help.html +++ b/website/src/components/Screengrabs/atmos-terraform--help.html @@ -20,8 +20,8 @@ - 'atmos terraform deploy' command supports '--deploy-run-init=true/false' flag to enable/disable running 'terraform init' before executing the command - 'atmos terraform apply' and 'atmos terraform deploy' commands support '--from-plan' flag. If the flag is specified, the commands will use the planfile previously generated by 'atmos terraform plan' command instead of generating a new planfile - 'atmos terraform apply' and 'atmos terraform deploy' commands commands support '--planfile' flag to specify the path to a planfile. The '--planfile' flag should be used instead of the planfile argument in the native 'terraform apply <planfile>' command - - 'atmos terraform clean' command deletes the '.terraform' folder, '.terraform.lock.hcl' lock file, and the previously generated 'planfile', 'varfile' and 'backend.tf.json' file for the specified component and stack. Use --skip-lock-file flag to skip deleting the lock file. - - 'atmos terraform clean' command supports '--everything' flag to delete all the Terraform state files and directories for all components and stacks . Use --force flag to skip the confirmation prompt. + - 'atmos terraform clean' command delete all the Terraform state files and directories for all components and stacks . deletes the '.terraform' folder, '.terraform.lock.hcl' lock file, and the previously generated 'planfile', 'varfile' and 'backend.tf.json' file for the specified component and stack. Use --skip-lock-file flag to skip deleting the lock file. + - 'atmos terraform clean' . Use --force flag to skip the confirmation prompt. - 'atmos terraform workspace' command first runs 'terraform init -reconfigure', then 'terraform workspace select', and if the workspace was not created before, it then runs 'terraform workspace new' - 'atmos terraform import' command searches for 'region' in the variables for the specified component and stack, and if it finds it, sets 'AWS_REGION=<region>' ENV var before executing the command - 'atmos terraform generate backend' command generates a backend config file for an 'atmos' component in a stack diff --git a/website/src/components/Screengrabs/atmos-terraform-clean--help.html b/website/src/components/Screengrabs/atmos-terraform-clean--help.html index 7d5710b43..0d664de16 100644 --- a/website/src/components/Screengrabs/atmos-terraform-clean--help.html +++ b/website/src/components/Screengrabs/atmos-terraform-clean--help.html @@ -8,7 +8,7 @@ 'atmos terraform clean' command deletes the following folders and files from the component's directory: - + - delete all the Terraform state files and directories for all components and stacks - '.terraform' folder - folder that the 'TF_DATA_DIR' ENV var points to - '.terraform.lock.hcl' file @@ -17,7 +17,7 @@ - generated 'backend.tf.json' file Usage: atmos terraform clean <component> -s <stack> <flags> -Use '--everything' flag to delete all the files and folders mentioned above. '--force' to delete the files without confirmation. +Use '--force' to delete the files without confirmation. Use '--skip-lock-file' flag to skip deleting the lock file. For more details refer to https://atmos.tools/cli/commands/terraform/clean From 338d8597a9c3670ef984ba82b5ee742d35f01226 Mon Sep 17 00:00:00 2001 From: haitham911 Date: Sun, 5 Jan 2025 01:56:58 +0200 Subject: [PATCH 09/24] add integration test for terraform apply and terraform clean --- tests/cli_test.go | 97 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/tests/cli_test.go b/tests/cli_test.go index 255503cc8..18d50dfb8 100644 --- a/tests/cli_test.go +++ b/tests/cli_test.go @@ -248,3 +248,100 @@ func verifyFileContains(t *testing.T, filePatterns map[string][]string) bool { } return success } +func TestCLITerraformClean(t *testing.T) { + // Capture the starting working directory + startingDir, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get the current working directory: %v", err) + } + + // Initialize PathManager and update PATH + pathManager := NewPathManager() + pathManager.Prepend("../build", "..") + err = pathManager.Apply() + if err != nil { + t.Fatalf("Failed to apply updated PATH: %v", err) + } + fmt.Printf("Updated PATH: %s\n", pathManager.GetPath()) + defer func() { + // Change back to the original working directory after the test + if err := os.Chdir(startingDir); err != nil { + t.Fatalf("Failed to change back to the starting directory: %v", err) + } + }() + workDir := "../examples/quick-start-simple" + err = os.Chdir(workDir) + if err != nil { + t.Fatalf("Failed to change directory to %q: %v", workDir, err) + } + binaryPath, err := exec.LookPath("atmos") + if err != nil { + t.Fatalf("Binary not found: %s. Current PATH: %s", "atmos", pathManager.GetPath()) + } + cmd := exec.Command(binaryPath, "terraform", "apply", "station", "-s", "dev") + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + // ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE + envVars := os.Environ() + envVars = append(envVars, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") + cmd.Env = envVars + + // run terraform apply station -s dev and terraform apply station -s prod + err = cmd.Run() + if err != nil { + t.Log(stdout.String()) + t.Fatalf("Failed to run terraform apply station -s dev: %v", stderr.String()) + return + } + cmd = exec.Command(binaryPath, "terraform", "apply", "station", "-s", "dev") + err = cmd.Run() + if err != nil { + t.Log(stdout.String()) + t.Fatalf("Failed to run terraform apply station -s prod: %v", stderr.String()) + return + } + // get command error sta + // check if the state files and directories for the component and stack are exist + stateFiles := []string{ + "./components/terraform/weather/.terraform", + "./components/terraform/weather/terraform.tfstate.d", + "./components/terraform/weather/.terraform.lock.hcl", + } + for _, file := range stateFiles { + fileAbs, err := filepath.Abs(file) + if err != nil { + t.Fatalf("Failed to resolve absolute path for %q: %v", file, err) + } + if _, err := os.Stat(fileAbs); errors.Is(err, os.ErrNotExist) { + t.Errorf("Reason: Expected file exist: %q", fileAbs) + return + } + } + + // run atmos terraform clean + cmd = exec.Command(binaryPath, "terraform", "clean", "--force") + err = cmd.Run() + if err != nil { + t.Log(stdout.String()) + t.Fatalf("Failed to run atmos terraform clean: %v", stderr.String()) + } + // check if the state files and directories for the component and stack are deleted + for _, file := range stateFiles { + fileAbs, err := filepath.Abs(file) + if err != nil { + t.Fatalf("Failed to resolve absolute path for %q: %v", file, err) + } + _, err = os.Stat(fileAbs) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + continue + } + t.Errorf("Reason: error %q", fileAbs) + return + + } + return + } + +} From 673552575d4670888d9637cfef938540fc0009ba Mon Sep 17 00:00:00 2001 From: haitham911 Date: Sun, 5 Jan 2025 02:22:16 +0200 Subject: [PATCH 10/24] chang dev with prod --- tests/cli_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/cli_test.go b/tests/cli_test.go index 18d50dfb8..42474aa21 100644 --- a/tests/cli_test.go +++ b/tests/cli_test.go @@ -294,7 +294,10 @@ func TestCLITerraformClean(t *testing.T) { t.Fatalf("Failed to run terraform apply station -s dev: %v", stderr.String()) return } - cmd = exec.Command(binaryPath, "terraform", "apply", "station", "-s", "dev") + cmd = exec.Command(binaryPath, "terraform", "apply", "station", "-s", "prod") + envVars = append(envVars, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") + cmd.Env = envVars + err = cmd.Run() if err != nil { t.Log(stdout.String()) From d9f4ff60cf1072c3455dd5ed39aabc536823cc93 Mon Sep 17 00:00:00 2001 From: haitham911 Date: Tue, 21 Jan 2025 15:36:29 +0200 Subject: [PATCH 11/24] move TestCLITerraformClean to new file --- tests/cli_terraform_test.go | 112 ++++++++++++++++++++++++++++++++++++ tests/cli_test.go | 100 -------------------------------- 2 files changed, 112 insertions(+), 100 deletions(-) create mode 100644 tests/cli_terraform_test.go diff --git a/tests/cli_terraform_test.go b/tests/cli_terraform_test.go new file mode 100644 index 000000000..7adcaa596 --- /dev/null +++ b/tests/cli_terraform_test.go @@ -0,0 +1,112 @@ +package tests + +import ( + "bytes" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" +) + +func TestCLITerraformClean(t *testing.T) { + // Capture the starting working directory + startingDir, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get the current working directory: %v", err) + } + + // Initialize PathManager and update PATH + pathManager := NewPathManager() + pathManager.Prepend("../build", "..") + err = pathManager.Apply() + if err != nil { + t.Fatalf("Failed to apply updated PATH: %v", err) + } + fmt.Printf("Updated PATH: %s\n", pathManager.GetPath()) + defer func() { + // Change back to the original working directory after the test + if err := os.Chdir(startingDir); err != nil { + t.Fatalf("Failed to change back to the starting directory: %v", err) + } + }() + workDir := "../examples/quick-start-simple" + err = os.Chdir(workDir) + if err != nil { + t.Fatalf("Failed to change directory to %q: %v", workDir, err) + } + binaryPath, err := exec.LookPath("atmos") + if err != nil { + t.Fatalf("Binary not found: %s. Current PATH: %s", "atmos", pathManager.GetPath()) + } + cmd := exec.Command(binaryPath, "terraform", "apply", "station", "-s", "dev") + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + // ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE + envVars := os.Environ() + envVars = append(envVars, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") + cmd.Env = envVars + + // run terraform apply station -s dev and terraform apply station -s prod + err = cmd.Run() + if err != nil { + t.Log(stdout.String()) + t.Fatalf("Failed to run terraform apply station -s dev: %v", stderr.String()) + return + } + cmd = exec.Command(binaryPath, "terraform", "apply", "station", "-s", "prod") + envVars = append(envVars, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") + cmd.Env = envVars + + err = cmd.Run() + if err != nil { + t.Log(stdout.String()) + t.Fatalf("Failed to run terraform apply station -s prod: %v", stderr.String()) + return + } + // get command error sta + // check if the state files and directories for the component and stack are exist + stateFiles := []string{ + "./components/terraform/weather/.terraform", + "./components/terraform/weather/terraform.tfstate.d", + "./components/terraform/weather/.terraform.lock.hcl", + } + for _, file := range stateFiles { + fileAbs, err := filepath.Abs(file) + if err != nil { + t.Fatalf("Failed to resolve absolute path for %q: %v", file, err) + } + if _, err := os.Stat(fileAbs); errors.Is(err, os.ErrNotExist) { + t.Errorf("Reason: Expected file exist: %q", fileAbs) + return + } + } + + // run atmos terraform clean + cmd = exec.Command(binaryPath, "terraform", "clean", "--force") + err = cmd.Run() + if err != nil { + t.Log(stdout.String()) + t.Fatalf("Failed to run atmos terraform clean: %v", stderr.String()) + } + // check if the state files and directories for the component and stack are deleted + for _, file := range stateFiles { + fileAbs, err := filepath.Abs(file) + if err != nil { + t.Fatalf("Failed to resolve absolute path for %q: %v", file, err) + } + _, err = os.Stat(fileAbs) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + continue + } + t.Errorf("Reason: error %q", fileAbs) + return + + } + return + } + +} diff --git a/tests/cli_test.go b/tests/cli_test.go index 42474aa21..255503cc8 100644 --- a/tests/cli_test.go +++ b/tests/cli_test.go @@ -248,103 +248,3 @@ func verifyFileContains(t *testing.T, filePatterns map[string][]string) bool { } return success } -func TestCLITerraformClean(t *testing.T) { - // Capture the starting working directory - startingDir, err := os.Getwd() - if err != nil { - t.Fatalf("Failed to get the current working directory: %v", err) - } - - // Initialize PathManager and update PATH - pathManager := NewPathManager() - pathManager.Prepend("../build", "..") - err = pathManager.Apply() - if err != nil { - t.Fatalf("Failed to apply updated PATH: %v", err) - } - fmt.Printf("Updated PATH: %s\n", pathManager.GetPath()) - defer func() { - // Change back to the original working directory after the test - if err := os.Chdir(startingDir); err != nil { - t.Fatalf("Failed to change back to the starting directory: %v", err) - } - }() - workDir := "../examples/quick-start-simple" - err = os.Chdir(workDir) - if err != nil { - t.Fatalf("Failed to change directory to %q: %v", workDir, err) - } - binaryPath, err := exec.LookPath("atmos") - if err != nil { - t.Fatalf("Binary not found: %s. Current PATH: %s", "atmos", pathManager.GetPath()) - } - cmd := exec.Command(binaryPath, "terraform", "apply", "station", "-s", "dev") - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - // ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE - envVars := os.Environ() - envVars = append(envVars, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") - cmd.Env = envVars - - // run terraform apply station -s dev and terraform apply station -s prod - err = cmd.Run() - if err != nil { - t.Log(stdout.String()) - t.Fatalf("Failed to run terraform apply station -s dev: %v", stderr.String()) - return - } - cmd = exec.Command(binaryPath, "terraform", "apply", "station", "-s", "prod") - envVars = append(envVars, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") - cmd.Env = envVars - - err = cmd.Run() - if err != nil { - t.Log(stdout.String()) - t.Fatalf("Failed to run terraform apply station -s prod: %v", stderr.String()) - return - } - // get command error sta - // check if the state files and directories for the component and stack are exist - stateFiles := []string{ - "./components/terraform/weather/.terraform", - "./components/terraform/weather/terraform.tfstate.d", - "./components/terraform/weather/.terraform.lock.hcl", - } - for _, file := range stateFiles { - fileAbs, err := filepath.Abs(file) - if err != nil { - t.Fatalf("Failed to resolve absolute path for %q: %v", file, err) - } - if _, err := os.Stat(fileAbs); errors.Is(err, os.ErrNotExist) { - t.Errorf("Reason: Expected file exist: %q", fileAbs) - return - } - } - - // run atmos terraform clean - cmd = exec.Command(binaryPath, "terraform", "clean", "--force") - err = cmd.Run() - if err != nil { - t.Log(stdout.String()) - t.Fatalf("Failed to run atmos terraform clean: %v", stderr.String()) - } - // check if the state files and directories for the component and stack are deleted - for _, file := range stateFiles { - fileAbs, err := filepath.Abs(file) - if err != nil { - t.Fatalf("Failed to resolve absolute path for %q: %v", file, err) - } - _, err = os.Stat(fileAbs) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - continue - } - t.Errorf("Reason: error %q", fileAbs) - return - - } - return - } - -} From 2ad7321061195ecdac7faa2320f37bcd08aff286 Mon Sep 17 00:00:00 2001 From: haitham911 Date: Tue, 21 Jan 2025 17:04:33 +0200 Subject: [PATCH 12/24] add clean help --- internal/exec/help.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/internal/exec/help.go b/internal/exec/help.go index a9bf9a271..bb19a9e19 100644 --- a/internal/exec/help.go +++ b/internal/exec/help.go @@ -35,6 +35,24 @@ func processHelp( u.PrintMessage(fmt.Sprintf("atmos %s -s [options]", componentType)) u.PrintMessage(fmt.Sprintf("atmos %s --stack [options]", componentType)) u.PrintMessage(fmt.Sprintf("\nFor more details, execute '%s --help'\n", componentType)) + } else if componentType == "terraform" && command == "clean" { + u.PrintMessage("\n'atmos terraform clean' command deletes the following folders and files from the component's directory:\n\n" + + " - '.terraform' folder\n" + + " - folder that the 'TF_DATA_DIR' ENV var points to\n" + + " - '.terraform.lock.hcl' file\n" + + " - generated varfile for the component in the stack\n" + + " - generated planfile for the component in the stack\n" + + " - generated 'backend.tf.json' file\n" + + " - 'terraform.tfstate.d' folder (if '--everything' flag is used)\n\n" + + "Usage: atmos terraform clean -s \n\n" + + "Use '--everything' flag to also delete the Terraform state files and and directories with confirm message.\n\n" + + "Use --force to forcefully delete Terraform state files and directories for the component.\n\n" + + "- If no component is specified, the command will apply to all components and stacks.\n" + + "- If no stack is specified, the command will apply to all stacks for the specified component.\n" + + "Use '--skip-lock-file' flag to skip deleting the '.terraform.lock.hcl' file.\n\n" + + "If no component or stack is specified, the clean operation will apply globally to all components.\n\n" + + "For more details refer to https://atmos.tools/cli/commands/terraform/clean\n") + } return nil From b87a85106328f5c5a9e3647807173166a951c9a8 Mon Sep 17 00:00:00 2001 From: haitham911 Date: Tue, 21 Jan 2025 17:24:02 +0200 Subject: [PATCH 13/24] improve TestCLITerraformClean --- tests/cli_terraform_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/cli_terraform_test.go b/tests/cli_terraform_test.go index 7adcaa596..2923c9ada 100644 --- a/tests/cli_terraform_test.go +++ b/tests/cli_terraform_test.go @@ -57,7 +57,6 @@ func TestCLITerraformClean(t *testing.T) { return } cmd = exec.Command(binaryPath, "terraform", "apply", "station", "-s", "prod") - envVars = append(envVars, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") cmd.Env = envVars err = cmd.Run() @@ -86,10 +85,13 @@ func TestCLITerraformClean(t *testing.T) { // run atmos terraform clean cmd = exec.Command(binaryPath, "terraform", "clean", "--force") + var stdoutClean, stderrClean bytes.Buffer + cmd.Stdout = &stdoutClean + cmd.Stderr = &stderrClean err = cmd.Run() + t.Logf("Clean command output:\n%s", stdoutClean.String()) if err != nil { - t.Log(stdout.String()) - t.Fatalf("Failed to run atmos terraform clean: %v", stderr.String()) + t.Fatalf("Failed to run atmos terraform clean: %v", stderrClean.String()) } // check if the state files and directories for the component and stack are deleted for _, file := range stateFiles { @@ -98,15 +100,13 @@ func TestCLITerraformClean(t *testing.T) { t.Fatalf("Failed to resolve absolute path for %q: %v", file, err) } _, err = os.Stat(fileAbs) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - continue - } - t.Errorf("Reason: error %q", fileAbs) - return - + if err == nil { + t.Errorf("Expected Terraform state file to be deleted: %q", fileAbs) + continue + } + if !errors.Is(err, os.ErrNotExist) { + t.Errorf("Unexpected error checking file %q: %v", fileAbs, err) } - return } } From e3b16d9190c4c7c0cc2ff9d4b54eef2cee21c68a Mon Sep 17 00:00:00 2001 From: haitham911 Date: Tue, 21 Jan 2025 17:44:18 +0200 Subject: [PATCH 14/24] improve TestCLITerraformClean --- tests/cli_terraform_test.go | 38 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/cli_terraform_test.go b/tests/cli_terraform_test.go index 2923c9ada..2cdc6c0e5 100644 --- a/tests/cli_terraform_test.go +++ b/tests/cli_terraform_test.go @@ -40,29 +40,33 @@ func TestCLITerraformClean(t *testing.T) { if err != nil { t.Fatalf("Binary not found: %s. Current PATH: %s", "atmos", pathManager.GetPath()) } - cmd := exec.Command(binaryPath, "terraform", "apply", "station", "-s", "dev") + cmdDev := exec.Command(binaryPath, "terraform", "apply", "station", "-s", "dev") var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr + cmdDev.Stdout = &stdout + cmdDev.Stderr = &stderr // ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE - envVars := os.Environ() - envVars = append(envVars, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") - cmd.Env = envVars + envVarsDev := os.Environ() + envVarsDev = append(envVarsDev, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") + cmdDev.Env = envVarsDev // run terraform apply station -s dev and terraform apply station -s prod - err = cmd.Run() + err = cmdDev.Run() if err != nil { t.Log(stdout.String()) t.Fatalf("Failed to run terraform apply station -s dev: %v", stderr.String()) return } - cmd = exec.Command(binaryPath, "terraform", "apply", "station", "-s", "prod") - cmd.Env = envVars - - err = cmd.Run() + cmdProd := exec.Command(binaryPath, "terraform", "apply", "station", "-s", "prod") + envVarsProd := os.Environ() + envVarsProd = append(envVarsProd, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") + cmdProd.Env = envVarsProd + var stdoutProd, stderrProd bytes.Buffer + cmdDev.Stdout = &stdoutProd + cmdDev.Stderr = &stderrProd + err = cmdProd.Run() if err != nil { - t.Log(stdout.String()) - t.Fatalf("Failed to run terraform apply station -s prod: %v", stderr.String()) + t.Log(stdoutProd.String()) + t.Fatalf("Failed to run terraform apply station -s prod: %v", stderrProd.String()) return } // get command error sta @@ -84,11 +88,11 @@ func TestCLITerraformClean(t *testing.T) { } // run atmos terraform clean - cmd = exec.Command(binaryPath, "terraform", "clean", "--force") + cmdClean := exec.Command(binaryPath, "terraform", "clean", "--force") var stdoutClean, stderrClean bytes.Buffer - cmd.Stdout = &stdoutClean - cmd.Stderr = &stderrClean - err = cmd.Run() + cmdClean.Stdout = &stdoutClean + cmdClean.Stderr = &stderrClean + err = cmdClean.Run() t.Logf("Clean command output:\n%s", stdoutClean.String()) if err != nil { t.Fatalf("Failed to run atmos terraform clean: %v", stderrClean.String()) From 24f738505f3e07e0dc8758a3d2ea0f98fc9d9f76 Mon Sep 17 00:00:00 2001 From: haitham911 Date: Tue, 21 Jan 2025 17:51:25 +0200 Subject: [PATCH 15/24] improve clean test --- tests/cli_terraform_test.go | 103 ++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 47 deletions(-) diff --git a/tests/cli_terraform_test.go b/tests/cli_terraform_test.go index 2cdc6c0e5..ae83058cf 100644 --- a/tests/cli_terraform_test.go +++ b/tests/cli_terraform_test.go @@ -31,73 +31,85 @@ func TestCLITerraformClean(t *testing.T) { t.Fatalf("Failed to change back to the starting directory: %v", err) } }() + + // Define the work directory and change to it workDir := "../examples/quick-start-simple" - err = os.Chdir(workDir) - if err != nil { + if err := os.Chdir(workDir); err != nil { t.Fatalf("Failed to change directory to %q: %v", workDir, err) } + + // Find the binary path for "atmos" binaryPath, err := exec.LookPath("atmos") if err != nil { t.Fatalf("Binary not found: %s. Current PATH: %s", "atmos", pathManager.GetPath()) } - cmdDev := exec.Command(binaryPath, "terraform", "apply", "station", "-s", "dev") - var stdout, stderr bytes.Buffer - cmdDev.Stdout = &stdout - cmdDev.Stderr = &stderr - // ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE - envVarsDev := os.Environ() - envVarsDev = append(envVarsDev, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") - cmdDev.Env = envVarsDev - // run terraform apply station -s dev and terraform apply station -s prod - err = cmdDev.Run() - if err != nil { - t.Log(stdout.String()) - t.Fatalf("Failed to run terraform apply station -s dev: %v", stderr.String()) - return - } - cmdProd := exec.Command(binaryPath, "terraform", "apply", "station", "-s", "prod") - envVarsProd := os.Environ() - envVarsProd = append(envVarsProd, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") - cmdProd.Env = envVarsProd - var stdoutProd, stderrProd bytes.Buffer - cmdDev.Stdout = &stdoutProd - cmdDev.Stderr = &stderrProd - err = cmdProd.Run() - if err != nil { - t.Log(stdoutProd.String()) - t.Fatalf("Failed to run terraform apply station -s prod: %v", stderrProd.String()) - return - } - // get command error sta - // check if the state files and directories for the component and stack are exist + // Run terraform apply for dev environment + runTerraformApply(t, binaryPath, "dev") + + // Run terraform apply for prod environment + runTerraformApply(t, binaryPath, "prod") + + // Verify if state files exist before cleaning stateFiles := []string{ "./components/terraform/weather/.terraform", "./components/terraform/weather/terraform.tfstate.d", "./components/terraform/weather/.terraform.lock.hcl", } + verifyStateFilesExist(t, stateFiles) + + // Run terraform clean + runTerraformClean(t, binaryPath) + + // Verify if state files have been deleted after clean + verifyStateFilesDeleted(t, stateFiles) +} + +// runTerraformApply runs the terraform apply command for a given environment. +func runTerraformApply(t *testing.T, binaryPath, environment string) { + cmd := exec.Command(binaryPath, "terraform", "apply", "station", "-s", environment) + envVars := os.Environ() + envVars = append(envVars, "ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE=true") + cmd.Env = envVars + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + t.Log(stdout.String()) + if err != nil { + t.Fatalf("Failed to run terraform apply station -s %s: %v", environment, stderr.String()) + } +} + +// verifyStateFilesExist checks if the state files exist before cleaning. +func verifyStateFilesExist(t *testing.T, stateFiles []string) { for _, file := range stateFiles { fileAbs, err := filepath.Abs(file) if err != nil { t.Fatalf("Failed to resolve absolute path for %q: %v", file, err) } if _, err := os.Stat(fileAbs); errors.Is(err, os.ErrNotExist) { - t.Errorf("Reason: Expected file exist: %q", fileAbs) - return + t.Errorf("Expected file to exist before cleaning: %q", fileAbs) } } +} - // run atmos terraform clean - cmdClean := exec.Command(binaryPath, "terraform", "clean", "--force") - var stdoutClean, stderrClean bytes.Buffer - cmdClean.Stdout = &stdoutClean - cmdClean.Stderr = &stderrClean - err = cmdClean.Run() - t.Logf("Clean command output:\n%s", stdoutClean.String()) +// runTerraformClean runs the terraform clean command. +func runTerraformClean(t *testing.T, binaryPath string) { + cmd := exec.Command(binaryPath, "terraform", "clean", "--force") + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + t.Logf("Clean command output:\n%s", stdout.String()) if err != nil { - t.Fatalf("Failed to run atmos terraform clean: %v", stderrClean.String()) + t.Fatalf("Failed to run terraform clean: %v", stderr.String()) } - // check if the state files and directories for the component and stack are deleted +} + +// verifyStateFilesDeleted checks if the state files have been deleted after cleaning. +func verifyStateFilesDeleted(t *testing.T, stateFiles []string) { for _, file := range stateFiles { fileAbs, err := filepath.Abs(file) if err != nil { @@ -106,11 +118,8 @@ func TestCLITerraformClean(t *testing.T) { _, err = os.Stat(fileAbs) if err == nil { t.Errorf("Expected Terraform state file to be deleted: %q", fileAbs) - continue - } - if !errors.Is(err, os.ErrNotExist) { + } else if !errors.Is(err, os.ErrNotExist) { t.Errorf("Unexpected error checking file %q: %v", fileAbs, err) } } - } From 87f9bc4c1ee153e832e10f1c13858ecc3381d87c Mon Sep 17 00:00:00 2001 From: haitham911 Date: Tue, 21 Jan 2025 19:15:58 +0200 Subject: [PATCH 16/24] add clean runCLITerraformCleanComponent --- tests/cli_terraform_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/cli_terraform_test.go b/tests/cli_terraform_test.go index ae83058cf..3d4b52dd6 100644 --- a/tests/cli_terraform_test.go +++ b/tests/cli_terraform_test.go @@ -43,6 +43,11 @@ func TestCLITerraformClean(t *testing.T) { if err != nil { t.Fatalf("Binary not found: %s. Current PATH: %s", "atmos", pathManager.GetPath()) } + // Run terraform apply for prod environment + runTerraformApply(t, binaryPath, "prod") + verifyStateFilesExist(t, []string{"./components/terraform/weather/terraform.tfstate.d/prod-station"}) + runCLITerraformCleanComponent(t, binaryPath, "prod") + verifyStateFilesDeleted(t, []string{"./components/terraform/weather/terraform.tfstate.d/prod-station"}) // Run terraform apply for dev environment runTerraformApply(t, binaryPath, "dev") @@ -63,6 +68,7 @@ func TestCLITerraformClean(t *testing.T) { // Verify if state files have been deleted after clean verifyStateFilesDeleted(t, stateFiles) + } // runTerraformApply runs the terraform apply command for a given environment. @@ -123,3 +129,15 @@ func verifyStateFilesDeleted(t *testing.T, stateFiles []string) { } } } + +func runCLITerraformCleanComponent(t *testing.T, binaryPath, environment string) { + cmd := exec.Command(binaryPath, "terraform", "clean", "station", "-s", environment, "--force") + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + t.Logf("Clean command output:\n%s", stdout.String()) + if err != nil { + t.Fatalf("Failed to run terraform clean: %v", stderr.String()) + } +} From d5cca3252c2ca00c4bdcebf150d97ab3617ecdea Mon Sep 17 00:00:00 2001 From: haitham911 Date: Tue, 21 Jan 2025 23:02:50 +0200 Subject: [PATCH 17/24] modify docs --- website/docs/cli/commands/terraform/terraform-clean.mdx | 2 +- website/docs/cli/commands/terraform/usage.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/cli/commands/terraform/terraform-clean.mdx b/website/docs/cli/commands/terraform/terraform-clean.mdx index 4834df177..11ecf178e 100644 --- a/website/docs/cli/commands/terraform/terraform-clean.mdx +++ b/website/docs/cli/commands/terraform/terraform-clean.mdx @@ -23,7 +23,7 @@ Execute the `terraform clean` command like this: atmos terraform clean -s [--skip-lock-file] [--everything] [--force] :::warning -The `clean` default behavior and will delete all Terraform-related files including state files, with a confirmation prompt before proceeding. The `--force` flag will bypass confirmation prompts. +The `clean` command, by default, deletes all Terraform-related files, including local state files, but will prompt for confirmation before proceeding. Using the `--force` flag skips the confirmation prompt and executes the deletion immediately. Use these flags with extreme caution as they can lead to irreversible data loss. ::: ``` diff --git a/website/docs/cli/commands/terraform/usage.mdx b/website/docs/cli/commands/terraform/usage.mdx index 3a28d2ce2..a2a7e98f0 100644 --- a/website/docs/cli/commands/terraform/usage.mdx +++ b/website/docs/cli/commands/terraform/usage.mdx @@ -63,7 +63,7 @@ HCL-based domain-specific language and its interpreter. Atmos works with [OpenTo The `--force` flag bypasses the safety confirmation prompt and forces the deletion. Use with caution. :::warning - The `clean` performs destructive operations that can lead to permanent state loss. Always ensure you have remote state configured in your components before proceeding. + The `clean` command performs destructive operations that can lead to permanent state loss, if not using remote backends. Always ensure you have remote state configured in your components before proceeding. ::: - `atmos terraform workspace` command first runs `terraform init -reconfigure`, then `terraform workspace select`, and if the workspace was not From 73c6a80f29363d51913c4a26c0369257e79d2c3c Mon Sep 17 00:00:00 2001 From: haitham911 Date: Wed, 22 Jan 2025 17:37:18 +0200 Subject: [PATCH 18/24] improve test --- tests/cli_terraform_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/cli_terraform_test.go b/tests/cli_terraform_test.go index 3d4b52dd6..433e55458 100644 --- a/tests/cli_terraform_test.go +++ b/tests/cli_terraform_test.go @@ -52,9 +52,6 @@ func TestCLITerraformClean(t *testing.T) { // Run terraform apply for dev environment runTerraformApply(t, binaryPath, "dev") - // Run terraform apply for prod environment - runTerraformApply(t, binaryPath, "prod") - // Verify if state files exist before cleaning stateFiles := []string{ "./components/terraform/weather/.terraform", From dfd8ada510e1fc14226f5880ceea92ee4faad46e Mon Sep 17 00:00:00 2001 From: haitham911 Date: Thu, 23 Jan 2025 00:30:49 +0200 Subject: [PATCH 19/24] remove clean help from help.go --- internal/exec/help.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/internal/exec/help.go b/internal/exec/help.go index bb19a9e19..3c800059c 100644 --- a/internal/exec/help.go +++ b/internal/exec/help.go @@ -35,25 +35,6 @@ func processHelp( u.PrintMessage(fmt.Sprintf("atmos %s -s [options]", componentType)) u.PrintMessage(fmt.Sprintf("atmos %s --stack [options]", componentType)) u.PrintMessage(fmt.Sprintf("\nFor more details, execute '%s --help'\n", componentType)) - } else if componentType == "terraform" && command == "clean" { - u.PrintMessage("\n'atmos terraform clean' command deletes the following folders and files from the component's directory:\n\n" + - " - '.terraform' folder\n" + - " - folder that the 'TF_DATA_DIR' ENV var points to\n" + - " - '.terraform.lock.hcl' file\n" + - " - generated varfile for the component in the stack\n" + - " - generated planfile for the component in the stack\n" + - " - generated 'backend.tf.json' file\n" + - " - 'terraform.tfstate.d' folder (if '--everything' flag is used)\n\n" + - "Usage: atmos terraform clean -s \n\n" + - "Use '--everything' flag to also delete the Terraform state files and and directories with confirm message.\n\n" + - "Use --force to forcefully delete Terraform state files and directories for the component.\n\n" + - "- If no component is specified, the command will apply to all components and stacks.\n" + - "- If no stack is specified, the command will apply to all stacks for the specified component.\n" + - "Use '--skip-lock-file' flag to skip deleting the '.terraform.lock.hcl' file.\n\n" + - "If no component or stack is specified, the clean operation will apply globally to all components.\n\n" + - "For more details refer to https://atmos.tools/cli/commands/terraform/clean\n") - } - return nil } From 76d5cdaa0d2a9476dcd1648ae8acab95541bddfc Mon Sep 17 00:00:00 2001 From: haitham911 Date: Thu, 23 Jan 2025 00:36:27 +0200 Subject: [PATCH 20/24] add line --- internal/exec/help.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/exec/help.go b/internal/exec/help.go index 3c800059c..a9bf9a271 100644 --- a/internal/exec/help.go +++ b/internal/exec/help.go @@ -36,5 +36,6 @@ func processHelp( u.PrintMessage(fmt.Sprintf("atmos %s --stack [options]", componentType)) u.PrintMessage(fmt.Sprintf("\nFor more details, execute '%s --help'\n", componentType)) } + return nil } From 2cf9f384410c8f2e53a2f6b2baec9006717395b6 Mon Sep 17 00:00:00 2001 From: aknysh Date: Mon, 27 Jan 2025 22:26:19 -0500 Subject: [PATCH 21/24] updates --- website/docs/cli/commands/terraform/terraform-clean.mdx | 2 +- website/docs/cli/commands/terraform/usage.mdx | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/website/docs/cli/commands/terraform/terraform-clean.mdx b/website/docs/cli/commands/terraform/terraform-clean.mdx index 11ecf178e..51cff97ce 100644 --- a/website/docs/cli/commands/terraform/terraform-clean.mdx +++ b/website/docs/cli/commands/terraform/terraform-clean.mdx @@ -21,12 +21,12 @@ Execute the `terraform clean` command like this: ```shell atmos terraform clean -s [--skip-lock-file] [--everything] [--force] +``` :::warning The `clean` command, by default, deletes all Terraform-related files, including local state files, but will prompt for confirmation before proceeding. Using the `--force` flag skips the confirmation prompt and executes the deletion immediately. Use these flags with extreme caution as they can lead to irreversible data loss. ::: -``` :::tip Run `atmos terraform clean --help` to see all the available options diff --git a/website/docs/cli/commands/terraform/usage.mdx b/website/docs/cli/commands/terraform/usage.mdx index a2a7e98f0..c3aed6b08 100644 --- a/website/docs/cli/commands/terraform/usage.mdx +++ b/website/docs/cli/commands/terraform/usage.mdx @@ -59,11 +59,14 @@ HCL-based domain-specific language and its interpreter. Atmos works with [OpenTo - `atmos terraform clean` command deletes the `.terraform` folder, `.terraform.lock.hcl` lock file, and the previously generated `planfile` and `varfile` for the specified component and stack. Use the `--skip-lock-file` flag to skip deleting the `.terraform.lock.hcl` file. - It deletes all local Terraform state files and directories (including `terraform.tfstate.d/`) for all components and stacks. + It deletes all local Terraform state files and directories + (including [`terraform.tfstate.d`](https://developer.hashicorp.com/terraform/cli/workspaces#workspace-internals) + used for local state) for a component in a stack. The `--force` flag bypasses the safety confirmation prompt and forces the deletion. Use with caution. :::warning - The `clean` command performs destructive operations that can lead to permanent state loss, if not using remote backends. Always ensure you have remote state configured in your components before proceeding. + The `clean` command performs destructive operations that can lead to permanent state loss, if not using remote backends. + Always ensure you have remote state configured in your components before proceeding. ::: - `atmos terraform workspace` command first runs `terraform init -reconfigure`, then `terraform workspace select`, and if the workspace was not From ff8df5f82578913f4ab12928ac727b4b6eba0139 Mon Sep 17 00:00:00 2001 From: haitham911 Date: Thu, 30 Jan 2025 03:39:34 +0200 Subject: [PATCH 22/24] fix clean cmd --- internal/exec/terraform.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/exec/terraform.go b/internal/exec/terraform.go index 5686d3c09..c1acc4a95 100644 --- a/internal/exec/terraform.go +++ b/internal/exec/terraform.go @@ -36,8 +36,7 @@ func shouldProcessStacks(info *schema.ConfigAndStacksInfo) (bool, bool) { shouldProcessStacks := true shouldCheckStack := true - if info.SubCommand == "clean" && - u.SliceContainsString(info.AdditionalArgsAndFlags, forceFlag) { + if info.SubCommand == "clean" { if info.ComponentFromArg == "" { shouldProcessStacks = false } From 319cd712ac9cfd9fb97ee7e2b6cdcf85c3a448b9 Mon Sep 17 00:00:00 2001 From: haitham911 Date: Thu, 30 Jan 2025 03:39:52 +0200 Subject: [PATCH 23/24] add test for clean cmd --- tests/cli_terraform_test.go | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/cli_terraform_test.go b/tests/cli_terraform_test.go index 433e55458..72b22e175 100644 --- a/tests/cli_terraform_test.go +++ b/tests/cli_terraform_test.go @@ -43,6 +43,12 @@ func TestCLITerraformClean(t *testing.T) { if err != nil { t.Fatalf("Binary not found: %s. Current PATH: %s", "atmos", pathManager.GetPath()) } + // test terraform clean + runTerraformCleanForce(t, binaryPath) + runCLITerraformClean(t, binaryPath) + runCLITerraformCleanSpecificComponent(t, binaryPath) + runCLITerraformCleanComponentStack(t, binaryPath) + // Run terraform apply for prod environment runTerraformApply(t, binaryPath, "prod") verifyStateFilesExist(t, []string{"./components/terraform/weather/terraform.tfstate.d/prod-station"}) @@ -138,3 +144,50 @@ func runCLITerraformCleanComponent(t *testing.T, binaryPath, environment string) t.Fatalf("Failed to run terraform clean: %v", stderr.String()) } } +func runCLITerraformClean(t *testing.T, binaryPath string) { + cmd := exec.Command(binaryPath, "terraform", "clean") + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + t.Logf("Clean command output:\n%s", stdout.String()) + if err != nil { + t.Fatalf("Failed to run terraform clean: %v", stderr.String()) + } + +} +func runCLITerraformCleanSpecificComponent(t *testing.T, binaryPath string) { + cmd := exec.Command(binaryPath, "terraform", "clean", "station") + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + t.Logf("Clean command output:\n%s", stdout.String()) + if err != nil { + t.Fatalf("Failed to run terraform clean: %v", stderr.String()) + } + +} +func runCLITerraformCleanComponentStack(t *testing.T, binaryPath string) { + cmd := exec.Command(binaryPath, "terraform", "clean", "station", "-s", "dev") + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + t.Logf("Clean command output:\n%s", stdout.String()) + if err != nil { + t.Fatalf("Failed to run terraform clean: %v", stderr.String()) + } + +} +func runTerraformCleanForce(t *testing.T, binaryPath string) { + cmd := exec.Command(binaryPath, "terraform", "clean", "--force") + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + t.Logf("Clean command output:\n%s", stdout.String()) + if err != nil { + t.Fatalf("Failed to run terraform clean: %v", stderr.String()) + } +} From 00346397256ad8893ae19ffc90e2aa3460dbdc6e Mon Sep 17 00:00:00 2001 From: haitham911 Date: Thu, 30 Jan 2025 03:50:42 +0200 Subject: [PATCH 24/24] improve test --- tests/cli_terraform_test.go | 42 +++++++++++-------------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/tests/cli_terraform_test.go b/tests/cli_terraform_test.go index 72b22e175..53a63d0b0 100644 --- a/tests/cli_terraform_test.go +++ b/tests/cli_terraform_test.go @@ -43,11 +43,15 @@ func TestCLITerraformClean(t *testing.T) { if err != nil { t.Fatalf("Binary not found: %s. Current PATH: %s", "atmos", pathManager.GetPath()) } - // test terraform clean - runTerraformCleanForce(t, binaryPath) - runCLITerraformClean(t, binaryPath) - runCLITerraformCleanSpecificComponent(t, binaryPath) - runCLITerraformCleanComponentStack(t, binaryPath) + + // Force clean everything + runTerraformCleanCommand(t, binaryPath, "--force") + // Clean everything + runTerraformCleanCommand(t, binaryPath) + // Clean specific component + runTerraformCleanCommand(t, binaryPath, "station") + // Clean component with stack + runTerraformCleanCommand(t, binaryPath, "station", "-s", "dev") // Run terraform apply for prod environment runTerraformApply(t, binaryPath, "prod") @@ -156,32 +160,10 @@ func runCLITerraformClean(t *testing.T, binaryPath string) { } } -func runCLITerraformCleanSpecificComponent(t *testing.T, binaryPath string) { - cmd := exec.Command(binaryPath, "terraform", "clean", "station") - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() - t.Logf("Clean command output:\n%s", stdout.String()) - if err != nil { - t.Fatalf("Failed to run terraform clean: %v", stderr.String()) - } -} -func runCLITerraformCleanComponentStack(t *testing.T, binaryPath string) { - cmd := exec.Command(binaryPath, "terraform", "clean", "station", "-s", "dev") - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() - t.Logf("Clean command output:\n%s", stdout.String()) - if err != nil { - t.Fatalf("Failed to run terraform clean: %v", stderr.String()) - } - -} -func runTerraformCleanForce(t *testing.T, binaryPath string) { - cmd := exec.Command(binaryPath, "terraform", "clean", "--force") +func runTerraformCleanCommand(t *testing.T, binaryPath string, args ...string) { + cmdArgs := append([]string{"terraform", "clean"}, args...) + cmd := exec.Command(binaryPath, cmdArgs...) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr