Skip to content

Commit

Permalink
Update modules (#65)
Browse files Browse the repository at this point in the history
* Update stack and spacelift packages

* Update stack and spacelift packages

* Update stack and spacelift packages

* Update stack and spacelift packages

* Update stack and spacelift packages

* Update stack and spacelift packages

* Update stack and spacelift packages

* Update stack and spacelift packages
  • Loading branch information
aknysh committed Oct 29, 2021
1 parent a51156b commit 538a5d7
Show file tree
Hide file tree
Showing 15 changed files with 526 additions and 47 deletions.
6 changes: 1 addition & 5 deletions .github/auto-release.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
name-template: 'v$RESOLVED_VERSION'

# https://golang.org/ref/mod#versions
tag-template: 'v$RESOLVED_VERSION'

tag-template: '$RESOLVED_VERSION'
version-template: '$MAJOR.$MINOR.$PATCH'

version-resolver:
major:
labels:
Expand Down
11 changes: 8 additions & 3 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ var (
ProcessedConfig ProcessedConfiguration
)

// InitConfig processes and merges configurations in the following order: system dir, home dir, current dir, ENV vars, command-line arguments
// InitConfig finds and merges CLI configurations in the following order: system dir, home dir, current dir, ENV vars, command-line arguments
// https://dev.to/techschoolguru/load-config-from-file-environment-variables-in-golang-with-viper-2j2d
// https://medium.com/@bnprashanth256/reading-configuration-files-and-environment-variables-in-go-golang-c2607f912b63
func InitConfig(configAndStacksInfo ConfigAndStacksInfo) error {
func InitConfig() error {
// Config is loaded from the following locations (from lower to higher priority):
// system dir (`/usr/local/etc/atmos` on Linux, `%LOCALAPPDATA%/atmos` on Windows)
// home dir (~/.atmos)
Expand Down Expand Up @@ -148,8 +148,13 @@ func InitConfig(configAndStacksInfo ConfigAndStacksInfo) error {
return err
}

return nil
}

// ProcessConfig processes and checks CLI configuration
func ProcessConfig(configAndStacksInfo ConfigAndStacksInfo) error {
// Process ENV vars
err = processEnvVars()
err := processEnvVars()
if err != nil {
return err
}
Expand Down
7 changes: 6 additions & 1 deletion internal/exec/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ func ExecuteDescribeComponent(cmd *cobra.Command, args []string) error {
var configAndStacksInfo c.ConfigAndStacksInfo
configAndStacksInfo.Stack = stack

err = c.InitConfig(configAndStacksInfo)
err = c.InitConfig()
if err != nil {
return err
}

err = c.ProcessConfig(configAndStacksInfo)
if err != nil {
return err
}
Expand Down
7 changes: 6 additions & 1 deletion internal/exec/terraform_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ func ExecuteTerraformGenerateBackend(cmd *cobra.Command, args []string) error {
var configAndStacksInfo c.ConfigAndStacksInfo
configAndStacksInfo.Stack = stack

err = c.InitConfig(configAndStacksInfo)
err = c.InitConfig()
if err != nil {
return err
}

err = c.ProcessConfig(configAndStacksInfo)
if err != nil {
return err
}
Expand Down
16 changes: 15 additions & 1 deletion internal/exec/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ func findComponentConfig(
var componentBackendSection map[interface{}]interface{}
var ok bool

if len(stack) == 0 {
return 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")
}
if len(componentType) == 0 {
return 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))
}
Expand Down Expand Up @@ -155,7 +164,12 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str
}

// Process and merge CLI configurations
err = c.InitConfig(configAndStacksInfo)
err = c.InitConfig()
if err != nil {
return configAndStacksInfo, err
}

err = c.ProcessConfig(configAndStacksInfo)
if err != nil {
return configAndStacksInfo, err
}
Expand Down
48 changes: 48 additions & 0 deletions pkg/component/atmos.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 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)

components:
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: "../../examples/complete/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
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: "../../examples/complete/components/helmfile"
# 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: "../../examples/complete/stacks"
# Can also be set using `ATMOS_STACKS_INCLUDED_PATHS` ENV var (comma-separated values string)
included_paths:
- "**/*"
# Can also be set using `ATMOS_STACKS_EXCLUDED_PATHS` ENV var (comma-separated values string)
excluded_paths:
- "globals/**/*"
- "catalog/**/*"
- "**/*globals*"
# Can also be set using `ATMOS_STACKS_NAME_PATTERN` ENV var
name_pattern: "{tenant}-{environment}-{stage}"

logs:
verbose: false
colors: true
235 changes: 235 additions & 0 deletions pkg/component/component_processor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
package component

import (
"fmt"
c "github.com/cloudposse/atmos/internal/config"
s "github.com/cloudposse/atmos/pkg/stack"
"github.com/pkg/errors"
"strings"
)

// ProcessComponentInStack accepts a component and a stack name and returns the component configuration in the stack
func ProcessComponentInStack(component string, stack string) (map[string]interface{}, error) {
var configAndStacksInfo c.ConfigAndStacksInfo
configAndStacksInfo.Stack = stack

err := c.InitConfig()
if err != nil {
return nil, err
}

err = c.ProcessConfig(configAndStacksInfo)
if err != nil {
return nil, err
}

_, stacksMap, err := s.ProcessYAMLConfigFiles(
c.ProcessedConfig.StacksBaseAbsolutePath,
c.ProcessedConfig.StackConfigFilesAbsolutePaths,
true,
true)

if err != nil {
return nil, err
}

var componentSection map[string]interface{}
var componentVarsSection map[interface{}]interface{}

// Check and process stacks
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 nil, err
}
}
} else {
if len(c.Config.Stacks.NamePattern) < 1 {
return nil, errors.New("stack name pattern must be provided in 'stacks.name_pattern' config or 'ATMOS_STACKS_NAME_PATTERN' ENV variable")
}

stackParts := strings.Split(stack, "-")
stackNamePatternParts := strings.Split(c.Config.Stacks.NamePattern, "-")

var tenant string
var environment string
var stage string
var tenantFound bool
var environmentFound bool
var stageFound bool

for i, part := range stackNamePatternParts {
if part == "{tenant}" {
tenant = stackParts[i]
} else if part == "{environment}" {
environment = stackParts[i]
} else if part == "{stage}" {
stage = stackParts[i]
}
}

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
}
}

