Merge branch 'master' into update_go-cty_regex
This commit is contained in:
commit
e518a0a8b7
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
"github.com/hashicorp/hcl/v2/hclparse"
|
"github.com/hashicorp/hcl/v2/hclparse"
|
||||||
|
"github.com/hashicorp/packer/builder/null"
|
||||||
. "github.com/hashicorp/packer/hcl2template/internal"
|
. "github.com/hashicorp/packer/hcl2template/internal"
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
@ -21,6 +22,7 @@ func getBasicParser() *Parser {
|
||||||
BuilderSchemas: packer.MapOfBuilder{
|
BuilderSchemas: packer.MapOfBuilder{
|
||||||
"amazon-ebs": func() (packer.Builder, error) { return &MockBuilder{}, nil },
|
"amazon-ebs": func() (packer.Builder, error) { return &MockBuilder{}, nil },
|
||||||
"virtualbox-iso": func() (packer.Builder, error) { return &MockBuilder{}, nil },
|
"virtualbox-iso": func() (packer.Builder, error) { return &MockBuilder{}, nil },
|
||||||
|
"null": func() (packer.Builder, error) { return &null.Builder{}, nil },
|
||||||
},
|
},
|
||||||
ProvisionersSchemas: packer.MapOfProvisioner{
|
ProvisionersSchemas: packer.MapOfProvisioner{
|
||||||
"shell": func() (packer.Provisioner, error) { return &MockProvisioner{}, nil },
|
"shell": func() (packer.Provisioner, error) { return &MockProvisioner{}, nil },
|
||||||
|
@ -58,7 +60,7 @@ func testParse(t *testing.T, tests []parseTest) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
gotCfg, gotDiags := tt.parser.parse(tt.args.filename, tt.args.vars)
|
gotCfg, gotDiags := tt.parser.parse(tt.args.filename, tt.args.vars)
|
||||||
if tt.parseWantDiags == (gotDiags == nil) {
|
if tt.parseWantDiags == (gotDiags == nil) {
|
||||||
t.Fatalf("Parser.parse() unexpected diagnostics. %s", gotDiags)
|
t.Fatalf("Parser.parse() unexpected %q diagnostics.", gotDiags)
|
||||||
}
|
}
|
||||||
if tt.parseWantDiagHasErrors != gotDiags.HasErrors() {
|
if tt.parseWantDiagHasErrors != gotDiags.HasErrors() {
|
||||||
t.Fatalf("Parser.parse() unexpected diagnostics HasErrors. %s", gotDiags)
|
t.Fatalf("Parser.parse() unexpected diagnostics HasErrors. %s", gotDiags)
|
||||||
|
@ -120,6 +122,7 @@ func testParse(t *testing.T, tests []parseTest) {
|
||||||
packer.CoreBuild{},
|
packer.CoreBuild{},
|
||||||
packer.CoreBuildProvisioner{},
|
packer.CoreBuildProvisioner{},
|
||||||
packer.CoreBuildPostProcessor{},
|
packer.CoreBuildPostProcessor{},
|
||||||
|
null.Builder{},
|
||||||
),
|
),
|
||||||
); diff != "" {
|
); diff != "" {
|
||||||
t.Fatalf("Parser.getBuilds() wrong packer builds. %s", diff)
|
t.Fatalf("Parser.getBuilds() wrong packer builds. %s", diff)
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"github.com/hashicorp/hcl/v2/hcldec"
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
"github.com/zclconf/go-cty/cty/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NestedMockConfig struct {
|
type NestedMockConfig struct {
|
||||||
|
@ -38,6 +40,27 @@ type MockConfig struct {
|
||||||
NestedSlice []NestedMockConfig `mapstructure:"nested_slice"`
|
NestedSlice []NestedMockConfig `mapstructure:"nested_slice"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *MockConfig) Prepare(raws ...interface{}) error {
|
||||||
|
for i, raw := range raws {
|
||||||
|
cval, ok := raw.(cty.Value)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(cval, cty.DynamicPseudoType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ccval, err := json.Unmarshal(b, cty.DynamicPseudoType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
raws[i] = ccval
|
||||||
|
}
|
||||||
|
return config.Decode(b, &config.DecodeOpts{
|
||||||
|
Interpolate: true,
|
||||||
|
}, raws...)
|
||||||
|
}
|
||||||
|
|
||||||
//////
|
//////
|
||||||
// MockBuilder
|
// MockBuilder
|
||||||
//////
|
//////
|
||||||
|
@ -51,9 +74,7 @@ var _ packer.Builder = new(MockBuilder)
|
||||||
func (b *MockBuilder) ConfigSpec() hcldec.ObjectSpec { return b.Config.FlatMapstructure().HCL2Spec() }
|
func (b *MockBuilder) ConfigSpec() hcldec.ObjectSpec { return b.Config.FlatMapstructure().HCL2Spec() }
|
||||||
|
|
||||||
func (b *MockBuilder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
func (b *MockBuilder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||||
return nil, nil, config.Decode(&b.Config, &config.DecodeOpts{
|
return nil, nil, b.Config.Prepare(raws...)
|
||||||
Interpolate: true,
|
|
||||||
}, raws...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *MockBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
func (b *MockBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
||||||
|
@ -75,9 +96,7 @@ func (b *MockProvisioner) ConfigSpec() hcldec.ObjectSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *MockProvisioner) Prepare(raws ...interface{}) error {
|
func (b *MockProvisioner) Prepare(raws ...interface{}) error {
|
||||||
return config.Decode(&b.Config, &config.DecodeOpts{
|
return b.Config.Prepare(raws...)
|
||||||
Interpolate: true,
|
|
||||||
}, raws...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *MockProvisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error {
|
func (b *MockProvisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, _ map[string]interface{}) error {
|
||||||
|
@ -99,9 +118,7 @@ func (b *MockPostProcessor) ConfigSpec() hcldec.ObjectSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *MockPostProcessor) Configure(raws ...interface{}) error {
|
func (b *MockPostProcessor) Configure(raws ...interface{}) error {
|
||||||
return config.Decode(&b.Config, &config.DecodeOpts{
|
return b.Config.Prepare(raws...)
|
||||||
Interpolate: true,
|
|
||||||
}, raws...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *MockPostProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer.Artifact) (packer.Artifact, bool, bool, error) {
|
func (b *MockPostProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer.Artifact) (packer.Artifact, bool, bool, error) {
|
||||||
|
@ -124,9 +141,7 @@ func (b *MockCommunicator) ConfigSpec() hcldec.ObjectSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *MockCommunicator) Configure(raws ...interface{}) ([]string, error) {
|
func (b *MockCommunicator) Configure(raws ...interface{}) ([]string, error) {
|
||||||
return nil, config.Decode(&b.Config, &config.DecodeOpts{
|
return nil, b.Config.Prepare(raws...)
|
||||||
Interpolate: true,
|
|
||||||
}, raws...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////
|
//////
|
||||||
|
|
|
@ -123,9 +123,14 @@ func (p *Parser) parse(filename string, vars map[string]string) (*PackerConfig,
|
||||||
varFiles = append(varFiles, f)
|
varFiles = append(varFiles, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
diags = append(diags, cfg.InputVariables.collectVariableValues(os.Environ(), varFiles, vars)...)
|
diags = append(diags, cfg.collectInputVariableValues(os.Environ(), varFiles, vars)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, moreDiags := cfg.InputVariables.Values()
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
_, moreDiags = cfg.LocalVariables.Values()
|
||||||
|
diags = append(diags, moreDiags...)
|
||||||
|
|
||||||
// decode the actual content
|
// decode the actual content
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
diags = append(diags, p.decodeConfig(file, cfg)...)
|
diags = append(diags, p.decodeConfig(file, cfg)...)
|
||||||
|
|
|
@ -29,6 +29,7 @@ variable "super_secret_password" {
|
||||||
description = <<IMSENSIBLE
|
description = <<IMSENSIBLE
|
||||||
Handle with care plz
|
Handle with care plz
|
||||||
IMSENSIBLE
|
IMSENSIBLE
|
||||||
|
default = null
|
||||||
}
|
}
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
variable "broken_type" {
|
variable "broken_variable" {
|
||||||
invalid = true
|
invalid = true
|
||||||
|
default = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
variable "foo" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
build {
|
||||||
|
sources = [
|
||||||
|
"source.null.null-builder",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
source "null" "null-builder" {
|
||||||
|
communicator = "none"
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
variable "foo" {
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
build {
|
||||||
|
sources = [
|
||||||
|
"source.null.null-builder${var.foo}",
|
||||||
|
]
|
||||||
|
}
|
|
@ -60,6 +60,9 @@ func (p *Parser) decodeBuildConfig(block *hcl.Block) (*BuildBlock, hcl.Diagnosti
|
||||||
Config hcl.Body `hcl:",remain"`
|
Config hcl.Body `hcl:",remain"`
|
||||||
}
|
}
|
||||||
diags := gohcl.DecodeBody(block.Body, nil, &b)
|
diags := gohcl.DecodeBody(block.Body, nil, &b)
|
||||||
|
if diags.HasErrors() {
|
||||||
|
return nil, diags
|
||||||
|
}
|
||||||
|
|
||||||
for _, buildFrom := range b.FromSources {
|
for _, buildFrom := range b.FromSources {
|
||||||
ref := sourceRefFromString(buildFrom)
|
ref := sourceRefFromString(buildFrom)
|
||||||
|
@ -84,6 +87,9 @@ func (p *Parser) decodeBuildConfig(block *hcl.Block) (*BuildBlock, hcl.Diagnosti
|
||||||
|
|
||||||
content, moreDiags := b.Config.Content(buildSchema)
|
content, moreDiags := b.Config.Content(buildSchema)
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
|
if diags.HasErrors() {
|
||||||
|
return nil, diags
|
||||||
|
}
|
||||||
for _, block := range content.Blocks {
|
for _, block := range content.Blocks {
|
||||||
switch block.Type {
|
switch block.Type {
|
||||||
case buildProvisionerLabel:
|
case buildProvisionerLabel:
|
||||||
|
|
|
@ -2,6 +2,7 @@ package hcl2template
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
"github.com/hashicorp/packer/helper/common"
|
"github.com/hashicorp/packer/helper/common"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
@ -24,19 +25,27 @@ type PackerConfig struct {
|
||||||
InputVariables Variables
|
InputVariables Variables
|
||||||
LocalVariables Variables
|
LocalVariables Variables
|
||||||
|
|
||||||
|
ValidationOptions
|
||||||
|
|
||||||
// Builds is the list of Build blocks defined in the config files.
|
// Builds is the list of Build blocks defined in the config files.
|
||||||
Builds Builds
|
Builds Builds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ValidationOptions struct {
|
||||||
|
Strict bool
|
||||||
|
}
|
||||||
|
|
||||||
// EvalContext returns the *hcl.EvalContext that will be passed to an hcl
|
// EvalContext returns the *hcl.EvalContext that will be passed to an hcl
|
||||||
// decoder in order to tell what is the actual value of a var or a local and
|
// decoder in order to tell what is the actual value of a var or a local and
|
||||||
// the list of defined functions.
|
// the list of defined functions.
|
||||||
func (cfg *PackerConfig) EvalContext() *hcl.EvalContext {
|
func (cfg *PackerConfig) EvalContext() *hcl.EvalContext {
|
||||||
|
inputVariables, _ := cfg.InputVariables.Values()
|
||||||
|
localVariables, _ := cfg.LocalVariables.Values()
|
||||||
ectx := &hcl.EvalContext{
|
ectx := &hcl.EvalContext{
|
||||||
Functions: Functions(cfg.Basedir),
|
Functions: Functions(cfg.Basedir),
|
||||||
Variables: map[string]cty.Value{
|
Variables: map[string]cty.Value{
|
||||||
"var": cty.ObjectVal(cfg.InputVariables.Values()),
|
"var": cty.ObjectVal(inputVariables),
|
||||||
"local": cty.ObjectVal(cfg.LocalVariables.Values()),
|
"local": cty.ObjectVal(localVariables),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return ectx
|
return ectx
|
||||||
|
@ -76,20 +85,19 @@ func (c *PackerConfig) parseLocalVariables(f *hcl.File) ([]*Local, hcl.Diagnosti
|
||||||
|
|
||||||
content, moreDiags := f.Body.Content(configSchema)
|
content, moreDiags := f.Body.Content(configSchema)
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
var allLocals []*Local
|
var locals []*Local
|
||||||
|
|
||||||
for _, block := range content.Blocks {
|
for _, block := range content.Blocks {
|
||||||
switch block.Type {
|
switch block.Type {
|
||||||
case localsLabel:
|
case localsLabel:
|
||||||
attrs, moreDiags := block.Body.JustAttributes()
|
attrs, moreDiags := block.Body.JustAttributes()
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
locals := make([]*Local, 0, len(attrs))
|
|
||||||
for name, attr := range attrs {
|
for name, attr := range attrs {
|
||||||
if _, found := c.LocalVariables[name]; found {
|
if _, found := c.LocalVariables[name]; found {
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Duplicate variable",
|
Summary: "Duplicate value in " + localsLabel,
|
||||||
Detail: "Duplicate " + name + " variable definition found.",
|
Detail: "Duplicate " + name + " definition found.",
|
||||||
Subject: attr.NameRange.Ptr(),
|
Subject: attr.NameRange.Ptr(),
|
||||||
Context: block.DefRange.Ptr(),
|
Context: block.DefRange.Ptr(),
|
||||||
})
|
})
|
||||||
|
@ -100,11 +108,10 @@ func (c *PackerConfig) parseLocalVariables(f *hcl.File) ([]*Local, hcl.Diagnosti
|
||||||
Expr: attr.Expr,
|
Expr: attr.Expr,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
allLocals = append(allLocals, locals...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return allLocals, diags
|
return locals, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PackerConfig) evaluateLocalVariables(locals []*Local) hcl.Diagnostics {
|
func (c *PackerConfig) evaluateLocalVariables(locals []*Local) hcl.Diagnostics {
|
||||||
|
@ -156,6 +163,7 @@ func (c *PackerConfig) evaluateLocalVariable(local *Local) hcl.Diagnostics {
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
c.LocalVariables[local.Name] = &Variable{
|
c.LocalVariables[local.Name] = &Variable{
|
||||||
|
Name: local.Name,
|
||||||
DefaultValue: value,
|
DefaultValue: value,
|
||||||
Type: value.Type(),
|
Type: value.Type(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,19 @@ func TestParser_complete(t *testing.T) {
|
||||||
Basedir: "testdata/complete",
|
Basedir: "testdata/complete",
|
||||||
InputVariables: Variables{
|
InputVariables: Variables{
|
||||||
"foo": &Variable{
|
"foo": &Variable{
|
||||||
|
Name: "foo",
|
||||||
DefaultValue: cty.StringVal("value"),
|
DefaultValue: cty.StringVal("value"),
|
||||||
},
|
},
|
||||||
"image_id": &Variable{
|
"image_id": &Variable{
|
||||||
|
Name: "image_id",
|
||||||
DefaultValue: cty.StringVal("image-id-default"),
|
DefaultValue: cty.StringVal("image-id-default"),
|
||||||
},
|
},
|
||||||
"port": &Variable{
|
"port": &Variable{
|
||||||
|
Name: "port",
|
||||||
DefaultValue: cty.NumberIntVal(42),
|
DefaultValue: cty.NumberIntVal(42),
|
||||||
},
|
},
|
||||||
"availability_zone_names": &Variable{
|
"availability_zone_names": &Variable{
|
||||||
|
Name: "availability_zone_names",
|
||||||
DefaultValue: cty.ListVal([]cty.Value{
|
DefaultValue: cty.ListVal([]cty.Value{
|
||||||
cty.StringVal("A"),
|
cty.StringVal("A"),
|
||||||
cty.StringVal("B"),
|
cty.StringVal("B"),
|
||||||
|
@ -41,15 +45,18 @@ func TestParser_complete(t *testing.T) {
|
||||||
},
|
},
|
||||||
LocalVariables: Variables{
|
LocalVariables: Variables{
|
||||||
"feefoo": &Variable{
|
"feefoo": &Variable{
|
||||||
|
Name: "feefoo",
|
||||||
DefaultValue: cty.StringVal("value_image-id-default"),
|
DefaultValue: cty.StringVal("value_image-id-default"),
|
||||||
},
|
},
|
||||||
"standard_tags": &Variable{
|
"standard_tags": &Variable{
|
||||||
|
Name: "standard_tags",
|
||||||
DefaultValue: cty.ObjectVal(map[string]cty.Value{
|
DefaultValue: cty.ObjectVal(map[string]cty.Value{
|
||||||
"Component": cty.StringVal("user-service"),
|
"Component": cty.StringVal("user-service"),
|
||||||
"Environment": cty.StringVal("production"),
|
"Environment": cty.StringVal("production"),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
"abc_map": &Variable{
|
"abc_map": &Variable{
|
||||||
|
Name: "abc_map",
|
||||||
DefaultValue: cty.TupleVal([]cty.Value{
|
DefaultValue: cty.TupleVal([]cty.Value{
|
||||||
cty.ObjectVal(map[string]cty.Value{
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
"id": cty.StringVal("a"),
|
"id": cty.StringVal("a"),
|
||||||
|
|
|
@ -39,13 +39,15 @@ type Variable struct {
|
||||||
// declaration, the type of the default variable will be used. This will
|
// declaration, the type of the default variable will be used. This will
|
||||||
// allow to ensure that users set this variable correctly.
|
// allow to ensure that users set this variable correctly.
|
||||||
Type cty.Type
|
Type cty.Type
|
||||||
|
// Common name of the variable
|
||||||
|
Name string
|
||||||
// Description of the variable
|
// Description of the variable
|
||||||
Description string
|
Description string
|
||||||
// When Sensitive is set to true Packer will try it best to hide/obfuscate
|
// When Sensitive is set to true Packer will try it best to hide/obfuscate
|
||||||
// the variable from the output stream. By replacing the text.
|
// the variable from the output stream. By replacing the text.
|
||||||
Sensitive bool
|
Sensitive bool
|
||||||
|
|
||||||
block *hcl.Block
|
Range hcl.Range
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Variable) GoString() string {
|
func (v *Variable) GoString() string {
|
||||||
|
@ -60,27 +62,37 @@ func (v *Variable) Value() (cty.Value, *hcl.Diagnostic) {
|
||||||
v.EnvValue,
|
v.EnvValue,
|
||||||
v.DefaultValue,
|
v.DefaultValue,
|
||||||
} {
|
} {
|
||||||
if !value.IsNull() {
|
if value != cty.NilVal {
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cty.NilVal, &hcl.Diagnostic{
|
|
||||||
|
value := cty.NullVal(cty.DynamicPseudoType)
|
||||||
|
|
||||||
|
return value, &hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Unset variable",
|
Summary: fmt.Sprintf("Unset variable %q", v.Name),
|
||||||
Detail: "A used variable must be set; see " +
|
Detail: "A used variable must be set or have a default value; see " +
|
||||||
"https://packer.io/docs/configuration/from-1.5/syntax.html for details.",
|
"https://packer.io/docs/configuration/from-1.5/syntax.html for " +
|
||||||
Context: v.block.DefRange.Ptr(),
|
"details.",
|
||||||
|
Context: v.Range.Ptr(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Variables map[string]*Variable
|
type Variables map[string]*Variable
|
||||||
|
|
||||||
func (variables Variables) Values() map[string]cty.Value {
|
func (variables Variables) Values() (map[string]cty.Value, hcl.Diagnostics) {
|
||||||
res := map[string]cty.Value{}
|
res := map[string]cty.Value{}
|
||||||
|
var diags hcl.Diagnostics
|
||||||
for k, v := range variables {
|
for k, v := range variables {
|
||||||
res[k], _ = v.Value()
|
value, diag := v.Value()
|
||||||
|
if diag != nil {
|
||||||
|
diags = append(diags, diag)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
return res
|
res[k] = value
|
||||||
|
}
|
||||||
|
return res, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeVariable decodes a variable key and value into Variables
|
// decodeVariable decodes a variable key and value into Variables
|
||||||
|
@ -108,8 +120,10 @@ func (variables *Variables) decodeVariable(key string, attr *hcl.Attribute, ectx
|
||||||
}
|
}
|
||||||
|
|
||||||
(*variables)[key] = &Variable{
|
(*variables)[key] = &Variable{
|
||||||
|
Name: key,
|
||||||
DefaultValue: value,
|
DefaultValue: value,
|
||||||
Type: value.Type(),
|
Type: value.Type(),
|
||||||
|
Range: attr.Range,
|
||||||
}
|
}
|
||||||
|
|
||||||
return diags
|
return diags
|
||||||
|
@ -142,10 +156,13 @@ func (variables *Variables) decodeVariableBlock(block *hcl.Block, ectx *hcl.Eval
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name := block.Labels[0]
|
||||||
|
|
||||||
res := &Variable{
|
res := &Variable{
|
||||||
|
Name: name,
|
||||||
Description: b.Description,
|
Description: b.Description,
|
||||||
Sensitive: b.Sensitive,
|
Sensitive: b.Sensitive,
|
||||||
block: block,
|
Range: block.DefRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs, moreDiags := b.Rest.JustAttributes()
|
attrs, moreDiags := b.Rest.JustAttributes()
|
||||||
|
@ -206,7 +223,7 @@ func (variables *Variables) decodeVariableBlock(block *hcl.Block, ectx *hcl.Eval
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
(*variables)[block.Labels[0]] = res
|
(*variables)[name] = res
|
||||||
|
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
@ -215,8 +232,9 @@ func (variables *Variables) decodeVariableBlock(block *hcl.Block, ectx *hcl.Eval
|
||||||
// them.
|
// them.
|
||||||
const VarEnvPrefix = "PKR_VAR_"
|
const VarEnvPrefix = "PKR_VAR_"
|
||||||
|
|
||||||
func (variables Variables) collectVariableValues(env []string, files []*hcl.File, argv map[string]string) hcl.Diagnostics {
|
func (cfg *PackerConfig) collectInputVariableValues(env []string, files []*hcl.File, argv map[string]string) hcl.Diagnostics {
|
||||||
var diags hcl.Diagnostics
|
var diags hcl.Diagnostics
|
||||||
|
variables := cfg.InputVariables
|
||||||
|
|
||||||
for _, raw := range env {
|
for _, raw := range env {
|
||||||
if !strings.HasPrefix(raw, VarEnvPrefix) {
|
if !strings.HasPrefix(raw, VarEnvPrefix) {
|
||||||
|
@ -245,6 +263,7 @@ func (variables Variables) collectVariableValues(env []string, files []*hcl.File
|
||||||
if moreDiags.HasErrors() {
|
if moreDiags.HasErrors() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val, valDiags := expr.Value(nil)
|
val, valDiags := expr.Value(nil)
|
||||||
diags = append(diags, valDiags...)
|
diags = append(diags, valDiags...)
|
||||||
if variable.Type != cty.NilType {
|
if variable.Type != cty.NilType {
|
||||||
|
@ -310,7 +329,20 @@ func (variables Variables) collectVariableValues(env []string, files []*hcl.File
|
||||||
for name, attr := range attrs {
|
for name, attr := range attrs {
|
||||||
variable, found := variables[name]
|
variable, found := variables[name]
|
||||||
if !found {
|
if !found {
|
||||||
// No file defines this variable; let's skip it
|
sev := hcl.DiagWarning
|
||||||
|
if cfg.ValidationOptions.Strict {
|
||||||
|
sev = hcl.DiagError
|
||||||
|
}
|
||||||
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
|
Severity: sev,
|
||||||
|
Summary: "Undefined variable",
|
||||||
|
Detail: fmt.Sprintf("A %q variable was set but was "+
|
||||||
|
"not found in known variables. To declare "+
|
||||||
|
"variable %q, place this block in one of your"+
|
||||||
|
".pkr files, such as variables.pkr.hcl",
|
||||||
|
name, name),
|
||||||
|
Context: attr.Range.Ptr(),
|
||||||
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,8 +372,8 @@ func (variables Variables) collectVariableValues(env []string, files []*hcl.File
|
||||||
variable, found := variables[name]
|
variable, found := variables[name]
|
||||||
if !found {
|
if !found {
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
Severity: hcl.DiagWarning,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Unknown -var variable",
|
Summary: "Undefined -var variable",
|
||||||
Detail: fmt.Sprintf("A %q variable was passed in the command "+
|
Detail: fmt.Sprintf("A %q variable was passed in the command "+
|
||||||
"line but was not found in known variables."+
|
"line but was not found in known variables."+
|
||||||
"To declare variable %q, place this block in one of your"+
|
"To declare variable %q, place this block in one of your"+
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/zclconf/go-cty/cty/convert"
|
"github.com/zclconf/go-cty/cty/convert"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
|
"github.com/hashicorp/packer/builder/null"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,36 +25,46 @@ func TestParse_variables(t *testing.T) {
|
||||||
Basedir: filepath.Join("testdata", "variables"),
|
Basedir: filepath.Join("testdata", "variables"),
|
||||||
InputVariables: Variables{
|
InputVariables: Variables{
|
||||||
"image_name": &Variable{
|
"image_name": &Variable{
|
||||||
|
Name: "image_name",
|
||||||
DefaultValue: cty.StringVal("foo-image-{{user `my_secret`}}"),
|
DefaultValue: cty.StringVal("foo-image-{{user `my_secret`}}"),
|
||||||
},
|
},
|
||||||
"key": &Variable{
|
"key": &Variable{
|
||||||
|
Name: "key",
|
||||||
DefaultValue: cty.StringVal("value"),
|
DefaultValue: cty.StringVal("value"),
|
||||||
},
|
},
|
||||||
"my_secret": &Variable{
|
"my_secret": &Variable{
|
||||||
|
Name: "my_secret",
|
||||||
DefaultValue: cty.StringVal("foo"),
|
DefaultValue: cty.StringVal("foo"),
|
||||||
},
|
},
|
||||||
"image_id": &Variable{
|
"image_id": &Variable{
|
||||||
|
Name: "image_id",
|
||||||
DefaultValue: cty.StringVal("image-id-default"),
|
DefaultValue: cty.StringVal("image-id-default"),
|
||||||
},
|
},
|
||||||
"port": &Variable{
|
"port": &Variable{
|
||||||
|
Name: "port",
|
||||||
DefaultValue: cty.NumberIntVal(42),
|
DefaultValue: cty.NumberIntVal(42),
|
||||||
},
|
},
|
||||||
"availability_zone_names": &Variable{
|
"availability_zone_names": &Variable{
|
||||||
|
Name: "availability_zone_names",
|
||||||
DefaultValue: cty.ListVal([]cty.Value{
|
DefaultValue: cty.ListVal([]cty.Value{
|
||||||
cty.StringVal("us-west-1a"),
|
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{
|
||||||
|
Name: "super_secret_password",
|
||||||
Sensitive: true,
|
Sensitive: true,
|
||||||
|
DefaultValue: cty.NullVal(cty.String),
|
||||||
Description: fmt.Sprintln("Handle with care plz"),
|
Description: fmt.Sprintln("Handle with care plz"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
LocalVariables: Variables{
|
LocalVariables: Variables{
|
||||||
"owner": &Variable{
|
"owner": &Variable{
|
||||||
|
Name: "owner",
|
||||||
DefaultValue: cty.StringVal("Community Team"),
|
DefaultValue: cty.StringVal("Community Team"),
|
||||||
},
|
},
|
||||||
"service_name": &Variable{
|
"service_name": &Variable{
|
||||||
|
Name: "service_name",
|
||||||
DefaultValue: cty.StringVal("forum"),
|
DefaultValue: cty.StringVal("forum"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -68,7 +79,9 @@ func TestParse_variables(t *testing.T) {
|
||||||
&PackerConfig{
|
&PackerConfig{
|
||||||
Basedir: filepath.Join("testdata", "variables"),
|
Basedir: filepath.Join("testdata", "variables"),
|
||||||
InputVariables: Variables{
|
InputVariables: Variables{
|
||||||
"boolean_value": &Variable{},
|
"boolean_value": &Variable{
|
||||||
|
Name: "boolean_value",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
true, true,
|
true, true,
|
||||||
|
@ -81,7 +94,9 @@ func TestParse_variables(t *testing.T) {
|
||||||
&PackerConfig{
|
&PackerConfig{
|
||||||
Basedir: filepath.Join("testdata", "variables"),
|
Basedir: filepath.Join("testdata", "variables"),
|
||||||
InputVariables: Variables{
|
InputVariables: Variables{
|
||||||
"boolean_value": &Variable{},
|
"boolean_value": &Variable{
|
||||||
|
Name: "boolean_value",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
true, true,
|
true, true,
|
||||||
|
@ -94,26 +109,84 @@ func TestParse_variables(t *testing.T) {
|
||||||
&PackerConfig{
|
&PackerConfig{
|
||||||
Basedir: filepath.Join("testdata", "variables"),
|
Basedir: filepath.Join("testdata", "variables"),
|
||||||
InputVariables: Variables{
|
InputVariables: Variables{
|
||||||
"broken_type": &Variable{},
|
"broken_type": &Variable{
|
||||||
|
Name: "broken_type",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
true, true,
|
true, true,
|
||||||
[]packer.Build{},
|
[]packer.Build{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{"invalid default type",
|
|
||||||
|
{"unknown key",
|
||||||
defaultParser,
|
defaultParser,
|
||||||
parseTestArgs{"testdata/variables/unknown_key.pkr.hcl", nil},
|
parseTestArgs{"testdata/variables/unknown_key.pkr.hcl", nil},
|
||||||
&PackerConfig{
|
&PackerConfig{
|
||||||
Basedir: filepath.Join("testdata", "variables"),
|
Basedir: filepath.Join("testdata", "variables"),
|
||||||
InputVariables: Variables{
|
InputVariables: Variables{
|
||||||
"broken_type": &Variable{},
|
"broken_variable": &Variable{
|
||||||
|
Name: "broken_variable",
|
||||||
|
DefaultValue: cty.BoolVal(true),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
true, false,
|
true, false,
|
||||||
[]packer.Build{},
|
[]packer.Build{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{"unset used variable",
|
||||||
|
defaultParser,
|
||||||
|
parseTestArgs{"testdata/variables/unset_used_string_variable.pkr.hcl", nil},
|
||||||
|
&PackerConfig{
|
||||||
|
Basedir: filepath.Join("testdata", "variables"),
|
||||||
|
InputVariables: Variables{
|
||||||
|
"foo": &Variable{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true, true,
|
||||||
|
[]packer.Build{},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{"unset unused variable",
|
||||||
|
defaultParser,
|
||||||
|
parseTestArgs{"testdata/variables/unset_unused_string_variable.pkr.hcl", nil},
|
||||||
|
&PackerConfig{
|
||||||
|
Basedir: filepath.Join("testdata", "variables"),
|
||||||
|
InputVariables: Variables{
|
||||||
|
"foo": &Variable{
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Sources: map[SourceRef]*SourceBlock{
|
||||||
|
SourceRef{"null", "null-builder"}: &SourceBlock{
|
||||||
|
Name: "null-builder",
|
||||||
|
Type: "null",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Builds: Builds{
|
||||||
|
&BuildBlock{
|
||||||
|
Sources: []SourceRef{SourceRef{"null", "null-builder"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true, true,
|
||||||
|
[]packer.Build{
|
||||||
|
&packer.CoreBuild{
|
||||||
|
Type: "null",
|
||||||
|
Builder: &null.Builder{},
|
||||||
|
Provisioners: []packer.CoreBuildProvisioner{},
|
||||||
|
PostProcessors: [][]packer.CoreBuildPostProcessor{},
|
||||||
|
Prepared: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
|
||||||
{"locals within another locals usage in different files",
|
{"locals within another locals usage in different files",
|
||||||
defaultParser,
|
defaultParser,
|
||||||
parseTestArgs{"testdata/variables/complicated", nil},
|
parseTestArgs{"testdata/variables/complicated", nil},
|
||||||
|
@ -121,23 +194,29 @@ func TestParse_variables(t *testing.T) {
|
||||||
Basedir: "testdata/variables/complicated",
|
Basedir: "testdata/variables/complicated",
|
||||||
InputVariables: Variables{
|
InputVariables: Variables{
|
||||||
"name_prefix": &Variable{
|
"name_prefix": &Variable{
|
||||||
|
Name: "name_prefix",
|
||||||
DefaultValue: cty.StringVal("foo"),
|
DefaultValue: cty.StringVal("foo"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
LocalVariables: Variables{
|
LocalVariables: Variables{
|
||||||
"name_prefix": &Variable{
|
"name_prefix": &Variable{
|
||||||
|
Name: "name_prefix",
|
||||||
DefaultValue: cty.StringVal("foo"),
|
DefaultValue: cty.StringVal("foo"),
|
||||||
},
|
},
|
||||||
"foo": &Variable{
|
"foo": &Variable{
|
||||||
|
Name: "foo",
|
||||||
DefaultValue: cty.StringVal("foo"),
|
DefaultValue: cty.StringVal("foo"),
|
||||||
},
|
},
|
||||||
"bar": &Variable{
|
"bar": &Variable{
|
||||||
|
Name: "bar",
|
||||||
DefaultValue: cty.StringVal("foo"),
|
DefaultValue: cty.StringVal("foo"),
|
||||||
},
|
},
|
||||||
"for_var": &Variable{
|
"for_var": &Variable{
|
||||||
|
Name: "for_var",
|
||||||
DefaultValue: cty.StringVal("foo"),
|
DefaultValue: cty.StringVal("foo"),
|
||||||
},
|
},
|
||||||
"bar_var": &Variable{
|
"bar_var": &Variable{
|
||||||
|
Name: "bar_var",
|
||||||
DefaultValue: cty.TupleVal([]cty.Value{
|
DefaultValue: cty.TupleVal([]cty.Value{
|
||||||
cty.StringVal("foo"),
|
cty.StringVal("foo"),
|
||||||
cty.StringVal("foo"),
|
cty.StringVal("foo"),
|
||||||
|
@ -174,8 +253,10 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
variables Variables
|
variables Variables
|
||||||
|
validationOptions ValidationOptions
|
||||||
args args
|
args args
|
||||||
wantDiags bool
|
wantDiags bool
|
||||||
|
wantDiagsHasError bool
|
||||||
wantVariables Variables
|
wantVariables Variables
|
||||||
wantValues map[string]cty.Value
|
wantValues map[string]cty.Value
|
||||||
}{
|
}{
|
||||||
|
@ -329,11 +410,39 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{name: "undefined but set value",
|
{name: "undefined but set value - pkrvar file - normal mode",
|
||||||
variables: Variables{},
|
variables: Variables{},
|
||||||
args: args{
|
args: args{
|
||||||
env: []string{`PKR_VAR_unused_string=value`},
|
hclFiles: []string{`undefined_string="value"`},
|
||||||
hclFiles: []string{`unused_string="value"`},
|
},
|
||||||
|
|
||||||
|
// output
|
||||||
|
wantDiags: true,
|
||||||
|
wantDiagsHasError: false,
|
||||||
|
wantVariables: Variables{},
|
||||||
|
wantValues: map[string]cty.Value{},
|
||||||
|
},
|
||||||
|
|
||||||
|
{name: "undefined but set value - pkrvar file - strict mode",
|
||||||
|
variables: Variables{},
|
||||||
|
validationOptions: ValidationOptions{
|
||||||
|
Strict: true,
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
hclFiles: []string{`undefined_string="value"`},
|
||||||
|
},
|
||||||
|
|
||||||
|
// output
|
||||||
|
wantDiags: true,
|
||||||
|
wantDiagsHasError: true,
|
||||||
|
wantVariables: Variables{},
|
||||||
|
wantValues: map[string]cty.Value{},
|
||||||
|
},
|
||||||
|
|
||||||
|
{name: "undefined but set value - env",
|
||||||
|
variables: Variables{},
|
||||||
|
args: args{
|
||||||
|
env: []string{`PKR_VAR_undefined_string=value`},
|
||||||
},
|
},
|
||||||
|
|
||||||
// output
|
// output
|
||||||
|
@ -342,16 +451,17 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
||||||
wantValues: map[string]cty.Value{},
|
wantValues: map[string]cty.Value{},
|
||||||
},
|
},
|
||||||
|
|
||||||
{name: "undefined but set value - args",
|
{name: "undefined but set value - argv",
|
||||||
variables: Variables{},
|
variables: Variables{},
|
||||||
args: args{
|
args: args{
|
||||||
argv: map[string]string{
|
argv: map[string]string{
|
||||||
"unused_string": "value",
|
"undefined_string": "value",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// output
|
// output
|
||||||
wantDiags: true,
|
wantDiags: true,
|
||||||
|
wantDiagsHasError: true,
|
||||||
wantVariables: Variables{},
|
wantVariables: Variables{},
|
||||||
wantValues: map[string]cty.Value{},
|
wantValues: map[string]cty.Value{},
|
||||||
},
|
},
|
||||||
|
@ -368,6 +478,7 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
||||||
|
|
||||||
// output
|
// output
|
||||||
wantDiags: true,
|
wantDiags: true,
|
||||||
|
wantDiagsHasError: true,
|
||||||
wantVariables: Variables{
|
wantVariables: Variables{
|
||||||
"used_string": &Variable{
|
"used_string": &Variable{
|
||||||
Type: cty.List(cty.String),
|
Type: cty.List(cty.String),
|
||||||
|
@ -391,6 +502,7 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
||||||
|
|
||||||
// output
|
// output
|
||||||
wantDiags: true,
|
wantDiags: true,
|
||||||
|
wantDiagsHasError: true,
|
||||||
wantVariables: Variables{
|
wantVariables: Variables{
|
||||||
"used_string": &Variable{
|
"used_string": &Variable{
|
||||||
Type: cty.Bool,
|
Type: cty.Bool,
|
||||||
|
@ -416,6 +528,7 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
||||||
|
|
||||||
// output
|
// output
|
||||||
wantDiags: true,
|
wantDiags: true,
|
||||||
|
wantDiagsHasError: true,
|
||||||
wantVariables: Variables{
|
wantVariables: Variables{
|
||||||
"used_string": &Variable{
|
"used_string": &Variable{
|
||||||
Type: cty.Bool,
|
Type: cty.Bool,
|
||||||
|
@ -435,6 +548,7 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
||||||
|
|
||||||
// output
|
// output
|
||||||
wantDiags: true,
|
wantDiags: true,
|
||||||
|
wantDiagsHasError: true,
|
||||||
wantVariables: Variables{},
|
wantVariables: Variables{},
|
||||||
wantValues: map[string]cty.Value{},
|
wantValues: map[string]cty.Value{},
|
||||||
},
|
},
|
||||||
|
@ -450,9 +564,17 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
||||||
}
|
}
|
||||||
files = append(files, file)
|
files = append(files, file)
|
||||||
}
|
}
|
||||||
if gotDiags := tt.variables.collectVariableValues(tt.args.env, files, tt.args.argv); (gotDiags == nil) == tt.wantDiags {
|
cfg := &PackerConfig{
|
||||||
|
InputVariables: tt.variables,
|
||||||
|
ValidationOptions: tt.validationOptions,
|
||||||
|
}
|
||||||
|
gotDiags := cfg.collectInputVariableValues(tt.args.env, files, tt.args.argv)
|
||||||
|
if (gotDiags == nil) == tt.wantDiags {
|
||||||
t.Fatalf("Variables.collectVariableValues() = %v, want %v", gotDiags, tt.wantDiags)
|
t.Fatalf("Variables.collectVariableValues() = %v, want %v", gotDiags, tt.wantDiags)
|
||||||
}
|
}
|
||||||
|
if tt.wantDiagsHasError != gotDiags.HasErrors() {
|
||||||
|
t.Fatalf("Variables.collectVariableValues() unexpected diagnostics HasErrors. %s", gotDiags)
|
||||||
|
}
|
||||||
if diff := cmp.Diff(fmt.Sprintf("%#v", tt.wantVariables), fmt.Sprintf("%#v", tt.variables)); diff != "" {
|
if diff := cmp.Diff(fmt.Sprintf("%#v", tt.wantVariables), fmt.Sprintf("%#v", tt.variables)); diff != "" {
|
||||||
t.Fatalf("didn't get expected variables: %s", diff)
|
t.Fatalf("didn't get expected variables: %s", diff)
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,3 +278,48 @@ precedence over earlier ones:
|
||||||
|
|
||||||
~> **Important:** Variables with map and object values behave the same way as
|
~> **Important:** Variables with map and object values behave the same way as
|
||||||
other variables: the last value found overrides the previous values.
|
other variables: the last value found overrides the previous values.
|
||||||
|
|
||||||
|
### A variable value must be known :
|
||||||
|
|
||||||
|
Take the following variable for example:
|
||||||
|
|
||||||
|
``` hcl
|
||||||
|
variable "foo" {
|
||||||
|
type = string
|
||||||
|
```
|
||||||
|
|
||||||
|
Here `foo` must have a known value but you can default it to `null` to make
|
||||||
|
this behavior optional :
|
||||||
|
|
||||||
|
| | no default | `default = null` | `default = "xy"` |
|
||||||
|
|:---------------------------:|:----------------------------:|:----------------:|:----------------:|
|
||||||
|
| foo unused | error, "foo needs to be set" | - | - |
|
||||||
|
| var.foo | error, "foo needs to be set" | null¹ | xy |
|
||||||
|
| `PKR_VAR_foo=yz`<br>var.foo | yz | yz | yz |
|
||||||
|
| `-var foo=yz`<br>var.foo | yz | yz | yz |
|
||||||
|
|
||||||
|
1: Null is a valid value. Packer will only error when the receiving field needs
|
||||||
|
a value, example:
|
||||||
|
|
||||||
|
``` hcl
|
||||||
|
variable "example" {
|
||||||
|
type = string
|
||||||
|
default = null
|
||||||
|
}
|
||||||
|
|
||||||
|
source "example" "foo" {
|
||||||
|
arg = var.example
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above case, as long as "arg" is optional for an "example" source, there is no error and arg won’t be set.
|
||||||
|
|
||||||
|
|
||||||
|
### Setting an unknown variable will not always fail :
|
||||||
|
|
||||||
|
| Usage | packer validate | any other packer command |
|
||||||
|
|:------------------------------:|:-----------------------:|:-------------------------:|
|
||||||
|
| `bar=yz` in .pkrvars.hcl file. | error, "bar undeclared" | warning, "bar undeclared" |
|
||||||
|
| `var.bar` in .pkr.hcl file | error, "bar undeclared" | error, "bar undeclared" |
|
||||||
|
| `-var bar=yz` argument | error, "bar undeclared" | error, "bar undeclared" |
|
||||||
|
| `export PKR_VAR_bar=yz` | - | - |
|
||||||
|
|
Loading…
Reference in New Issue