add execVersion, improve env handling
diff --git a/tfexec/terraform.go b/tfexec/terraform.go index 87d0479..4eb1123 100644 --- a/tfexec/terraform.go +++ b/tfexec/terraform.go
@@ -14,15 +14,16 @@ ) type Terraform struct { - execPath string - workingDir string - Env []string + execPath string + workingDir string + execVersion string + env []string } // NewTerraform returns a Terraform struct with default values for all fields. // If a blank execPath is supplied, NewTerraform will attempt to locate an // appropriate binary on the system PATH. -func NewTerraform(workingDir string, execPath string) (*Terraform, error) { +func NewTerraform(workingDir string, execPath string, env map[string]string) (*Terraform, error) { var err error if workingDir == "" { return nil, fmt.Errorf("Terraform cannot be initialised with empty workdir") @@ -37,16 +38,65 @@ if err != nil { return nil, err } + } - // TODO for maximum helpfulness, check execPath looks like a terraform binary - - passthroughEnv := os.Environ() - - return &Terraform{ + tf := Terraform{ execPath: execPath, workingDir: workingDir, - Env: passthroughEnv, - }, nil + env: getTerraformEnv(env), + } + + execVersion, err := tf.version() + if err != nil { + return nil, fmt.Errorf("error running 'terraform version': %s", err) + } + + tf.execVersion = execVersion + + return &tf, nil +} + +func getTerraformEnv(env map[string]string) []string { + var ret []string + + if env == nil { + for _, e := range os.Environ() { + ret = append(ret, e) + } + } + + // always propagate CHECKPOINT_DISABLE env var unless it is + // explicitly overridden + c := os.Getenv("CHECKPOINT_DISABLE") + if c != "" { + ret = append(ret, "CHECKPOINT_DISABLE="+c) + } + + for k, v := range env { + ret = append(ret, k+"="+v) + } + + return ret +} + +func (tf *Terraform) version() (string, error) { + versionCmd := tf.buildTerraformCmd(context.Background(), "version") + var errBuf strings.Builder + var outBuf bytes.Buffer + versionCmd.Stderr = &errBuf + versionCmd.Stdout = &outBuf + + err := versionCmd.Run() + if err != nil { + fmt.Println(errBuf.String()) + return "", fmt.Errorf("%s, %s", err, errBuf.String()) + } + + // fmt.Println(outBuf.String()) + + // parse it + + return outBuf.String(), nil } type initConfig struct {
diff --git a/tfexec/terraform_cmd.go b/tfexec/terraform_cmd.go index ee6e005..d42e28e 100644 --- a/tfexec/terraform_cmd.go +++ b/tfexec/terraform_cmd.go
@@ -3,28 +3,21 @@ import ( "context" "fmt" - "os" "os/exec" "strconv" ) -func (t *Terraform) buildTerraformCmd(ctx context.Context, args ...string) *exec.Cmd { - var env []string - for _, e := range os.Environ() { - env = append(env, e) - } +func (tf *Terraform) buildTerraformCmd(ctx context.Context, args ...string) *exec.Cmd { + env := append(tf.env, "TF_LOG=") // so logging can't pollute our stderr output - env = append(env, "TF_LOG=") // so logging can't pollute our stderr output - env = append(env, "TF_INPUT=0") - - cmd := exec.CommandContext(ctx, t.execPath, args...) - cmd.Env = t.Env - cmd.Dir = t.workingDir + cmd := exec.CommandContext(ctx, tf.execPath, args...) + cmd.Env = env + cmd.Dir = tf.workingDir return cmd } -func (t *Terraform) InitCmd(ctx context.Context, opts ...InitOption) *exec.Cmd { +func (tf *Terraform) InitCmd(ctx context.Context, opts ...InitOption) *exec.Cmd { c := defaultInitOptions for _, o := range opts { @@ -66,10 +59,10 @@ } } - return t.buildTerraformCmd(ctx, args...) + return tf.buildTerraformCmd(ctx, args...) } -func (t *Terraform) ApplyCmd(ctx context.Context, opts ...ApplyOption) *exec.Cmd { +func (tf *Terraform) ApplyCmd(ctx context.Context, opts ...ApplyOption) *exec.Cmd { c := defaultApplyOptions for _, o := range opts { @@ -117,10 +110,10 @@ args = append(args, c.dirOrPlan) } - return t.buildTerraformCmd(ctx, args...) + return tf.buildTerraformCmd(ctx, args...) } -func (t *Terraform) DestroyCmd(ctx context.Context, opts ...DestroyOption) *exec.Cmd { +func (tf *Terraform) DestroyCmd(ctx context.Context, opts ...DestroyOption) *exec.Cmd { c := defaultDestroyOptions for _, o := range opts { @@ -163,7 +156,7 @@ } } - return t.buildTerraformCmd(ctx, args...) + return tf.buildTerraformCmd(ctx, args...) } func (tf *Terraform) PlanCmd(ctx context.Context, opts ...PlanOption) *exec.Cmd { @@ -285,9 +278,9 @@ return tf.buildTerraformCmd(ctx, allArgs...) } -func (t *Terraform) ProvidersSchemaCmd(ctx context.Context, args ...string) *exec.Cmd { +func (tf *Terraform) ProvidersSchemaCmd(ctx context.Context, args ...string) *exec.Cmd { allArgs := []string{"providers", "schema", "-json", "-no-color"} allArgs = append(allArgs, args...) - return t.buildTerraformCmd(ctx, allArgs...) + return tf.buildTerraformCmd(ctx, allArgs...) }
diff --git a/tfexec/terraform_test.go b/tfexec/terraform_test.go index c13b1fd..45d371e 100644 --- a/tfexec/terraform_test.go +++ b/tfexec/terraform_test.go
@@ -20,7 +20,10 @@ const testTerraformStateFileName = "terraform.tfstate" func TestInitCmd(t *testing.T) { - tf, err := NewTerraform("/dev/null", "") + td := testTempDir(t) + defer os.RemoveAll(td) + + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) } @@ -49,7 +52,10 @@ } func TestPlanCmd(t *testing.T) { - tf, err := NewTerraform("/dev/null", "") + td := testTempDir(t) + defer os.RemoveAll(td) + + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) } @@ -78,7 +84,10 @@ } func TestApplyCmd(t *testing.T) { - tf, err := NewTerraform("/dev/null", "") + td := testTempDir(t) + defer os.RemoveAll(td) + + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) } @@ -95,7 +104,10 @@ } func TestDestroyCmd(t *testing.T) { - tf, err := NewTerraform("/dev/null", "") + td := testTempDir(t) + defer os.RemoveAll(td) + + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) } @@ -124,7 +136,10 @@ } func TestImportCmd(t *testing.T) { - tf, err := NewTerraform("/dev/null", "") + td := testTempDir(t) + defer os.RemoveAll(td) + + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) } @@ -162,7 +177,10 @@ } func TestOutputCmd(t *testing.T) { - tf, err := NewTerraform("/dev/null", "") + td := testTempDir(t) + defer os.RemoveAll(td) + + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) } @@ -192,7 +210,10 @@ } func TestStateShowCmd(t *testing.T) { - tf, err := NewTerraform("/dev/null", "") + td := testTempDir(t) + defer os.RemoveAll(td) + + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) } @@ -210,7 +231,10 @@ } func TestProvidersSchemaCmd(t *testing.T) { - tf, err := NewTerraform("/dev/null", "") + td := testTempDir(t) + defer os.RemoveAll(td) + + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) } @@ -231,7 +255,7 @@ td := testTempDir(t) defer os.RemoveAll(td) - tf, err := NewTerraform(td, "") + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) } @@ -285,7 +309,7 @@ td := testTempDir(t) defer os.RemoveAll(td) - tf, err := NewTerraform(td, "") + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) } @@ -313,7 +337,7 @@ td := testTempDir(t) defer os.RemoveAll(td) - tf, err := NewTerraform(td, "") + tf, err := NewTerraform(td, "", nil) if err != nil { t.Fatal(err) }