Skip to content

Commit

Permalink
tfexec: Add (Terraform).SetLog() method (hashicorp#291)
Browse files Browse the repository at this point in the history
* tfexec: Add (Terraform).SetLog() method

Reference: hashicorp#290

* tfexec: Add testing for SetLog

* tfexec: Add SetLogPath testing

* tfexec: Ensure SetLog version compatibility is 0.15 minimum, testing updates for darwin/arm64
  • Loading branch information
bflad authored and rstandt committed Jun 21, 2022
1 parent 63846f5 commit 531dc1c
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 5 deletions.
3 changes: 1 addition & 2 deletions tfexec/cmd.go
Expand Up @@ -149,8 +149,7 @@ func (tf *Terraform) buildEnv(mergeEnv map[string]string) []string {
env[logPathEnvVar] = ""
} else {
env[logPathEnvVar] = tf.logPath
// Log levels other than TRACE are currently unreliable, the CLI recommends using TRACE only.
env[logEnvVar] = "TRACE"
env[logEnvVar] = tf.log
}

// constant automation override env vars
Expand Down
30 changes: 27 additions & 3 deletions tfexec/terraform.go
Expand Up @@ -48,9 +48,14 @@ type Terraform struct {
skipProviderVerify bool
env map[string]string

stdout io.Writer
stderr io.Writer
logger printfer
stdout io.Writer
stderr io.Writer
logger printfer

// TF_LOG environment variable, defaults to TRACE if logPath is set.
log string

// TF_LOG_PATH environment variable
logPath string

versionLock sync.Mutex
Expand Down Expand Up @@ -122,10 +127,29 @@ func (tf *Terraform) SetStderr(w io.Writer) {
tf.stderr = w
}

// SetLog sets the TF_LOG environment variable for Terraform CLI execution.
// This must be combined with a call to SetLogPath to take effect.
//
// This is only compatible with Terraform CLI 0.15.0 or later as setting the
// log level was unreliable in earlier versions. It will default to TRACE for
// those earlier versions when SetLogPath is called.
func (tf *Terraform) SetLog(log string) error {
err := tf.compatible(context.Background(), tf0_15_0, nil)
if err != nil {
return err
}
tf.log = log
return nil
}

// SetLogPath sets the TF_LOG_PATH environment variable for Terraform CLI
// execution.
func (tf *Terraform) SetLogPath(path string) error {
tf.logPath = path
// Prevent setting the log path without enabling logging
if tf.log == "" {
tf.log = "TRACE"
}
return nil
}

Expand Down
211 changes: 211 additions & 0 deletions tfexec/terraform_test.go
Expand Up @@ -5,6 +5,8 @@ import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"testing"

"github.com/hashicorp/terraform-exec/tfexec/internal/testutil"
Expand Down Expand Up @@ -61,6 +63,215 @@ func TestSetEnv(t *testing.T) {
}
}

func TestSetLog(t *testing.T) {
td := t.TempDir()

tf, err := NewTerraform(td, tfVersion(t, testutil.Latest_v1))

if err != nil {
t.Fatalf("unexpected NewTerraform error: %s", err)
}

// Required so all testing environment variables are not copied.
err = tf.SetEnv(map[string]string{
"CLEARENV": "1",
})

if err != nil {
t.Fatalf("unexpected SetEnv error: %s", err)
}

t.Run("case 1: SetLog <= 0.15 error", func(t *testing.T) {
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
t.Skip("Terraform for darwin/arm64 is not available until v1")
}

td012 := t.TempDir()

tf012, err := NewTerraform(td012, tfVersion(t, testutil.Latest012))

if err != nil {
t.Fatalf("unexpected NewTerraform error: %s", err)
}

err = tf012.SetLog("TRACE")

if err == nil {
t.Fatal("expected SetLog error, got none")
}
})

t.Run("case 2: SetLog TRACE no SetLogPath", func(t *testing.T) {
err := tf.SetLog("TRACE")

if err != nil {
t.Fatalf("unexpected SetLog error: %s", err)
}

initCmd, err := tf.initCmd(context.Background())

if err != nil {
t.Fatalf("unexpected command error: %s", err)
}

assertCmd(t, []string{
"init",
"-no-color",
"-force-copy",
"-input=false",
"-backend=true",
"-get=true",
"-upgrade=false",
}, map[string]string{
"CLEARENV": "1",
"TF_LOG": "",
}, initCmd)
})

t.Run("case 3: SetLog TRACE and SetLogPath", func(t *testing.T) {
tfLogPath := filepath.Join(td, "test.log")

err := tf.SetLog("TRACE")

if err != nil {
t.Fatalf("unexpected SetLog error: %s", err)
}

err = tf.SetLogPath(tfLogPath)

if err != nil {
t.Fatalf("unexpected SetLogPath error: %s", err)
}

initCmd, err := tf.initCmd(context.Background())

if err != nil {
t.Fatalf("unexpected command error: %s", err)
}

assertCmd(t, []string{
"init",
"-no-color",
"-force-copy",
"-input=false",
"-backend=true",
"-get=true",
"-upgrade=false",
}, map[string]string{
"CLEARENV": "1",
"TF_LOG": "TRACE",
"TF_LOG_PATH": tfLogPath,
}, initCmd)
})

t.Run("case 4: SetLog DEBUG and SetLogPath", func(t *testing.T) {
tfLogPath := filepath.Join(td, "test.log")

err := tf.SetLog("DEBUG")

if err != nil {
t.Fatalf("unexpected SetLog error: %s", err)
}

err = tf.SetLogPath(tfLogPath)

if err != nil {
t.Fatalf("unexpected SetLogPath error: %s", err)
}

initCmd, err := tf.initCmd(context.Background())

if err != nil {
t.Fatalf("unexpected command error: %s", err)
}

assertCmd(t, []string{
"init",
"-no-color",
"-force-copy",
"-input=false",
"-backend=true",
"-get=true",
"-upgrade=false",
}, map[string]string{
"CLEARENV": "1",
"TF_LOG": "DEBUG",
"TF_LOG_PATH": tfLogPath,
}, initCmd)
})
}

