diff --git a/go.mod b/go.mod index 857517e..0f802ee 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,6 @@ go 1.12 require ( github.com/hashicorp/go-getter v1.4.0 - github.com/hashicorp/terraform-exec v0.3.0 + github.com/hashicorp/terraform-exec v0.7.0 github.com/hashicorp/terraform-json v0.5.0 ) diff --git a/go.sum b/go.sum index 3151061..5e86d4e 100644 --- a/go.sum +++ b/go.sum @@ -55,11 +55,13 @@ github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCS github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/terraform-exec v0.3.0 h1:5WLBsnv9BoEUGlHJZETROZZxw+qO3/TFQEh6JMP2uaY= -github.com/hashicorp/terraform-exec v0.3.0/go.mod h1:yKWvMPtkTaHpeAmllw+1qdHZ7E5u+pAZ+x8e2jQF6gM= +github.com/hashicorp/terraform-exec v0.7.0 h1:mq3S/IqleQI5BM/NO//q4DZesI+Ie8fVkte9zpFX3qc= +github.com/hashicorp/terraform-exec v0.7.0/go.mod h1:fkVMi6NCKxBbnGaOgn5el3BnkZVatoFR+kfURdI+3YM= github.com/hashicorp/terraform-json v0.5.0 h1:7TV3/F3y7QVSuN4r9BEXqnWqrAyeOtON8f0wvREtyzs= github.com/hashicorp/terraform-json v0.5.0/go.mod h1:eAbqb4w0pSlRmdvl8fOyHAi/+8jnkVYN28gJkSJrLhU= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= diff --git a/helper.go b/helper.go index a987889..a2cc470 100644 --- a/helper.go +++ b/helper.go @@ -194,9 +194,9 @@ func (h *Helper) NewWorkingDir() (*WorkingDir, error) { } return &WorkingDir{ - h: h, - baseArgs: []string{"-no-color"}, - baseDir: dir, + h: h, + baseDir: dir, + terraformExec: h.terraformExec, }, nil } diff --git a/working_dir.go b/working_dir.go index e917923..6c690a9 100644 --- a/working_dir.go +++ b/working_dir.go @@ -2,11 +2,13 @@ package tftest import ( "bytes" + "context" "fmt" "io/ioutil" "os" "path/filepath" + "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" ) @@ -26,6 +28,12 @@ type WorkingDir struct { // configDir contains the singular config file generated for each test configDir string + // tf is the instance of tfexec.Terraform used for running Terraform commands + tf *tfexec.Terraform + + // terraformExec is a path to a terraform binary, inherited from Helper + terraformExec string + env map[string]string } @@ -81,7 +89,13 @@ func (wd *WorkingDir) SetConfig(cfg string) error { return err } + tf, err := tfexec.NewTerraform(wd.baseDir, wd.terraformExec) + if err != nil { + return err + } + wd.configDir = configDir + wd.tf = tf // Changing configuration invalidates any saved plan. err = wd.ClearPlan() @@ -142,19 +156,14 @@ func (wd *WorkingDir) RequireClearPlan(t TestControl) { } } -func (wd *WorkingDir) init() error { - args := []string{"init", wd.configDir} - args = append(args, wd.baseArgs...) - return wd.runTerraform(nil, args...) -} - // Init runs "terraform init" for the given working directory, forcing Terraform // to use the current version of the plugin under test. func (wd *WorkingDir) Init() error { if wd.configDir == "" { return fmt.Errorf("must call SetConfig before Init") } - return wd.init() + + return wd.tf.Init(context.Background(), tfexec.Dir(wd.configDir)) } // RequireInit is a variant of Init that will fail the test via the given @@ -174,10 +183,7 @@ func (wd *WorkingDir) planFilename() string { // CreatePlan runs "terraform plan" to create a saved plan file, which if successful // will then be used for the next call to Apply. func (wd *WorkingDir) CreatePlan() error { - args := []string{"plan", "-refresh=false"} - args = append(args, wd.baseArgs...) - args = append(args, "-out=tfplan", wd.configDir) - return wd.runTerraform(nil, args...) + return wd.tf.Plan(context.Background(), tfexec.Refresh(false), tfexec.Out("tfplan"), tfexec.Dir(wd.configDir)) } // RequireCreatePlan is a variant of CreatePlan that will fail the test via @@ -195,11 +201,9 @@ func (wd *WorkingDir) RequireCreatePlan(t TestControl) { // this will apply the saved plan. Otherwise, it will implicitly create a new // plan and apply it. func (wd *WorkingDir) Apply() error { - args := []string{"apply", "-refresh=false"} - args = append(args, wd.baseArgs...) - + args := []tfexec.ApplyOption{tfexec.Refresh(false)} if wd.HasSavedPlan() { - args = append(args, "tfplan") + args = append(args, tfexec.DirOrPlan("tfplan")) } else { // we need to use a relative config dir here or we get an // error about Terraform not having any configuration. See @@ -209,11 +213,9 @@ func (wd *WorkingDir) Apply() error { if err != nil { return err } - args = append(args, "-auto-approve") - args = append(args, configDir) + args = append(args, tfexec.DirOrPlan(configDir)) } - - return wd.runTerraform(nil, args...) + return wd.tf.Apply(context.Background(), args...) } // RequireApply is a variant of Apply that will fail the test via @@ -232,11 +234,7 @@ func (wd *WorkingDir) RequireApply(t TestControl) { // If destroy fails then remote objects might still exist, and continue to // exist after a particular test is concluded. func (wd *WorkingDir) Destroy() error { - args := []string{"destroy", "-refresh=false"} - args = append(args, wd.baseArgs...) - - args = append(args, "-auto-approve", wd.configDir) - return wd.runTerraform(nil, args...) + return wd.tf.Destroy(context.Background(), tfexec.Refresh(false), tfexec.Dir(wd.configDir)) } // RequireDestroy is a variant of Destroy that will fail the test via @@ -269,18 +267,7 @@ func (wd *WorkingDir) SavedPlan() (*tfjson.Plan, error) { return nil, fmt.Errorf("there is no current saved plan") } - var ret tfjson.Plan - - args := []string{"show"} - args = append(args, wd.baseArgs...) - args = append(args, "-json", wd.planFilename()) - - err := wd.runTerraformJSON(&ret, args...) - if err != nil { - return nil, err - } - - return &ret, nil + return wd.tf.ShowPlanFile(context.Background(), wd.planFilename()) } // RequireSavedPlan is a variant of SavedPlan that will fail the test via @@ -306,11 +293,9 @@ func (wd *WorkingDir) SavedPlanStdout() (string, error) { var ret bytes.Buffer - args := []string{"show"} - args = append(args, wd.baseArgs...) - args = append(args, wd.planFilename()) - - err := wd.runTerraform(&ret, args...) + wd.tf.SetStdout(&ret) + defer wd.tf.SetStdout(ioutil.Discard) + _, err := wd.tf.ShowPlanFile(context.Background(), wd.planFilename()) if err != nil { return "", err } @@ -334,18 +319,7 @@ func (wd *WorkingDir) RequireSavedPlanStdout(t TestControl) string { // // If the state cannot be read, State returns an error. func (wd *WorkingDir) State() (*tfjson.State, error) { - var ret tfjson.State - - args := []string{"show"} - args = append(args, wd.baseArgs...) - args = append(args, "-json") - - err := wd.runTerraformJSON(&ret, args...) - if err != nil { - return nil, err - } - - return &ret, nil + return wd.tf.Show(context.Background()) } // RequireState is a variant of State that will fail the test via @@ -362,10 +336,7 @@ func (wd *WorkingDir) RequireState(t TestControl) *tfjson.State { // Import runs terraform import func (wd *WorkingDir) Import(resource, id string) error { - args := []string{"import"} - args = append(args, wd.baseArgs...) - args = append(args, "-config="+wd.configDir, resource, id) - return wd.runTerraform(nil, args...) + return wd.tf.Import(context.Background(), resource, id, tfexec.Config(wd.configDir)) } // RequireImport is a variant of Import that will fail the test via @@ -380,11 +351,7 @@ func (wd *WorkingDir) RequireImport(t TestControl, resource, id string) { // Refresh runs terraform refresh func (wd *WorkingDir) Refresh() error { - args := []string{"refresh"} - args = append(args, wd.baseArgs...) - args = append(args, "-state="+filepath.Join(wd.baseDir, "terraform.tfstate")) - args = append(args, wd.configDir) - return wd.runTerraform(nil, args...) + return wd.tf.Refresh(context.Background(), tfexec.State(filepath.Join(wd.baseDir, "terraform.tfstate")), tfexec.Dir(wd.configDir)) } // RequireRefresh is a variant of Refresh that will fail the test via @@ -401,15 +368,7 @@ func (wd *WorkingDir) RequireRefresh(t TestControl) { // // If the schemas cannot be read, Schemas returns an error. func (wd *WorkingDir) Schemas() (*tfjson.ProviderSchemas, error) { - args := []string{"providers", wd.configDir, "schema"} - - var ret tfjson.ProviderSchemas - err := wd.runTerraformJSON(&ret, args...) - if err != nil { - return nil, err - } - - return &ret, nil + return wd.tf.ProvidersSchema(context.Background()) } // RequireSchemas is a variant of Schemas that will fail the test via