mirror of
https://github.com/hashicorp/packer.git
synced 2026-02-28 04:21:10 -05:00
* Update validation options for undeclared variables
In an effort to help users move from JSON to HCL2 templates the support for
variable definitions files are being updated to ignore undeclared
variable warnings on build execution. For legacy JSON templates builds no
warnings are displayed when var-files contain undeclared variables.
Since preferred mode HCL2 templates is to be explicit with variable
declarations - they must be declared to be used - validation for
undeclared variables still warns when running `packer validate`. A new
flag has been added to the validate command that can be used to disable
undeclared variable warnings.
* Update validation test for unused variables
Example Run
```
~> go run . validate -no-warn-undeclared-var -var-file
command/test-fixtures/validate/var-file-tests/undeclared.pkrvars.hcl
command/test-fixtures/validate/var-file-tests/basic.pkr.hcl
The configuration is valid.
~> go run . validate -var-file
command/test-fixtures/validate/var-file-tests/undeclared.pkrvars.hcl
command/test-fixtures/validate/var-file-tests/basic.pkr.hcl
Warning: Undefined variable
The variable "unused" was set but was not declared as an input variable.
To declare variable "unused" place this block in one of your .pkr.hcl
files,
such as variables.pkr.hcl
variable "unused" {
type = string
default = null
}
The configuration is valid.
~> go run . build -var-file
command/test-fixtures/validate/var-file-tests/undeclared.pkrvars.hcl
command/test-fixtures/validate/var-file-tests/basic.pkr.hcl
file.chocolate: output will be in this color.
Build 'file.chocolate' finished after 744 microseconds.
==> Wait completed after 798 microseconds
==> Builds finished. The artifacts of successful builds are:
--> file.chocolate: Stored file: chocolate.txt
```
* Rename Strict field to WarnOnUndeclaredVar
The field name Strict is a bit vague since it is only used for
checking against undeclared variables within a var-file definition.
To mitigate against potential overloading of this field it is
being renamed to be more explicit on its usage.
* command/build: Add warn-on-undeclared-var flag
Now that the default behaviour is to not display warnings for undeclared variables
an optional flag has been added to toggle the old behaviour.
```
~> go run . build -warn-on-undeclared-var -var-file command/test-fixtures/validate/var-file-tests/undeclared.pkrvars.hcl command/test-fixtures/validate/var-file-tests/basic.pkr.hcl
Warning: Undefined variable
The variable "unused" was set but was not declared as an input variable.
To declare variable "unused" place this block in one of your .pkr.hcl files,
such as variables.pkr.hcl
variable "unused" {
type = string
default = null
}
file.chocolate: output will be in this color.
Build 'file.chocolate' finished after 762 microseconds.
==> Wait completed after 799 microseconds
==> Builds finished. The artifacts of successful builds are:
--> file.chocolate: Stored file: chocolate.txt
```
176 lines
5 KiB
Go
176 lines
5 KiB
Go
package command
|
|
|
|
import (
|
|
"bufio"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/hashicorp/hcl/v2/hclparse"
|
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
"github.com/hashicorp/packer-plugin-sdk/template"
|
|
kvflag "github.com/hashicorp/packer/command/flag-kv"
|
|
"github.com/hashicorp/packer/hcl2template"
|
|
"github.com/hashicorp/packer/helper/wrappedstreams"
|
|
"github.com/hashicorp/packer/packer"
|
|
"github.com/hashicorp/packer/version"
|
|
)
|
|
|
|
// FlagSetFlags is an enum to define what flags are present in the
|
|
// default FlagSet returned by Meta.FlagSet
|
|
type FlagSetFlags uint
|
|
|
|
const (
|
|
FlagSetNone FlagSetFlags = 0
|
|
FlagSetBuildFilter FlagSetFlags = 1 << iota
|
|
FlagSetVars
|
|
)
|
|
|
|
// Meta contains the meta-options and functionality that nearly every
|
|
// Packer command inherits.
|
|
type Meta struct {
|
|
CoreConfig *packer.CoreConfig
|
|
Ui packersdk.Ui
|
|
Version string
|
|
}
|
|
|
|
// Core returns the core for the given template given the configured
|
|
// CoreConfig and user variables on this Meta.
|
|
func (m *Meta) Core(tpl *template.Template, cla *MetaArgs) (*packer.Core, error) {
|
|
// Copy the config so we don't modify it
|
|
config := *m.CoreConfig
|
|
config.Template = tpl
|
|
|
|
fj := &kvflag.FlagJSON{}
|
|
// First populate fj with contents from var files
|
|
for _, file := range cla.VarFiles {
|
|
err := fj.Set(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
// Now read fj values back into flagvars and set as config.Variables. Only
|
|
// add to flagVars if the key doesn't already exist, because flagVars comes
|
|
// from the command line and should not be overridden by variable files.
|
|
if cla.Vars == nil {
|
|
cla.Vars = map[string]string{}
|
|
}
|
|
for k, v := range *fj {
|
|
if _, exists := cla.Vars[k]; !exists {
|
|
cla.Vars[k] = v
|
|
}
|
|
}
|
|
config.Variables = cla.Vars
|
|
|
|
core := packer.NewCore(&config)
|
|
return core, nil
|
|
}
|
|
|
|
// FlagSet returns a FlagSet with the common flags that every
|
|
// command implements. The exact behavior of FlagSet can be configured
|
|
// using the flags as the second parameter, for example to disable
|
|
// build settings on the commands that don't handle builds.
|
|
func (m *Meta) FlagSet(n string, _ FlagSetFlags) *flag.FlagSet {
|
|
f := flag.NewFlagSet(n, flag.ContinueOnError)
|
|
|
|
// Create an io.Writer that writes to our Ui properly for errors.
|
|
// This is kind of a hack, but it does the job. Basically: create
|
|
// a pipe, use a scanner to break it into lines, and output each line
|
|
// to the UI. Do this forever.
|
|
errR, errW := io.Pipe()
|
|
errScanner := bufio.NewScanner(errR)
|
|
go func() {
|
|
for errScanner.Scan() {
|
|
m.Ui.Error(errScanner.Text())
|
|
}
|
|
}()
|
|
f.SetOutput(errW)
|
|
|
|
return f
|
|
}
|
|
|
|
// ValidateFlags should be called after parsing flags to validate the
|
|
// given flags
|
|
func (m *Meta) ValidateFlags() error {
|
|
// TODO
|
|
return nil
|
|
}
|
|
|
|
// StdinPiped returns true if the input is piped.
|
|
func (m *Meta) StdinPiped() bool {
|
|
fi, err := wrappedstreams.Stdin().Stat()
|
|
if err != nil {
|
|
// If there is an error, let's just say its not piped
|
|
return false
|
|
}
|
|
|
|
return fi.Mode()&os.ModeNamedPipe != 0
|
|
}
|
|
|
|
func (m *Meta) GetConfig(cla *MetaArgs) (packer.Handler, int) {
|
|
cfgType, err := cla.GetConfigType()
|
|
if err != nil {
|
|
m.Ui.Error(fmt.Sprintf("%q: %s", cla.Path, err))
|
|
return nil, 1
|
|
}
|
|
|
|
switch cfgType {
|
|
case ConfigTypeHCL2:
|
|
// TODO(azr): allow to pass a slice of files here.
|
|
return m.GetConfigFromHCL(cla)
|
|
default:
|
|
// TODO: uncomment once we've polished HCL a bit more.
|
|
// c.Ui.Say(`Legacy JSON Configuration Will Be Used.
|
|
// The template will be parsed in the legacy configuration style. This style
|
|
// will continue to work but users are encouraged to move to the new style.
|
|
// See: https://packer.io/guides/hcl
|
|
// `)
|
|
return m.GetConfigFromJSON(cla)
|
|
}
|
|
}
|
|
|
|
func (m *Meta) GetConfigFromHCL(cla *MetaArgs) (*hcl2template.PackerConfig, int) {
|
|
parser := &hcl2template.Parser{
|
|
CorePackerVersion: version.SemVer,
|
|
CorePackerVersionString: version.FormattedVersion(),
|
|
Parser: hclparse.NewParser(),
|
|
PluginConfig: m.CoreConfig.Components.PluginConfig,
|
|
ValidationOptions: hcl2template.ValidationOptions{
|
|
WarnOnUndeclaredVar: cla.WarnOnUndeclaredVar,
|
|
},
|
|
}
|
|
cfg, diags := parser.Parse(cla.Path, cla.VarFiles, cla.Vars)
|
|
return cfg, writeDiags(m.Ui, parser.Files(), diags)
|
|
}
|
|
|
|
func (m *Meta) GetConfigFromJSON(cla *MetaArgs) (packer.Handler, int) {
|
|
// Parse the template
|
|
var tpl *template.Template
|
|
var err error
|
|
if cla.Path == "" {
|
|
// here cla validation passed so this means we want a default builder
|
|
// and we probably are in the console command
|
|
tpl, err = template.Parse(TiniestBuilder)
|
|
} else {
|
|
tpl, err = template.ParseFile(cla.Path)
|
|
}
|
|
|
|
if err != nil {
|
|
m.Ui.Error(fmt.Sprintf("Failed to parse file as legacy JSON template: "+
|
|
"if you are using an HCL template, check your file extensions; they "+
|
|
"should be either *.pkr.hcl or *.pkr.json; see the docs for more "+
|
|
"details: https://www.packer.io/docs/templates/hcl_templates. \n"+
|
|
"Original error: %s", err))
|
|
return nil, 1
|
|
}
|
|
|
|
// Get the core
|
|
core, err := m.Core(tpl, cla)
|
|
ret := 0
|
|
if err != nil {
|
|
m.Ui.Error(err.Error())
|
|
ret = 1
|
|
}
|
|
return core, ret
|
|
}
|