Skip to content

Commit

Permalink
Fix terraform import command. Add terraform clean command. Update…
Browse files Browse the repository at this point in the history
… spacelift processor (#73)

* Use varfile in `terraform import`

* Add `terraform clean` command

* Add `terraform clean` command

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* Update spacelift processor

* `terraform clean` uses `TF_DATA_DIR` ENV var

* `terraform clean` uses `TF_DATA_DIR` ENV var
  • Loading branch information
aknysh committed Nov 8, 2021
1 parent d1f3e94 commit 846ced7
Show file tree
Hide file tree
Showing 8 changed files with 650 additions and 144 deletions.
6 changes: 3 additions & 3 deletions internal/exec/helmfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@ func ExecuteHelmfile(cmd *cobra.Command, args []string) error {
info.SubCommand = "sync"
}

context := getContextFromVars(info.ComponentVarsSection)
context := c.GetContextFromVars(info.ComponentVarsSection)

// Prepare AWS profile
helmAwsProfile := replaceContextTokens(context, c.Config.Components.Helmfile.HelmAwsProfilePattern)
helmAwsProfile := c.ReplaceContextTokens(context, c.Config.Components.Helmfile.HelmAwsProfilePattern)
color.Cyan(fmt.Sprintf("\nUsing AWS_PROFILE=%s\n\n", helmAwsProfile))

// Download kubeconfig by running `aws eks update-kubeconfig`
kubeconfigPath := fmt.Sprintf("%s/%s-kubecfg", c.Config.Components.Helmfile.KubeconfigPath, info.ContextPrefix)
clusterName := replaceContextTokens(context, c.Config.Components.Helmfile.ClusterNamePattern)
clusterName := c.ReplaceContextTokens(context, c.Config.Components.Helmfile.ClusterNamePattern)
color.Cyan(fmt.Sprintf("Downloading kubeconfig from the cluster '%s' and saving it to %s\n\n", clusterName, kubeconfigPath))

err = execCommand("aws",
Expand Down
64 changes: 47 additions & 17 deletions internal/exec/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,42 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error {
))
}

varFile := fmt.Sprintf("%s-%s.terraform.tfvars.json", info.ContextPrefix, info.Component)
planFile := fmt.Sprintf("%s-%s.planfile", info.ContextPrefix, info.Component)

if info.SubCommand == "clean" {
fmt.Println("Deleting '.terraform' folder")
_ = os.RemoveAll(path.Join(componentPath, ".terraform"))

fmt.Println("Deleting '.terraform.lock.hcl' file")
_ = os.Remove(path.Join(componentPath, ".terraform.lock.hcl"))

fmt.Println(fmt.Sprintf("Deleting terraform varfile: %s", varFile))
_ = os.Remove(path.Join(componentPath, varFile))

fmt.Println(fmt.Sprintf("Deleting terraform planfile: %s", planFile))
_ = os.Remove(path.Join(componentPath, planFile))

tfDataDir := os.Getenv("TF_DATA_DIR")
if len(tfDataDir) > 0 && tfDataDir != "." && tfDataDir != "/" && tfDataDir != "./" {
color.Cyan("Found ENV var TF_DATA_DIR=%s", tfDataDir)
var userAnswer string
fmt.Println(fmt.Sprintf("Do you want to delete the folder '%s'? (only 'yes' will be accepted to approve)", tfDataDir))
fmt.Print("Enter a value: ")
count, err := fmt.Scanln(&userAnswer)
if count > 0 && err != nil {
return err
}
if userAnswer == "yes" {
fmt.Println(fmt.Sprintf("Deleting folder '%s'", tfDataDir))
_ = os.RemoveAll(tfDataDir)
}
}

fmt.Println()
return nil
}

// Write variables to a file
var varFileName, varFileNameFromArg string

