diff --git a/README.md b/README.md index 901b372..d6ae87f 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,16 @@ mixins: workingDir: myinfra initFile: providers.tf ``` +Or +```yaml +mixins: +- terraform: + clientVersion: 1.0.3 + workingDirs: + - infra1 + - infra2 + initFile: providers.tf +``` ### clientVersion The Terraform client version can be specified via the `clientVersion` configuration when declaring this mixin. @@ -44,11 +54,15 @@ The Terraform client version can be specified via the `clientVersion` configurat ### workingDir The `workingDir` configuration setting is the relative path to your terraform files. Defaults to "terraform". +### workingDirs +The `workingDirs` configuraiton setting is used when multiple terraform plans are part of a single bundle. When the `workingDirs` setting is specified then the `workingDir` setting is ignored. + ### initFile Terraform providers are installed into the bundle during porter build. We recommend that you put your provider declarations into a single file, e.g. "terraform/providers.tf". Then use `initFile` to specify the relative path to this file within workingDir. This will dramatically improve Docker image layer caching and performance when building, publishing and installing the bundle. +If `workingDirs` is specified instead of `workingDir` then the `initFile` must be the same in all of the terraform plans for the bundle. > Note: this approach isn't suitable when using terraform modules as those need to be "initilized" as well but aren't specified in the `initFile`. You shouldn't specifiy an `initFile` in this situation. ### User Agent Opt Out @@ -77,28 +91,7 @@ You can add your own custom strings to the user agent string by editing your [te ### Let Porter do the heavy lifting -The simplest way to use this mixin with Porter is to let Porter track the Terraform [state](https://www.terraform.io/docs/state/index.html) as actions are executed. This can be done via a parameter of type `file` that has a source of a corresponding output (of the same `file` type). Each time the bundle is executed, the output will capture the updated state file and inject it into the next action via its parameter correlate. - -Here is an example setup that works with Porter v0.38: - -```yaml -parameters: - - name: tfstate - type: file - # This designates the path within the installer to place the parameter value - path: /cnab/app/terraform/terraform.tfstate - # Here we tell Porter that the value for this parameter should come from the 'tfstate' output - source: - output: tfstate - -outputs: - - name: tfstate - type: file - # This designates the path within the installer to read the output from - path: /cnab/app/terraform/terraform.tfstate -``` - -If you are working with the Porter v1 prerelease, use the new state section: +The simplest way to use this mixin with Porter is to let Porter track the Terraform [state](https://www.terraform.io/docs/state/index.html) as actions are executed. This can be done via the state section: ```yaml state: @@ -108,10 +101,30 @@ state: path: terraform/terraform.tfvars.json ``` -The [TabbyCats Tracker bundle](https://github.com/carolynvs/tabbycat-demo) is a good example of how to use the terraform mixin with the Porter v1 prerelease. +The [TabbyCats Tracker bundle](https://github.com/carolynvs/tabbycat-demo) is a good example of how to use the terraform mixin with Porter v1. The specified path inside the installer (`/cnab/app/terraform/terraform.tfstate`) should be where Terraform will be looking to read/write its state. For a full example bundle using this approach, see the [basic-tf-example](examples/basic-tf-example). +Any arbitrary file can be added to the state including any files created by terraform during install or upgrade. + +When working with multiple different terraform plans in the same bundle make sure to specify the path to the corresponding plans state: + +```yaml +state: + - name: infra1-tfstate + path: infra1/terraform.tfstate + - name: infra1-tfvars + path: infra1/terraform.tfvars.json + - name: infra1-file + path: infra1/infra1-file + - name: infra2-tfstate + path: infra2/terraform.tfstate + - name: infra2-tfvars + path: infra2/terraform.tfvars.json + - name: infra2-file + path: infra2/infra2-file +``` + ### Remote Backends Alternatively, state can be managed by a remote backend. When doing so, each action step needs to supply the remote backend config via `backendConfig`. In the step examples below, the configuration has key/value pairs according to the [Azurerm](https://www.terraform.io/docs/backends/types/azurerm.html) backend. @@ -303,3 +316,10 @@ install: See the Porter [Outputs documentation](https://porter.sh/wiring/#outputs) on how to wire up outputs for use in a bundle. + + +## Multiple Terraform Plans In A Single Bundle + +Multiple terraform plans can be specified for a single bundle. When using the mixin with this configuration then every step **MUST** include a `workingDir` configuration setting so that porter can resolve the corresponding plan for that step at runtime. + +The `workingDir` and `workingDirs` configuration settings are mutally exclusive. If the `workingDirs` configuration setting is provided then anything set for `workingDir` will be ignored at bundle build time. \ No newline at end of file diff --git a/examples/multiple-mixin-configs/infra1/main.tf b/examples/multiple-mixin-configs/infra1/main.tf index d787415..6f06b7c 100644 --- a/examples/multiple-mixin-configs/infra1/main.tf +++ b/examples/multiple-mixin-configs/infra1/main.tf @@ -1,4 +1,4 @@ resource "local_file" "foo" { content = var.infra1_var - filename = "${path.module}/infra1" + filename = "${path.module}/infra1-file" } diff --git a/examples/multiple-mixin-configs/infra2/main.tf b/examples/multiple-mixin-configs/infra2/main.tf index acb92a5..fa25494 100644 --- a/examples/multiple-mixin-configs/infra2/main.tf +++ b/examples/multiple-mixin-configs/infra2/main.tf @@ -1,4 +1,4 @@ resource "local_file" "foo" { content = var.infra2_var - filename = "${path.module}/infra1" + filename = "${path.module}/infra2-file" } diff --git a/examples/multiple-mixin-configs/porter.yaml b/examples/multiple-mixin-configs/porter.yaml index 045176d..c85d337 100644 --- a/examples/multiple-mixin-configs/porter.yaml +++ b/examples/multiple-mixin-configs/porter.yaml @@ -78,3 +78,17 @@ outputs: applyTo: - 'install' - 'upgrade' + +state: + - name: infra1-tfstate + path: infra1/terraform.tfstate + - name: infra1-tfvars + path: infra1/terraform.tfvars.json + - name: infra1-file + path: infra1/infra1-file + - name: infra2-tfstate + path: infra2/terraform.tfstate + - name: infra2-tfvars + path: infra2/terraform.tfvars.json + - name: infra2-file + path: infra2/infra2-file diff --git a/pkg/terraform/build.go b/pkg/terraform/build.go index ab664cc..2b7f809 100644 --- a/pkg/terraform/build.go +++ b/pkg/terraform/build.go @@ -26,6 +26,7 @@ RUN cd $BUNDLE_DIR/{{.}} && \ terraform init -backend=false && \ rm -fr .terraform/providers && \ terraform providers mirror /usr/local/share/terraform/plugins +{{end}} {{else}} COPY {{.WorkingDir}}/{{.InitFile}} $BUNDLE_DIR/{{.WorkingDir}}/ RUN cd $BUNDLE_DIR/{{.WorkingDir}} && \ @@ -33,7 +34,6 @@ RUN cd $BUNDLE_DIR/{{.WorkingDir}} && \ rm -fr .terraform/providers && \ terraform providers mirror /usr/local/share/terraform/plugins {{end}} -{{end}} ` // BuildInput represents stdin passed to the mixin for the build command. @@ -81,7 +81,6 @@ func (m *Mixin) Build(ctx context.Context) error { fmt.Fprintf(m.Err, "DEBUG: List of working dirs was provided, using :\n%v\n", input.Config.WorkingDirs) } } - tmpl, err := template.New("Dockerfile").Parse(dockerfileLines) if err != nil { return errors.Wrapf(err, "error parsing terraform mixin Dockerfile template") diff --git a/pkg/terraform/install.go b/pkg/terraform/install.go index d4aa277..e0da305 100644 --- a/pkg/terraform/install.go +++ b/pkg/terraform/install.go @@ -13,8 +13,6 @@ const defaultTerraformVarsFilename = "terraform.tfvars.json" // Install runs a terraform apply func (m *Mixin) Install(ctx context.Context) error { - fmt.Fprintf(m.Err, "\n\nIN INSTALL\n\n") - fmt.Printf("\n\nIN INSTALL\n\n") action, err := m.loadAction(ctx) if err != nil { return err diff --git a/pkg/terraform/terraform.go b/pkg/terraform/terraform.go index 003e8af..31e18d3 100644 --- a/pkg/terraform/terraform.go +++ b/pkg/terraform/terraform.go @@ -142,18 +142,12 @@ func (m *Mixin) commandPreRun(ctx context.Context, step *Step) error { if step.LogLevel != "" { os.Setenv("TF_LOG", step.LogLevel) } - fmt.Fprintf(m.Err, "\n\nSTEP:\n\n%+v\n\n", step) - fmt.Fprintf(m.Err, "\n\nCONFIG:\n\n%+v\n\n", m.config) // Determine the working directory for this step. - // If the config has WorkingDirs set then validate that the step has WorkingDir set to one of the values - // in the configs "WorkingDirs" + // TODO: (gettys) this should validate against the mixin config configuration settings if step.WorkingDir != "" { stepDir := step.GetWorkingDir() - fmt.Fprintf(m.Err, "\n\nCDing STEP WORKINGDIR to %v\n\n", stepDir) - // Validate that the step working dir is one of the values in the mixin config m.Chdir(stepDir) } else { - fmt.Fprintf(m.Err, "\n\nCDing CONFIG to %v\n\n", m.config.WorkingDir) m.Chdir(m.config.WorkingDir) } if m.DebugMode { @@ -162,7 +156,6 @@ func (m *Mixin) commandPreRun(ctx context.Context, step *Step) error { // Initialize Terraform fmt.Println("Initializing Terraform...") - fmt.Println("HELLO") err := m.Init(ctx, step.BackendConfig) if err != nil { return fmt.Errorf("could not init terraform, %s", err) diff --git a/scripts/test/test-cli.sh b/scripts/test/test-cli.sh index e0fa2df..0c85a79 100755 --- a/scripts/test/test-cli.sh +++ b/scripts/test/test-cli.sh @@ -26,6 +26,7 @@ function verify-output() { } +##### Basic Example Test ##### # Copy terraform assets cp -r ${REPO_DIR}/examples/basic-tf-example/terraform . @@ -73,3 +74,29 @@ verify-output "json_encoded_html_string_var" '?new#conn&string$characters~!' verify-output "complex_object_var" '{"nested_object":{"internal_value":"https://new.connection.com?test&test=$hello"},"top_value":"https://my.updated.service?test=$id<>"}' ${PORTER_HOME}/porter uninstall --debug + +rm -rf * + +##### Multiple Working Dirs Test ##### +# Copy terraform assets +cp -r ${REPO_DIR}/examples/multiple-mixin-configs/ . + +${PORTER_HOME}/porter build +${PORTER_HOME}/porter install --verbosity=debug \ + --param infra1_var="foo" \ + --param infra2_var="bar" + +verify-output "infra1_output" "foo" +verify-output "infra2_output" "bar" + +${PORTER_HOME}/porter upgrade --verbosity=debug \ + --param infra1_var="upgradeFoo" \ + --param infra2_var="upgradeBar" + +verify-output "infra1_output" "upgradeFoo" +verify-output "infra2_output" "upgradeBar" + +${PORTER_HOME}/porter uninstall --verbosity=debug + + +