Merge pull request #1 from 3bbbeau/rm/workflow_logic

rm: workflows
This commit is contained in:
Beau 2024-03-20 13:51:03 +00:00 committed by GitHub
commit bd4221af3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 141 additions and 432 deletions

2
.env
View File

@ -1 +1 @@
VERSION=v0.0.1 VERSION=v0.0.2

View File

@ -8,14 +8,14 @@
## Quick start ## Quick start
### CLI ### CLI
tfvars-atlantis-config generate --automerge --autoplan --parallel --output=atlantis.yaml tfvars-atlantis-config generate --use-workspaces --automerge --autoplan --parallel --output=atlantis.yaml
### Atlantis Server Side Config ### Atlantis Server Side Config
```yaml ```yaml
repos: repos:
- id: /.*/ - id: /.*/
pre_workflow_hooks: pre_workflow_hooks:
- run: tfvars-atlantis-config generate --automerge --autoplan --parallel --output=atlantis.yaml - run: tfvars-atlantis-config generate --use-workspaces --automerge --autoplan --parallel --output=atlantis.yaml
``` ```
@ -51,7 +51,7 @@ parallel_apply: true
projects: projects:
- name: my-terraform-dev - name: my-terraform-dev
dir: my-terraform dir: my-terraform
workflow: my-terraform-dev workspace: dev
autoplan: autoplan:
when_modified: when_modified:
- '*.tf' - '*.tf'
@ -59,33 +59,12 @@ projects:
enabled: true enabled: true
- name: my-terraform-prod - name: my-terraform-prod
dir: my-terraform dir: my-terraform
workflow: my-terraform-prod workspace: prod
autoplan: autoplan:
when_modified: when_modified:
- '*.tf' - '*.tf'
- prod.tfvars - prod.tfvars
enabled: true enabled: true
workflows:
my-terraform-dev:
plan:
steps:
- init
- plan:
extra_args:
- -var-file=dev.tfvars
apply:
steps:
- apply
my-terraform-prod:
plan:
steps:
- init
- plan:
extra_args:
- -var-file=prod.tfvars
apply:
steps:
- apply
``` ```
## Why you should use it? ## Why you should use it?
@ -107,24 +86,49 @@ runtime.
| `--autoplan` | Enable auto plan. | false | | `--autoplan` | Enable auto plan. | false |
| `--default-terraform-version` | Default terraform version to run for Atlantis. Default is determined by the Terraform version constraints. | "" | | `--default-terraform-version` | Default terraform version to run for Atlantis. Default is determined by the Terraform version constraints. | "" |
| `--debug` | Enable debug logging. | false | | `--debug` | Enable debug logging. | false |
| `--multienv` | Enable injection of environment specific environment variables to each workflow. | false |
| `--output` | Path of the file where configuration will be generated, usually `atlantis.yaml`. Default is to write to `stdout` | `stdout` | | `--output` | Path of the file where configuration will be generated, usually `atlantis.yaml`. Default is to write to `stdout` | `stdout` |
| `--parallel` | Enables plans and applys to happen in parallel. | false | | `--parallel` | Enables plans and applys to happen in parallel. | false |
| `--root` | Path to the root directory of the git repo you want to build config for. Default is current dir. | `.` | | `--root` | Path to the root directory of the git repo you want to build config for. Default is current dir. | `.` |
| `--use-workspaces` | Whether to use Terraform workspaces for projects. | false | | `--use-workspaces` | Whether to use Terraform workspaces for projects. | false |
## Multienv ## Workflows
When `--multienv` is enabled, prefixed environment variables will be This utility does not generate workflows. You can use use the `$WORKSPACE`
environment variable as part of a generic plan step to use the generated
configuration.
See the [multienv](#multienv--provider-configuration) for a working example.
## Multienv / Provider configuration
You can run the `multienv` command in a workflow. Prefixed environment variables will be
stripped of their prefix and injected into each workflow for the duration stripped of their prefix and injected into each workflow for the duration
the workflow is run during plan/apply stages. the workflow is run during plan/apply stages.
### Example This is useful when you want to configure providers via environment variables
on a per-workspace basis.
```
workflows:
default:
plan:
steps:
- init
- multienv: tfvars-atlantis-config multienv
- plan:
extra_args: ["-var-file", "$WORKSPACE.tfvars"]
apply:
steps:
- multienv: tfvars-atlantis-config multienv
- apply
```
### Example
Workspace `dev`:
_dev.tfvars_: _dev.tfvars_:
- `DEV_FOO_VAR="BAR"` -> `FOO_VAR="BAR"` - `DEV_FOO_VAR="BAR"` -> `FOO_VAR="BAR"`
- `DEV_AWS_ACCESS_KEY="..."` -> `AWS_ACCESS_KEY="..."` - `DEV_AWS_ACCESS_KEY="..."` -> `AWS_ACCESS_KEY="..."`
Workspace `stg`:
_stg.tfvars_: _stg.tfvars_:
- `STG_FOO_VAR="BAR"` -> `FOO_VAR="BAR"` - `STG_FOO_VAR="BAR"` -> `FOO_VAR="BAR"`

View File

@ -38,7 +38,6 @@ func NewFlags() (*Flags, error) {
AutoPlan: false, AutoPlan: false,
DefaultTerraformVersion: "", DefaultTerraformVersion: "",
Root: pwd, Root: pwd,
MultiEnv: false,
Output: "", Output: "",
Parallel: false, Parallel: false,
UseWorkspaces: false, UseWorkspaces: false,
@ -52,7 +51,6 @@ func (flags *Flags) AddFlags(cmd *cobra.Command) {
cmd.Flags().BoolVar(&flags.Parallel, "parallel", flags.Parallel, "Enables plans and applys to happen in parallel. Default is disabled") cmd.Flags().BoolVar(&flags.Parallel, "parallel", flags.Parallel, "Enables plans and applys to happen in parallel. Default is disabled")
cmd.Flags().StringVar(&flags.Output, "output", flags.Output, "Path of the file where configuration will be generated. Default is stdout") cmd.Flags().StringVar(&flags.Output, "output", flags.Output, "Path of the file where configuration will be generated. Default is stdout")
cmd.Flags().StringVar(&flags.Root, "root", flags.Root, "Path to the root directory of the git repo you want to build config for. Default is current dir") cmd.Flags().StringVar(&flags.Root, "root", flags.Root, "Path to the root directory of the git repo you want to build config for. Default is current dir")
cmd.Flags().BoolVar(&flags.MultiEnv, "multienv", flags.MultiEnv, "Enable injection of environment specific environment variables to each workflow. Default is disabled")
cmd.Flags().StringVar(&flags.DefaultTerraformVersion, "terraform-version", flags.DefaultTerraformVersion, "Default terraform version to run for Atlantis. Default is determined by the Terraform version constraints.") cmd.Flags().StringVar(&flags.DefaultTerraformVersion, "terraform-version", flags.DefaultTerraformVersion, "Default terraform version to run for Atlantis. Default is determined by the Terraform version constraints.")
cmd.Flags().BoolVar(&flags.UseWorkspaces, "use-workspaces", flags.UseWorkspaces, "Use workspaces for projects. Default is disabled") cmd.Flags().BoolVar(&flags.UseWorkspaces, "use-workspaces", flags.UseWorkspaces, "Use workspaces for projects. Default is disabled")
@ -67,7 +65,6 @@ func (flags *Flags) toOptions() repocfg.Options {
Automerge: flags.AutoMerge, Automerge: flags.AutoMerge,
Autoplan: flags.AutoPlan, Autoplan: flags.AutoPlan,
DefaultTerraformVersion: flags.DefaultTerraformVersion, DefaultTerraformVersion: flags.DefaultTerraformVersion,
MultiEnv: flags.MultiEnv,
Parallel: flags.Parallel, Parallel: flags.Parallel,
UseWorkspaces: flags.UseWorkspaces, UseWorkspaces: flags.UseWorkspaces,
} }

98
cmd/multienv.go Normal file
View File

@ -0,0 +1,98 @@
package cmd
import (
"errors"
"fmt"
"os"
"strings"
"github.com/3bbbeau/tfvars-atlantis-config/logger"
"github.com/spf13/cobra"
)
const (
// ATLANTIS_WORKSPACE is the environment variable that contains the name of
// the workspace that Atlantis is currently running for.
ATLANTIS_WORKSPACE = "WORKSPACE"
)
// NewMultiEnvCmd creates a new `multienv` command
func NewMultiEnvCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "multienv",
Short: "Returns a string representing the multienv for Atlantis",
Long: `Returns a string representing the multienv for Atlantis, e.g.:
EnvVar1Name=value1,EnvVar2Name=value2,EnvVar3Name=value3
Reference: https://www.runatlantis.io/docs/custom-workflows.html#multiple-environment-variables-multienv-command
This is useful when you want to configure providers via environment variables
on a per-workspace/environment basis.`,
RunE: func(cmd *cobra.Command, args []string) error {
workspace := os.Getenv(ATLANTIS_WORKSPACE)
if len(workspace) == 0 {
return fmt.Errorf("environment variable %s is not set. no workspace for multienv", ATLANTIS_WORKSPACE)
}
multienv, err := multienv(os.Getenv(ATLANTIS_WORKSPACE))
if err != nil {
if errors.Is(err, ErrNoEnvVars) {
logger.FromContext(cmd.Context()).Debug("no matching prefixed environment variables found")
// Return nil to indicate success, but no multienv string
return nil
}
return err
}
// Atlanis expects the multienv string to be written to stdout
fmt.Print(multienv)
return nil
},
}
return cmd
}
var ErrNoEnvVars error = fmt.Errorf("no matching prefixed environment variables found")
// Generates the Atlantis multienv string for multi-environment
// Terraform projects, e.g:
//
// EnvVar1Name=value1,EnvVar2Name=value2,EnvVar3Name=value3
//
// Given a prefix for the workspace name, strips the
// environment name from existing environment vars while keeping their value.
//
// This is useful when you want to configure providers via environment variables
// on a per-environment basis.
//
// Example:
//
// DEV_AWS_ACCESS_KEY_ID="foo"
// DEV_AWS_SECRET_ACCESS_KEY="bar"
// ->
// AWS_ACCESS_KEY_ID=$DEV_AWS_ACCESS_KEY_ID
// AWS_SECRET_ACCESS_KEY=$DEV_AWS_SECRET_ACCESS_KEY
func multienv(prefix string) (string, error) {
prefix = strings.ToUpper(prefix)
if !strings.HasSuffix(prefix, "_") {
prefix += "_"
}
strippedEnviron := []string{}
for _, v := range os.Environ() {
if strings.HasPrefix(v, prefix) {
// Limits the split to only two parts, separating the key from the first
// occurrence of '=', otherwise if the value contains '=' character(s) the
// string would be split into more than two parts.
split := strings.SplitN(v, "=", 2)
// Strips the prefix from the environment variable name, e.g. "DEV_" from
// "DEV_AWS_ACCESS_KEY_ID" and let it equal to the original environment variable
strippedEnviron = append(strippedEnviron, fmt.Sprintf("%s=%s", strings.TrimPrefix(split[0], prefix), split[1]))
}
}
if len(strippedEnviron) == 0 {
return "", ErrNoEnvVars
}
return strings.Join(strippedEnviron, ","), nil
}

