Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tfexec: Add (Terraform).SetLog() method #291

Merged
merged 4 commits into from
May 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions tfexec/cmd.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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