tenantFound = true
environmentFound = true
stageFound = true

// Search for tenant in stack
if len(tenant) > 0 {
if tenantInStack, ok := componentVarsSection["tenant"].(string); !ok || tenantInStack != tenant {
tenantFound = false
}
}

// Search for environment in stack
if len(environment) > 0 {
if environmentInStack, ok := componentVarsSection["environment"].(string); !ok || environmentInStack != environment {
environmentFound = false
}
}

// Search for stage in stack
if len(stage) > 0 {
if stageInStack, ok := componentVarsSection["stage"].(string); !ok || stageInStack != stage {
stageFound = false
}
}

if tenantFound == true && environmentFound == true && stageFound == true {
stack = stackName
break
}
}

if tenantFound == false || environmentFound == false || stageFound == false {
return nil, errors.New(fmt.Sprintf("\nCould not find config for the component '%s' in the stack '%s'.\n"+
"Check that all attributes in the stack name pattern '%s' are defined in stack config files.\n"+
"Are the component and stack names correct? Did you forget an import?",
component,
stack,
c.Config.Stacks.NamePattern,
))
}
}

baseComponentName := ""
if baseComponent, baseComponentExist := componentSection["component"]; baseComponentExist {
baseComponentName = baseComponent.(string)
}

// workspace
var workspace string
if len(baseComponentName) == 0 {
workspace = stack
} else {
workspace = fmt.Sprintf("%s-%s", stack, component)
}
componentSection["workspace"] = strings.Replace(workspace, "/", "-", -1)

return componentSection, nil
}

// ProcessComponentFromContext accepts context (tenant, environment, stage) and returns the component configuration in the stack
func ProcessComponentFromContext(component string, tenant string, environment string, stage string) (map[string]interface{}, error) {
var stack string

err := c.InitConfig()
if err != nil {
return nil, err
}

if len(c.Config.Stacks.NamePattern) < 1 {
return nil, errors.New("stack name pattern must be provided in 'stacks.name_pattern' config or 'ATMOS_STACKS_NAME_PATTERN' ENV variable")
}

stackNamePatternParts := strings.Split(c.Config.Stacks.NamePattern, "-")

for _, part := range stackNamePatternParts {
if part == "{tenant}" {
if len(tenant) == 0 {
return nil, errors.New(fmt.Sprintf("stack name pattern '%s' includes '{tenant}', but tenant is not provided", c.Config.Stacks.NamePattern))
}
if len(stack) == 0 {
stack = tenant
} else {
stack = fmt.Sprintf("%s-%s", stack, tenant)
}
} else if part == "{environment}" {
if len(environment) == 0 {
return nil, errors.New(fmt.Sprintf("stack name pattern '%s' includes '{environment}', but environment is not provided", c.Config.Stacks.NamePattern))
}
if len(stack) == 0 {
stack = environment
} else {
stack = fmt.Sprintf("%s-%s", stack, environment)
}
} else if part == "{stage}" {
if len(stage) == 0 {
return nil, errors.New(fmt.Sprintf("stack name pattern '%s' includes '{stage}', but stage is not provided", c.Config.Stacks.NamePattern))
}
if len(stack) == 0 {
stack = stage
} else {
stack = fmt.Sprintf("%s-%s", stack, stage)
}
}
}

return ProcessComponentInStack(component, stack)
}

// findComponentConfig finds component config sections
func findComponentConfig(
stack string,
stacksMap map[string]interface{},
componentType string,
component string,
) (map[string]interface{}, map[interface{}]interface{}, map[interface{}]interface{}, 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 componentBackendSection map[interface{}]interface{}
var ok bool

if len(stack) == 0 {
return 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")
}
if len(componentType) == 0 {
return 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))
}
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))
}
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))
}
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))
}
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))
}
if componentBackendSection, ok = componentSection["backend"].(map[interface{}]interface{}); !ok {
componentBackendSection = nil
}

return componentSection, componentVarsSection, componentBackendSection, nil
}
Loading

0 comments on commit 538a5d7

Please sign in to comment.