From 551e48714e25bc4c4f5771396198e1b336860efb Mon Sep 17 00:00:00 2001 From: Lakshmi Javadekar <103459615+lakshmimsft@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:15:51 -0800 Subject: [PATCH] Updating environment variables set at Terraform process to include env variables at current process (#7272) # Description We encountered an issue with the current implementation of setting environment variables at the Terraform process. The environment variables set at current process are not copied over by default when calling tf.SetEnv(). We're updating code to include environment variables set at current process and then apply them to the Terraform process. ## Type of change - This pull request fixes a bug in Radius and has an approved issue #7269 . Fixes: #7269 --- pkg/recipes/terraform/execute.go | 37 ++++++++++---- pkg/recipes/terraform/execute_test.go | 51 ++++++++++++++++++- .../corerp-resources-terraform-redis.bicep | 6 +++ 3 files changed, 84 insertions(+), 10 deletions(-) diff --git a/pkg/recipes/terraform/execute.go b/pkg/recipes/terraform/execute.go index 18cf5c1f90..232b548bda 100644 --- a/pkg/recipes/terraform/execute.go +++ b/pkg/recipes/terraform/execute.go @@ -20,11 +20,14 @@ import ( "context" "errors" "fmt" + "os" + "strings" "time" install "github.com/hashicorp/hc-install" "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" + "github.com/radius-project/radius/pkg/corerp/datamodel" "github.com/radius-project/radius/pkg/metrics" "github.com/radius-project/radius/pkg/recipes" "github.com/radius-project/radius/pkg/recipes/recipecontext" @@ -88,10 +91,12 @@ func (e *executor) Deploy(ctx context.Context, options Options) (*tfjson.State, return nil, err } - // Set environment variables for the Terraform process. - err = e.setEnvironmentVariables(ctx, tf, options.EnvConfig) - if err != nil { - return nil, err + if options.EnvConfig != nil { + // Set environment variables for the Terraform process. + err = e.setEnvironmentVariables(ctx, tf, &options.EnvConfig.RecipeConfig) + if err != nil { + return nil, err + } } // Run TF Init and Apply in the working directory @@ -200,13 +205,14 @@ func (e *executor) GetRecipeMetadata(ctx context.Context, options Options) (map[ }, nil } -// setEnvironmentVariables sets environment variables for the Terraform process by reading values from the environment configuration. +// setEnvironmentVariables sets environment variables for the Terraform process by reading values from the recipe configuration. // Terraform process will use environment variables as input for the recipe deployment. -func (e executor) setEnvironmentVariables(ctx context.Context, tf *tfexec.Terraform, envConfig *recipes.Configuration) error { - if envConfig != nil && envConfig.RecipeConfig.Env.AdditionalProperties != nil { - envVars := map[string]string{} +func (e executor) setEnvironmentVariables(ctx context.Context, tf *tfexec.Terraform, recipeConfig *datamodel.RecipeConfigProperties) error { + if recipeConfig != nil && recipeConfig.Env.AdditionalProperties != nil && len(recipeConfig.Env.AdditionalProperties) > 0 { + // populate envVars with the environment variables from current process + envVars := splitEnvVar(os.Environ()) - for key, value := range envConfig.RecipeConfig.Env.AdditionalProperties { + for key, value := range recipeConfig.Env.AdditionalProperties { envVars[key] = value } @@ -218,6 +224,19 @@ func (e executor) setEnvironmentVariables(ctx context.Context, tf *tfexec.Terraf return nil } +// splitEnvVar splits a slice of environment variables into a map of keys and values. +func splitEnvVar(envVars []string) map[string]string { + parsedEnvVars := make(map[string]string) + for _, item := range envVars { + splits := strings.SplitN(item, "=", 2) // Split on the first "=" + if len(splits) == 2 { + parsedEnvVars[splits[0]] = splits[1] + } + } + + return parsedEnvVars +} + // generateConfig generates Terraform configuration with required inputs for the module, providers and backend to be initialized and applied. func (e *executor) generateConfig(ctx context.Context, tf *tfexec.Terraform, options Options) (string, error) { logger := ucplog.FromContextOrDiscard(ctx) diff --git a/pkg/recipes/terraform/execute_test.go b/pkg/recipes/terraform/execute_test.go index 431cbf3679..2baa02243d 100644 --- a/pkg/recipes/terraform/execute_test.go +++ b/pkg/recipes/terraform/execute_test.go @@ -18,6 +18,7 @@ package terraform import ( "path/filepath" + reflect "reflect" "testing" "github.com/hashicorp/terraform-exec/tfexec" @@ -168,8 +169,56 @@ func TestSetEnvironmentVariables(t *testing.T) { e := executor{} - err = e.setEnvironmentVariables(ctx, tf, tc.opts.EnvConfig) + err = e.setEnvironmentVariables(ctx, tf, &tc.opts.EnvConfig.RecipeConfig) + require.NoError(t, err) }) } } + +func TestSplitEnvVar(t *testing.T) { + tests := []struct { + name string + envVars []string + want map[string]string + }{ + { + name: "nil input", + envVars: nil, + want: map[string]string{}, + }, + { + name: "empty input", + envVars: []string{}, + want: map[string]string{}, + }, + { + name: "single variable", + envVars: []string{"VAR1=value1"}, + want: map[string]string{"VAR1": "value1"}, + }, + { + name: "multiple variables", + envVars: []string{"VAR1=value1", "VAR2=value2"}, + want: map[string]string{"VAR1": "value1", "VAR2": "value2"}, + }, + { + name: "variable with no value", + envVars: []string{"VAR1="}, + want: map[string]string{"VAR1": ""}, + }, + { + name: "variable with equals sign in value", + envVars: []string{"VAR1=value1=value2"}, + want: map[string]string{"VAR1": "value1=value2"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := splitEnvVar(tt.envVars); !reflect.DeepEqual(got, tt.want) { + t.Errorf("splitEnvVar() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/test/functional/shared/resources/testdata/corerp-resources-terraform-redis.bicep b/test/functional/shared/resources/testdata/corerp-resources-terraform-redis.bicep index e810ade66d..a5619fd0af 100644 --- a/test/functional/shared/resources/testdata/corerp-resources-terraform-redis.bicep +++ b/test/functional/shared/resources/testdata/corerp-resources-terraform-redis.bicep @@ -17,6 +17,12 @@ resource env 'Applications.Core/environments@2023-10-01-preview' = { resourceId: 'self' namespace: 'corerp-resources-terraform-redis-env' } + recipeConfig: { + env: { + MY_ENV_VAR_1: 'env-var-value-1' + MY_ENV_VAR_2: 'env-var-value-2' + } + } recipes: { 'Applications.Core/extenders': { default: {