Skip to content

Commit

Permalink
Fix an error when trying to load default varaibles
Browse files Browse the repository at this point in the history
I simply removed a lot of code that was handling error recovery. The new library we use returns the desired values even if there are error in the code.
  • Loading branch information
jocgir committed Nov 5, 2024
1 parent 7967ec1 commit 8c32d14
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 100 deletions.
103 changes: 5 additions & 98 deletions util/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@ package util
import (
"fmt"
"os"
"path"
"regexp"
"strings"
"sync"

"github.com/coveooss/gotemplate/v3/collections"
"github.com/coveooss/gotemplate/v3/json"
"github.com/coveooss/gotemplate/v3/template"
"github.com/coveooss/gotemplate/v3/utils"
"github.com/coveooss/gotemplate/v3/yaml"
"github.com/coveooss/multilogger"
"github.com/coveooss/multilogger/errors"
Expand All @@ -29,10 +26,7 @@ type (
}
)

var (
reTfVariables = regexp.MustCompile(`(?ms)^variable\s+".*?"\s+{(\s*\n.*?^}|\s*}$)`)
cacheDefault sync.Map
)
var cacheDefault sync.Map

// LoadDefaultValues returns a map of the variables defined in the tfvars file
func LoadDefaultValues(folder string, logger *multilogger.Logger, keepInCache bool) (importedVariables map[string]interface{}, allVariables map[string]*tfconfig.Variable, err error) {
Expand All @@ -51,7 +45,7 @@ func LoadDefaultValues(folder string, logger *multilogger.Logger, keepInCache bo
return cached.values, cached.all, nil
}

return loadDefaultValues(folder, true, logger)
return loadDefaultValues(folder, logger)
}

// LoadVariablesFromHcl parses HCL content to get a map of the attributes
Expand Down Expand Up @@ -162,9 +156,9 @@ func LoadVariablesFromSource(content, fileName, cwd string, applyTemplate bool,
return
}

func loadDefaultValues(folder string, retry bool, logger *multilogger.Logger) (map[string]interface{}, map[string]*tfconfig.Variable, error) {
func loadDefaultValues(folder string, logger *multilogger.Logger) (map[string]interface{}, map[string]*tfconfig.Variable, error) {
module, diags := tfconfig.LoadModule(folder)
if diags.HasErrors() {
if diags.HasErrors() && logger != nil {
var err errors.Array
for _, diag := range diags {
if diag.Pos != nil && diag.Pos.Filename != "" {
Expand All @@ -174,21 +168,8 @@ func loadDefaultValues(folder string, retry bool, logger *multilogger.Logger) (m
err = append(err, fmt.Errorf("(%c) %s, %s", diag.Severity, diag.Summary, diag.Detail))
}
}
if !retry {
return nil, nil, err
}

// If we get an error while loading variables, we try to isolate variables definition in a temporary folder.
if logger != nil {
logger.Debugf("First try with terraform native LoadConfigDir failed\n%v", color.HiBlackString(err.Error()))
logger.Debug("Retrying with a temporary folder containing only variables definitions")
}
tmpDir, errTmpDir := createTerraformVariablesTemporaryFolder(folder)
if tmpDir == "" || errTmpDir != nil {
return nil, nil, append(err, errTmpDir)
}
defer func() { os.RemoveAll(tmpDir) }()
return loadDefaultValues(tmpDir, false, logger)
logger.Debugf("Ignored error while trying to load the default variables:\n%v", color.HiBlackString(err.Error()))
}

variablesMap := map[string]interface{}{}
Expand All @@ -203,77 +184,3 @@ func loadDefaultValues(folder string, retry bool, logger *multilogger.Logger) (m
}
return variablesMap, module.Variables, nil
}

func isOverride(filename string) bool {
return path.Base(filename) == "override.tf" ||
path.Base(filename) == "override.tf.json" ||
strings.HasSuffix(filename, "_override.tf") ||
strings.HasSuffix(filename, "_override.tf.json")
}

// Create a temporary folder containing only the terraform code used to declare and initialize variables.
// This allows us to load the default variable value to make them available to gotemplate code before we
// apply gotemplate on terraform file. If we don't, any terraform code that is not considered as valid
// code will cause an error and make LoadDefaultValues fail.
func createTerraformVariablesTemporaryFolder(folder string) (tmpDir string, err error) {
var files []string

if _, err = os.Stat(folder); err != nil {
return
}
if files, err = utils.FindFiles(folder, false, false, "*.tf", "*.tf.json"); err != nil {
return
}

// The variables are spitted up into 4 different files depending if they are declared
// in a .tf file or a .tf.json file and if they are declared in an override file or not.
generated := make(map[string]interface{})
for _, filename := range files {
name, ext := "regular", path.Ext(filename)
if isOverride(filename) {
name = "override"
}

var content []byte
if content, err = os.ReadFile(filename); err != nil {
return
}

if ext == ".tf" {
if found := reTfVariables.FindAllString(string(content), -1); len(found) > 0 {
currentValue, _ := generated[name+ext].(string)
generated[name+ext] = currentValue + fmt.Sprintf("// %s\n%s\n", filename, strings.Join(found, "\n\n"))
}
} else {
ext = ".tf.json"
var jsonContent json.Dictionary
if err = json.Unmarshal(content, &jsonContent); err == nil {
variables := jsonContent.Clone("variable")
if variables.Len() > 0 {
currentValue, _ := generated[name+ext].(json.Dictionary)
generated[name+ext] = variables.Merge(currentValue)
}
}
}
}

if len(generated) > 0 {
// We write the resulting files
if tmpDir, err = os.MkdirTemp("", "load_defaults"); err != nil {
return
}
for filename, value := range generated {
var content []byte
switch value := value.(type) {
case string:
content = []byte(value)
case json.Dictionary:
content = []byte(value.PrettyPrint())
}
if err = os.WriteFile(path.Join(tmpDir, filename), content, 0644); err != nil {
return
}
}
}
return
}
4 changes: 2 additions & 2 deletions util/terraform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func TestLoadDefaultValues(t *testing.T) {
wantResult map[string]interface{}
err string
}{
{"All Types",
{
"All Types",
testFixtureDefaultValues,
map[string]interface{}{
"a": "A (a.tf)",
Expand All @@ -34,7 +35,6 @@ func TestLoadDefaultValues(t *testing.T) {
},
"",
},
{"Invalid Folder", "Invalid", nil, "Module directory Invalid does not exist or cannot be read"},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit 8c32d14

Please sign in to comment.