From 1be085d38da8793f117daa3bfc7025c3bd179e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20N=C3=A4gele?= Date: Tue, 21 Apr 2020 10:52:22 +0200 Subject: [PATCH] Add module vars and improved params support (#13) * It is now possible to specify module-specific vars and var files that are only considered when the corresponding module is run * Params can now also be specified in the config file. Additionally, it is possible to specify and constrain required params that must be specified on the command-line when running gotf --- .gitignore | 1 + README.md | 173 ++++++++---- cmd/gotf/gotf_test.go | 91 +++++-- cmd/gotf/testdata/01_networking/dev.tfvars | 1 + cmd/gotf/testdata/01_networking/prod.tfvars | 1 + .../{01_testmodule => 01_networking}/test.tf | 22 ++ .../testdata/01_testmodule/test-dev.tfvars | 1 - .../testdata/01_testmodule/test-prod.tfvars | 1 - cmd/gotf/testdata/02_compute/dev.tfvars | 1 + cmd/gotf/testdata/02_compute/prod.tfvars | 1 + cmd/gotf/testdata/02_compute/test.tf | 48 ++++ cmd/gotf/testdata/global-dev.tfvars | 1 + cmd/gotf/testdata/global-prod.tfvars | 1 + cmd/gotf/testdata/global.tfvars | 1 + cmd/gotf/testdata/test-config-dev.yaml | 22 -- cmd/gotf/testdata/test-config-prod.yaml | 22 -- cmd/gotf/testdata/test-config.yaml | 49 ++++ go.sum | 1 + pkg/config/config.go | 195 ++++++++++--- pkg/config/config_test.go | 257 ++++++++++++++++-- pkg/config/testdata/global-dev.tfvars | 0 pkg/config/testdata/global-prod.tfvars | 0 pkg/config/testdata/global.tfvars | 0 pkg/config/testdata/test-config.yaml | 48 ++++ pkg/config/testdata/test_config.yaml | 20 -- pkg/config/testdata/testmodule/test.tf | 24 -- .../testdata/testmodule/test1-prod.tfvars | 1 - .../testdata/testmodule/test2-prod.tfvars | 1 - .../testdata/testmodule1/test1-dev.tfvars | 0 .../testdata/testmodule1/test1-prod.tfvars | 0 .../testdata/testmodule1/test2-dev.tfvars | 0 .../testdata/testmodule1/test2-prod.tfvars | 0 .../testdata/testmodule2/test1-dev.tfvars | 0 .../testdata/testmodule2/test1-prod.tfvars | 0 .../testdata/testmodule2/test2-dev.tfvars | 0 .../testdata/testmodule2/test2-prod.tfvars | 0 pkg/gotf/gotf.go | 25 +- 37 files changed, 757 insertions(+), 252 deletions(-) create mode 100644 cmd/gotf/testdata/01_networking/dev.tfvars create mode 100644 cmd/gotf/testdata/01_networking/prod.tfvars rename cmd/gotf/testdata/{01_testmodule => 01_networking}/test.tf (57%) delete mode 100644 cmd/gotf/testdata/01_testmodule/test-dev.tfvars delete mode 100644 cmd/gotf/testdata/01_testmodule/test-prod.tfvars create mode 100644 cmd/gotf/testdata/02_compute/dev.tfvars create mode 100644 cmd/gotf/testdata/02_compute/prod.tfvars create mode 100644 cmd/gotf/testdata/02_compute/test.tf create mode 100644 cmd/gotf/testdata/global-dev.tfvars create mode 100644 cmd/gotf/testdata/global-prod.tfvars create mode 100644 cmd/gotf/testdata/global.tfvars delete mode 100644 cmd/gotf/testdata/test-config-dev.yaml delete mode 100644 cmd/gotf/testdata/test-config-prod.yaml create mode 100644 cmd/gotf/testdata/test-config.yaml create mode 100644 pkg/config/testdata/global-dev.tfvars create mode 100644 pkg/config/testdata/global-prod.tfvars create mode 100644 pkg/config/testdata/global.tfvars create mode 100644 pkg/config/testdata/test-config.yaml delete mode 100644 pkg/config/testdata/test_config.yaml delete mode 100644 pkg/config/testdata/testmodule/test.tf delete mode 100644 pkg/config/testdata/testmodule/test1-prod.tfvars delete mode 100644 pkg/config/testdata/testmodule/test2-prod.tfvars create mode 100644 pkg/config/testdata/testmodule1/test1-dev.tfvars create mode 100644 pkg/config/testdata/testmodule1/test1-prod.tfvars create mode 100644 pkg/config/testdata/testmodule1/test2-dev.tfvars create mode 100644 pkg/config/testdata/testmodule1/test2-prod.tfvars create mode 100644 pkg/config/testdata/testmodule2/test1-dev.tfvars create mode 100644 pkg/config/testdata/testmodule2/test1-prod.tfvars create mode 100644 pkg/config/testdata/testmodule2/test2-dev.tfvars create mode 100644 pkg/config/testdata/testmodule2/test2-prod.tfvars diff --git a/.gitignore b/.gitignore index 363d17d..66bc738 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .project .settings dist +testdata/gotf* diff --git a/README.md b/README.md index 72fc793..0cdbf18 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,6 @@ Flags: `gotf` is configured via config file. By default, `gotf.yaml` is loaded from the current directory. -In a real-world scenario, you will probably have a config file per environment. Config files support templating as specified below. ### Parameters @@ -57,14 +56,36 @@ Config files support templating as specified below. Optionally sets a specific Terraform version to use. `gotf` will download this version and cache it in `$XDG_CACHE_HOME/gotf/terraform/` verifying GPG signature and SHA256 sum. -#### `varFiles` +#### `params` -Variable files for Terraform which are added to the Terraform environment via `TF_CLI_ARGS_=-var-file=` for commands that support them. +Config entries that can be used for templating. See section on templating below for details. + +#### `requiredParams` + +In addition to specifying `params` in the config file, they may also be specified on the command-line using the `-p|--param` flag. +Params that are required can be configured here. +Allowed values for a `param` must be specified as list. +If no restrictions apply, no value or an empty list must be specified. +Values must be strings. + +#### `globalVarFiles` + +A list of variables files which are added to the Terraform environment via `TF_CLI_ARGS_=-var-file=` for commands that support them. +They are resolved relative to this config file. + +#### `moduleVarFiles.` + +A list of module-specific variables files which are added to the Terraform environment if the corresponding module is run via `TF_CLI_ARGS_=-var-file=` for commands that support them. They are resolved relative to this config file. -#### `vars` +#### `globalVars` + +Variables which are added to the Terraform environment via `TF_VAR_=value` for commands that support them. -A list of variables that are added to the Terraform environment via `TF_VAR_=value` for commands that support them. +#### `moduleVars.` + +Module-specific variables which are added to the Terraform environment if the corresponding module is run via `TF_VAR_=value` for commands that support them. +Module-specific variables override global ones. #### `envs` @@ -77,12 +98,21 @@ Backend configs are always added as variables (`TF_VAR_backend_=value`) for ### Example ```yaml -terraformVersion: 0.12.21 +terraformVersion: 0.12.24 + +requiredParams: + environment: + - dev + - prod -varFiles: - - test-{{ .Params.env }}.tfvars +params: + param: myval -vars: +globalVarFiles: + - global-{{ .Params.environment }}.tfvars + - global.tfvars + +globalVars: foo: foovalue templated_var: "{{ .Params.param }}" mapvar: |- @@ -96,19 +126,30 @@ vars: value2 = false } } - module_path: "{{ .Params.moduleDir }}" - module: "{{ base .Params.moduleDir }}" - state_key_prefix: '{{ (splitn "_" 2 (base .Params.moduleDir))._1 }}' + module_dir: "{{ .Params.moduleDir }}" + state_key: '{{ (splitn "_" 2 .Params.moduleDir)._1 }}' + +moduleVarFiles: + 01_networking: + - 01_networking/{{ .Params.environment }}.tfvars + 02_compute: + - 02_compute/{{ .Params.environment }}.tfvars + +moduleVars: + 01_networking: + myvar: value for networking + 02_compute: + myvar: value for compute envs: BAR: barvalue TEMPLATED_ENV: "{{ .Params.param }}" backendConfigs: - key: "{{ .Vars.state_key_prefix }}_{{ .Vars.templated_var }}_{{ .Params.key_suffix }}" - storage_account_name: be_storage_account_name_{{ .Vars.foo }}_{{ .Envs.BAR }} - resource_group_name: be_resource_group_name_{{ .Vars.foo }}_{{ .Envs.BAR }} - container_name: be_container_name_{{ .Vars.foo }}_{{ .Envs.BAR }} + key: "{{ .Vars.state_key }}" + storage_account_name: mytfstateaccount{{ .Params.environment }} + resource_group_name: mytfstate-{{ .Params.environment }} + container_name: mytfstate-{{ .Params.environment }} ``` ### Templating @@ -116,28 +157,38 @@ backendConfigs: Go templating can be used in the config file as follows. The [Sprig](https://masterminds.github.io/sprig/) function library is included. -* In the first templating pass, `varFiles`, `vars`, and `envs` are processed. - All parameters specified using the `-p|--param` flag are available in the `.Params` object. - The module directory passed with the `--module-dir|-m` parameter is available as `module` dir in the `.Params` object. +* In the first templating pass, `globalVarFiles`, `globalVars`, `moduleVarFiles`, `moduleVars`, and `envs` are processed. + All parameters specified under `params` and using the `-p|--param` flag are available in the `.Params` object. + CLI params override those specified in the config file. + The basename of the module directory passed with the `--module-dir|-m` parameter is available as `moduleDir` dir in the `.Params` object. * In the second templating pass, `backendConfigs` are processed. - `vars` are available as `.Vars`, `envs` are available as `.Envs` with the results from the first templating pass. + `globalVars` and ` moduleVars` are available as `.Vars` and `envs` are available as `.Envs` with the results from the first templating pass. Additionally, `.Params` is also available again. Using the above config file, running `terraform init` could look like this: ```console -$ gotf -c example-config.yaml -p param=myval -p key_suffix=mysuffix -m my_modules/01_testmodule init +$ gotf -c example-config.yaml -p environment=dev -m 01_networking init ``` After processing, the config file would look like this: ```yaml -terraformVersion: 0.12.21 +terraformVersion: 0.12.24 + +requiredParams: + environment: + - dev + - prod + +params: + param: myval -varFiles: - - test-prod.tfvars +globalVarFiles: + - global-dev.tfvars + - global.tfvars -vars: +globalVars: foo: foovalue templated_var: "myval" mapvar: |- @@ -151,19 +202,30 @@ vars: value2 = false } } - module_path: "my_modules/01_testmodule" - module: "01_testmodule" - state_key_prefix: 'testmodule' + module_dir: "01_networking" + state_key: 'networking' + +moduleVarFiles: + 01_networking: + - 01_networking/dev.tfvars + 02_compute: + - 02_compute/dev.tfvars + +moduleVars: + 01_networking: + myvar: value for networking + 02_compute: + myvar: value for compute envs: BAR: barvalue TEMPLATED_ENV: "myval" backendConfigs: - key: testmodule_myval_mysuffix - storage_account_name: be_storage_account_name_foovalue_barvalue - resource_group_name: be_resource_group_name_foovalue_barvalue - container_name: be_container_name_foovalue_barvalue + key: "networking" + storage_account_name: mytfstateaccountdev + resource_group_name: mytfstate-dev + container_name: mytfstate-dev ``` ## Debug Output @@ -172,26 +234,33 @@ Specifying the `--debug` flag produces debug output which is written to stderr. For example, the integration test in [cmd/gotf/gotf_test.go](cmd/gotf/gotf_test.go) produces the following debug output before running Terraform: ```console -gotf> Loading config file: testdata/test-config-prod.yaml -gotf> Processing varFiles... -gotf> Processing vars... +gotf> Loading config file: testdata/test-config.yaml +gotf> Processing var files... +gotf> Processing global var files... +gotf> Processing module var files... +gotf> Processing global vars... +gotf> Processing module vars... gotf> Processing envs... -gotf> Proessing backendConfigs... -gotf> Using Terraform version 0.12.21 -gotf> Downloading Terraform distro... -gotf> Downloading SHA256 sums file... -gotf> Downloading SHA256 sums signature file... -gotf> Verifying GPG signature... -gotf> Verifying SHA256 sum... -gotf> Unzipping distro... -gotf> Terraform binary: /Users/myuser/Library/Caches/gotf/terraform/0.12.21/terraform +gotf> Processing backend configs... +gotf> Using Terraform version 0.12.24 +gotf> Terraform version 0.12.24 already installed. +gotf> Terraform binary: /Users/myuser/Library/Caches/gotf/terraform/0.12.24/terraform gotf> gotf> Terraform command-line: gotf> ----------------------- -gotf> /Users/myuser/Library/Caches/gotf/terraform/0.12.21/terraform init -no-color +gotf> /Users/myuser/Library/Caches/gotf/terraform/0.12.24/terraform init -no-color gotf> gotf> Terraform environment: gotf> ---------------------- +gotf> TEMPLATED_ENV=myval +gotf> TF_CLI_ARGS_destroy=-var-file="../global-prod.tfvars" -var-file="../global.tfvars" -var-file="prod.tfvars" +gotf> TF_VAR_myvar=value for networking +gotf> TF_CLI_ARGS_init=-backend-config=path=".terraform/terraform-networking-prod.tfstate" +gotf> TF_CLI_ARGS_import=-var-file="../global-prod.tfvars" -var-file="../global.tfvars" -var-file="prod.tfvars" +gotf> TF_VAR_module_dir=01_networking +gotf> TF_CLI_ARGS_plan=-var-file="../global-prod.tfvars" -var-file="../global.tfvars" -var-file="prod.tfvars" +gotf> TF_CLI_ARGS_refresh=-var-file="../global-prod.tfvars" -var-file="../global.tfvars" -var-file="prod.tfvars" +gotf> TF_VAR_templated_var=myval gotf> TF_VAR_mapvar={ entry1 = { value1 = testvalue1 @@ -202,15 +271,9 @@ gotf> TF_VAR_mapvar={ value2 = false } } -gotf> TF_VAR_backend_path=.terraform/terraform-testmodule-prod.tfstate -gotf> TF_CLI_ARGS_init=-backend-config=path=".terraform/terraform-testmodule-prod.tfstate" -gotf> TF_VAR_state_key=testmodule -gotf> TF_VAR_foo=42 +gotf> TF_VAR_backend_path=.terraform/terraform-networking-prod.tfstate gotf> BAR=barvalue -gotf> TF_CLI_ARGS_apply=-var-file="../01_testmodule/test-prod.tfvars" -gotf> TF_CLI_ARGS_destroy=-var-file="../01_testmodule/test-prod.tfvars" -gotf> TF_CLI_ARGS_plan=-var-file="../01_testmodule/test-prod.tfvars" -gotf> TF_CLI_ARGS_refresh=-var-file="../01_testmodule/test-prod.tfvars" -gotf> TF_CLI_ARGS_import=-var-file="../01_testmodule/test-prod.tfvars" -gotf> TF_VAR_module_dir=01_testmodule +gotf> TF_CLI_ARGS_apply=-var-file="../global-prod.tfvars" -var-file="../global.tfvars" -var-file="prod.tfvars" +gotf> TF_VAR_state_key=networking +gotf> TF_VAR_foo=42 ``` diff --git a/cmd/gotf/gotf_test.go b/cmd/gotf/gotf_test.go index c9477a2..064f4f4 100644 --- a/cmd/gotf/gotf_test.go +++ b/cmd/gotf/gotf_test.go @@ -20,6 +20,8 @@ import ( "path/filepath" "testing" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" ) @@ -35,22 +37,27 @@ func TestExecute(t *testing.T) { runs []testRun }{ { - name: "happy path", + name: "networking module prod", runs: []testRun{ { - args: []string{"-d", "-c", "testdata/test-config-prod.yaml", "-p", "env=prod", "-m", "testdata/01_testmodule", "init", "-no-color"}, + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=prod", "-m", "testdata/01_networking", "init", "-no-color"}, want: []string{"Terraform has been successfully initialized!"}, }, { - args: []string{"-d", "-c", "testdata/test-config-prod.yaml", "-p", "env=prod", "-m", "testdata/01_testmodule", "plan", "-no-color"}, + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=prod", "-m", "testdata/01_networking", "plan", "-input=false", "-no-color"}, want: []string{ "# null_resource.echo will be created", "Plan: 1 to add, 0 to change, 0 to destroy.", }, }, { - args: []string{"-d", "-c", "testdata/test-config-prod.yaml", "-p", "env=prod", "-m", "testdata/01_testmodule", "apply", "-auto-approve", "-no-color"}, - want: []string{`foo = 42 + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=prod", "-m", "testdata/01_networking", "apply", "-auto-approve", "-no-color"}, + want: []string{ + "State path: .terraform/terraform-networking-prod.tfstate", + `bar = module1_prod +envSpecificVar = prodvalue +foo = 42 +globalVar = globalvalue mapvar = { entry1 = { value1 = testvalue1 @@ -60,7 +67,46 @@ mapvar = { value1 = testvalue2 value2 = false } -}`}, +} +myvar = value for networking +`}, + }, + }, + }, + { + name: "compute module dev", + runs: []testRun{ + { + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=dev", "-m", "testdata/02_compute", "init", "-no-color"}, + want: []string{"Terraform has been successfully initialized!"}, + }, + { + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=dev", "-m", "testdata/02_compute", "plan", "-input=false", "-no-color"}, + want: []string{ + "# null_resource.echo will be created", + "Plan: 1 to add, 0 to change, 0 to destroy.", + }, + }, + { + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=dev", "-m", "testdata/02_compute", "apply", "-auto-approve", "-no-color"}, + want: []string{ + "State path: .terraform/terraform-compute-dev.tfstate", + `bar = module2_dev +envSpecificVar = devvalue +foo = 42 +globalVar = globalvalue +mapvar = { + entry1 = { + value1 = testvalue1 + value2 = true + } + entry2 = { + value1 = testvalue2 + value2 = false + } +} +myvar = value for compute +`}, }, }, }, @@ -68,34 +114,34 @@ mapvar = { name: "backend check", runs: []testRun{ { - args: []string{"-d", "-c", "testdata/test-config-prod.yaml", "-p", "env=prod", "-m", "testdata/01_testmodule", "init", "-no-color"}, + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=prod", "-m", "testdata/01_networking", "init", "-no-color"}, want: []string{"Terraform has been successfully initialized!"}, }, { - args: []string{"-d", "-c", "testdata/test-config-prod.yaml", "-p", "env=prod", "-m", "testdata/01_testmodule", "plan", "-no-color"}, + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=prod", "-m", "testdata/01_networking", "plan", "-input=false", "-no-color"}, want: []string{ "# null_resource.echo will be created", "Plan: 1 to add, 0 to change, 0 to destroy.", }, }, { - args: []string{"-d", "-c", "testdata/test-config-dev.yaml", "-p", "env=dev", "-m", "testdata/01_testmodule", "plan", "-no-color"}, + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=dev", "-m", "testdata/01_networking", "plan", "-input=false", "-no-color"}, want: []string{ "Configured backend does not match current environment", - "Got: .terraform/terraform-testmodule-prod.tfstate", - "Want: .terraform/terraform-testmodule-dev.tfstate", + "Got: .terraform/terraform-networking-prod.tfstate", + "Want: .terraform/terraform-networking-dev.tfstate", "Run terraform init -reconfigure!", }, wantErr: true, }, { - args: []string{"-d", "-c", "testdata/test-config-dev.yaml", "-p", "env=dev", "-m", "testdata/01_testmodule", "init", "-reconfigure"}, + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=dev", "-m", "testdata/01_networking", "init", "-reconfigure"}, want: []string{ "Terraform has been successfully initialized!", }, }, { - args: []string{"-d", "-c", "testdata/test-config-dev.yaml", "-p", "env=dev", "-m", "testdata/01_testmodule", "plan", "-no-color"}, + args: []string{"-d", "-c", "testdata/test-config.yaml", "-p", "environment=dev", "-m", "testdata/01_networking", "plan", "-input=false", "-no-color"}, want: []string{ "# null_resource.echo will be created", "Plan: 1 to add, 0 to change, 0 to destroy.", @@ -106,20 +152,27 @@ mapvar = { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - os.RemoveAll("testdata/01_testmodule/.terraform") + tempDir, err := ioutil.TempDir("testdata", "gotf") + t.Cleanup(func() { os.RemoveAll(tempDir) }) - tempDir, err := ioutil.TempDir("", "gotf") panicOnError(err) - defer os.RemoveAll(tempDir) + panicOnError(os.Setenv("XDG_CACHE_HOME", filepath.Join(tempDir, "tfcache"))) - panicOnError(os.Setenv("XDG_CACHE_HOME", filepath.Join(tempDir, "test"))) + t.Cleanup(func() { + os.RemoveAll("testdata/01_networking/.terraform") + os.RemoveAll("testdata/02_compute/.terraform") + os.Remove("testdata/01_networking/terraform.tfstate") + os.Remove("testdata/02_compute/terraform.tfstate") + os.Remove("testdata/01_networking/.terraform.tfstate.lock.info") + os.Remove("testdata/02_compute/.terraform.tfstate.lock.info") + }) for _, run := range tt.runs { got, err := runGotf(run.args) if run.wantErr { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } for _, want := range run.want { assert.Contains(t, got, want) diff --git a/cmd/gotf/testdata/01_networking/dev.tfvars b/cmd/gotf/testdata/01_networking/dev.tfvars new file mode 100644 index 0000000..9d27bcd --- /dev/null +++ b/cmd/gotf/testdata/01_networking/dev.tfvars @@ -0,0 +1 @@ +bar = "module1_dev" diff --git a/cmd/gotf/testdata/01_networking/prod.tfvars b/cmd/gotf/testdata/01_networking/prod.tfvars new file mode 100644 index 0000000..1dd64b0 --- /dev/null +++ b/cmd/gotf/testdata/01_networking/prod.tfvars @@ -0,0 +1 @@ +bar = "module1_prod" diff --git a/cmd/gotf/testdata/01_testmodule/test.tf b/cmd/gotf/testdata/01_networking/test.tf similarity index 57% rename from cmd/gotf/testdata/01_testmodule/test.tf rename to cmd/gotf/testdata/01_networking/test.tf index dad58f3..725cd5a 100644 --- a/cmd/gotf/testdata/01_testmodule/test.tf +++ b/cmd/gotf/testdata/01_networking/test.tf @@ -8,6 +8,16 @@ variable "bar" {} variable "mapvar" {} +variable "myvar" {} + +variable "globalVar" {} + +variable "envSpecificVar" {} + +output "bar" { + value = var.bar +} + output "foo" { value = var.foo } @@ -16,6 +26,18 @@ output "mapvar" { value = var.mapvar } +output "globalVar" { + value = var.globalVar +} + +output "envSpecificVar" { + value = var.envSpecificVar +} + +output "myvar" { + value = var.myvar +} + resource "null_resource" "echo" { provisioner "local-exec" { command = "echo foo=${var.foo}" diff --git a/cmd/gotf/testdata/01_testmodule/test-dev.tfvars b/cmd/gotf/testdata/01_testmodule/test-dev.tfvars deleted file mode 100644 index 9b678e2..0000000 --- a/cmd/gotf/testdata/01_testmodule/test-dev.tfvars +++ /dev/null @@ -1 +0,0 @@ -bar = "bazvalue" diff --git a/cmd/gotf/testdata/01_testmodule/test-prod.tfvars b/cmd/gotf/testdata/01_testmodule/test-prod.tfvars deleted file mode 100644 index 9b678e2..0000000 --- a/cmd/gotf/testdata/01_testmodule/test-prod.tfvars +++ /dev/null @@ -1 +0,0 @@ -bar = "bazvalue" diff --git a/cmd/gotf/testdata/02_compute/dev.tfvars b/cmd/gotf/testdata/02_compute/dev.tfvars new file mode 100644 index 0000000..c4f9ed5 --- /dev/null +++ b/cmd/gotf/testdata/02_compute/dev.tfvars @@ -0,0 +1 @@ +bar = "module2_dev" diff --git a/cmd/gotf/testdata/02_compute/prod.tfvars b/cmd/gotf/testdata/02_compute/prod.tfvars new file mode 100644 index 0000000..89e554f --- /dev/null +++ b/cmd/gotf/testdata/02_compute/prod.tfvars @@ -0,0 +1 @@ +bar = "module2_prod" diff --git a/cmd/gotf/testdata/02_compute/test.tf b/cmd/gotf/testdata/02_compute/test.tf new file mode 100644 index 0000000..725cd5a --- /dev/null +++ b/cmd/gotf/testdata/02_compute/test.tf @@ -0,0 +1,48 @@ +terraform { + backend "local" {} +} + +variable "foo" {} + +variable "bar" {} + +variable "mapvar" {} + +variable "myvar" {} + +variable "globalVar" {} + +variable "envSpecificVar" {} + +output "bar" { + value = var.bar +} + +output "foo" { + value = var.foo +} + +output "mapvar" { + value = var.mapvar +} + +output "globalVar" { + value = var.globalVar +} + +output "envSpecificVar" { + value = var.envSpecificVar +} + +output "myvar" { + value = var.myvar +} + +resource "null_resource" "echo" { + provisioner "local-exec" { + command = "echo foo=${var.foo}" + } + provisioner "local-exec" { + command = "echo baz=${var.bar}" + } +} diff --git a/cmd/gotf/testdata/global-dev.tfvars b/cmd/gotf/testdata/global-dev.tfvars new file mode 100644 index 0000000..759b7a7 --- /dev/null +++ b/cmd/gotf/testdata/global-dev.tfvars @@ -0,0 +1 @@ +envSpecificVar = "devvalue" diff --git a/cmd/gotf/testdata/global-prod.tfvars b/cmd/gotf/testdata/global-prod.tfvars new file mode 100644 index 0000000..e28f7bd --- /dev/null +++ b/cmd/gotf/testdata/global-prod.tfvars @@ -0,0 +1 @@ +envSpecificVar = "prodvalue" diff --git a/cmd/gotf/testdata/global.tfvars b/cmd/gotf/testdata/global.tfvars new file mode 100644 index 0000000..f6924c4 --- /dev/null +++ b/cmd/gotf/testdata/global.tfvars @@ -0,0 +1 @@ +globalVar = "globalvalue" diff --git a/cmd/gotf/testdata/test-config-dev.yaml b/cmd/gotf/testdata/test-config-dev.yaml deleted file mode 100644 index 257022f..0000000 --- a/cmd/gotf/testdata/test-config-dev.yaml +++ /dev/null @@ -1,22 +0,0 @@ -terraformVersion: "0.12.21" -varFiles: - - 01_testmodule/test-{{ .Params.env }}.tfvars -vars: - foo: 42 - mapvar: |- - { - entry1 = { - value1 = testvalue1 - value2 = true - } - entry2 = { - value1 = testvalue2 - value2 = false - } - } - module_dir: "{{ base .Params.moduleDir }}" - state_key: '{{ (splitn "_" 2 (base .Params.moduleDir))._1 }}' -envs: - BAR: barvalue -backendConfigs: - path: .terraform/terraform-{{ .Vars.state_key }}-dev.tfstate diff --git a/cmd/gotf/testdata/test-config-prod.yaml b/cmd/gotf/testdata/test-config-prod.yaml deleted file mode 100644 index 86165da..0000000 --- a/cmd/gotf/testdata/test-config-prod.yaml +++ /dev/null @@ -1,22 +0,0 @@ -terraformVersion: "0.12.21" -varFiles: - - 01_testmodule/test-{{ .Params.env }}.tfvars -vars: - foo: 42 - mapvar: |- - { - entry1 = { - value1 = testvalue1 - value2 = true - } - entry2 = { - value1 = testvalue2 - value2 = false - } - } - module_dir: "{{ base .Params.moduleDir }}" - state_key: '{{ (splitn "_" 2 (base .Params.moduleDir))._1 }}' -envs: - BAR: barvalue -backendConfigs: - path: .terraform/terraform-{{ .Vars.state_key }}-prod.tfstate diff --git a/cmd/gotf/testdata/test-config.yaml b/cmd/gotf/testdata/test-config.yaml new file mode 100644 index 0000000..22ee0cb --- /dev/null +++ b/cmd/gotf/testdata/test-config.yaml @@ -0,0 +1,49 @@ +terraformVersion: 0.12.24 + +requiredParams: + environment: + - dev + - prod + +params: + param: myval + +globalVarFiles: + - global-{{ .Params.environment }}.tfvars + - global.tfvars + +globalVars: + foo: 42 + templated_var: "{{ .Params.param }}" + mapvar: |- + { + entry1 = { + value1 = testvalue1 + value2 = true + } + entry2 = { + value1 = testvalue2 + value2 = false + } + } + module_dir: "{{ .Params.moduleDir }}" + state_key: '{{ (splitn "_" 2 .Params.moduleDir)._1 }}' + +moduleVarFiles: + 01_networking: + - 01_networking/{{ .Params.environment }}.tfvars + 02_compute: + - 02_compute/{{ .Params.environment }}.tfvars + +moduleVars: + 01_networking: + myvar: value for networking + 02_compute: + myvar: value for compute + +envs: + BAR: barvalue + TEMPLATED_ENV: "{{ .Params.param }}" + +backendConfigs: + path: .terraform/terraform-{{ .Vars.state_key }}-{{ .Params.environment }}.tfstate diff --git a/go.sum b/go.sum index 956bdea..018e8f9 100644 --- a/go.sum +++ b/go.sum @@ -440,6 +440,7 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/tj/assert v0.0.0-20171129193455-018094318fb0 h1:Rw8kxzWo1mr6FSaYXjQELRe88y2KdfynXdnK72rdjtA= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao= diff --git a/pkg/config/config.go b/pkg/config/config.go index 91ba869..e689bfb 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -16,7 +16,6 @@ package config import ( "fmt" - "io" "io/ioutil" "log" "path/filepath" @@ -27,98 +26,206 @@ import ( "gopkg.in/yaml.v2" ) +type fileConfig struct { + TerraformVersion string `yaml:"terraformVersion"` + RequiredParams map[string][]string `yaml:"requiredParams"` + Params map[string]string `yaml:"params"` + GlobalVarFiles []string `yaml:"globalVarFiles"` + ModuleVarFiles map[string][]string `yaml:"moduleVarFiles"` + GlobalVars map[string]string `yaml:"globalVars"` + ModuleVars map[string]map[string]string `yaml:"moduleVars"` + Envs map[string]string `yaml:"envs"` + BackendConfigs map[string]string `yaml:"backendConfigs"` +} + type Config struct { - TerraformVersion string `yaml:"terraformVersion"` - VarFiles []string `yaml:"varFiles"` - Vars map[string]string `yaml:"vars"` - Envs map[string]string `yaml:"envs"` - BackendConfigs map[string]string `yaml:"backendConfigs"` + TerraformVersion string + VarFiles []string + Vars map[string]string + Envs map[string]string + BackendConfigs map[string]string } -func Load(configFile string, moduleDir string, params map[string]string) (*Config, error) { +const moduleDirParamName = "moduleDir" + +func Load(configFile string, modulePath string, cliParams map[string]string) (*Config, error) { log.Println("Loading config file:", configFile) cfgData, err := ioutil.ReadFile(configFile) if err != nil { - return nil, fmt.Errorf("could not load config file %q: %w", configFile, err) + return nil, err } - cfg, err := load(cfgData) + fileCfg, err := load(cfgData) if err != nil { - return nil, fmt.Errorf("could not load config file %q: %w", configFile, err) + return nil, err } - templatingInput := map[string]interface{}{ - "Params": params, + if err := checkRequiredParams(fileCfg, cliParams); err != nil { + return nil, err } - log.Println("Processing varFiles...") - cfgFileDir := filepath.Dir(configFile) - cfgFileDirRelativeToModuleDir, err := filepath.Rel(moduleDir, cfgFileDir) - if err != nil { + params := make(map[string]string) + if err := appendParams(params, fileCfg.Params); err != nil { return nil, err } - for i, f := range cfg.VarFiles { - sb := strings.Builder{} - err := renderTemplate(&sb, templatingInput, f) + if err := appendParams(params, cliParams); err != nil { + return nil, err + } + params[moduleDirParamName] = filepath.Base(modulePath) + + log.Println("Processing var files...") + cfgFileDir := filepath.Dir(configFile) + + cfg := &Config{ + TerraformVersion: fileCfg.TerraformVersion, + VarFiles: []string{}, + Vars: make(map[string]string), + Envs: make(map[string]string), + BackendConfigs: make(map[string]string), + } + + log.Println("Processing global var files...") + for _, f := range fileCfg.GlobalVarFiles { + varFilePath, err := computeModuleRelativeVarFilePath(f, params, cfgFileDir, modulePath) if err != nil { return nil, err } - varFile := sb.String() - if !filepath.IsAbs(f) { - varFile = filepath.Join(cfgFileDirRelativeToModuleDir, varFile) + cfg.VarFiles = append(cfg.VarFiles, varFilePath) + } + + moduleDir := params[moduleDirParamName] + if moduleVarFiles, ok := fileCfg.ModuleVarFiles[moduleDir]; ok { + log.Println("Processing module var files...") + for _, f := range moduleVarFiles { + varFilePath, err := computeModuleRelativeVarFilePath(f, params, cfgFileDir, modulePath) + if err != nil { + return nil, err + } + cfg.VarFiles = append(cfg.VarFiles, varFilePath) } - cfg.VarFiles[i] = varFile } - log.Println("Processing vars...") - for key, value := range cfg.Vars { - sb := strings.Builder{} - if err := renderTemplate(&sb, templatingInput, value); err != nil { + log.Println("Processing global vars...") + for key, value := range fileCfg.GlobalVars { + result, err := computeValue(value, params) + if err != nil { return nil, err } - cfg.Vars[key] = sb.String() + cfg.Vars[key] = result + } + + if moduleVars, ok := fileCfg.ModuleVars[moduleDir]; ok { + log.Println("Processing module vars...") + for key, value := range moduleVars { + result, err := computeValue(value, params) + if err != nil { + return nil, err + } + cfg.Vars[key] = result + } } log.Println("Processing envs...") - for key, value := range cfg.Envs { - sb := strings.Builder{} - if err := renderTemplate(&sb, templatingInput, value); err != nil { + for key, value := range fileCfg.Envs { + result, err := computeValue(value, params) + if err != nil { return nil, err } - cfg.Envs[key] = sb.String() + cfg.Envs[key] = result } - templatingInput = map[string]interface{}{ + templatingInput := map[string]interface{}{ "Vars": cfg.Vars, "Envs": cfg.Envs, "Params": params, } - log.Println("Proessing backendConfigs...") - for key, value := range cfg.BackendConfigs { - sb := strings.Builder{} - if err := renderTemplate(&sb, templatingInput, value); err != nil { + log.Println("Processing backend configs...") + for key, value := range fileCfg.BackendConfigs { + result, err := renderTemplate(templatingInput, value) + if err != nil { return nil, err } - cfg.BackendConfigs[key] = sb.String() + cfg.BackendConfigs[key] = result } return cfg, nil } -func load(cfgData []byte) (*Config, error) { - var cfg Config +func checkRequiredParams(fileCfg *fileConfig, cliParams map[string]string) error { + for k, v := range fileCfg.RequiredParams { + value, ok := cliParams[k] + if !ok { + return fmt.Errorf("required parameter %q must be specified", k) + } + if len(v) > 0 { + var hasAllowedValue bool + for _, allowed := range v { + if value == allowed { + hasAllowedValue = true + break + } + } + if !hasAllowedValue { + return fmt.Errorf("value for required parameter %q must be one of %v", k, v) + } + } + } + return nil +} + +func load(cfgData []byte) (*fileConfig, error) { + var cfg fileConfig if err := yaml.Unmarshal(cfgData, &cfg); err != nil { return nil, err } return &cfg, nil } -func renderTemplate(wr io.Writer, data map[string]interface{}, text string) error { +func renderTemplate(data map[string]interface{}, tmpl string) (string, error) { + wr := strings.Builder{} tpl := template.New("gotpl").Funcs(sprig.TxtFuncMap()).Option("missingkey=error") - tpl, err := tpl.Parse(text) + tpl, err := tpl.Parse(tmpl) if err != nil { - return err + return "", err + } + if err := tpl.Execute(&wr, data); err != nil { + return "", err + } + return wr.String(), nil +} + +func computeModuleRelativeVarFilePath(varFilePathTemplate string, params map[string]string, cfgFileDir string, modulePath string) (string, error) { + templatingInput := map[string]interface{}{ + "Params": params, + } + varFilePath, err := renderTemplate(templatingInput, varFilePathTemplate) + if err != nil { + return "", err + } + if !filepath.IsAbs(varFilePath) { + varFilePath := filepath.Join(cfgFileDir, varFilePath) + if varFilePath, err = filepath.Rel(modulePath, varFilePath); err != nil { + return "", err + } + return varFilePath, nil + } + return varFilePath, nil +} + +func computeValue(valueTemplate string, params map[string]string) (string, error) { + templatingInput := map[string]interface{}{ + "Params": params, + } + return renderTemplate(templatingInput, valueTemplate) +} + +func appendParams(dst map[string]string, src map[string]string) error { + for k, v := range src { + if k == moduleDirParamName { + return fmt.Errorf("param %q is reserved and set automatically", moduleDirParamName) + } + dst[k] = v } - return tpl.Execute(wr, data) + return nil } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 3c06f1d..55094af 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -18,32 +18,239 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestLoad(t *testing.T) { - got, err := Load("testdata/test_config.yaml", "testdata/testmodule", map[string]string{ - "param": "paramvalue", - "env": "prod", - }) - assert.NoError(t, err) - - assert.Equal(t, []string{ - "../testmodule/test1-prod.tfvars", - "../testmodule/test2-prod.tfvars", - }, got.VarFiles) - assert.Equal(t, map[string]string{ - "foo": "foovalue", - "templatedVar": "paramvalue", - "mapvar": "{\n value1 = \"testvalue\"\n value2 = true\n}", - }, got.Vars) - assert.Equal(t, map[string]string{ - "BAR": "barvalue", - "TEMPLATED_ENV": "paramvalue", - }, got.Envs) - assert.Equal(t, map[string]string{ - "backend_key": "be_key_foovalue_barvalue", - "backend_storage_account_name": "be_storage_account_name_foovalue_barvalue", - "backend_resource_group_name": "be_resource_group_name_foovalue_barvalue", - "backend_container_name": "be_container_name_foovalue_barvalue", - }, got.BackendConfigs) + type args struct { + configFile string + moduleDir string + params map[string]string + } + tests := []struct { + name string + args args + want *Config + wantErr bool + wantErrMsg string + }{ + { + name: "Load dev config testmodule1", + args: struct { + configFile string + moduleDir string + params map[string]string + }{ + configFile: "testdata/test-config.yaml", + moduleDir: "testmodule1", + params: map[string]string{ + "environment": "dev", + }, + }, + want: &Config{ + TerraformVersion: "0.12.24", + VarFiles: []string{ + "../testdata/global.tfvars", + "../testdata/global-dev.tfvars", + "../testdata/testmodule1/test1-dev.tfvars", + "../testdata/testmodule1/test2-dev.tfvars", + }, + Vars: map[string]string{ + "foo": "foovalue", + "templatedVar": "paramvalue", + "mapvar": "{\n value1 = \"testvalue\"\n value2 = true\n}", + "moduleVar1": "testmodule1_value1", + "moduleVar2": "testmodule1_value2", + }, + Envs: map[string]string{ + "BAR": "barvalue", + "TEMPLATED_ENV": "paramvalue", + }, + BackendConfigs: map[string]string{ + "key": "testmodule1", + "storage_account_name": "mytfstateaccountdev", + "resource_group_name": "mytfstate-dev", + "container_name": "mytfstate-dev", + }, + }, + }, + { + name: "Load dev config testmodule2", + args: struct { + configFile string + moduleDir string + params map[string]string + }{ + configFile: "testdata/test-config.yaml", + moduleDir: "testmodule2", + params: map[string]string{ + "environment": "dev", + }, + }, + want: &Config{ + TerraformVersion: "0.12.24", + VarFiles: []string{ + "../testdata/global.tfvars", + "../testdata/global-dev.tfvars", + "../testdata/testmodule2/test1-dev.tfvars", + "../testdata/testmodule2/test2-dev.tfvars", + }, + Vars: map[string]string{ + "foo": "foovalue", + "templatedVar": "paramvalue", + "mapvar": "{\n value1 = \"testvalue\"\n value2 = true\n}", + "moduleVar1": "testmodule2_value1", + "moduleVar2": "testmodule2_value2", + }, + Envs: map[string]string{ + "BAR": "barvalue", + "TEMPLATED_ENV": "paramvalue", + }, + BackendConfigs: map[string]string{ + "key": "testmodule2", + "storage_account_name": "mytfstateaccountdev", + "resource_group_name": "mytfstate-dev", + "container_name": "mytfstate-dev", + }, + }, + }, + { + name: "Load prod config testmodule1", + args: struct { + configFile string + moduleDir string + params map[string]string + }{ + configFile: "testdata/test-config.yaml", + moduleDir: "testmodule1", + params: map[string]string{ + "environment": "prod", + }, + }, + want: &Config{ + TerraformVersion: "0.12.24", + VarFiles: []string{ + "../testdata/global.tfvars", + "../testdata/global-prod.tfvars", + "../testdata/testmodule1/test1-prod.tfvars", + "../testdata/testmodule1/test2-prod.tfvars", + }, + Vars: map[string]string{ + "foo": "foovalue", + "templatedVar": "paramvalue", + "mapvar": "{\n value1 = \"testvalue\"\n value2 = true\n}", + "moduleVar1": "testmodule1_value1", + "moduleVar2": "testmodule1_value2", + }, + Envs: map[string]string{ + "BAR": "barvalue", + "TEMPLATED_ENV": "paramvalue", + }, + BackendConfigs: map[string]string{ + "key": "testmodule1", + "storage_account_name": "mytfstateaccountprod", + "resource_group_name": "mytfstate-prod", + "container_name": "mytfstate-prod", + }, + }, + }, + { + name: "Load prod config testmodule2", + args: struct { + configFile string + moduleDir string + params map[string]string + }{ + configFile: "testdata/test-config.yaml", + moduleDir: "testmodule2", + params: map[string]string{ + "environment": "prod", + }, + }, + want: &Config{ + TerraformVersion: "0.12.24", + VarFiles: []string{ + "../testdata/global.tfvars", + "../testdata/global-prod.tfvars", + "../testdata/testmodule2/test1-prod.tfvars", + "../testdata/testmodule2/test2-prod.tfvars", + }, + Vars: map[string]string{ + "foo": "foovalue", + "templatedVar": "paramvalue", + "mapvar": "{\n value1 = \"testvalue\"\n value2 = true\n}", + "moduleVar1": "testmodule2_value1", + "moduleVar2": "testmodule2_value2", + }, + Envs: map[string]string{ + "BAR": "barvalue", + "TEMPLATED_ENV": "paramvalue", + }, + BackendConfigs: map[string]string{ + "key": "testmodule2", + "storage_account_name": "mytfstateaccountprod", + "resource_group_name": "mytfstate-prod", + "container_name": "mytfstate-prod", + }, + }, + }, + { + name: "Missing required param", + args: struct { + configFile string + moduleDir string + params map[string]string + }{ + configFile: "testdata/test-config.yaml", + moduleDir: "testmodule1", + }, + wantErr: true, + wantErrMsg: `required parameter "environment" must be specified`, + }, + { + name: "Invalid required param value", + args: struct { + configFile string + moduleDir string + params map[string]string + }{ + configFile: "testdata/test-config.yaml", + moduleDir: "testmodule1", + params: map[string]string{ + "environment": "invalid", + }, + }, + wantErr: true, + wantErrMsg: `value for required parameter "environment" must be one of [dev prod]`, + }, + { + name: "Module dir explicitly set", + args: struct { + configFile string + moduleDir string + params map[string]string + }{ + configFile: "testdata/test-config.yaml", + moduleDir: "testmodule1", + params: map[string]string{ + "environment": "dev", + "moduleDir": "dummy", + }, + }, + wantErr: true, + wantErrMsg: `param "moduleDir" is reserved and set automatically`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Load(tt.args.configFile, tt.args.moduleDir, tt.args.params) + if tt.wantErr { + require.Error(t, err) + assert.Equal(t, tt.wantErrMsg, err.Error()) + } else { + require.NoError(t, err) + assert.Equal(t, tt.want, got) + } + }) + } } diff --git a/pkg/config/testdata/global-dev.tfvars b/pkg/config/testdata/global-dev.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config/testdata/global-prod.tfvars b/pkg/config/testdata/global-prod.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config/testdata/global.tfvars b/pkg/config/testdata/global.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config/testdata/test-config.yaml b/pkg/config/testdata/test-config.yaml new file mode 100644 index 0000000..105b818 --- /dev/null +++ b/pkg/config/testdata/test-config.yaml @@ -0,0 +1,48 @@ +terraformVersion: "0.12.24" + +requiredParams: + environment: + - dev + - prod + +params: + param: paramvalue + +moduleVars: + testmodule1: + moduleVar1: testmodule1_value1 + moduleVar2: testmodule1_value2 + testmodule2: + moduleVar1: testmodule2_value1 + moduleVar2: testmodule2_value2 + +moduleVarFiles: + testmodule1: + - testmodule1/test1-{{ .Params.environment }}.tfvars + - testmodule1/test2-{{ .Params.environment }}.tfvars + testmodule2: + - testmodule2/test1-{{ .Params.environment }}.tfvars + - testmodule2/test2-{{ .Params.environment }}.tfvars + +globalVarFiles: + - global.tfvars + - global-{{ .Params.environment }}.tfvars + +globalVars: + foo: foovalue + templatedVar: "{{ .Params.param }}" + mapvar: |- + { + value1 = "testvalue" + value2 = true + } + +envs: + BAR: barvalue + TEMPLATED_ENV: "{{ .Params.param }}" + +backendConfigs: + key: "{{ .Params.moduleDir }}" + storage_account_name: mytfstateaccount{{ .Params.environment }} + resource_group_name: mytfstate-{{ .Params.environment }} + container_name: mytfstate-{{ .Params.environment }} diff --git a/pkg/config/testdata/test_config.yaml b/pkg/config/testdata/test_config.yaml deleted file mode 100644 index 0a89ec6..0000000 --- a/pkg/config/testdata/test_config.yaml +++ /dev/null @@ -1,20 +0,0 @@ -terraformVersion: "0.12.21" -varFiles: - - testmodule/test1-{{ .Params.env }}.tfvars - - testmodule/test2-{{ .Params.env }}.tfvars -vars: - foo: foovalue - templatedVar: "{{ .Params.param }}" - mapvar: |- - { - value1 = "testvalue" - value2 = true - } -envs: - BAR: barvalue - TEMPLATED_ENV: "{{ .Params.param }}" -backendConfigs: - backend_key: be_key_{{ .Vars.foo }}_{{ .Envs.BAR }} - backend_storage_account_name: be_storage_account_name_{{ .Vars.foo }}_{{ .Envs.BAR }} - backend_resource_group_name: be_resource_group_name_{{ .Vars.foo }}_{{ .Envs.BAR }} - backend_container_name: be_container_name_{{ .Vars.foo }}_{{ .Envs.BAR }} diff --git a/pkg/config/testdata/testmodule/test.tf b/pkg/config/testdata/testmodule/test.tf deleted file mode 100644 index e2c4734..0000000 --- a/pkg/config/testdata/testmodule/test.tf +++ /dev/null @@ -1,24 +0,0 @@ -terraform { - backend "local" {} -} - -variable "foo" {} - -variable "bar" {} - -variable "baz" {} - -variable "mapvar" { - type = map(object({ - value1 = string - value2 = bool - })) -} - -output "foo" { - value = var.foo -} - -output "baz" { - value = var.baz -} diff --git a/pkg/config/testdata/testmodule/test1-prod.tfvars b/pkg/config/testdata/testmodule/test1-prod.tfvars deleted file mode 100644 index 29c4d10..0000000 --- a/pkg/config/testdata/testmodule/test1-prod.tfvars +++ /dev/null @@ -1 +0,0 @@ -bar = "barvalue" diff --git a/pkg/config/testdata/testmodule/test2-prod.tfvars b/pkg/config/testdata/testmodule/test2-prod.tfvars deleted file mode 100644 index d38d803..0000000 --- a/pkg/config/testdata/testmodule/test2-prod.tfvars +++ /dev/null @@ -1 +0,0 @@ -baz = "bazvalue" diff --git a/pkg/config/testdata/testmodule1/test1-dev.tfvars b/pkg/config/testdata/testmodule1/test1-dev.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config/testdata/testmodule1/test1-prod.tfvars b/pkg/config/testdata/testmodule1/test1-prod.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config/testdata/testmodule1/test2-dev.tfvars b/pkg/config/testdata/testmodule1/test2-dev.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config/testdata/testmodule1/test2-prod.tfvars b/pkg/config/testdata/testmodule1/test2-prod.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config/testdata/testmodule2/test1-dev.tfvars b/pkg/config/testdata/testmodule2/test1-dev.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config/testdata/testmodule2/test1-prod.tfvars b/pkg/config/testdata/testmodule2/test1-prod.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config/testdata/testmodule2/test2-dev.tfvars b/pkg/config/testdata/testmodule2/test2-dev.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/config/testdata/testmodule2/test2-prod.tfvars b/pkg/config/testdata/testmodule2/test2-prod.tfvars new file mode 100644 index 0000000..e69de29 diff --git a/pkg/gotf/gotf.go b/pkg/gotf/gotf.go index c114cd8..94d1873 100644 --- a/pkg/gotf/gotf.go +++ b/pkg/gotf/gotf.go @@ -88,29 +88,20 @@ func Run(args Args) error { log.SetOutput(ioutil.Discard) } - params := make(map[string]string) - for k, v := range args.Params { - if k == "moduleDir" { - return fmt.Errorf("param 'module-dir' is reserved for internal use and set automatically") - } - params[k] = v - } - params["moduleDir"] = args.ModuleDir - - cfg, err := config.Load(args.ConfigFile, args.ModuleDir, params) + cfg, err := config.Load(args.ConfigFile, args.ModuleDir, args.Params) if err != nil { - return err + return fmt.Errorf("could not load config file %q: %w", args.ConfigFile, err) } - var binary string + var tfBinary string if cfg.TerraformVersion != "" { log.Println("Using Terraform version", cfg.TerraformVersion) cacheDir, err := xdg.CacheFile(filepath.Join("gotf", "terraform", cfg.TerraformVersion)) if err != nil { return err } - binary = filepath.Join(cacheDir, "terraform") - if _, err := os.Stat(binary); err != nil { + tfBinary = filepath.Join(cacheDir, "terraform") + if _, err := os.Stat(tfBinary); err != nil { if os.IsNotExist(err) { installer := terraform.NewInstaller(urlTemplates, cfg.TerraformVersion, hashicorpPGPKey, cacheDir) if err = installer.Install(); err != nil { @@ -123,12 +114,12 @@ func Run(args Args) error { log.Println("Terraform version", cfg.TerraformVersion, "already installed.") } } else { - binary = "terraform" + tfBinary = "terraform" } - log.Println("Terraform binary:", binary) + log.Println("Terraform binary:", tfBinary) shell := sh.Shell{} - tf := terraform.NewTerraform(cfg, args.ModuleDir, args.Params, shell, binary) + tf := terraform.NewTerraform(cfg, args.ModuleDir, args.Params, shell, tfBinary) return tf.Execute(args.Args...) }