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

feat: add multiple working dirs #115

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 141 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,32 @@ 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.

### 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
Expand Down Expand Up @@ -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:
Expand All @@ -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.
Expand Down Expand Up @@ -303,3 +316,108 @@ 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.

```yaml
schemaVersion: 1.0.0
name: mulitple-mixin-configs
version: 0.1.0
registry: ghcr.io/getporter

parameters:
- name: infra1_var
type: string
default: 'infra1'
applyTo:
- 'install'
- 'upgrade'
- 'uninstall'
- name: infra2_var
type: string
default: 'infra2'
applyTo:
- 'install'
- 'upgrade'
- 'uninstall'
mixins:
- terraform:
workingDirs:
- infra1
- infra2

install:
- terraform:
description: 'infra 1'
workingDir: 'infra1'
vars:
infra1_var: ${bundle.parameters.infra1_var}
outputs:
- name: infra1_output
- terraform:
description: 'infra 2'
workingDir: 'infra2'
vars:
infra2_var: ${bundle.parameters.infra2_var}
outputs:
- name: infra2_output

upgrade:
- terraform:
description: 'Upgrade infra 1 assets'
workingDir: 'infra1'
vars:
infra1_var: ${bundle.parameters.infra1_var}
outputs:
- name: infra1_output
- terraform:
description: 'infra 2'
workingDir: 'infra2'
vars:
infra2_var: ${bundle.parameters.infra2_var}
outputs:
- name: infra2_output

uninstall:
- terraform:
description: 'Uninstall infra 1 assets'
workingDir: 'infra1'
vars:
infra1_var: ${bundle.parameters.infra1_var}
- terraform:
description: 'infra 2'
workingDir: 'infra2'
vars:
infra2_var: ${bundle.parameters.infra2_var}
outputs:
- name: infra1_output
type: string
applyTo:
- 'install'
- 'upgrade'
- name: infra2_output
type: string
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

```
13 changes: 13 additions & 0 deletions examples/multiple-mixin-configs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Basic Terraform Example Bundle

This example demonstrates how to define and use variables and outputs of different data types in a bundle.

## Try it out

```
cd examples/basic-tf-example
porter build
porter install
porter upgrade
porter uninstall
```
4 changes: 4 additions & 0 deletions examples/multiple-mixin-configs/infra1/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
resource "local_file" "foo" {
content = var.infra1_var
filename = "${path.module}/infra1-file"
}
3 changes: 3 additions & 0 deletions examples/multiple-mixin-configs/infra1/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "infra1_output" {
value = var.infra1_var
}
4 changes: 4 additions & 0 deletions examples/multiple-mixin-configs/infra1/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
variable "infra1_var" {
type = string
description = "Variable for infra 1 working dir"
}
4 changes: 4 additions & 0 deletions examples/multiple-mixin-configs/infra2/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
resource "local_file" "foo" {
content = var.infra2_var
filename = "${path.module}/infra2-file"
}
3 changes: 3 additions & 0 deletions examples/multiple-mixin-configs/infra2/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "infra2_output" {
value = var.infra2_var
}
4 changes: 4 additions & 0 deletions examples/multiple-mixin-configs/infra2/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
variable "infra2_var" {
type = string
description = "Variable for infra 2 working dir"
}
94 changes: 94 additions & 0 deletions examples/multiple-mixin-configs/porter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
schemaVersion: 1.0.0
name: mulitple-mixin-configs
version: 0.1.0
registry: ghcr.io/getporter

parameters:
- name: infra1_var
type: string
default: 'infra1'
applyTo:
- 'install'
- 'upgrade'
- 'uninstall'
- name: infra2_var
type: string
default: 'infra2'
applyTo:
- 'install'
- 'upgrade'
- 'uninstall'
mixins:
- terraform:
workingDirs:
- infra1
- infra2

install:
- terraform:
description: 'infra 1'
workingDir: 'infra1'
vars:
infra1_var: ${bundle.parameters.infra1_var}
outputs:
- name: infra1_output
- terraform:
description: 'infra 2'
workingDir: 'infra2'
vars:
infra2_var: ${bundle.parameters.infra2_var}
outputs:
- name: infra2_output

upgrade:
- terraform:
description: 'Upgrade infra 1 assets'
workingDir: 'infra1'
vars:
infra1_var: ${bundle.parameters.infra1_var}
outputs:
- name: infra1_output
- terraform:
description: 'infra 2'
workingDir: 'infra2'
vars:
infra2_var: ${bundle.parameters.infra2_var}
outputs:
- name: infra2_output

uninstall:
- terraform:
description: 'Uninstall infra 1 assets'
workingDir: 'infra1'
vars:
infra1_var: ${bundle.parameters.infra1_var}
- terraform:
description: 'infra 2'
workingDir: 'infra2'
vars:
infra2_var: ${bundle.parameters.infra2_var}
outputs:
- name: infra1_output
type: string
applyTo:
- 'install'
- 'upgrade'
- name: infra2_output
type: string
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
15 changes: 11 additions & 4 deletions magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
package main

import (
"os"

"get.porter.sh/magefiles/mixins"
"get.porter.sh/magefiles/porter"
"github.com/carolynvs/magex/shx"
)

const (
mixinName = "terraform"
mixinPackage = "get.porter.sh/mixin/terraform"
mixinBin = "bin/mixins/" + mixinName
mixinName = "terraform"
mixinPackage = "get.porter.sh/mixin/terraform"
mixinBin = "bin/mixins/" + mixinName
DefaultPorterVersion = "v1.0.16"
)

var (
Expand Down Expand Up @@ -68,7 +71,11 @@ func Clean() {
// Install porter locally
func EnsureLocalPorter() {
porter.UseBinForPorterHome()
porter.EnsurePorter()
version := DefaultPorterVersion
if os.Getenv("PORTER_VERSION") != "" {
version = os.Getenv("PORTER_VERSION")
}
porter.EnsurePorterAt(version)
}

func TestIntegration() {
Expand Down
4 changes: 4 additions & 0 deletions pkg/terraform/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ func (s Step) GetCommand() string {
}

func (s Step) GetWorkingDir() string {
if s.WorkingDir != "" {
return s.WorkingDir
}
return "."
}

Expand Down Expand Up @@ -143,6 +146,7 @@ type TerraformFields struct {
DisableVarFile bool `yaml:"disableVarFile,omitempty"`
LogLevel string `yaml:"logLevel,omitempty"`
BackendConfig map[string]interface{} `yaml:"backendConfig,omitempty"`
WorkingDir string `yaml:"workingDir,omitempty"`
}

type Output struct {
Expand Down
Loading