Expand All @@ -66,19 +102,17 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error {
varFileName = varFileNameFromArg
} else {
if len(info.ComponentFolderPrefix) == 0 {
varFileName = fmt.Sprintf("%s/%s/%s-%s.terraform.tfvars.json",
varFileName = path.Join(
c.Config.Components.Terraform.BasePath,
finalComponent,
info.ContextPrefix,
info.Component,
varFile,
)
} else {
varFileName = fmt.Sprintf("%s/%s/%s/%s-%s.terraform.tfvars.json",
varFileName = path.Join(
c.Config.Components.Terraform.BasePath,
info.ComponentFolderPrefix,
finalComponent,
info.ContextPrefix,
info.Component,
varFile,
)
}
}
Expand Down Expand Up @@ -170,9 +204,6 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error {
}
fmt.Println(fmt.Sprintf(fmt.Sprintf("Working dir: %s", workingDir)))

planFile := fmt.Sprintf("%s-%s.planfile", info.ContextPrefix, info.Component)
varFile := fmt.Sprintf("%s-%s.terraform.tfvars.json", info.ContextPrefix, info.Component)

var workspaceName string
if len(info.BaseComponent) > 0 {
workspaceName = fmt.Sprintf("%s-%s", info.ContextPrefix, info.Component)
Expand All @@ -189,6 +220,9 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error {
case "destroy":
allArgsAndFlags = append(allArgsAndFlags, []string{"-var-file", varFile}...)
break
case "import":
allArgsAndFlags = append(allArgsAndFlags, []string{"-var-file", varFile}...)
break
case "apply":
if info.UseTerraformPlan == true {
allArgsAndFlags = append(allArgsAndFlags, []string{planFile}...)
Expand All @@ -201,16 +235,12 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error {
allArgsAndFlags = append(allArgsAndFlags, info.AdditionalArgsAndFlags...)

// Run `terraform workspace`
if info.SubCommand != "clean" {
err = execCommand(info.Command, []string{"workspace", "select", workspaceName}, componentPath, nil)
err = execCommand(info.Command, []string{"workspace", "select", workspaceName}, componentPath, nil)
if err != nil {
err = execCommand(info.Command, []string{"workspace", "new", workspaceName}, componentPath, nil)
if err != nil {
err = execCommand(info.Command, []string{"workspace", "new", workspaceName}, componentPath, nil)
if err != nil {
return err
}
return err
}
} else {
return errors.New("terraform clean not implemented")
}

// Check if the terraform command requires a user interaction,
Expand Down
118 changes: 20 additions & 98 deletions internal/exec/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package exec
import (
"errors"
"fmt"
"github.com/cloudposse/atmos/pkg/config"
c "github.com/cloudposse/atmos/pkg/config"
g "github.com/cloudposse/atmos/pkg/globals"
s "github.com/cloudposse/atmos/pkg/stack"
"github.com/cloudposse/atmos/pkg/utils"
Expand Down Expand Up @@ -91,8 +91,8 @@ func findComponentConfig(
}

// processConfigAndStacks processes CLI config and stacks
func processConfigAndStacks(componentType string, cmd *cobra.Command, args []string) (config.ConfigAndStacksInfo, error) {
var configAndStacksInfo config.ConfigAndStacksInfo
func processConfigAndStacks(componentType string, cmd *cobra.Command, args []string) (c.ConfigAndStacksInfo, error) {
var configAndStacksInfo c.ConfigAndStacksInfo

if len(args) < 1 {
return configAndStacksInfo, errors.New("invalid number of arguments")
Expand Down Expand Up @@ -134,20 +134,20 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str
}

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

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

// Process stack config file(s)
_, stacksMap, err := s.ProcessYAMLConfigFiles(
config.ProcessedConfig.StacksBaseAbsolutePath,
config.ProcessedConfig.StackConfigFilesAbsolutePaths,
c.ProcessedConfig.StacksBaseAbsolutePath,
c.ProcessedConfig.StackConfigFilesAbsolutePaths,
false,
true)

Expand All @@ -159,27 +159,27 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str
if g.LogVerbose {
fmt.Println()
var msg string
if config.ProcessedConfig.StackType == "Directory" {
if c.ProcessedConfig.StackType == "Directory" {
msg = "Found the config file for the provided stack:"
} else {
msg = "Found config files:"
}
color.Cyan(msg)
err = utils.PrintAsYAML(config.ProcessedConfig.StackConfigFilesRelativePaths)
err = utils.PrintAsYAML(c.ProcessedConfig.StackConfigFilesRelativePaths)
if err != nil {
return configAndStacksInfo, err
}
}

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

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

// Check and process stacks
if config.ProcessedConfig.StackType == "Directory" {
if c.ProcessedConfig.StackType == "Directory" {
_, configAndStacksInfo.ComponentVarsSection,
configAndStacksInfo.ComponentBackendSection,
configAndStacksInfo.ComponentBackendType,
Expand All @@ -198,7 +198,7 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str
return configAndStacksInfo,
errors.New(fmt.Sprintf("Stack '%s' does not match the stack name pattern '%s'",
configAndStacksInfo.Stack,
config.Config.Stacks.NamePattern))
c.Config.Stacks.NamePattern))
}

var tenant string
Expand Down Expand Up @@ -269,7 +269,7 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str
"Are the component and stack names correct? Did you forget an import?",
configAndStacksInfo.ComponentFromArg,
configAndStacksInfo.Stack,
config.Config.Stacks.NamePattern,
c.Config.Stacks.NamePattern,
))
}
}
Expand Down Expand Up @@ -310,59 +310,18 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str
}

// Process context
configAndStacksInfo.Context = getContextFromVars(configAndStacksInfo.ComponentVarsSection)
contextPrefix := ""

for _, part := range stackNamePatternParts {
if part == "{tenant}" {
if len(configAndStacksInfo.Context.Tenant) == 0 {
return configAndStacksInfo,
errors.New(fmt.Sprintf("The stack name pattern '%s' specifies 'tenant`, but the stack %s does not have a tenant defined",
config.Config.Stacks.NamePattern,
configAndStacksInfo.Stack,
))
}
if len(contextPrefix) == 0 {
contextPrefix = configAndStacksInfo.Context.Tenant
} else {
contextPrefix = contextPrefix + "-" + configAndStacksInfo.Context.Tenant
}
} else if part == "{environment}" {
if len(configAndStacksInfo.Context.Environment) == 0 {
return configAndStacksInfo,
errors.New(fmt.Sprintf("The stack name pattern '%s' specifies 'environment`, but the stack %s does not have an environment defined",
config.Config.Stacks.NamePattern,
configAndStacksInfo.Stack,
))
}
if len(contextPrefix) == 0 {
contextPrefix = configAndStacksInfo.Context.Environment
} else {
contextPrefix = contextPrefix + "-" + configAndStacksInfo.Context.Environment
}
} else if part == "{stage}" {
if len(configAndStacksInfo.Context.Stage) == 0 {
return configAndStacksInfo,
errors.New(fmt.Sprintf("The stack name pattern '%s' specifies 'stage`, but the stack %s does not have a stage defined",
config.Config.Stacks.NamePattern,
configAndStacksInfo.Stack,
))
}
if len(contextPrefix) == 0 {
contextPrefix = configAndStacksInfo.Context.Stage
} else {
contextPrefix = contextPrefix + "-" + configAndStacksInfo.Context.Stage
}
}
configAndStacksInfo.Context = c.GetContextFromVars(configAndStacksInfo.ComponentVarsSection)
configAndStacksInfo.ContextPrefix, err = c.GetContextPrefix(configAndStacksInfo.Stack, configAndStacksInfo.Context, c.Config.Stacks.NamePattern)
if err != nil {
return configAndStacksInfo, err
}

configAndStacksInfo.ContextPrefix = contextPrefix
return configAndStacksInfo, nil
}

// processArgsAndFlags removes common args and flags from the provided list of arguments/flags
func processArgsAndFlags(inputArgsAndFlags []string) (config.ArgsAndFlagsInfo, error) {
var info config.ArgsAndFlagsInfo
func processArgsAndFlags(inputArgsAndFlags []string) (c.ArgsAndFlagsInfo, error) {
var info c.ArgsAndFlagsInfo
var additionalArgsAndFlags []string
var globalOptions []string

Expand Down Expand Up @@ -515,43 +474,6 @@ func execCommand(command string, args []string, dir string, env []string) error
return cmd.Run()
}

func getContextFromVars(vars map[interface{}]interface{}) config.Context {
var context config.Context

if namespace, ok := vars["namespace"].(string); ok {
context.Namespace = namespace
}

if tenant, ok := vars["tenant"].(string); ok {
context.Tenant = tenant
}

if environment, ok := vars["environment"].(string); ok {
context.Environment = environment
}

if stage, ok := vars["stage"].(string); ok {
context.Stage = stage
}

if region, ok := vars["region"].(string); ok {
context.Region = region
}

return context
}

func replaceContextTokens(context config.Context, pattern string) string {
return strings.Replace(
strings.Replace(
strings.Replace(
strings.Replace(pattern,
"{namespace}", context.Namespace, 1),
"{environment}", context.Environment, 1),
"{tenant}", context.Tenant, 1),
"{stage}", context.Stage, 1)
}

func generateComponentBackendConfig(backendType string, backendConfig map[interface{}]interface{}) map[string]interface{} {
return map[string]interface{}{
"terraform": map[string]interface{}{
Expand Down
Loading

0 comments on commit 846ced7

Please sign in to comment.