From 84e9fd033cca997f64a3605ae03ab7503d8f2df3 Mon Sep 17 00:00:00 2001 From: Andriy Knysh Date: Fri, 12 Nov 2021 09:44:20 -0500 Subject: [PATCH] Detect ENV vars in YAML stack config and set them for command execution. Make `workspace_key_prefix` config DRY (#77) * Detect ENV vars in YAML stack config and set them for command execution * Detect ENV vars in YAML stack config and set them for command execution * Detect ENV vars in YAML stack config and set them for command execution * Make `workspace_key_prefix` config DRY * Make `workspace_key_prefix` config DRY * Make `workspace_key_prefix` config DRY * Make `workspace_key_prefix` config DRY * Make `workspace_key_prefix` config DRY --- .../terraform/test-component-override.yaml | 4 ++ .../catalog/terraform/test-component.yaml | 7 +-- .../terraform/top-level-component1.yaml | 3 -- internal/exec/describe.go | 11 ++-- internal/exec/helmfile.go | 11 ++-- internal/exec/terraform.go | 34 +++++++----- internal/exec/terraform_generate.go | 29 +++++++--- internal/exec/utils.go | 53 ++++++++++++++----- pkg/config/schema.go | 2 + pkg/stack/stack_processor.go | 33 ++++++++---- pkg/stack/stack_processor_test.go | 8 +++ 11 files changed, 138 insertions(+), 57 deletions(-) diff --git a/examples/complete/stacks/catalog/terraform/test-component-override.yaml b/examples/complete/stacks/catalog/terraform/test-component-override.yaml index d81a6cc36..6231c857a 100644 --- a/examples/complete/stacks/catalog/terraform/test-component-override.yaml +++ b/examples/complete/stacks/catalog/terraform/test-component-override.yaml @@ -16,6 +16,10 @@ components: component: "test/test-component" # Other variables can be overridden here vars: {} + env: + TEST_ENV_VAR1: "val1-override" + TEST_ENV_VAR3: "val3-override" + TEST_ENV_VAR4: "val4" # Override remote state backend for this component remote_state_backend_type: static # s3, remote, vault, static, etc. remote_state_backend: diff --git a/examples/complete/stacks/catalog/terraform/test-component.yaml b/examples/complete/stacks/catalog/terraform/test-component.yaml index 1a942337d..c3e5a8921 100644 --- a/examples/complete/stacks/catalog/terraform/test-component.yaml +++ b/examples/complete/stacks/catalog/terraform/test-component.yaml @@ -9,11 +9,12 @@ import: components: terraform: "test/test-component": - backend: - s3: - workspace_key_prefix: test-test-component settings: spacelift: workspace_enabled: true vars: enabled: true + env: + TEST_ENV_VAR1: "val1" + TEST_ENV_VAR2: "val2" + TEST_ENV_VAR3: "val3" diff --git a/examples/complete/stacks/catalog/terraform/top-level-component1.yaml b/examples/complete/stacks/catalog/terraform/top-level-component1.yaml index a726aebb8..ba155d0bd 100644 --- a/examples/complete/stacks/catalog/terraform/top-level-component1.yaml +++ b/examples/complete/stacks/catalog/terraform/top-level-component1.yaml @@ -9,9 +9,6 @@ import: components: terraform: top-level-component1: - backend: - s3: - workspace_key_prefix: top-level-component1 settings: spacelift: workspace_enabled: true diff --git a/internal/exec/describe.go b/internal/exec/describe.go index 72bc20d59..ce9f33fc4 100644 --- a/internal/exec/describe.go +++ b/internal/exec/describe.go @@ -73,12 +73,12 @@ func ExecuteDescribeComponent(cmd *cobra.Command, args []string) error { if c.ProcessedConfig.StackType == "Directory" { componentSection, componentVarsSection, - _, _, _, _, + _, _, _, _, _, err = findComponentConfig(stack, stacksMap, "terraform", component) if err != nil { componentSection, componentVarsSection, - _, _, _, _, + _, _, _, _, _, err = findComponentConfig(stack, stacksMap, "helmfile", component) if err != nil { return err @@ -116,12 +116,12 @@ func ExecuteDescribeComponent(cmd *cobra.Command, args []string) error { for stackName := range stacksMap { componentSection, componentVarsSection, - _, _, _, _, + _, _, _, _, _, err = findComponentConfig(stackName, stacksMap, "terraform", component) if err != nil { componentSection, componentVarsSection, - _, _, _, _, + _, _, _, _, _, err = findComponentConfig(stackName, stacksMap, "helmfile", component) if err != nil { continue @@ -174,7 +174,8 @@ func ExecuteDescribeComponent(cmd *cobra.Command, args []string) error { } if g.LogVerbose { - color.Cyan("\nComponent config:\n\n") + fmt.Println() + color.Cyan("Component config:\n\n") } err = u.PrintAsYAML(componentSection) diff --git a/internal/exec/helmfile.go b/internal/exec/helmfile.go index 48c9d24a6..c6eddb9c2 100644 --- a/internal/exec/helmfile.go +++ b/internal/exec/helmfile.go @@ -118,9 +118,9 @@ func ExecuteHelmfile(cmd *cobra.Command, args []string) error { var workingDir string if len(info.ComponentFolderPrefix) == 0 { - workingDir = fmt.Sprintf("%s/%s", c.Config.Components.Helmfile.BasePath, info.Component) + workingDir = path.Join(c.Config.Components.Helmfile.BasePath, info.Component) } else { - workingDir = fmt.Sprintf("%s/%s/%s", c.Config.Components.Helmfile.BasePath, info.ComponentFolderPrefix, info.Component) + workingDir = path.Join(c.Config.Components.Helmfile.BasePath, info.ComponentFolderPrefix, info.Component) } fmt.Println(fmt.Sprintf("Working dir: %s\n\n", workingDir)) @@ -135,7 +135,7 @@ func ExecuteHelmfile(cmd *cobra.Command, args []string) error { allArgsAndFlags = append(allArgsAndFlags, info.AdditionalArgsAndFlags...) // Prepare ENV vars - envVars := []string{ + envVars := append(info.ComponentEnvList, []string{ fmt.Sprintf("AWS_PROFILE=%s", helmAwsProfile), fmt.Sprintf("KUBECONFIG=%s", kubeconfigPath), fmt.Sprintf("NAMESPACE=%s", context.Namespace), @@ -144,6 +144,11 @@ func ExecuteHelmfile(cmd *cobra.Command, args []string) error { fmt.Sprintf("STAGE=%s", context.Stage), fmt.Sprintf("REGION=%s", context.Region), fmt.Sprintf("STACK=%s", info.Stack), + }...) + + color.Cyan("Using ENV vars:\n") + for _, v := range envVars { + fmt.Println(v) } err = execCommand(info.Command, allArgsAndFlags, componentPath, envVars) diff --git a/internal/exec/terraform.go b/internal/exec/terraform.go index 466d7b94a..be2746336 100644 --- a/internal/exec/terraform.go +++ b/internal/exec/terraform.go @@ -131,19 +131,21 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error { } // Auto generate backend file - var backendFileName string if c.Config.Components.Terraform.AutoGenerateBackendFile == true { + var backendFileName string fmt.Println() if len(info.ComponentFolderPrefix) == 0 { - backendFileName = fmt.Sprintf("%s/%s/backend.tf.json", + backendFileName = path.Join( c.Config.Components.Terraform.BasePath, finalComponent, + "backend.tf.json", ) } else { - backendFileName = fmt.Sprintf("%s/%s/%s/backend.tf.json", + backendFileName = path.Join( c.Config.Components.Terraform.BasePath, info.ComponentFolderPrefix, finalComponent, + "backend.tf.json", ) } color.Cyan("Writing backend config to file:") @@ -167,7 +169,7 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error { if info.SubCommand == "workspace" { initCommandWithArguments = []string{"init", "-reconfigure"} } - err = execCommand(info.Command, initCommandWithArguments, componentPath, nil) + err = execCommand(info.Command, initCommandWithArguments, componentPath, info.ComponentEnvList) if err != nil { return err } @@ -201,12 +203,21 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error { var workingDir string if len(info.ComponentFolderPrefix) == 0 { - workingDir = fmt.Sprintf("%s/%s", c.Config.Components.Terraform.BasePath, finalComponent) + workingDir = path.Join(c.Config.Components.Terraform.BasePath, finalComponent) } else { - workingDir = fmt.Sprintf("%s/%s/%s", c.Config.Components.Terraform.BasePath, info.ComponentFolderPrefix, finalComponent) + workingDir = path.Join(c.Config.Components.Terraform.BasePath, info.ComponentFolderPrefix, finalComponent) } fmt.Println(fmt.Sprintf(fmt.Sprintf("Working dir: %s", workingDir))) + // Print ENV vars if they are found in the component stack config + if len(info.ComponentEnvList) > 0 { + fmt.Println() + color.Cyan("Using ENV vars:\n") + for _, v := range info.ComponentEnvList { + fmt.Println(v) + } + } + var workspaceName string if len(info.BaseComponent) > 0 { workspaceName = fmt.Sprintf("%s-%s", info.ContextPrefix, info.Component) @@ -238,9 +249,9 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error { allArgsAndFlags = append(allArgsAndFlags, info.AdditionalArgsAndFlags...) // Run `terraform workspace` - err = execCommand(info.Command, []string{"workspace", "select", workspaceName}, componentPath, nil) + err = execCommand(info.Command, []string{"workspace", "select", workspaceName}, componentPath, info.ComponentEnvList) if err != nil { - err = execCommand(info.Command, []string{"workspace", "new", workspaceName}, componentPath, nil) + err = execCommand(info.Command, []string{"workspace", "new", workspaceName}, componentPath, info.ComponentEnvList) if err != nil { return err } @@ -264,7 +275,7 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error { // Execute the command if info.SubCommand != "workspace" { - err = execCommand(info.Command, allArgsAndFlags, componentPath, nil) + err = execCommand(info.Command, allArgsAndFlags, componentPath, info.ComponentEnvList) if err != nil { return err } @@ -276,11 +287,6 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error { _ = os.Remove(planFilePath) } - err = os.Remove(varFileName) - if err != nil { - color.Yellow("Error deleting terraform varfile: %s\n", err) - } - return nil } diff --git a/internal/exec/terraform_generate.go b/internal/exec/terraform_generate.go index 562fdd9c6..3ba2787dc 100644 --- a/internal/exec/terraform_generate.go +++ b/internal/exec/terraform_generate.go @@ -9,6 +9,7 @@ import ( "github.com/fatih/color" "github.com/pkg/errors" "github.com/spf13/cobra" + "path" "strings" ) @@ -75,6 +76,7 @@ func ExecuteTerraformGenerateBackend(cmd *cobra.Command, args []string) error { if config.ProcessedConfig.StackType == "Directory" { componentSection, componentVarsSection, + _, componentBackendSection, componentBackendType, _, _, @@ -83,7 +85,9 @@ func ExecuteTerraformGenerateBackend(cmd *cobra.Command, args []string) error { return err } } else { - color.Cyan("Searching for stack config where the component '%s' is defined\n", component) + if g.LogVerbose == true { + color.Cyan("Searching for stack config where the component '%s' is defined\n", component) + } if len(config.Config.Stacks.NamePattern) < 1 { return errors.New("stack name pattern must be provided in 'stacks.name_pattern' config or 'ATMOS_STACKS_NAME_PATTERN' ENV variable") @@ -112,6 +116,7 @@ func ExecuteTerraformGenerateBackend(cmd *cobra.Command, args []string) error { for stackName := range stacksMap { componentSection, componentVarsSection, + _, componentBackendSection, componentBackendType, _, _, @@ -146,7 +151,9 @@ func ExecuteTerraformGenerateBackend(cmd *cobra.Command, args []string) error { } if tenantFound == true && environmentFound == true && stageFound == true { - color.Green("Found stack config for the '%s' component in the '%s' stack\n\n", component, stackName) + if g.LogVerbose == true { + color.Green("Found stack config for the '%s' component in the '%s' stack\n\n", component, stackName) + } stack = stackName break } @@ -173,7 +180,8 @@ func ExecuteTerraformGenerateBackend(cmd *cobra.Command, args []string) error { var componentBackendConfig = generateComponentBackendConfig(componentBackendType, componentBackendSection) - color.Cyan("\nComponent backend config:\n\n") + fmt.Println() + color.Cyan("Component backend config:\n\n") err = utils.PrintAsJSON(componentBackendConfig) if err != nil { return err @@ -197,12 +205,17 @@ func ExecuteTerraformGenerateBackend(cmd *cobra.Command, args []string) error { finalComponent = component } - // Write backend to file - var varFileName = fmt.Sprintf("%s/%s/backend.tf.json", config.Config.Components.Terraform.BasePath, finalComponent) + // Write backend config to file + var backendFileName = path.Join( + config.Config.Components.Terraform.BasePath, + finalComponent, + "backend.tf.json", + ) - color.Cyan("\nWriting backend config to file:") - fmt.Println(varFileName) - err = utils.WriteToFileAsJSON(varFileName, componentBackendConfig, 0644) + fmt.Println() + color.Cyan("Writing backend config to file:") + fmt.Println(backendFileName) + err = utils.WriteToFileAsJSON(backendFileName, componentBackendConfig, 0644) if err != nil { return err } diff --git a/internal/exec/utils.go b/internal/exec/utils.go index 3183b6557..da6b495ae 100644 --- a/internal/exec/utils.go +++ b/internal/exec/utils.go @@ -37,13 +37,14 @@ func findComponentConfig( stacksMap map[string]interface{}, componentType string, component string, -) (map[string]interface{}, map[interface{}]interface{}, map[interface{}]interface{}, string, string, string, error) { +) (map[string]interface{}, map[interface{}]interface{}, map[interface{}]interface{}, map[interface{}]interface{}, string, string, string, error) { var stackSection map[interface{}]interface{} var componentsSection map[string]interface{} var componentTypeSection map[string]interface{} var componentSection map[string]interface{} var componentVarsSection map[interface{}]interface{} + var componentEnvSection map[interface{}]interface{} var componentBackendSection map[interface{}]interface{} var componentBackendType string var baseComponentPath string @@ -51,28 +52,28 @@ func findComponentConfig( var ok bool if len(stack) == 0 { - return nil, nil, nil, "", "", "", errors.New("stack must be provided and must not be empty") + return nil, nil, nil, nil, "", "", "", errors.New("stack must be provided and must not be empty") } if len(component) == 0 { - return nil, nil, nil, "", "", "", errors.New("component must be provided and must not be empty") + return nil, nil, nil, nil, "", "", "", errors.New("component must be provided and must not be empty") } if len(componentType) == 0 { - return nil, nil, nil, "", "", "", errors.New("component type must be provided and must not be empty") + return nil, nil, nil, nil, "", "", "", errors.New("component type must be provided and must not be empty") } if stackSection, ok = stacksMap[stack].(map[interface{}]interface{}); !ok { - return nil, nil, nil, "", "", "", errors.New(fmt.Sprintf("Stack '%s' does not exist", stack)) + return nil, nil, nil, nil, "", "", "", errors.New(fmt.Sprintf("Stack '%s' does not exist", stack)) } if componentsSection, ok = stackSection["components"].(map[string]interface{}); !ok { - return nil, nil, nil, "", "", "", errors.New(fmt.Sprintf("'components' section is missing in the stack '%s'", stack)) + return nil, nil, nil, nil, "", "", "", errors.New(fmt.Sprintf("'components' section is missing in the stack '%s'", stack)) } if componentTypeSection, ok = componentsSection[componentType].(map[string]interface{}); !ok { - return nil, nil, nil, "", "", "", errors.New(fmt.Sprintf("'components/%s' section is missing in the stack '%s'", componentType, stack)) + return nil, nil, nil, nil, "", "", "", errors.New(fmt.Sprintf("'components/%s' section is missing in the stack '%s'", componentType, stack)) } if componentSection, ok = componentTypeSection[component].(map[string]interface{}); !ok { - return nil, nil, nil, "", "", "", errors.New(fmt.Sprintf("Invalid or missing configuration for the component '%s' in the stack '%s'", component, stack)) + return nil, nil, nil, nil, "", "", "", errors.New(fmt.Sprintf("Invalid or missing configuration for the component '%s' in the stack '%s'", component, stack)) } if componentVarsSection, ok = componentSection["vars"].(map[interface{}]interface{}); !ok { - return nil, nil, nil, "", "", "", errors.New(fmt.Sprintf("Missing 'vars' section for the component '%s' in the stack '%s'", component, stack)) + return nil, nil, nil, nil, "", "", "", errors.New(fmt.Sprintf("Missing 'vars' section for the component '%s' in the stack '%s'", component, stack)) } if componentBackendSection, ok = componentSection["backend"].(map[interface{}]interface{}); !ok { componentBackendSection = nil @@ -86,8 +87,18 @@ func findComponentConfig( if command, ok = componentSection["command"].(string); !ok { command = "" } + if componentEnvSection, ok = componentSection["env"].(map[interface{}]interface{}); !ok { + componentEnvSection = map[interface{}]interface{}{} + } - return componentSection, componentVarsSection, componentBackendSection, componentBackendType, baseComponentPath, command, nil + return componentSection, + componentVarsSection, + componentEnvSection, + componentBackendSection, + componentBackendType, + baseComponentPath, + command, + nil } // processConfigAndStacks processes CLI config and stacks @@ -181,6 +192,7 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str // Check and process stacks if c.ProcessedConfig.StackType == "Directory" { _, configAndStacksInfo.ComponentVarsSection, + configAndStacksInfo.ComponentEnvSection, configAndStacksInfo.ComponentBackendSection, configAndStacksInfo.ComponentBackendType, configAndStacksInfo.BaseComponentPath, @@ -188,6 +200,8 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str if err != nil { return configAndStacksInfo, err } + + configAndStacksInfo.ComponentEnvList = convertEnvVars(configAndStacksInfo.ComponentEnvSection) } else { if g.LogVerbose { color.Cyan("Searching for stack config where the component '%s' is defined\n", configAndStacksInfo.ComponentFromArg) @@ -220,6 +234,7 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str for stackName := range stacksMap { _, configAndStacksInfo.ComponentVarsSection, + configAndStacksInfo.ComponentEnvSection, configAndStacksInfo.ComponentBackendSection, configAndStacksInfo.ComponentBackendType, configAndStacksInfo.BaseComponentPath, @@ -228,6 +243,8 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str continue } + configAndStacksInfo.ComponentEnvList = convertEnvVars(configAndStacksInfo.ComponentEnvSection) + tenantFound = true environmentFound = true stageFound = true @@ -468,9 +485,10 @@ func execCommand(command string, args []string, dir string, env []string) error cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stdout - color.Cyan("\nExecuting command:\n") - fmt.Println(cmd.String()) + fmt.Println() + color.Cyan("Executing command:\n") + fmt.Println(cmd.String()) return cmd.Run() } @@ -483,3 +501,14 @@ func generateComponentBackendConfig(backendType string, backendConfig map[interf }, } } + +// Convert ENV vars from a map to a list of strings in the format ["key1=val1", "key2=val2", "key3=val3" ...] +func convertEnvVars(envVarsMap map[interface{}]interface{}) []string { + res := []string{} + if envVarsMap != nil { + for k, v := range envVarsMap { + res = append(res, fmt.Sprintf("%s=%s", k, v)) + } + } + return res +} diff --git a/pkg/config/schema.go b/pkg/config/schema.go index f0a2c0936..fa2b68af1 100644 --- a/pkg/config/schema.go +++ b/pkg/config/schema.go @@ -81,6 +81,8 @@ type ConfigAndStacksInfo struct { Command string SubCommand string ComponentVarsSection map[interface{}]interface{} + ComponentEnvSection map[interface{}]interface{} + ComponentEnvList []string ComponentBackendSection map[interface{}]interface{} ComponentBackendType string AdditionalArgsAndFlags []string diff --git a/pkg/stack/stack_processor.go b/pkg/stack/stack_processor.go index 02a2f8df3..8d419e601 100644 --- a/pkg/stack/stack_processor.go +++ b/pkg/stack/stack_processor.go @@ -342,7 +342,8 @@ func ProcessConfig( if allTerraformComponents, ok := globalComponentsSection["terraform"]; ok { allTerraformComponentsMap := allTerraformComponents.(map[interface{}]interface{}) - for component, v := range allTerraformComponentsMap { + for cmp, v := range allTerraformComponentsMap { + component := cmp.(string) componentMap := v.(map[interface{}]interface{}) componentVars := map[interface{}]interface{}{} @@ -433,7 +434,7 @@ func ProcessConfig( baseComponentTerraformCommand = baseComponentCommandSection.(string) } } else { - return nil, errors.New("Terraform component '" + component.(string) + "' defines attribute 'component: " + + return nil, errors.New("Terraform component '" + component + "' defines attribute 'component: " + baseComponentName + "', " + "but `" + baseComponentName + "' is not defined in the stack '" + stack + "'") } } @@ -474,6 +475,19 @@ func ProcessConfig( finalComponentBackend = i.(map[interface{}]interface{}) } + // Check if `backend` section has `workspace_key_prefix` for `s3` backend type + // If it does not, use the component name instead + // It will also be propagated to `remote_state_backend` section of `s3` type + if finalComponentBackendType == "s3" { + if _, ok2 := finalComponentBackend["workspace_key_prefix"].(string); !ok2 { + workspaceKeyPrefixComponent := component + if baseComponentName != "" { + workspaceKeyPrefixComponent = baseComponentName + } + finalComponentBackend["workspace_key_prefix"] = strings.Replace(workspaceKeyPrefixComponent, "/", "-", -1) + } + } + // Final remote state backend finalComponentRemoteStateBackendType := finalComponentBackendType if len(globalRemoteStateBackendType) > 0 { @@ -530,7 +544,7 @@ func ProcessConfig( } if processStackDeps == true { - componentStacks, err := FindComponentStacks("terraform", component.(string), baseComponentName, componentStackMap) + componentStacks, err := FindComponentStacks("terraform", component, baseComponentName, componentStackMap) if err != nil { return nil, err } @@ -540,7 +554,7 @@ func ProcessConfig( } if processComponentDeps == true { - componentDeps, err := FindComponentDependencies(stackName, "terraform", component.(string), baseComponentName, importsConfig) + componentDeps, err := FindComponentDependencies(stackName, "terraform", component, baseComponentName, importsConfig) if err != nil { return nil, err } @@ -549,7 +563,7 @@ func ProcessConfig( comp["deps"] = []string{} } - terraformComponents[component.(string)] = comp + terraformComponents[component] = comp } } } @@ -559,7 +573,8 @@ func ProcessConfig( if allHelmfileComponents, ok := globalComponentsSection["helmfile"]; ok { allHelmfileComponentsMap := allHelmfileComponents.(map[interface{}]interface{}) - for component, v := range allHelmfileComponentsMap { + for cmp, v := range allHelmfileComponentsMap { + component := cmp.(string) componentMap := v.(map[interface{}]interface{}) componentVars := map[interface{}]interface{}{} @@ -604,7 +619,7 @@ func ProcessConfig( comp["command"] = componentHelmfileCommand if processStackDeps == true { - componentStacks, err := FindComponentStacks("helmfile", component.(string), "", componentStackMap) + componentStacks, err := FindComponentStacks("helmfile", component, "", componentStackMap) if err != nil { return nil, err } @@ -614,7 +629,7 @@ func ProcessConfig( } if processComponentDeps == true { - componentDeps, err := FindComponentDependencies(stackName, "helmfile", component.(string), "", importsConfig) + componentDeps, err := FindComponentDependencies(stackName, "helmfile", component, "", importsConfig) if err != nil { return nil, err } @@ -623,7 +638,7 @@ func ProcessConfig( comp["deps"] = []string{} } - helmfileComponents[component.(string)] = comp + helmfileComponents[component] = comp } } } diff --git a/pkg/stack/stack_processor_test.go b/pkg/stack/stack_processor_test.go index 3b0614b80..d3cb0f755 100644 --- a/pkg/stack/stack_processor_test.go +++ b/pkg/stack/stack_processor_test.go @@ -113,6 +113,14 @@ func TestStackProcessor(t *testing.T) { assert.Equal(t, 3, testTestComponentOverrideComponentRemoteStateBackendVal3) assert.Equal(t, nil, testTestComponentOverrideComponentRemoteStateBackendVal4) + topLevelComponent1 := terraformComponents["top-level-component1"].(map[interface{}]interface{}) + topLevelComponent1Backend := topLevelComponent1["backend"].(map[interface{}]interface{}) + topLevelComponent1RemoteSateBackend := topLevelComponent1["remote_state_backend"].(map[interface{}]interface{}) + topLevelComponent1BackendWorkspaceKeyPrefix := topLevelComponent1Backend["workspace_key_prefix"] + topLevelComponent1RemoteStateBackendWorkspaceKeyPrefix := topLevelComponent1RemoteSateBackend["workspace_key_prefix"] + assert.Equal(t, "top-level-component1", topLevelComponent1BackendWorkspaceKeyPrefix) + assert.Equal(t, "top-level-component1", topLevelComponent1RemoteStateBackendWorkspaceKeyPrefix) + yamlConfig, err := yaml.Marshal(mapConfig1) assert.Nil(t, err) t.Log(string(yamlConfig))