Skip to content

Commit 6c52da2

Browse files
committed
do our own parsing for variable extraction
1 parent b120eb6 commit 6c52da2

File tree

1 file changed

+63
-33
lines changed

1 file changed

+63
-33
lines changed

tf/tf.go

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package tf
22

33
import (
4+
"os"
5+
"path"
46
"slices"
7+
"strings"
58

69
"github.com/gruntwork-io/terragrunt/internal/errors"
7-
"github.com/hashicorp/terraform-config-inspect/tfconfig"
10+
"github.com/hashicorp/hcl/v2"
11+
"github.com/hashicorp/hcl/v2/hclparse"
812
)
913

1014
const (
@@ -129,49 +133,75 @@ var (
129133
}
130134
)
131135

132-
// diagnosticDoesNotAffectModuleVariables tells you if a diagnostic can be ignored for the purpose of extracting
133-
// variables defined in a module.
134-
func diagnosticDoesNotAffectModuleVariables(d tfconfig.Diagnostic) bool {
135-
ignorableErrors := []tfconfig.Diagnostic{
136-
// These two occur when a module block uses a variable in the `version` or `source` fields.
137-
// Terraform doesn't support this, but OpenTofu does. Either way our ability to extract variables is unaffected.
138-
//
139-
// What we really need is an OpenTofu version of terraform-config-inspect. This may work for now but as / if
140-
// syntax continues to diverge we may run into other issues.
141-
{Summary: "Variables not allowed", Detail: "Variables may not be used here."},
142-
{Summary: "Unsuitable value type", Detail: "Unsuitable value: value must be known"},
143-
}
144-
if d.Severity != tfconfig.DiagError {
145-
return true
136+
// ModuleVariables will return all the variables defined in the downloaded terraform modules, taking into
137+
// account all the generated sources. This function will return the required and optional variables separately.
138+
func ModuleVariables(modulePath string) ([]string, []string, error) {
139+
parser := hclparse.NewParser()
140+
141+
files, err := os.ReadDir(modulePath)
142+
if err != nil {
143+
return nil, nil, err
146144
}
147145

148-
i := slices.IndexFunc(ignorableErrors, func(ie tfconfig.Diagnostic) bool {
149-
return ie.Summary == d.Summary && ie.Detail == d.Detail
150-
})
146+
hclFiles := []*hcl.File{}
147+
allDiags := hcl.Diagnostics{}
151148

152-
return i != -1
153-
}
149+
for _, file := range files {
150+
if file.IsDir() {
151+
continue
152+
}
154153

155-
// ModuleVariables will return all the variables defined in the downloaded terraform modules, taking into
156-
// account all the generated sources. This function will return the required and optional variables separately.
157-
func ModuleVariables(modulePath string) ([]string, []string, error) {
158-
module, diags := tfconfig.LoadModule(modulePath)
154+
parseFunc := parser.ParseHCLFile
155+
156+
suffix := ""
157+
for s := range strings.SplitSeq(file.Name(), ".") {
158+
suffix = s
159+
}
159160

160-
diags = slices.DeleteFunc(diags, diagnosticDoesNotAffectModuleVariables)
161-
if diags.HasErrors() {
162-
return nil, nil, errors.New(diags)
161+
if suffix == "json" {
162+
parseFunc = parser.ParseJSONFile
163+
}
164+
165+
if !(slices.Contains([]string{"tf", "tofu", "json"}, suffix)) {
166+
continue
167+
}
168+
169+
file, parseDiags := parseFunc(path.Join(modulePath, file.Name()))
170+
171+
hclFiles = append(hclFiles, file)
172+
allDiags = append(allDiags, parseDiags...)
163173
}
164174

165-
required := []string{}
166-
optional := []string{}
175+
body := hcl.MergeFiles(hclFiles)
167176

168-
for _, variable := range module.Variables {
169-
if variable.Required {
170-
required = append(required, variable.Name)
177+
varsSchema := &hcl.BodySchema{
178+
Blocks: []hcl.BlockHeaderSchema{
179+
{
180+
Type: "variable",
181+
LabelNames: []string{"name"},
182+
},
183+
},
184+
}
185+
186+
varsContent, _, contentDiags := body.PartialContent(varsSchema)
187+
allDiags = append(allDiags, contentDiags...)
188+
optional, required := []string{}, []string{}
189+
190+
for _, b := range varsContent.Blocks {
191+
name := b.Labels[0]
192+
attributes, attrDiags := b.Body.JustAttributes()
193+
194+
allDiags = append(allDiags, attrDiags...)
195+
if _, ok := attributes["default"]; ok {
196+
optional = append(optional, name)
171197
} else {
172-
optional = append(optional, variable.Name)
198+
required = append(required, name)
173199
}
174200
}
175201

202+
if allDiags.HasErrors() {
203+
return nil, nil, errors.New(allDiags)
204+
}
205+
176206
return required, optional, nil
177207
}

0 commit comments

Comments
 (0)