diff --git a/internal/logging/keys.go b/internal/logging/keys.go index cb0e9876ed..593c2c2f41 100644 --- a/internal/logging/keys.go +++ b/internal/logging/keys.go @@ -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 diff --git a/internal/plugintest/environment_variables.go b/internal/plugintest/environment_variables.go index 6aaf205cba..6fd001a07d 100644 --- a/internal/plugintest/environment_variables.go +++ b/internal/plugintest/environment_variables.go @@ -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 @@ -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 @@ -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 diff --git a/internal/plugintest/helper.go b/internal/plugintest/helper.go index d3daa69609..0411eae0a8 100644 --- a/internal/plugintest/helper.go +++ b/internal/plugintest/helper.go @@ -139,31 +139,94 @@ 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 } @@ -171,7 +234,7 @@ func (h *Helper) NewWorkingDir(ctx context.Context, t TestControl) (*WorkingDir, // 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)