View File

@ -35,6 +35,7 @@ func New() (*cobra.Command, error) {
return nil, fmt.Errorf("creating generate command: %w", err) return nil, fmt.Errorf("creating generate command: %w", err)
} }
cmd.AddCommand(gCmd) cmd.AddCommand(gCmd)
cmd.AddCommand(NewMultiEnvCmd())
return cmd, nil return cmd, nil
} }

View File

@ -41,7 +41,6 @@ func ProjectsFrom(c Component, opts Options) ([]ExtRawProject, error) {
// The directory of this project relative to the repo root. // The directory of this project relative to the repo root.
Dir: ptr(c.Path), Dir: ptr(c.Path),
Workflow: ptr(friendlyName(c.Path, v)),
}, },
} }

View File

@ -32,7 +32,6 @@ func Test_ProjectsFrom(t *testing.T) {
Project: raw.Project{ Project: raw.Project{
Name: ptr("test-env"), Name: ptr("test-env"),
Dir: ptr("test"), Dir: ptr("test"),
Workflow: ptr("test-env"),
Workspace: ptr("env"), Workspace: ptr("env"),
TerraformVersion: ptr("8.8.8"), TerraformVersion: ptr("8.8.8"),
Autoplan: &raw.Autoplan{ Autoplan: &raw.Autoplan{

View File

@ -15,7 +15,6 @@ type Options struct {
Autoplan bool Autoplan bool
DefaultTerraformVersion string DefaultTerraformVersion string
Parallel bool Parallel bool
MultiEnv bool
UseWorkspaces bool UseWorkspaces bool
} }
@ -58,19 +57,6 @@ func NewRepoCfg(components []Component, opts Options) (*ExtRawRepoCfg, error) {
repoCfg.Projects = append(repoCfg.Projects, projects...) repoCfg.Projects = append(repoCfg.Projects, projects...)
workflows := map[string]raw.Workflow{}
for _, c := range components {
generated, err := WorkflowsFrom(c, opts)
if err != nil {
return nil, fmt.Errorf("failed while creating workflows with component %+v: %w", c, err)
}
for _, wf := range generated {
workflows[wf.Name] = wf.Workflow
}
}
repoCfg.Workflows = workflows
return repoCfg, nil return repoCfg, nil
} }
@ -80,20 +66,9 @@ func (rc *ExtRawRepoCfg) MarshalYAML() (interface{}, error) {
{Key: "automerge", Value: rc.Automerge}, {Key: "automerge", Value: rc.Automerge},
{Key: "parallel_plan", Value: rc.ParallelPlan}, {Key: "parallel_plan", Value: rc.ParallelPlan},
{Key: "parallel_apply", Value: rc.ParallelApply}, {Key: "parallel_apply", Value: rc.ParallelApply},
{Key: "projects", Value: rc.Projects}, {Key: "projects", Value: rc.Projects},
} }
workflows := yaml.MapSlice{}
for name, wf := range rc.Workflows {
workflows = append(workflows, yaml.MapItem{
Key: name,
Value: yaml.MapSlice{
{Key: "plan", Value: wf.Plan},
{Key: "apply", Value: wf.Apply},
},
})
}
m = append(m, yaml.MapItem{Key: "workflows", Value: workflows})
return m, nil return m, nil
} }

