mirror of
https://github.com/3bbbeau/tfvars-atlantis-config.git
synced 2024-11-22 22:10:52 +00:00
176 lines
4.2 KiB
Go
176 lines
4.2 KiB
Go
|
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
|
||
|
}
|