Browse Source

Added tf_data_dir parameter and changed plugin to respect TF_DATA_DIR

This commit will enable the plugin to do parallel builds.
The first change is to use Terraform's TF_DATA_DIR environment variable to change the state directory so they will not conflict each other in parallel executions
The second change is to change the plan's output to a different file so they will not be overriden by another parallel execution
pull/94/head
Caio Quirino 6 years ago
parent
commit
5ba916070a
No known key found for this signature in database GPG Key ID: 94564A4043FF99AC
  1. 30
      main.go
  2. 114
      plugin.go
  3. 55
      plugin_test.go

30
main.go

@ -108,6 +108,11 @@ func main() {
Usage: "a list of var files to use. Each value is passed as -var-file=<value>", Usage: "a list of var files to use. Each value is passed as -var-file=<value>",
EnvVar: "PLUGIN_VAR_FILES", EnvVar: "PLUGIN_VAR_FILES",
}, },
cli.StringSliceFlag{
Name: "tf_data_dir",
Usage: "changes the location where Terraform keeps its per-working-directory data, such as the current remote backend configuration.",
EnvVar: "PLUGIN_TF_DATA_DIR",
},
} }
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {
@ -144,18 +149,19 @@ func run(c *cli.Context) error {
plugin := Plugin{ plugin := Plugin{
Config: Config{ Config: Config{
Actions: c.StringSlice("actions"), Actions: c.StringSlice("actions"),
Vars: vars, Vars: vars,
Secrets: secrets, Secrets: secrets,
InitOptions: initOptions, InitOptions: initOptions,
FmtOptions: fmtOptions, FmtOptions: fmtOptions,
Cacert: c.String("ca_cert"), Cacert: c.String("ca_cert"),
Sensitive: c.Bool("sensitive"), Sensitive: c.Bool("sensitive"),
RoleARN: c.String("role_arn_to_assume"), RoleARN: c.String("role_arn_to_assume"),
RootDir: c.String("root_dir"), RootDir: c.String("root_dir"),
Parallelism: c.Int("parallelism"), Parallelism: c.Int("parallelism"),
Targets: c.StringSlice("targets"), Targets: c.StringSlice("targets"),
VarFiles: c.StringSlice("var_files"), VarFiles: c.StringSlice("var_files"),
TerraformDataDir: c.String("tf_data_dir"),
}, },
Netrc: Netrc{ Netrc: Netrc{
Login: c.String("netrc.username"), Login: c.String("netrc.username"),

114
plugin.go

@ -21,18 +21,19 @@ import (
type ( type (
// Config holds input parameters for the plugin // Config holds input parameters for the plugin
Config struct { Config struct {
Actions []string Actions []string
Vars map[string]string Vars map[string]string
Secrets map[string]string Secrets map[string]string
InitOptions InitOptions InitOptions InitOptions
FmtOptions FmtOptions FmtOptions FmtOptions
Cacert string Cacert string
Sensitive bool Sensitive bool
RoleARN string RoleARN string
RootDir string RootDir string
Parallelism int Parallelism int
Targets []string Targets []string
VarFiles []string VarFiles []string
TerraformDataDir string
} }
// Netrc is credentials for cloning // Netrc is credentials for cloning
@ -96,8 +97,8 @@ func (p Plugin) Exec() error {
commands = append(commands, installCaCert(p.Config.Cacert)) commands = append(commands, installCaCert(p.Config.Cacert))
} }
commands = append(commands, deleteCache()) commands = append(commands, deleteCache(p.Config))
commands = append(commands, initCommand(p.Config.InitOptions)) commands = append(commands, initCommand(p.Config))
commands = append(commands, getModules()) commands = append(commands, getModules())
// Add commands listed from Actions // Add commands listed from Actions
@ -120,7 +121,7 @@ func (p Plugin) Exec() error {
} }
} }
commands = append(commands, deleteCache()) commands = append(commands, deleteCache(p.Config))
for _, c := range commands { for _, c := range commands {
if c.Dir == "" { if c.Dir == "" {
@ -183,11 +184,12 @@ func assumeRole(roleArn string) {
os.Setenv("AWS_SESSION_TOKEN", value.SessionToken) os.Setenv("AWS_SESSION_TOKEN", value.SessionToken)
} }
func deleteCache() *exec.Cmd { func deleteCache(config Config) *exec.Cmd {
terraformDataDir := getTerraformDataDir(config)
return exec.Command( return exec.Command(
"rm", "rm",
"-rf", "-rf",
".terraform", terraformDataDir,
) )
} }
@ -198,30 +200,30 @@ func getModules() *exec.Cmd {
) )
} }
func initCommand(config InitOptions) *exec.Cmd { func initCommand(config Config) *exec.Cmd {
args := []string{ args := []string{
"init", "init",
} }
for _, v := range config.BackendConfig { for _, v := range config.InitOptions.BackendConfig {
args = append(args, fmt.Sprintf("-backend-config=%s", v)) args = append(args, fmt.Sprintf("-backend-config=%s", v))
} }
// True is default in TF // True is default in TF
if config.Lock != nil { if config.InitOptions.Lock != nil {
args = append(args, fmt.Sprintf("-lock=%t", *config.Lock)) args = append(args, fmt.Sprintf("-lock=%t", *config.InitOptions.Lock))
} }
// "0s" is default in TF // "0s" is default in TF
if config.LockTimeout != "" { if config.InitOptions.LockTimeout != "" {
args = append(args, fmt.Sprintf("-lock-timeout=%s", config.LockTimeout)) args = append(args, fmt.Sprintf("-lock-timeout=%s", config.InitOptions.LockTimeout))
} }
// Fail Terraform execution on prompt // Fail Terraform execution on prompt
args = append(args, "-input=false") args = append(args, "-input=false")
return exec.Command( return createTerraformCommand(
"terraform", config,
args..., args...,
) )
} }
@ -253,9 +255,10 @@ func tfApply(config Config) *exec.Cmd {
if config.InitOptions.LockTimeout != "" { if config.InitOptions.LockTimeout != "" {
args = append(args, fmt.Sprintf("-lock-timeout=%s", config.InitOptions.LockTimeout)) args = append(args, fmt.Sprintf("-lock-timeout=%s", config.InitOptions.LockTimeout))
} }
args = append(args, "plan.tfout") args = append(args, getTfoutPath(config))
return exec.Command(
"terraform", return createTerraformCommand(
config,
args..., args...,
) )
} }
@ -279,8 +282,8 @@ func tfDestroy(config Config) *exec.Cmd {
args = append(args, fmt.Sprintf("-lock-timeout=%s", config.InitOptions.LockTimeout)) args = append(args, fmt.Sprintf("-lock-timeout=%s", config.InitOptions.LockTimeout))
} }
args = append(args, "-force") args = append(args, "-force")
return exec.Command( return createTerraformCommand(
"terraform", config,
args..., args...,
) )
} }
@ -293,7 +296,7 @@ func tfPlan(config Config, destroy bool) *exec.Cmd {
if destroy { if destroy {
args = append(args, "-destroy") args = append(args, "-destroy")
} else { } else {
args = append(args, "-out=plan.tfout") args = append(args, fmt.Sprintf("-out=%s", getTfoutPath(config)))
} }
for _, v := range config.Targets { for _, v := range config.Targets {
@ -310,8 +313,8 @@ func tfPlan(config Config, destroy bool) *exec.Cmd {
if config.InitOptions.LockTimeout != "" { if config.InitOptions.LockTimeout != "" {
args = append(args, fmt.Sprintf("-lock-timeout=%s", config.InitOptions.LockTimeout)) args = append(args, fmt.Sprintf("-lock-timeout=%s", config.InitOptions.LockTimeout))
} }
return exec.Command( return createTerraformCommand(
"terraform", config,
args..., args...,
) )
} }
@ -326,8 +329,8 @@ func tfValidate(config Config) *exec.Cmd {
for k, v := range config.Vars { for k, v := range config.Vars {
args = append(args, "-var", fmt.Sprintf("%s=%s", k, v)) args = append(args, "-var", fmt.Sprintf("%s=%s", k, v))
} }
return exec.Command( return createTerraformCommand(
"terraform", config,
args..., args...,
) )
} }
@ -348,12 +351,49 @@ func tfFmt(config Config) *exec.Cmd {
if config.FmtOptions.Check != nil { if config.FmtOptions.Check != nil {
args = append(args, fmt.Sprintf("-check=%t", *config.FmtOptions.Check)) args = append(args, fmt.Sprintf("-check=%t", *config.FmtOptions.Check))
} }
return exec.Command( return createTerraformCommand(
"terraform", config,
args..., args...,
) )
} }
func getTerraformDataDir(config Config) string {
// Override terraform data dir
var terraformDataDir string
if config.TerraformDataDir != "" {
terraformDataDir = config.TerraformDataDir
} else if os.Getenv("TF_DATA_DIR") != "" {
terraformDataDir = os.Getenv("TF_DATA_DIR")
} else {
terraformDataDir = ".terraform"
}
return terraformDataDir
}
func createEnvironmentVariables(config Config) []string {
var environmentVariables []string = []string{}
terraformDataDir := getTerraformDataDir(config)
if terraformDataDir != ".terraform" {
environmentVariables = append(environmentVariables, fmt.Sprintf("TF_DATA_DIR=%s", terraformDataDir))
}
return environmentVariables
}
func createTerraformCommand(config Config, args ...string) *exec.Cmd {
command := exec.Command("terraform", args...)
command.Env = append(command.Env, createEnvironmentVariables(config)...)
return command
}
func getTfoutPath(config Config) string {
terraformDataDir := getTerraformDataDir(config)
if terraformDataDir == ".terraform" {
return "plan.tfout"
} else {
return fmt.Sprintf("%s.plan.tfout", terraformDataDir)
}
}
func vars(vs map[string]string) []string { func vars(vs map[string]string) []string {
var args []string var args []string
for k, v := range vs { for k, v := range vs {

55
plugin_test.go

@ -204,4 +204,59 @@ func TestPlugin(t *testing.T) {
} }
}) })
}) })
g.Describe("tfDataDir", func() {
g.It("Should override the terraform data dir environment variable when provided", func() {
type args struct {
config Config
}
tests := []struct {
name string
args args
want *exec.Cmd
expectedEnvVars []string
}{
{
"with TerraformDataDir",
args{config: Config{TerraformDataDir: ".overriden_terraform_dir"}},
exec.Command("terraform", "apply", ".overriden_terraform_dir.plan.tfout"),
[]string{"TF_DATA_DIR=.overriden_terraform_dir"},
},
{
"with TerraformDataDir value as .terraform",
args{config: Config{TerraformDataDir: ".terraform"}},
exec.Command("terraform", "apply", "plan.tfout"),
[]string{},
},
{
"without TerraformDataDir",
args{config: Config{}},
exec.Command("terraform", "apply", "plan.tfout"),
[]string{},
},
}
for _, tt := range tests {
applied := tfApply(tt.args.config)
appliedEnv := applied.Env
applied.Env = nil
g.Assert(applied).Equal(tt.want)
var found int = 0
for _, expectedEnvVar := range tt.expectedEnvVars {
for _, env := range appliedEnv {
if expectedEnvVar == env {
found += 1
break
}
}
}
g.Assert(found).Equal(len(tt.expectedEnvVars))
}
})
})
} }

Loading…
Cancel
Save