View File

@ -29,60 +29,14 @@ func Test_NewFrom(t *testing.T) {
Automerge: ptr(false), Automerge: ptr(false),
ParallelPlan: ptr(false), ParallelPlan: ptr(false),
ParallelApply: ptr(false), ParallelApply: ptr(false),
Workflows: map[string]raw.Workflow{
"test-dev": {
Plan: &raw.Stage{
Steps: []raw.Step{
{
Key: ptr("init"),
},
{
Map: map[string]map[string][]string{
"plan": {"extra_args": []string{"-var-file=vars/dev.tfvars"}},
},
},
},
},
Apply: &raw.Stage{
Steps: []raw.Step{
{
Key: ptr("apply"),
},
},
},
},
"test-stg": {
Plan: &raw.Stage{
Steps: []raw.Step{
{
Key: ptr("init"),
},
{
Map: map[string]map[string][]string{
"plan": {"extra_args": []string{"-var-file=vars/nested/stg.tfvars"}},
},
},
},
},
Apply: &raw.Stage{
Steps: []raw.Step{
{
Key: ptr("apply"),
},
},
},
},
},
Projects: []raw.Project{ Projects: []raw.Project{
{ {
Name: ptr("test-dev"), Name: ptr("test-dev"),
Dir: ptr("test"), Dir: ptr("test"),
Workflow: ptr("test-dev"),
}, },
{ {
Name: ptr("test-stg"), Name: ptr("test-stg"),
Dir: ptr("test"), Dir: ptr("test"),
Workflow: ptr("test-stg"),
}, },
}, },
}, },

