From 01855d28f7e5d450e04e7c3942a32b7134cd007c Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Tue, 21 Sep 2021 15:04:49 -0700 Subject: [PATCH] fix test TF infra, add TF manager for testing --- .gitignore | 3 + go.mod | 1 + go.sum | 12 + plugin/testdata/{terraform => }/basic/iam.tf | 30 ++- plugin/testdata/{terraform => }/basic/main.tf | 0 .../testdata/{terraform => }/basic/vpc_ec2.tf | 2 +- testing/testdata/main.tf | 8 + testing/testing.go | 215 ++++++++++++++++++ testing/testing_test.go | 35 +++ 9 files changed, 300 insertions(+), 6 deletions(-) create mode 100644 .gitignore rename plugin/testdata/{terraform => }/basic/iam.tf (67%) rename plugin/testdata/{terraform => }/basic/main.tf (100%) rename plugin/testdata/{terraform => }/basic/vpc_ec2.tf (94%) create mode 100644 testing/testdata/main.tf create mode 100644 testing/testing.go create mode 100644 testing/testing_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f164bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.terraform* +terraform.tfstate* +plan.tfplan diff --git a/go.mod b/go.mod index 95bc6a5..5925bba 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,6 @@ require ( github.com/aws/aws-sdk-go v1.40.44 github.com/hashicorp/boundary/sdk v0.0.11-0.20210917171013-480e801d6447 github.com/hashicorp/go-secure-stdlib/awsutil v0.1.3 + github.com/hashicorp/terraform-json v0.12.0 // indirect google.golang.org/protobuf v1.27.1 ) diff --git a/go.sum b/go.sum index 0f0cdd5..ac74f87 100644 --- a/go.sum +++ b/go.sum @@ -58,6 +58,7 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -147,6 +148,7 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -236,6 +238,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/terraform-json v0.12.0 h1:8czPgEEWWPROStjkWPUnTQDXmpmZPlkQAwYYLETaTvw= +github.com/hashicorp/terraform-json v0.12.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE= github.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VKPAydKXS5yN0= github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10= @@ -272,6 +276,7 @@ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NB github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= @@ -285,6 +290,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -293,6 +299,7 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -344,6 +351,7 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -367,9 +375,12 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/tencentcloud/tencentcloud-sdk-go v1.0.162/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/yandex-cloud/go-genproto v0.0.0-20200722140432-762fe965ce77/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= github.com/yandex-cloud/go-sdk v0.0.0-20200722140627-2194e5077f13/go.mod h1:LEdAMqa1v/7KYe4b13ALLkonuDxLph57ibUb50ctvJk= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= +github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -417,6 +428,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/plugin/testdata/terraform/basic/iam.tf b/plugin/testdata/basic/iam.tf similarity index 67% rename from plugin/testdata/terraform/basic/iam.tf rename to plugin/testdata/basic/iam.tf index ef6acc7..d186b56 100644 --- a/plugin/testdata/terraform/basic/iam.tf +++ b/plugin/testdata/basic/iam.tf @@ -15,8 +15,6 @@ resource "aws_iam_user" "user" { resource "aws_iam_access_key" "user_initial_key" { user = aws_iam_user.user.name - - tags = var.project_path_tags } resource "aws_iam_policy" "ec2_describeinstances" { @@ -36,11 +34,13 @@ resource "aws_iam_policy" "ec2_describeinstances" { ] } EOF + + tags = var.project_path_tags } -resource "aws_iam_user_policy_attachement" "user_ec2_describeinstances_attachment" { +resource "aws_iam_user_policy_attachment" "user_ec2_describeinstances_attachment" { user = aws_iam_user.user.name - policy_arn = aws_iam_policy.ec2_describeinstancest.arn + policy_arn = aws_iam_policy.ec2_describeinstances.arn } resource "aws_iam_policy" "user_self_manage_policy" { @@ -62,9 +62,29 @@ resource "aws_iam_policy" "user_self_manage_policy" { ] } EOF + + tags = var.project_path_tags } -resource "aws_iam_user_policy_attachement" "user_self_manage_policy_attachment" { +resource "aws_iam_user_policy_attachment" "user_self_manage_policy_attachment" { user = aws_iam_user.user.name policy_arn = aws_iam_policy.user_self_manage_policy.arn } + +output "iam_user_name" { + value = aws_iam_user.user.name +} + +output "iam_user_arn" { + value = aws_iam_user.user.arn +} + +output "iam_access_key_id" { + value = aws_iam_access_key.user_initial_key.id + sensitive = true +} + +output "iam_secret_access_key" { + value = aws_iam_access_key.user_initial_key.secret + sensitive = true +} diff --git a/plugin/testdata/terraform/basic/main.tf b/plugin/testdata/basic/main.tf similarity index 100% rename from plugin/testdata/terraform/basic/main.tf rename to plugin/testdata/basic/main.tf diff --git a/plugin/testdata/terraform/basic/vpc_ec2.tf b/plugin/testdata/basic/vpc_ec2.tf similarity index 94% rename from plugin/testdata/terraform/basic/vpc_ec2.tf rename to plugin/testdata/basic/vpc_ec2.tf index fca2c55..08ed58d 100644 --- a/plugin/testdata/terraform/basic/vpc_ec2.tf +++ b/plugin/testdata/basic/vpc_ec2.tf @@ -57,7 +57,7 @@ resource "aws_instance" "instances" { ami = data.aws_ami.ubuntu.id instance_type = "t3.nano" - tags = merge(instance_tags[count.index], var.project_path_tags) + tags = merge(var.instance_tags[count.index], var.project_path_tags) } output "instance_tags" { diff --git a/testing/testdata/main.tf b/testing/testdata/main.tf new file mode 100644 index 0000000..ac6b10d --- /dev/null +++ b/testing/testdata/main.tf @@ -0,0 +1,8 @@ +resource "random_id" "foo" { + byte_length = 2 + prefix = "test-foo" +} + +output "random_id_decimal" { + value = random_id.foo.dec +} diff --git a/testing/testing.go b/testing/testing.go new file mode 100644 index 0000000..915f430 --- /dev/null +++ b/testing/testing.go @@ -0,0 +1,215 @@ +package testing + +import ( + "encoding/json" + "errors" + "fmt" + "os/exec" + "sync" + + tfjson "github.com/hashicorp/terraform-json" +) + +// TestTerraformer is an object representing the lifecycle of a +// particular Terraform project. +// +// Note that Terraform must be installed in the system PATH (not to +// be confused with the working directory below). This object does +// not manage anything with regards to installation or version +// management. +type TestTerraformer struct { + // The full path to the Terraform binary, set during initialization + // to the binary found in the system path. + tfPath string + + // The working directory for Terraform commands. + workDir string + + // The Terraform state. Use the Output method to look up particular + // outputs from the Terraform state. + state *tfjson.State + + // Keeps track of whether or not we have run the init command + // during this run of Terraform. This only needs to be done once. + // Do not use initErr directly; it stores the init error if one was + // encountered on init. + initOnce sync.Once + initErr error +} + +// NewTestTerraformer initializes a Terraform directory. It just +// returns an initialized project, it does not perform any actions on +// it. +func NewTestTerraformer(workDir string) (*TestTerraformer, error) { + tfPath, err := exec.LookPath("terraform") + if err != nil { + return nil, fmt.Errorf("LookPath error: %w", err) + } + + if workDir == "" { + return nil, errors.New("workDir is empty") + } + + return &TestTerraformer{ + tfPath: tfPath, + workDir: workDir, + }, nil +} + +// Deploy runs the following commands, in order: init, apply, plan, +// json. The output of the JSON command is then imported to the +// internal state. +func (t *TestTerraformer) Deploy() error { + if err := t.init(); err != nil { + return fmt.Errorf("error running Terraform init: %w", err) + } + + if err := t.apply(); err != nil { + return fmt.Errorf("error running Terraform apply: %w", err) + } + + if err := t.plan(); err != nil { + return fmt.Errorf("error running Terraform plan: %w", err) + } + + output, err := t.showJson() + if err != nil { + return fmt.Errorf("error getting Terraform plan JSON data: %w", err) + } + + newState := new(tfjson.Plan) + if err := json.Unmarshal(output, newState); err != nil { + return fmt.Errorf("error reading new Terraform plan JSON data: %w", err) + } + + t.state = newState.PriorState + return nil +} + +// Destroy runs the following commands, in order: init, destroy. The +// state is also cleared. +func (t *TestTerraformer) Destroy() error { + if err := t.init(); err != nil { + return fmt.Errorf("error running Terraform init: %w", err) + } + + if err := t.destroy(); err != nil { + return fmt.Errorf("error running Terraform destroy: %w", err) + } + + t.state = nil + return nil +} + +// GetOutput returns the value of the output located at key. +// +// Outputs for the root module are supported only. It's an error to +// run this when no state exists. +func (t *TestTerraformer) GetOutput(key string) (interface{}, error) { + if key == "" { + return nil, errors.New("key must not be empty") + } + + if t.state == nil { + return nil, errors.New("state is nil") + } + + if t.state.Values == nil { + return nil, errors.New("state integrity error: no state values") + } + + if t.state.Values.Outputs == nil { + return nil, errors.New("state integrity error: outputs is nil") + } + + output, ok := t.state.Values.Outputs[key] + if !ok { + return nil, fmt.Errorf("output with key %q not found", key) + } + + if output == nil { + return nil, fmt.Errorf("state integrity error: output at key %q is nil", key) + } + + return output.Value, nil +} + +// init runs "terraform init" in the project's working directory. +// +// This is only ever done once. +func (t *TestTerraformer) init() error { + t.initOnce.Do(func() { + cmd := exec.Command(t.tfPath, "init") + cmd.Dir = t.workDir + stdoutStderr, err := cmd.CombinedOutput() + if err != nil { + t.initErr = fmt.Errorf("%s\n%s", stdoutStderr, err) + } + }) + + return t.initErr +} + +// apply runs "terraform apply" in the project's working directory. +func (t *TestTerraformer) apply() error { + cmd := exec.Command(t.tfPath, "apply", "-input=false", "-auto-approve") + cmd.Dir = t.workDir + stdoutStderr, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s\n%s", stdoutStderr, err) + } + + return nil +} + +// destroy runs "terraform destroy" in the project's working directory. +func (t *TestTerraformer) destroy() error { + cmd := exec.Command(t.tfPath, "destroy", "-input=false", "-auto-approve") + cmd.Dir = t.workDir + stdoutStderr, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s\n%s", stdoutStderr, err) + } + + return nil +} + +// plan runs "terraform plan" in the project's working directory. +// +// Note that plan is not run as part of the apply workflow; rather, +// it's run to get the data for for importing the state via +// terraform-json. The apply method ignores the plan generated here. +func (t *TestTerraformer) plan() error { + cmd := exec.Command(t.tfPath, "plan", "-input=false", "-out=plan.tfplan") + cmd.Dir = t.workDir + stdoutStderr, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s\n%s", stdoutStderr, err) + } + + return nil +} + +// showJson runs "terraform show -json". The output is returned along +// with any error. +// +// Output is only the results of stdout and should be suitable for +// parsing. +// +// This method expects the plan.tfplan file to be generated in the +// working directory already. This can be done with the plan method. +func (t *TestTerraformer) showJson() ([]byte, error) { + cmd := exec.Command(t.tfPath, "show", "-json", "plan.tfplan") + cmd.Dir = t.workDir + output, err := cmd.Output() + if err != nil { + exitErr, ok := err.(*exec.ExitError) + if !ok { + return nil, err + } + + return nil, fmt.Errorf("%s\n%s", exitErr.Stderr, exitErr) + } + + return output, nil +} diff --git a/testing/testing_test.go b/testing/testing_test.go new file mode 100644 index 0000000..9b93050 --- /dev/null +++ b/testing/testing_test.go @@ -0,0 +1,35 @@ +package testing + +import ( + "strconv" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestTestTerraformer(t *testing.T) { + require := require.New(t) + + tf, err := NewTestTerraformer("testdata") + require.NoError(err) + require.NotNil(tf) + require.Nil(tf.state) + + err = tf.Deploy() + require.NoError(err) + require.NotNil(tf.state) + + data, err := tf.GetOutput("random_id_decimal") + require.NoError(err) + dataStr, ok := data.(string) + require.True(ok) + require.True(strings.HasPrefix(dataStr, "test-foo")) + dataInt, err := strconv.Atoi(strings.TrimPrefix(dataStr, "test-foo")) + require.NoError(err) + require.NotZero(dataInt) + + err = tf.Destroy() + require.NoError(err) + require.Nil(tf.state) +}