Skip to content

Commit

Permalink
Generate and write terraform config in JSON format
Browse files Browse the repository at this point in the history
  • Loading branch information
kachawla committed May 8, 2023
1 parent 4a5fb32 commit 161b71e
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 15 deletions.

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions pkg/corerp/api/v20220315privatepreview/environment_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ func (dst *EnvironmentResource) ConvertFrom(src v1.DataModelInterface) error {
recipes[resourceType] = map[string]*EnvironmentRecipeProperties{}
for recipeName, recipeDetails := range recipe {
recipes[resourceType][recipeName] = &EnvironmentRecipeProperties{
TemplateKind: to.Ptr(recipeDetails.TemplateKind),
TemplatePath: to.Ptr(recipeDetails.TemplatePath),
Parameters: recipeDetails.Parameters,
}
}

// if recipeDetails.TemplateKind != "" {
// recipes[key].TemplateKind = to.Ptr(recipeDetails.TemplateKind)
// }
// Temporary check until until we make templateKind required.
if recipeDetails.TemplateKind != "" {
recipes[resourceType][recipeName].TemplateKind = to.Ptr(recipeDetails.TemplateKind)
}
}
}
dst.Properties.Recipes = recipes
}
Expand Down
3 changes: 1 addition & 2 deletions pkg/recipes/configloader/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ func getRecipeDefinition(environment *v20220315privatepreview.EnvironmentResourc
}

return &recipes.Definition{
Driver: recipes.DriverBicep,
// Driver: *found.TemplateKind,
Driver: *found.TemplateKind,
ResourceType: resource.Type(),
Parameters: found.Parameters,
TemplatePath: *found.TemplatePath,
Expand Down
2 changes: 2 additions & 0 deletions pkg/recipes/configloader/environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,14 @@ func TestGetRecipeDefinition(t *testing.T) {
Recipes: map[string]map[string]*model.EnvironmentRecipeProperties{
"Applications.Link/mongoDatabases": {
"cosmosDB": {
TemplateKind: to.Ptr(recipes.TemplateKindBicep),
TemplatePath: to.Ptr("radiusdev.azurecr.io/recipes/mongodatabases/azure:1.0"),
Parameters: map[string]any{
"foo": "bar",
},
},
"default": {
TemplateKind: to.Ptr(recipes.TemplateKindBicep),
TemplatePath: to.Ptr("radiusdev.azurecr.io/recipes/mongoDefault/azure:1.0"),
},
},
Expand Down
91 changes: 84 additions & 7 deletions pkg/recipes/driver/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package driver

import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -35,14 +36,30 @@ func NewTerraformDriver() Driver {
type terraformDriver struct {
}

func (d *terraformDriver) Execute(ctx context.Context, configuration recipes.Configuration, recipe recipes.Metadata, definition recipes.Definition) (*recipes.RecipeOutput, error) {
type TerraformConfig struct {
Terraform TerraformDefinition `json:"terraform"`
Provider map[string]interface{} `json:"provider"`
Module map[string]ModuleData `json:"module"`
}

type TerraformDefinition struct {
RequiredProviders map[string]interface{} `json:"required_providers"`
RequiredVersion string `json:"required_version"`
}

type ModuleData struct {
Source string `json:"source"`
Version string `json:"version"`
}

func (d *terraformDriver) Execute(ctx context.Context, configuration recipes.Configuration, recipe recipes.Metadata, definition recipes.Definition) (recipeOutput *recipes.RecipeOutput, err error) {
logger := logr.FromContextOrDiscard(ctx)
logger.Info(fmt.Sprintf("Deploying recipe: %q, template: %q", recipe.Name, definition.TemplatePath))

// Create Terraform installation directory
installDir := filepath.Join(installDirRoot, util.NormalizeStringToLower(recipe.ResourceID), uuid.NewString())
logger.Info(fmt.Sprintf("Creating Terraform install directory: %q", installDir))
if err := os.MkdirAll(installDir, os.ModePerm); err != nil {
if err = os.MkdirAll(installDir, os.ModePerm); err != nil {
return nil, fmt.Errorf("failed to create directory for terraform installation for resource %q: %w", recipe.ResourceID, err)
}
defer os.RemoveAll(installDir)
Expand All @@ -60,21 +77,28 @@ func (d *terraformDriver) Execute(ctx context.Context, configuration recipes.Con
return nil, fmt.Errorf("failed to install terraform for resource %q: %w", recipe.ResourceID, err)
}
// TODO check if anything else is needed to clean up Terraform installation.
defer installer.Remove(ctx)
defer func() {
err = installer.Remove(ctx)
}()

// Create working directory for Terraform execution
workingDir := filepath.Join(workingDirRoot, util.NormalizeStringToLower(recipe.ResourceID), uuid.NewString())
logger.Info(fmt.Sprintf("Creating Terraform working directory: %q", workingDir))
if err := os.Mkdir(workingDir, os.ModePerm); err != nil {
if err = os.Mkdir(workingDir, os.ModePerm); err != nil {
return nil, fmt.Errorf("failed to create working directory for terraform execution for Radius resource %q. %w", recipe.ResourceID, err)
}
defer os.RemoveAll(workingDir)

if err := d.initAndApply(ctx, recipe.ResourceID, workingDir, execPath); err != nil {
return nil, err
// Generate Terraform json config in the working directory
if err = d.generateJsonConfig(ctx, workingDir, recipe.Name, definition.TemplatePath); err != nil {
return
}

return &recipes.RecipeOutput{}, nil
if err = d.initAndApply(ctx, recipe.ResourceID, workingDir, execPath); err != nil {
return
}

return
}

// Runs Terraform init and apply in the provided working directory.
Expand All @@ -96,3 +120,56 @@ func (d *terraformDriver) initAndApply(ctx context.Context, resourceID, workingD

return nil
}

// Generate Terraform configuration in JSON format for required providers and modules
// and write it to a file in the specified working directory.
// This JSON configuration is needed to initialize and apply Terraform modules.
// See https://www.terraform.io/docs/language/syntax/json.html for more information
// on the JSON syntax for Terraform configuration.
// templatePath is the path to the Terraform module source, e.g. "Azure/cosmosdb/azurerm".
func (d *terraformDriver) generateJsonConfig(ctx context.Context, workingDir, recipeName, templatePath string) error {
// TODO hardcoding provider data until we implement a way to pass this in.
tfConfig := TerraformConfig{
Terraform: TerraformDefinition{
RequiredProviders: map[string]interface{}{
"azurerm": map[string]interface{}{
"source": "hashicorp/azurerm",
"version": "~> 3.0.2",
},
},
RequiredVersion: ">= 1.1.0",
},
Provider: map[string]interface{}{
"azurerm": map[string]interface{}{
"features": map[string]interface{}{},
},
},
Module: map[string]ModuleData{
recipeName: {
Source: templatePath,
Version: "1.0.0", // TODO determine how to pass this in.
},
},
}

// Convert the Terraform config to JSON
jsonData, err := json.MarshalIndent(tfConfig, "", " ")
if err != nil {
return fmt.Errorf("Error marshalling JSON: %w", err)
}

// Write the JSON data to a file in the working directory
configFilePath := fmt.Sprintf("%s/main.json", workingDir)
file, err := os.Create(configFilePath)
if err != nil {
return fmt.Errorf("Error creating file: %w", err)
}
defer file.Close()

_, err = file.Write(jsonData)
if err != nil {
return fmt.Errorf("Error writing to file: %w", err)
}

return nil
}

0 comments on commit 161b71e

Please sign in to comment.