View File

@ -1,8 +1,6 @@
package repocfg package repocfg
import ( import (
"fmt"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
) )
@ -10,7 +8,7 @@ import (
// Ptr returns a pointer to type T // Ptr returns a pointer to type T
func ptr[T any](v T) *T { return &v } func ptr[T any](v T) *T { return &v }
// friendlyName creates a contextual name used for Atlantis projects and workflows // friendlyName creates a contextual name used for Atlantis projects
func friendlyName(path, environment string) string { func friendlyName(path, environment string) string {
name := []string{strings.ReplaceAll(path, "/", "-"), pathWithoutExtension(filepath.Base(environment))} name := []string{strings.ReplaceAll(path, "/", "-"), pathWithoutExtension(filepath.Base(environment))}
return strings.TrimSuffix(strings.Join(name, "-"), "-") return strings.TrimSuffix(strings.Join(name, "-"), "-")
@ -21,48 +19,3 @@ func friendlyName(path, environment string) string {
func pathWithoutExtension(path string) string { func pathWithoutExtension(path string) string {
return strings.TrimSuffix(strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)), filepath.Base(path)) return strings.TrimSuffix(strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)), filepath.Base(path))
} }
var ErrNoEnvVars error = fmt.Errorf("no matching prefixed environment variables found")
// Generates the Atlantis multienv string within stages for multi-environment
// Terraform projects, e.g:
//
// EnvVar1Name=value1,EnvVar2Name=value2,EnvVar3Name=value3
//
// Given a prefix for the environment name of the workflow, strips the
// environment name from existing environment vars while keeping their value.
//
// This is useful when you want to configure providers via environment variables
// on a per-environment basis.
//
// Example:
//
// DEV_AWS_ACCESS_KEY_ID="foo"
// DEV_AWS_SECRET_ACCESS_KEY="bar"
// ->
// AWS_ACCESS_KEY_ID=$DEV_AWS_ACCESS_KEY_ID
// AWS_SECRET_ACCESS_KEY=$DEV_AWS_SECRET_ACCESS_KEY
func prefixedEnviron(prefix string) (*string, error) {
prefix = strings.ToUpper(prefix)
if !strings.HasSuffix(prefix, "_") {
prefix += "_"
}
strippedEnviron := []string{}
for _, v := range os.Environ() {
if strings.HasPrefix(v, prefix) {
// Limits the split to only two parts, separating the key from the first
// occurrence of '=', otherwise if the value contains '=' character(s) the
// string would be split into more than two parts.
name := strings.SplitN(v, "=", 2)[0]
// Strips the prefix from the environment variable name, e.g. "DEV_" from
// "DEV_AWS_ACCESS_KEY_ID" and let it equal to the original environment variable
strippedEnviron = append(strippedEnviron, fmt.Sprintf("%s=$%s", strings.TrimPrefix(name, prefix), name))
}
}
if len(strippedEnviron) == 0 {
return nil, ErrNoEnvVars
}
return ptr(fmt.Sprintf("echo %s", strings.Join(strippedEnviron, ","))), nil
}

