Skip to content

Commit

Permalink
Updating environment variables set at Terraform process to include en…
Browse files Browse the repository at this point in the history
…v variables at current process (radius-project#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
radius-project#7269 .

Fixes: radius-project#7269
  • Loading branch information
lakshmimsft authored Mar 8, 2024
1 parent 3baec57 commit 551e487
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 10 deletions.
37 changes: 28 additions & 9 deletions pkg/recipes/terraform/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand All @@ -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)
Expand Down
51 changes: 50 additions & 1 deletion pkg/recipes/terraform/execute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package terraform

import (
"path/filepath"
reflect "reflect"
"testing"

"github.com/hashicorp/terraform-exec/tfexec"
Expand Down Expand Up @@ -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)
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down

0 comments on commit 551e487

Please sign in to comment.