From 342dae4d9df607f9b077d188391de0751c658c46 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Fri, 27 Dec 2024 12:11:35 +0100 Subject: [PATCH 01/32] Add editorconfig-checker to atmos cli issue: https://linear.app/cloudposse/issue/DEV-2836/implement-atmos-validate-editorconfig --- cmd/editor_config.go | 137 +++++++++++++++++++++++++++++++++++++++++++ go.mod | 4 ++ go.sum | 8 +++ 3 files changed, 149 insertions(+) create mode 100644 cmd/editor_config.go diff --git a/cmd/editor_config.go b/cmd/editor_config.go new file mode 100644 index 000000000..552d3d22a --- /dev/null +++ b/cmd/editor_config.go @@ -0,0 +1,137 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/config" + er "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/error" + "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/files" + "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/logger" + "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/utils" + "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/validation" + "github.com/spf13/cobra" +) + +var ( + editorConfigVersion = "v3.0.0" + defaultConfigFilePath = ".ecrc" + currentConfig *config.Config + cliConfig config.Config + configFilePath string + tmpExclude string +) + +var editorConfigCmd *cobra.Command = &cobra.Command{ + Use: "editorconfig-checker", + Short: "EditorConfig Checker CLI", + Long: "A command-line tool to check your files against the EditorConfig rules", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + initializeConfig() + }, + Run: func(cmd *cobra.Command, args []string) { + runMainLogic() + }, +} + +// initializeConfig breaks the initialization cycle by separating the config setup +func initializeConfig() { + if configFilePath == "" { + configFilePath = defaultConfigFilePath + } + + var err error + currentConfig, err = config.NewConfig(configFilePath) + if err != nil { + logger.Error(err.Error()) + os.Exit(1) + } + + _ = currentConfig.Parse() + + if tmpExclude != "" { + currentConfig.Exclude = append(currentConfig.Exclude, tmpExclude) + } + + currentConfig.Merge(cliConfig) +} + +// runMainLogic contains the main logic +func runMainLogic() { + config := *currentConfig + config.Logger.Debug(config.GetAsString()) + config.Logger.Verbose("Exclude Regexp: %s", config.GetExcludesAsRegularExpression()) + + if utils.FileExists(config.Path) && config.Version != "" && config.Version != editorConfigVersion { + config.Logger.Error("Version from config file is not the same as the version of the binary") + config.Logger.Error("Binary: %s, Config: %s", editorConfigVersion, config.Version) + os.Exit(1) + } + + if handleReturnableFlags(config) { + return + } + + filePaths, err := files.GetFiles(config) + if err != nil { + config.Logger.Error(err.Error()) + os.Exit(1) + } + + if config.DryRun { + for _, file := range filePaths { + config.Logger.Output(file) + } + os.Exit(0) + } + + errors := validation.ProcessValidation(filePaths, config) + errorCount := er.GetErrorCount(errors) + + if errorCount != 0 { + er.PrintErrors(errors, config) + config.Logger.Error(fmt.Sprintf("\n%d errors found", errorCount)) + os.Exit(1) + } + + config.Logger.Verbose("%d files checked", len(filePaths)) +} + +// handleReturnableFlags handles early termination flags +func handleReturnableFlags(config config.Config) bool { + if config.ShowVersion { + config.Logger.Output(editorConfigVersion) + return true + } + if config.Help { + config.Logger.Output("USAGE:") + return true + } + return false +} + +// addPersistentFlags adds flags to the root command +func addPersistentFlags(cmd *cobra.Command) { + cmd.PersistentFlags().StringVar(&configFilePath, "config", "", "Path to the configuration file") + cmd.PersistentFlags().StringVar(&tmpExclude, "exclude", "", "Regex to exclude files from checking") + cmd.PersistentFlags().BoolVar(&cliConfig.IgnoreDefaults, "ignore-defaults", false, "Ignore default excludes") + cmd.PersistentFlags().BoolVar(&cliConfig.DryRun, "dry-run", false, "Show which files would be checked") + cmd.PersistentFlags().BoolVar(&cliConfig.ShowVersion, "version", false, "Print the version number") + cmd.PersistentFlags().BoolVar(&cliConfig.Help, "help", false, "Print help information") + cmd.PersistentFlags().StringVar(&cliConfig.Format, "format", "default", "Specify the output format: default, gcc") + cmd.PersistentFlags().BoolVar(&cliConfig.Verbose, "verbose", false, "Print debugging information") + cmd.PersistentFlags().BoolVar(&cliConfig.NoColor, "no-color", false, "Don't print colors") + cmd.PersistentFlags().BoolVar(&cliConfig.Disable.TrimTrailingWhitespace, "disable-trim-trailing-whitespace", false, "Disable trailing whitespace check") + cmd.PersistentFlags().BoolVar(&cliConfig.Disable.EndOfLine, "disable-end-of-line", false, "Disable end-of-line check") + cmd.PersistentFlags().BoolVar(&cliConfig.Disable.InsertFinalNewline, "disable-insert-final-newline", false, "Disable final newline check") + cmd.PersistentFlags().BoolVar(&cliConfig.Disable.Indentation, "disable-indentation", false, "Disable indentation check") + cmd.PersistentFlags().BoolVar(&cliConfig.Disable.IndentSize, "disable-indent-size", false, "Disable indent size check") + cmd.PersistentFlags().BoolVar(&cliConfig.Disable.MaxLineLength, "disable-max-line-length", false, "Disable max line length check") +} + +func init() { + // Add flags + addPersistentFlags(editorConfigCmd) + // Add command + validateCmd.AddCommand(editorConfigCmd) +} diff --git a/go.mod b/go.mod index 80f5d7b35..0890fedcd 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/charmbracelet/huh v0.6.0 github.com/charmbracelet/lipgloss v1.0.0 github.com/charmbracelet/log v0.4.0 + github.com/editorconfig-checker/editorconfig-checker/v3 v3.0.3 github.com/elewis787/boa v0.1.2 github.com/fatih/color v1.18.0 github.com/go-git/go-git/v5 v5.12.0 @@ -95,6 +96,7 @@ require ( github.com/aws/smithy-go v1.22.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect + github.com/baulk/chardet v0.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 // indirect @@ -122,10 +124,12 @@ require ( github.com/docker/libkv v0.2.2-0.20180912205406-458977154600 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // indirect + github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.4 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-ini/ini v1.67.0 // indirect diff --git a/go.sum b/go.sum index b4488f801..943e3f75d 100644 --- a/go.sum +++ b/go.sum @@ -385,6 +385,8 @@ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWp github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/baulk/chardet v0.1.0 h1:6/r5nPMikB9OG1Njs10VfVHZTDMFH6BdybHPISpfUVA= +github.com/baulk/chardet v0.1.0/go.mod h1:0ibN6068qswel5Hv54U7GNJUU57njfzPJrLIq7Y8xas= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -513,6 +515,10 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA= github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ= github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/editorconfig-checker/editorconfig-checker/v3 v3.0.3 h1:WO9Yd7/KjfGDYUeSBsGKh+Uj+K+/oTnJ3elDQ7Oq7yU= +github.com/editorconfig-checker/editorconfig-checker/v3 v3.0.3/go.mod h1:9mvpU+I3xMoU+l0vNtR98SUX/AsoAhBCntntRtNIu3Y= +github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 h1:dKG8sc7n321deIVRcQtwlMNoBEra7j0qQ8RwxO8RN0w= +github.com/editorconfig/editorconfig-core-go/v2 v2.6.2/go.mod h1:7dvD3GCm7eBw53xZ/lsiq72LqobdMg3ITbMBxnmJmqY= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elewis787/boa v0.1.2 h1:xNKWJ9X2MWbLSLLOA31N4l1Jdec9FZSkbTvXy3C8rw4= @@ -553,6 +559,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= +github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= From 667e1e56b7726b0a1e5cc69eda4121c262e54189 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Fri, 27 Dec 2024 14:13:52 +0100 Subject: [PATCH 02/32] Add positive test case for editorconfig-checker --- cmd/editor_config.go | 8 ++++++-- examples/demo-stacks/.ecrc | 38 ++++++++++++++++++++++++++++++++++++++ tests/test_cases.yaml | 16 +++++++++++++++- 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 examples/demo-stacks/.ecrc diff --git a/cmd/editor_config.go b/cmd/editor_config.go index 552d3d22a..a87d61af4 100644 --- a/cmd/editor_config.go +++ b/cmd/editor_config.go @@ -4,6 +4,8 @@ import ( "fmt" "os" + u "github.com/cloudposse/atmos/pkg/utils" + "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/config" er "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/error" "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/files" @@ -36,6 +38,7 @@ var editorConfigCmd *cobra.Command = &cobra.Command{ // initializeConfig breaks the initialization cycle by separating the config setup func initializeConfig() { + u.LogInfo(atmosConfig, fmt.Sprintf("EditorConfig Checker CLI Version: %s", editorConfigVersion)) if configFilePath == "" { configFilePath = defaultConfigFilePath } @@ -59,8 +62,8 @@ func initializeConfig() { // runMainLogic contains the main logic func runMainLogic() { config := *currentConfig - config.Logger.Debug(config.GetAsString()) - config.Logger.Verbose("Exclude Regexp: %s", config.GetExcludesAsRegularExpression()) + u.LogDebug(atmosConfig, config.GetAsString()) + u.LogTrace(atmosConfig, fmt.Sprintf("Exclude Regexp: %s", config.GetExcludesAsRegularExpression())) if utils.FileExists(config.Path) && config.Version != "" && config.Version != editorConfigVersion { config.Logger.Error("Version from config file is not the same as the version of the binary") @@ -95,6 +98,7 @@ func runMainLogic() { } config.Logger.Verbose("%d files checked", len(filePaths)) + u.LogInfo(atmosConfig, "No errors found") } // handleReturnableFlags handles early termination flags diff --git a/examples/demo-stacks/.ecrc b/examples/demo-stacks/.ecrc new file mode 100644 index 000000000..836760da3 --- /dev/null +++ b/examples/demo-stacks/.ecrc @@ -0,0 +1,38 @@ +# EditorConfig for Terraform repository + +root = true + +# Terraform files +[*.tf] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +# HCL files (for Terraform configurations) +[*.hcl] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +# Atmos configurations (if they exist as YAML or TOML) +[*.yml] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.toml] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 9ebb6641f..d43c8aa73 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -136,7 +136,21 @@ tests: stderr: - 'unknown command "non-existent" for "atmos"' exit_code: 1 - + - name: atmos validate editorconfig-check + enabled: true + description: "Ensure atmos CLI validates based on the .ecrc file." + workdir: "../examples/demo-stacks/" + command: "atmos" + args: + - "validate" + - "editorconfig-checker" + expect: + stdout: + - "EditorConfig Checker CLI Version: v3.0.0" + - "No errors found" + stderr: + - "^$" + exit_code: 0 - name: atmos describe config -f yaml enabled: true description: "Ensure atmos CLI outputs the Atmos configuration in YAML." From 167092ea3a39fb5413e1e935255ea0527ee17786 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Fri, 27 Dec 2024 14:28:11 +0100 Subject: [PATCH 03/32] Update logger to atmos logger --- cmd/editor_config.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cmd/editor_config.go b/cmd/editor_config.go index a87d61af4..646caffd4 100644 --- a/cmd/editor_config.go +++ b/cmd/editor_config.go @@ -9,7 +9,6 @@ import ( "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/config" er "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/error" "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/files" - "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/logger" "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/utils" "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/validation" "github.com/spf13/cobra" @@ -46,7 +45,7 @@ func initializeConfig() { var err error currentConfig, err = config.NewConfig(configFilePath) if err != nil { - logger.Error(err.Error()) + u.LogError(atmosConfig, err) os.Exit(1) } @@ -66,8 +65,8 @@ func runMainLogic() { u.LogTrace(atmosConfig, fmt.Sprintf("Exclude Regexp: %s", config.GetExcludesAsRegularExpression())) if utils.FileExists(config.Path) && config.Version != "" && config.Version != editorConfigVersion { - config.Logger.Error("Version from config file is not the same as the version of the binary") - config.Logger.Error("Binary: %s, Config: %s", editorConfigVersion, config.Version) + u.LogError(atmosConfig, fmt.Errorf("Version from config file is not the same as the version of the binary")) + u.LogError(atmosConfig, fmt.Errorf("Binary: %s, Config: %s", editorConfigVersion, config.Version)) os.Exit(1) } @@ -77,13 +76,13 @@ func runMainLogic() { filePaths, err := files.GetFiles(config) if err != nil { - config.Logger.Error(err.Error()) + u.LogError(atmosConfig, err) os.Exit(1) } if config.DryRun { for _, file := range filePaths { - config.Logger.Output(file) + u.LogInfo(atmosConfig, file) } os.Exit(0) } @@ -93,11 +92,11 @@ func runMainLogic() { if errorCount != 0 { er.PrintErrors(errors, config) - config.Logger.Error(fmt.Sprintf("\n%d errors found", errorCount)) + u.LogError(atmosConfig, fmt.Errorf("\n%d errors found", errorCount)) os.Exit(1) } - config.Logger.Verbose("%d files checked", len(filePaths)) + u.LogTrace(atmosConfig, fmt.Sprintf("%d files checked", len(filePaths))) u.LogInfo(atmosConfig, "No errors found") } From 3db160ddfd880059dea47e26711857f5493d2412 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Fri, 27 Dec 2024 14:38:27 +0100 Subject: [PATCH 04/32] refactored code --- cmd/editor_config.go | 23 ++++++++++++++++++----- tests/test_cases.yaml | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/cmd/editor_config.go b/cmd/editor_config.go index 646caffd4..07ff2be7a 100644 --- a/cmd/editor_config.go +++ b/cmd/editor_config.go @@ -15,7 +15,7 @@ import ( ) var ( - editorConfigVersion = "v3.0.0" + editorConfigVersion = "v3.0.3" defaultConfigFilePath = ".ecrc" currentConfig *config.Config cliConfig config.Config @@ -49,7 +49,10 @@ func initializeConfig() { os.Exit(1) } - _ = currentConfig.Parse() + if err := currentConfig.Parse(); err != nil { + u.LogError(atmosConfig, fmt.Errorf("failed to parse config: %w", err)) + os.Exit(1) + } if tmpExclude != "" { currentConfig.Exclude = append(currentConfig.Exclude, tmpExclude) @@ -64,9 +67,8 @@ func runMainLogic() { u.LogDebug(atmosConfig, config.GetAsString()) u.LogTrace(atmosConfig, fmt.Sprintf("Exclude Regexp: %s", config.GetExcludesAsRegularExpression())) - if utils.FileExists(config.Path) && config.Version != "" && config.Version != editorConfigVersion { - u.LogError(atmosConfig, fmt.Errorf("Version from config file is not the same as the version of the binary")) - u.LogError(atmosConfig, fmt.Errorf("Binary: %s, Config: %s", editorConfigVersion, config.Version)) + if err := checkVersion(config); err != nil { + u.LogError(atmosConfig, err) os.Exit(1) } @@ -100,6 +102,17 @@ func runMainLogic() { u.LogInfo(atmosConfig, "No errors found") } +func checkVersion(config config.Config) error { + if !utils.FileExists(config.Path) || config.Version == "" { + return nil + } + if config.Version != editorConfigVersion { + return fmt.Errorf("version mismatch: binary=%s, config=%s", + editorConfigVersion, config.Version) + } + return nil +} + // handleReturnableFlags handles early termination flags func handleReturnableFlags(config config.Config) bool { if config.ShowVersion { diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index d43c8aa73..5dfe49b5d 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -146,7 +146,7 @@ tests: - "editorconfig-checker" expect: stdout: - - "EditorConfig Checker CLI Version: v3.0.0" + - "EditorConfig Checker CLI Version: v3.0.3" - "No errors found" stderr: - "^$" From 80feea287ab161fd9056319badc97d858473bed6 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 28 Dec 2024 13:30:16 +0100 Subject: [PATCH 05/32] Temp atmos config fix to fix unit test --- cmd/editor_config.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/editor_config.go b/cmd/editor_config.go index 646caffd4..497626d9c 100644 --- a/cmd/editor_config.go +++ b/cmd/editor_config.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/cloudposse/atmos/pkg/schema" u "github.com/cloudposse/atmos/pkg/utils" "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/config" @@ -37,7 +38,7 @@ var editorConfigCmd *cobra.Command = &cobra.Command{ // initializeConfig breaks the initialization cycle by separating the config setup func initializeConfig() { - u.LogInfo(atmosConfig, fmt.Sprintf("EditorConfig Checker CLI Version: %s", editorConfigVersion)) + u.LogInfo(schema.AtmosConfiguration{}, fmt.Sprintf("EditorConfig Checker CLI Version: %s", editorConfigVersion)) if configFilePath == "" { configFilePath = defaultConfigFilePath } @@ -97,7 +98,7 @@ func runMainLogic() { } u.LogTrace(atmosConfig, fmt.Sprintf("%d files checked", len(filePaths))) - u.LogInfo(atmosConfig, "No errors found") + u.LogInfo(schema.AtmosConfiguration{}, "No errors found") } // handleReturnableFlags handles early termination flags From 6ae752b334df5fc701c3cba1a0473266585822a6 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 28 Dec 2024 15:19:13 +0100 Subject: [PATCH 06/32] Fix logging in editor_config --- cmd/editor_config.go | 22 ++++++++----------- .../.editorconfig} | 0 tests/test_cases.yaml | 4 ++-- 3 files changed, 11 insertions(+), 15 deletions(-) rename examples/{demo-stacks/.ecrc => quick-start-simple/.editorconfig} (100%) diff --git a/cmd/editor_config.go b/cmd/editor_config.go index 497626d9c..21d723de1 100644 --- a/cmd/editor_config.go +++ b/cmd/editor_config.go @@ -4,7 +4,6 @@ import ( "fmt" "os" - "github.com/cloudposse/atmos/pkg/schema" u "github.com/cloudposse/atmos/pkg/utils" "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/config" @@ -25,9 +24,9 @@ var ( ) var editorConfigCmd *cobra.Command = &cobra.Command{ - Use: "editorconfig-checker", - Short: "EditorConfig Checker CLI", - Long: "A command-line tool to check your files against the EditorConfig rules", + Use: "editorconfig", + Short: "Validate all files against the EditorConfig", + Long: "Validate all files against the project's EditorConfig rules", PersistentPreRun: func(cmd *cobra.Command, args []string) { initializeConfig() }, @@ -38,7 +37,7 @@ var editorConfigCmd *cobra.Command = &cobra.Command{ // initializeConfig breaks the initialization cycle by separating the config setup func initializeConfig() { - u.LogInfo(schema.AtmosConfiguration{}, fmt.Sprintf("EditorConfig Checker CLI Version: %s", editorConfigVersion)) + u.LogInfo(atmosConfig, fmt.Sprintf("EditorConfig Checker CLI Version: %s", editorConfigVersion)) if configFilePath == "" { configFilePath = defaultConfigFilePath } @@ -67,8 +66,7 @@ func runMainLogic() { if utils.FileExists(config.Path) && config.Version != "" && config.Version != editorConfigVersion { u.LogError(atmosConfig, fmt.Errorf("Version from config file is not the same as the version of the binary")) - u.LogError(atmosConfig, fmt.Errorf("Binary: %s, Config: %s", editorConfigVersion, config.Version)) - os.Exit(1) + u.LogErrorAndExit(atmosConfig, fmt.Errorf("Binary: %s, Config: %s", editorConfigVersion, config.Version)) } if handleReturnableFlags(config) { @@ -77,8 +75,7 @@ func runMainLogic() { filePaths, err := files.GetFiles(config) if err != nil { - u.LogError(atmosConfig, err) - os.Exit(1) + u.LogErrorAndExit(atmosConfig, err) } if config.DryRun { @@ -93,12 +90,11 @@ func runMainLogic() { if errorCount != 0 { er.PrintErrors(errors, config) - u.LogError(atmosConfig, fmt.Errorf("\n%d errors found", errorCount)) - os.Exit(1) + u.LogErrorAndExit(atmosConfig, fmt.Errorf("\n%d errors found", errorCount)) } - u.LogTrace(atmosConfig, fmt.Sprintf("%d files checked", len(filePaths))) - u.LogInfo(schema.AtmosConfiguration{}, "No errors found") + u.LogDebug(atmosConfig, fmt.Sprintf("%d files checked", len(filePaths))) + u.LogInfo(atmosConfig, "No errors found") } // handleReturnableFlags handles early termination flags diff --git a/examples/demo-stacks/.ecrc b/examples/quick-start-simple/.editorconfig similarity index 100% rename from examples/demo-stacks/.ecrc rename to examples/quick-start-simple/.editorconfig diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index d43c8aa73..3ccda5490 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -139,11 +139,11 @@ tests: - name: atmos validate editorconfig-check enabled: true description: "Ensure atmos CLI validates based on the .ecrc file." - workdir: "../examples/demo-stacks/" + workdir: "../examples/quick-start-simple/" command: "atmos" args: - "validate" - - "editorconfig-checker" + - "editorconfig" expect: stdout: - "EditorConfig Checker CLI Version: v3.0.0" From 97028ef825438830551cae2bad21632b51ccec8a Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 28 Dec 2024 21:44:36 +0100 Subject: [PATCH 07/32] added EditorConfig Checker in Atmos config --- cmd/editor_config.go | 58 +++++++++++++++++++++++++++++++++++++++++--- pkg/schema/schema.go | 32 ++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/cmd/editor_config.go b/cmd/editor_config.go index 57dd0f350..a0eedb881 100644 --- a/cmd/editor_config.go +++ b/cmd/editor_config.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/cloudposse/atmos/pkg/schema" u "github.com/cloudposse/atmos/pkg/utils" "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/config" @@ -32,12 +33,18 @@ var editorConfigCmd *cobra.Command = &cobra.Command{ initializeConfig() }, Run: func(cmd *cobra.Command, args []string) { + if cliConfig.Help { + cmd.Help() + os.Exit(0) + } runMainLogic() }, } // initializeConfig breaks the initialization cycle by separating the config setup func initializeConfig() { + replaceAtmosConfigInConfig(atmosConfig) + u.LogInfo(atmosConfig, fmt.Sprintf("EditorConfig Checker CLI Version: %s", editorConfigVersion)) if configFilePath == "" { configFilePath = defaultConfigFilePath @@ -47,7 +54,6 @@ func initializeConfig() { currentConfig, err = config.NewConfig(configFilePath) if err != nil { u.LogErrorAndExit(atmosConfig, err) - os.Exit(1) } if initEditorConfig { @@ -66,6 +72,51 @@ func initializeConfig() { currentConfig.Merge(cliConfig) } +func replaceAtmosConfigInConfig(atmosConfig schema.AtmosConfiguration) { + if atmosConfig.Validate.EditorConfig.ConfigFilePath != "" { + configFilePath = atmosConfig.Validate.EditorConfig.ConfigFilePath + } + if atmosConfig.Validate.EditorConfig.Exclude != "" { + tmpExclude = atmosConfig.Validate.EditorConfig.Exclude + } + if atmosConfig.Validate.EditorConfig.Init { + initEditorConfig = atmosConfig.Validate.EditorConfig.Init + } + if atmosConfig.Validate.EditorConfig.IgnoreDefaults { + cliConfig.IgnoreDefaults = atmosConfig.Validate.EditorConfig.IgnoreDefaults + } + if atmosConfig.Validate.EditorConfig.DryRun { + cliConfig.DryRun = atmosConfig.Validate.EditorConfig.DryRun + } + if atmosConfig.Validate.EditorConfig.Format != "" { + cliConfig.Format = atmosConfig.Validate.EditorConfig.Format + } + if atmosConfig.Logs.Level == "trace" { + cliConfig.Verbose = true + } + if atmosConfig.Validate.EditorConfig.NoColor { + cliConfig.NoColor = atmosConfig.Validate.EditorConfig.NoColor + } + if atmosConfig.Validate.EditorConfig.Disable.TrimTrailingWhitespace { + cliConfig.Disable.TrimTrailingWhitespace = atmosConfig.Validate.EditorConfig.Disable.TrimTrailingWhitespace + } + if atmosConfig.Validate.EditorConfig.Disable.EndOfLine { + cliConfig.Disable.EndOfLine = atmosConfig.Validate.EditorConfig.Disable.EndOfLine + } + if atmosConfig.Validate.EditorConfig.Disable.InsertFinalNewline { + cliConfig.Disable.InsertFinalNewline = atmosConfig.Validate.EditorConfig.Disable.InsertFinalNewline + } + if atmosConfig.Validate.EditorConfig.Disable.Indentation { + cliConfig.Disable.Indentation = atmosConfig.Validate.EditorConfig.Disable.Indentation + } + if atmosConfig.Validate.EditorConfig.Disable.IndentSize { + cliConfig.Disable.IndentSize = atmosConfig.Validate.EditorConfig.Disable.IndentSize + } + if atmosConfig.Validate.EditorConfig.Disable.MaxLineLength { + cliConfig.Disable.MaxLineLength = atmosConfig.Validate.EditorConfig.Disable.MaxLineLength + } +} + // runMainLogic contains the main logic func runMainLogic() { config := *currentConfig @@ -133,12 +184,12 @@ func handleReturnableFlags(config config.Config) bool { func addPersistentFlags(cmd *cobra.Command) { cmd.PersistentFlags().StringVar(&configFilePath, "config", "", "Path to the configuration file") cmd.PersistentFlags().StringVar(&tmpExclude, "exclude", "", "Regex to exclude files from checking") + cmd.PersistentFlags().BoolVar(&initEditorConfig, "init", false, "creates an initial configuration") + cmd.PersistentFlags().BoolVar(&cliConfig.IgnoreDefaults, "ignore-defaults", false, "Ignore default excludes") cmd.PersistentFlags().BoolVar(&cliConfig.DryRun, "dry-run", false, "Show which files would be checked") cmd.PersistentFlags().BoolVar(&cliConfig.ShowVersion, "version", false, "Print the version number") - cmd.PersistentFlags().BoolVar(&cliConfig.Help, "help", false, "Print help information") cmd.PersistentFlags().StringVar(&cliConfig.Format, "format", "default", "Specify the output format: default, gcc") - cmd.PersistentFlags().BoolVar(&cliConfig.Verbose, "verbose", false, "Print debugging information") cmd.PersistentFlags().BoolVar(&cliConfig.NoColor, "no-color", false, "Don't print colors") cmd.PersistentFlags().BoolVar(&cliConfig.Disable.TrimTrailingWhitespace, "disable-trim-trailing-whitespace", false, "Disable trailing whitespace check") cmd.PersistentFlags().BoolVar(&cliConfig.Disable.EndOfLine, "disable-end-of-line", false, "Disable end-of-line check") @@ -146,7 +197,6 @@ func addPersistentFlags(cmd *cobra.Command) { cmd.PersistentFlags().BoolVar(&cliConfig.Disable.Indentation, "disable-indentation", false, "Disable indentation check") cmd.PersistentFlags().BoolVar(&cliConfig.Disable.IndentSize, "disable-indent-size", false, "Disable indent size check") cmd.PersistentFlags().BoolVar(&cliConfig.Disable.MaxLineLength, "disable-max-line-length", false, "Disable max line length check") - cmd.PersistentFlags().BoolVar(&initEditorConfig, "init", false, "creates an initial configuration") } func init() { diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index 8108be1bd..55c8215d4 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -1,6 +1,8 @@ package schema -import "github.com/cloudposse/atmos/pkg/store" +import ( + "github.com/cloudposse/atmos/pkg/store" +) type AtmosSectionMapType = map[string]any @@ -30,13 +32,39 @@ type AtmosConfiguration struct { StackType string `yaml:"stackType,omitempty" json:"StackType,omitempty" mapstructure:"stackType"` Default bool `yaml:"default" json:"default" mapstructure:"default"` Version Version `yaml:"version,omitempty" json:"version,omitempty" mapstructure:"version"` - + Validate Validate `yaml:"validate,omitempty" json:"validate,omitempty" mapstructure:"validate"` // Stores is never read from yaml, it is populated in processStoreConfig and it's used to pass to the populated store // registry through to the yaml parsing functions when !store is run and to pass the registry to the hooks // functions to be able to call stores from within hooks. Stores store.StoreRegistry `yaml:"stores_registry,omitempty" json:"stores_registry,omitempty" mapstructure:"stores_registry"` } +type Validate struct { + EditorConfig EditorConfig `yaml:"editorconfig,omitempty" json:"editorconfig,omitempty" mapstructure:"editorconfig"` +} + +type EditorConfig struct { + IgnoreDefaults bool `yaml:"ignore_defaults,omitempty" json:"ignore_defaults,omitempty" mapstructure:"ignore_defaults"` + DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,omitempty" mapstructure:"dry_run"` + Format string `yaml:"format,omitempty" json:"format,omitempty" mapstructure:"format"` + NoColor bool `yaml:"no_color,omitempty" json:"no_color,omitempty" mapstructure:"no_color"` + Disable DisabledChecks `yaml:"disable,omitempty" json:"disable,omitempty" mapstructure:"disable"` + + ConfigFilePath string `yaml:"config_file_path,omitempty" json:"config_file_path,omitempty" mapstructure:"config_file_path"` + Exclude string `yaml:"exclude,omitempty" json:"exclude,omitempty" mapstructure:"exclude"` + Init bool `yaml:"init,omitempty" json:"init,omitempty" mapstructure:"init"` +} + +// DisabledChecks is a Struct which represents disabled checks +type DisabledChecks struct { + EndOfLine bool `yaml:"end_of_line,omitempty" json:"end_of_line,omitempty" mapstructure:"end_of_line"` + InsertFinalNewline bool `yaml:"insert_final_newline,omitempty" json:"insert_final_newline,omitempty" mapstructure:"insert_final_newline"` + Indentation bool `yaml:"indentation,omitempty" json:"indentation,omitempty" mapstructure:"indentation"` + IndentSize bool `yaml:"indent_size,omitempty" json:"indent_size,omitempty" mapstructure:"indent_size"` + MaxLineLength bool `yaml:"max_line_length,omitempty" json:"max_line_length,omitempty" mapstructure:"max_line_length"` + TrimTrailingWhitespace bool `yaml:"trim_trailing_whitespace,omitempty" json:"trim_trailing_whitespace,omitempty" mapstructure:"trim_trailing_whitespace"` +} + type AtmosSettings struct { ListMergeStrategy string `yaml:"list_merge_strategy" json:"list_merge_strategy" mapstructure:"list_merge_strategy"` Docs Docs `yaml:"docs,omitempty" json:"docs,omitempty" mapstructure:"docs"` From 6b86d1af0745fa71cdce9e1a27fb404ef1377d56 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 28 Dec 2024 22:23:44 +0100 Subject: [PATCH 08/32] Removed editor config checker version info --- cmd/editor_config.go | 26 ++++---------------------- examples/quick-start-simple/atmos.yaml | 1 - tests/test_cases.yaml | 1 - 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/cmd/editor_config.go b/cmd/editor_config.go index a0eedb881..aa07bb185 100644 --- a/cmd/editor_config.go +++ b/cmd/editor_config.go @@ -6,6 +6,7 @@ import ( "github.com/cloudposse/atmos/pkg/schema" u "github.com/cloudposse/atmos/pkg/utils" + "github.com/cloudposse/atmos/pkg/version" "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/config" er "github.com/editorconfig-checker/editorconfig-checker/v3/pkg/error" @@ -16,7 +17,6 @@ import ( ) var ( - editorConfigVersion = "v3.0.3" defaultConfigFilePath = ".editorconfig" initEditorConfig bool currentConfig *config.Config @@ -45,7 +45,6 @@ var editorConfigCmd *cobra.Command = &cobra.Command{ func initializeConfig() { replaceAtmosConfigInConfig(atmosConfig) - u.LogInfo(atmosConfig, fmt.Sprintf("EditorConfig Checker CLI Version: %s", editorConfigVersion)) if configFilePath == "" { configFilePath = defaultConfigFilePath } @@ -57,7 +56,7 @@ func initializeConfig() { } if initEditorConfig { - err := currentConfig.Save(editorConfigVersion) + err := currentConfig.Save(version.Version) if err != nil { u.LogErrorAndExit(atmosConfig, err) } @@ -127,10 +126,6 @@ func runMainLogic() { u.LogErrorAndExit(atmosConfig, err) } - if handleReturnableFlags(config) { - return - } - filePaths, err := files.GetFiles(config) if err != nil { u.LogErrorAndExit(atmosConfig, err) @@ -159,27 +154,14 @@ func checkVersion(config config.Config) error { if !utils.FileExists(config.Path) || config.Version == "" { return nil } - if config.Version != editorConfigVersion { + if config.Version != version.Version { return fmt.Errorf("version mismatch: binary=%s, config=%s", - editorConfigVersion, config.Version) + version.Version, config.Version) } return nil } -// handleReturnableFlags handles early termination flags -func handleReturnableFlags(config config.Config) bool { - if config.ShowVersion { - config.Logger.Output(editorConfigVersion) - return true - } - if config.Help { - config.Logger.Output("USAGE:") - return true - } - return false -} - // addPersistentFlags adds flags to the root command func addPersistentFlags(cmd *cobra.Command) { cmd.PersistentFlags().StringVar(&configFilePath, "config", "", "Path to the configuration file") diff --git a/examples/quick-start-simple/atmos.yaml b/examples/quick-start-simple/atmos.yaml index 555d07515..37ec5031e 100644 --- a/examples/quick-start-simple/atmos.yaml +++ b/examples/quick-start-simple/atmos.yaml @@ -17,5 +17,4 @@ stacks: name_pattern: "{stage}" logs: - file: "/dev/stderr" level: Info diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index a37d5f0e7..cda0c0004 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -146,7 +146,6 @@ tests: - "editorconfig" expect: stdout: - - "EditorConfig Checker CLI Version: v3.0.3" - "No errors found" stderr: - "^$" From 97ce514360c2634ad65b1f7ccdda9e8084fec1f5 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 28 Dec 2024 22:32:22 +0100 Subject: [PATCH 09/32] Added atmos.yaml schema help --- atmos.yaml | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/atmos.yaml b/atmos.yaml index e9793b62f..16ebeb3a9 100644 --- a/atmos.yaml +++ b/atmos.yaml @@ -91,6 +91,44 @@ logs: # Can also be set using 'ATMOS_LOGS_LEVEL' ENV var, or '--logs-level' command-line argument level: Info +validate: + # The configuration settings for the editorconfig-checker. + + editorconfig: + # A comma-separated list of file paths or patterns to exclude from checks. + exclude: "" + + # If set to true, the default ignore patterns (like .git/*) will not be applied. + ignore_defaults: false + + # Runs the checker without making any changes or producing output, useful for testing configuration. + dry_run: false + + # Specifies the output format. Options: "default", "json". + format: "default" + + # Disables colored output in the terminal. + no_color: false + + disable: + # Disables checking for trailing whitespace at the end of lines. + trim_trailing_whitespace: false + + # Disables checking for consistent line endings (e.g., LF vs. CRLF). + end_of_line: false + + # Disables checking for the presence of a newline at the end of files. + insert_final_newline: false + + # Disables checking for consistent indentation style (e.g., tabs or spaces). + indentation: false + + # Disables checking for consistent indentation size (e.g., 2 spaces or 4 spaces). + indent_size: false + + # Disables checking for lines exceeding a maximum length. + max_line_length: false + # Custom CLI commands commands: - name: tf From 2a322b36edfc30c7ffc5d1d9769d6b9798fd3b39 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 28 Dec 2024 22:38:27 +0100 Subject: [PATCH 10/32] Update tests/test_cases.yaml Co-authored-by: Erik Osterman (CEO @ Cloud Posse) --- tests/test_cases.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index cda0c0004..aa09bb99e 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -136,7 +136,7 @@ tests: stderr: - 'unknown command "non-existent" for "atmos"' exit_code: 1 - - name: atmos validate editorconfig-check + - name: atmos validate editorconfig enabled: true description: "Ensure atmos CLI validates based on the .ecrc file." workdir: "../examples/quick-start-simple/" From 63d44fb2788e129f34d759be3bbe2e1e7e16ff1f Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sun, 29 Dec 2024 16:38:29 +0100 Subject: [PATCH 11/32] try to fix windows workflow --- .github/workflows/test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 04bad3a7d..f4cb93adb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -86,6 +86,11 @@ jobs: timeout-minutes: 15 runs-on: ${{ matrix.flavor.os }} steps: + - name: Set Git Preferences for windows + if: matrix.flavor.target == 'windows' + run: | + git config --global core.autocrlf false + git config --global core.eol lf - name: Check out code into the Go module directory uses: actions/checkout@v4 From df45545af8c553ef0420ff471f8a1084681ca835 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Mon, 30 Dec 2024 23:40:15 +0100 Subject: [PATCH 12/32] Add reason for git config before checkout step --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f4cb93adb..ca48554b2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -86,6 +86,9 @@ jobs: timeout-minutes: 15 runs-on: ${{ matrix.flavor.os }} steps: + # Configures Git to ensure LF line endings on Windows to pass the `atmos validate editorconfig` check. + # The check expects LF endings, but on Windows, files might default to CRLF due to core.autocrlf. + # Since the validation runs in CI, we enforce consistent settings across all OS environments. - name: Set Git Preferences for windows if: matrix.flavor.target == 'windows' run: | From 64cfd52556dac5f1ed750ee1d2e551cbfca24b5d Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 4 Jan 2025 22:34:59 +0100 Subject: [PATCH 13/32] Change to print message based on existing working --- cmd/editor_config.go | 2 +- examples/quick-start-simple/atmos.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/editor_config.go b/cmd/editor_config.go index aa07bb185..6b0be0560 100644 --- a/cmd/editor_config.go +++ b/cmd/editor_config.go @@ -147,7 +147,7 @@ func runMainLogic() { } u.LogDebug(atmosConfig, fmt.Sprintf("%d files checked", len(filePaths))) - u.LogInfo(atmosConfig, "No errors found") + u.PrintMessage("No errors found") } func checkVersion(config config.Config) error { diff --git a/examples/quick-start-simple/atmos.yaml b/examples/quick-start-simple/atmos.yaml index 37ec5031e..5456297a0 100644 --- a/examples/quick-start-simple/atmos.yaml +++ b/examples/quick-start-simple/atmos.yaml @@ -18,3 +18,4 @@ stacks: logs: level: Info + file: "/dev/stderr" From e206e49da6b466c60e1acf1058f68d2ebab26041 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 4 Jan 2025 22:56:50 +0100 Subject: [PATCH 14/32] Added negative test case for editorconfig --- examples/quick-start-advanced/.editorconfig | 38 +++++++++++++++++++++ tests/test_cases.yaml | 15 +++++++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 examples/quick-start-advanced/.editorconfig diff --git a/examples/quick-start-advanced/.editorconfig b/examples/quick-start-advanced/.editorconfig new file mode 100644 index 000000000..836760da3 --- /dev/null +++ b/examples/quick-start-advanced/.editorconfig @@ -0,0 +1,38 @@ +# EditorConfig for Terraform repository + +root = true + +# Terraform files +[*.tf] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +# HCL files (for Terraform configurations) +[*.hcl] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +# Atmos configurations (if they exist as YAML or TOML) +[*.yml] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.toml] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index aa09bb99e..90b85bed7 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -138,7 +138,7 @@ tests: exit_code: 1 - name: atmos validate editorconfig enabled: true - description: "Ensure atmos CLI validates based on the .ecrc file." + description: "Ensure atmos CLI validates success case" workdir: "../examples/quick-start-simple/" command: "atmos" args: @@ -150,6 +150,19 @@ tests: stderr: - "^$" exit_code: 0 + - name: atmos validate editorconfig error + enabled: true + description: "Ensure atmos CLI gives error during validation based on editorconfig" + workdir: "../examples/quick-start-advanced/" + command: "atmos" + args: + - "validate" + - "editorconfig" + expect: + stderr: + - "4 errors found" + exit_code: 1 + - name: atmos describe config -f yaml enabled: true description: "Ensure atmos CLI outputs the Atmos configuration in YAML." From b1480a80a65a796afaa23c3650eb705dbdfccc15 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sun, 5 Jan 2025 21:45:11 +0100 Subject: [PATCH 15/32] changed negative testcase --- examples/{quick-start-advanced => tests}/.editorconfig | 0 tests/test_cases.yaml | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename examples/{quick-start-advanced => tests}/.editorconfig (100%) diff --git a/examples/quick-start-advanced/.editorconfig b/examples/tests/.editorconfig similarity index 100% rename from examples/quick-start-advanced/.editorconfig rename to examples/tests/.editorconfig diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 90b85bed7..dc693f910 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -153,14 +153,14 @@ tests: - name: atmos validate editorconfig error enabled: true description: "Ensure atmos CLI gives error during validation based on editorconfig" - workdir: "../examples/quick-start-advanced/" + workdir: "../examples/tests/" command: "atmos" args: - "validate" - "editorconfig" expect: stderr: - - "4 errors found" + - "18 errors found" exit_code: 1 - name: atmos describe config -f yaml From fd306d15cc239dc8d6eb88ab09972be49db8b358 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Tue, 7 Jan 2025 06:40:57 +0100 Subject: [PATCH 16/32] update editorconfig schema --- atmos.yaml | 33 ++++++++++++++-------------- cmd/editor_config.go | 51 ++++++++++++++++++++++---------------------- pkg/config/config.go | 3 ++- pkg/schema/schema.go | 33 ++++++++++++---------------- 4 files changed, 58 insertions(+), 62 deletions(-) diff --git a/atmos.yaml b/atmos.yaml index 38576c13d..02ff006c9 100644 --- a/atmos.yaml +++ b/atmos.yaml @@ -95,8 +95,8 @@ validate: # The configuration settings for the editorconfig-checker. editorconfig: - # A comma-separated list of file paths or patterns to exclude from checks. - exclude: "" + # A list of file paths or patterns to exclude from checks. + exclude: [] # If set to true, the default ignore patterns (like .git/*) will not be applied. ignore_defaults: false @@ -107,27 +107,26 @@ validate: # Specifies the output format. Options: "default", "json". format: "default" - # Disables colored output in the terminal. - no_color: false + # Enables/Disables colored output in the terminal if . + color: true - disable: - # Disables checking for trailing whitespace at the end of lines. - trim_trailing_whitespace: false + # Disables checking for trailing whitespace at the end of lines. + disable_trim_trailing_whitespace: false - # Disables checking for consistent line endings (e.g., LF vs. CRLF). - end_of_line: false + # Disables checking for consistent line endings (e.g., LF vs. CRLF). + disable_end_of_line: false - # Disables checking for the presence of a newline at the end of files. - insert_final_newline: false + # Disables checking for the presence of a newline at the end of files. + disable_insert_final_newline: false - # Disables checking for consistent indentation style (e.g., tabs or spaces). - indentation: false + # Disables checking for consistent indentation style (e.g., tabs or spaces). + disable_indentation: false - # Disables checking for consistent indentation size (e.g., 2 spaces or 4 spaces). - indent_size: false + # Disables checking for consistent indentation size (e.g., 2 spaces or 4 spaces). + disable_indent_size: false - # Disables checking for lines exceeding a maximum length. - max_line_length: false + # Disables checking for lines exceeding a maximum length. + disable_max_line_length: false # Custom CLI commands commands: diff --git a/cmd/editor_config.go b/cmd/editor_config.go index 6b0be0560..cfe308616 100644 --- a/cmd/editor_config.go +++ b/cmd/editor_config.go @@ -3,6 +3,7 @@ package cmd import ( "fmt" "os" + "strings" "github.com/cloudposse/atmos/pkg/schema" u "github.com/cloudposse/atmos/pkg/utils" @@ -30,7 +31,7 @@ var editorConfigCmd *cobra.Command = &cobra.Command{ Short: "Validate all files against the EditorConfig", Long: "Validate all files against the project's EditorConfig rules", PersistentPreRun: func(cmd *cobra.Command, args []string) { - initializeConfig() + initializeConfig(cmd) }, Run: func(cmd *cobra.Command, args []string) { if cliConfig.Help { @@ -42,8 +43,8 @@ var editorConfigCmd *cobra.Command = &cobra.Command{ } // initializeConfig breaks the initialization cycle by separating the config setup -func initializeConfig() { - replaceAtmosConfigInConfig(atmosConfig) +func initializeConfig(cmd *cobra.Command) { + replaceAtmosConfigInConfig(cmd, atmosConfig) if configFilePath == "" { configFilePath = defaultConfigFilePath @@ -71,48 +72,48 @@ func initializeConfig() { currentConfig.Merge(cliConfig) } -func replaceAtmosConfigInConfig(atmosConfig schema.AtmosConfiguration) { - if atmosConfig.Validate.EditorConfig.ConfigFilePath != "" { +func replaceAtmosConfigInConfig(cmd *cobra.Command, atmosConfig schema.AtmosConfiguration) { + if !cmd.Flags().Changed("config") && atmosConfig.Validate.EditorConfig.ConfigFilePath != "" { configFilePath = atmosConfig.Validate.EditorConfig.ConfigFilePath } - if atmosConfig.Validate.EditorConfig.Exclude != "" { - tmpExclude = atmosConfig.Validate.EditorConfig.Exclude + if !cmd.Flags().Changed("exclude") && len(atmosConfig.Validate.EditorConfig.Exclude) > 0 { + tmpExclude = strings.Join(atmosConfig.Validate.EditorConfig.Exclude, ",") } - if atmosConfig.Validate.EditorConfig.Init { + if !cmd.Flags().Changed("init") && atmosConfig.Validate.EditorConfig.Init { initEditorConfig = atmosConfig.Validate.EditorConfig.Init } - if atmosConfig.Validate.EditorConfig.IgnoreDefaults { + if !cmd.Flags().Changed("ignore-defaults") && atmosConfig.Validate.EditorConfig.IgnoreDefaults { cliConfig.IgnoreDefaults = atmosConfig.Validate.EditorConfig.IgnoreDefaults } - if atmosConfig.Validate.EditorConfig.DryRun { + if !cmd.Flags().Changed("dry-run") && atmosConfig.Validate.EditorConfig.DryRun { cliConfig.DryRun = atmosConfig.Validate.EditorConfig.DryRun } - if atmosConfig.Validate.EditorConfig.Format != "" { + if !cmd.Flags().Changed("format") && atmosConfig.Validate.EditorConfig.Format != "" { cliConfig.Format = atmosConfig.Validate.EditorConfig.Format } if atmosConfig.Logs.Level == "trace" { cliConfig.Verbose = true } - if atmosConfig.Validate.EditorConfig.NoColor { - cliConfig.NoColor = atmosConfig.Validate.EditorConfig.NoColor + if !cmd.Flags().Changed("no-color") && !atmosConfig.Validate.EditorConfig.Color { + cliConfig.NoColor = !atmosConfig.Validate.EditorConfig.Color } - if atmosConfig.Validate.EditorConfig.Disable.TrimTrailingWhitespace { - cliConfig.Disable.TrimTrailingWhitespace = atmosConfig.Validate.EditorConfig.Disable.TrimTrailingWhitespace + if !cmd.Flags().Changed("disable-trim-trailing-whitespace") && atmosConfig.Validate.EditorConfig.DisableTrimTrailingWhitespace { + cliConfig.Disable.TrimTrailingWhitespace = atmosConfig.Validate.EditorConfig.DisableTrimTrailingWhitespace } - if atmosConfig.Validate.EditorConfig.Disable.EndOfLine { - cliConfig.Disable.EndOfLine = atmosConfig.Validate.EditorConfig.Disable.EndOfLine + if !cmd.Flags().Changed("disable-end-of-line") && atmosConfig.Validate.EditorConfig.DisableEndOfLine { + cliConfig.Disable.EndOfLine = atmosConfig.Validate.EditorConfig.DisableEndOfLine } - if atmosConfig.Validate.EditorConfig.Disable.InsertFinalNewline { - cliConfig.Disable.InsertFinalNewline = atmosConfig.Validate.EditorConfig.Disable.InsertFinalNewline + if !cmd.Flags().Changed("disable-insert-final-newline") && atmosConfig.Validate.EditorConfig.DisableInsertFinalNewline { + cliConfig.Disable.InsertFinalNewline = atmosConfig.Validate.EditorConfig.DisableInsertFinalNewline } - if atmosConfig.Validate.EditorConfig.Disable.Indentation { - cliConfig.Disable.Indentation = atmosConfig.Validate.EditorConfig.Disable.Indentation + if !cmd.Flags().Changed("disable-indentation") && atmosConfig.Validate.EditorConfig.DisableIndentation { + cliConfig.Disable.Indentation = atmosConfig.Validate.EditorConfig.DisableIndentation } - if atmosConfig.Validate.EditorConfig.Disable.IndentSize { - cliConfig.Disable.IndentSize = atmosConfig.Validate.EditorConfig.Disable.IndentSize + if !cmd.Flags().Changed("disable-indent-size") && atmosConfig.Validate.EditorConfig.DisableIndentSize { + cliConfig.Disable.IndentSize = atmosConfig.Validate.EditorConfig.DisableIndentSize } - if atmosConfig.Validate.EditorConfig.Disable.MaxLineLength { - cliConfig.Disable.MaxLineLength = atmosConfig.Validate.EditorConfig.Disable.MaxLineLength + if !cmd.Flags().Changed("disable-max-line-length") && atmosConfig.Validate.EditorConfig.DisableMaxLineLength { + cliConfig.Disable.MaxLineLength = atmosConfig.Validate.EditorConfig.DisableMaxLineLength } } diff --git a/pkg/config/config.go b/pkg/config/config.go index df60ff8d5..592751a9e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -226,7 +226,8 @@ func InitCliConfig(configAndStacksInfo schema.ConfigAndStacksInfo, processStacks return atmosConfig, err } } - + // We want the editorconfig color by default to be true + atmosConfig.Validate.EditorConfig.Color = true // https://gist.github.com/chazcheadle/45bf85b793dea2b71bd05ebaa3c28644 // https://sagikazarmark.hu/blog/decoding-custom-formats-with-viper/ err = v.Unmarshal(&atmosConfig) diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index a5090a25c..16bd7e43b 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -44,25 +44,20 @@ type Validate struct { } type EditorConfig struct { - IgnoreDefaults bool `yaml:"ignore_defaults,omitempty" json:"ignore_defaults,omitempty" mapstructure:"ignore_defaults"` - DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,omitempty" mapstructure:"dry_run"` - Format string `yaml:"format,omitempty" json:"format,omitempty" mapstructure:"format"` - NoColor bool `yaml:"no_color,omitempty" json:"no_color,omitempty" mapstructure:"no_color"` - Disable DisabledChecks `yaml:"disable,omitempty" json:"disable,omitempty" mapstructure:"disable"` - - ConfigFilePath string `yaml:"config_file_path,omitempty" json:"config_file_path,omitempty" mapstructure:"config_file_path"` - Exclude string `yaml:"exclude,omitempty" json:"exclude,omitempty" mapstructure:"exclude"` - Init bool `yaml:"init,omitempty" json:"init,omitempty" mapstructure:"init"` -} - -// DisabledChecks is a Struct which represents disabled checks -type DisabledChecks struct { - EndOfLine bool `yaml:"end_of_line,omitempty" json:"end_of_line,omitempty" mapstructure:"end_of_line"` - InsertFinalNewline bool `yaml:"insert_final_newline,omitempty" json:"insert_final_newline,omitempty" mapstructure:"insert_final_newline"` - Indentation bool `yaml:"indentation,omitempty" json:"indentation,omitempty" mapstructure:"indentation"` - IndentSize bool `yaml:"indent_size,omitempty" json:"indent_size,omitempty" mapstructure:"indent_size"` - MaxLineLength bool `yaml:"max_line_length,omitempty" json:"max_line_length,omitempty" mapstructure:"max_line_length"` - TrimTrailingWhitespace bool `yaml:"trim_trailing_whitespace,omitempty" json:"trim_trailing_whitespace,omitempty" mapstructure:"trim_trailing_whitespace"` + IgnoreDefaults bool `yaml:"ignore_defaults,omitempty" json:"ignore_defaults,omitempty" mapstructure:"ignore_defaults"` + DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,omitempty" mapstructure:"dry_run"` + Format string `yaml:"format,omitempty" json:"format,omitempty" mapstructure:"format"` + Color bool `yaml:"color,omitempty" json:"color,omitempty" mapstructure:"color"` + ConfigFilePath string `yaml:"config_file_path,omitempty" json:"config_file_path,omitempty" mapstructure:"config_file_path"` + Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty" mapstructure:"exclude"` + Init bool `yaml:"init,omitempty" json:"init,omitempty" mapstructure:"init"` + + DisableEndOfLine bool `yaml:"disable_end_of_line,omitempty" json:"disable_end_of_line,omitempty" mapstructure:"disable_end_of_line"` + DisableInsertFinalNewline bool `yaml:"disable_insert_final_newline,omitempty" json:"disable_insert_final_newline,omitempty" mapstructure:"disable_insert_final_newline"` + DisableIndentation bool `yaml:"disable_indentation,omitempty" json:"disable_indentation,omitempty" mapstructure:"disable_indentation"` + DisableIndentSize bool `yaml:"disable_indent_size,omitempty" json:"disable_indent_size,omitempty" mapstructure:"disable_indent_size"` + DisableMaxLineLength bool `yaml:"disable_max_line_length,omitempty" json:"disable_max_line_length,omitempty" mapstructure:"disable_max_line_length"` + DisableTrimTrailingWhitespace bool `yaml:"disable_trim_trailing_whitespace,omitempty" json:"disable_trim_trailing_whitespace,omitempty" mapstructure:"disable_trim_trailing_whitespace"` } type Terminal struct { From e05c0331f39af1f2e7bfd9ab6cead5b211bfbf89 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Tue, 7 Jan 2025 20:34:49 +0100 Subject: [PATCH 17/32] Update the test cases based on new fixtures --- {examples/tests => tests/fixtures}/.editorconfig | 0 tests/test_cases.yaml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename {examples/tests => tests/fixtures}/.editorconfig (100%) diff --git a/examples/tests/.editorconfig b/tests/fixtures/.editorconfig similarity index 100% rename from examples/tests/.editorconfig rename to tests/fixtures/.editorconfig diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 3bac9c96e..4a57949f2 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -153,7 +153,7 @@ tests: - name: atmos validate editorconfig error enabled: true description: "Ensure atmos CLI gives error during validation based on editorconfig" - workdir: "../examples/tests/" + workdir: "../tests/fixtures" command: "atmos" args: - "validate" From 29d7eb6d9754cce237ee371d254f29a2c214d9b0 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Tue, 7 Jan 2025 21:34:42 +0100 Subject: [PATCH 18/32] fix verbose log level --- cmd/editor_config.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/editor_config.go b/cmd/editor_config.go index cfe308616..adff7b4eb 100644 --- a/cmd/editor_config.go +++ b/cmd/editor_config.go @@ -91,8 +91,12 @@ func replaceAtmosConfigInConfig(cmd *cobra.Command, atmosConfig schema.AtmosConf if !cmd.Flags().Changed("format") && atmosConfig.Validate.EditorConfig.Format != "" { cliConfig.Format = atmosConfig.Validate.EditorConfig.Format } - if atmosConfig.Logs.Level == "trace" { + if !cmd.Flags().Changed("logs-level") && atmosConfig.Logs.Level == "trace" { cliConfig.Verbose = true + } else if cmd.Flags().Changed("logs-level") { + if v, err := cmd.Flags().GetString("logs-level"); err == nil && v == "trace" { + cliConfig.Verbose = true + } } if !cmd.Flags().Changed("no-color") && !atmosConfig.Validate.EditorConfig.Color { cliConfig.NoColor = !atmosConfig.Validate.EditorConfig.Color From e4a3738d6c19d3a1000380b729f7f895844bdefe Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Tue, 7 Jan 2025 21:35:12 +0100 Subject: [PATCH 19/32] Add editor config documentation --- .../core-concepts/validate/editorconfig.mdx | 70 +++++++++++++++++++ .../docs/core-concepts/validate/validate.mdx | 62 ++++++++-------- 2 files changed, 103 insertions(+), 29 deletions(-) create mode 100644 website/docs/core-concepts/validate/editorconfig.mdx diff --git a/website/docs/core-concepts/validate/editorconfig.mdx b/website/docs/core-concepts/validate/editorconfig.mdx new file mode 100644 index 000000000..e3c8378ba --- /dev/null +++ b/website/docs/core-concepts/validate/editorconfig.mdx @@ -0,0 +1,70 @@ +--- +title: EditorConfig Validation +sidebar_position: 2 +sidebar_label: EditorConfig +description: Use EditorConfig Checker to validate your configurations. +id: editorconfig-validation +--- +import Terminal from '@site/src/components/Terminal' +import File from '@site/src/components/File' +import Intro from '@site/src/components/Intro' + + +Atmos now supports validation of your configurations using the [EditorConfig Checker](https://github.com/editorconfig-checker/editorconfig-checker). This tool ensures that your files comply with the rules defined in your `.editorconfig` file, promoting consistency across your project. + + +## Example + + +```shell +# Validate all files in the current project using EditorConfig +atmos validate editorconfig +``` + + +### Configuration + +To use the `atmos validate editorconfig` command, ensure that your project contains a properly configured `.editorconfig` file at the root level or in relevant directories. This file defines the coding styles for the project, such as indentation, line endings, and character encodings. + + +```ini +# EditorConfig is awesome: https://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false +``` + + + +### Output + +The `atmos validate editorconfig` command will provide detailed output indicating whether the files comply with the `.editorconfig` rules or if there are any violations. For example: + + +```console +scenarios/complete/modules/label/context.tf: + 267: Wrong amount of left-padding spaces(want multiple of 2) + 268: Wrong amount of left-padding spaces(want multiple of 2) + +2 errors found +``` + + +### Troubleshooting + +If validation fails, review your `.editorconfig` file and ensure the rules align with your project's requirements. You can also run the command with verbose output for more details: + + +```shell +atmos validate editorconfig --logs-level trace +``` + diff --git a/website/docs/core-concepts/validate/validate.mdx b/website/docs/core-concepts/validate/validate.mdx index 3aac5c71b..345c11ec4 100644 --- a/website/docs/core-concepts/validate/validate.mdx +++ b/website/docs/core-concepts/validate/validate.mdx @@ -2,7 +2,7 @@ title: Validating Stack Configurations sidebar_position: 5 sidebar_label: Validate Configurations -description: Use JSON Schema and OPA policies to validate Components. +description: Use JSON Schema, OPA policies, and EditorConfig Checker to validate Components. id: validating --- import Terminal from '@site/src/components/Terminal' @@ -11,12 +11,12 @@ import Intro from '@site/src/components/Intro' Validation is essential for ensuring clean and correct configurations, especially in environments where multiple teams contribute -to the development and deployment processes. Atmos enhances this validation process in two significant ways with [JSON Schema](https://json-schema.org/) and [OPA](https://www.openpolicyagent.org/) policies. +to the development and deployment processes. Atmos enhances this validation process in three significant ways with [JSON Schema](https://json-schema.org/), [OPA](https://www.openpolicyagent.org/) policies, and the [EditorConfig Checker](https://github.com/editorconfig-checker/editorconfig-checker). ## Types of Validation -Atmos supports two types of native validation. +Atmos supports three types of native validation. ### JSON Schema @@ -26,10 +26,14 @@ JSON Schema is an industry standard and provides a vocabulary to annotate and va ### Open Policy Agent (OPA) The [Open Policy Agent](https://www.openpolicyagent.org/docs/latest/) (OPA, pronounced “oh-pa”) is another open-source industry standard that provides -a general-purpose policy engine to unify policy enforcement across your stacks. The OPA language (Rego) is a high-level declarative language for specifying policy as code. Atmos has native support for the OPA decision-making engine to enforce policies across all the components in your stacks (e.g. for microservice configurations). +a general-purpose policy engine to unify policy enforcement across your stacks. The OPA language (Rego) is a high-level declarative language for specifying policy as code. Atmos has native support for the OPA decision-making engine to enforce policies across all the components in your stacks (e.g., for microservice configurations). This is powerful stuff: because you can define many policies, it's possible to validate components differently for different environments or teams. +### EditorConfig Checker + +The [EditorConfig Checker](https://github.com/editorconfig-checker/editorconfig-checker) is a tool that ensures adherence to the rules defined in your `.editorconfig` file. This ensures consistency in coding styles across teams, which is particularly important in collaborative environments. Atmos supports running the EditorConfig Checker to validate the configurations in your project. + ## Validate Your Configurations :::tip @@ -55,41 +59,41 @@ The command checks and validates the following: - Schema: if all sections in all YAML manifest files are correctly configured and have valid data types - Misconfiguration and duplication of components in stacks. If the same Atmos component in the same Atmos stack is - defined in more than one stack manifest file, and the component configurations are different, an error message will - be displayed similar to the following: +defined in more than one stack manifest file, and the component configurations are different, an error message will +be displayed similar to the following: - - ```console - The Atmos component 'vpc' in the stack 'plat-ue2-dev' is defined in more than one - top-level stack manifest file: orgs/acme/plat/dev/us-east-2-extras, orgs/acme/plat/dev/us-east-2. + +```console +The Atmos component 'vpc' in the stack 'plat-ue2-dev' is defined in more than one +top-level stack manifest file: orgs/acme/plat/dev/us-east-2-extras, orgs/acme/plat/dev/us-east-2. - The component configurations in the stack manifest are different. +The component configurations in the stack manifest are different. - To check and compare the component configurations in the stack manifests, run the following commands: - - atmos describe component vpc -s orgs/acme/plat/dev/us-east-2-extras - - atmos describe component vpc -s orgs/acme/plat/dev/us-east-2 +To check and compare the component configurations in the stack manifests, run the following commands: +- atmos describe component vpc -s orgs/acme/plat/dev/us-east-2-extras +- atmos describe component vpc -s orgs/acme/plat/dev/us-east-2 - You can use the '--file' flag to write the results of the above commands to files - (refer to https://atmos.tools/cli/commands/describe/component). +You can use the '--file' flag to write the results of the above commands to files +(refer to https://atmos.tools/cli/commands/describe/component). - You can then use the Linux 'diff' command to compare the files line by line and show the differences - (refer to https://man7.org/linux/man-pages/man1/diff.1.html) +You can then use the Linux 'diff' command to compare the files line by line and show the differences +(refer to https://man7.org/linux/man-pages/man1/diff.1.html) - When searching for the component 'vpc' in the stack 'plat-ue2-dev', Atmos can't decide which - stack manifest file to use to get the configuration for the component. This is a stack misconfiguration. +When searching for the component 'vpc' in the stack 'plat-ue2-dev', Atmos can't decide which +stack manifest file to use to get the configuration for the component. This is a stack misconfiguration. - Consider the following solutions to fix the issue: +Consider the following solutions to fix the issue: - - Ensure that the same instance of the Atmos 'vpc' component in the stack 'plat-ue2-dev' - is only defined once (in one YAML stack manifest file) +- Ensure that the same instance of the Atmos 'vpc' component in the stack 'plat-ue2-dev' + is only defined once (in one YAML stack manifest file) - - When defining multiple instances of the same component in the stack, - ensure each has a unique name +- When defining multiple instances of the same component in the stack, + ensure each has a unique name - - Use multiple-inheritance to combine multiple configurations together - (refer to https://atmos.tools/core-concepts/stacks/inheritance) - ``` - +- Use multiple-inheritance to combine multiple configurations together + (refer to https://atmos.tools/core-concepts/stacks/inheritance) +``` + :::tip For more details, refer to [`atmos validate stacks`](/cli/commands/validate/stacks) CLI command From 9da0d5d7fbf1440315efe9946e2d42c57f0f340f Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Thu, 9 Jan 2025 21:35:46 +0100 Subject: [PATCH 20/32] Update website/docs/core-concepts/validate/editorconfig.mdx Co-authored-by: Erik Osterman (CEO @ Cloud Posse) --- website/docs/core-concepts/validate/editorconfig.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/core-concepts/validate/editorconfig.mdx b/website/docs/core-concepts/validate/editorconfig.mdx index e3c8378ba..6cc375641 100644 --- a/website/docs/core-concepts/validate/editorconfig.mdx +++ b/website/docs/core-concepts/validate/editorconfig.mdx @@ -10,7 +10,7 @@ import File from '@site/src/components/File' import Intro from '@site/src/components/Intro' -Atmos now supports validation of your configurations using the [EditorConfig Checker](https://github.com/editorconfig-checker/editorconfig-checker). This tool ensures that your files comply with the rules defined in your `.editorconfig` file, promoting consistency across your project. +Atmos supports validation of EditorConfigs to check the formatting of your configuration files. By enforcing the canonical rules specified in your `.editorconfig` file, it helps ensure consistent formatting across your project. ## Example From c29f04fd3e34a3046d99cbf57dcd480635a0927e Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 11 Jan 2025 02:22:45 +0100 Subject: [PATCH 21/32] Added editorconfig help page --- .../validate/validate-editorconfig.mdx | 57 +++ .../atmos-validate-editorconfig--help.html | 389 ++++++++++++++++++ 2 files changed, 446 insertions(+) create mode 100644 website/docs/cli/commands/validate/validate-editorconfig.mdx create mode 100644 website/src/components/Screengrabs/atmos-validate-editorconfig--help.html diff --git a/website/docs/cli/commands/validate/validate-editorconfig.mdx b/website/docs/cli/commands/validate/validate-editorconfig.mdx new file mode 100644 index 000000000..475d75d0f --- /dev/null +++ b/website/docs/cli/commands/validate/validate-editorconfig.mdx @@ -0,0 +1,57 @@ +--- +title: atmos validate editorconfig +sidebar_label: editorconfig +sidebar_class_name: command +id: editorconfig +description: Use this command to validate an Atmos component in a stack using JSON Schema and OPA policies. +--- +import Screengrab from '@site/src/components/Screengrab' + +:::note purpose +Use this command to validate an Atmos component in a stack using JSON Schema and OPA policies. +::: + + + +## Usage + +Execute the `validate editorconfig` command like this: + +```shell +atmos validate editorconfig +``` + +This command validates an Atmos component in a stack using JSON Schema and OPA policies. + +:::tip +Run `atmos validate editorconfig --help` to see all the available options +::: + +## Examples + +```shell +atmos validate editorconfig +atmos validate editorconfig --logs-level trace +atmos validate editorconfig --no-color +atmos validate editorconfig --dry-run +``` + +## Flags + +| Flag | Description | Alias | Required | +|:---------------------------------------|:----------------------------------------------------------------------|:------|:---------| +|`--config string` |Path to the configuration file | |no | +|`--disable-end-of-line` |Disable end-of-line check (default "false") | |no | +|`--disable-indent-size` |Disable indent size check (default "false") | |no | +|`--disable-indentation` |Disable indentation check (default "false")||no| +|`--disable-insert-final-newline` |Disable final newline check (default "false")||no| +|`--disable-max-line-length` |Disable max line length check (default "false")||no| +|`--disable-trim-trailing-whitespace` |Disable trailing whitespace check (default "false")||no| +|`--dry-run` |Show which files would be checked (default "false")||no| +|`--exclude string` |Regex to exclude files from checking||no| +|`--format string` |Specify the output format: default, gcc (default "default")||no| +|`--help` |help for editorconfig (default "false")||no| +|`--ignore-defaults` |Ignore default excludes (default "false")||no| +|`--init` |creates an initial configuration (default "false")||no| +|`--no-color` |Don't print colors (default "false")||no| +|`--version` |Print the version number (default "false")||no| \ No newline at end of file diff --git a/website/src/components/Screengrabs/atmos-validate-editorconfig--help.html b/website/src/components/Screengrabs/atmos-validate-editorconfig--help.html new file mode 100644 index 000000000..1274b2987 --- /dev/null +++ b/website/src/components/Screengrabs/atmos-validate-editorconfig--help.html @@ -0,0 +1,389 @@ + + + + + + terminal-to-html Preview + + + +
  +  +Usage: +  + atmos validate editorconfig [flags] +  +  +Flags: +  + --config string Path to the configuration file +  + --disable-end-of-line Disable end-of-line check (default + "false") +  + --disable-indent-size Disable indent size check (default + "false") +  + --disable-indentation Disable indentation check (default + "false") +  + --disable-insert-final-newline Disable final newline check + (default "false") +  + --disable-max-line-length Disable max line length check + (default "false") +  + --disable-trim-trailing-whitespace Disable trailing whitespace check + (default "false") +  + --dry-run Show which files would be checked + (default "false") +  + --exclude string Regex to exclude files from + checking +  + --format string Specify the output format: + default, gcc (default "default") +  + -h, --help help for editorconfig (default + "false") +  + --ignore-defaults Ignore default excludes (default + "false") +  + --init creates an initial configuration + (default "false") +  + --no-color Don't print colors (default + "false") +  + --version Print the version number (default + "false") +  +  +Global Flags: +  + --logs-file string The file to write Atmos logs to. Logs can be + written to any file or any standard file + descriptor, including '/dev/stdout', + '/dev/stderr' and '/dev/null' (default + "/dev/stdout") +  + --logs-level string Logs level. Supported log levels are Trace, + Debug, Info, Warning, Off. If the log level + is set to Off, Atmos will not log any + messages (default "Info") +  + --redirect-stderr string File descriptor to redirect 'stderr' to. + Errors can be redirected to any file or any + standard file descriptor (including + '/dev/null'): atmos <command> + --redirect-stderr /dev/stdout +  +The '--' (double-dash) can be used to signify the end of Atmos-specific options +and the beginning of additional native arguments and flags for the specific command being run. +  +Example: + atmos atmos <subcommand> <component> -s <stack> -- <native-flags>