func TestSetLogPath(t *testing.T) {
td := t.TempDir()

tf, err := NewTerraform(td, tfVersion(t, testutil.Latest_v1))

if err != nil {
t.Fatalf("unexpected NewTerraform error: %s", err)
}

// Required so all testing environment variables are not copied.
err = tf.SetEnv(map[string]string{
"CLEARENV": "1",
})

if err != nil {
t.Fatalf("unexpected SetEnv error: %s", err)
}

t.Run("case 1: No SetLogPath", func(t *testing.T) {
initCmd, err := tf.initCmd(context.Background())

if err != nil {
t.Fatalf("unexpected command error: %s", err)
}

assertCmd(t, []string{
"init",
"-no-color",
"-force-copy",
"-input=false",
"-backend=true",
"-get=true",
"-upgrade=false",
}, map[string]string{
"CLEARENV": "1",
"TF_LOG": "",
"TF_LOG_PATH": "",
}, initCmd)
})

t.Run("case 2: SetLogPath sets TF_LOG and TF_LOG_PATH", func(t *testing.T) {
tfLogPath := filepath.Join(td, "test.log")

err = tf.SetLogPath(tfLogPath)

if err != nil {
t.Fatalf("unexpected SetLogPath error: %s", err)
}

initCmd, err := tf.initCmd(context.Background())

if err != nil {
t.Fatalf("unexpected command error: %s", err)
}

assertCmd(t, []string{
"init",
"-no-color",
"-force-copy",
"-input=false",
"-backend=true",
"-get=true",
"-upgrade=false",
}, map[string]string{
"CLEARENV": "1",
"TF_LOG": "TRACE",
"TF_LOG_PATH": tfLogPath,
}, initCmd)
})
}

func TestCheckpointDisablePropagation(t *testing.T) {
td := t.TempDir()

Expand Down

0 comments on commit 531dc1c

Please sign in to comment.