Skip to content

Commit

Permalink
Add atmos terraform generate backends command (#200)
Browse files Browse the repository at this point in the history
* SAdd `atmos terraform generate backends` command

* SAdd `atmos terraform generate backends` command

* SAdd `atmos terraform generate backends` command
  • Loading branch information
aknysh committed Sep 11, 2022
1 parent 9556860 commit c7e5f64
Show file tree
Hide file tree
Showing 14 changed files with 263 additions and 13 deletions.
2 changes: 1 addition & 1 deletion cmd/describe_component.go
Expand Up @@ -10,7 +10,7 @@ import (
var describeComponentCmd = &cobra.Command{
Use: "component",
Short: "Execute 'describe component' command",
Long: `This command shows configuration for a component in a stack: atmos describe component <component> -s <stack>`,
Long: `This command shows configuration for an atmos component in a stack: atmos describe component <component> -s <stack>`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteDescribeComponent(cmd, args)
Expand Down
2 changes: 1 addition & 1 deletion cmd/describe_stacks.go
Expand Up @@ -10,7 +10,7 @@ import (
var describeStacksCmd = &cobra.Command{
Use: "stacks",
Short: "Execute 'describe stacks' command",
Long: `This command shows configuration for stacks and components in the stacks: atmos describe stacks <options>`,
Long: `This command shows configuration for atmos stacks and components in the stacks: atmos describe stacks <options>`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: true},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteDescribeStacks(cmd, args)
Expand Down
2 changes: 1 addition & 1 deletion cmd/helmfile_generate_varfile.go
Expand Up @@ -10,7 +10,7 @@ import (
var helmfileGenerateVarfileCmd = &cobra.Command{
Use: "varfile",
Short: "Execute 'helmfile generate varfile' command",
Long: `This command generates a varfile for a helmfile component: atmos helmfile generate varfile <component> -s <stack> -f <file>`,
Long: `This command generates a varfile for an atmos helmfile component: atmos helmfile generate varfile <component> -s <stack> -f <file>`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteHelmfileGenerateVarfile(cmd, args)
Expand Down
31 changes: 31 additions & 0 deletions cmd/terraform_generate_backends.go
@@ -0,0 +1,31 @@
package cmd

import (
e "github.com/cloudposse/atmos/internal/exec"
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/spf13/cobra"
)

// terraformGenerateBackendsCmd generates backend configs for all terraform components
var terraformGenerateBackendsCmd = &cobra.Command{
Use: "backends",
Short: "Execute 'terraform generate backends' command",
Long: `This command generates backend configs for all terraform components`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteTerraformGenerateBackendsCmd(cmd, args)
if err != nil {
u.PrintErrorToStdErrorAndExit(err)
}
},
}

func init() {
terraformGenerateBackendsCmd.DisableFlagParsing = false

terraformGenerateBackendsCmd.PersistentFlags().String("format", "hcl", "Output format.\n"+
"Supported formats: hcl, json ('hcl' is default).\n"+
"atmos terraform generate backends --format=hcl/json")

terraformGenerateCmd.AddCommand(terraformGenerateBackendsCmd)
}
2 changes: 1 addition & 1 deletion cmd/terraform_generate_varfile.go
Expand Up @@ -10,7 +10,7 @@ import (
var terraformGenerateVarfileCmd = &cobra.Command{
Use: "varfile",
Short: "Execute 'terraform generate varfile' command",
Long: `This command generates a varfile for a terraform component: atmos terraform generate varfile <component> -s <stack> -f <file>`,
Long: `This command generates a varfile for an atmos terraform component: atmos terraform generate varfile <component> -s <stack> -f <file>`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteTerraformGenerateVarfile(cmd, args)
Expand Down
6 changes: 3 additions & 3 deletions cmd/terraform_generate_varfiles.go
Expand Up @@ -10,7 +10,7 @@ import (
var terraformGenerateVarfilesCmd = &cobra.Command{
Use: "varfiles",
Short: "Execute 'terraform generate varfiles' command",
Long: `This command generates varfiles for all terraform components in all stacks`,
Long: `This command generates varfiles for all atmos terraform components in all stacks`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteTerraformGenerateVarfilesCmd(cmd, args)
Expand Down Expand Up @@ -48,8 +48,8 @@ func init() {
)

terraformGenerateVarfilesCmd.PersistentFlags().String("format", "json", "Output format.\n"+
"Supported formats: json, yaml, hcl.\n"+
"atmos terraform generate varfiles --file-template <file_template> --format=json/yaml/hcl ('json' is default)")
"Supported formats: json, yaml, hcl ('json' is default).\n"+
"atmos terraform generate varfiles --file-template <file_template> --format=json/yaml/hcl")

err := terraformGenerateVarfilesCmd.MarkPersistentFlagRequired("file-template")
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions examples/complete/stacks/mixins/region/us-east-1.yaml
Expand Up @@ -5,6 +5,8 @@ vars:
components:
terraform:
vpc:
metadata:
component: infra/vpc
vars:
availability_zones:
- us-east-1a
Expand Down
2 changes: 2 additions & 0 deletions examples/complete/stacks/mixins/region/us-east-2.yaml
Expand Up @@ -5,6 +5,8 @@ vars:
components:
terraform:
vpc:
metadata:
component: infra/vpc
vars:
availability_zones:
- us-east-2a
Expand Down
5 changes: 5 additions & 0 deletions go.mod
Expand Up @@ -7,6 +7,7 @@ require (
github.com/fatih/color v1.13.0
github.com/hashicorp/go-getter v1.6.2
github.com/hashicorp/hcl v1.0.0
github.com/hashicorp/hcl/v2 v2.14.0
github.com/imdario/mergo v0.3.13
github.com/json-iterator/go v1.1.12
github.com/mitchellh/go-homedir v1.1.0
Expand All @@ -15,6 +16,7 @@ require (
github.com/spf13/cobra v1.5.0
github.com/spf13/viper v1.13.0
github.com/stretchr/testify v1.8.0
github.com/zclconf/go-cty v1.11.0
gopkg.in/yaml.v2 v2.4.0
)

Expand All @@ -23,6 +25,8 @@ require (
cloud.google.com/go/compute v1.7.0 // indirect
cloud.google.com/go/iam v0.4.0 // indirect
cloud.google.com/go/storage v1.22.1 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/aws/aws-sdk-go v1.15.78 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand All @@ -44,6 +48,7 @@ require (
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
Expand Down
13 changes: 13 additions & 0 deletions go.sum
Expand Up @@ -66,7 +66,11 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY=
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
Expand Down Expand Up @@ -114,6 +118,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -218,6 +223,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl/v2 v2.14.0 h1:jX6+Q38Ly9zaAJlAjnFVyeNSNCKKW8D0wvyg7vij5Wc=
github.com/hashicorp/hcl/v2 v2.14.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
Expand All @@ -239,6 +246,7 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
Expand All @@ -255,6 +263,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
Expand Down Expand Up @@ -283,6 +293,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
Expand Down Expand Up @@ -316,6 +327,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0=
github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
Expand Down
4 changes: 2 additions & 2 deletions internal/exec/terraform_generate_backend.go
Expand Up @@ -43,7 +43,7 @@ func ExecuteTerraformGenerateBackend(cmd *cobra.Command, args []string) error {
return fmt.Errorf("\nCould not find 'backend' config for the '%s' component.\n", component)
}

var componentBackendConfig = generateComponentBackendConfig(info.ComponentBackendType, info.ComponentBackendSection)
componentBackendConfig := generateComponentBackendConfig(info.ComponentBackendType, info.ComponentBackendSection)

u.PrintInfoVerbose("Component backend config:\n\n")
err = u.PrintAsJSON(componentBackendConfig)
Expand All @@ -67,7 +67,7 @@ func ExecuteTerraformGenerateBackend(cmd *cobra.Command, args []string) error {

fmt.Println()
u.PrintInfo("Writing the backend config to file:")
fmt.Println(backendFilePath)
u.PrintMessage(backendFilePath)

if !info.DryRun {
err = u.WriteToFileAsJSON(backendFilePath, componentBackendConfig, 0644)
Expand Down
138 changes: 138 additions & 0 deletions internal/exec/terraform_generate_backends.go
@@ -0,0 +1,138 @@
package exec

import (
"fmt"
c "github.com/cloudposse/atmos/pkg/config"
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/spf13/cobra"
"path"
"path/filepath"
)

// ExecuteTerraformGenerateBackendsCmd executes `terraform generate backends` command
func ExecuteTerraformGenerateBackendsCmd(cmd *cobra.Command, args []string) error {
flags := cmd.Flags()

format, err := flags.GetString("format")
if err != nil {
return err
}
if format != "" && format != "json" && format != "hcl" {
return fmt.Errorf("invalid '--format' argument '%s'. Valid values are 'json' and 'hcl", format)
}
if format == "" {
format = "hcl"
}

return ExecuteTerraformGenerateBackends(format)
}

// ExecuteTerraformGenerateBackends generates backend configs for all terraform components
func ExecuteTerraformGenerateBackends(format string) error {
var configAndStacksInfo c.ConfigAndStacksInfo
stacksMap, err := FindStacksMap(configAndStacksInfo, false)
if err != nil {
return err
}

fmt.Println()

var ok bool
var componentsSection map[string]any
var terraformSection map[string]any
var componentSection map[string]any
var backendSection map[any]any
var backendType string
processedTerraformComponents := map[string]any{}

for _, stackSection := range stacksMap {
if componentsSection, ok = stackSection.(map[any]any)["components"].(map[string]any); !ok {
continue
}

if terraformSection, ok = componentsSection["terraform"].(map[string]any); !ok {
continue
}

for componentName, compSection := range terraformSection {
if componentSection, ok = compSection.(map[string]any); !ok {
continue
}

// Find terraform component.
// If `component` attribute is present, it's the terraform component.
// Otherwise, the YAML component name is the terraform component.
terraformComponent := componentName
if componentAttribute, ok := componentSection["component"].(string); ok {
terraformComponent = componentAttribute
}

// If the terraform component has been already processed, continue
if u.MapKeyExists(processedTerraformComponents, terraformComponent) {
continue
}

processedTerraformComponents[terraformComponent] = terraformComponent

// Component backend
if backendSection, ok = componentSection["backend"].(map[any]any); !ok {
continue
}

// Backend type
if backendType, ok = componentSection["backend_type"].(string); !ok {
continue
}

// Component metadata
metadataSection := map[any]any{}
if metadataSection, ok = componentSection["metadata"].(map[any]any); ok {
if componentType, ok := metadataSection["type"].(string); ok {
// Don't process abstract components
if componentType == "abstract" {
continue
}
}
}

// Absolute path to the terraform component
backendFilePath := path.Join(
c.Config.BasePath,
c.Config.Components.Terraform.BasePath,
terraformComponent,
"backend.tf",
)

if format == "json" {
backendFilePath = backendFilePath + ".json"
}

backendFileAbsolutePath, err := filepath.Abs(backendFilePath)
if err != nil {
return err
}

// Write the backend config to the file
u.PrintMessage(fmt.Sprintf("Writing backend config for the terraform component '%s' to file '%s'", terraformComponent, backendFilePath))

if format == "json" {
componentBackendConfig := generateComponentBackendConfig(backendType, backendSection)
err = u.WriteToFileAsJSON(backendFileAbsolutePath, componentBackendConfig, 0644)
if err != nil {
return err
}
} else if format == "hcl" {
err = u.WriteTerraformBackendConfigToFileAsHcl(backendFileAbsolutePath, backendType, backendSection)
if err != nil {
return err
}
} else {
return fmt.Errorf("invalid '--format' argument '%s'. Valid values are 'hcl' (default) and 'json", format)
}
}
}

fmt.Println()

return nil
}

0 comments on commit c7e5f64

Please sign in to comment.