Skip to content

Commit

Permalink
Add --from-plan flag to terraform apply/deploy commands. Add `descr…
Browse files Browse the repository at this point in the history
…ibe config` CLI command (#71)

* Add `--from-plan` flag to terraform apply/deploy commands

* Add `describe config` CLI command

* Update `GetGlobMatches` function

* Update `GetGlobMatches` function

* Update `GetGlobMatches` function

* Update `GetGlobMatches` function

* Update `GetGlobMatches` function

* Update doublestar to V4

* partial implementation of `terraform clean`

* cleanup after testing

* go imports formatting

* Update `GetGlobMatches` function

Co-authored-by: Nuru <[email protected]>
  • Loading branch information
aknysh and Nuru authored Nov 3, 2021
1 parent 302aa83 commit d1f3e94
Show file tree
Hide file tree
Showing 14 changed files with 140 additions and 34 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ build:

deps:
go mod download

.PHONY: lint build deps
2 changes: 1 addition & 1 deletion cmd/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
var describeCmd = &cobra.Command{
Use: "describe",
Short: "describe",
Long: `This command shows configuration for stacks and components`,
Long: `This command shows configuration for CLI, stacks and components`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
}

Expand Down
32 changes: 32 additions & 0 deletions cmd/describe_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cmd

import (
"fmt"
e "github.com/cloudposse/atmos/internal/exec"
"github.com/fatih/color"
"github.com/spf13/cobra"
"os"
)

// describeComponentCmd describes configuration for components
var describeConfigCmd = &cobra.Command{
Use: "config",
Short: "describe config",
Long: `This command shows CLI configuration`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteDescribeConfig(cmd, args)
if err != nil {
color.Red("%s\n", err)
fmt.Println()
os.Exit(1)
}
},
}

