diff --git a/tfexec/apply.go b/tfexec/apply.go index 82d09d5f..40d9e69b 100644 --- a/tfexec/apply.go +++ b/tfexec/apply.go @@ -17,6 +17,7 @@ type applyConfig struct { parallelism int reattachInfo ReattachInfo refresh bool + replaceAddrs []string state string stateOut string targets []string @@ -73,6 +74,10 @@ func (opt *RefreshOption) configureApply(conf *applyConfig) { conf.refresh = opt.refresh } +func (opt *ReplaceOption) configureApply(conf *applyConfig) { + conf.replaceAddrs = append(conf.replaceAddrs, opt.address) +} + func (opt *VarOption) configureApply(conf *applyConfig) { conf.vars = append(conf.vars, opt.assignment) } @@ -126,6 +131,15 @@ func (tf *Terraform) applyCmd(ctx context.Context, opts ...ApplyOption) (*exec.C args = append(args, "-refresh="+strconv.FormatBool(c.refresh)) // string slice opts: split into separate args + if c.replaceAddrs != nil { + err := tf.compatible(ctx, tf0_15_2, nil) + if err != nil { + return nil, fmt.Errorf("replace option was introduced in Terraform 0.15.2: %w", err) + } + for _, addr := range c.replaceAddrs { + args = append(args, "-replace="+addr) + } + } if c.targets != nil { for _, ta := range c.targets { args = append(args, "-target="+ta) diff --git a/tfexec/apply_test.go b/tfexec/apply_test.go index f1c6a1f2..e43fe2e8 100644 --- a/tfexec/apply_test.go +++ b/tfexec/apply_test.go @@ -10,7 +10,7 @@ import ( func TestApplyCmd(t *testing.T) { td := testTempDir(t) - tf, err := NewTerraform(td, tfVersion(t, testutil.Latest012)) + tf, err := NewTerraform(td, tfVersion(t, testutil.Latest_v1)) if err != nil { t.Fatal(err) } @@ -29,6 +29,8 @@ func TestApplyCmd(t *testing.T) { Lock(false), Parallelism(99), Refresh(false), + Replace("aws_instance.test"), + Replace("google_pubsub_topic.test"), Target("target1"), Target("target2"), Var("var1=foo"), @@ -53,6 +55,8 @@ func TestApplyCmd(t *testing.T) { "-lock=false", "-parallelism=99", "-refresh=false", + "-replace=aws_instance.test", + "-replace=google_pubsub_topic.test", "-target=target1", "-target=target2", "-var", "var1=foo", diff --git a/tfexec/options.go b/tfexec/options.go index 4d573efd..fb2d5bd7 100644 --- a/tfexec/options.go +++ b/tfexec/options.go @@ -296,6 +296,14 @@ func Refresh(refresh bool) *RefreshOption { return &RefreshOption{refresh} } +type ReplaceOption struct { + address string +} + +func Replace(address string) *ReplaceOption { + return &ReplaceOption{address} +} + type StateOption struct { path string } diff --git a/tfexec/plan.go b/tfexec/plan.go index bfe77db7..bf41094b 100644 --- a/tfexec/plan.go +++ b/tfexec/plan.go @@ -16,6 +16,7 @@ type planConfig struct { parallelism int reattachInfo ReattachInfo refresh bool + replaceAddrs []string state string targets []string vars []string @@ -63,6 +64,10 @@ func (opt *RefreshOption) configurePlan(conf *planConfig) { conf.refresh = opt.refresh } +func (opt *ReplaceOption) configurePlan(conf *planConfig) { + conf.replaceAddrs = append(conf.replaceAddrs, opt.address) +} + func (opt *ParallelismOption) configurePlan(conf *planConfig) { conf.parallelism = opt.parallelism } @@ -132,6 +137,15 @@ func (tf *Terraform) planCmd(ctx context.Context, opts ...PlanOption) (*exec.Cmd args = append(args, "-refresh="+strconv.FormatBool(c.refresh)) // unary flags: pass if true + if c.replaceAddrs != nil { + err := tf.compatible(ctx, tf0_15_2, nil) + if err != nil { + return nil, fmt.Errorf("replace option was introduced in Terraform 0.15.2: %w", err) + } + for _, addr := range c.replaceAddrs { + args = append(args, "-replace="+addr) + } + } if c.destroy { args = append(args, "-destroy") } diff --git a/tfexec/plan_test.go b/tfexec/plan_test.go index b30c74f8..16a376bf 100644 --- a/tfexec/plan_test.go +++ b/tfexec/plan_test.go @@ -10,7 +10,7 @@ import ( func TestPlanCmd(t *testing.T) { td := testTempDir(t) - tf, err := NewTerraform(td, tfVersion(t, testutil.Latest012)) + tf, err := NewTerraform(td, tfVersion(t, testutil.Latest_v1)) if err != nil { t.Fatal(err) } @@ -37,7 +37,22 @@ func TestPlanCmd(t *testing.T) { }) t.Run("override all defaults", func(t *testing.T) { - planCmd, err := tf.planCmd(context.Background(), Destroy(true), Lock(false), LockTimeout("22s"), Out("whale"), Parallelism(42), Refresh(false), State("marvin"), Target("zaphod"), Target("beeblebrox"), Var("android=paranoid"), Var("brain_size=planet"), VarFile("trillian"), Dir("earth")) + planCmd, err := tf.planCmd(context.Background(), + Destroy(true), + Lock(false), + LockTimeout("22s"), + Out("whale"), + Parallelism(42), + Refresh(false), + Replace("ford.prefect"), + Replace("arthur.dent"), + State("marvin"), + Target("zaphod"), + Target("beeblebrox"), + Var("android=paranoid"), + Var("brain_size=planet"), + VarFile("trillian"), + Dir("earth")) if err != nil { t.Fatal(err) } @@ -54,6 +69,8 @@ func TestPlanCmd(t *testing.T) { "-lock=false", "-parallelism=42", "-refresh=false", + "-replace=ford.prefect", + "-replace=arthur.dent", "-destroy", "-target=zaphod", "-target=beeblebrox", diff --git a/tfexec/version.go b/tfexec/version.go index 0bdcbcef..acc1227c 100644 --- a/tfexec/version.go +++ b/tfexec/version.go @@ -19,6 +19,7 @@ var ( tf0_13_0 = version.Must(version.NewVersion("0.13.0")) tf0_14_0 = version.Must(version.NewVersion("0.14.0")) tf0_15_0 = version.Must(version.NewVersion("0.15.0")) + tf0_15_2 = version.Must(version.NewVersion("0.15.2")) tf1_1_0 = version.Must(version.NewVersion("1.1.0")) )