Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add atmos terraform generate backends command #200

Merged
merged 3 commits into from Sep 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 {
aknysh marked this conversation as resolved.
Show resolved Hide resolved
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
}