Fix HCL2 local variables decoding to allow local usage within another local in the same locals block (#8755)
This commit is contained in:
parent
dde74232f2
commit
591b684f08
|
@ -80,6 +80,31 @@ func testParse(t *testing.T, tests []parseTest) {
|
||||||
); diff != "" {
|
); diff != "" {
|
||||||
t.Fatalf("Parser.parse() wrong packer config. %s", diff)
|
t.Fatalf("Parser.parse() wrong packer config. %s", diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if gotCfg != nil && !tt.parseWantDiagHasErrors {
|
||||||
|
gotInputVar := gotCfg.InputVariables
|
||||||
|
for name, value := range tt.parseWantCfg.InputVariables {
|
||||||
|
if variable, ok := gotInputVar[name]; ok {
|
||||||
|
if variable.DefaultValue.GoString() != value.DefaultValue.GoString() {
|
||||||
|
t.Fatalf("Parser.parse() input variable %s expected '%s' but was '%s'", name, value.DefaultValue.GoString(), variable.DefaultValue.GoString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Fatalf("Parser.parse() missing input variable. %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gotLocalVar := gotCfg.LocalVariables
|
||||||
|
for name, value := range tt.parseWantCfg.LocalVariables {
|
||||||
|
if variable, ok := gotLocalVar[name]; ok {
|
||||||
|
if variable.DefaultValue.GoString() != value.DefaultValue.GoString() {
|
||||||
|
t.Fatalf("Parser.parse() local variable %s expected '%s' but was '%s'", name, value.DefaultValue.GoString(), variable.DefaultValue.GoString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Fatalf("Parser.parse() missing local variable. %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if gotDiags.HasErrors() {
|
if gotDiags.HasErrors() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,11 +95,16 @@ func (p *Parser) parse(filename string, vars map[string]string) (*PackerConfig,
|
||||||
// can use input variables so we decode them firsthand.
|
// can use input variables so we decode them firsthand.
|
||||||
{
|
{
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
diags = append(diags, p.decodeInputVariables(file, cfg)...)
|
diags = append(diags, cfg.decodeInputVariables(file)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var locals []*Local
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
diags = append(diags, p.decodeLocalVariables(file, cfg)...)
|
moreLocals, morediags := cfg.parseLocalVariables(file)
|
||||||
|
diags = append(diags, morediags...)
|
||||||
|
locals = append(locals, moreLocals...)
|
||||||
}
|
}
|
||||||
|
diags = append(diags, cfg.evaluateLocalVariables(locals)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse var files
|
// parse var files
|
||||||
|
@ -129,49 +134,6 @@ func (p *Parser) parse(filename string, vars map[string]string) (*PackerConfig,
|
||||||
return cfg, diags
|
return cfg, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeLocalVariables looks in the found blocks for 'variables' and
|
|
||||||
// 'variable' blocks. It should be called firsthand so that other blocks can
|
|
||||||
// use the variables.
|
|
||||||
func (p *Parser) decodeInputVariables(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
|
||||||
var diags hcl.Diagnostics
|
|
||||||
|
|
||||||
content, moreDiags := f.Body.Content(configSchema)
|
|
||||||
diags = append(diags, moreDiags...)
|
|
||||||
|
|
||||||
for _, block := range content.Blocks {
|
|
||||||
switch block.Type {
|
|
||||||
case variableLabel:
|
|
||||||
moreDiags := cfg.InputVariables.decodeConfig(block, nil)
|
|
||||||
diags = append(diags, moreDiags...)
|
|
||||||
case variablesLabel:
|
|
||||||
moreDiags := cfg.InputVariables.decodeConfigMap(block, nil)
|
|
||||||
diags = append(diags, moreDiags...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return diags
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodeLocalVariables looks in the found blocks for 'locals' blocks. It
|
|
||||||
// should be called after parsing input variables so that they can be
|
|
||||||
// referenced.
|
|
||||||
func (p *Parser) decodeLocalVariables(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
|
||||||
var diags hcl.Diagnostics
|
|
||||||
|
|
||||||
content, moreDiags := f.Body.Content(configSchema)
|
|
||||||
diags = append(diags, moreDiags...)
|
|
||||||
|
|
||||||
for _, block := range content.Blocks {
|
|
||||||
switch block.Type {
|
|
||||||
case localsLabel:
|
|
||||||
moreDiags := cfg.LocalVariables.decodeConfigMap(block, cfg.EvalContext())
|
|
||||||
diags = append(diags, moreDiags...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return diags
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodeConfig looks in the found blocks for everything that is not a variable
|
// decodeConfig looks in the found blocks for everything that is not a variable
|
||||||
// block. It should be called after parsing input variables and locals so that
|
// block. It should be called after parsing input variables and locals so that
|
||||||
// they can be referenced.
|
// they can be referenced.
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
locals {
|
||||||
|
for_var = "${local.name_prefix}"
|
||||||
|
bar_var = [
|
||||||
|
local.for_var,
|
||||||
|
local.foo,
|
||||||
|
var.name_prefix,
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
locals {
|
||||||
|
name_prefix = "${var.name_prefix}"
|
||||||
|
foo = "${local.name_prefix}"
|
||||||
|
bar = local.name_prefix
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
variable "name_prefix" {
|
||||||
|
default = "foo"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
locals {
|
||||||
|
first = local.second
|
||||||
|
second = local.first
|
||||||
|
}
|
|
@ -42,6 +42,127 @@ func (cfg *PackerConfig) EvalContext() *hcl.EvalContext {
|
||||||
return ectx
|
return ectx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// decodeInputVariables looks in the found blocks for 'variables' and
|
||||||
|
// 'variable' blocks. It should be called firsthand so that other blocks can
|
||||||
|
// use the variables.
|
||||||
|
func (c *PackerConfig) decodeInputVariables(f *hcl.File) hcl.Diagnostics {
|
||||||
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
|
content, moreDiags := f.Body.Content(configSchema)
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
|
||||||
|
for _, block := range content.Blocks {
|
||||||
|
switch block.Type {
|
||||||
|
case variableLabel:
|
||||||
|
moreDiags := c.InputVariables.decodeVariableBlock(block, nil)
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
case variablesLabel:
|
||||||
|
attrs, moreDiags := block.Body.JustAttributes()
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
for key, attr := range attrs {
|
||||||
|
moreDiags = c.InputVariables.decodeVariable(key, attr, nil)
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseLocalVariables looks in the found blocks for 'locals' blocks. It
|
||||||
|
// should be called after parsing input variables so that they can be
|
||||||
|
// referenced.
|
||||||
|
func (c *PackerConfig) parseLocalVariables(f *hcl.File) ([]*Local, hcl.Diagnostics) {
|
||||||
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
|
content, moreDiags := f.Body.Content(configSchema)
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
var allLocals []*Local
|
||||||
|
|
||||||
|
for _, block := range content.Blocks {
|
||||||
|
switch block.Type {
|
||||||
|
case localsLabel:
|
||||||
|
attrs, moreDiags := block.Body.JustAttributes()
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
locals := make([]*Local, 0, len(attrs))
|
||||||
|
for name, attr := range attrs {
|
||||||
|
if _, found := c.LocalVariables[name]; found {
|
||||||
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: "Duplicate variable",
|
||||||
|
Detail: "Duplicate " + name + " variable definition found.",
|
||||||
|
Subject: attr.NameRange.Ptr(),
|
||||||
|
Context: block.DefRange.Ptr(),
|
||||||
|
})
|
||||||
|
return nil, diags
|
||||||
|
}
|
||||||
|
locals = append(locals, &Local{
|
||||||
|
Name: name,
|
||||||
|
Expr: attr.Expr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
allLocals = append(allLocals, locals...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allLocals, diags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PackerConfig) evaluateLocalVariables(locals []*Local) hcl.Diagnostics {
|
||||||
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
|
if len(locals) > 0 && c.LocalVariables == nil {
|
||||||
|
c.LocalVariables = Variables{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var retry, previousL int
|
||||||
|
for len(locals) > 0 {
|
||||||
|
local := locals[0]
|
||||||
|
moreDiags := c.evaluateLocalVariable(local)
|
||||||
|
if moreDiags.HasErrors() {
|
||||||
|
if len(locals) == 1 {
|
||||||
|
// If this is the only local left there's no need
|
||||||
|
// to try evaluating again
|
||||||
|
return append(diags, moreDiags...)
|
||||||
|
}
|
||||||
|
if previousL == len(locals) {
|
||||||
|
if retry == 100 {
|
||||||
|
// To get to this point, locals must have a circle dependency
|
||||||
|
return append(diags, moreDiags...)
|
||||||
|
}
|
||||||
|
retry++
|
||||||
|
}
|
||||||
|
previousL = len(locals)
|
||||||
|
|
||||||
|
// If local uses another local that has not been evaluated yet this could be the reason of errors
|
||||||
|
// Push local to the end of slice to be evaluated later
|
||||||
|
locals = append(locals, local)
|
||||||
|
} else {
|
||||||
|
retry = 0
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
}
|
||||||
|
// Remove local from slice
|
||||||
|
locals = append(locals[:0], locals[1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PackerConfig) evaluateLocalVariable(local *Local) hcl.Diagnostics {
|
||||||
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
|
value, moreDiags := local.Expr.Value(c.EvalContext())
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
if moreDiags.HasErrors() {
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
c.LocalVariables[local.Name] = &Variable{
|
||||||
|
DefaultValue: value,
|
||||||
|
Type: value.Type(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return diags
|
||||||
|
}
|
||||||
|
|
||||||
// getCoreBuildProvisioners takes a list of provisioner block, starts according
|
// getCoreBuildProvisioners takes a list of provisioner block, starts according
|
||||||
// provisioners and sends parsed HCL2 over to it.
|
// provisioners and sends parsed HCL2 over to it.
|
||||||
func (p *Parser) getCoreBuildProvisioners(blocks []*ProvisionerBlock, ectx *hcl.EvalContext, generatedVars map[string]string) ([]packer.CoreBuildProvisioner, hcl.Diagnostics) {
|
func (p *Parser) getCoreBuildProvisioners(blocks []*ProvisionerBlock, ectx *hcl.EvalContext, generatedVars map[string]string) ([]packer.CoreBuildProvisioner, hcl.Diagnostics) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -21,15 +22,46 @@ func TestParser_complete(t *testing.T) {
|
||||||
&PackerConfig{
|
&PackerConfig{
|
||||||
Basedir: "testdata/complete",
|
Basedir: "testdata/complete",
|
||||||
InputVariables: Variables{
|
InputVariables: Variables{
|
||||||
"foo": &Variable{},
|
"foo": &Variable{
|
||||||
"image_id": &Variable{},
|
DefaultValue: cty.StringVal("value"),
|
||||||
"port": &Variable{},
|
},
|
||||||
"availability_zone_names": &Variable{},
|
"image_id": &Variable{
|
||||||
|
DefaultValue: cty.StringVal("image-id-default"),
|
||||||
|
},
|
||||||
|
"port": &Variable{
|
||||||
|
DefaultValue: cty.NumberIntVal(42),
|
||||||
|
},
|
||||||
|
"availability_zone_names": &Variable{
|
||||||
|
DefaultValue: cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("A"),
|
||||||
|
cty.StringVal("B"),
|
||||||
|
cty.StringVal("C"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
LocalVariables: Variables{
|
LocalVariables: Variables{
|
||||||
"feefoo": &Variable{},
|
"feefoo": &Variable{
|
||||||
"standard_tags": &Variable{},
|
DefaultValue: cty.StringVal("value_image-id-default"),
|
||||||
"abc_map": &Variable{},
|
},
|
||||||
|
"standard_tags": &Variable{
|
||||||
|
DefaultValue: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"Component": cty.StringVal("user-service"),
|
||||||
|
"Environment": cty.StringVal("production"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
"abc_map": &Variable{
|
||||||
|
DefaultValue: cty.TupleVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("a"),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("b"),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("c"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Sources: map[SourceRef]*SourceBlock{
|
Sources: map[SourceRef]*SourceBlock{
|
||||||
refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"},
|
refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"},
|
||||||
|
|
|
@ -12,6 +12,14 @@ import (
|
||||||
"github.com/zclconf/go-cty/cty/convert"
|
"github.com/zclconf/go-cty/cty/convert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Local represents a single entry from a "locals" block in a module or file.
|
||||||
|
// The "locals" block itself is not represented, because it serves only to
|
||||||
|
// provide context for us to interpret its contents.
|
||||||
|
type Local struct {
|
||||||
|
Name string
|
||||||
|
Expr hcl.Expression
|
||||||
|
}
|
||||||
|
|
||||||
type Variable struct {
|
type Variable struct {
|
||||||
// CmdValue, VarfileValue, EnvValue, DefaultValue are possible values of
|
// CmdValue, VarfileValue, EnvValue, DefaultValue are possible values of
|
||||||
// the variable; The first value set from these will be the one used. If
|
// the variable; The first value set from these will be the one used. If
|
||||||
|
@ -75,44 +83,40 @@ func (variables Variables) Values() map[string]cty.Value {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeConfig decodes a "variables" section the way packer 1 used to
|
// decodeVariable decodes a variable key and value into Variables
|
||||||
func (variables *Variables) decodeConfigMap(block *hcl.Block, ectx *hcl.EvalContext) hcl.Diagnostics {
|
func (variables *Variables) decodeVariable(key string, attr *hcl.Attribute, ectx *hcl.EvalContext) hcl.Diagnostics {
|
||||||
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
if (*variables) == nil {
|
if (*variables) == nil {
|
||||||
(*variables) = Variables{}
|
(*variables) = Variables{}
|
||||||
}
|
}
|
||||||
attrs, diags := block.Body.JustAttributes()
|
|
||||||
|
|
||||||
if diags.HasErrors() {
|
|
||||||
return diags
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, attr := range attrs {
|
|
||||||
if _, found := (*variables)[key]; found {
|
if _, found := (*variables)[key]; found {
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Duplicate variable",
|
Summary: "Duplicate variable",
|
||||||
Detail: "Duplicate " + key + " variable definition found.",
|
Detail: "Duplicate " + key + " variable definition found.",
|
||||||
Subject: attr.NameRange.Ptr(),
|
Subject: attr.NameRange.Ptr(),
|
||||||
Context: block.DefRange.Ptr(),
|
|
||||||
})
|
})
|
||||||
continue
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
value, moreDiags := attr.Expr.Value(ectx)
|
value, moreDiags := attr.Expr.Value(ectx)
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
if moreDiags.HasErrors() {
|
if moreDiags.HasErrors() {
|
||||||
continue
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
(*variables)[key] = &Variable{
|
(*variables)[key] = &Variable{
|
||||||
DefaultValue: value,
|
DefaultValue: value,
|
||||||
Type: value.Type(),
|
Type: value.Type(),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeConfig decodes a "variables" section the way packer 1 used to
|
// decodeVariableBlock decodes a "variables" section the way packer 1 used to
|
||||||
func (variables *Variables) decodeConfig(block *hcl.Block, ectx *hcl.EvalContext) hcl.Diagnostics {
|
func (variables *Variables) decodeVariableBlock(block *hcl.Block, ectx *hcl.EvalContext) hcl.Diagnostics {
|
||||||
if (*variables) == nil {
|
if (*variables) == nil {
|
||||||
(*variables) = Variables{}
|
(*variables) = Variables{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,25 @@ func TestParse_variables(t *testing.T) {
|
||||||
&PackerConfig{
|
&PackerConfig{
|
||||||
Basedir: filepath.Join("testdata", "variables"),
|
Basedir: filepath.Join("testdata", "variables"),
|
||||||
InputVariables: Variables{
|
InputVariables: Variables{
|
||||||
"image_name": &Variable{},
|
"image_name": &Variable{
|
||||||
"key": &Variable{},
|
DefaultValue: cty.StringVal("foo-image-{{user `my_secret`}}"),
|
||||||
"my_secret": &Variable{},
|
},
|
||||||
"image_id": &Variable{},
|
"key": &Variable{
|
||||||
"port": &Variable{},
|
DefaultValue: cty.StringVal("value"),
|
||||||
|
},
|
||||||
|
"my_secret": &Variable{
|
||||||
|
DefaultValue: cty.StringVal("foo"),
|
||||||
|
},
|
||||||
|
"image_id": &Variable{
|
||||||
|
DefaultValue: cty.StringVal("image-id-default"),
|
||||||
|
},
|
||||||
|
"port": &Variable{
|
||||||
|
DefaultValue: cty.NumberIntVal(42),
|
||||||
|
},
|
||||||
"availability_zone_names": &Variable{
|
"availability_zone_names": &Variable{
|
||||||
|
DefaultValue: cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("us-west-1a"),
|
||||||
|
}),
|
||||||
Description: fmt.Sprintln("Describing is awesome ;D"),
|
Description: fmt.Sprintln("Describing is awesome ;D"),
|
||||||
},
|
},
|
||||||
"super_secret_password": &Variable{
|
"super_secret_password": &Variable{
|
||||||
|
@ -37,8 +50,12 @@ func TestParse_variables(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
LocalVariables: Variables{
|
LocalVariables: Variables{
|
||||||
"owner": &Variable{},
|
"owner": &Variable{
|
||||||
"service_name": &Variable{},
|
DefaultValue: cty.StringVal("Community Team"),
|
||||||
|
},
|
||||||
|
"service_name": &Variable{
|
||||||
|
DefaultValue: cty.StringVal("forum"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
false, false,
|
false, false,
|
||||||
|
@ -97,6 +114,53 @@ func TestParse_variables(t *testing.T) {
|
||||||
[]packer.Build{},
|
[]packer.Build{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
{"locals within another locals usage in different files",
|
||||||
|
defaultParser,
|
||||||
|
parseTestArgs{"testdata/variables/complicated", nil},
|
||||||
|
&PackerConfig{
|
||||||
|
Basedir: "testdata/variables/complicated",
|
||||||
|
InputVariables: Variables{
|
||||||
|
"name_prefix": &Variable{
|
||||||
|
DefaultValue: cty.StringVal("foo"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LocalVariables: Variables{
|
||||||
|
"name_prefix": &Variable{
|
||||||
|
DefaultValue: cty.StringVal("foo"),
|
||||||
|
},
|
||||||
|
"foo": &Variable{
|
||||||
|
DefaultValue: cty.StringVal("foo"),
|
||||||
|
},
|
||||||
|
"bar": &Variable{
|
||||||
|
DefaultValue: cty.StringVal("foo"),
|
||||||
|
},
|
||||||
|
"for_var": &Variable{
|
||||||
|
DefaultValue: cty.StringVal("foo"),
|
||||||
|
},
|
||||||
|
"bar_var": &Variable{
|
||||||
|
DefaultValue: cty.TupleVal([]cty.Value{
|
||||||
|
cty.StringVal("foo"),
|
||||||
|
cty.StringVal("foo"),
|
||||||
|
cty.StringVal("foo"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false, false,
|
||||||
|
[]packer.Build{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{"recursive locals",
|
||||||
|
defaultParser,
|
||||||
|
parseTestArgs{"testdata/variables/recursive_locals.pkr.hcl", nil},
|
||||||
|
&PackerConfig{
|
||||||
|
Basedir: filepath.Join("testdata", "variables"),
|
||||||
|
LocalVariables: Variables{},
|
||||||
|
},
|
||||||
|
true, true,
|
||||||
|
[]packer.Build{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
testParse(t, tests)
|
testParse(t, tests)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue