mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-26 16:36:26 -06:00
configs: Parser.LoadValuesFile
This method loads a "values file" -- also known as a "tfvars file" -- and returns the values found inside. A values file is an HCL file (in either native or JSON syntax) whose top-level body is treated as a set of arbitrary key/value pairs whose values may not depend on any variables or functions. We will load values files through a configs.Parser -- even though values files are not strictly-speaking part of configuration -- because this causes them to be registered in our source code cache so that we can generate source code snippets if we need to report any diagnostics.
This commit is contained in:
parent
a0f4a313ef
commit
05e3b47ba1
43
configs/parser_values.go
Normal file
43
configs/parser_values.go
Normal file
@ -0,0 +1,43 @@
|
||||
package configs
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl2/hcl"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// LoadValuesFile reads the file at the given path and parses it as a "values
|
||||
// file", which is an HCL config file whose top-level attributes are treated
|
||||
// as arbitrary key.value pairs.
|
||||
//
|
||||
// If the file cannot be read -- for example, if it does not exist -- then
|
||||
// a nil map will be returned along with error diagnostics. Callers may wish
|
||||
// to disregard the returned diagnostics in this case and instead generate
|
||||
// their own error message(s) with additional context.
|
||||
//
|
||||
// If the returned diagnostics has errors when a non-nil map is returned
|
||||
// then the map may be incomplete but should be valid enough for careful
|
||||
// static analysis.
|
||||
//
|
||||
// This method wraps LoadHCLFile, and so it inherits the syntax selection
|
||||
// behaviors documented for that method.
|
||||
func (p *Parser) LoadValuesFile(path string) (map[string]cty.Value, hcl.Diagnostics) {
|
||||
body, diags := p.LoadHCLFile(path)
|
||||
if body == nil {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
vals := make(map[string]cty.Value)
|
||||
attrs, attrDiags := body.JustAttributes()
|
||||
diags = append(diags, attrDiags...)
|
||||
if attrs == nil {
|
||||
return vals, diags
|
||||
}
|
||||
|
||||
for name, attr := range attrs {
|
||||
val, valDiags := attr.Expr.Value(nil)
|
||||
diags = append(diags, valDiags...)
|
||||
vals[name] = val
|
||||
}
|
||||
|
||||
return vals, diags
|
||||
}
|
113
configs/parser_values_test.go
Normal file
113
configs/parser_values_test.go
Normal file
@ -0,0 +1,113 @@
|
||||
package configs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestParserLoadValuesFile(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Source string
|
||||
Want map[string]cty.Value
|
||||
DiagCount int
|
||||
}{
|
||||
"empty.tfvars": {
|
||||
"",
|
||||
map[string]cty.Value{},
|
||||
0,
|
||||
},
|
||||
"empty.json": {
|
||||
"{}",
|
||||
map[string]cty.Value{},
|
||||
0,
|
||||
},
|
||||
"zerolen.json": {
|
||||
"",
|
||||
map[string]cty.Value{},
|
||||
2, // syntax error and missing root object
|
||||
},
|
||||
"one-number.tfvars": {
|
||||
"foo = 1\n",
|
||||
map[string]cty.Value{
|
||||
"foo": cty.NumberIntVal(1),
|
||||
},
|
||||
0,
|
||||
},
|
||||
"one-number.tfvars.json": {
|
||||
`{"foo": 1}`,
|
||||
map[string]cty.Value{
|
||||
"foo": cty.NumberIntVal(1),
|
||||
},
|
||||
0,
|
||||
},
|
||||
"two-bools.tfvars": {
|
||||
"foo = true\nbar = false\n",
|
||||
map[string]cty.Value{
|
||||
"foo": cty.True,
|
||||
"bar": cty.False,
|
||||
},
|
||||
0,
|
||||
},
|
||||
"two-bools.tfvars.json": {
|
||||
`{"foo": true, "bar": false}`,
|
||||
map[string]cty.Value{
|
||||
"foo": cty.True,
|
||||
"bar": cty.False,
|
||||
},
|
||||
0,
|
||||
},
|
||||
"invalid-syntax.tfvars": {
|
||||
"foo bar baz\n",
|
||||
map[string]cty.Value{},
|
||||
1, // attribute or block definition required
|
||||
},
|
||||
"block.tfvars": {
|
||||
"foo = true\ninvalid {\n}\n",
|
||||
map[string]cty.Value{
|
||||
"foo": cty.True,
|
||||
},
|
||||
1, // blocks are not allowed
|
||||
},
|
||||
"variables.tfvars": {
|
||||
"baz = true\nfoo = var.baz\n",
|
||||
map[string]cty.Value{
|
||||
"baz": cty.True,
|
||||
"foo": cty.DynamicVal,
|
||||
},
|
||||
1, // variables cannot be referenced here
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
p := testParser(map[string]string{
|
||||
name: test.Source,
|
||||
})
|
||||
got, diags := p.LoadValuesFile(name)
|
||||
if len(diags) != test.DiagCount {
|
||||
t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
|
||||
for _, diag := range diags {
|
||||
t.Logf("- %s", diag)
|
||||
}
|
||||
}
|
||||
|
||||
if len(got) != len(test.Want) {
|
||||
t.Errorf("wrong number of result keys %d; want %d", len(got), len(test.Want))
|
||||
}
|
||||
|
||||
for name, gotVal := range got {
|
||||
wantVal := test.Want[name]
|
||||
if wantVal == cty.NilVal {
|
||||
t.Errorf("unexpected result key %q", name)
|
||||
continue
|
||||
}
|
||||
|
||||
if !gotVal.RawEquals(wantVal) {
|
||||
t.Errorf("wrong value for %q\ngot: %#v\nwant: %#v", name, gotVal, wantVal)
|
||||
continue
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user