Skip to content

Commit

Permalink
Support TF_LOG_CORE and TF_LOG_PROVIDER environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
bflad committed Jul 1, 2022
1 parent 0a62475 commit fe4e8c4
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 18 deletions.
6 changes: 6 additions & 0 deletions internal/logging/keys.go
Expand Up @@ -34,6 +34,12 @@ const (
// The Terraform CLI logging level (TF_LOG) for used for an acceptance test.
KeyTestTerraformLogLevel = "test_terraform_log_level"

// The Terraform CLI logging level (TF_LOG_CORE) for used for an acceptance test.
KeyTestTerraformLogCoreLevel = "test_terraform_log_core_level"

// The Terraform CLI logging level (TF_LOG_PROVIDER) for used for an acceptance test.
KeyTestTerraformLogProviderLevel = "test_terraform_log_provider_level"

// The path to the Terraform CLI logging file used for an acceptance test.
//
// This should match where the rest of the acceptance test logs are going
Expand Down
53 changes: 43 additions & 10 deletions internal/plugintest/environment_variables.go
Expand Up @@ -10,6 +10,24 @@ const (
// CLI installation, if installation is required.
EnvTfAccTempDir = "TF_ACC_TEMP_DIR"

// Environment variable with level to filter Terraform logs during
// acceptance testing. This value sets TF_LOG in a safe manner when
// executing Terraform CLI commands, which would otherwise interfere
// with the testing framework using TF_LOG to set the Go standard library
// log package level.
//
// This value takes precedence over TF_LOG_CORE, due to precedence rules
// in the Terraform core code, so it is not possible to set this to a level
// and also TF_LOG_CORE=OFF. Use TF_LOG_CORE and TF_LOG_PROVIDER in that
// case instead.
//
// If not set, but TF_ACC_LOG_PATH or TF_LOG_PATH_MASK is set, it defaults
// to TRACE. If Terraform CLI is version 0.14 or earlier, it will have no
// separate affect from the TF_ACC_LOG_PATH or TF_LOG_PATH_MASK behavior,
// as those earlier versions of Terraform are unreliable with the logging
// level being outside TRACE.
EnvTfAccLog = "TF_ACC_LOG"

// Environment variable with path to save Terraform logs during acceptance
// testing. This value sets TF_LOG_PATH in a safe manner when executing
// Terraform CLI commands, which would otherwise be ignored since it could
Expand All @@ -18,17 +36,16 @@ const (
// If TF_LOG_PATH_MASK is set, it takes precedence over this value.
EnvTfAccLogPath = "TF_ACC_LOG_PATH"

// Environment variable with level to filter Terraform logs during
// acceptance testing. This value sets TF_LOG in a safe manner when
// executing Terraform CLI commands, which would otherwise be ignored
// since it could interfere with how the underlying execution is performed.
// Environment variable with level to filter Terraform core logs during
// acceptance testing. This value sets TF_LOG_CORE separate from
// TF_LOG_PROVIDER when calling Terraform.
//
// If not set, but TF_ACC_LOG_PATH or TF_LOG_PATH_MASK is set, it defaults
// to TRACE. If Terraform CLI is version 0.14 or earlier, it will have no
// separate affect from the TF_ACC_LOG_PATH or TF_LOG_PATH_MASK behavior,
// as those earlier versions of Terraform are unreliable with the logging
// level being outside TRACE.
EnvTfLog = "TF_LOG"
// This value has no affect when TF_ACC_LOG is set (which sets Terraform's
// TF_LOG), due to precedence rules in the Terraform core code. Use
// TF_LOG_CORE and TF_LOG_PROVIDER in that case instead.
//
// If not set, defaults to TF_ACC_LOG behaviors.
EnvTfLogCore = "TF_LOG_CORE"

// Environment variable with path containing the string %s, which is
// replaced with the test name, to save separate Terraform logs during
Expand All @@ -39,6 +56,22 @@ const (
// Takes precedence over TF_ACC_LOG_PATH.
EnvTfLogPathMask = "TF_LOG_PATH_MASK"

// Environment variable with level to filter Terraform provider logs during
// acceptance testing. This value sets TF_LOG_PROVIDER separate from
// TF_LOG_CORE.
//
// During testing, this only affects external providers whose logging goes
// through Terraform. The logging for the provider under test is controlled
// by the testing framework as it is running the provider code. Provider
// code using the Go standard library log package is controlled by TF_LOG
// for historical compatibility.
//
// This value takes precedence over TF_ACC_LOG for external provider logs,
// due to rules in the Terraform core code.
//
// If not set, defaults to TF_ACC_LOG behaviors.
EnvTfLogProvider = "TF_LOG_PROVIDER"

// Environment variable with acceptance testing Terraform CLI version to
// download from releases.hashicorp.com, checksum verify, and install. The
// value can be any valid Terraform CLI version, such as 1.1.6, with or
Expand Down
79 changes: 71 additions & 8 deletions internal/plugintest/helper.go
Expand Up @@ -139,39 +139,102 @@ func (h *Helper) NewWorkingDir(ctx context.Context, t TestControl) (*WorkingDir,
return nil, fmt.Errorf("unable to disable terraform-exec provider verification: %w", err)
}

if tfLog := os.Getenv(EnvTfLog); tfLog != "" {
tfAccLog := os.Getenv(EnvTfAccLog)
tfAccLogPath := os.Getenv(EnvTfAccLogPath)
tfLogCore := os.Getenv(EnvTfLogCore)
tfLogPathMask := os.Getenv(EnvTfLogPathMask)
tfLogProvider := os.Getenv(EnvTfLogProvider)

if tfAccLog != "" && tfLogCore != "" {
err = fmt.Errorf(
"Invalid environment variable configuration. Cannot set both TF_ACC_LOG and TF_LOG_CORE. " +
"Use TF_LOG_CORE and TF_LOG_PROVIDER to separately control the Terraform CLI logging subsystems. " +
"To control the Go standard library log package for the provider under test, use TF_LOG.",
)
logging.HelperResourceError(ctx, err.Error())
return nil, err
}

if tfAccLog != "" {
logging.HelperResourceTrace(
ctx,
fmt.Sprintf("Setting terraform-exec log level via %s environment variable, if Terraform CLI is version 0.15 or later", EnvTfLog),
map[string]interface{}{logging.KeyTestTerraformLogLevel: tfLog},
fmt.Sprintf("Setting terraform-exec log level via %s environment variable, if Terraform CLI is version 0.15 or later", EnvTfAccLog),
map[string]interface{}{logging.KeyTestTerraformLogLevel: tfAccLog},
)

err := tf.SetLog(tfLog)
err := tf.SetLog(tfAccLog)

if err != nil {
if !errors.As(err, new(*tfexec.ErrVersionMismatch)) {
return nil, fmt.Errorf("unable to set terraform-exec log level (%s): %w", tfLog, err)
logging.HelperResourceError(
ctx,
"Unable to set terraform-exec log level",
map[string]interface{}{logging.KeyError: err.Error()},
)
return nil, fmt.Errorf("unable to set terraform-exec log level (%s): %w", tfAccLog, err)
}

logging.HelperResourceWarn(
ctx,
fmt.Sprintf("Unable to set terraform-exec log level via %s environment variable, as Terraform CLI is version 0.14 or earlier. It will default to TRACE.", EnvTfLog),
fmt.Sprintf("Unable to set terraform-exec log level via %s environment variable, as Terraform CLI is version 0.14 or earlier. It will default to TRACE.", EnvTfAccLog),
map[string]interface{}{logging.KeyTestTerraformLogLevel: "TRACE"},
)
}
}

if tfLogCore != "" {
logging.HelperResourceTrace(
ctx,
fmt.Sprintf("Setting terraform-exec core log level via %s environment variable, if Terraform CLI is version 0.15 or later", EnvTfLogCore),
map[string]interface{}{
logging.KeyTestTerraformLogCoreLevel: tfLogCore,
},
)

err := tf.SetLogCore(tfLogCore)

if err != nil {
logging.HelperResourceError(
ctx,
"Unable to set terraform-exec core log level",
map[string]interface{}{logging.KeyError: err.Error()},
)
return nil, fmt.Errorf("unable to set terraform-exec core log level (%s): %w", tfLogCore, err)
}
}

if tfLogProvider != "" {
logging.HelperResourceTrace(
ctx,
fmt.Sprintf("Setting terraform-exec provider log level via %s environment variable, if Terraform CLI is version 0.15 or later", EnvTfLogProvider),
map[string]interface{}{
logging.KeyTestTerraformLogCoreLevel: tfLogProvider,
},
)

err := tf.SetLogProvider(tfLogProvider)

if err != nil {
logging.HelperResourceError(
ctx,
"Unable to set terraform-exec provider log level",
map[string]interface{}{logging.KeyError: err.Error()},
)
return nil, fmt.Errorf("unable to set terraform-exec provider log level (%s): %w", tfLogProvider, err)
}
}

var logPath, logPathEnvVar string

if tfAccLogPath := os.Getenv(EnvTfAccLogPath); tfAccLogPath != "" {
if tfAccLogPath != "" {
logPath = tfAccLogPath
logPathEnvVar = EnvTfAccLogPath
}

// Similar to helper/logging.LogOutput() and
// terraform-plugin-log/tfsdklog.RegisterTestSink(), the TF_LOG_PATH_MASK
// environment variable should take precedence over TF_ACC_LOG_PATH.
if tfLogPathMask := os.Getenv(EnvTfLogPathMask); tfLogPathMask != "" {
if tfLogPathMask != "" {
// Escape special characters which may appear if we have subtests
testName := strings.Replace(t.Name(), "/", "__", -1)
logPath = fmt.Sprintf(tfLogPathMask, testName)
Expand Down

0 comments on commit fe4e8c4

Please sign in to comment.