From 803ec0f34125104e1c7d46bc95893b52032b7cfe Mon Sep 17 00:00:00 2001 From: Andriy Knysh Date: Mon, 18 Apr 2022 10:57:30 -0400 Subject: [PATCH] Add `metadata.terraform_workspace_pattern` (#138) * updates * updates * updates * Add `metadata.terraform_workspace_pattern` * Add `metadata.terraform_workspace_pattern` * Add `metadata.terraform_workspace_pattern` --- .../terraform/test-component-override-2.yaml | 4 ++ internal/exec/stack_utils.go | 37 +++++++++++++++ internal/exec/utils.go | 21 ++++----- pkg/config/schema.go | 13 +++--- pkg/config/utils.go | 21 +++++---- pkg/spacelift/spacelift_stack_processor.go | 45 ++++++++++++------- 6 files changed, 99 insertions(+), 42 deletions(-) create mode 100644 internal/exec/stack_utils.go diff --git a/examples/complete/stacks/catalog/terraform/test-component-override-2.yaml b/examples/complete/stacks/catalog/terraform/test-component-override-2.yaml index 944130946..ab7f062e9 100644 --- a/examples/complete/stacks/catalog/terraform/test-component-override-2.yaml +++ b/examples/complete/stacks/catalog/terraform/test-component-override-2.yaml @@ -40,3 +40,7 @@ components: val2: "5" val3: 7 val4: null + metadata: + # Override Terraform workspace + # Note that by default, Terraform workspace is generated from the context, e.g. `-` + terraform_workspace_pattern: "{tenant}-{environment}-{stage}-{component}" diff --git a/internal/exec/stack_utils.go b/internal/exec/stack_utils.go new file mode 100644 index 000000000..507d29810 --- /dev/null +++ b/internal/exec/stack_utils.go @@ -0,0 +1,37 @@ +package exec + +import ( + "fmt" + c "github.com/cloudposse/atmos/pkg/config" + "strings" +) + +// BuildTerraformWorkspace builds Terraform workspace +func BuildTerraformWorkspace( + stack string, + stackNamePattern string, + componentMetadata map[interface{}]interface{}, + context c.Context, +) (string, error) { + + contextPrefix, err := c.GetContextPrefix(stack, context, stackNamePattern) + if err != nil { + return "", err + } + + var workspace string + + if terraformWorkspacePattern, terraformWorkspacePatternExist := componentMetadata["terraform_workspace_pattern"].(string); terraformWorkspacePatternExist { + // Terraform workspace can be overridden per component in YAML config `metadata.terraform_workspace_pattern` + workspace = c.ReplaceContextTokens(context, terraformWorkspacePattern) + } else if terraformWorkspace, terraformWorkspaceExist := componentMetadata["terraform_workspace"].(string); terraformWorkspaceExist { + // Terraform workspace can be overridden per component in YAML config `metadata.terraform_workspace` + workspace = terraformWorkspace + } else if context.BaseComponent == "" { + workspace = contextPrefix + } else { + workspace = fmt.Sprintf("%s-%s", contextPrefix, context.Component) + } + + return strings.Replace(workspace, "/", "-", -1), nil +} diff --git a/internal/exec/utils.go b/internal/exec/utils.go index 0564c9c99..565cff2de 100644 --- a/internal/exec/utils.go +++ b/internal/exec/utils.go @@ -417,23 +417,23 @@ func ProcessStacks(configAndStacksInfo c.ConfigAndStacksInfo, checkStack bool) ( // Process context configAndStacksInfo.Context = c.GetContextFromVars(configAndStacksInfo.ComponentVarsSection) + configAndStacksInfo.Context.Component = configAndStacksInfo.ComponentFromArg + configAndStacksInfo.Context.BaseComponent = configAndStacksInfo.BaseComponentPath configAndStacksInfo.ContextPrefix, err = c.GetContextPrefix(configAndStacksInfo.Stack, configAndStacksInfo.Context, c.Config.Stacks.NamePattern) if err != nil { return configAndStacksInfo, err } // workspace - var workspace string - // Terraform workspace can be overridden per component in YAML config `metadata.terraform_workspace` - if componentTerraformWorkspace, componentTerraformWorkspaceExist := configAndStacksInfo.ComponentMetadataSection["terraform_workspace"].(string); componentTerraformWorkspaceExist { - workspace = componentTerraformWorkspace - } else if len(configAndStacksInfo.BaseComponent) == 0 { - workspace = configAndStacksInfo.ContextPrefix - } else { - workspace = fmt.Sprintf("%s-%s", configAndStacksInfo.ContextPrefix, configAndStacksInfo.ComponentFromArg) + workspace, err := BuildTerraformWorkspace( + configAndStacksInfo.Stack, + c.Config.Stacks.NamePattern, + configAndStacksInfo.ComponentMetadataSection, + configAndStacksInfo.Context, + ) + if err != nil { + return configAndStacksInfo, err } - - workspace = strings.Replace(workspace, "/", "-", -1) configAndStacksInfo.TerraformWorkspace = workspace configAndStacksInfo.ComponentSection["workspace"] = workspace @@ -637,6 +637,7 @@ func processArgsAndFlags(inputArgsAndFlags []string) (c.ArgsAndFlagsInfo, error) return info, nil } +// generateComponentBackendConfig generates backend config for components func generateComponentBackendConfig(backendType string, backendConfig map[interface{}]interface{}) map[string]interface{} { return map[string]interface{}{ "terraform": map[string]interface{}{ diff --git a/pkg/config/schema.go b/pkg/config/schema.go index cdc2a384d..fc9fb78ff 100644 --- a/pkg/config/schema.go +++ b/pkg/config/schema.go @@ -56,12 +56,13 @@ type ProcessedConfiguration struct { } type Context struct { - Namespace string - Tenant string - Environment string - Stage string - Region string - Component string + Namespace string + Tenant string + Environment string + Stage string + Region string + Component string + BaseComponent string } type ArgsAndFlagsInfo struct { diff --git a/pkg/config/utils.go b/pkg/config/utils.go index d3bf6afc2..c11b113fe 100644 --- a/pkg/config/utils.go +++ b/pkg/config/utils.go @@ -428,16 +428,15 @@ func GetContextPrefix(stack string, context Context, stackNamePattern string) (s return contextPrefix, nil } -// ReplaceContextTokens replaces tokens in the context pattern +// ReplaceContextTokens replaces context tokens in the provided pattern and returns a string with all the tokens replaced func ReplaceContextTokens(context Context, pattern string) string { - return strings.Replace( - strings.Replace( - strings.Replace( - strings.Replace( - strings.Replace(pattern, - "{component}", context.Component, 1), - "{namespace}", context.Namespace, 1), - "{environment}", context.Environment, 1), - "{tenant}", context.Tenant, 1), - "{stage}", context.Stage, 1) + r := strings.NewReplacer( + "{base-component}", context.BaseComponent, + "{component}", context.Component, + "{namespace}", context.Namespace, + "{environment}", context.Environment, + "{tenant}", context.Tenant, + "{stage}", context.Stage, + ) + return r.Replace(pattern) } diff --git a/pkg/spacelift/spacelift_stack_processor.go b/pkg/spacelift/spacelift_stack_processor.go index 1f6483b83..c32907057 100644 --- a/pkg/spacelift/spacelift_stack_processor.go +++ b/pkg/spacelift/spacelift_stack_processor.go @@ -2,6 +2,7 @@ package spacelift import ( "fmt" + e "github.com/cloudposse/atmos/internal/exec" c "github.com/cloudposse/atmos/pkg/config" s "github.com/cloudposse/atmos/pkg/stack" u "github.com/cloudposse/atmos/pkg/utils" @@ -366,7 +367,27 @@ func TransformStackConfigToSpaceliftStacks( componentInheritance = i.([]string) } + // Process base component + // Base component can be specified in two places: + // `component` attribute (legacy) + // `metadata.component` attribute + // `metadata.component` takes precedence over `component` + baseComponentName := "" + if baseComponent, baseComponentExist := componentMap["component"]; baseComponentExist { + baseComponentName = baseComponent.(string) + } + // First check if component's `metadata` section exists + // Then check if `metadata.component` exists + if componentMetadata, componentMetadataExists := componentMap["metadata"].(map[interface{}]interface{}); componentMetadataExists { + if componentFromMetadata, componentFromMetadataExists := componentMetadata["component"].(string); componentFromMetadataExists { + baseComponentName = componentFromMetadata + } + } + context := c.GetContextFromVars(componentVars) + context.Component = component + context.BaseComponent = baseComponentName + contextPrefix, err := c.GetContextPrefix(stackName, context, stackNamePattern) if err != nil { return nil, err @@ -381,11 +402,6 @@ func TransformStackConfigToSpaceliftStacks( spaceliftConfig["deps"] = componentDeps spaceliftConfig["stacks"] = componentStacks spaceliftConfig["inheritance"] = componentInheritance - - baseComponentName := "" - if baseComponent, baseComponentExist := componentMap["component"]; baseComponentExist { - baseComponentName = baseComponent.(string) - } spaceliftConfig["base_component"] = baseComponentName // backend @@ -409,16 +425,16 @@ func TransformStackConfigToSpaceliftStacks( spaceliftConfig["metadata"] = componentMetadata // workspace - var workspace string - // Terraform workspace can be overridden per component in YAML config `metadata.terraform_workspace` - if componentTerraformWorkspace, componentTerraformWorkspaceExist := componentMetadata["terraform_workspace"].(string); componentTerraformWorkspaceExist { - workspace = componentTerraformWorkspace - } else if backendTypeName == "s3" && baseComponentName == "" { - workspace = contextPrefix - } else { - workspace = fmt.Sprintf("%s-%s", contextPrefix, component) + workspace, err := e.BuildTerraformWorkspace( + stackName, + stackNamePattern, + componentMetadata, + context, + ) + if err != nil { + return nil, err } - spaceliftConfig["workspace"] = strings.Replace(workspace, "/", "-", -1) + spaceliftConfig["workspace"] = workspace // labels labels := []string{} @@ -460,7 +476,6 @@ func TransformStackConfigToSpaceliftStacks( spaceliftConfig["labels"] = u.UniqueStrings(labels) // Spacelift stack name - context.Component = component spaceliftStackName, spaceliftStackNamePattern := buildSpaceliftStackName(spaceliftSettings, context, contextPrefix) // Add Spacelift stack config to the final map