func init() {
describeConfigCmd.DisableFlagParsing = false
describeConfigCmd.PersistentFlags().StringP("format", "f", "json", "'atmos describe config -f json' or 'atmos describe config -f yaml'")

describeCmd.AddCommand(describeConfigCmd)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/cloudposse/atmos
go 1.16

require (
github.com/bmatcuk/doublestar v1.3.4
github.com/bmatcuk/doublestar/v4 v4.0.2
github.com/fatih/color v1.13.0
github.com/imdario/mergo v0.3.12
github.com/json-iterator/go v1.1.12
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
github.com/bmatcuk/doublestar/v4 v4.0.2 h1:X0krlUVAVmtr2cRoTqR8aDMrDqnB36ht8wpWTiQ3jsA=
github.com/bmatcuk/doublestar/v4 v4.0.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
Expand Down
52 changes: 40 additions & 12 deletions internal/exec/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package exec

import (
"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"
u "github.com/cloudposse/atmos/pkg/utils"
Expand All @@ -24,15 +24,15 @@ func ExecuteDescribeComponent(cmd *cobra.Command, args []string) error {
return err
}

var configAndStacksInfo config.ConfigAndStacksInfo
var configAndStacksInfo c.ConfigAndStacksInfo
configAndStacksInfo.Stack = stack

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

err = config.ProcessConfig(configAndStacksInfo)
err = c.ProcessConfig(configAndStacksInfo)
if err != nil {
return err
}
Expand All @@ -43,22 +43,22 @@ func ExecuteDescribeComponent(cmd *cobra.Command, args []string) error {
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 = u.PrintAsYAML(config.ProcessedConfig.StackConfigFilesRelativePaths)
err = u.PrintAsYAML(c.ProcessedConfig.StackConfigFilesRelativePaths)
if err != nil {
return err
}
}

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

Expand All @@ -70,7 +70,7 @@ func ExecuteDescribeComponent(cmd *cobra.Command, args []string) error {
var componentVarsSection map[interface{}]interface{}

// Check and process stacks
if config.ProcessedConfig.StackType == "Directory" {
if c.ProcessedConfig.StackType == "Directory" {
componentSection,
componentVarsSection,
_, _, _, _,
Expand All @@ -89,12 +89,12 @@ func ExecuteDescribeComponent(cmd *cobra.Command, args []string) error {
color.Cyan("Searching for stack config where the component '%s' is defined\n", component)
}

if len(config.Config.Stacks.NamePattern) < 1 {
if len(c.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")
}

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

var tenant string
var environment string
Expand Down Expand Up @@ -168,7 +168,7 @@ func ExecuteDescribeComponent(cmd *cobra.Command, args []string) error {
"Are the component and stack names correct? Did you forget an import?",
component,
stack,
config.Config.Stacks.NamePattern,
c.Config.Stacks.NamePattern,
))
}
}
Expand All @@ -184,3 +184,31 @@ func ExecuteDescribeComponent(cmd *cobra.Command, args []string) error {

return nil
}

// ExecuteDescribeConfig executes `describe config` command
func ExecuteDescribeConfig(cmd *cobra.Command, args []string) error {
flags := cmd.Flags()

format, err := flags.GetString("format")
if err != nil {
return err
}

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

if format == "json" {
err = u.PrintAsJSON(c.Config)
} else if format == "yaml" {
err = u.PrintAsYAML(c.Config)
} else {
err = errors.New("invalid flag '--format'. Accepted values are 'json' or 'yaml'")
}
if err != nil {
return err
}

return nil
}
23 changes: 16 additions & 7 deletions internal/exec/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error {
runTerraformInit := true
if info.SubCommand == "init" ||
info.SubCommand == "workspace" ||
info.SubCommand == "clean" ||
(info.SubCommand == "deploy" && c.Config.Components.Terraform.DeployRunInit == false) {
runTerraformInit = false
}
Expand All @@ -138,13 +139,13 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error {
// Handle `terraform deploy` custom command
if info.SubCommand == "deploy" {
info.SubCommand = "apply"
if !utils.SliceContainsString(info.AdditionalArgsAndFlags, autoApproveFlag) {
if info.UseTerraformPlan == false && !utils.SliceContainsString(info.AdditionalArgsAndFlags, autoApproveFlag) {
info.AdditionalArgsAndFlags = append(info.AdditionalArgsAndFlags, autoApproveFlag)
}
}

// Handle Config.Components.Terraform.ApplyAutoApprove flag
if info.SubCommand == "apply" && c.Config.Components.Terraform.ApplyAutoApprove == true {
if info.SubCommand == "apply" && c.Config.Components.Terraform.ApplyAutoApprove == true && info.UseTerraformPlan == false {
if !utils.SliceContainsString(info.AdditionalArgsAndFlags, autoApproveFlag) {
info.AdditionalArgsAndFlags = append(info.AdditionalArgsAndFlags, autoApproveFlag)
}
Expand Down Expand Up @@ -189,19 +190,27 @@ func ExecuteTerraform(cmd *cobra.Command, args []string) error {
allArgsAndFlags = append(allArgsAndFlags, []string{"-var-file", varFile}...)
break
case "apply":
allArgsAndFlags = append(allArgsAndFlags, []string{"-var-file", varFile}...)
if info.UseTerraformPlan == true {
allArgsAndFlags = append(allArgsAndFlags, []string{planFile}...)
} else {
allArgsAndFlags = append(allArgsAndFlags, []string{"-var-file", varFile}...)
}
break
}

allArgsAndFlags = append(allArgsAndFlags, info.AdditionalArgsAndFlags...)

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

// Check if the terraform command requires a user interaction,
Expand Down
6 changes: 6 additions & 0 deletions internal/exec/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var (
g.GlobalOptionsFlag,
g.DeployRunInitFlag,
g.AutoGenerateBackendFileFlag,
g.FromPlanFlag,
}
)

Expand Down Expand Up @@ -125,6 +126,7 @@ func processConfigAndStacks(componentType string, cmd *cobra.Command, args []str
configAndStacksInfo.ConfigDir = argsAndFlagsInfo.ConfigDir
configAndStacksInfo.DeployRunInit = argsAndFlagsInfo.DeployRunInit
configAndStacksInfo.AutoGenerateBackendFile = argsAndFlagsInfo.AutoGenerateBackendFile
configAndStacksInfo.UseTerraformPlan = argsAndFlagsInfo.UseTerraformPlan

// Check if component was provided
if len(configAndStacksInfo.ComponentFromArg) < 1 {
Expand Down Expand Up @@ -454,6 +456,10 @@ func processArgsAndFlags(inputArgsAndFlags []string) (config.ArgsAndFlagsInfo, e
info.AutoGenerateBackendFile = autoGenerateBackendFileFlagParts[1]
}

if arg == g.FromPlanFlag {
info.UseTerraformPlan = true
}

for _, f := range commonFlags {
if arg == f {
indexesToRemove = append(indexesToRemove, i)
Expand Down
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package main

import (
"os"

"github.com/cloudposse/atmos/cmd"
"github.com/fatih/color"
"os"
)

func main() {
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type ArgsAndFlagsInfo struct {
StacksDir string
DeployRunInit string
AutoGenerateBackendFile string
UseTerraformPlan bool
}

type ConfigAndStacksInfo struct {
Expand All @@ -92,4 +93,5 @@ type ConfigAndStacksInfo struct {
ContextPrefix string
DeployRunInit string
AutoGenerateBackendFile string
UseTerraformPlan bool
}
2 changes: 1 addition & 1 deletion pkg/config/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package config

import (
"errors"
"github.com/bmatcuk/doublestar"
"github.com/bmatcuk/doublestar/v4"
g "github.com/cloudposse/atmos/pkg/globals"
s "github.com/cloudposse/atmos/pkg/stack"
u "github.com/cloudposse/atmos/pkg/utils"
Expand Down
2 changes: 2 additions & 0 deletions pkg/globals/globals.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const (
StackDirFlag = "--stacks-dir"
DeployRunInitFlag = "--deploy-run-init"
AutoGenerateBackendFileFlag = "--auto-generate-backend-file"

FromPlanFlag = "--from-plan"
)

var (
Expand Down
14 changes: 11 additions & 3 deletions pkg/stack/stack_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,18 @@ func ProcessYAMLConfigFile(
}

if importMatches == nil {
errorMessage := fmt.Sprintf("Invalid import in the config file %s.\nNo matches found for the import '%s'",
errorMessage := fmt.Sprintf("Invalid import in the config file %s.\nNo matches found for the import '%s' using the pattern '%s'",
filePath,
strings.Replace(impWithExt, basePath+"/", "", 1))
return nil, nil, errors.New(errorMessage)
imp,
impWithExtPath)

importMatches, err = GetGlobMatches(impWithExtPath)
if err != nil {
return nil, nil, err
}
if importMatches == nil {
return nil, nil, errors.New(errorMessage)
}
}

for _, importFile := range importMatches {
Expand Down
28 changes: 22 additions & 6 deletions pkg/stack/stack_processor_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ package stack

import (
"fmt"
"github.com/bmatcuk/doublestar"
g "github.com/cloudposse/atmos/pkg/globals"
"github.com/cloudposse/atmos/pkg/utils"
"io/ioutil"
"os"
"path"
"path/filepath"
"sort"
"strings"
"sync"

"github.com/bmatcuk/doublestar/v4"
g "github.com/cloudposse/atmos/pkg/globals"
"github.com/cloudposse/atmos/pkg/utils"
"github.com/fatih/color"
)

var (
Expand Down Expand Up @@ -231,11 +233,25 @@ func GetGlobMatches(pattern string) ([]string, error) {
return strings.Split(fmt.Sprintf("%s", existingMatches), ","), nil
}

matches, err := doublestar.Glob(pattern)
base, cleanPattern := doublestar.SplitPattern(pattern)
f := os.DirFS(base)

matches, err := doublestar.Glob(f, cleanPattern)
if err != nil {
return nil, err
}
getGlobMatchesSyncMap.Store(pattern, strings.Join(matches, ","))

return matches, nil
if matches == nil {
color.Red(fmt.Sprintf("Import of %s (-> %s + %s) failed to find a match.", pattern, base, cleanPattern))
return nil, nil
}

var fullMatches []string
for _, match := range matches {
fullMatches = append(fullMatches, path.Join(base, match))
}

getGlobMatchesSyncMap.Store(pattern, strings.Join(fullMatches, ","))

return fullMatches, nil
}

0 comments on commit d1f3e94

Please sign in to comment.