diff --git a/tfexec/apply.go b/tfexec/apply.go index d35e4e31..95cd266e 100644 --- a/tfexec/apply.go +++ b/tfexec/apply.go @@ -15,7 +15,7 @@ type applyConfig struct { // LockTimeout must be a string with time unit, e.g. '10s' lockTimeout string parallelism int - reattachInfo string + reattachInfo ReattachInfo refresh bool state string stateOut string @@ -87,10 +87,15 @@ func (opt *ReattachOption) configureApply(conf *applyConfig) { // Apply represents the terraform apply subcommand. func (tf *Terraform) Apply(ctx context.Context, opts ...ApplyOption) error { - return tf.runTerraformCmd(tf.applyCmd(ctx, opts...)) + cmd, err := tf.applyCmd(ctx, opts...) + if err != nil { + return err + } + tf.runTerraformCmd(cmd) + return nil } -func (tf *Terraform) applyCmd(ctx context.Context, opts ...ApplyOption) *exec.Cmd { +func (tf *Terraform) applyCmd(ctx context.Context, opts ...ApplyOption) (*exec.Cmd, error) { c := defaultApplyOptions for _, o := range opts { @@ -139,9 +144,13 @@ func (tf *Terraform) applyCmd(ctx context.Context, opts ...ApplyOption) *exec.Cm } mergeEnv := map[string]string{} - if c.reattachInfo != "" { - mergeEnv[reattachEnvVar] = c.reattachInfo + if c.reattachInfo != nil { + reattachStr, err := marshalReattachString(c.reattachInfo) + if err != nil { + return nil, err + } + mergeEnv[reattachEnvVar] = reattachStr } - return tf.buildTerraformCmd(ctx, mergeEnv, args...) + return tf.buildTerraformCmd(ctx, mergeEnv, args...), nil } diff --git a/tfexec/destroy.go b/tfexec/destroy.go index d2c9ed09..45064fa2 100644 --- a/tfexec/destroy.go +++ b/tfexec/destroy.go @@ -15,7 +15,7 @@ type destroyConfig struct { // LockTimeout must be a string with time unit, e.g. '10s' lockTimeout string parallelism int - reattachInfo string + reattachInfo ReattachInfo refresh bool state string stateOut string @@ -88,10 +88,15 @@ func (opt *ReattachOption) configureDestroy(conf *destroyConfig) { // Destroy represents the terraform destroy subcommand. func (tf *Terraform) Destroy(ctx context.Context, opts ...DestroyOption) error { - return tf.runTerraformCmd(tf.destroyCmd(ctx, opts...)) + cmd, err := tf.destroyCmd(ctx, opts...) + if err != nil { + return err + } + tf.runTerraformCmd(cmd) + return nil } -func (tf *Terraform) destroyCmd(ctx context.Context, opts ...DestroyOption) *exec.Cmd { +func (tf *Terraform) destroyCmd(ctx context.Context, opts ...DestroyOption) (*exec.Cmd, error) { c := defaultDestroyOptions for _, o := range opts { @@ -140,9 +145,13 @@ func (tf *Terraform) destroyCmd(ctx context.Context, opts ...DestroyOption) *exe } mergeEnv := map[string]string{} - if c.reattachInfo != "" { - mergeEnv[reattachEnvVar] = c.reattachInfo + if c.reattachInfo != nil { + reattachStr, err := marshalReattachString(c.reattachInfo) + if err != nil { + return nil, err + } + mergeEnv[reattachEnvVar] = reattachStr } - return tf.buildTerraformCmd(ctx, mergeEnv, args...) + return tf.buildTerraformCmd(ctx, mergeEnv, args...), nil } diff --git a/tfexec/import.go b/tfexec/import.go index 8e657537..fca0a37f 100644 --- a/tfexec/import.go +++ b/tfexec/import.go @@ -14,7 +14,7 @@ type importConfig struct { allowMissingConfig bool lock bool lockTimeout string - reattachInfo string + reattachInfo ReattachInfo state string stateOut string vars []string @@ -74,10 +74,15 @@ func (opt *VarFileOption) configureImport(conf *importConfig) { // Import represents the terraform import subcommand. func (tf *Terraform) Import(ctx context.Context, address, id string, opts ...ImportOption) error { - return tf.runTerraformCmd(tf.importCmd(ctx, address, id, opts...)) + cmd, err := tf.importCmd(ctx, address, id, opts...) + if err != nil { + return err + } + tf.runTerraformCmd(cmd) + return nil } -func (tf *Terraform) importCmd(ctx context.Context, address, id string, opts ...ImportOption) *exec.Cmd { +func (tf *Terraform) importCmd(ctx context.Context, address, id string, opts ...ImportOption) (*exec.Cmd, error) { c := defaultImportOptions for _, o := range opts { @@ -125,10 +130,13 @@ func (tf *Terraform) importCmd(ctx context.Context, address, id string, opts ... args = append(args, address, id) mergeEnv := map[string]string{} - if c.reattachInfo != "" { - mergeEnv[reattachEnvVar] = c.reattachInfo + if c.reattachInfo != nil { + reattachStr, err := marshalReattachString(c.reattachInfo) + if err != nil { + return nil, err + } + mergeEnv[reattachEnvVar] = reattachStr } - return tf.buildTerraformCmd(ctx, mergeEnv, args...) - + return tf.buildTerraformCmd(ctx, mergeEnv, args...), nil } diff --git a/tfexec/init.go b/tfexec/init.go index 10775fb6..a23a6157 100644 --- a/tfexec/init.go +++ b/tfexec/init.go @@ -17,7 +17,7 @@ type initConfig struct { lock bool lockTimeout string pluginDir []string - reattachInfo string + reattachInfo ReattachInfo reconfigure bool upgrade bool verifyPlugins bool @@ -94,10 +94,15 @@ func (opt *VerifyPluginsOption) configureInit(conf *initConfig) { // Init represents the terraform init subcommand. func (tf *Terraform) Init(ctx context.Context, opts ...InitOption) error { - return tf.runTerraformCmd(tf.initCmd(ctx, opts...)) + cmd, err := tf.initCmd(ctx, opts...) + if err != nil { + return err + } + tf.runTerraformCmd(cmd) + return nil } -func (tf *Terraform) initCmd(ctx context.Context, opts ...InitOption) *exec.Cmd { +func (tf *Terraform) initCmd(ctx context.Context, opts ...InitOption) (*exec.Cmd, error) { c := defaultInitOptions for _, o := range opts { @@ -145,9 +150,13 @@ func (tf *Terraform) initCmd(ctx context.Context, opts ...InitOption) *exec.Cmd } mergeEnv := map[string]string{} - if c.reattachInfo != "" { - mergeEnv[reattachEnvVar] = c.reattachInfo + if c.reattachInfo != nil { + reattachStr, err := marshalReattachString(c.reattachInfo) + if err != nil { + return nil, err + } + mergeEnv[reattachEnvVar] = reattachStr } - return tf.buildTerraformCmd(ctx, mergeEnv, args...) + return tf.buildTerraformCmd(ctx, mergeEnv, args...), nil } diff --git a/tfexec/options.go b/tfexec/options.go index 549ee8e1..b8179259 100644 --- a/tfexec/options.go +++ b/tfexec/options.go @@ -1,5 +1,9 @@ package tfexec +import ( + "encoding/json" +) + // AllowMissingConfigOption represents the -allow-missing-config flag. type AllowMissingConfigOption struct { allowMissingConfig bool @@ -172,12 +176,37 @@ func PluginDir(pluginDir string) *PluginDirOption { return &PluginDirOption{pluginDir} } +type ReattachInfo map[string]ReattachConfig + +// ReattachConfig holds the information Terraform needs to be able to attach +// itself to a provider process, so it can drive the process. +type ReattachConfig struct { + Protocol string + Pid int + Test bool + Addr ReattachConfigAddr +} + +// ReattachConfigAddr is a JSON-encoding friendly version of net.Addr. +type ReattachConfigAddr struct { + Network string + String string +} + type ReattachOption struct { - info string + info ReattachInfo +} + +func marshalReattachString(info ReattachInfo) (string, error) { + reattachStr, err := json.Marshal(info) + if err != nil { + return "", err + } + return string(reattachStr), nil } -func Reattach(reattach string) *ReattachOption { - return &ReattachOption{reattach} +func Reattach(info ReattachInfo) *ReattachOption { + return &ReattachOption{info} } type ReconfigureOption struct { diff --git a/tfexec/plan.go b/tfexec/plan.go index 9cff8b5c..3d907e0a 100644 --- a/tfexec/plan.go +++ b/tfexec/plan.go @@ -14,7 +14,7 @@ type planConfig struct { lockTimeout string out string parallelism int - reattachInfo string + reattachInfo ReattachInfo refresh bool state string targets []string @@ -92,15 +92,18 @@ func (opt *DestroyFlagOption) configurePlan(conf *planConfig) { // The returned error is nil if `terraform plan` has been executed and exits // with either 0 or 2. func (tf *Terraform) Plan(ctx context.Context, opts ...PlanOption) (bool, error) { - cmd := tf.planCmd(ctx, opts...) - err := tf.runTerraformCmd(cmd) + cmd, err := tf.planCmd(ctx, opts...) + if err != nil { + return false, err + } + err = tf.runTerraformCmd(cmd) if err != nil && cmd.ProcessState.ExitCode() == 2 { return true, nil } return false, err } -func (tf *Terraform) planCmd(ctx context.Context, opts ...PlanOption) *exec.Cmd { +func (tf *Terraform) planCmd(ctx context.Context, opts ...PlanOption) (*exec.Cmd, error) { c := defaultPlanOptions for _, o := range opts { @@ -151,10 +154,13 @@ func (tf *Terraform) planCmd(ctx context.Context, opts ...PlanOption) *exec.Cmd } mergeEnv := map[string]string{} - if c.reattachInfo != "" { - mergeEnv[reattachEnvVar] = c.reattachInfo + if c.reattachInfo != nil { + reattachStr, err := marshalReattachString(c.reattachInfo) + if err != nil { + return nil, err + } + mergeEnv[reattachEnvVar] = reattachStr } - return tf.buildTerraformCmd(ctx, mergeEnv, args...) - + return tf.buildTerraformCmd(ctx, mergeEnv, args...), nil } diff --git a/tfexec/refresh.go b/tfexec/refresh.go index 8a4b9225..eec834e8 100644 --- a/tfexec/refresh.go +++ b/tfexec/refresh.go @@ -11,7 +11,7 @@ type refreshConfig struct { dir string lock bool lockTimeout string - reattachInfo string + reattachInfo ReattachInfo state string stateOut string targets []string @@ -71,10 +71,15 @@ func (opt *VarFileOption) configureRefresh(conf *refreshConfig) { // Refresh represents the terraform refresh subcommand. func (tf *Terraform) Refresh(ctx context.Context, opts ...RefreshCmdOption) error { - return tf.runTerraformCmd(tf.refreshCmd(ctx, opts...)) + cmd, err := tf.refreshCmd(ctx, opts...) + if err != nil { + return err + } + tf.runTerraformCmd(cmd) + return nil } -func (tf *Terraform) refreshCmd(ctx context.Context, opts ...RefreshCmdOption) *exec.Cmd { +func (tf *Terraform) refreshCmd(ctx context.Context, opts ...RefreshCmdOption) (*exec.Cmd, error) { c := defaultRefreshOptions for _, o := range opts { @@ -121,10 +126,13 @@ func (tf *Terraform) refreshCmd(ctx context.Context, opts ...RefreshCmdOption) * } mergeEnv := map[string]string{} - if c.reattachInfo != "" { - mergeEnv[reattachEnvVar] = c.reattachInfo + if c.reattachInfo != nil { + reattachStr, err := marshalReattachString(c.reattachInfo) + if err != nil { + return nil, err + } + mergeEnv[reattachEnvVar] = reattachStr } - return tf.buildTerraformCmd(ctx, mergeEnv, args...) - + return tf.buildTerraformCmd(ctx, mergeEnv, args...), nil } diff --git a/tfexec/show.go b/tfexec/show.go index 05dcc390..b133ef4b 100644 --- a/tfexec/show.go +++ b/tfexec/show.go @@ -9,7 +9,7 @@ import ( ) type showConfig struct { - reattachInfo string + reattachInfo ReattachInfo } var defaultShowOptions = showConfig{} @@ -37,8 +37,12 @@ func (tf *Terraform) Show(ctx context.Context, opts ...ShowOption) (*tfjson.Stat } mergeEnv := map[string]string{} - if c.reattachInfo != "" { - mergeEnv[reattachEnvVar] = c.reattachInfo + if c.reattachInfo != nil { + reattachStr, err := marshalReattachString(c.reattachInfo) + if err != nil { + return nil, err + } + mergeEnv[reattachEnvVar] = reattachStr } showCmd := tf.showCmd(ctx, mergeEnv) @@ -75,8 +79,12 @@ func (tf *Terraform) ShowStateFile(ctx context.Context, statePath string, opts . } mergeEnv := map[string]string{} - if c.reattachInfo != "" { - mergeEnv[reattachEnvVar] = c.reattachInfo + if c.reattachInfo != nil { + reattachStr, err := marshalReattachString(c.reattachInfo) + if err != nil { + return nil, err + } + mergeEnv[reattachEnvVar] = reattachStr } showCmd := tf.showCmd(ctx, mergeEnv, statePath) @@ -113,8 +121,12 @@ func (tf *Terraform) ShowPlanFile(ctx context.Context, planPath string, opts ... } mergeEnv := map[string]string{} - if c.reattachInfo != "" { - mergeEnv[reattachEnvVar] = c.reattachInfo + if c.reattachInfo != nil { + reattachStr, err := marshalReattachString(c.reattachInfo) + if err != nil { + return nil, err + } + mergeEnv[reattachEnvVar] = reattachStr } showCmd := tf.showCmd(ctx, mergeEnv, planPath)