View File

@ -27,7 +27,7 @@ func Test_Ptr(t *testing.T) {
} }
// Tests the friendlyName helper function. Given a path and an environment it // Tests the friendlyName helper function. Given a path and an environment it
// should provide a contextual name to be used for Atlantis projects and workflows. // should provide a contextual name to be used for Atlantis projects.
func Test_FriendlyName(t *testing.T) { func Test_FriendlyName(t *testing.T) {
t.Parallel() t.Parallel()

View File

@ -1,175 +0,0 @@
package repocfg
import (
"errors"
"fmt"
"path/filepath"
"github.com/runatlantis/atlantis/server/core/config/raw"
)
// ExtRawWorkflow extends the raw.Workflow type to add additional methods
// and fields
type ExtRawWorkflow struct {
Name string
Args *ExtraArgs
Workspace string
raw.Workflow
}
// ExtraArgs is a type aliased map of extra arguments to be passed within a workflow
type ExtraArgs map[string][]string
// hasArgs returns true if the component has a target (i.e. a Terraform variable file)
func hasArgs(c Component) bool {
return len(c.VarFiles) > 0
}
// Add generates the extra_args for the stage and adds them to the stage's Args
func (args *ExtraArgs) Add(v []string) {
(*args)["extra_args"] = v
}
// relativeVarFile returns the path of the variable file v relative to
// the component's path.
func relativeVarFile(c, v string) (*string, error) {
absComponent, err := filepath.Abs(c)
if err != nil {
return nil, fmt.Errorf("absolute path for component %s: %s", c, err)
}
absVarFile, err := filepath.Abs(v)
if err != nil {
return nil, fmt.Errorf("absolute path for %s: %s", v, err)
}
rel, err := filepath.Rel(absComponent, absVarFile)
if err != nil {
return nil, fmt.Errorf("relative path for %s: %s", v, err)
}
return &rel, nil
}
// WorkflowsFrom creates new Atlantis workflows from a Terraform component
// and calls the stage methods to generate all of its fields.
func WorkflowsFrom(c Component, opts Options) ([]ExtRawWorkflow, error) {
var workflows []ExtRawWorkflow
for _, v := range c.VarFiles {
wf := ExtRawWorkflow{
Name: friendlyName(c.Path, v),
Workflow: raw.Workflow{
Plan: &raw.Stage{},
Apply: &raw.Stage{},
},
Args: &ExtraArgs{},
Workspace: pathWithoutExtension(v),
}
if hasArgs(c) {
rel, err := relativeVarFile(c.Path, v)
if err != nil {
return nil, fmt.Errorf("relative variable file for %s: %s", v, err)
}
wf.Args.Add([]string{"-var-file=" + *rel})
}
if opts.MultiEnv {
err := wf.AddMultiEnv(v)
if err != nil {
return nil, fmt.Errorf("adding multienv for %s: %s", v, err)
}
}
if opts.UseWorkspaces {
wf.AddWorkspace()
}
wf.PlanStage(opts)
wf.ApplyStage(opts)
err := wf.Validate()
if err != nil {
return nil, fmt.Errorf("workflow validation failed for %s: %s", wf.Name, err)
}
workflows = append(workflows, wf)
}
return workflows, nil
}
// PlanStage generates the plan stage for the workflow
func (wf *ExtRawWorkflow) PlanStage(opts Options) {
if wf.Plan == nil {
wf.Plan = new(raw.Stage)
}
init := raw.Step{
Key: ptr("init"),
}
wf.Plan.Steps = append(wf.Plan.Steps, init)
if len(*wf.Args) > 0 {
wf.Plan.Steps = append(wf.Plan.Steps, raw.Step{
Map: map[string]map[string][]string{
"plan": *wf.Args,
},
})
} else {
wf.Plan.Steps = append(wf.Plan.Steps, raw.Step{
Key: ptr("plan"),
})
}
}
// ApplyStage generates the apply stage for the workflow
//
// Unlike the plan stage, the apply stage doesn't need extra args for -var-file,
// as it uses the planfile generated by the plan stage.
func (wf *ExtRawWorkflow) ApplyStage(opts Options) {
if wf.Apply == nil {
wf.Apply = new(raw.Stage)
}
wf.Apply.Steps = append(wf.Apply.Steps, raw.Step{
Key: ptr("apply"),
})
}
// AddWorkspace sets TF_WORKSPACE in the plan and apply stages
// to the value of the workspace from the project configuration.
//
// This is needed because Atlantis will use the default workspace
// which doesn't exist.
func (wf *ExtRawWorkflow) AddWorkspace() {
for _, stg := range []*raw.Stage{wf.Plan, wf.Apply} {
stg.Steps = append(stg.Steps, raw.Step{
EnvOrRun: map[string]map[string]string{
"env": {
"name": "TF_WORKSPACE",
"value": wf.Workspace,
},
},
})
}
}
// AddMultiEnv adds the multienv step to the plan and apply stages
func (wf *ExtRawWorkflow) AddMultiEnv(env string) error {
environ, err := prefixedEnviron(pathWithoutExtension(env))
if errors.Is(err, ErrNoEnvVars) {
return nil
}
if err != nil {
return fmt.Errorf("adding multienv for environment %s: %w", env, err)
}
for _, stg := range []*raw.Stage{wf.Plan, wf.Apply} {
stg.Steps = append(stg.Steps, raw.Step{
StringVal: map[string]string{
"multienv": *environ,
},
})
}
return nil
}

View File

@ -1,96 +0,0 @@
package repocfg
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/runatlantis/atlantis/server/core/config/raw"
)
// Tests creating a new workflow from a Terraform component
func Test_WorkflowsFrom(t *testing.T) {
t.Parallel()
tests := []struct {
name string
component Component
want []ExtRawWorkflow
}{
{
name: "new-workflow",
component: Component{
Path: "test",
VarFiles: []string{"test/vars/dev.tfvars", "test/vars/stg.tfvars"},
},
want: []ExtRawWorkflow{
{
Name: "test-dev",
Args: &ExtraArgs{
"extra_args": []string{"-var-file=vars/dev.tfvars"},
},
Workspace: "dev",
Workflow: raw.Workflow{
Plan: &raw.Stage{
Steps: []raw.Step{
{
Key: ptr("init"),
},
{
Map: map[string]map[string][]string{
"plan": {"extra_args": []string{"-var-file=vars/dev.tfvars"}},
},
},
},
},
Apply: &raw.Stage{
Steps: []raw.Step{
{
Key: ptr("apply"),
},
},
},
},
},
{
Name: "test-stg",
Args: &ExtraArgs{
"extra_args": []string{"-var-file=vars/stg.tfvars"},
},
Workspace: "stg",
Workflow: raw.Workflow{
Plan: &raw.Stage{
Steps: []raw.Step{
{
Key: ptr("init"),
},
{
Map: map[string]map[string][]string{
"plan": {"extra_args": []string{"-var-file=vars/stg.tfvars"}},
},
},
},
},
Apply: &raw.Stage{
Steps: []raw.Step{
{
Key: ptr("apply"),
},
},
},
},
},
},
},
}
for _, tc := range tests {
got, err := WorkflowsFrom(tc.component, Options{})
if err != nil {
t.Errorf("WorkflowsFrom(): %s", err)
}
if !cmp.Equal(got, tc.want) {
t.Errorf(`WorkflowFrom()
diff %s`, cmp.Diff(got, tc.want))
}
}
}