+ + From 9c139b0f194ca64c20a8ebb4e4f4d524c59bcace Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 11 Jan 2025 02:50:12 +0100 Subject: [PATCH 22/32] Set git attributes --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitattributes b/.gitattributes index 4f3d4e564..44bb7c1d2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,3 +12,5 @@ website/src/components/screengrabs/**/*.html linguist-generated=true binary *.eps binary *.ansi binary *.mp4 binary +# Ensure all files use LF line endings +* text eol=lf \ No newline at end of file From ea121eae143f07d0fe32e13ccdf60b7b6b74b245 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 11 Jan 2025 03:00:49 +0100 Subject: [PATCH 23/32] validate.mdx to remove unwanted diff --- .../docs/core-concepts/validate/validate.mdx | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/website/docs/core-concepts/validate/validate.mdx b/website/docs/core-concepts/validate/validate.mdx index 946525a6e..92c935539 100644 --- a/website/docs/core-concepts/validate/validate.mdx +++ b/website/docs/core-concepts/validate/validate.mdx @@ -70,41 +70,41 @@ The command checks and validates the following: - Schema: if all sections in all YAML manifest files are correctly configured and have valid data types - Misconfiguration and duplication of components in stacks. If the same Atmos component in the same Atmos stack is -defined in more than one stack manifest file, and the component configurations are different, an error message will -be displayed similar to the following: + defined in more than one stack manifest file, and the component configurations are different, an error message will + be displayed similar to the following: - -```console -The Atmos component 'vpc' in the stack 'plat-ue2-dev' is defined in more than one -top-level stack manifest file: orgs/acme/plat/dev/us-east-2-extras, orgs/acme/plat/dev/us-east-2. + + ```console + The Atmos component 'vpc' in the stack 'plat-ue2-dev' is defined in more than one + top-level stack manifest file: orgs/acme/plat/dev/us-east-2-extras, orgs/acme/plat/dev/us-east-2. -The component configurations in the stack manifest are different. + The component configurations in the stack manifest are different. -To check and compare the component configurations in the stack manifests, run the following commands: -- atmos describe component vpc -s orgs/acme/plat/dev/us-east-2-extras -- atmos describe component vpc -s orgs/acme/plat/dev/us-east-2 + To check and compare the component configurations in the stack manifests, run the following commands: + - atmos describe component vpc -s orgs/acme/plat/dev/us-east-2-extras + - atmos describe component vpc -s orgs/acme/plat/dev/us-east-2 -You can use the '--file' flag to write the results of the above commands to files -(refer to https://atmos.tools/cli/commands/describe/component). + You can use the '--file' flag to write the results of the above commands to files + (refer to https://atmos.tools/cli/commands/describe/component). -You can then use the Linux 'diff' command to compare the files line by line and show the differences -(refer to https://man7.org/linux/man-pages/man1/diff.1.html) + You can then use the Linux 'diff' command to compare the files line by line and show the differences + (refer to https://man7.org/linux/man-pages/man1/diff.1.html) -When searching for the component 'vpc' in the stack 'plat-ue2-dev', Atmos can't decide which -stack manifest file to use to get the configuration for the component. This is a stack misconfiguration. + When searching for the component 'vpc' in the stack 'plat-ue2-dev', Atmos can't decide which + stack manifest file to use to get the configuration for the component. This is a stack misconfiguration. -Consider the following solutions to fix the issue: + Consider the following solutions to fix the issue: -- Ensure that the same instance of the Atmos 'vpc' component in the stack 'plat-ue2-dev' - is only defined once (in one YAML stack manifest file) + - Ensure that the same instance of the Atmos 'vpc' component in the stack 'plat-ue2-dev' + is only defined once (in one YAML stack manifest file) -- When defining multiple instances of the same component in the stack, - ensure each has a unique name + - When defining multiple instances of the same component in the stack, + ensure each has a unique name -- Use multiple-inheritance to combine multiple configurations together - (refer to https://atmos.tools/core-concepts/stacks/inheritance) -``` - + - Use multiple-inheritance to combine multiple configurations together + (refer to https://atmos.tools/core-concepts/stacks/inheritance) + ``` + ## Validate Atmos Manifests using JSON Schema From 7cde93148332611dfb759ad62d05e5eb90b9e1d7 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 11 Jan 2025 03:17:10 +0100 Subject: [PATCH 24/32] Update website/docs/cli/commands/validate/validate-editorconfig.mdx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- website/docs/cli/commands/validate/validate-editorconfig.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/cli/commands/validate/validate-editorconfig.mdx b/website/docs/cli/commands/validate/validate-editorconfig.mdx index 475d75d0f..c93fa98fb 100644 --- a/website/docs/cli/commands/validate/validate-editorconfig.mdx +++ b/website/docs/cli/commands/validate/validate-editorconfig.mdx @@ -8,7 +8,7 @@ description: Use this command to validate an Atmos component in a stack using JS import Screengrab from '@site/src/components/Screengrab' :::note purpose -Use this command to validate an Atmos component in a stack using JSON Schema and OPA policies. +Use this command to validate files against the rules defined in .editorconfig file. ::: From cb42f797678da88879b686af6f6efe8b2576e012 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sat, 11 Jan 2025 03:19:40 +0100 Subject: [PATCH 25/32] Update website/docs/cli/commands/validate/validate-editorconfig.mdx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- website/docs/cli/commands/validate/validate-editorconfig.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/cli/commands/validate/validate-editorconfig.mdx b/website/docs/cli/commands/validate/validate-editorconfig.mdx index c93fa98fb..b929b4c03 100644 --- a/website/docs/cli/commands/validate/validate-editorconfig.mdx +++ b/website/docs/cli/commands/validate/validate-editorconfig.mdx @@ -21,7 +21,7 @@ Execute the `validate editorconfig` command like this: atmos validate editorconfig ``` -This command validates an Atmos component in a stack using JSON Schema and OPA policies. +This command validates files against the formatting rules defined in your .editorconfig file. :::tip Run `atmos validate editorconfig --help` to see all the available options From 313e77de9322d7c1751d250e5762ae61ce8ebb2a Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Sun, 12 Jan 2025 14:41:02 +0100 Subject: [PATCH 26/32] editorconfig test cases --- tests/test-cases/editorconfig.yaml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/test-cases/editorconfig.yaml diff --git a/tests/test-cases/editorconfig.yaml b/tests/test-cases/editorconfig.yaml new file mode 100644 index 000000000..b5874f9b4 --- /dev/null +++ b/tests/test-cases/editorconfig.yaml @@ -0,0 +1,27 @@ +tests: + - name: atmos validate editorconfig + enabled: true + description: "Ensure atmos CLI validates success case" + workdir: "../examples/quick-start-simple/" + command: "atmos" + args: + - "validate" + - "editorconfig" + expect: + stdout: + - "No errors found" + stderr: + - "^$" + exit_code: 0 + - name: atmos validate editorconfig error + enabled: true + description: "Ensure atmos CLI gives error during validation based on editorconfig" + workdir: "../tests/fixtures" + command: "atmos" + args: + - "validate" + - "editorconfig" + expect: + stderr: + - "18 errors found" + exit_code: 1 From 1590ec5ce467f8447c048ee0fc5f69ecff74763f Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Wed, 15 Jan 2025 06:51:02 +0100 Subject: [PATCH 27/32] refactor file name for editorconfig --- cmd/{editor_config.go => validate_editorconfig.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmd/{editor_config.go => validate_editorconfig.go} (100%) diff --git a/cmd/editor_config.go b/cmd/validate_editorconfig.go similarity index 100% rename from cmd/editor_config.go rename to cmd/validate_editorconfig.go From 7639df7c3ee958bd1ffd2c64bb641be0385646f2 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Wed, 15 Jan 2025 21:26:09 +0100 Subject: [PATCH 28/32] Update .github/workflows/test.yml Co-authored-by: Erik Osterman (CEO @ Cloud Posse) --- .github/workflows/test.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3f95f611e..ce2d45555 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -86,14 +86,6 @@ jobs: timeout-minutes: 15 runs-on: ${{ matrix.flavor.os }} steps: - # Configures Git to ensure LF line endings on Windows to pass the `atmos validate editorconfig` check. - # The check expects LF endings, but on Windows, files might default to CRLF due to core.autocrlf. - # Since the validation runs in CI, we enforce consistent settings across all OS environments. - - name: Set Git Preferences for windows - if: matrix.flavor.target == 'windows' - run: | - git config --global core.autocrlf false - git config --global core.eol lf - name: Check out code into the Go module directory uses: actions/checkout@v4 From aa4251d5eadcc0be60d97fc48e8c09e8d343e77b Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Wed, 15 Jan 2025 21:26:57 +0100 Subject: [PATCH 29/32] Update .gitattributes Co-authored-by: Erik Osterman (CEO @ Cloud Posse) --- .gitattributes | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitattributes b/.gitattributes index c019fd8e4..cc0fb28eb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,6 +15,4 @@ website/src/components/Screengrabs/**/*.html linguist-generated=true binary *.ai binary *.eps binary *.ansi binary -*.mp4 binary -# Ensure all files use LF line endings -* text eol=lf \ No newline at end of file +*.mp4 binary \ No newline at end of file From 72dfe12ed2f377e656ff623051938db0c7935802 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Wed, 15 Jan 2025 21:51:45 +0100 Subject: [PATCH 30/32] changed editorconfig fixture directory --- .../editorconfig}/.editorconfig | 0 .../fixtures/scenarios/editorconfig/README.md | 5 + .../scenarios/editorconfig/atmos.yaml | 396 ++++++++++++++++++ .../infra/account-map/component.yaml | 40 ++ .../infra/vpc-flow-logs-bucket/component.yaml | 52 +++ .../infra/vpc-flow-logs-bucket/context.tf | 279 ++++++++++++ .../infra/vpc-flow-logs-bucket/main.tf | 18 + .../infra/vpc-flow-logs-bucket/outputs.tf | 9 + .../infra/vpc-flow-logs-bucket/providers.tf | 19 + .../infra/vpc-flow-logs-bucket/variables.tf | 64 +++ .../infra/vpc-flow-logs-bucket/versions.tf | 10 + .../terraform/infra/vpc/component.yaml | 36 ++ .../components/terraform/infra/vpc/context.tf | 279 ++++++++++++ .../components/terraform/infra/vpc/main.tf | 173 ++++++++ .../components/terraform/infra/vpc/outputs.tf | 109 +++++ .../terraform/infra/vpc/providers.tf | 3 + .../terraform/infra/vpc/remote-state.tf | 20 + .../terraform/infra/vpc/variables.tf | 165 ++++++++ .../terraform/infra/vpc/versions.tf | 14 + .../terraform/infra/vpc/vpc-flow-logs.tf | 15 + .../terraform/infra/vpc2/component.yaml | 26 ++ .../components/terraform/mixins/context.tf | 279 ++++++++++++ .../terraform/mixins/introspection.mixin.tf | 34 ++ .../test/template-functions-test/context.tf | 279 ++++++++++++ .../test/template-functions-test/main.tf | 6 + .../test/template-functions-test/outputs.tf | 22 + .../test/template-functions-test2/context.tf | 279 ++++++++++++ .../test/template-functions-test2/outputs.tf | 14 + .../template-functions-test2/variables.tf | 14 + .../terraform/test/test-component/context.tf | 279 ++++++++++++ .../terraform/test/test-component/main.tf | 17 + .../terraform/test/test-component/outputs.tf | 9 + .../test/test-component/variables.tf | 38 ++ .../terraform/test/test-component/versions.tf | 3 + .../test/test2/test-component-2/context.tf | 279 ++++++++++++ .../test/test2/test-component-2/main.tf | 17 + .../test/test2/test-component-2/outputs.tf | 9 + .../test/test2/test-component-2/variables.tf | 38 ++ .../test/test2/test-component-2/versions.tf | 3 + .../terraform/top-level-component1/context.tf | 279 ++++++++++++ .../terraform/top-level-component1/main.tf | 16 + .../terraform/top-level-component1/outputs.tf | 9 + .../policies/policy1.rego | 5 + .../top-level-component1/variables.tf | 38 ++ .../top-level-component1/versions.tf | 3 + .../scenarios/editorconfig/vendor.yaml | 102 +++++ ...config.yaml => validate-editorconfig.yaml} | 4 +- 47 files changed, 3805 insertions(+), 2 deletions(-) rename tests/fixtures/{ => scenarios/editorconfig}/.editorconfig (100%) create mode 100644 tests/fixtures/scenarios/editorconfig/README.md create mode 100644 tests/fixtures/scenarios/editorconfig/atmos.yaml create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/account-map/component.yaml create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/component.yaml create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/context.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/main.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/outputs.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/providers.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/variables.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/versions.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/component.yaml create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/context.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/main.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/outputs.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/providers.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/remote-state.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/variables.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/versions.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/vpc-flow-logs.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc2/component.yaml create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/mixins/context.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/mixins/introspection.mixin.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/context.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/main.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/outputs.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/context.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/outputs.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/variables.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/context.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/main.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/outputs.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/variables.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/versions.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/context.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/main.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/outputs.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/variables.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/versions.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/context.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/main.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/outputs.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/policies/policy1.rego create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/variables.tf create mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/versions.tf create mode 100644 tests/fixtures/scenarios/editorconfig/vendor.yaml rename tests/test-cases/{editorconfig.yaml => validate-editorconfig.yaml} (87%) diff --git a/tests/fixtures/.editorconfig b/tests/fixtures/scenarios/editorconfig/.editorconfig similarity index 100% rename from tests/fixtures/.editorconfig rename to tests/fixtures/scenarios/editorconfig/.editorconfig diff --git a/tests/fixtures/scenarios/editorconfig/README.md b/tests/fixtures/scenarios/editorconfig/README.md new file mode 100644 index 000000000..7e72940f5 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/README.md @@ -0,0 +1,5 @@ +# Atmos Tests + +These configurations are used by the Atmos tests and CI to test/validate configurations and behaviors of Atmos. +Thus, they contain many deliberately broken examples and should not be used. +Instead, please see our [Quick Start](../quick-start) example. diff --git a/tests/fixtures/scenarios/editorconfig/atmos.yaml b/tests/fixtures/scenarios/editorconfig/atmos.yaml new file mode 100644 index 000000000..41811f20a --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/atmos.yaml @@ -0,0 +1,396 @@ +# CLI config is loaded from the following locations (from lowest to highest priority): +# system dir ('/usr/local/etc/atmos' on Linux, '%LOCALAPPDATA%/atmos' on Windows) +# home dir (~/.atmos) +# current directory +# ENV vars +# Command-line arguments +# +# It supports POSIX-style Globs for file names/paths (double-star '**' is supported) +# https://en.wikipedia.org/wiki/Glob_(programming) + +# Base path for components, stacks and workflows configurations. +# Can also be set using 'ATMOS_BASE_PATH' ENV var, or '--base-path' command-line argument. +# Supports both absolute and relative paths. +# If not provided or is an empty string, 'components.terraform.base_path', 'components.helmfile.base_path', 'stacks.base_path' and 'workflows.base_path' +# are independent settings (supporting both absolute and relative paths). +# If 'base_path' is provided, 'components.terraform.base_path', 'components.helmfile.base_path', 'stacks.base_path' and 'workflows.base_path' +# are considered paths relative to 'base_path'. +base_path: "." + +components: + terraform: + # Optional `command` specifies the executable to be called by `atmos` when running Terraform commands + # If not defined, `terraform` is used + # Examples: + # command: terraform + # command: /usr/local/bin/terraform + # command: /usr/local/bin/terraform-1.8 + # command: tofu + # command: /usr/local/bin/tofu-1.7.1 + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_COMMAND' ENV var, or '--terraform-command' command-line argument + command: terraform + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_BASE_PATH' ENV var, or '--terraform-dir' command-line argument + # Supports both absolute and relative paths + base_path: "components/terraform" + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE' ENV var + apply_auto_approve: false + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_DEPLOY_RUN_INIT' ENV var, or '--deploy-run-init' command-line argument + deploy_run_init: true + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_INIT_RUN_RECONFIGURE' ENV var, or '--init-run-reconfigure' command-line argument + init_run_reconfigure: true + # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_AUTO_GENERATE_BACKEND_FILE' ENV var, or '--auto-generate-backend-file' command-line argument + auto_generate_backend_file: false + helmfile: + # Can also be set using 'ATMOS_COMPONENTS_HELMFILE_BASE_PATH' ENV var, or '--helmfile-dir' command-line argument + # Supports both absolute and relative paths + base_path: "components/helmfile" + # Can also be set using 'ATMOS_COMPONENTS_HELMFILE_USE_EKS' ENV var + # If not specified, defaults to 'true' + use_eks: true + # Can also be set using 'ATMOS_COMPONENTS_HELMFILE_KUBECONFIG_PATH' ENV var + kubeconfig_path: "/dev/shm" + # Can also be set using 'ATMOS_COMPONENTS_HELMFILE_HELM_AWS_PROFILE_PATTERN' ENV var + helm_aws_profile_pattern: "{namespace}-{tenant}-gbl-{stage}-helm" + # Can also be set using 'ATMOS_COMPONENTS_HELMFILE_CLUSTER_NAME_PATTERN' ENV var + cluster_name_pattern: "{namespace}-{tenant}-{environment}-{stage}-eks-cluster" + +stacks: + # Can also be set using 'ATMOS_STACKS_BASE_PATH' ENV var, or '--config-dir' and '--stacks-dir' command-line arguments + # Supports both absolute and relative paths + base_path: "stacks" + # Can also be set using 'ATMOS_STACKS_INCLUDED_PATHS' ENV var (comma-separated values string) + included_paths: + - "orgs/**/*" + # Can also be set using 'ATMOS_STACKS_EXCLUDED_PATHS' ENV var (comma-separated values string) + excluded_paths: + - "**/_defaults.yaml" + # To define Atmos stack naming convention, use either `name_pattern` or `name_template`. + # `name_template` has higher priority (if `name_template` is specified, `name_pattern` will be ignored). + # `name_pattern` can also be set using 'ATMOS_STACKS_NAME_PATTERN' ENV var + # name_pattern: "{tenant}-{environment}-{stage}" + # `name_template` is a Golang template. + # For the template tokens, and you can use any Atmos sections and attributes that the Atmos command + # `atmos describe component -s ` generates (refer to https://atmos.tools/cli/commands/describe/component). + # `name_template` can also be set using 'ATMOS_STACKS_NAME_TEMPLATE' ENV var + name_template: "{{.vars.tenant}}-{{.vars.environment}}-{{.vars.stage}}" + +workflows: + # Can also be set using 'ATMOS_WORKFLOWS_BASE_PATH' ENV var, or '--workflows-dir' command-line argument + # Supports both absolute and relative paths + base_path: "stacks/workflows" + +logs: + # Can also be set using 'ATMOS_LOGS_FILE' ENV var, or '--logs-file' command-line argument + # File or standard file descriptor to write logs to + # Logs can be written to any file or any standard file descriptor, including `/dev/stdout`, `/dev/stderr` and `/dev/null` + file: "/dev/stderr" + # Supported log levels: Trace, Debug, Info, Warning, Off + # Can also be set using 'ATMOS_LOGS_LEVEL' ENV var, or '--logs-level' command-line argument + level: Info + +# Custom CLI commands +commands: + # No arguments or flags are required + - name: "test" + description: "Run all tests" + steps: + - atmos vendor pull + + # test for infinite loop + - name: loop + description: This command tests circuit breaker for infinite loop + steps: + - "echo Hello world!" + - atmos loop + + - name: tf + description: Execute 'terraform' commands + # subcommands + commands: + - name: plan + description: This command plans terraform components + arguments: + - name: component + description: Name of the component + flags: + - name: stack + shorthand: s + description: Name of the stack + required: true + env: + - key: ENV_VAR_1 + value: ENV_VAR_1_value + - key: ENV_VAR_2 + # 'valueCommand' is an external command to execute to get the value for the ENV var + # Either 'value' or 'valueCommand' can be specified for the ENV var, but not both + valueCommand: echo ENV_VAR_2_value + # steps support Go templates + steps: + - atmos terraform plan {{ .Arguments.component }} -s {{ .Flags.stack }} + - name: terraform + description: Execute 'terraform' commands + # subcommands + commands: + - name: provision + description: This command provisions terraform components + arguments: + - name: component + description: Name of the component + flags: + - name: stack + shorthand: s + description: Name of the stack + required: true + # ENV var values support Go templates + env: + - key: ATMOS_COMPONENT + value: "{{ .Arguments.component }}" + - key: ATMOS_STACK + value: "{{ .Flags.stack }}" + steps: + - atmos terraform plan $ATMOS_COMPONENT -s $ATMOS_STACK + - atmos terraform apply $ATMOS_COMPONENT -s $ATMOS_STACK + - name: show + description: Execute 'show' commands + # subcommands + commands: + - name: component + description: Execute 'show component' command + arguments: + - name: component + description: Name of the component + flags: + - name: stack + shorthand: s + description: Name of the stack + required: true + # ENV var values support Go templates and have access to {{ .ComponentConfig.xxx.yyy.zzz }} Go template variables + env: + - key: ATMOS_COMPONENT + value: "{{ .Arguments.component }}" + - key: ATMOS_STACK + value: "{{ .Flags.stack }}" + - key: ATMOS_TENANT + value: "{{ .ComponentConfig.vars.tenant }}" + - key: ATMOS_STAGE + value: "{{ .ComponentConfig.vars.stage }}" + - key: ATMOS_ENVIRONMENT + value: "{{ .ComponentConfig.vars.environment }}" + - key: ATMOS_IS_PROD + value: "{{ .ComponentConfig.settings.config.is_prod }}" + # If a custom command defines 'component_config' section with 'component' and 'stack', 'atmos' generates the config for the component in the stack + # and makes it available in {{ .ComponentConfig.xxx.yyy.zzz }} Go template variables, + # exposing all the component sections (which are also shown by 'atmos describe component' command) + component_config: + component: "{{ .Arguments.component }}" + stack: "{{ .Flags.stack }}" + # Steps support using Go templates and can access all configuration settings (e.g. {{ .ComponentConfig.xxx.yyy.zzz }}) + # Steps also have access to the ENV vars defined in the 'env' section of the 'command' + steps: + - 'echo Atmos component from argument: "{{ .Arguments.component }}"' + - 'echo ATMOS_COMPONENT: "$ATMOS_COMPONENT"' + - 'echo Atmos stack: "{{ .Flags.stack }}"' + - 'echo Terraform component: "{{ .ComponentConfig.component }}"' + - 'echo Backend S3 bucket: "{{ .ComponentConfig.backend.bucket }}"' + - 'echo Terraform workspace: "{{ .ComponentConfig.workspace }}"' + - 'echo Namespace: "{{ .ComponentConfig.vars.namespace }}"' + - 'echo Tenant: "{{ .ComponentConfig.vars.tenant }}"' + - 'echo Environment: "{{ .ComponentConfig.vars.environment }}"' + - 'echo Stage: "{{ .ComponentConfig.vars.stage }}"' + - 'echo settings.spacelift.workspace_enabled: "{{ .ComponentConfig.settings.spacelift.workspace_enabled }}"' + - 'echo Dependencies: "{{ .ComponentConfig.deps }}"' + - 'echo settings.config.is_prod: "{{ .ComponentConfig.settings.config.is_prod }}"' + - 'echo ATMOS_IS_PROD: "$ATMOS_IS_PROD"' + + - name: set-eks-cluster + description: | + Download 'kubeconfig' and set EKS cluster. + + Example usage: + atmos set-eks-cluster eks/cluster -s tenant1-ue1-dev -r admin + atmos set-eks-cluster eks/cluster -s tenant2-uw2-prod --role reader + verbose: false # Set to `true` to see verbose outputs + arguments: + - name: component + description: Name of the component + flags: + - name: stack + shorthand: s + description: Name of the stack + required: true + - name: role + shorthand: r + description: IAM role to use + required: true + # If a custom command defines 'component_config' section with 'component' and 'stack', + # Atmos generates the config for the component in the stack + # and makes it available in {{ .ComponentConfig.xxx.yyy.zzz }} Go template variables, + # exposing all the component sections (which are also shown by 'atmos describe component' command) + component_config: + component: "{{ .Arguments.component }}" + stack: "{{ .Flags.stack }}" + env: + - key: KUBECONFIG + value: /dev/shm/kubecfg.{{ .Flags.stack }}-{{ .Flags.role }} + steps: + - > + aws + --profile {{ .ComponentConfig.vars.namespace }}-{{ .ComponentConfig.vars.tenant }}-gbl-{{ .ComponentConfig.vars.stage }}-{{ .Flags.role }} + --region {{ .ComponentConfig.vars.region }} + eks update-kubeconfig + --name={{ .ComponentConfig.vars.namespace }}-{{ .Flags.stack }}-eks-cluster + --kubeconfig="${KUBECONFIG}" + > /dev/null + - chmod 600 ${KUBECONFIG} + - echo ${KUBECONFIG} + +# Integrations +integrations: + # Atlantis integration + # https://www.runatlantis.io/docs/repo-level-atlantis-yaml.html + atlantis: + # Path and name of the Atlantis config file 'atlantis.yaml' + # Supports absolute and relative paths + # All the intermediate folders will be created automatically (e.g. 'path: /config/atlantis/atlantis.yaml') + # Can be overridden on the command line by using '--output-path' command-line argument in 'atmos atlantis generate repo-config' command + # If not specified (set to an empty string/omitted here, and set to an empty string on the command line), the content of the file will be dumped to 'stdout' + # On Linux/macOS, you can also use '--output-path=/dev/stdout' to dump the content to 'stdout' without setting it to an empty string in 'atlantis.path' + path: "atlantis.yaml" + + # Config templates + # Select a template by using the '--config-template ' command-line argument in 'atmos atlantis generate repo-config' command + config_templates: + config-1: + version: 3 + automerge: true + delete_source_branch_on_merge: true + parallel_plan: true + parallel_apply: true + allowed_regexp_prefixes: + - dev/ + - staging/ + - prod/ + + # Project templates + # Select a template by using the '--project-template ' command-line argument in 'atmos atlantis generate repo-config' command + project_templates: + project-1: + # generate a project entry for each component in every stack + name: "{tenant}-{environment}-{stage}-{component}" + workspace: "{workspace}" + dir: "{component-path}" + terraform_version: v1.2 + delete_source_branch_on_merge: true + autoplan: + enabled: true + when_modified: + - "**/*.tf" + - "varfiles/$PROJECT_NAME.tfvars.json" + apply_requirements: + - "approved" + + # Workflow templates + # https://www.runatlantis.io/docs/custom-workflows.html#custom-init-plan-apply-commands + # https://www.runatlantis.io/docs/custom-workflows.html#custom-run-command + workflow_templates: + workflow-1: + plan: + steps: + - run: terraform init -input=false + # When using workspaces, you need to select the workspace using the $WORKSPACE environment variable + - run: terraform workspace select $WORKSPACE || terraform workspace new $WORKSPACE + # You must output the plan using '-out $PLANFILE' because Atlantis expects plans to be in a specific location + - run: terraform plan -input=false -refresh -out $PLANFILE -var-file varfiles/$PROJECT_NAME.tfvars.json + apply: + steps: + - run: terraform apply $PLANFILE + + # Atmos Pro integration + pro: + host: app.cloudposse.com + timeout: 3 + events: + pull_request: + - on: [open, synchronize, reopen] + workflow: atmos-plan.yml + dispatch_only_top_level_stacks: true + - on: [merge] + workflow: atmos-apply.yaml + release: + - on: [publish] + workflow: atmos-apply.yaml + + # GitHub integration + github: + gitops: + opentofu-version: 1.8.4 + terraform-version: 1.9.8 + infracost-enabled: false + +# Validation schemas (for validating atmos stacks and components) +schemas: + # https://json-schema.org + jsonschema: + # Can also be set using 'ATMOS_SCHEMAS_JSONSCHEMA_BASE_PATH' ENV var, or '--schemas-jsonschema-dir' command-line argument + # Supports both absolute and relative paths + base_path: "stacks/schemas/jsonschema" + # https://www.openpolicyagent.org + opa: + # Can also be set using 'ATMOS_SCHEMAS_OPA_BASE_PATH' ENV var, or '--schemas-opa-dir' command-line argument + # Supports both absolute and relative paths + base_path: "stacks/schemas/opa" + # JSON Schema to validate Atmos manifests + # https://atmos.tools/cli/schemas/ + # https://atmos.tools/cli/commands/validate/stacks/ + # https://atmos.tools/quick-start/advanced/configure-validation/ + # https://atmos.tools/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json + # https://json-schema.org/draft/2020-12/release-notes + # https://www.schemastore.org/json + # https://github.com/SchemaStore/schemastore + atmos: + # Can also be set using 'ATMOS_SCHEMAS_ATMOS_MANIFEST' ENV var, or '--schemas-atmos-manifest' command-line argument + # Supports both absolute and relative paths (relative to the `base_path` setting in `atmos.yaml`) + manifest: "../../schemas/atmos/atmos-manifest/1.0/atmos-manifest.json" + +# CLI command aliases +aliases: + # Aliases for Atmos native commands + tf: terraform + tp: terraform plan + up: terraform apply + down: terraform destroy + ds: describe stacks + dc: describe component + # Aliases for Atmos custom commands + ls: list stacks + lc: list components + +# `Go` templates in Atmos manifests +# https://atmos.tools/core-concepts/stacks/templates +# https://pkg.go.dev/text/template +templates: + settings: + enabled: true + evaluations: 1 + # https://masterminds.github.io/sprig + sprig: + enabled: true + # https://docs.gomplate.ca + gomplate: + enabled: true + timeout: 5 + # https://docs.gomplate.ca/datasources + datasources: {} + +settings: + # `list_merge_strategy` specifies how lists are merged in Atmos stack manifests. + # Can also be set using 'ATMOS_SETTINGS_LIST_MERGE_STRATEGY' environment variable, or '--settings-list-merge-strategy' command-line argument + # The following strategies are supported: + # `replace`: Most recent list imported wins (the default behavior). + # `append`: The sequence of lists is appended in the same order as imports. + # `merge`: The items in the destination list are deep-merged with the items in the source list. + # The items in the source list take precedence. + # The items are processed starting from the first up to the length of the source list (the remaining items are not processed). + # If the source and destination lists have the same length, all items in the destination lists are + # deep-merged with all items in the source list. + list_merge_strategy: replace diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/account-map/component.yaml b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/account-map/component.yaml new file mode 100644 index 000000000..1240d1d0d --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/account-map/component.yaml @@ -0,0 +1,40 @@ +# 'infra/account-map' component vendoring config + +apiVersion: atmos/v1 +kind: ComponentVendorConfig +metadata: + name: account-map-vendor-config + description: Source and mixins config for vendoring of 'vpc-flow-logs-bucket' component +spec: + source: + # Source 'uri' supports the following protocols: Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP, + # and all URL and archive formats as described in https://github.com/hashicorp/go-getter + # In 'uri', Golang templates are supported https://pkg.go.dev/text/template + # If 'version' is provided, '{{.Version}}' will be replaced with the 'version' value before pulling the files from 'uri' + uri: github.com/cloudposse/terraform-aws-components.git//modules/account-map?ref={{.Version}} + version: 1.372.0 + # Only include the files that match the 'included_paths' patterns + # If 'included_paths' is not specified, all files will be matched except those that match the patterns from 'excluded_paths' + # 'included_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) + # https://en.wikipedia.org/wiki/Glob_(programming) + # https://github.com/bmatcuk/doublestar#patterns + included_paths: + # include '.tf', '.tfvars' and '.md' files from the root folder + - "**/*.tf" + - "**/*.tfvars" + - "**/*.md" + # include the 'modules' folder and all sub-folders + # note that if you don't include the folders, the files in the folders will not be included + - "**/modules/**" + # include '.tf', '.tfvars' and '.md' files from the 'modules' folder and all sub-folders + - "**/modules/**/*.tf" + - "**/modules/**/*.tfvars" + - "**/modules/**/*.md" + # Exclude the files that match any of the 'excluded_paths' patterns + # Note that we are excluding 'context.tf' since a newer version of it will be downloaded using 'mixins' + # 'excluded_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) + excluded_paths: [] + + # mixins override files from 'source' with the same 'filename' (e.g. 'context.tf' will override 'context.tf' from the 'source') + # mixins are processed in the order they are declared in the list + mixins: [] diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/component.yaml b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/component.yaml new file mode 100644 index 000000000..f7942287f --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/component.yaml @@ -0,0 +1,52 @@ +# 'infra/vpc-flow-logs-bucket' component vendoring config + +# 'component.yaml' in the component folder is processed by the 'atmos' commands +# 'atmos vendor pull -c infra/vpc-flow-logs-bucket' or 'atmos vendor pull --component infra/vpc-flow-logs-bucket' + +# > atmos vendor pull -c infra/vpc-flow-logs-bucket +# Pulling sources for the component 'infra/vpc-flow-logs-bucket' from 'github.com/cloudposse/terraform-aws-components.git//modules/vpc-flow-logs-bucket?ref=0.194.0' +# into 'tests/fixtures/scenarios/complete/components/terraform/infra/vpc-flow-logs-bucket' +# +# Including the file 'README.md' since it matches the '**/*.md' pattern from 'included_paths' +# Excluding the file 'context.tf' since it matches the '**/context.tf' pattern from 'excluded_paths' +# Including the file 'default.auto.tfvars' since it matches the '**/*.tfvars' pattern from 'included_paths' +# Including the file 'main.tf' since it matches the '**/*.tf' pattern from 'included_paths' +# Including the file 'outputs.tf' since it matches the '**/*.tf' pattern from 'included_paths' +# Including the file 'providers.tf' since it matches the '**/*.tf' pattern from 'included_paths' +# Including the file 'variables.tf' since it matches the '**/*.tf' pattern from 'included_paths' +# Including the file 'versions.tf' since it matches the '**/*.tf' pattern from 'included_paths' +# +# Pulling the mixin 'https://raw.githubusercontent.com/cloudposse/terraform-null-label/0.25.0/exports/context.tf' +# for the component 'infra/vpc-flow-logs-bucket' into 'tests/fixtures/scenarios/complete/components/terraform/infra/vpc-flow-logs-bucket' +# Pulling the mixin 'https://raw.githubusercontent.com/cloudposse/terraform-aws-components/0.194.0/modules/datadog-agent/introspection.mixin.tf' +# for the component 'infra/vpc-flow-logs-bucket' into 'tests/fixtures/scenarios/complete/components/terraform/infra/vpc-flow-logs-bucket' + +apiVersion: atmos/v1 +kind: ComponentVendorConfig +metadata: + name: vpc-flow-logs-bucket-vendor-config + description: Source and mixins config for vendoring of 'vpc-flow-logs-bucket' component +spec: + source: + # Source 'uri' supports the following protocols: Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP, + # and all URL and archive formats as described in https://github.com/hashicorp/go-getter + # In 'uri', Golang templates are supported https://pkg.go.dev/text/template + # If 'version' is provided, '{{.Version}}' will be replaced with the 'version' value before pulling the files from 'uri' + uri: github.com/cloudposse/terraform-aws-components.git//modules/vpc-flow-logs-bucket?ref={{.Version}} + version: 1.372.0 + # Only include the files that match the 'included_paths' patterns + # If 'included_paths' is not specified, all files will be matched except those that match the patterns from 'excluded_paths' + # 'included_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) + # https://en.wikipedia.org/wiki/Glob_(programming) + # https://github.com/bmatcuk/doublestar#patterns + included_paths: + - "**/*.tf" + # Exclude the files that match any of the 'excluded_paths' patterns + # Note that we are excluding 'context.tf' since a newer version of it will be downloaded using 'mixins' + # 'excluded_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) + excluded_paths: + - "**/context.tf" + + # mixins override files from 'source' with the same 'filename' (e.g. 'context.tf' will override 'context.tf' from the 'source') + # mixins are processed in the order they are declared in the list + mixins: [] diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/context.tf new file mode 100644 index 000000000..5e0ef8856 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/context.tf @@ -0,0 +1,279 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.25.0" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + tenant = var.tenant + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + descriptor_formats = var.descriptor_formats + labels_as_tags = var.labels_as_tags + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + tenant = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + descriptor_formats = {} + # Note: we have to use [] instead of null for unset lists due to + # https://github.com/hashicorp/terraform/issues/28137 + # which was not fixed until Terraform 1.0.0, + # but we want the default to be all the labels in `label_order` + # and we want users to be able to prevent all tag generation + # by setting `labels_as_tags` to `[]`, so we need + # a different sentinel to indicate "default" + labels_as_tags = ["unset"] + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" +} + +variable "tenant" { + type = string + default = null + description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" +} + +variable "environment" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +variable "labels_as_tags" { + type = set(string) + default = ["default"] + description = <<-EOT + Set of labels (ID elements) to include as tags in the `tags` output. + Default is to include all labels. + Tags with empty values will not be included in the `tags` output. + Set to `[]` to suppress all generated tags. + **Notes:** + The value of the `name` tag, if included, will be the `id`, not the `name`. + Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be + changed in later chained modules. Attempts to change it will be silently ignored. + EOT +} + +variable "tags" { + type = map(string) + default = {} + description = <<-EOT + Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). + Neither the tag keys nor the tag values will be modified by this module. + EOT +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = <<-EOT + Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. + This is for some rare cases where resources want additional configuration of tags + and therefore take a list of maps with tag key, value, and additional configuration. + EOT +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "descriptor_formats" { + type = any + default = {} + description = <<-EOT + Describe additional descriptors to be output in the `descriptors` output map. + Map of maps. Keys are names of descriptors. Values are maps of the form + `{ + format = string + labels = list(string) + }` + (Type is `any` so the map values can later be enhanced to provide additional options.) + `format` is a Terraform format string to be passed to the `format()` function. + `labels` is a list of labels, in order, to pass to `format()` function. + Label values will be normalized before being passed to `format()` so they will be + identical to how they appear in `id`. + Default is `{}` (`descriptors` output will be empty). + EOT +} + +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/main.tf new file mode 100644 index 000000000..88eaa98fe --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/main.tf @@ -0,0 +1,18 @@ +module "flow_logs_s3_bucket" { + source = "cloudposse/vpc-flow-logs-s3-bucket/aws" + version = "1.0.1" + + lifecycle_prefix = var.lifecycle_prefix + lifecycle_tags = var.lifecycle_tags + lifecycle_rule_enabled = var.lifecycle_rule_enabled + noncurrent_version_expiration_days = var.noncurrent_version_expiration_days + noncurrent_version_transition_days = var.noncurrent_version_transition_days + standard_transition_days = var.standard_transition_days + glacier_transition_days = var.glacier_transition_days + expiration_days = var.expiration_days + traffic_type = var.traffic_type + force_destroy = var.force_destroy + flow_log_enabled = false + + context = module.this.context +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/outputs.tf new file mode 100644 index 000000000..f195f3f81 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/outputs.tf @@ -0,0 +1,9 @@ +output "vpc_flow_logs_bucket_id" { + value = module.flow_logs_s3_bucket.bucket_id + description = "VPC Flow Logs bucket ID" +} + +output "vpc_flow_logs_bucket_arn" { + value = module.flow_logs_s3_bucket.bucket_arn + description = "VPC Flow Logs bucket ARN" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/providers.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/providers.tf new file mode 100644 index 000000000..ef923e10a --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/providers.tf @@ -0,0 +1,19 @@ +provider "aws" { + region = var.region + + # Profile is deprecated in favor of terraform_role_arn. When profiles are not in use, terraform_profile_name is null. + profile = module.iam_roles.terraform_profile_name + + dynamic "assume_role" { + # module.iam_roles.terraform_role_arn may be null, in which case do not assume a role. + for_each = compact([module.iam_roles.terraform_role_arn]) + content { + role_arn = assume_role.value + } + } +} + +module "iam_roles" { + source = "../account-map/modules/iam-roles" + context = module.this.context +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/variables.tf new file mode 100644 index 000000000..6b87353ed --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/variables.tf @@ -0,0 +1,64 @@ +variable "region" { + type = string + description = "AWS Region" +} + +variable "lifecycle_prefix" { + type = string + description = "Prefix filter. Used to manage object lifecycle events" + default = "" +} + +variable "lifecycle_tags" { + type = map(string) + description = "Tags filter. Used to manage object lifecycle events" + default = {} +} + +variable "force_destroy" { + type = bool + description = "A boolean that indicates all objects should be deleted from the bucket so that the bucket can be destroyed without error. These objects are not recoverable" + default = false +} + +variable "lifecycle_rule_enabled" { + type = bool + description = "Enable lifecycle events on this bucket" + default = true +} + +variable "noncurrent_version_expiration_days" { + type = number + description = "Specifies when noncurrent object versions expire" + default = 90 +} + +variable "noncurrent_version_transition_days" { + type = number + description = "Specifies when noncurrent object versions transitions" + default = 30 +} + +variable "standard_transition_days" { + type = number + description = "Number of days to persist in the standard storage tier before moving to the infrequent access tier" + default = 30 +} + +variable "glacier_transition_days" { + type = number + description = "Number of days after which to move the data to the glacier storage tier" + default = 60 +} + +variable "expiration_days" { + type = number + description = "Number of days after which to expunge the objects" + default = 90 +} + +variable "traffic_type" { + type = string + description = "The type of traffic to capture. Valid values: `ACCEPT`, `REJECT`, `ALL`" + default = "ALL" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/versions.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/versions.tf new file mode 100644 index 000000000..cc73ffd35 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.9.0" + } + } +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/component.yaml b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/component.yaml new file mode 100644 index 000000000..498f75293 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/component.yaml @@ -0,0 +1,36 @@ +# 'infra/vpc' component vendoring config + +# 'component.yaml' in the component folder is processed by the 'atmos' commands +# 'atmos vendor pull -c infra/vpc' or 'atmos vendor pull --component infra/vpc' + +apiVersion: atmos/v1 +kind: ComponentVendorConfig +metadata: + name: vpc-vendor-config + description: Source and mixins config for vendoring of 'vpc' component +spec: + source: + # Source 'uri' supports the following protocols: Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP, + # and all URL and archive formats as described in https://github.com/hashicorp/go-getter + # In 'uri', Golang templates are supported https://pkg.go.dev/text/template + # If 'version' is provided, '{{.Version}}' will be replaced with the 'version' value before pulling the files from 'uri' + uri: github.com/cloudposse/terraform-aws-components.git//modules/vpc?ref={{.Version}} + version: 1.372.0 + # Only include the files that match the 'included_paths' patterns + # If 'included_paths' is not specified, all files will be matched except those that match the patterns from 'excluded_paths' + # 'included_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) + # https://en.wikipedia.org/wiki/Glob_(programming) + # https://github.com/bmatcuk/doublestar#patterns + included_paths: + - "**/*.tf" + - "**/*.tfvars" + + # mixins override files from 'source' with the same 'filename' (e.g. 'context.tf' will override 'context.tf' from the 'source') + # mixins are processed in the order they are declared in the list + mixins: + # https://github.com/hashicorp/go-getter/issues/98 + # Mixins 'uri' supports the following protocols: local files (absolute and relative paths), Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP + # - uri: https://raw.githubusercontent.com/cloudposse/terraform-null-label/0.25.0/exports/context.tf + # This mixin `uri` is relative to the current `vpc` folder + - uri: ../../mixins/context.tf + filename: context.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/context.tf new file mode 100644 index 000000000..5e0ef8856 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/context.tf @@ -0,0 +1,279 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.25.0" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + tenant = var.tenant + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + descriptor_formats = var.descriptor_formats + labels_as_tags = var.labels_as_tags + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + tenant = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + descriptor_formats = {} + # Note: we have to use [] instead of null for unset lists due to + # https://github.com/hashicorp/terraform/issues/28137 + # which was not fixed until Terraform 1.0.0, + # but we want the default to be all the labels in `label_order` + # and we want users to be able to prevent all tag generation + # by setting `labels_as_tags` to `[]`, so we need + # a different sentinel to indicate "default" + labels_as_tags = ["unset"] + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" +} + +variable "tenant" { + type = string + default = null + description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" +} + +variable "environment" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +variable "labels_as_tags" { + type = set(string) + default = ["default"] + description = <<-EOT + Set of labels (ID elements) to include as tags in the `tags` output. + Default is to include all labels. + Tags with empty values will not be included in the `tags` output. + Set to `[]` to suppress all generated tags. + **Notes:** + The value of the `name` tag, if included, will be the `id`, not the `name`. + Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be + changed in later chained modules. Attempts to change it will be silently ignored. + EOT +} + +variable "tags" { + type = map(string) + default = {} + description = <<-EOT + Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). + Neither the tag keys nor the tag values will be modified by this module. + EOT +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = <<-EOT + Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. + This is for some rare cases where resources want additional configuration of tags + and therefore take a list of maps with tag key, value, and additional configuration. + EOT +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "descriptor_formats" { + type = any + default = {} + description = <<-EOT + Describe additional descriptors to be output in the `descriptors` output map. + Map of maps. Keys are names of descriptors. Values are maps of the form + `{ + format = string + labels = list(string) + }` + (Type is `any` so the map values can later be enhanced to provide additional options.) + `format` is a Terraform format string to be passed to the `format()` function. + `labels` is a list of labels, in order, to pass to `format()` function. + Label values will be normalized before being passed to `format()` so they will be + identical to how they appear in `id`. + Default is `{}` (`descriptors` output will be empty). + EOT +} + +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/main.tf new file mode 100644 index 000000000..dde5622f2 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/main.tf @@ -0,0 +1,173 @@ +locals { + enabled = module.this.enabled + + nat_eip_aws_shield_protection_enabled = local.enabled && var.nat_eip_aws_shield_protection_enabled + vpc_flow_logs_enabled = local.enabled && var.vpc_flow_logs_enabled + + # The usage of specific kubernetes.io/cluster/* resource tags were required before Kubernetes 1.19, + # but are now deprecated. See https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html + + max_subnet_count = ( + var.max_subnet_count > 0 ? var.max_subnet_count : ( + length(var.availability_zone_ids) > 0 ? length(var.availability_zone_ids) : length(var.availability_zones) + ) + ) + + availability_zones = length(var.availability_zones) > 0 ? ( + (substr( + var.availability_zones[0], + 0, + length(var.region) + ) == var.region) ? var.availability_zones : formatlist("${var.region}%s", var.availability_zones) + ) : var.availability_zones + + short_region = module.utils.region_az_alt_code_maps["to_short"][var.region] + + availability_zone_ids = length(var.availability_zone_ids) > 0 ? ( + (substr( + var.availability_zone_ids[0], + 0, + length(local.short_region) + ) == local.short_region) ? var.availability_zone_ids : formatlist("${local.short_region}%s", var.availability_zone_ids) + ) : var.availability_zone_ids + + # required tags to make ALB ingress work https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html + # https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html + public_subnets_additional_tags = { + (var.subnet_type_tag_key) = "public", + "kubernetes.io/role/elb" = 1, + } + + private_subnets_additional_tags = { + (var.subnet_type_tag_key) = "private", + "kubernetes.io/role/internal-elb" = 1, + } + + gateway_endpoint_map = { for v in var.gateway_vpc_endpoints : v => { + name = v + policy = null + route_table_ids = module.subnets.private_route_table_ids + } } + + # If we use a separate security group for each endpoint interface, + # we will use the interface service name as the key: security_group_ids = [module.endpoint_security_groups[v].id] + # If we use a single security group for all endpoint interfaces, + # we will use local.interface_endpoint_security_group_key as the key. + interface_endpoint_security_group_key = "VPC Endpoint interfaces" + + interface_endpoint_map = { for v in var.interface_vpc_endpoints : v => { + name = v + policy = null + private_dns_enabled = true # Allow applications to use normal service DNS names to access the service + security_group_ids = [module.endpoint_security_groups[local.interface_endpoint_security_group_key].id] + subnet_ids = module.subnets.private_subnet_ids + } } +} + +module "utils" { + source = "cloudposse/utils/aws" + version = "1.4.0" +} + +module "vpc" { + source = "cloudposse/vpc/aws" + version = "2.1.1" + + ipv4_primary_cidr_block = var.ipv4_primary_cidr_block + internet_gateway_enabled = var.public_subnets_enabled + assign_generated_ipv6_cidr_block = var.assign_generated_ipv6_cidr_block + + # Required for DNS resolution of VPC Endpoint interfaces, and generally harmless + # See https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html#vpc-dns-support + dns_hostnames_enabled = true + dns_support_enabled = true + + context = module.this.context +} + +# We could create a security group per endpoint, +# but until we are ready to customize them by service, it is just a waste +# of resources. We use a single security group for all endpoints. +# Security groups can be updated without recreating the endpoint or +# interrupting service, so this is an easy change to make later. +module "endpoint_security_groups" { + for_each = local.enabled && try(length(var.interface_vpc_endpoints), 0) > 0 ? toset([local.interface_endpoint_security_group_key]) : [] + + source = "cloudposse/security-group/aws" + version = "2.2.0" + + create_before_destroy = true + preserve_security_group_id = false + attributes = [each.value] + vpc_id = module.vpc.vpc_id + + rules_map = { + ingress = [{ + key = "vpc_ingress" + type = "ingress" + from_port = 0 + to_port = 65535 + protocol = "-1" # allow ping + cidr_blocks = compact(concat([module.vpc.vpc_cidr_block], module.vpc.additional_cidr_blocks)) + ipv6_cidr_blocks = compact(concat([module.vpc.vpc_ipv6_cidr_block], module.vpc.additional_ipv6_cidr_blocks)) + description = "Ingress from VPC to ${each.value}" + }] + } + + allow_all_egress = true + + context = module.this.context +} + +module "vpc_endpoints" { + source = "cloudposse/vpc/aws//modules/vpc-endpoints" + version = "2.1.0" + + enabled = (length(var.interface_vpc_endpoints) + length(var.gateway_vpc_endpoints)) > 0 + + vpc_id = module.vpc.vpc_id + gateway_vpc_endpoints = local.gateway_endpoint_map + interface_vpc_endpoints = local.interface_endpoint_map + + context = module.this.context +} + +module "subnets" { + source = "cloudposse/dynamic-subnets/aws" + version = "2.4.1" + + availability_zones = local.availability_zones + availability_zone_ids = local.availability_zone_ids + ipv4_cidr_block = [module.vpc.vpc_cidr_block] + ipv4_cidrs = var.ipv4_cidrs + ipv6_enabled = false + igw_id = var.public_subnets_enabled ? [module.vpc.igw_id] : [] + map_public_ip_on_launch = var.map_public_ip_on_launch + max_subnet_count = local.max_subnet_count + nat_gateway_enabled = var.nat_gateway_enabled + nat_instance_enabled = var.nat_instance_enabled + nat_instance_type = var.nat_instance_type + public_subnets_enabled = var.public_subnets_enabled + public_subnets_additional_tags = local.public_subnets_additional_tags + private_subnets_additional_tags = local.private_subnets_additional_tags + vpc_id = module.vpc.vpc_id + + context = module.this.context +} + +data "aws_caller_identity" "current" { + count = local.nat_eip_aws_shield_protection_enabled ? 1 : 0 +} + +data "aws_eip" "eip" { + for_each = local.nat_eip_aws_shield_protection_enabled ? toset(module.subnets.nat_ips) : [] + + public_ip = each.key +} + +resource "aws_shield_protection" "nat_eip_shield_protection" { + for_each = local.nat_eip_aws_shield_protection_enabled ? data.aws_eip.eip : {} + + name = data.aws_eip.eip[each.key].id + resource_arn = "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current[0].account_id}:eip-allocation/${data.aws_eip.eip[each.key].id}" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/outputs.tf new file mode 100644 index 000000000..3a98630a9 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/outputs.tf @@ -0,0 +1,109 @@ +output "public_subnet_ids" { + value = module.subnets.public_subnet_ids + description = "Public subnet IDs" +} + +output "public_subnet_cidrs" { + value = module.subnets.public_subnet_cidrs + description = "Public subnet CIDRs" +} + +output "private_subnet_ids" { + value = module.subnets.private_subnet_ids + description = "Private subnet IDs" +} + +output "private_subnet_cidrs" { + value = module.subnets.private_subnet_cidrs + description = "Private subnet CIDRs" +} + +output "subnets" { + value = { + public : { + ids : module.subnets.public_subnet_ids + cidr : module.subnets.public_subnet_cidrs + } + private : { + ids : module.subnets.private_subnet_ids + cidr : module.subnets.private_subnet_cidrs + } + } + description = "Subnets info map" +} + +output "vpc_default_network_acl_id" { + value = module.vpc.vpc_default_network_acl_id + description = "The ID of the network ACL created by default on VPC creation" +} + +output "vpc_default_security_group_id" { + value = module.vpc.vpc_default_security_group_id + description = "The ID of the security group created by default on VPC creation" +} + +output "vpc_id" { + value = module.vpc.vpc_id + description = "VPC ID" +} + +output "vpc_cidr" { + value = module.vpc.vpc_cidr_block + description = "VPC CIDR" +} + +output "vpc" { + value = { + id : module.vpc.vpc_id + cidr : module.vpc.vpc_cidr_block + subnet_type_tag_key : var.subnet_type_tag_key + } + description = "VPC info map" +} + +output "private_route_table_ids" { + value = module.subnets.private_route_table_ids + description = "Private subnet route table IDs" +} + +output "public_route_table_ids" { + value = module.subnets.public_route_table_ids + description = "Public subnet route table IDs" +} + +output "route_tables" { + value = { + public : { + ids : module.subnets.public_route_table_ids + } + private : { + ids : module.subnets.private_route_table_ids + } + } + description = "Route tables info map" +} + +output "nat_gateway_ids" { + value = module.subnets.nat_gateway_ids + description = "NAT Gateway IDs" +} + +output "nat_instance_ids" { + value = module.subnets.nat_instance_ids + description = "NAT Instance IDs" +} + +output "nat_gateway_public_ips" { + value = module.subnets.nat_gateway_public_ips + description = "NAT Gateway public IPs" +} + +output "max_subnet_count" { + value = local.max_subnet_count + description = "Maximum allowed number of subnets before all subnet CIDRs need to be recomputed" +} + +output "availability_zones" { + description = "List of Availability Zones where subnets were created" + value = local.availability_zones +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/providers.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/providers.tf new file mode 100644 index 000000000..dc58d9a25 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/providers.tf @@ -0,0 +1,3 @@ +provider "aws" { + region = var.region +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/remote-state.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/remote-state.tf new file mode 100644 index 000000000..e785ec5b1 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/remote-state.tf @@ -0,0 +1,20 @@ +module "vpc_flow_logs_bucket" { + count = var.vpc_flow_logs_enabled ? 1 : 0 + + source = "cloudposse/stack-config/yaml//modules/remote-state" + version = "1.5.0" + + # Specify the Atmos component name (defined in YAML stack config files) + # for which to get the remote state outputs + component = var.vpc_flow_logs_bucket_component_name + + # Override the context variables to point to a different Atmos stack if the + # `vpc-flow-logs-bucket` Atmos component is provisioned in another AWS account, OU or region + stage = try(coalesce(var.vpc_flow_logs_bucket_stage_name, module.this.stage), null) + environment = try(coalesce(var.vpc_flow_logs_bucket_environment_name, module.this.environment), null) + tenant = try(coalesce(var.vpc_flow_logs_bucket_tenant_name, module.this.tenant), null) + + # `context` input is a way to provide the information about the stack (using the context + # variables `namespace`, `tenant`, `environment`, and `stage` defined in the stack config) + context = module.this.context +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/variables.tf new file mode 100644 index 000000000..ff1376f6c --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/variables.tf @@ -0,0 +1,165 @@ +variable "region" { + type = string + description = "AWS Region" +} + +variable "availability_zones" { + type = list(string) + description = <<-EOT + List of Availability Zones (AZs) where subnets will be created. Ignored when `availability_zone_ids` is set. + The order of zones in the list ***must be stable*** or else Terraform will continually make changes. + If no AZs are specified, then `max_subnet_count` AZs will be selected in alphabetical order. + If `max_subnet_count > 0` and `length(var.availability_zones) > max_subnet_count`, the list + will be truncated. We recommend setting `availability_zones` and `max_subnet_count` explicitly as constant + (not computed) values for predictability, consistency, and stability. + EOT + default = [] +} + +variable "availability_zone_ids" { + type = list(string) + description = <<-EOT + List of Availability Zones IDs where subnets will be created. Overrides `availability_zones`. + Useful in some regions when using only some AZs and you want to use the same ones across multiple accounts. + EOT + default = [] +} + +variable "ipv4_primary_cidr_block" { + type = string + description = <<-EOT + The primary IPv4 CIDR block for the VPC. + Either `ipv4_primary_cidr_block` or `ipv4_primary_cidr_block_association` must be set, but not both. + EOT + default = null +} + +variable "ipv4_cidrs" { + type = list(object({ + private = list(string) + public = list(string) + })) + description = <<-EOT + Lists of CIDRs to assign to subnets. Order of CIDRs in the lists must not change over time. + Lists may contain more CIDRs than needed. + EOT + default = [] + validation { + condition = length(var.ipv4_cidrs) < 2 + error_message = "Only 1 ipv4_cidrs object can be provided. Lists of CIDRs are passed via the `public` and `private` attributes of the single object." + } +} + +variable "assign_generated_ipv6_cidr_block" { + type = bool + description = "When `true`, assign AWS generated IPv6 CIDR block to the VPC. Conflicts with `ipv6_ipam_pool_id`." + default = false +} + +variable "public_subnets_enabled" { + type = bool + description = <<-EOT + If false, do not create public subnets. + Since NAT gateways and instances must be created in public subnets, these will also not be created when `false`. + EOT + default = true +} + +variable "nat_gateway_enabled" { + type = bool + description = "Flag to enable/disable NAT gateways" + default = true +} + +variable "nat_instance_enabled" { + type = bool + description = "Flag to enable/disable NAT instances" + default = false +} + +variable "nat_instance_type" { + type = string + description = "NAT Instance type" + default = "t3.micro" +} + +variable "map_public_ip_on_launch" { + type = bool + default = true + description = "Instances launched into a public subnet should be assigned a public IP address" +} + +variable "subnet_type_tag_key" { + type = string + description = "Key for subnet type tag to provide information about the type of subnets, e.g. `cpco/subnet/type=private` or `cpcp/subnet/type=public`" +} + +variable "max_subnet_count" { + type = number + default = 0 + description = "Sets the maximum amount of subnets to deploy. 0 will deploy a subnet for every provided availability zone (in `region_availability_zones` variable) within the region" +} + +variable "vpc_flow_logs_enabled" { + type = bool + description = "Enable or disable the VPC Flow Logs" + default = true +} + +variable "vpc_flow_logs_traffic_type" { + type = string + description = "The type of traffic to capture. Valid values: `ACCEPT`, `REJECT`, `ALL`" + default = "ALL" +} + +variable "vpc_flow_logs_log_destination_type" { + type = string + description = "The type of the logging destination. Valid values: `cloud-watch-logs`, `s3`" + default = "s3" +} + +variable "vpc_flow_logs_bucket_component_name" { + type = string + description = "The name of the VPC Flow Logs bucket component" + default = "vpc-flow-logs-bucket" +} + +variable "vpc_flow_logs_bucket_environment_name" { + type = string + description = "The name of the environment where the VPC Flow Logs bucket is provisioned" + default = "" +} + +variable "vpc_flow_logs_bucket_stage_name" { + type = string + description = "The stage (account) name where the VPC Flow Logs bucket is provisioned" + default = "" +} + +variable "vpc_flow_logs_bucket_tenant_name" { + type = string + description = <<-EOT + The name of the tenant where the VPC Flow Logs bucket is provisioned. + + If the `tenant` label is not used, leave this as `null`. + EOT + default = null +} + +variable "nat_eip_aws_shield_protection_enabled" { + type = bool + description = "Enable or disable AWS Shield Advanced protection for NAT EIPs. If set to 'true', a subscription to AWS Shield Advanced must exist in this account." + default = false +} + +variable "gateway_vpc_endpoints" { + type = set(string) + description = "A list of Gateway VPC Endpoints to provision into the VPC. Only valid values are \"dynamodb\" and \"s3\"." + default = [] +} + +variable "interface_vpc_endpoints" { + type = set(string) + description = "A list of Interface VPC Endpoints to provision into the VPC." + default = [] +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/versions.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/versions.tf new file mode 100644 index 000000000..08c4806a4 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.3.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.9.0" + } + utils = { + source = "cloudposse/utils" + version = ">= 1.18.0" + } + } +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/vpc-flow-logs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/vpc-flow-logs.tf new file mode 100644 index 000000000..51bfe7b37 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/vpc-flow-logs.tf @@ -0,0 +1,15 @@ +# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log +# https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html + +resource "aws_flow_log" "default" { + count = local.vpc_flow_logs_enabled ? 1 : 0 + + # Use the remote state output `vpc_flow_logs_bucket_arn` of the `vpc_flow_logs_bucket` component + log_destination = module.vpc_flow_logs_bucket[0].outputs.vpc_flow_logs_bucket_arn + + log_destination_type = var.vpc_flow_logs_log_destination_type + traffic_type = var.vpc_flow_logs_traffic_type + vpc_id = module.vpc.vpc_id + + tags = module.this.tags +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc2/component.yaml b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc2/component.yaml new file mode 100644 index 000000000..4a46b4ca3 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc2/component.yaml @@ -0,0 +1,26 @@ +# This is an example of how to download a Terraform component from an OCI registry (https://opencontainers.org), e.g. AWS Public ECR + +# 'component.yaml' in the component folder is processed by the 'atmos' commands +# 'atmos vendor pull -c infra/vpc2' or 'atmos vendor pull --component infra/vpc2' + +apiVersion: atmos/v1 +kind: ComponentVendorConfig +metadata: + name: stable/aws/vpc + description: Config for vendoring of 'stable/aws/vpc' component +spec: + source: + # Source 'uri' supports the following protocols: OCI (https://opencontainers.org), Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP, + # and all URL and archive formats as described in https://github.com/hashicorp/go-getter + # In 'uri', Golang templates are supported https://pkg.go.dev/text/template + # If 'version' is provided, '{{.Version}}' will be replaced with the 'version' value before pulling the files from 'uri' + # Download the component from the AWS public ECR registry (https://docs.aws.amazon.com/AmazonECR/latest/public/public-registries.html) + uri: "oci://public.ecr.aws/cloudposse/components/terraform/stable/aws/vpc:{{.Version}}" + version: "latest" + # Only include the files that match the 'included_paths' patterns + # If 'included_paths' is not specified, all files will be matched except those that match the patterns from 'excluded_paths' + # 'included_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) + # https://en.wikipedia.org/wiki/Glob_(programming) + # https://github.com/bmatcuk/doublestar#patterns + included_paths: + - "**/*.*" diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/context.tf new file mode 100644 index 000000000..5e0ef8856 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/context.tf @@ -0,0 +1,279 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.25.0" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + tenant = var.tenant + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + descriptor_formats = var.descriptor_formats + labels_as_tags = var.labels_as_tags + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + tenant = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + descriptor_formats = {} + # Note: we have to use [] instead of null for unset lists due to + # https://github.com/hashicorp/terraform/issues/28137 + # which was not fixed until Terraform 1.0.0, + # but we want the default to be all the labels in `label_order` + # and we want users to be able to prevent all tag generation + # by setting `labels_as_tags` to `[]`, so we need + # a different sentinel to indicate "default" + labels_as_tags = ["unset"] + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" +} + +variable "tenant" { + type = string + default = null + description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" +} + +variable "environment" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +variable "labels_as_tags" { + type = set(string) + default = ["default"] + description = <<-EOT + Set of labels (ID elements) to include as tags in the `tags` output. + Default is to include all labels. + Tags with empty values will not be included in the `tags` output. + Set to `[]` to suppress all generated tags. + **Notes:** + The value of the `name` tag, if included, will be the `id`, not the `name`. + Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be + changed in later chained modules. Attempts to change it will be silently ignored. + EOT +} + +variable "tags" { + type = map(string) + default = {} + description = <<-EOT + Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). + Neither the tag keys nor the tag values will be modified by this module. + EOT +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = <<-EOT + Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. + This is for some rare cases where resources want additional configuration of tags + and therefore take a list of maps with tag key, value, and additional configuration. + EOT +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "descriptor_formats" { + type = any + default = {} + description = <<-EOT + Describe additional descriptors to be output in the `descriptors` output map. + Map of maps. Keys are names of descriptors. Values are maps of the form + `{ + format = string + labels = list(string) + }` + (Type is `any` so the map values can later be enhanced to provide additional options.) + `format` is a Terraform format string to be passed to the `format()` function. + `labels` is a list of labels, in order, to pass to `format()` function. + Label values will be normalized before being passed to `format()` so they will be + identical to how they appear in `id`. + Default is `{}` (`descriptors` output will be empty). + EOT +} + +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/introspection.mixin.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/introspection.mixin.tf new file mode 100644 index 000000000..3af29f58d --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/introspection.mixin.tf @@ -0,0 +1,34 @@ +# This mixin is meant to be added to Terraform components in order to append a `Component` tag to all resources in the +# configuration, specifying which component the resources belong to. +# +# It's important to note that all modules and resources within the component then need to use `module.introspection.context` +# and `module.introspection.tags`, respectively, rather than `module.this.context` and `module.this.tags`. +# + +locals { + # Throw an error if lookup fails + check_required_tags = module.this.enabled ? [ + for k in var.required_tags : lookup(module.this.tags, k) + ] : [] +} + +variable "required_tags" { + type = list(string) + description = "List of required tag names" + default = [] +} + +# `introspection` module will contain the additional tags +module "introspection" { + source = "cloudposse/label/null" + version = "0.25.0" + + tags = merge( + var.tags, + { + "Component" = basename(abspath(path.module)) + } + ) + + context = module.this.context +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/context.tf new file mode 100644 index 000000000..5e0ef8856 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/context.tf @@ -0,0 +1,279 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.25.0" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + tenant = var.tenant + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + descriptor_formats = var.descriptor_formats + labels_as_tags = var.labels_as_tags + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + tenant = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + descriptor_formats = {} + # Note: we have to use [] instead of null for unset lists due to + # https://github.com/hashicorp/terraform/issues/28137 + # which was not fixed until Terraform 1.0.0, + # but we want the default to be all the labels in `label_order` + # and we want users to be able to prevent all tag generation + # by setting `labels_as_tags` to `[]`, so we need + # a different sentinel to indicate "default" + labels_as_tags = ["unset"] + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" +} + +variable "tenant" { + type = string + default = null + description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" +} + +variable "environment" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +variable "labels_as_tags" { + type = set(string) + default = ["default"] + description = <<-EOT + Set of labels (ID elements) to include as tags in the `tags` output. + Default is to include all labels. + Tags with empty values will not be included in the `tags` output. + Set to `[]` to suppress all generated tags. + **Notes:** + The value of the `name` tag, if included, will be the `id`, not the `name`. + Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be + changed in later chained modules. Attempts to change it will be silently ignored. + EOT +} + +variable "tags" { + type = map(string) + default = {} + description = <<-EOT + Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). + Neither the tag keys nor the tag values will be modified by this module. + EOT +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = <<-EOT + Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. + This is for some rare cases where resources want additional configuration of tags + and therefore take a list of maps with tag key, value, and additional configuration. + EOT +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "descriptor_formats" { + type = any + default = {} + description = <<-EOT + Describe additional descriptors to be output in the `descriptors` output map. + Map of maps. Keys are names of descriptors. Values are maps of the form + `{ + format = string + labels = list(string) + }` + (Type is `any` so the map values can later be enhanced to provide additional options.) + `format` is a Terraform format string to be passed to the `format()` function. + `labels` is a list of labels, in order, to pass to `format()` function. + Label values will be normalized before being passed to `format()` so they will be + identical to how they appear in `id`. + Default is `{}` (`descriptors` output will be empty). + EOT +} + +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/main.tf new file mode 100644 index 000000000..3d0c15f53 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/main.tf @@ -0,0 +1,6 @@ +module "test_label" { + source = "cloudposse/label/null" + version = "0.25.0" + + context = module.this.context +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/outputs.tf new file mode 100644 index 000000000..2af5e9cef --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/outputs.tf @@ -0,0 +1,22 @@ +output "test_label_id" { + value = module.test_label.id + description = "Test label ID" +} + +output "test_list" { + value = [ + "list_item_1", + "list_item_2", + "list_item_3" + ] + description = "Test list" +} + +output "test_map" { + value = { + a = 1, + b = 2, + c = 3 + } + description = "Test map" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/context.tf new file mode 100644 index 000000000..5e0ef8856 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/context.tf @@ -0,0 +1,279 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.25.0" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + tenant = var.tenant + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + descriptor_formats = var.descriptor_formats + labels_as_tags = var.labels_as_tags + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + tenant = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + descriptor_formats = {} + # Note: we have to use [] instead of null for unset lists due to + # https://github.com/hashicorp/terraform/issues/28137 + # which was not fixed until Terraform 1.0.0, + # but we want the default to be all the labels in `label_order` + # and we want users to be able to prevent all tag generation + # by setting `labels_as_tags` to `[]`, so we need + # a different sentinel to indicate "default" + labels_as_tags = ["unset"] + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" +} + +variable "tenant" { + type = string + default = null + description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" +} + +variable "environment" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +variable "labels_as_tags" { + type = set(string) + default = ["default"] + description = <<-EOT + Set of labels (ID elements) to include as tags in the `tags` output. + Default is to include all labels. + Tags with empty values will not be included in the `tags` output. + Set to `[]` to suppress all generated tags. + **Notes:** + The value of the `name` tag, if included, will be the `id`, not the `name`. + Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be + changed in later chained modules. Attempts to change it will be silently ignored. + EOT +} + +variable "tags" { + type = map(string) + default = {} + description = <<-EOT + Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). + Neither the tag keys nor the tag values will be modified by this module. + EOT +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = <<-EOT + Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. + This is for some rare cases where resources want additional configuration of tags + and therefore take a list of maps with tag key, value, and additional configuration. + EOT +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "descriptor_formats" { + type = any + default = {} + description = <<-EOT + Describe additional descriptors to be output in the `descriptors` output map. + Map of maps. Keys are names of descriptors. Values are maps of the form + `{ + format = string + labels = list(string) + }` + (Type is `any` so the map values can later be enhanced to provide additional options.) + `format` is a Terraform format string to be passed to the `format()` function. + `labels` is a list of labels, in order, to pass to `format()` function. + Label values will be normalized before being passed to `format()` so they will be + identical to how they appear in `id`. + Default is `{}` (`descriptors` output will be empty). + EOT +} + +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/outputs.tf new file mode 100644 index 000000000..76c897196 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/outputs.tf @@ -0,0 +1,14 @@ +output "region" { + value = var.region + description = "Test label ID" +} + +output "test_label_id" { + value = var.test_label_id + description = "Test label ID" +} + +output "test_label_id_2" { + value = var.test_label_id_2 + description = "Test label ID 2" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/variables.tf new file mode 100644 index 000000000..cfbc1e4f9 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/variables.tf @@ -0,0 +1,14 @@ +variable "region" { + type = string + description = "Region" +} + +variable "test_label_id" { + type = string + description = "Test label ID" +} + +variable "test_label_id_2" { + type = string + description = "Test label ID 2" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/context.tf new file mode 100644 index 000000000..5e0ef8856 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/context.tf @@ -0,0 +1,279 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.25.0" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + tenant = var.tenant + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + descriptor_formats = var.descriptor_formats + labels_as_tags = var.labels_as_tags + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + tenant = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + descriptor_formats = {} + # Note: we have to use [] instead of null for unset lists due to + # https://github.com/hashicorp/terraform/issues/28137 + # which was not fixed until Terraform 1.0.0, + # but we want the default to be all the labels in `label_order` + # and we want users to be able to prevent all tag generation + # by setting `labels_as_tags` to `[]`, so we need + # a different sentinel to indicate "default" + labels_as_tags = ["unset"] + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" +} + +variable "tenant" { + type = string + default = null + description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" +} + +variable "environment" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +variable "labels_as_tags" { + type = set(string) + default = ["default"] + description = <<-EOT + Set of labels (ID elements) to include as tags in the `tags` output. + Default is to include all labels. + Tags with empty values will not be included in the `tags` output. + Set to `[]` to suppress all generated tags. + **Notes:** + The value of the `name` tag, if included, will be the `id`, not the `name`. + Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be + changed in later chained modules. Attempts to change it will be silently ignored. + EOT +} + +variable "tags" { + type = map(string) + default = {} + description = <<-EOT + Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). + Neither the tag keys nor the tag values will be modified by this module. + EOT +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = <<-EOT + Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. + This is for some rare cases where resources want additional configuration of tags + and therefore take a list of maps with tag key, value, and additional configuration. + EOT +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "descriptor_formats" { + type = any + default = {} + description = <<-EOT + Describe additional descriptors to be output in the `descriptors` output map. + Map of maps. Keys are names of descriptors. Values are maps of the form + `{ + format = string + labels = list(string) + }` + (Type is `any` so the map values can later be enhanced to provide additional options.) + `format` is a Terraform format string to be passed to the `format()` function. + `labels` is a list of labels, in order, to pass to `format()` function. + Label values will be normalized before being passed to `format()` so they will be + identical to how they appear in `id`. + Default is `{}` (`descriptors` output will be empty). + EOT +} + +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/main.tf new file mode 100644 index 000000000..6ad57234b --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/main.tf @@ -0,0 +1,17 @@ +module "service_1_label" { + source = "cloudposse/label/null" + version = "0.25.0" + + name = var.service_1_name + + context = module.this.context +} + +module "service_2_label" { + source = "cloudposse/label/null" + version = "0.25.0" + + name = var.service_2_name + + context = module.this.context +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/outputs.tf new file mode 100644 index 000000000..d87b49b2d --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/outputs.tf @@ -0,0 +1,9 @@ +output "service_1_id" { + value = module.service_1_label.id + description = "Service 1 ID" +} + +output "service_2_id" { + value = module.service_2_label.id + description = "Service 2 ID" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/variables.tf new file mode 100644 index 000000000..90c60dc3a --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/variables.tf @@ -0,0 +1,38 @@ +variable "region" { + type = string + description = "Region" +} + +variable "service_1_name" { + type = string + description = "Service 1 name" +} + +variable "service_1_list" { + type = list(string) + description = "Service 1 list" + default = [] +} + +variable "service_1_map" { + type = map(string) + description = "Service 1 map" + default = {} +} + +variable "service_2_name" { + type = string + description = "Service 2 name" +} + +variable "service_2_list" { + type = list(string) + description = "Service 2 list" + default = [] +} + +variable "service_2_map" { + type = map(string) + description = "Service 2 map" + default = {} +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/versions.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/versions.tf new file mode 100644 index 000000000..429c0b36d --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/versions.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 1.0.0" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/context.tf new file mode 100644 index 000000000..5e0ef8856 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/context.tf @@ -0,0 +1,279 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.25.0" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + tenant = var.tenant + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + descriptor_formats = var.descriptor_formats + labels_as_tags = var.labels_as_tags + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + tenant = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + descriptor_formats = {} + # Note: we have to use [] instead of null for unset lists due to + # https://github.com/hashicorp/terraform/issues/28137 + # which was not fixed until Terraform 1.0.0, + # but we want the default to be all the labels in `label_order` + # and we want users to be able to prevent all tag generation + # by setting `labels_as_tags` to `[]`, so we need + # a different sentinel to indicate "default" + labels_as_tags = ["unset"] + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" +} + +variable "tenant" { + type = string + default = null + description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" +} + +variable "environment" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +variable "labels_as_tags" { + type = set(string) + default = ["default"] + description = <<-EOT + Set of labels (ID elements) to include as tags in the `tags` output. + Default is to include all labels. + Tags with empty values will not be included in the `tags` output. + Set to `[]` to suppress all generated tags. + **Notes:** + The value of the `name` tag, if included, will be the `id`, not the `name`. + Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be + changed in later chained modules. Attempts to change it will be silently ignored. + EOT +} + +variable "tags" { + type = map(string) + default = {} + description = <<-EOT + Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). + Neither the tag keys nor the tag values will be modified by this module. + EOT +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = <<-EOT + Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. + This is for some rare cases where resources want additional configuration of tags + and therefore take a list of maps with tag key, value, and additional configuration. + EOT +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "descriptor_formats" { + type = any + default = {} + description = <<-EOT + Describe additional descriptors to be output in the `descriptors` output map. + Map of maps. Keys are names of descriptors. Values are maps of the form + `{ + format = string + labels = list(string) + }` + (Type is `any` so the map values can later be enhanced to provide additional options.) + `format` is a Terraform format string to be passed to the `format()` function. + `labels` is a list of labels, in order, to pass to `format()` function. + Label values will be normalized before being passed to `format()` so they will be + identical to how they appear in `id`. + Default is `{}` (`descriptors` output will be empty). + EOT +} + +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/main.tf new file mode 100644 index 000000000..6ad57234b --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/main.tf @@ -0,0 +1,17 @@ +module "service_1_label" { + source = "cloudposse/label/null" + version = "0.25.0" + + name = var.service_1_name + + context = module.this.context +} + +module "service_2_label" { + source = "cloudposse/label/null" + version = "0.25.0" + + name = var.service_2_name + + context = module.this.context +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/outputs.tf new file mode 100644 index 000000000..d87b49b2d --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/outputs.tf @@ -0,0 +1,9 @@ +output "service_1_id" { + value = module.service_1_label.id + description = "Service 1 ID" +} + +output "service_2_id" { + value = module.service_2_label.id + description = "Service 2 ID" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/variables.tf new file mode 100644 index 000000000..90c60dc3a --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/variables.tf @@ -0,0 +1,38 @@ +variable "region" { + type = string + description = "Region" +} + +variable "service_1_name" { + type = string + description = "Service 1 name" +} + +variable "service_1_list" { + type = list(string) + description = "Service 1 list" + default = [] +} + +variable "service_1_map" { + type = map(string) + description = "Service 1 map" + default = {} +} + +variable "service_2_name" { + type = string + description = "Service 2 name" +} + +variable "service_2_list" { + type = list(string) + description = "Service 2 list" + default = [] +} + +variable "service_2_map" { + type = map(string) + description = "Service 2 map" + default = {} +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/versions.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/versions.tf new file mode 100644 index 000000000..429c0b36d --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/versions.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 1.0.0" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/context.tf new file mode 100644 index 000000000..5e0ef8856 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/context.tf @@ -0,0 +1,279 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.25.0" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + tenant = var.tenant + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + descriptor_formats = var.descriptor_formats + labels_as_tags = var.labels_as_tags + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + tenant = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + descriptor_formats = {} + # Note: we have to use [] instead of null for unset lists due to + # https://github.com/hashicorp/terraform/issues/28137 + # which was not fixed until Terraform 1.0.0, + # but we want the default to be all the labels in `label_order` + # and we want users to be able to prevent all tag generation + # by setting `labels_as_tags` to `[]`, so we need + # a different sentinel to indicate "default" + labels_as_tags = ["unset"] + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" +} + +variable "tenant" { + type = string + default = null + description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" +} + +variable "environment" { + type = string + default = null + description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = <<-EOT + ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. + This is the only ID element not also included as a `tag`. + The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. + EOT +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between ID elements. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = <<-EOT + ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, + in the order they appear in the list. New attributes are appended to the + end of the list. The elements of the list are joined by the `delimiter` + and treated as a single ID element. + EOT +} + +variable "labels_as_tags" { + type = set(string) + default = ["default"] + description = <<-EOT + Set of labels (ID elements) to include as tags in the `tags` output. + Default is to include all labels. + Tags with empty values will not be included in the `tags` output. + Set to `[]` to suppress all generated tags. + **Notes:** + The value of the `name` tag, if included, will be the `id`, not the `name`. + Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be + changed in later chained modules. Attempts to change it will be silently ignored. + EOT +} + +variable "tags" { + type = map(string) + default = {} + description = <<-EOT + Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). + Neither the tag keys nor the tag values will be modified by this module. + EOT +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = <<-EOT + Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. + This is for some rare cases where resources want additional configuration of tags + and therefore take a list of maps with tag key, value, and additional configuration. + EOT +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The order in which the labels (ID elements) appear in the `id`. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Terraform regular expression (regex) string. + Characters matching the regex will be removed from the ID elements. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for keep the existing setting, which defaults to `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of the `tags` keys (label names) for tags generated by this module. + Does not affect keys of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + Controls the letter case of ID elements (labels) as included in `id`, + set as tag values, and output by this module individually. + Does not affect values of tags passed in via the `tags` input. + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "descriptor_formats" { + type = any + default = {} + description = <<-EOT + Describe additional descriptors to be output in the `descriptors` output map. + Map of maps. Keys are names of descriptors. Values are maps of the form + `{ + format = string + labels = list(string) + }` + (Type is `any` so the map values can later be enhanced to provide additional options.) + `format` is a Terraform format string to be passed to the `format()` function. + `labels` is a list of labels, in order, to pass to `format()` function. + Label values will be normalized before being passed to `format()` so they will be + identical to how they appear in `id`. + Default is `{}` (`descriptors` output will be empty). + EOT +} + +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/main.tf new file mode 100644 index 000000000..3496a4e33 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/main.tf @@ -0,0 +1,16 @@ +module "service_1_label" { + source = "cloudposse/label/null" + version = "0.25.0" + + name = var.service_1_name + + context = module.this.context +} + +module "service_2_label" { + source = "../../../modules/label" + + name = var.service_2_name + + context = module.this.context +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/outputs.tf new file mode 100644 index 000000000..ae890ae82 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/outputs.tf @@ -0,0 +1,9 @@ +output "service_1_id" { + value = module.service_1_label.id + description = "Service 1 ID" +} + +output "service_2_id" { + value = module.service_2_label.label.id + description = "Service 2 ID" +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/policies/policy1.rego b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/policies/policy1.rego new file mode 100644 index 000000000..6fa6fec89 --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/policies/policy1.rego @@ -0,0 +1,5 @@ +package atmos + +allow { + true +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/variables.tf new file mode 100644 index 000000000..90c60dc3a --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/variables.tf @@ -0,0 +1,38 @@ +variable "region" { + type = string + description = "Region" +} + +variable "service_1_name" { + type = string + description = "Service 1 name" +} + +variable "service_1_list" { + type = list(string) + description = "Service 1 list" + default = [] +} + +variable "service_1_map" { + type = map(string) + description = "Service 1 map" + default = {} +} + +variable "service_2_name" { + type = string + description = "Service 2 name" +} + +variable "service_2_list" { + type = list(string) + description = "Service 2 list" + default = [] +} + +variable "service_2_map" { + type = map(string) + description = "Service 2 map" + default = {} +} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/versions.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/versions.tf new file mode 100644 index 000000000..429c0b36d --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/versions.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 1.0.0" +} diff --git a/tests/fixtures/scenarios/editorconfig/vendor.yaml b/tests/fixtures/scenarios/editorconfig/vendor.yaml new file mode 100644 index 000000000..17bba24fc --- /dev/null +++ b/tests/fixtures/scenarios/editorconfig/vendor.yaml @@ -0,0 +1,102 @@ +# atmos vendor pull +# atmos vendor pull --component vpc-mixin-1 +# atmos vendor pull -c vpc-mixin-2 +# atmos vendor pull -c vpc-mixin-3 +# atmos vendor pull -c vpc-mixin-4 +# atmos vendor pull --tags test +# atmos vendor pull --tags networking,storage + +apiVersion: atmos/v1 +kind: AtmosVendorConfig +metadata: + name: example-vendor-config + description: Atmos vendoring manifest +spec: + # `imports` or `sources` (or both) must be defined in a vendoring manifest + imports: + - "vendor/vendor2" + - "vendor/vendor3.yaml" + + sources: + # `source` supports the following protocols: local paths (absolute and relative), OCI (https://opencontainers.org), + # Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP, + # and all URL and archive formats as described in https://github.com/hashicorp/go-getter. + # In 'source', Golang templates are supported https://pkg.go.dev/text/template. + # If 'version' is provided, '{{.Version}}' will be replaced with the 'version' value before pulling the files from 'source'. + # Download the component from the AWS public ECR registry (https://docs.aws.amazon.com/AmazonECR/latest/public/public-registries.html). + - component: "vpc" + source: "oci://public.ecr.aws/cloudposse/components/terraform/stable/aws/vpc:{{.Version}}" + version: "latest" + targets: + - "components/terraform/infra/vpc3" + # Only include the files that match the 'included_paths' patterns. + # If 'included_paths' is not specified, all files will be matched except those that match the patterns from 'excluded_paths'. + # 'included_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported). + # https://en.wikipedia.org/wiki/Glob_(programming) + # https://github.com/bmatcuk/doublestar#patterns + included_paths: + - "**/*.tf" + - "**/*.tfvars" + - "**/*.md" + # Tags can be used to vendor component that have the specific tags + # `atmos vendor pull --tags test` + # Refer to https://atmos.tools/cli/commands/vendor/pull + tags: + - test + - networking + - component: "vpc-flow-logs-bucket" + source: "github.com/cloudposse/terraform-aws-components.git//modules/vpc-flow-logs-bucket?ref={{.Version}}" + version: "1.323.0" + targets: + - "components/terraform/infra/vpc-flow-logs-bucket/{{.Version}}" + excluded_paths: + - "**/*.yaml" + - "**/*.yml" + # Tags can be used to vendor component that have the specific tags + # `atmos vendor pull --tags networking,storage` + # Refer to https://atmos.tools/cli/commands/vendor/pull + tags: + - test + - storage + - component: "vpc-mixin-1" + source: "https://raw.githubusercontent.com/cloudposse/terraform-null-label/0.25.0/exports/context.tf" + targets: + - "components/terraform/infra/vpc3" + # Tags can be used to vendor component that have the specific tags + # `atmos vendor pull --tags test` + # Refer to https://atmos.tools/cli/commands/vendor/pull + tags: + - test + - component: "vpc-mixin-2" + # Copy a local file into a local folder (keeping the same file name) + # This `source` is relative to the current folder + source: "components/terraform/mixins/context.tf" + targets: + - "components/terraform/infra/vpc3" + # Tags can be used to vendor component that have the specific tags + # `atmos vendor pull --tags test` + # Refer to https://atmos.tools/cli/commands/vendor/pull + tags: + - test + - component: "vpc-mixin-3" + # Copy a local folder into a local folder + # This `source` is relative to the current folder + source: "components/terraform/mixins" + targets: + - "components/terraform/infra/vpc3" + # Tags can be used to vendor component that have the specific tags + # `atmos vendor pull --tags test` + # Refer to https://atmos.tools/cli/commands/vendor/pull + tags: + - test + - component: "vpc-mixin-4" + # Copy a local file into a local file with a different file name + # This `source` is relative to the current folder + source: "components/terraform/mixins/context.tf" + targets: + - "components/terraform/infra/vpc3/context-copy.tf" + # Tags can be used to vendor component that have the specific tags + # `atmos vendor pull --tags test` + # Refer to https://atmos.tools/cli/commands/vendor/pull + tags: + - test diff --git a/tests/test-cases/editorconfig.yaml b/tests/test-cases/validate-editorconfig.yaml similarity index 87% rename from tests/test-cases/editorconfig.yaml rename to tests/test-cases/validate-editorconfig.yaml index b5874f9b4..9af4bdd8d 100644 --- a/tests/test-cases/editorconfig.yaml +++ b/tests/test-cases/validate-editorconfig.yaml @@ -16,12 +16,12 @@ tests: - name: atmos validate editorconfig error enabled: true description: "Ensure atmos CLI gives error during validation based on editorconfig" - workdir: "../tests/fixtures" + workdir: "../tests/fixtures/scenarios/editorconfig" command: "atmos" args: - "validate" - "editorconfig" expect: stderr: - - "18 errors found" + - "16 errors found" exit_code: 1 From 68307908a12f45afebd3e39392ad5c08527af5de Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Wed, 15 Jan 2025 22:18:56 +0100 Subject: [PATCH 31/32] Removed files to minimal --- .../infra/account-map/component.yaml | 40 --- .../infra/vpc-flow-logs-bucket/component.yaml | 52 ---- .../infra/vpc-flow-logs-bucket/main.tf | 18 -- .../infra/vpc-flow-logs-bucket/outputs.tf | 9 - .../infra/vpc-flow-logs-bucket/providers.tf | 19 -- .../infra/vpc-flow-logs-bucket/variables.tf | 64 ---- .../infra/vpc-flow-logs-bucket/versions.tf | 10 - .../terraform/infra/vpc/component.yaml | 36 --- .../components/terraform/infra/vpc/context.tf | 279 ------------------ .../components/terraform/infra/vpc/main.tf | 173 ----------- .../components/terraform/infra/vpc/outputs.tf | 109 ------- .../terraform/infra/vpc/providers.tf | 3 - .../terraform/infra/vpc/remote-state.tf | 20 -- .../terraform/infra/vpc/variables.tf | 165 ----------- .../terraform/infra/vpc/versions.tf | 14 - .../terraform/infra/vpc/vpc-flow-logs.tf | 15 - .../terraform/infra/vpc2/component.yaml | 26 -- .../components/terraform/mixins/context.tf | 279 ------------------ .../terraform/mixins/introspection.mixin.tf | 34 --- .../test/template-functions-test/context.tf | 279 ------------------ .../test/template-functions-test/main.tf | 6 - .../test/template-functions-test/outputs.tf | 22 -- .../test/template-functions-test2/context.tf | 279 ------------------ .../test/template-functions-test2/outputs.tf | 14 - .../template-functions-test2/variables.tf | 14 - .../terraform/test/test-component/context.tf | 279 ------------------ .../terraform/test/test-component/main.tf | 17 -- .../terraform/test/test-component/outputs.tf | 9 - .../test/test-component/variables.tf | 38 --- .../terraform/test/test-component/versions.tf | 3 - .../test/test2/test-component-2/context.tf | 279 ------------------ .../test/test2/test-component-2/main.tf | 17 -- .../test/test2/test-component-2/outputs.tf | 9 - .../test/test2/test-component-2/variables.tf | 38 --- .../test/test2/test-component-2/versions.tf | 3 - .../terraform/top-level-component1/context.tf | 279 ------------------ .../terraform/top-level-component1/main.tf | 16 - .../terraform/top-level-component1/outputs.tf | 9 - .../policies/policy1.rego | 5 - .../top-level-component1/variables.tf | 38 --- .../top-level-component1/versions.tf | 3 - .../vpc-flow-logs-bucket => }/context.tf | 0 tests/test-cases/validate-editorconfig.yaml | 2 +- 43 files changed, 1 insertion(+), 3022 deletions(-) delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/account-map/component.yaml delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/component.yaml delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/main.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/outputs.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/providers.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/variables.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/versions.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/component.yaml delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/context.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/main.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/outputs.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/providers.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/remote-state.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/variables.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/versions.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/vpc-flow-logs.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc2/component.yaml delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/mixins/context.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/mixins/introspection.mixin.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/context.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/main.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/outputs.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/context.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/outputs.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/variables.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/context.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/main.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/outputs.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/variables.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/versions.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/context.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/main.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/outputs.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/variables.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/versions.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/context.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/main.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/outputs.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/policies/policy1.rego delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/variables.tf delete mode 100644 tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/versions.tf rename tests/fixtures/scenarios/editorconfig/{components/terraform/infra/vpc-flow-logs-bucket => }/context.tf (100%) diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/account-map/component.yaml b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/account-map/component.yaml deleted file mode 100644 index 1240d1d0d..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/account-map/component.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# 'infra/account-map' component vendoring config - -apiVersion: atmos/v1 -kind: ComponentVendorConfig -metadata: - name: account-map-vendor-config - description: Source and mixins config for vendoring of 'vpc-flow-logs-bucket' component -spec: - source: - # Source 'uri' supports the following protocols: Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP, - # and all URL and archive formats as described in https://github.com/hashicorp/go-getter - # In 'uri', Golang templates are supported https://pkg.go.dev/text/template - # If 'version' is provided, '{{.Version}}' will be replaced with the 'version' value before pulling the files from 'uri' - uri: github.com/cloudposse/terraform-aws-components.git//modules/account-map?ref={{.Version}} - version: 1.372.0 - # Only include the files that match the 'included_paths' patterns - # If 'included_paths' is not specified, all files will be matched except those that match the patterns from 'excluded_paths' - # 'included_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) - # https://en.wikipedia.org/wiki/Glob_(programming) - # https://github.com/bmatcuk/doublestar#patterns - included_paths: - # include '.tf', '.tfvars' and '.md' files from the root folder - - "**/*.tf" - - "**/*.tfvars" - - "**/*.md" - # include the 'modules' folder and all sub-folders - # note that if you don't include the folders, the files in the folders will not be included - - "**/modules/**" - # include '.tf', '.tfvars' and '.md' files from the 'modules' folder and all sub-folders - - "**/modules/**/*.tf" - - "**/modules/**/*.tfvars" - - "**/modules/**/*.md" - # Exclude the files that match any of the 'excluded_paths' patterns - # Note that we are excluding 'context.tf' since a newer version of it will be downloaded using 'mixins' - # 'excluded_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) - excluded_paths: [] - - # mixins override files from 'source' with the same 'filename' (e.g. 'context.tf' will override 'context.tf' from the 'source') - # mixins are processed in the order they are declared in the list - mixins: [] diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/component.yaml b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/component.yaml deleted file mode 100644 index f7942287f..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/component.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# 'infra/vpc-flow-logs-bucket' component vendoring config - -# 'component.yaml' in the component folder is processed by the 'atmos' commands -# 'atmos vendor pull -c infra/vpc-flow-logs-bucket' or 'atmos vendor pull --component infra/vpc-flow-logs-bucket' - -# > atmos vendor pull -c infra/vpc-flow-logs-bucket -# Pulling sources for the component 'infra/vpc-flow-logs-bucket' from 'github.com/cloudposse/terraform-aws-components.git//modules/vpc-flow-logs-bucket?ref=0.194.0' -# into 'tests/fixtures/scenarios/complete/components/terraform/infra/vpc-flow-logs-bucket' -# -# Including the file 'README.md' since it matches the '**/*.md' pattern from 'included_paths' -# Excluding the file 'context.tf' since it matches the '**/context.tf' pattern from 'excluded_paths' -# Including the file 'default.auto.tfvars' since it matches the '**/*.tfvars' pattern from 'included_paths' -# Including the file 'main.tf' since it matches the '**/*.tf' pattern from 'included_paths' -# Including the file 'outputs.tf' since it matches the '**/*.tf' pattern from 'included_paths' -# Including the file 'providers.tf' since it matches the '**/*.tf' pattern from 'included_paths' -# Including the file 'variables.tf' since it matches the '**/*.tf' pattern from 'included_paths' -# Including the file 'versions.tf' since it matches the '**/*.tf' pattern from 'included_paths' -# -# Pulling the mixin 'https://raw.githubusercontent.com/cloudposse/terraform-null-label/0.25.0/exports/context.tf' -# for the component 'infra/vpc-flow-logs-bucket' into 'tests/fixtures/scenarios/complete/components/terraform/infra/vpc-flow-logs-bucket' -# Pulling the mixin 'https://raw.githubusercontent.com/cloudposse/terraform-aws-components/0.194.0/modules/datadog-agent/introspection.mixin.tf' -# for the component 'infra/vpc-flow-logs-bucket' into 'tests/fixtures/scenarios/complete/components/terraform/infra/vpc-flow-logs-bucket' - -apiVersion: atmos/v1 -kind: ComponentVendorConfig -metadata: - name: vpc-flow-logs-bucket-vendor-config - description: Source and mixins config for vendoring of 'vpc-flow-logs-bucket' component -spec: - source: - # Source 'uri' supports the following protocols: Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP, - # and all URL and archive formats as described in https://github.com/hashicorp/go-getter - # In 'uri', Golang templates are supported https://pkg.go.dev/text/template - # If 'version' is provided, '{{.Version}}' will be replaced with the 'version' value before pulling the files from 'uri' - uri: github.com/cloudposse/terraform-aws-components.git//modules/vpc-flow-logs-bucket?ref={{.Version}} - version: 1.372.0 - # Only include the files that match the 'included_paths' patterns - # If 'included_paths' is not specified, all files will be matched except those that match the patterns from 'excluded_paths' - # 'included_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) - # https://en.wikipedia.org/wiki/Glob_(programming) - # https://github.com/bmatcuk/doublestar#patterns - included_paths: - - "**/*.tf" - # Exclude the files that match any of the 'excluded_paths' patterns - # Note that we are excluding 'context.tf' since a newer version of it will be downloaded using 'mixins' - # 'excluded_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) - excluded_paths: - - "**/context.tf" - - # mixins override files from 'source' with the same 'filename' (e.g. 'context.tf' will override 'context.tf' from the 'source') - # mixins are processed in the order they are declared in the list - mixins: [] diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/main.tf deleted file mode 100644 index 88eaa98fe..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/main.tf +++ /dev/null @@ -1,18 +0,0 @@ -module "flow_logs_s3_bucket" { - source = "cloudposse/vpc-flow-logs-s3-bucket/aws" - version = "1.0.1" - - lifecycle_prefix = var.lifecycle_prefix - lifecycle_tags = var.lifecycle_tags - lifecycle_rule_enabled = var.lifecycle_rule_enabled - noncurrent_version_expiration_days = var.noncurrent_version_expiration_days - noncurrent_version_transition_days = var.noncurrent_version_transition_days - standard_transition_days = var.standard_transition_days - glacier_transition_days = var.glacier_transition_days - expiration_days = var.expiration_days - traffic_type = var.traffic_type - force_destroy = var.force_destroy - flow_log_enabled = false - - context = module.this.context -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/outputs.tf deleted file mode 100644 index f195f3f81..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/outputs.tf +++ /dev/null @@ -1,9 +0,0 @@ -output "vpc_flow_logs_bucket_id" { - value = module.flow_logs_s3_bucket.bucket_id - description = "VPC Flow Logs bucket ID" -} - -output "vpc_flow_logs_bucket_arn" { - value = module.flow_logs_s3_bucket.bucket_arn - description = "VPC Flow Logs bucket ARN" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/providers.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/providers.tf deleted file mode 100644 index ef923e10a..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/providers.tf +++ /dev/null @@ -1,19 +0,0 @@ -provider "aws" { - region = var.region - - # Profile is deprecated in favor of terraform_role_arn. When profiles are not in use, terraform_profile_name is null. - profile = module.iam_roles.terraform_profile_name - - dynamic "assume_role" { - # module.iam_roles.terraform_role_arn may be null, in which case do not assume a role. - for_each = compact([module.iam_roles.terraform_role_arn]) - content { - role_arn = assume_role.value - } - } -} - -module "iam_roles" { - source = "../account-map/modules/iam-roles" - context = module.this.context -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/variables.tf deleted file mode 100644 index 6b87353ed..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/variables.tf +++ /dev/null @@ -1,64 +0,0 @@ -variable "region" { - type = string - description = "AWS Region" -} - -variable "lifecycle_prefix" { - type = string - description = "Prefix filter. Used to manage object lifecycle events" - default = "" -} - -variable "lifecycle_tags" { - type = map(string) - description = "Tags filter. Used to manage object lifecycle events" - default = {} -} - -variable "force_destroy" { - type = bool - description = "A boolean that indicates all objects should be deleted from the bucket so that the bucket can be destroyed without error. These objects are not recoverable" - default = false -} - -variable "lifecycle_rule_enabled" { - type = bool - description = "Enable lifecycle events on this bucket" - default = true -} - -variable "noncurrent_version_expiration_days" { - type = number - description = "Specifies when noncurrent object versions expire" - default = 90 -} - -variable "noncurrent_version_transition_days" { - type = number - description = "Specifies when noncurrent object versions transitions" - default = 30 -} - -variable "standard_transition_days" { - type = number - description = "Number of days to persist in the standard storage tier before moving to the infrequent access tier" - default = 30 -} - -variable "glacier_transition_days" { - type = number - description = "Number of days after which to move the data to the glacier storage tier" - default = 60 -} - -variable "expiration_days" { - type = number - description = "Number of days after which to expunge the objects" - default = 90 -} - -variable "traffic_type" { - type = string - description = "The type of traffic to capture. Valid values: `ACCEPT`, `REJECT`, `ALL`" - default = "ALL" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/versions.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/versions.tf deleted file mode 100644 index cc73ffd35..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.0.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 4.9.0" - } - } -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/component.yaml b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/component.yaml deleted file mode 100644 index 498f75293..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/component.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# 'infra/vpc' component vendoring config - -# 'component.yaml' in the component folder is processed by the 'atmos' commands -# 'atmos vendor pull -c infra/vpc' or 'atmos vendor pull --component infra/vpc' - -apiVersion: atmos/v1 -kind: ComponentVendorConfig -metadata: - name: vpc-vendor-config - description: Source and mixins config for vendoring of 'vpc' component -spec: - source: - # Source 'uri' supports the following protocols: Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP, - # and all URL and archive formats as described in https://github.com/hashicorp/go-getter - # In 'uri', Golang templates are supported https://pkg.go.dev/text/template - # If 'version' is provided, '{{.Version}}' will be replaced with the 'version' value before pulling the files from 'uri' - uri: github.com/cloudposse/terraform-aws-components.git//modules/vpc?ref={{.Version}} - version: 1.372.0 - # Only include the files that match the 'included_paths' patterns - # If 'included_paths' is not specified, all files will be matched except those that match the patterns from 'excluded_paths' - # 'included_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) - # https://en.wikipedia.org/wiki/Glob_(programming) - # https://github.com/bmatcuk/doublestar#patterns - included_paths: - - "**/*.tf" - - "**/*.tfvars" - - # mixins override files from 'source' with the same 'filename' (e.g. 'context.tf' will override 'context.tf' from the 'source') - # mixins are processed in the order they are declared in the list - mixins: - # https://github.com/hashicorp/go-getter/issues/98 - # Mixins 'uri' supports the following protocols: local files (absolute and relative paths), Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP - # - uri: https://raw.githubusercontent.com/cloudposse/terraform-null-label/0.25.0/exports/context.tf - # This mixin `uri` is relative to the current `vpc` folder - - uri: ../../mixins/context.tf - filename: context.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/context.tf deleted file mode 100644 index 5e0ef8856..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/context.tf +++ /dev/null @@ -1,279 +0,0 @@ -# -# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label -# All other instances of this file should be a copy of that one -# -# -# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf -# and then place it in your Terraform module to automatically get -# Cloud Posse's standard configuration inputs suitable for passing -# to Cloud Posse modules. -# -# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf -# -# Modules should access the whole context as `module.this.context` -# to get the input variables with nulls for defaults, -# for example `context = module.this.context`, -# and access individual variables as `module.this.`, -# with final values filled in. -# -# For example, when using defaults, `module.this.context.delimiter` -# will be null, and `module.this.delimiter` will be `-` (hyphen). -# - -module "this" { - source = "cloudposse/label/null" - version = "0.25.0" # requires Terraform >= 0.13.0 - - enabled = var.enabled - namespace = var.namespace - tenant = var.tenant - environment = var.environment - stage = var.stage - name = var.name - delimiter = var.delimiter - attributes = var.attributes - tags = var.tags - additional_tag_map = var.additional_tag_map - label_order = var.label_order - regex_replace_chars = var.regex_replace_chars - id_length_limit = var.id_length_limit - label_key_case = var.label_key_case - label_value_case = var.label_value_case - descriptor_formats = var.descriptor_formats - labels_as_tags = var.labels_as_tags - - context = var.context -} - -# Copy contents of cloudposse/terraform-null-label/variables.tf here - -variable "context" { - type = any - default = { - enabled = true - namespace = null - tenant = null - environment = null - stage = null - name = null - delimiter = null - attributes = [] - tags = {} - additional_tag_map = {} - regex_replace_chars = null - label_order = [] - id_length_limit = null - label_key_case = null - label_value_case = null - descriptor_formats = {} - # Note: we have to use [] instead of null for unset lists due to - # https://github.com/hashicorp/terraform/issues/28137 - # which was not fixed until Terraform 1.0.0, - # but we want the default to be all the labels in `label_order` - # and we want users to be able to prevent all tag generation - # by setting `labels_as_tags` to `[]`, so we need - # a different sentinel to indicate "default" - labels_as_tags = ["unset"] - } - description = <<-EOT - Single object for setting entire context at once. - See description of individual variables for details. - Leave string and numeric variables as `null` to use default value. - Individual variable settings (non-null) override settings in context object, - except for attributes, tags, and additional_tag_map, which are merged. - EOT - - validation { - condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`." - } - - validation { - condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "enabled" { - type = bool - default = null - description = "Set to false to prevent the module from creating any resources" -} - -variable "namespace" { - type = string - default = null - description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" -} - -variable "tenant" { - type = string - default = null - description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" -} - -variable "environment" { - type = string - default = null - description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" -} - -variable "stage" { - type = string - default = null - description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" -} - -variable "name" { - type = string - default = null - description = <<-EOT - ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. - This is the only ID element not also included as a `tag`. - The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. - EOT -} - -variable "delimiter" { - type = string - default = null - description = <<-EOT - Delimiter to be used between ID elements. - Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. - EOT -} - -variable "attributes" { - type = list(string) - default = [] - description = <<-EOT - ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, - in the order they appear in the list. New attributes are appended to the - end of the list. The elements of the list are joined by the `delimiter` - and treated as a single ID element. - EOT -} - -variable "labels_as_tags" { - type = set(string) - default = ["default"] - description = <<-EOT - Set of labels (ID elements) to include as tags in the `tags` output. - Default is to include all labels. - Tags with empty values will not be included in the `tags` output. - Set to `[]` to suppress all generated tags. - **Notes:** - The value of the `name` tag, if included, will be the `id`, not the `name`. - Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be - changed in later chained modules. Attempts to change it will be silently ignored. - EOT -} - -variable "tags" { - type = map(string) - default = {} - description = <<-EOT - Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). - Neither the tag keys nor the tag values will be modified by this module. - EOT -} - -variable "additional_tag_map" { - type = map(string) - default = {} - description = <<-EOT - Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. - This is for some rare cases where resources want additional configuration of tags - and therefore take a list of maps with tag key, value, and additional configuration. - EOT -} - -variable "label_order" { - type = list(string) - default = null - description = <<-EOT - The order in which the labels (ID elements) appear in the `id`. - Defaults to ["namespace", "environment", "stage", "name", "attributes"]. - You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. - EOT -} - -variable "regex_replace_chars" { - type = string - default = null - description = <<-EOT - Terraform regular expression (regex) string. - Characters matching the regex will be removed from the ID elements. - If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. - EOT -} - -variable "id_length_limit" { - type = number - default = null - description = <<-EOT - Limit `id` to this many characters (minimum 6). - Set to `0` for unlimited length. - Set to `null` for keep the existing setting, which defaults to `0`. - Does not affect `id_full`. - EOT - validation { - condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 - error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." - } -} - -variable "label_key_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of the `tags` keys (label names) for tags generated by this module. - Does not affect keys of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper`. - Default value: `title`. - EOT - - validation { - condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) - error_message = "Allowed values: `lower`, `title`, `upper`." - } -} - -variable "label_value_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of ID elements (labels) as included in `id`, - set as tag values, and output by this module individually. - Does not affect values of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper` and `none` (no transformation). - Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. - Default value: `lower`. - EOT - - validation { - condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "descriptor_formats" { - type = any - default = {} - description = <<-EOT - Describe additional descriptors to be output in the `descriptors` output map. - Map of maps. Keys are names of descriptors. Values are maps of the form - `{ - format = string - labels = list(string) - }` - (Type is `any` so the map values can later be enhanced to provide additional options.) - `format` is a Terraform format string to be passed to the `format()` function. - `labels` is a list of labels, in order, to pass to `format()` function. - Label values will be normalized before being passed to `format()` so they will be - identical to how they appear in `id`. - Default is `{}` (`descriptors` output will be empty). - EOT -} - -#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/main.tf deleted file mode 100644 index dde5622f2..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/main.tf +++ /dev/null @@ -1,173 +0,0 @@ -locals { - enabled = module.this.enabled - - nat_eip_aws_shield_protection_enabled = local.enabled && var.nat_eip_aws_shield_protection_enabled - vpc_flow_logs_enabled = local.enabled && var.vpc_flow_logs_enabled - - # The usage of specific kubernetes.io/cluster/* resource tags were required before Kubernetes 1.19, - # but are now deprecated. See https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html - - max_subnet_count = ( - var.max_subnet_count > 0 ? var.max_subnet_count : ( - length(var.availability_zone_ids) > 0 ? length(var.availability_zone_ids) : length(var.availability_zones) - ) - ) - - availability_zones = length(var.availability_zones) > 0 ? ( - (substr( - var.availability_zones[0], - 0, - length(var.region) - ) == var.region) ? var.availability_zones : formatlist("${var.region}%s", var.availability_zones) - ) : var.availability_zones - - short_region = module.utils.region_az_alt_code_maps["to_short"][var.region] - - availability_zone_ids = length(var.availability_zone_ids) > 0 ? ( - (substr( - var.availability_zone_ids[0], - 0, - length(local.short_region) - ) == local.short_region) ? var.availability_zone_ids : formatlist("${local.short_region}%s", var.availability_zone_ids) - ) : var.availability_zone_ids - - # required tags to make ALB ingress work https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html - # https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html - public_subnets_additional_tags = { - (var.subnet_type_tag_key) = "public", - "kubernetes.io/role/elb" = 1, - } - - private_subnets_additional_tags = { - (var.subnet_type_tag_key) = "private", - "kubernetes.io/role/internal-elb" = 1, - } - - gateway_endpoint_map = { for v in var.gateway_vpc_endpoints : v => { - name = v - policy = null - route_table_ids = module.subnets.private_route_table_ids - } } - - # If we use a separate security group for each endpoint interface, - # we will use the interface service name as the key: security_group_ids = [module.endpoint_security_groups[v].id] - # If we use a single security group for all endpoint interfaces, - # we will use local.interface_endpoint_security_group_key as the key. - interface_endpoint_security_group_key = "VPC Endpoint interfaces" - - interface_endpoint_map = { for v in var.interface_vpc_endpoints : v => { - name = v - policy = null - private_dns_enabled = true # Allow applications to use normal service DNS names to access the service - security_group_ids = [module.endpoint_security_groups[local.interface_endpoint_security_group_key].id] - subnet_ids = module.subnets.private_subnet_ids - } } -} - -module "utils" { - source = "cloudposse/utils/aws" - version = "1.4.0" -} - -module "vpc" { - source = "cloudposse/vpc/aws" - version = "2.1.1" - - ipv4_primary_cidr_block = var.ipv4_primary_cidr_block - internet_gateway_enabled = var.public_subnets_enabled - assign_generated_ipv6_cidr_block = var.assign_generated_ipv6_cidr_block - - # Required for DNS resolution of VPC Endpoint interfaces, and generally harmless - # See https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html#vpc-dns-support - dns_hostnames_enabled = true - dns_support_enabled = true - - context = module.this.context -} - -# We could create a security group per endpoint, -# but until we are ready to customize them by service, it is just a waste -# of resources. We use a single security group for all endpoints. -# Security groups can be updated without recreating the endpoint or -# interrupting service, so this is an easy change to make later. -module "endpoint_security_groups" { - for_each = local.enabled && try(length(var.interface_vpc_endpoints), 0) > 0 ? toset([local.interface_endpoint_security_group_key]) : [] - - source = "cloudposse/security-group/aws" - version = "2.2.0" - - create_before_destroy = true - preserve_security_group_id = false - attributes = [each.value] - vpc_id = module.vpc.vpc_id - - rules_map = { - ingress = [{ - key = "vpc_ingress" - type = "ingress" - from_port = 0 - to_port = 65535 - protocol = "-1" # allow ping - cidr_blocks = compact(concat([module.vpc.vpc_cidr_block], module.vpc.additional_cidr_blocks)) - ipv6_cidr_blocks = compact(concat([module.vpc.vpc_ipv6_cidr_block], module.vpc.additional_ipv6_cidr_blocks)) - description = "Ingress from VPC to ${each.value}" - }] - } - - allow_all_egress = true - - context = module.this.context -} - -module "vpc_endpoints" { - source = "cloudposse/vpc/aws//modules/vpc-endpoints" - version = "2.1.0" - - enabled = (length(var.interface_vpc_endpoints) + length(var.gateway_vpc_endpoints)) > 0 - - vpc_id = module.vpc.vpc_id - gateway_vpc_endpoints = local.gateway_endpoint_map - interface_vpc_endpoints = local.interface_endpoint_map - - context = module.this.context -} - -module "subnets" { - source = "cloudposse/dynamic-subnets/aws" - version = "2.4.1" - - availability_zones = local.availability_zones - availability_zone_ids = local.availability_zone_ids - ipv4_cidr_block = [module.vpc.vpc_cidr_block] - ipv4_cidrs = var.ipv4_cidrs - ipv6_enabled = false - igw_id = var.public_subnets_enabled ? [module.vpc.igw_id] : [] - map_public_ip_on_launch = var.map_public_ip_on_launch - max_subnet_count = local.max_subnet_count - nat_gateway_enabled = var.nat_gateway_enabled - nat_instance_enabled = var.nat_instance_enabled - nat_instance_type = var.nat_instance_type - public_subnets_enabled = var.public_subnets_enabled - public_subnets_additional_tags = local.public_subnets_additional_tags - private_subnets_additional_tags = local.private_subnets_additional_tags - vpc_id = module.vpc.vpc_id - - context = module.this.context -} - -data "aws_caller_identity" "current" { - count = local.nat_eip_aws_shield_protection_enabled ? 1 : 0 -} - -data "aws_eip" "eip" { - for_each = local.nat_eip_aws_shield_protection_enabled ? toset(module.subnets.nat_ips) : [] - - public_ip = each.key -} - -resource "aws_shield_protection" "nat_eip_shield_protection" { - for_each = local.nat_eip_aws_shield_protection_enabled ? data.aws_eip.eip : {} - - name = data.aws_eip.eip[each.key].id - resource_arn = "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current[0].account_id}:eip-allocation/${data.aws_eip.eip[each.key].id}" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/outputs.tf deleted file mode 100644 index 3a98630a9..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/outputs.tf +++ /dev/null @@ -1,109 +0,0 @@ -output "public_subnet_ids" { - value = module.subnets.public_subnet_ids - description = "Public subnet IDs" -} - -output "public_subnet_cidrs" { - value = module.subnets.public_subnet_cidrs - description = "Public subnet CIDRs" -} - -output "private_subnet_ids" { - value = module.subnets.private_subnet_ids - description = "Private subnet IDs" -} - -output "private_subnet_cidrs" { - value = module.subnets.private_subnet_cidrs - description = "Private subnet CIDRs" -} - -output "subnets" { - value = { - public : { - ids : module.subnets.public_subnet_ids - cidr : module.subnets.public_subnet_cidrs - } - private : { - ids : module.subnets.private_subnet_ids - cidr : module.subnets.private_subnet_cidrs - } - } - description = "Subnets info map" -} - -output "vpc_default_network_acl_id" { - value = module.vpc.vpc_default_network_acl_id - description = "The ID of the network ACL created by default on VPC creation" -} - -output "vpc_default_security_group_id" { - value = module.vpc.vpc_default_security_group_id - description = "The ID of the security group created by default on VPC creation" -} - -output "vpc_id" { - value = module.vpc.vpc_id - description = "VPC ID" -} - -output "vpc_cidr" { - value = module.vpc.vpc_cidr_block - description = "VPC CIDR" -} - -output "vpc" { - value = { - id : module.vpc.vpc_id - cidr : module.vpc.vpc_cidr_block - subnet_type_tag_key : var.subnet_type_tag_key - } - description = "VPC info map" -} - -output "private_route_table_ids" { - value = module.subnets.private_route_table_ids - description = "Private subnet route table IDs" -} - -output "public_route_table_ids" { - value = module.subnets.public_route_table_ids - description = "Public subnet route table IDs" -} - -output "route_tables" { - value = { - public : { - ids : module.subnets.public_route_table_ids - } - private : { - ids : module.subnets.private_route_table_ids - } - } - description = "Route tables info map" -} - -output "nat_gateway_ids" { - value = module.subnets.nat_gateway_ids - description = "NAT Gateway IDs" -} - -output "nat_instance_ids" { - value = module.subnets.nat_instance_ids - description = "NAT Instance IDs" -} - -output "nat_gateway_public_ips" { - value = module.subnets.nat_gateway_public_ips - description = "NAT Gateway public IPs" -} - -output "max_subnet_count" { - value = local.max_subnet_count - description = "Maximum allowed number of subnets before all subnet CIDRs need to be recomputed" -} - -output "availability_zones" { - description = "List of Availability Zones where subnets were created" - value = local.availability_zones -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/providers.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/providers.tf deleted file mode 100644 index dc58d9a25..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/providers.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider "aws" { - region = var.region -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/remote-state.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/remote-state.tf deleted file mode 100644 index e785ec5b1..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/remote-state.tf +++ /dev/null @@ -1,20 +0,0 @@ -module "vpc_flow_logs_bucket" { - count = var.vpc_flow_logs_enabled ? 1 : 0 - - source = "cloudposse/stack-config/yaml//modules/remote-state" - version = "1.5.0" - - # Specify the Atmos component name (defined in YAML stack config files) - # for which to get the remote state outputs - component = var.vpc_flow_logs_bucket_component_name - - # Override the context variables to point to a different Atmos stack if the - # `vpc-flow-logs-bucket` Atmos component is provisioned in another AWS account, OU or region - stage = try(coalesce(var.vpc_flow_logs_bucket_stage_name, module.this.stage), null) - environment = try(coalesce(var.vpc_flow_logs_bucket_environment_name, module.this.environment), null) - tenant = try(coalesce(var.vpc_flow_logs_bucket_tenant_name, module.this.tenant), null) - - # `context` input is a way to provide the information about the stack (using the context - # variables `namespace`, `tenant`, `environment`, and `stage` defined in the stack config) - context = module.this.context -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/variables.tf deleted file mode 100644 index ff1376f6c..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/variables.tf +++ /dev/null @@ -1,165 +0,0 @@ -variable "region" { - type = string - description = "AWS Region" -} - -variable "availability_zones" { - type = list(string) - description = <<-EOT - List of Availability Zones (AZs) where subnets will be created. Ignored when `availability_zone_ids` is set. - The order of zones in the list ***must be stable*** or else Terraform will continually make changes. - If no AZs are specified, then `max_subnet_count` AZs will be selected in alphabetical order. - If `max_subnet_count > 0` and `length(var.availability_zones) > max_subnet_count`, the list - will be truncated. We recommend setting `availability_zones` and `max_subnet_count` explicitly as constant - (not computed) values for predictability, consistency, and stability. - EOT - default = [] -} - -variable "availability_zone_ids" { - type = list(string) - description = <<-EOT - List of Availability Zones IDs where subnets will be created. Overrides `availability_zones`. - Useful in some regions when using only some AZs and you want to use the same ones across multiple accounts. - EOT - default = [] -} - -variable "ipv4_primary_cidr_block" { - type = string - description = <<-EOT - The primary IPv4 CIDR block for the VPC. - Either `ipv4_primary_cidr_block` or `ipv4_primary_cidr_block_association` must be set, but not both. - EOT - default = null -} - -variable "ipv4_cidrs" { - type = list(object({ - private = list(string) - public = list(string) - })) - description = <<-EOT - Lists of CIDRs to assign to subnets. Order of CIDRs in the lists must not change over time. - Lists may contain more CIDRs than needed. - EOT - default = [] - validation { - condition = length(var.ipv4_cidrs) < 2 - error_message = "Only 1 ipv4_cidrs object can be provided. Lists of CIDRs are passed via the `public` and `private` attributes of the single object." - } -} - -variable "assign_generated_ipv6_cidr_block" { - type = bool - description = "When `true`, assign AWS generated IPv6 CIDR block to the VPC. Conflicts with `ipv6_ipam_pool_id`." - default = false -} - -variable "public_subnets_enabled" { - type = bool - description = <<-EOT - If false, do not create public subnets. - Since NAT gateways and instances must be created in public subnets, these will also not be created when `false`. - EOT - default = true -} - -variable "nat_gateway_enabled" { - type = bool - description = "Flag to enable/disable NAT gateways" - default = true -} - -variable "nat_instance_enabled" { - type = bool - description = "Flag to enable/disable NAT instances" - default = false -} - -variable "nat_instance_type" { - type = string - description = "NAT Instance type" - default = "t3.micro" -} - -variable "map_public_ip_on_launch" { - type = bool - default = true - description = "Instances launched into a public subnet should be assigned a public IP address" -} - -variable "subnet_type_tag_key" { - type = string - description = "Key for subnet type tag to provide information about the type of subnets, e.g. `cpco/subnet/type=private` or `cpcp/subnet/type=public`" -} - -variable "max_subnet_count" { - type = number - default = 0 - description = "Sets the maximum amount of subnets to deploy. 0 will deploy a subnet for every provided availability zone (in `region_availability_zones` variable) within the region" -} - -variable "vpc_flow_logs_enabled" { - type = bool - description = "Enable or disable the VPC Flow Logs" - default = true -} - -variable "vpc_flow_logs_traffic_type" { - type = string - description = "The type of traffic to capture. Valid values: `ACCEPT`, `REJECT`, `ALL`" - default = "ALL" -} - -variable "vpc_flow_logs_log_destination_type" { - type = string - description = "The type of the logging destination. Valid values: `cloud-watch-logs`, `s3`" - default = "s3" -} - -variable "vpc_flow_logs_bucket_component_name" { - type = string - description = "The name of the VPC Flow Logs bucket component" - default = "vpc-flow-logs-bucket" -} - -variable "vpc_flow_logs_bucket_environment_name" { - type = string - description = "The name of the environment where the VPC Flow Logs bucket is provisioned" - default = "" -} - -variable "vpc_flow_logs_bucket_stage_name" { - type = string - description = "The stage (account) name where the VPC Flow Logs bucket is provisioned" - default = "" -} - -variable "vpc_flow_logs_bucket_tenant_name" { - type = string - description = <<-EOT - The name of the tenant where the VPC Flow Logs bucket is provisioned. - - If the `tenant` label is not used, leave this as `null`. - EOT - default = null -} - -variable "nat_eip_aws_shield_protection_enabled" { - type = bool - description = "Enable or disable AWS Shield Advanced protection for NAT EIPs. If set to 'true', a subscription to AWS Shield Advanced must exist in this account." - default = false -} - -variable "gateway_vpc_endpoints" { - type = set(string) - description = "A list of Gateway VPC Endpoints to provision into the VPC. Only valid values are \"dynamodb\" and \"s3\"." - default = [] -} - -variable "interface_vpc_endpoints" { - type = set(string) - description = "A list of Interface VPC Endpoints to provision into the VPC." - default = [] -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/versions.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/versions.tf deleted file mode 100644 index 08c4806a4..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/versions.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_version = ">= 1.3.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 4.9.0" - } - utils = { - source = "cloudposse/utils" - version = ">= 1.18.0" - } - } -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/vpc-flow-logs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/vpc-flow-logs.tf deleted file mode 100644 index 51bfe7b37..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc/vpc-flow-logs.tf +++ /dev/null @@ -1,15 +0,0 @@ -# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log -# https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html - -resource "aws_flow_log" "default" { - count = local.vpc_flow_logs_enabled ? 1 : 0 - - # Use the remote state output `vpc_flow_logs_bucket_arn` of the `vpc_flow_logs_bucket` component - log_destination = module.vpc_flow_logs_bucket[0].outputs.vpc_flow_logs_bucket_arn - - log_destination_type = var.vpc_flow_logs_log_destination_type - traffic_type = var.vpc_flow_logs_traffic_type - vpc_id = module.vpc.vpc_id - - tags = module.this.tags -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc2/component.yaml b/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc2/component.yaml deleted file mode 100644 index 4a46b4ca3..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc2/component.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# This is an example of how to download a Terraform component from an OCI registry (https://opencontainers.org), e.g. AWS Public ECR - -# 'component.yaml' in the component folder is processed by the 'atmos' commands -# 'atmos vendor pull -c infra/vpc2' or 'atmos vendor pull --component infra/vpc2' - -apiVersion: atmos/v1 -kind: ComponentVendorConfig -metadata: - name: stable/aws/vpc - description: Config for vendoring of 'stable/aws/vpc' component -spec: - source: - # Source 'uri' supports the following protocols: OCI (https://opencontainers.org), Git, Mercurial, HTTP, HTTPS, Amazon S3, Google GCP, - # and all URL and archive formats as described in https://github.com/hashicorp/go-getter - # In 'uri', Golang templates are supported https://pkg.go.dev/text/template - # If 'version' is provided, '{{.Version}}' will be replaced with the 'version' value before pulling the files from 'uri' - # Download the component from the AWS public ECR registry (https://docs.aws.amazon.com/AmazonECR/latest/public/public-registries.html) - uri: "oci://public.ecr.aws/cloudposse/components/terraform/stable/aws/vpc:{{.Version}}" - version: "latest" - # Only include the files that match the 'included_paths' patterns - # If 'included_paths' is not specified, all files will be matched except those that match the patterns from 'excluded_paths' - # 'included_paths' support POSIX-style Globs for file names/paths (double-star `**` is supported) - # https://en.wikipedia.org/wiki/Glob_(programming) - # https://github.com/bmatcuk/doublestar#patterns - included_paths: - - "**/*.*" diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/context.tf deleted file mode 100644 index 5e0ef8856..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/context.tf +++ /dev/null @@ -1,279 +0,0 @@ -# -# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label -# All other instances of this file should be a copy of that one -# -# -# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf -# and then place it in your Terraform module to automatically get -# Cloud Posse's standard configuration inputs suitable for passing -# to Cloud Posse modules. -# -# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf -# -# Modules should access the whole context as `module.this.context` -# to get the input variables with nulls for defaults, -# for example `context = module.this.context`, -# and access individual variables as `module.this.`, -# with final values filled in. -# -# For example, when using defaults, `module.this.context.delimiter` -# will be null, and `module.this.delimiter` will be `-` (hyphen). -# - -module "this" { - source = "cloudposse/label/null" - version = "0.25.0" # requires Terraform >= 0.13.0 - - enabled = var.enabled - namespace = var.namespace - tenant = var.tenant - environment = var.environment - stage = var.stage - name = var.name - delimiter = var.delimiter - attributes = var.attributes - tags = var.tags - additional_tag_map = var.additional_tag_map - label_order = var.label_order - regex_replace_chars = var.regex_replace_chars - id_length_limit = var.id_length_limit - label_key_case = var.label_key_case - label_value_case = var.label_value_case - descriptor_formats = var.descriptor_formats - labels_as_tags = var.labels_as_tags - - context = var.context -} - -# Copy contents of cloudposse/terraform-null-label/variables.tf here - -variable "context" { - type = any - default = { - enabled = true - namespace = null - tenant = null - environment = null - stage = null - name = null - delimiter = null - attributes = [] - tags = {} - additional_tag_map = {} - regex_replace_chars = null - label_order = [] - id_length_limit = null - label_key_case = null - label_value_case = null - descriptor_formats = {} - # Note: we have to use [] instead of null for unset lists due to - # https://github.com/hashicorp/terraform/issues/28137 - # which was not fixed until Terraform 1.0.0, - # but we want the default to be all the labels in `label_order` - # and we want users to be able to prevent all tag generation - # by setting `labels_as_tags` to `[]`, so we need - # a different sentinel to indicate "default" - labels_as_tags = ["unset"] - } - description = <<-EOT - Single object for setting entire context at once. - See description of individual variables for details. - Leave string and numeric variables as `null` to use default value. - Individual variable settings (non-null) override settings in context object, - except for attributes, tags, and additional_tag_map, which are merged. - EOT - - validation { - condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`." - } - - validation { - condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "enabled" { - type = bool - default = null - description = "Set to false to prevent the module from creating any resources" -} - -variable "namespace" { - type = string - default = null - description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" -} - -variable "tenant" { - type = string - default = null - description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" -} - -variable "environment" { - type = string - default = null - description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" -} - -variable "stage" { - type = string - default = null - description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" -} - -variable "name" { - type = string - default = null - description = <<-EOT - ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. - This is the only ID element not also included as a `tag`. - The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. - EOT -} - -variable "delimiter" { - type = string - default = null - description = <<-EOT - Delimiter to be used between ID elements. - Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. - EOT -} - -variable "attributes" { - type = list(string) - default = [] - description = <<-EOT - ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, - in the order they appear in the list. New attributes are appended to the - end of the list. The elements of the list are joined by the `delimiter` - and treated as a single ID element. - EOT -} - -variable "labels_as_tags" { - type = set(string) - default = ["default"] - description = <<-EOT - Set of labels (ID elements) to include as tags in the `tags` output. - Default is to include all labels. - Tags with empty values will not be included in the `tags` output. - Set to `[]` to suppress all generated tags. - **Notes:** - The value of the `name` tag, if included, will be the `id`, not the `name`. - Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be - changed in later chained modules. Attempts to change it will be silently ignored. - EOT -} - -variable "tags" { - type = map(string) - default = {} - description = <<-EOT - Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). - Neither the tag keys nor the tag values will be modified by this module. - EOT -} - -variable "additional_tag_map" { - type = map(string) - default = {} - description = <<-EOT - Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. - This is for some rare cases where resources want additional configuration of tags - and therefore take a list of maps with tag key, value, and additional configuration. - EOT -} - -variable "label_order" { - type = list(string) - default = null - description = <<-EOT - The order in which the labels (ID elements) appear in the `id`. - Defaults to ["namespace", "environment", "stage", "name", "attributes"]. - You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. - EOT -} - -variable "regex_replace_chars" { - type = string - default = null - description = <<-EOT - Terraform regular expression (regex) string. - Characters matching the regex will be removed from the ID elements. - If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. - EOT -} - -variable "id_length_limit" { - type = number - default = null - description = <<-EOT - Limit `id` to this many characters (minimum 6). - Set to `0` for unlimited length. - Set to `null` for keep the existing setting, which defaults to `0`. - Does not affect `id_full`. - EOT - validation { - condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 - error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." - } -} - -variable "label_key_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of the `tags` keys (label names) for tags generated by this module. - Does not affect keys of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper`. - Default value: `title`. - EOT - - validation { - condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) - error_message = "Allowed values: `lower`, `title`, `upper`." - } -} - -variable "label_value_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of ID elements (labels) as included in `id`, - set as tag values, and output by this module individually. - Does not affect values of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper` and `none` (no transformation). - Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. - Default value: `lower`. - EOT - - validation { - condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "descriptor_formats" { - type = any - default = {} - description = <<-EOT - Describe additional descriptors to be output in the `descriptors` output map. - Map of maps. Keys are names of descriptors. Values are maps of the form - `{ - format = string - labels = list(string) - }` - (Type is `any` so the map values can later be enhanced to provide additional options.) - `format` is a Terraform format string to be passed to the `format()` function. - `labels` is a list of labels, in order, to pass to `format()` function. - Label values will be normalized before being passed to `format()` so they will be - identical to how they appear in `id`. - Default is `{}` (`descriptors` output will be empty). - EOT -} - -#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/introspection.mixin.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/introspection.mixin.tf deleted file mode 100644 index 3af29f58d..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/mixins/introspection.mixin.tf +++ /dev/null @@ -1,34 +0,0 @@ -# This mixin is meant to be added to Terraform components in order to append a `Component` tag to all resources in the -# configuration, specifying which component the resources belong to. -# -# It's important to note that all modules and resources within the component then need to use `module.introspection.context` -# and `module.introspection.tags`, respectively, rather than `module.this.context` and `module.this.tags`. -# - -locals { - # Throw an error if lookup fails - check_required_tags = module.this.enabled ? [ - for k in var.required_tags : lookup(module.this.tags, k) - ] : [] -} - -variable "required_tags" { - type = list(string) - description = "List of required tag names" - default = [] -} - -# `introspection` module will contain the additional tags -module "introspection" { - source = "cloudposse/label/null" - version = "0.25.0" - - tags = merge( - var.tags, - { - "Component" = basename(abspath(path.module)) - } - ) - - context = module.this.context -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/context.tf deleted file mode 100644 index 5e0ef8856..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/context.tf +++ /dev/null @@ -1,279 +0,0 @@ -# -# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label -# All other instances of this file should be a copy of that one -# -# -# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf -# and then place it in your Terraform module to automatically get -# Cloud Posse's standard configuration inputs suitable for passing -# to Cloud Posse modules. -# -# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf -# -# Modules should access the whole context as `module.this.context` -# to get the input variables with nulls for defaults, -# for example `context = module.this.context`, -# and access individual variables as `module.this.`, -# with final values filled in. -# -# For example, when using defaults, `module.this.context.delimiter` -# will be null, and `module.this.delimiter` will be `-` (hyphen). -# - -module "this" { - source = "cloudposse/label/null" - version = "0.25.0" # requires Terraform >= 0.13.0 - - enabled = var.enabled - namespace = var.namespace - tenant = var.tenant - environment = var.environment - stage = var.stage - name = var.name - delimiter = var.delimiter - attributes = var.attributes - tags = var.tags - additional_tag_map = var.additional_tag_map - label_order = var.label_order - regex_replace_chars = var.regex_replace_chars - id_length_limit = var.id_length_limit - label_key_case = var.label_key_case - label_value_case = var.label_value_case - descriptor_formats = var.descriptor_formats - labels_as_tags = var.labels_as_tags - - context = var.context -} - -# Copy contents of cloudposse/terraform-null-label/variables.tf here - -variable "context" { - type = any - default = { - enabled = true - namespace = null - tenant = null - environment = null - stage = null - name = null - delimiter = null - attributes = [] - tags = {} - additional_tag_map = {} - regex_replace_chars = null - label_order = [] - id_length_limit = null - label_key_case = null - label_value_case = null - descriptor_formats = {} - # Note: we have to use [] instead of null for unset lists due to - # https://github.com/hashicorp/terraform/issues/28137 - # which was not fixed until Terraform 1.0.0, - # but we want the default to be all the labels in `label_order` - # and we want users to be able to prevent all tag generation - # by setting `labels_as_tags` to `[]`, so we need - # a different sentinel to indicate "default" - labels_as_tags = ["unset"] - } - description = <<-EOT - Single object for setting entire context at once. - See description of individual variables for details. - Leave string and numeric variables as `null` to use default value. - Individual variable settings (non-null) override settings in context object, - except for attributes, tags, and additional_tag_map, which are merged. - EOT - - validation { - condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`." - } - - validation { - condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "enabled" { - type = bool - default = null - description = "Set to false to prevent the module from creating any resources" -} - -variable "namespace" { - type = string - default = null - description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" -} - -variable "tenant" { - type = string - default = null - description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" -} - -variable "environment" { - type = string - default = null - description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" -} - -variable "stage" { - type = string - default = null - description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" -} - -variable "name" { - type = string - default = null - description = <<-EOT - ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. - This is the only ID element not also included as a `tag`. - The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. - EOT -} - -variable "delimiter" { - type = string - default = null - description = <<-EOT - Delimiter to be used between ID elements. - Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. - EOT -} - -variable "attributes" { - type = list(string) - default = [] - description = <<-EOT - ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, - in the order they appear in the list. New attributes are appended to the - end of the list. The elements of the list are joined by the `delimiter` - and treated as a single ID element. - EOT -} - -variable "labels_as_tags" { - type = set(string) - default = ["default"] - description = <<-EOT - Set of labels (ID elements) to include as tags in the `tags` output. - Default is to include all labels. - Tags with empty values will not be included in the `tags` output. - Set to `[]` to suppress all generated tags. - **Notes:** - The value of the `name` tag, if included, will be the `id`, not the `name`. - Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be - changed in later chained modules. Attempts to change it will be silently ignored. - EOT -} - -variable "tags" { - type = map(string) - default = {} - description = <<-EOT - Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). - Neither the tag keys nor the tag values will be modified by this module. - EOT -} - -variable "additional_tag_map" { - type = map(string) - default = {} - description = <<-EOT - Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. - This is for some rare cases where resources want additional configuration of tags - and therefore take a list of maps with tag key, value, and additional configuration. - EOT -} - -variable "label_order" { - type = list(string) - default = null - description = <<-EOT - The order in which the labels (ID elements) appear in the `id`. - Defaults to ["namespace", "environment", "stage", "name", "attributes"]. - You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. - EOT -} - -variable "regex_replace_chars" { - type = string - default = null - description = <<-EOT - Terraform regular expression (regex) string. - Characters matching the regex will be removed from the ID elements. - If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. - EOT -} - -variable "id_length_limit" { - type = number - default = null - description = <<-EOT - Limit `id` to this many characters (minimum 6). - Set to `0` for unlimited length. - Set to `null` for keep the existing setting, which defaults to `0`. - Does not affect `id_full`. - EOT - validation { - condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 - error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." - } -} - -variable "label_key_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of the `tags` keys (label names) for tags generated by this module. - Does not affect keys of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper`. - Default value: `title`. - EOT - - validation { - condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) - error_message = "Allowed values: `lower`, `title`, `upper`." - } -} - -variable "label_value_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of ID elements (labels) as included in `id`, - set as tag values, and output by this module individually. - Does not affect values of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper` and `none` (no transformation). - Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. - Default value: `lower`. - EOT - - validation { - condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "descriptor_formats" { - type = any - default = {} - description = <<-EOT - Describe additional descriptors to be output in the `descriptors` output map. - Map of maps. Keys are names of descriptors. Values are maps of the form - `{ - format = string - labels = list(string) - }` - (Type is `any` so the map values can later be enhanced to provide additional options.) - `format` is a Terraform format string to be passed to the `format()` function. - `labels` is a list of labels, in order, to pass to `format()` function. - Label values will be normalized before being passed to `format()` so they will be - identical to how they appear in `id`. - Default is `{}` (`descriptors` output will be empty). - EOT -} - -#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/main.tf deleted file mode 100644 index 3d0c15f53..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -module "test_label" { - source = "cloudposse/label/null" - version = "0.25.0" - - context = module.this.context -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/outputs.tf deleted file mode 100644 index 2af5e9cef..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test/outputs.tf +++ /dev/null @@ -1,22 +0,0 @@ -output "test_label_id" { - value = module.test_label.id - description = "Test label ID" -} - -output "test_list" { - value = [ - "list_item_1", - "list_item_2", - "list_item_3" - ] - description = "Test list" -} - -output "test_map" { - value = { - a = 1, - b = 2, - c = 3 - } - description = "Test map" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/context.tf deleted file mode 100644 index 5e0ef8856..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/context.tf +++ /dev/null @@ -1,279 +0,0 @@ -# -# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label -# All other instances of this file should be a copy of that one -# -# -# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf -# and then place it in your Terraform module to automatically get -# Cloud Posse's standard configuration inputs suitable for passing -# to Cloud Posse modules. -# -# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf -# -# Modules should access the whole context as `module.this.context` -# to get the input variables with nulls for defaults, -# for example `context = module.this.context`, -# and access individual variables as `module.this.`, -# with final values filled in. -# -# For example, when using defaults, `module.this.context.delimiter` -# will be null, and `module.this.delimiter` will be `-` (hyphen). -# - -module "this" { - source = "cloudposse/label/null" - version = "0.25.0" # requires Terraform >= 0.13.0 - - enabled = var.enabled - namespace = var.namespace - tenant = var.tenant - environment = var.environment - stage = var.stage - name = var.name - delimiter = var.delimiter - attributes = var.attributes - tags = var.tags - additional_tag_map = var.additional_tag_map - label_order = var.label_order - regex_replace_chars = var.regex_replace_chars - id_length_limit = var.id_length_limit - label_key_case = var.label_key_case - label_value_case = var.label_value_case - descriptor_formats = var.descriptor_formats - labels_as_tags = var.labels_as_tags - - context = var.context -} - -# Copy contents of cloudposse/terraform-null-label/variables.tf here - -variable "context" { - type = any - default = { - enabled = true - namespace = null - tenant = null - environment = null - stage = null - name = null - delimiter = null - attributes = [] - tags = {} - additional_tag_map = {} - regex_replace_chars = null - label_order = [] - id_length_limit = null - label_key_case = null - label_value_case = null - descriptor_formats = {} - # Note: we have to use [] instead of null for unset lists due to - # https://github.com/hashicorp/terraform/issues/28137 - # which was not fixed until Terraform 1.0.0, - # but we want the default to be all the labels in `label_order` - # and we want users to be able to prevent all tag generation - # by setting `labels_as_tags` to `[]`, so we need - # a different sentinel to indicate "default" - labels_as_tags = ["unset"] - } - description = <<-EOT - Single object for setting entire context at once. - See description of individual variables for details. - Leave string and numeric variables as `null` to use default value. - Individual variable settings (non-null) override settings in context object, - except for attributes, tags, and additional_tag_map, which are merged. - EOT - - validation { - condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`." - } - - validation { - condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "enabled" { - type = bool - default = null - description = "Set to false to prevent the module from creating any resources" -} - -variable "namespace" { - type = string - default = null - description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" -} - -variable "tenant" { - type = string - default = null - description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" -} - -variable "environment" { - type = string - default = null - description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" -} - -variable "stage" { - type = string - default = null - description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" -} - -variable "name" { - type = string - default = null - description = <<-EOT - ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. - This is the only ID element not also included as a `tag`. - The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. - EOT -} - -variable "delimiter" { - type = string - default = null - description = <<-EOT - Delimiter to be used between ID elements. - Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. - EOT -} - -variable "attributes" { - type = list(string) - default = [] - description = <<-EOT - ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, - in the order they appear in the list. New attributes are appended to the - end of the list. The elements of the list are joined by the `delimiter` - and treated as a single ID element. - EOT -} - -variable "labels_as_tags" { - type = set(string) - default = ["default"] - description = <<-EOT - Set of labels (ID elements) to include as tags in the `tags` output. - Default is to include all labels. - Tags with empty values will not be included in the `tags` output. - Set to `[]` to suppress all generated tags. - **Notes:** - The value of the `name` tag, if included, will be the `id`, not the `name`. - Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be - changed in later chained modules. Attempts to change it will be silently ignored. - EOT -} - -variable "tags" { - type = map(string) - default = {} - description = <<-EOT - Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). - Neither the tag keys nor the tag values will be modified by this module. - EOT -} - -variable "additional_tag_map" { - type = map(string) - default = {} - description = <<-EOT - Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. - This is for some rare cases where resources want additional configuration of tags - and therefore take a list of maps with tag key, value, and additional configuration. - EOT -} - -variable "label_order" { - type = list(string) - default = null - description = <<-EOT - The order in which the labels (ID elements) appear in the `id`. - Defaults to ["namespace", "environment", "stage", "name", "attributes"]. - You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. - EOT -} - -variable "regex_replace_chars" { - type = string - default = null - description = <<-EOT - Terraform regular expression (regex) string. - Characters matching the regex will be removed from the ID elements. - If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. - EOT -} - -variable "id_length_limit" { - type = number - default = null - description = <<-EOT - Limit `id` to this many characters (minimum 6). - Set to `0` for unlimited length. - Set to `null` for keep the existing setting, which defaults to `0`. - Does not affect `id_full`. - EOT - validation { - condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 - error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." - } -} - -variable "label_key_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of the `tags` keys (label names) for tags generated by this module. - Does not affect keys of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper`. - Default value: `title`. - EOT - - validation { - condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) - error_message = "Allowed values: `lower`, `title`, `upper`." - } -} - -variable "label_value_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of ID elements (labels) as included in `id`, - set as tag values, and output by this module individually. - Does not affect values of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper` and `none` (no transformation). - Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. - Default value: `lower`. - EOT - - validation { - condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "descriptor_formats" { - type = any - default = {} - description = <<-EOT - Describe additional descriptors to be output in the `descriptors` output map. - Map of maps. Keys are names of descriptors. Values are maps of the form - `{ - format = string - labels = list(string) - }` - (Type is `any` so the map values can later be enhanced to provide additional options.) - `format` is a Terraform format string to be passed to the `format()` function. - `labels` is a list of labels, in order, to pass to `format()` function. - Label values will be normalized before being passed to `format()` so they will be - identical to how they appear in `id`. - Default is `{}` (`descriptors` output will be empty). - EOT -} - -#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/outputs.tf deleted file mode 100644 index 76c897196..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/outputs.tf +++ /dev/null @@ -1,14 +0,0 @@ -output "region" { - value = var.region - description = "Test label ID" -} - -output "test_label_id" { - value = var.test_label_id - description = "Test label ID" -} - -output "test_label_id_2" { - value = var.test_label_id_2 - description = "Test label ID 2" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/variables.tf deleted file mode 100644 index cfbc1e4f9..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/template-functions-test2/variables.tf +++ /dev/null @@ -1,14 +0,0 @@ -variable "region" { - type = string - description = "Region" -} - -variable "test_label_id" { - type = string - description = "Test label ID" -} - -variable "test_label_id_2" { - type = string - description = "Test label ID 2" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/context.tf deleted file mode 100644 index 5e0ef8856..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/context.tf +++ /dev/null @@ -1,279 +0,0 @@ -# -# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label -# All other instances of this file should be a copy of that one -# -# -# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf -# and then place it in your Terraform module to automatically get -# Cloud Posse's standard configuration inputs suitable for passing -# to Cloud Posse modules. -# -# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf -# -# Modules should access the whole context as `module.this.context` -# to get the input variables with nulls for defaults, -# for example `context = module.this.context`, -# and access individual variables as `module.this.`, -# with final values filled in. -# -# For example, when using defaults, `module.this.context.delimiter` -# will be null, and `module.this.delimiter` will be `-` (hyphen). -# - -module "this" { - source = "cloudposse/label/null" - version = "0.25.0" # requires Terraform >= 0.13.0 - - enabled = var.enabled - namespace = var.namespace - tenant = var.tenant - environment = var.environment - stage = var.stage - name = var.name - delimiter = var.delimiter - attributes = var.attributes - tags = var.tags - additional_tag_map = var.additional_tag_map - label_order = var.label_order - regex_replace_chars = var.regex_replace_chars - id_length_limit = var.id_length_limit - label_key_case = var.label_key_case - label_value_case = var.label_value_case - descriptor_formats = var.descriptor_formats - labels_as_tags = var.labels_as_tags - - context = var.context -} - -# Copy contents of cloudposse/terraform-null-label/variables.tf here - -variable "context" { - type = any - default = { - enabled = true - namespace = null - tenant = null - environment = null - stage = null - name = null - delimiter = null - attributes = [] - tags = {} - additional_tag_map = {} - regex_replace_chars = null - label_order = [] - id_length_limit = null - label_key_case = null - label_value_case = null - descriptor_formats = {} - # Note: we have to use [] instead of null for unset lists due to - # https://github.com/hashicorp/terraform/issues/28137 - # which was not fixed until Terraform 1.0.0, - # but we want the default to be all the labels in `label_order` - # and we want users to be able to prevent all tag generation - # by setting `labels_as_tags` to `[]`, so we need - # a different sentinel to indicate "default" - labels_as_tags = ["unset"] - } - description = <<-EOT - Single object for setting entire context at once. - See description of individual variables for details. - Leave string and numeric variables as `null` to use default value. - Individual variable settings (non-null) override settings in context object, - except for attributes, tags, and additional_tag_map, which are merged. - EOT - - validation { - condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`." - } - - validation { - condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "enabled" { - type = bool - default = null - description = "Set to false to prevent the module from creating any resources" -} - -variable "namespace" { - type = string - default = null - description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" -} - -variable "tenant" { - type = string - default = null - description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" -} - -variable "environment" { - type = string - default = null - description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" -} - -variable "stage" { - type = string - default = null - description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" -} - -variable "name" { - type = string - default = null - description = <<-EOT - ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. - This is the only ID element not also included as a `tag`. - The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. - EOT -} - -variable "delimiter" { - type = string - default = null - description = <<-EOT - Delimiter to be used between ID elements. - Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. - EOT -} - -variable "attributes" { - type = list(string) - default = [] - description = <<-EOT - ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, - in the order they appear in the list. New attributes are appended to the - end of the list. The elements of the list are joined by the `delimiter` - and treated as a single ID element. - EOT -} - -variable "labels_as_tags" { - type = set(string) - default = ["default"] - description = <<-EOT - Set of labels (ID elements) to include as tags in the `tags` output. - Default is to include all labels. - Tags with empty values will not be included in the `tags` output. - Set to `[]` to suppress all generated tags. - **Notes:** - The value of the `name` tag, if included, will be the `id`, not the `name`. - Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be - changed in later chained modules. Attempts to change it will be silently ignored. - EOT -} - -variable "tags" { - type = map(string) - default = {} - description = <<-EOT - Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). - Neither the tag keys nor the tag values will be modified by this module. - EOT -} - -variable "additional_tag_map" { - type = map(string) - default = {} - description = <<-EOT - Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. - This is for some rare cases where resources want additional configuration of tags - and therefore take a list of maps with tag key, value, and additional configuration. - EOT -} - -variable "label_order" { - type = list(string) - default = null - description = <<-EOT - The order in which the labels (ID elements) appear in the `id`. - Defaults to ["namespace", "environment", "stage", "name", "attributes"]. - You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. - EOT -} - -variable "regex_replace_chars" { - type = string - default = null - description = <<-EOT - Terraform regular expression (regex) string. - Characters matching the regex will be removed from the ID elements. - If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. - EOT -} - -variable "id_length_limit" { - type = number - default = null - description = <<-EOT - Limit `id` to this many characters (minimum 6). - Set to `0` for unlimited length. - Set to `null` for keep the existing setting, which defaults to `0`. - Does not affect `id_full`. - EOT - validation { - condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 - error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." - } -} - -variable "label_key_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of the `tags` keys (label names) for tags generated by this module. - Does not affect keys of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper`. - Default value: `title`. - EOT - - validation { - condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) - error_message = "Allowed values: `lower`, `title`, `upper`." - } -} - -variable "label_value_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of ID elements (labels) as included in `id`, - set as tag values, and output by this module individually. - Does not affect values of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper` and `none` (no transformation). - Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. - Default value: `lower`. - EOT - - validation { - condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "descriptor_formats" { - type = any - default = {} - description = <<-EOT - Describe additional descriptors to be output in the `descriptors` output map. - Map of maps. Keys are names of descriptors. Values are maps of the form - `{ - format = string - labels = list(string) - }` - (Type is `any` so the map values can later be enhanced to provide additional options.) - `format` is a Terraform format string to be passed to the `format()` function. - `labels` is a list of labels, in order, to pass to `format()` function. - Label values will be normalized before being passed to `format()` so they will be - identical to how they appear in `id`. - Default is `{}` (`descriptors` output will be empty). - EOT -} - -#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/main.tf deleted file mode 100644 index 6ad57234b..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/main.tf +++ /dev/null @@ -1,17 +0,0 @@ -module "service_1_label" { - source = "cloudposse/label/null" - version = "0.25.0" - - name = var.service_1_name - - context = module.this.context -} - -module "service_2_label" { - source = "cloudposse/label/null" - version = "0.25.0" - - name = var.service_2_name - - context = module.this.context -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/outputs.tf deleted file mode 100644 index d87b49b2d..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/outputs.tf +++ /dev/null @@ -1,9 +0,0 @@ -output "service_1_id" { - value = module.service_1_label.id - description = "Service 1 ID" -} - -output "service_2_id" { - value = module.service_2_label.id - description = "Service 2 ID" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/variables.tf deleted file mode 100644 index 90c60dc3a..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/variables.tf +++ /dev/null @@ -1,38 +0,0 @@ -variable "region" { - type = string - description = "Region" -} - -variable "service_1_name" { - type = string - description = "Service 1 name" -} - -variable "service_1_list" { - type = list(string) - description = "Service 1 list" - default = [] -} - -variable "service_1_map" { - type = map(string) - description = "Service 1 map" - default = {} -} - -variable "service_2_name" { - type = string - description = "Service 2 name" -} - -variable "service_2_list" { - type = list(string) - description = "Service 2 list" - default = [] -} - -variable "service_2_map" { - type = map(string) - description = "Service 2 map" - default = {} -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/versions.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/versions.tf deleted file mode 100644 index 429c0b36d..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test-component/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 1.0.0" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/context.tf deleted file mode 100644 index 5e0ef8856..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/context.tf +++ /dev/null @@ -1,279 +0,0 @@ -# -# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label -# All other instances of this file should be a copy of that one -# -# -# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf -# and then place it in your Terraform module to automatically get -# Cloud Posse's standard configuration inputs suitable for passing -# to Cloud Posse modules. -# -# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf -# -# Modules should access the whole context as `module.this.context` -# to get the input variables with nulls for defaults, -# for example `context = module.this.context`, -# and access individual variables as `module.this.`, -# with final values filled in. -# -# For example, when using defaults, `module.this.context.delimiter` -# will be null, and `module.this.delimiter` will be `-` (hyphen). -# - -module "this" { - source = "cloudposse/label/null" - version = "0.25.0" # requires Terraform >= 0.13.0 - - enabled = var.enabled - namespace = var.namespace - tenant = var.tenant - environment = var.environment - stage = var.stage - name = var.name - delimiter = var.delimiter - attributes = var.attributes - tags = var.tags - additional_tag_map = var.additional_tag_map - label_order = var.label_order - regex_replace_chars = var.regex_replace_chars - id_length_limit = var.id_length_limit - label_key_case = var.label_key_case - label_value_case = var.label_value_case - descriptor_formats = var.descriptor_formats - labels_as_tags = var.labels_as_tags - - context = var.context -} - -# Copy contents of cloudposse/terraform-null-label/variables.tf here - -variable "context" { - type = any - default = { - enabled = true - namespace = null - tenant = null - environment = null - stage = null - name = null - delimiter = null - attributes = [] - tags = {} - additional_tag_map = {} - regex_replace_chars = null - label_order = [] - id_length_limit = null - label_key_case = null - label_value_case = null - descriptor_formats = {} - # Note: we have to use [] instead of null for unset lists due to - # https://github.com/hashicorp/terraform/issues/28137 - # which was not fixed until Terraform 1.0.0, - # but we want the default to be all the labels in `label_order` - # and we want users to be able to prevent all tag generation - # by setting `labels_as_tags` to `[]`, so we need - # a different sentinel to indicate "default" - labels_as_tags = ["unset"] - } - description = <<-EOT - Single object for setting entire context at once. - See description of individual variables for details. - Leave string and numeric variables as `null` to use default value. - Individual variable settings (non-null) override settings in context object, - except for attributes, tags, and additional_tag_map, which are merged. - EOT - - validation { - condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`." - } - - validation { - condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "enabled" { - type = bool - default = null - description = "Set to false to prevent the module from creating any resources" -} - -variable "namespace" { - type = string - default = null - description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" -} - -variable "tenant" { - type = string - default = null - description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" -} - -variable "environment" { - type = string - default = null - description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" -} - -variable "stage" { - type = string - default = null - description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" -} - -variable "name" { - type = string - default = null - description = <<-EOT - ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. - This is the only ID element not also included as a `tag`. - The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. - EOT -} - -variable "delimiter" { - type = string - default = null - description = <<-EOT - Delimiter to be used between ID elements. - Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. - EOT -} - -variable "attributes" { - type = list(string) - default = [] - description = <<-EOT - ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, - in the order they appear in the list. New attributes are appended to the - end of the list. The elements of the list are joined by the `delimiter` - and treated as a single ID element. - EOT -} - -variable "labels_as_tags" { - type = set(string) - default = ["default"] - description = <<-EOT - Set of labels (ID elements) to include as tags in the `tags` output. - Default is to include all labels. - Tags with empty values will not be included in the `tags` output. - Set to `[]` to suppress all generated tags. - **Notes:** - The value of the `name` tag, if included, will be the `id`, not the `name`. - Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be - changed in later chained modules. Attempts to change it will be silently ignored. - EOT -} - -variable "tags" { - type = map(string) - default = {} - description = <<-EOT - Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). - Neither the tag keys nor the tag values will be modified by this module. - EOT -} - -variable "additional_tag_map" { - type = map(string) - default = {} - description = <<-EOT - Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. - This is for some rare cases where resources want additional configuration of tags - and therefore take a list of maps with tag key, value, and additional configuration. - EOT -} - -variable "label_order" { - type = list(string) - default = null - description = <<-EOT - The order in which the labels (ID elements) appear in the `id`. - Defaults to ["namespace", "environment", "stage", "name", "attributes"]. - You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. - EOT -} - -variable "regex_replace_chars" { - type = string - default = null - description = <<-EOT - Terraform regular expression (regex) string. - Characters matching the regex will be removed from the ID elements. - If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. - EOT -} - -variable "id_length_limit" { - type = number - default = null - description = <<-EOT - Limit `id` to this many characters (minimum 6). - Set to `0` for unlimited length. - Set to `null` for keep the existing setting, which defaults to `0`. - Does not affect `id_full`. - EOT - validation { - condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 - error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." - } -} - -variable "label_key_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of the `tags` keys (label names) for tags generated by this module. - Does not affect keys of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper`. - Default value: `title`. - EOT - - validation { - condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) - error_message = "Allowed values: `lower`, `title`, `upper`." - } -} - -variable "label_value_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of ID elements (labels) as included in `id`, - set as tag values, and output by this module individually. - Does not affect values of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper` and `none` (no transformation). - Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. - Default value: `lower`. - EOT - - validation { - condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "descriptor_formats" { - type = any - default = {} - description = <<-EOT - Describe additional descriptors to be output in the `descriptors` output map. - Map of maps. Keys are names of descriptors. Values are maps of the form - `{ - format = string - labels = list(string) - }` - (Type is `any` so the map values can later be enhanced to provide additional options.) - `format` is a Terraform format string to be passed to the `format()` function. - `labels` is a list of labels, in order, to pass to `format()` function. - Label values will be normalized before being passed to `format()` so they will be - identical to how they appear in `id`. - Default is `{}` (`descriptors` output will be empty). - EOT -} - -#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/main.tf deleted file mode 100644 index 6ad57234b..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/main.tf +++ /dev/null @@ -1,17 +0,0 @@ -module "service_1_label" { - source = "cloudposse/label/null" - version = "0.25.0" - - name = var.service_1_name - - context = module.this.context -} - -module "service_2_label" { - source = "cloudposse/label/null" - version = "0.25.0" - - name = var.service_2_name - - context = module.this.context -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/outputs.tf deleted file mode 100644 index d87b49b2d..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/outputs.tf +++ /dev/null @@ -1,9 +0,0 @@ -output "service_1_id" { - value = module.service_1_label.id - description = "Service 1 ID" -} - -output "service_2_id" { - value = module.service_2_label.id - description = "Service 2 ID" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/variables.tf deleted file mode 100644 index 90c60dc3a..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/variables.tf +++ /dev/null @@ -1,38 +0,0 @@ -variable "region" { - type = string - description = "Region" -} - -variable "service_1_name" { - type = string - description = "Service 1 name" -} - -variable "service_1_list" { - type = list(string) - description = "Service 1 list" - default = [] -} - -variable "service_1_map" { - type = map(string) - description = "Service 1 map" - default = {} -} - -variable "service_2_name" { - type = string - description = "Service 2 name" -} - -variable "service_2_list" { - type = list(string) - description = "Service 2 list" - default = [] -} - -variable "service_2_map" { - type = map(string) - description = "Service 2 map" - default = {} -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/versions.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/versions.tf deleted file mode 100644 index 429c0b36d..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/test/test2/test-component-2/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 1.0.0" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/context.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/context.tf deleted file mode 100644 index 5e0ef8856..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/context.tf +++ /dev/null @@ -1,279 +0,0 @@ -# -# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label -# All other instances of this file should be a copy of that one -# -# -# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf -# and then place it in your Terraform module to automatically get -# Cloud Posse's standard configuration inputs suitable for passing -# to Cloud Posse modules. -# -# curl -sL https://raw.githubusercontent.com/cloudposse/terraform-null-label/master/exports/context.tf -o context.tf -# -# Modules should access the whole context as `module.this.context` -# to get the input variables with nulls for defaults, -# for example `context = module.this.context`, -# and access individual variables as `module.this.`, -# with final values filled in. -# -# For example, when using defaults, `module.this.context.delimiter` -# will be null, and `module.this.delimiter` will be `-` (hyphen). -# - -module "this" { - source = "cloudposse/label/null" - version = "0.25.0" # requires Terraform >= 0.13.0 - - enabled = var.enabled - namespace = var.namespace - tenant = var.tenant - environment = var.environment - stage = var.stage - name = var.name - delimiter = var.delimiter - attributes = var.attributes - tags = var.tags - additional_tag_map = var.additional_tag_map - label_order = var.label_order - regex_replace_chars = var.regex_replace_chars - id_length_limit = var.id_length_limit - label_key_case = var.label_key_case - label_value_case = var.label_value_case - descriptor_formats = var.descriptor_formats - labels_as_tags = var.labels_as_tags - - context = var.context -} - -# Copy contents of cloudposse/terraform-null-label/variables.tf here - -variable "context" { - type = any - default = { - enabled = true - namespace = null - tenant = null - environment = null - stage = null - name = null - delimiter = null - attributes = [] - tags = {} - additional_tag_map = {} - regex_replace_chars = null - label_order = [] - id_length_limit = null - label_key_case = null - label_value_case = null - descriptor_formats = {} - # Note: we have to use [] instead of null for unset lists due to - # https://github.com/hashicorp/terraform/issues/28137 - # which was not fixed until Terraform 1.0.0, - # but we want the default to be all the labels in `label_order` - # and we want users to be able to prevent all tag generation - # by setting `labels_as_tags` to `[]`, so we need - # a different sentinel to indicate "default" - labels_as_tags = ["unset"] - } - description = <<-EOT - Single object for setting entire context at once. - See description of individual variables for details. - Leave string and numeric variables as `null` to use default value. - Individual variable settings (non-null) override settings in context object, - except for attributes, tags, and additional_tag_map, which are merged. - EOT - - validation { - condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`." - } - - validation { - condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "enabled" { - type = bool - default = null - description = "Set to false to prevent the module from creating any resources" -} - -variable "namespace" { - type = string - default = null - description = "ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique" -} - -variable "tenant" { - type = string - default = null - description = "ID element _(Rarely used, not included by default)_. A customer identifier, indicating who this instance of a resource is for" -} - -variable "environment" { - type = string - default = null - description = "ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT'" -} - -variable "stage" { - type = string - default = null - description = "ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release'" -} - -variable "name" { - type = string - default = null - description = <<-EOT - ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'. - This is the only ID element not also included as a `tag`. - The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. - EOT -} - -variable "delimiter" { - type = string - default = null - description = <<-EOT - Delimiter to be used between ID elements. - Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. - EOT -} - -variable "attributes" { - type = list(string) - default = [] - description = <<-EOT - ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`, - in the order they appear in the list. New attributes are appended to the - end of the list. The elements of the list are joined by the `delimiter` - and treated as a single ID element. - EOT -} - -variable "labels_as_tags" { - type = set(string) - default = ["default"] - description = <<-EOT - Set of labels (ID elements) to include as tags in the `tags` output. - Default is to include all labels. - Tags with empty values will not be included in the `tags` output. - Set to `[]` to suppress all generated tags. - **Notes:** - The value of the `name` tag, if included, will be the `id`, not the `name`. - Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be - changed in later chained modules. Attempts to change it will be silently ignored. - EOT -} - -variable "tags" { - type = map(string) - default = {} - description = <<-EOT - Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`). - Neither the tag keys nor the tag values will be modified by this module. - EOT -} - -variable "additional_tag_map" { - type = map(string) - default = {} - description = <<-EOT - Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`. - This is for some rare cases where resources want additional configuration of tags - and therefore take a list of maps with tag key, value, and additional configuration. - EOT -} - -variable "label_order" { - type = list(string) - default = null - description = <<-EOT - The order in which the labels (ID elements) appear in the `id`. - Defaults to ["namespace", "environment", "stage", "name", "attributes"]. - You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. - EOT -} - -variable "regex_replace_chars" { - type = string - default = null - description = <<-EOT - Terraform regular expression (regex) string. - Characters matching the regex will be removed from the ID elements. - If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. - EOT -} - -variable "id_length_limit" { - type = number - default = null - description = <<-EOT - Limit `id` to this many characters (minimum 6). - Set to `0` for unlimited length. - Set to `null` for keep the existing setting, which defaults to `0`. - Does not affect `id_full`. - EOT - validation { - condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 - error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." - } -} - -variable "label_key_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of the `tags` keys (label names) for tags generated by this module. - Does not affect keys of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper`. - Default value: `title`. - EOT - - validation { - condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) - error_message = "Allowed values: `lower`, `title`, `upper`." - } -} - -variable "label_value_case" { - type = string - default = null - description = <<-EOT - Controls the letter case of ID elements (labels) as included in `id`, - set as tag values, and output by this module individually. - Does not affect values of tags passed in via the `tags` input. - Possible values: `lower`, `title`, `upper` and `none` (no transformation). - Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs. - Default value: `lower`. - EOT - - validation { - condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) - error_message = "Allowed values: `lower`, `title`, `upper`, `none`." - } -} - -variable "descriptor_formats" { - type = any - default = {} - description = <<-EOT - Describe additional descriptors to be output in the `descriptors` output map. - Map of maps. Keys are names of descriptors. Values are maps of the form - `{ - format = string - labels = list(string) - }` - (Type is `any` so the map values can later be enhanced to provide additional options.) - `format` is a Terraform format string to be passed to the `format()` function. - `labels` is a list of labels, in order, to pass to `format()` function. - Label values will be normalized before being passed to `format()` so they will be - identical to how they appear in `id`. - Default is `{}` (`descriptors` output will be empty). - EOT -} - -#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/main.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/main.tf deleted file mode 100644 index 3496a4e33..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/main.tf +++ /dev/null @@ -1,16 +0,0 @@ -module "service_1_label" { - source = "cloudposse/label/null" - version = "0.25.0" - - name = var.service_1_name - - context = module.this.context -} - -module "service_2_label" { - source = "../../../modules/label" - - name = var.service_2_name - - context = module.this.context -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/outputs.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/outputs.tf deleted file mode 100644 index ae890ae82..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/outputs.tf +++ /dev/null @@ -1,9 +0,0 @@ -output "service_1_id" { - value = module.service_1_label.id - description = "Service 1 ID" -} - -output "service_2_id" { - value = module.service_2_label.label.id - description = "Service 2 ID" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/policies/policy1.rego b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/policies/policy1.rego deleted file mode 100644 index 6fa6fec89..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/policies/policy1.rego +++ /dev/null @@ -1,5 +0,0 @@ -package atmos - -allow { - true -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/variables.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/variables.tf deleted file mode 100644 index 90c60dc3a..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/variables.tf +++ /dev/null @@ -1,38 +0,0 @@ -variable "region" { - type = string - description = "Region" -} - -variable "service_1_name" { - type = string - description = "Service 1 name" -} - -variable "service_1_list" { - type = list(string) - description = "Service 1 list" - default = [] -} - -variable "service_1_map" { - type = map(string) - description = "Service 1 map" - default = {} -} - -variable "service_2_name" { - type = string - description = "Service 2 name" -} - -variable "service_2_list" { - type = list(string) - description = "Service 2 list" - default = [] -} - -variable "service_2_map" { - type = map(string) - description = "Service 2 map" - default = {} -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/versions.tf b/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/versions.tf deleted file mode 100644 index 429c0b36d..000000000 --- a/tests/fixtures/scenarios/editorconfig/components/terraform/top-level-component1/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 1.0.0" -} diff --git a/tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/context.tf b/tests/fixtures/scenarios/editorconfig/context.tf similarity index 100% rename from tests/fixtures/scenarios/editorconfig/components/terraform/infra/vpc-flow-logs-bucket/context.tf rename to tests/fixtures/scenarios/editorconfig/context.tf diff --git a/tests/test-cases/validate-editorconfig.yaml b/tests/test-cases/validate-editorconfig.yaml index 9af4bdd8d..ed1602ba4 100644 --- a/tests/test-cases/validate-editorconfig.yaml +++ b/tests/test-cases/validate-editorconfig.yaml @@ -23,5 +23,5 @@ tests: - "editorconfig" expect: stderr: - - "16 errors found" + - "2 errors found" exit_code: 1 From 40e6bb5083363bfd9f382f92859c8a96b4f09001 Mon Sep 17 00:00:00 2001 From: Sam Tholiya Date: Wed, 15 Jan 2025 22:19:52 +0100 Subject: [PATCH 32/32] Update tests/fixtures/scenarios/editorconfig/README.md Co-authored-by: Erik Osterman (CEO @ Cloud Posse) --- tests/fixtures/scenarios/editorconfig/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/fixtures/scenarios/editorconfig/README.md b/tests/fixtures/scenarios/editorconfig/README.md index 7e72940f5..c245c9cb1 100644 --- a/tests/fixtures/scenarios/editorconfig/README.md +++ b/tests/fixtures/scenarios/editorconfig/README.md @@ -1,5 +1,7 @@ -# Atmos Tests +# Atmos Editor Config Test Scenarios -These configurations are used by the Atmos tests and CI to test/validate configurations and behaviors of Atmos. -Thus, they contain many deliberately broken examples and should not be used. -Instead, please see our [Quick Start](../quick-start) example. +This directory contains configurations specifically designed to test and validate the behavior of editor configurations when working with Atmos. + +These files include intentionally broken examples and edge cases to help ensure that editors and tools handle Atmos configurations correctly. + +**Do not use these examples for actual deployments.**