Skip to content

Commit

Permalink
initial update
Browse files Browse the repository at this point in the history
  • Loading branch information
lakshmimsft committed Mar 22, 2024
1 parent 1a06994 commit dea0923
Show file tree
Hide file tree
Showing 13 changed files with 283 additions and 49 deletions.
47 changes: 32 additions & 15 deletions pkg/recipes/terraform/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package config

import (
"bytes"
"context"
"encoding/json"
"errors"
Expand Down Expand Up @@ -96,14 +97,22 @@ func (cfg *TerraformConfig) Save(ctx context.Context, workingDir string) error {
// JSON configuration syntax for Terraform requires the file to be named with .tf.json suffix.
// https://developer.hashicorp.com/terraform/language/syntax/json

// Convert the Terraform config to JSON
jsonData, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
// Create a buffer to write the JSON to
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
enc.SetIndent("", " ")

// Encode the Terraform config to JSON
if err := enc.Encode(cfg); err != nil {
return fmt.Errorf("error marshalling JSON: %w", err)
}

// Remove trailing newline
jsonData := strings.TrimSuffix(buf.String(), "\n")

logger.Info(fmt.Sprintf("Writing Terraform JSON config to file: %s", getMainConfigFilePath(workingDir)))
if err = os.WriteFile(getMainConfigFilePath(workingDir), jsonData, modeConfigFile); err != nil {
if err := os.WriteFile(getMainConfigFilePath(workingDir), []byte(jsonData), modeConfigFile); err != nil {
return fmt.Errorf("error creating file: %w", err)
}
return nil
Expand All @@ -113,7 +122,7 @@ func (cfg *TerraformConfig) Save(ctx context.Context, workingDir string) error {
// by Radius to generate custom provider configurations. Save() must be called to save
// the generated providers config. requiredProviders contains a list of provider names
// that are required for the module.
func (cfg *TerraformConfig) AddProviders(ctx context.Context, requiredProviders []string, ucpConfiguredProviders map[string]providers.Provider, envConfig *recipes.Configuration) error {
func (cfg *TerraformConfig) AddProviders(ctx context.Context, requiredProviders map[string]*RequiredProviderInfo, ucpConfiguredProviders map[string]providers.Provider, envConfig *recipes.Configuration) error {
providerConfigs, err := getProviderConfigs(ctx, requiredProviders, ucpConfiguredProviders, envConfig)
if err != nil {
return err
Expand Down Expand Up @@ -167,19 +176,24 @@ func newModuleConfig(moduleSource string, moduleVersion string, params ...Recipe

// getProviderConfigs generates the Terraform provider configurations. This is built from a combination of environment level recipe configuration for
// providers and the provider configurations registered with UCP. The environment level recipe configuration for providers takes precedence over UCP provider configurations.
func getProviderConfigs(ctx context.Context, requiredProviders []string, ucpConfiguredProviders map[string]providers.Provider, envConfig *recipes.Configuration) (map[string]any, error) {
func getProviderConfigs(ctx context.Context, requiredProviders map[string]*RequiredProviderInfo, ucpConfiguredProviders map[string]providers.Provider, envConfig *recipes.Configuration) (map[string]any, error) {
// Get recipe provider configurations from the environment configuration
providerConfigs := providers.GetRecipeProviderConfigs(ctx, envConfig)

// Build provider configurations for required providers excluding the ones already present in providerConfigs
for _, provider := range requiredProviders {
if _, ok := providerConfigs[provider]; ok {
// Environment level recipe configuration for providers will take precedence over
// UCP provider configuration (currently these include azurerm, aws, kubernetes providers)
for providerName := range requiredProviders {
if _, ok := ucpConfiguredProviders[providerName]; ok { // requiredProviders can contain providers not configured with UCP
if _, ok := providerConfigs[providerName]; ok {
// Environment level recipe configuration for providers will take precedence over
// UCP provider configuration (currently these include azurerm, aws, kubernetes providers)
continue
}
} else {
// If the provider under required_providers is not configured with UCP, skip this iteration.
continue
}

builder, ok := ucpConfiguredProviders[provider]
builder, ok := ucpConfiguredProviders[providerName]
if !ok {
// No-op: For any other provider under required_providers, Radius doesn't generate any custom configuration.
continue
Expand All @@ -190,23 +204,26 @@ func getProviderConfigs(ctx context.Context, requiredProviders []string, ucpConf
return nil, err
}
if len(config) > 0 {
providerConfigs[provider] = config
providerConfigs[providerName] = config
}
}

return providerConfigs, nil
}

// AddTerraformBackend adds backend configurations to store Terraform state file for the deployment.
// AddTerraformInfrastructure adds backend configurations to store Terraform state file for the deployment.
// It also sets the required providers for the Terraform configuration.
// Save() must be called to save the generated backend config.
// Currently, the supported backend for Terraform Recipes is Kubernetes secret. https://developer.hashicorp.com/terraform/language/settings/backends/kubernetes
func (cfg *TerraformConfig) AddTerraformBackend(resourceRecipe *recipes.ResourceMetadata, backend backends.Backend) (map[string]any, error) {
func (cfg *TerraformConfig) AddTerraformInfrastructure(resourceRecipe *recipes.ResourceMetadata, backend backends.Backend, requiredProviders map[string]*RequiredProviderInfo) (map[string]any, error) {
backendConfig, err := backend.BuildBackend(resourceRecipe)
if err != nil {
return nil, err
}

cfg.Terraform = &TerraformDefinition{
Backend: backendConfig,
Backend: backendConfig,
RequiredProviders: requiredProviders,
}

return backendConfig, nil
Expand Down
62 changes: 40 additions & 22 deletions pkg/recipes/terraform/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ func Test_AddProviders(t *testing.T) {
configTests := []struct {
desc string
envConfig recipes.Configuration
requiredProviders []string
requiredProviders map[string]*RequiredProviderInfo
expectedUCPConfiguredProviders []map[string]any
expectedConfigFile string
Err error
Expand Down Expand Up @@ -374,11 +374,11 @@ func Test_AddProviders(t *testing.T) {
},
},
},
requiredProviders: []string{
providers.AWSProviderName,
providers.AzureProviderName,
providers.KubernetesProviderName,
"sql",
requiredProviders: map[string]*RequiredProviderInfo{
providers.AWSProviderName: {},
providers.AzureProviderName: {},
providers.KubernetesProviderName: {},
"sql": {},
},
expectedConfigFile: "testdata/providers-valid.tf.json",
},
Expand All @@ -393,8 +393,11 @@ func Test_AddProviders(t *testing.T) {
},
},
},
requiredProviders: []string{
providers.AWSProviderName,
requiredProviders: map[string]*RequiredProviderInfo{
providers.AWSProviderName: {
Source: "hashicorp/aws",
Version: []string{">= 3.0"},
},
},
},
{
Expand All @@ -404,8 +407,11 @@ func Test_AddProviders(t *testing.T) {
},
Err: nil,
envConfig: recipes.Configuration{},
requiredProviders: []string{
providers.AWSProviderName,
requiredProviders: map[string]*RequiredProviderInfo{
providers.AWSProviderName: {
Source: "hashicorp/aws",
Version: []string{">= 3.0"},
},
},
expectedConfigFile: "testdata/providers-empty.tf.json",
},
Expand All @@ -425,8 +431,11 @@ func Test_AddProviders(t *testing.T) {
},
},
},
requiredProviders: []string{
providers.AWSProviderName,
requiredProviders: map[string]*RequiredProviderInfo{
providers.AWSProviderName: {
Source: "hashicorp/aws",
Version: []string{">= 3.0"},
},
},
expectedConfigFile: "testdata/providers-empty.tf.json",
},
Expand All @@ -439,8 +448,11 @@ func Test_AddProviders(t *testing.T) {
},
Err: nil,
envConfig: recipes.Configuration{},
requiredProviders: []string{
providers.AzureProviderName,
requiredProviders: map[string]*RequiredProviderInfo{
providers.AzureProviderName: {
Source: "hashicorp/azurerm",
Version: []string{">= 2.0"},
},
},
expectedConfigFile: "testdata/providers-emptyazureconfig.tf.json",
},
Expand Down Expand Up @@ -502,9 +514,15 @@ func Test_AddProviders(t *testing.T) {
},
},
},
requiredProviders: []string{
providers.AWSProviderName,
providers.KubernetesProviderName,
requiredProviders: map[string]*RequiredProviderInfo{
providers.AWSProviderName: {
Source: "hashicorp/aws",
Version: []string{">= 3.0"},
},
providers.KubernetesProviderName: {
Source: "hashicorp/kubernetes",
Version: []string{">= 2.0"},
},
},
expectedConfigFile: "testdata/providers-overridereqproviders.tf.json",
},
Expand Down Expand Up @@ -545,7 +563,7 @@ func Test_AddProviders(t *testing.T) {
},
},
requiredProviders: nil,
expectedConfigFile: "testdata/providers-empty.tf.json",
expectedConfigFile: "testdata/providers-emptyproviders.tf.json",
},
{
desc: "recipe providers and tfconfigproperties not populated",
Expand All @@ -555,22 +573,22 @@ func Test_AddProviders(t *testing.T) {
RecipeConfig: datamodel.RecipeConfigProperties{},
},
requiredProviders: nil,
expectedConfigFile: "testdata/providers-empty.tf.json",
expectedConfigFile: "testdata/providers-emptyproviders.tf.json",
},
{
desc: "envConfig set to empty recipe config",
expectedUCPConfiguredProviders: nil,
Err: nil,
envConfig: recipes.Configuration{},
requiredProviders: nil,
expectedConfigFile: "testdata/providers-empty.tf.json",
expectedConfigFile: "testdata/providers-emptyproviders.tf.json",
},
{
desc: "envConfig not populated",
expectedUCPConfiguredProviders: nil,
Err: nil,
requiredProviders: nil,
expectedConfigFile: "testdata/providers-empty.tf.json",
expectedConfigFile: "testdata/providers-emptyproviders.tf.json",
},
}

Expand All @@ -594,7 +612,7 @@ func Test_AddProviders(t *testing.T) {
}
require.NoError(t, err)
mBackend.EXPECT().BuildBackend(&resourceRecipe).AnyTimes().Return(expectedBackend, nil)
_, err = tfconfig.AddTerraformBackend(&resourceRecipe, mBackend)
_, err = tfconfig.AddTerraformInfrastructure(&resourceRecipe, mBackend, tc.requiredProviders)
require.NoError(t, err)
err = tfconfig.Save(ctx, workingDir)
require.NoError(t, err)
Expand Down
8 changes: 8 additions & 0 deletions pkg/recipes/terraform/config/testdata/providers-empty.tf.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
"namespace": "radius-system",
"secret_suffix": "test-secret-suffix"
}
},
"required_providers": {
"aws": {
"source": "hashicorp/aws",
"version": [
">= 3.0"
]
}
}
},
"module": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
"namespace": "radius-system",
"secret_suffix": "test-secret-suffix"
}
},
"required_providers": {
"azurerm": {
"source": "hashicorp/azurerm",
"version": [
">= 2.0"
]
}
}
},
"provider": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"terraform": {
"backend": {
"kubernetes": {
"config_path": "/home/radius/.kube/config",
"namespace": "radius-system",
"secret_suffix": "test-secret-suffix"
}
}
},
"module": {
"redis-azure": {
"redis_cache_name": "redis-test",
"resource_group_name": "test-rg",
"sku": "P",
"source": "Azure/redis/azurerm",
"version": "1.1.0"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@
"namespace": "radius-system",
"secret_suffix": "test-secret-suffix"
}
},
"required_providers": {
"aws": {
"source": "hashicorp/aws",
"version": [
">= 3.0"
]
},
"kubernetes": {
"source": "hashicorp/kubernetes",
"version": [
">= 2.0"
]
}
}
},
"provider": {
Expand Down
6 changes: 6 additions & 0 deletions pkg/recipes/terraform/config/testdata/providers-valid.tf.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
"namespace": "radius-system",
"secret_suffix": "test-secret-suffix"
}
},
"required_providers": {
"aws": {},
"azurerm": {},
"kubernetes": {},
"sql": {}
}
},
"provider": {
Expand Down
10 changes: 10 additions & 0 deletions pkg/recipes/terraform/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,14 @@ type TerraformDefinition struct {
// Backend defines where Terraform stores its state.
// https://developer.hashicorp.com/terraform/language/state
Backend map[string]interface{} `json:"backend"`

// RequiredProviders is the list of required Terraform providers.
RequiredProviders map[string]*RequiredProviderInfo `json:"required_providers,omitempty"`
}

// RequiredProviderInfo represents details for a provider listed under the required_providers block in a Terraform module.
type RequiredProviderInfo struct {
Source string `json:"source,omitempty"` // The source of the provider.
Version []string `json:"version,omitempty"` // The version of the provider.
ConfigurationAliases []string `json:"configuration_aliases,omitempty"` // The configuration aliases for the provider.
}
2 changes: 1 addition & 1 deletion pkg/recipes/terraform/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ func (e *executor) generateConfig(ctx context.Context, tf *tfexec.Terraform, opt
return "", err
}

backendConfig, err := tfConfig.AddTerraformBackend(options.ResourceRecipe, backends.NewKubernetesBackend(e.k8sClientSet))
backendConfig, err := tfConfig.AddTerraformInfrastructure(options.ResourceRecipe, backends.NewKubernetesBackend(e.k8sClientSet), loadedModule.RequiredProviders)
if err != nil {
return "", err
}
Expand Down
Loading

0 comments on commit dea0923

Please sign in to comment.