From 82d645100622e56063cffe293ac597d25f7f3f75 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 16 Nov 2020 17:04:23 -0800 Subject: [PATCH 1/6] Adds support for the terraform 0.12upgrade command --- tfexec/options.go | 16 ++++++++ tfexec/upgrade012.go | 79 +++++++++++++++++++++++++++++++++++++++ tfexec/upgrade012_test.go | 73 ++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 tfexec/upgrade012.go create mode 100644 tfexec/upgrade012_test.go diff --git a/tfexec/options.go b/tfexec/options.go index f5baf5d4..39b6f73f 100644 --- a/tfexec/options.go +++ b/tfexec/options.go @@ -99,6 +99,14 @@ func Destroy(destroy bool) *DestroyFlagOption { return &DestroyFlagOption{destroy} } +type ForceOption struct { + force bool +} + +func Force(force bool) *ForceOption { + return &ForceOption{force} +} + type ForceCopyOption struct { forceCopy bool } @@ -294,3 +302,11 @@ type VerifyPluginsOption struct { func VerifyPlugins(verifyPlugins bool) *VerifyPluginsOption { return &VerifyPluginsOption{verifyPlugins} } + +type YesOption struct { + yes bool +} + +func Yes(yes bool) *YesOption { + return &YesOption{yes} +} diff --git a/tfexec/upgrade012.go b/tfexec/upgrade012.go new file mode 100644 index 00000000..0d98a30e --- /dev/null +++ b/tfexec/upgrade012.go @@ -0,0 +1,79 @@ +package tfexec + +import ( + "context" + "fmt" + "os/exec" +) + +type upgrade012Config struct { + yes bool + force bool + + reattachInfo ReattachInfo +} + +var defaultUpgrade012Options = upgrade012Config{ + yes: false, + force: false, +} + +// Upgrade012Option represents options used in the Destroy method. +type Upgrade012Option interface { + configureUpgrade012(*upgrade012Config) +} + +func (opt *ForceOption) configureUpgrade012(conf *upgrade012Config) { + conf.force = opt.force +} + +func (opt *YesOption) configureUpgrade012(conf *upgrade012Config) { + conf.yes = opt.yes +} + +func (opt *ReattachOption) configureUpgrade012(conf *upgrade012Config) { + conf.reattachInfo = opt.info +} + +// Upgrade012 represents the terraform 0.12upgrade subcommand. +func (tf *Terraform) Upgrade012(ctx context.Context, opts ...Upgrade012Option) error { + cmd, err := tf.upgrade012Cmd(ctx, opts...) + if err != nil { + return err + } + return tf.runTerraformCmd(cmd) +} + +func (tf *Terraform) upgrade012Cmd(ctx context.Context, opts ...Upgrade012Option) (*exec.Cmd, error) { + err := tf.compatible(ctx, tf0_12_0, tf0_13_0) + if err != nil { + return nil, fmt.Errorf("terraform 0.12upgrade is only supported in 0.12 releases: %w", err) + } + + c := defaultUpgrade012Options + + for _, o := range opts { + o.configureUpgrade012(&c) + } + + args := []string{"0.12upgrade"} + + // boolean opts: only pass if set + if c.yes { + args = append(args, "-yes") + } + if c.force { + args = append(args, "-force") + } + + mergeEnv := map[string]string{} + if c.reattachInfo != nil { + reattachStr, err := c.reattachInfo.marshalString() + if err != nil { + return nil, err + } + mergeEnv[reattachEnvVar] = reattachStr + } + + return tf.buildTerraformCmd(ctx, mergeEnv, args...), nil +} diff --git a/tfexec/upgrade012_test.go b/tfexec/upgrade012_test.go new file mode 100644 index 00000000..63e1b58f --- /dev/null +++ b/tfexec/upgrade012_test.go @@ -0,0 +1,73 @@ +package tfexec + +import ( + "context" + "errors" + "testing" + + "github.com/hashicorp/terraform-exec/tfexec/internal/testutil" +) + +func TestUpgrade012(t *testing.T) { + td := testTempDir(t) + + t.Run("defaults", func(t *testing.T) { + tf, err := NewTerraform(td, tfVersion(t, testutil.Latest012)) + if err != nil { + t.Fatal(err) + } + + // empty env, to avoid environ mismatch in testing + tf.SetEnv(map[string]string{}) + + upgrade012Cmd, err := tf.upgrade012Cmd(context.Background()) + if err != nil { + t.Fatal(err) + } + + assertCmd(t, []string{ + "0.12upgrade", + }, nil, upgrade012Cmd) + }) + + t.Run("override all defaults", func(t *testing.T) { + tf, err := NewTerraform(td, tfVersion(t, testutil.Latest012)) + if err != nil { + t.Fatal(err) + } + + // empty env, to avoid environ mismatch in testing + tf.SetEnv(map[string]string{}) + + upgrade012Cmd, err := tf.upgrade012Cmd(context.Background(), Yes(true), Force(true)) + if err != nil { + t.Fatal(err) + } + + assertCmd(t, []string{ + "0.12upgrade", + "-yes", + "-force", + }, nil, upgrade012Cmd) + }) + + t.Run("unsupported on 0.13", func(t *testing.T) { + tf, err := NewTerraform(td, tfVersion(t, testutil.Latest013)) + if err != nil { + t.Fatal(err) + } + + // empty env, to avoid environ mismatch in testing + tf.SetEnv(map[string]string{}) + + _, err = tf.upgrade012Cmd(context.Background()) + if err == nil { + t.Fatal("expected old version to fail") + } + + var expectedErr *ErrVersionMismatch + if !errors.As(err, &expectedErr) { + t.Fatalf("error doesn't match: %#v", err) + } + }) +} From 720401243f255a98b1ebcf0746ef9e6f90231b6a Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Mon, 16 Nov 2020 17:16:50 -0800 Subject: [PATCH 2/6] Adds directory parameter --- tfexec/upgrade012.go | 12 +++++++++++- tfexec/upgrade012_test.go | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tfexec/upgrade012.go b/tfexec/upgrade012.go index 0d98a30e..2de0bfbb 100644 --- a/tfexec/upgrade012.go +++ b/tfexec/upgrade012.go @@ -7,8 +7,9 @@ import ( ) type upgrade012Config struct { - yes bool + dir string force bool + yes bool reattachInfo ReattachInfo } @@ -23,6 +24,10 @@ type Upgrade012Option interface { configureUpgrade012(*upgrade012Config) } +func (opt *DirOption) configureUpgrade012(conf *upgrade012Config) { + conf.dir = opt.path +} + func (opt *ForceOption) configureUpgrade012(conf *upgrade012Config) { conf.force = opt.force } @@ -66,6 +71,11 @@ func (tf *Terraform) upgrade012Cmd(ctx context.Context, opts ...Upgrade012Option args = append(args, "-force") } + // optional positional argument + if c.dir != "" { + args = append(args, c.dir) + } + mergeEnv := map[string]string{} if c.reattachInfo != nil { reattachStr, err := c.reattachInfo.marshalString() diff --git a/tfexec/upgrade012_test.go b/tfexec/upgrade012_test.go index 63e1b58f..2872975a 100644 --- a/tfexec/upgrade012_test.go +++ b/tfexec/upgrade012_test.go @@ -39,7 +39,7 @@ func TestUpgrade012(t *testing.T) { // empty env, to avoid environ mismatch in testing tf.SetEnv(map[string]string{}) - upgrade012Cmd, err := tf.upgrade012Cmd(context.Background(), Yes(true), Force(true)) + upgrade012Cmd, err := tf.upgrade012Cmd(context.Background(), Yes(true), Force(true), Dir("upgrade012dir")) if err != nil { t.Fatal(err) } @@ -48,6 +48,7 @@ func TestUpgrade012(t *testing.T) { "0.12upgrade", "-yes", "-force", + "upgrade012dir", }, nil, upgrade012Cmd) }) From 051afb351e5fa246a41d27b41f2932f876d5256e Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Tue, 17 Nov 2020 12:40:06 -0800 Subject: [PATCH 3/6] Adds -no-color to 0.12upgrade --- tfexec/upgrade012.go | 2 +- tfexec/upgrade012_test.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tfexec/upgrade012.go b/tfexec/upgrade012.go index 2de0bfbb..7e446691 100644 --- a/tfexec/upgrade012.go +++ b/tfexec/upgrade012.go @@ -61,7 +61,7 @@ func (tf *Terraform) upgrade012Cmd(ctx context.Context, opts ...Upgrade012Option o.configureUpgrade012(&c) } - args := []string{"0.12upgrade"} + args := []string{"0.12upgrade", "-no-color"} // boolean opts: only pass if set if c.yes { diff --git a/tfexec/upgrade012_test.go b/tfexec/upgrade012_test.go index 2872975a..cd169829 100644 --- a/tfexec/upgrade012_test.go +++ b/tfexec/upgrade012_test.go @@ -27,6 +27,7 @@ func TestUpgrade012(t *testing.T) { assertCmd(t, []string{ "0.12upgrade", + "-no-color", }, nil, upgrade012Cmd) }) @@ -46,6 +47,7 @@ func TestUpgrade012(t *testing.T) { assertCmd(t, []string{ "0.12upgrade", + "-no-color", "-yes", "-force", "upgrade012dir", From 1facd0cf3c6e89ea6d4fbcbcaa79cf832b8efb5a Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 18 Nov 2020 18:44:45 -0800 Subject: [PATCH 4/6] Always passes -yes flag --- tfexec/options.go | 8 -------- tfexec/upgrade012.go | 11 +--------- tfexec/upgrade012_test.go | 42 +++++++++++++++++++++++---------------- 3 files changed, 26 insertions(+), 35 deletions(-) diff --git a/tfexec/options.go b/tfexec/options.go index 39b6f73f..3f0fd00c 100644 --- a/tfexec/options.go +++ b/tfexec/options.go @@ -302,11 +302,3 @@ type VerifyPluginsOption struct { func VerifyPlugins(verifyPlugins bool) *VerifyPluginsOption { return &VerifyPluginsOption{verifyPlugins} } - -type YesOption struct { - yes bool -} - -func Yes(yes bool) *YesOption { - return &YesOption{yes} -} diff --git a/tfexec/upgrade012.go b/tfexec/upgrade012.go index 7e446691..5af05b7e 100644 --- a/tfexec/upgrade012.go +++ b/tfexec/upgrade012.go @@ -9,13 +9,11 @@ import ( type upgrade012Config struct { dir string force bool - yes bool reattachInfo ReattachInfo } var defaultUpgrade012Options = upgrade012Config{ - yes: false, force: false, } @@ -32,10 +30,6 @@ func (opt *ForceOption) configureUpgrade012(conf *upgrade012Config) { conf.force = opt.force } -func (opt *YesOption) configureUpgrade012(conf *upgrade012Config) { - conf.yes = opt.yes -} - func (opt *ReattachOption) configureUpgrade012(conf *upgrade012Config) { conf.reattachInfo = opt.info } @@ -61,12 +55,9 @@ func (tf *Terraform) upgrade012Cmd(ctx context.Context, opts ...Upgrade012Option o.configureUpgrade012(&c) } - args := []string{"0.12upgrade", "-no-color"} + args := []string{"0.12upgrade", "-no-color", "-yes"} // boolean opts: only pass if set - if c.yes { - args = append(args, "-yes") - } if c.force { args = append(args, "-force") } diff --git a/tfexec/upgrade012_test.go b/tfexec/upgrade012_test.go index cd169829..958d0d24 100644 --- a/tfexec/upgrade012_test.go +++ b/tfexec/upgrade012_test.go @@ -3,6 +3,7 @@ package tfexec import ( "context" "errors" + "fmt" "testing" "github.com/hashicorp/terraform-exec/tfexec/internal/testutil" @@ -28,6 +29,7 @@ func TestUpgrade012(t *testing.T) { assertCmd(t, []string{ "0.12upgrade", "-no-color", + "-yes", }, nil, upgrade012Cmd) }) @@ -40,7 +42,7 @@ func TestUpgrade012(t *testing.T) { // empty env, to avoid environ mismatch in testing tf.SetEnv(map[string]string{}) - upgrade012Cmd, err := tf.upgrade012Cmd(context.Background(), Yes(true), Force(true), Dir("upgrade012dir")) + upgrade012Cmd, err := tf.upgrade012Cmd(context.Background(), Force(true), Dir("upgrade012dir")) if err != nil { t.Fatal(err) } @@ -54,23 +56,29 @@ func TestUpgrade012(t *testing.T) { }, nil, upgrade012Cmd) }) - t.Run("unsupported on 0.13", func(t *testing.T) { - tf, err := NewTerraform(td, tfVersion(t, testutil.Latest013)) - if err != nil { - t.Fatal(err) - } + unsupportedVersions := []string{ + testutil.Latest011, + testutil.Latest013, + } + for _, tfv := range unsupportedVersions { + t.Run(fmt.Sprintf("unsupported on %s", tfv), func(t *testing.T) { + tf, err := NewTerraform(td, tfVersion(t, tfv)) + if err != nil { + t.Fatal(err) + } - // empty env, to avoid environ mismatch in testing - tf.SetEnv(map[string]string{}) + // empty env, to avoid environ mismatch in testing + tf.SetEnv(map[string]string{}) - _, err = tf.upgrade012Cmd(context.Background()) - if err == nil { - t.Fatal("expected old version to fail") - } + _, err = tf.upgrade012Cmd(context.Background()) + if err == nil { + t.Fatalf("expected unsupported version %s to fail", tfv) + } - var expectedErr *ErrVersionMismatch - if !errors.As(err, &expectedErr) { - t.Fatalf("error doesn't match: %#v", err) - } - }) + var expectedErr *ErrVersionMismatch + if !errors.As(err, &expectedErr) { + t.Fatalf("error doesn't match: %#v", err) + } + }) + } } From 3de9d3b7aad605a186cf6d15a0609d8b45567394 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 18 Nov 2020 22:51:25 -0800 Subject: [PATCH 5/6] Adds e2e test for Upgrade012 --- .../testdata/pre_011_syntax/file1.golden.txt | 31 +++++++++++++++++ .../e2etest/testdata/pre_011_syntax/file1.tf | 30 +++++++++++++++++ .../testdata/pre_011_syntax/file2.golden.txt | 26 +++++++++++++++ .../e2etest/testdata/pre_011_syntax/file2.tf | 25 ++++++++++++++ tfexec/internal/e2etest/upgrade012_test.go | 33 +++++++++++++++++++ 5 files changed, 145 insertions(+) create mode 100644 tfexec/internal/e2etest/testdata/pre_011_syntax/file1.golden.txt create mode 100644 tfexec/internal/e2etest/testdata/pre_011_syntax/file1.tf create mode 100644 tfexec/internal/e2etest/testdata/pre_011_syntax/file2.golden.txt create mode 100644 tfexec/internal/e2etest/testdata/pre_011_syntax/file2.tf create mode 100644 tfexec/internal/e2etest/upgrade012_test.go diff --git a/tfexec/internal/e2etest/testdata/pre_011_syntax/file1.golden.txt b/tfexec/internal/e2etest/testdata/pre_011_syntax/file1.golden.txt new file mode 100644 index 00000000..1b6a3631 --- /dev/null +++ b/tfexec/internal/e2etest/testdata/pre_011_syntax/file1.golden.txt @@ -0,0 +1,31 @@ +resource "aws_instance" "web" { + ami = data.aws_ami.amazon.id + instance_type = "t3.micro" + count = 2 + + tags = { + Name = "HelloWorld" + } +} + +resource "aws_elb" "web" { + instances = aws_instance.web.*.id + subnets = aws_subnet.test.*.id + listener { + instance_port = 8000 + instance_protocol = "http" + lb_port = 80 + lb_protocol = "http" + } +} + +data "aws_ami" "amazon" { + most_recent = true + owners = ["amazon"] + + filter { + name = "name" + values = ["amzn-ami-hvm-*-x86_64-gp2"] + } +} + diff --git a/tfexec/internal/e2etest/testdata/pre_011_syntax/file1.tf b/tfexec/internal/e2etest/testdata/pre_011_syntax/file1.tf new file mode 100644 index 00000000..b659870f --- /dev/null +++ b/tfexec/internal/e2etest/testdata/pre_011_syntax/file1.tf @@ -0,0 +1,30 @@ +resource "aws_instance" "web" { + ami = "${data.aws_ami.amazon.id}" + instance_type = "t3.micro" + count = 2 + + tags = { + Name = "HelloWorld" + } +} + +resource "aws_elb" "web" { + instances = ["${aws_instance.web.*.id}"] + subnets = ["${aws_subnet.test.*.id}"] + listener { + instance_port = 8000 + instance_protocol = "http" + lb_port = 80 + lb_protocol = "http" + } +} + +data "aws_ami" "amazon" { + most_recent = true + owners = ["amazon"] + + filter { + name = "name" + values = ["amzn-ami-hvm-*-x86_64-gp2"] + } +} diff --git a/tfexec/internal/e2etest/testdata/pre_011_syntax/file2.golden.txt b/tfexec/internal/e2etest/testdata/pre_011_syntax/file2.golden.txt new file mode 100644 index 00000000..64b58def --- /dev/null +++ b/tfexec/internal/e2etest/testdata/pre_011_syntax/file2.golden.txt @@ -0,0 +1,26 @@ +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +resource "aws_vpc" "test" { + cidr_block = var.vpc_cidr + enable_dns_hostnames = true +} + +resource "aws_subnet" "test" { + count = 2 + vpc_id = aws_vpc.test.id + + cidr_block = cidrsubnet(var.vpc_cidr, 2, count.index) + availability_zone = data.aws_availability_zones.available.names[count.index] +} + +variable "vpc_cidr" { + default = "10.1.0.0/16" +} + diff --git a/tfexec/internal/e2etest/testdata/pre_011_syntax/file2.tf b/tfexec/internal/e2etest/testdata/pre_011_syntax/file2.tf new file mode 100644 index 00000000..e263412f --- /dev/null +++ b/tfexec/internal/e2etest/testdata/pre_011_syntax/file2.tf @@ -0,0 +1,25 @@ +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +resource "aws_vpc" "test" { + cidr_block = "${var.vpc_cidr}" + enable_dns_hostnames = true +} + +resource "aws_subnet" "test" { + count = 2 + vpc_id = "${aws_vpc.test.id}" + + cidr_block = "${cidrsubnet(var.vpc_cidr, 2, count.index)}" + availability_zone = "${data.aws_availability_zones.available.names[count.index]}" +} + +variable "vpc_cidr" { + default = "10.1.0.0/16" +} diff --git a/tfexec/internal/e2etest/upgrade012_test.go b/tfexec/internal/e2etest/upgrade012_test.go new file mode 100644 index 00000000..c40de0c0 --- /dev/null +++ b/tfexec/internal/e2etest/upgrade012_test.go @@ -0,0 +1,33 @@ +package e2etest + +import ( + "context" + "path/filepath" + "testing" + + "github.com/hashicorp/go-version" + + "github.com/hashicorp/terraform-exec/tfexec" + "github.com/hashicorp/terraform-exec/tfexec/internal/testutil" +) + +func TestUpgrade012(t *testing.T) { + runTestVersions(t, []string{testutil.Latest012}, "pre_011_syntax", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) { + err := tf.Init(context.Background()) + if err != nil { + t.Fatalf("error running Init in test directory: %s", err) + } + + err = tf.Upgrade012(context.Background()) + if err != nil { + t.Fatalf("error from FormatWrite: %T %q", err, err) + } + + for file, golden := range map[string]string{ + "file1.tf": "file1.golden.txt", + "file2.tf": "file2.golden.txt", + } { + textFilesEqual(t, filepath.Join(tf.WorkingDir(), golden), filepath.Join(tf.WorkingDir(), file)) + } + }) +} From f3b9d3de85ef1c46870da9500b8e20e42f63b253 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 18 Nov 2020 22:51:53 -0800 Subject: [PATCH 6/6] Spelling --- tfexec/internal/e2etest/util_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tfexec/internal/e2etest/util_test.go b/tfexec/internal/e2etest/util_test.go index d5232a92..a74e30ce 100644 --- a/tfexec/internal/e2etest/util_test.go +++ b/tfexec/internal/e2etest/util_test.go @@ -72,7 +72,7 @@ func runTestVersions(t *testing.T, versions []string, fixtureName string, cb fun runningVersion, _, err := tf.Version(context.Background(), false) if err != nil { - t.Fatalf("unable to determin running version (expected %q): %s", tfv, err) + t.Fatalf("unable to determine running version (expected %q): %s", tfv, err) } if fixtureName != "" {