packer/command/cli.go
Wilken Rivera 57cbe4e203
core: Update validation options for undeclared variables (#12104)
* 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
```
2022-11-14 17:06:45 -05:00

184 lines
5.6 KiB
Go

package command
import (
"flag"
"strings"
"github.com/hashicorp/packer/command/enumflag"
kvflag "github.com/hashicorp/packer/command/flag-kv"
sliceflag "github.com/hashicorp/packer/command/flag-slice"
)
//go:generate enumer -type configType -trimprefix ConfigType -transform snake
type configType int
const (
ConfigTypeJSON configType = iota // default config type
ConfigTypeHCL2
)
func (c *configType) Set(value string) error {
v, err := configTypeString(value)
if err == nil {
*c = v
}
return err
}
// ConfigType tells what type of config we should use, it can return values
// like "hcl" or "json".
// Make sure Args was correctly set before.
func (ma *MetaArgs) GetConfigType() (configType, error) {
if ma.Path == "" {
return ma.ConfigType, nil
}
name := ma.Path
if name == "-" {
// TODO(azr): To allow piping HCL2 confs (when args is "-"), we probably
// will need to add a setting that says "this is an HCL config".
return ma.ConfigType, nil
}
if strings.HasSuffix(name, ".pkr.hcl") ||
strings.HasSuffix(name, ".pkr.json") {
return ConfigTypeHCL2, nil
}
isDir, err := isDir(name)
if isDir {
return ConfigTypeHCL2, err
}
return ma.ConfigType, err
}
// NewMetaArgs parses cli args and put possible values
func (ma *MetaArgs) AddFlagSets(fs *flag.FlagSet) {
fs.Var((*sliceflag.StringFlag)(&ma.Only), "only", "")
fs.Var((*sliceflag.StringFlag)(&ma.Except), "except", "")
fs.Var((*kvflag.Flag)(&ma.Vars), "var", "")
fs.Var((*kvflag.StringSlice)(&ma.VarFiles), "var-file", "")
fs.Var(&ma.ConfigType, "config-type", "set to 'hcl2' to run in hcl2 mode when no file is passed.")
}
// MetaArgs defines commonalities between all commands
type MetaArgs struct {
// TODO(azr): in the future, I want to allow passing multiple path to
// merge HCL confs together; but this will probably need an RFC first.
Path string
Only, Except []string
Vars map[string]string
VarFiles []string
// set to "hcl2" to force hcl2 mode
ConfigType configType
// WarnOnUndeclared does not have a common default, as the default varies per sub-command usage.
// Refer to individual command FlagSets for usage.
WarnOnUndeclaredVar bool
}
func (ba *BuildArgs) AddFlagSets(flags *flag.FlagSet) {
flags.BoolVar(&ba.Color, "color", true, "")
flags.BoolVar(&ba.Debug, "debug", false, "")
flags.BoolVar(&ba.Force, "force", false, "")
flags.BoolVar(&ba.TimestampUi, "timestamp-ui", false, "")
flags.BoolVar(&ba.MachineReadable, "machine-readable", false, "")
flags.Int64Var(&ba.ParallelBuilds, "parallel-builds", 0, "")
flagOnError := enumflag.New(&ba.OnError, "cleanup", "abort", "ask", "run-cleanup-provisioner")
flags.Var(flagOnError, "on-error", "")
flags.BoolVar(&ba.MetaArgs.WarnOnUndeclaredVar, "warn-on-undeclared-var", false, "Show warnings for variable files containing undeclared variables.")
ba.MetaArgs.AddFlagSets(flags)
}
// BuildArgs represents a parsed cli line for a `packer build`
type BuildArgs struct {
MetaArgs
Debug, Force bool
Color, TimestampUi, MachineReadable bool
ParallelBuilds int64
OnError string
}
func (ia *InitArgs) AddFlagSets(flags *flag.FlagSet) {
flags.BoolVar(&ia.Upgrade, "upgrade", false, "upgrade any present plugin to the highest allowed version.")
ia.MetaArgs.AddFlagSets(flags)
}
// InitArgs represents a parsed cli line for a `packer init <path>`
type InitArgs struct {
MetaArgs
Upgrade bool
}
// PluginsRequiredArgs represents a parsed cli line for a `packer plugins required <path>`
type PluginsRequiredArgs struct {
MetaArgs
}
// ConsoleArgs represents a parsed cli line for a `packer console`
type ConsoleArgs struct {
MetaArgs
}
func (fa *FixArgs) AddFlagSets(flags *flag.FlagSet) {
flags.BoolVar(&fa.Validate, "validate", true, "")
fa.MetaArgs.AddFlagSets(flags)
}
// FixArgs represents a parsed cli line for a `packer fix`
type FixArgs struct {
MetaArgs
Validate bool
}
func (va *ValidateArgs) AddFlagSets(flags *flag.FlagSet) {
flags.BoolVar(&va.SyntaxOnly, "syntax-only", false, "check syntax only")
flags.BoolVar(&va.NoWarnUndeclaredVar, "no-warn-undeclared-var", false, "Ignore warnings for variable files containing undeclared variables.")
va.MetaArgs.AddFlagSets(flags)
}
// ValidateArgs represents a parsed cli line for a `packer validate`
type ValidateArgs struct {
MetaArgs
SyntaxOnly, NoWarnUndeclaredVar bool
}
func (va *InspectArgs) AddFlagSets(flags *flag.FlagSet) {
va.MetaArgs.AddFlagSets(flags)
}
// InspectArgs represents a parsed cli line for a `packer inspect`
type InspectArgs struct {
MetaArgs
}
func (va *HCL2UpgradeArgs) AddFlagSets(flags *flag.FlagSet) {
flags.StringVar(&va.OutputFile, "output-file", "", "File where to put the hcl2 generated config. Defaults to JSON_TEMPLATE.pkr.hcl")
flags.BoolVar(&va.WithAnnotations, "with-annotations", false, "Adds helper annotations with information about the generated HCL2 blocks.")
va.MetaArgs.AddFlagSets(flags)
}
// HCL2UpgradeArgs represents a parsed cli line for a `packer hcl2_upgrade`
type HCL2UpgradeArgs struct {
MetaArgs
OutputFile string
WithAnnotations bool
}
func (va *FormatArgs) AddFlagSets(flags *flag.FlagSet) {
flags.BoolVar(&va.Check, "check", false, "check if the input is formatted")
flags.BoolVar(&va.Diff, "diff", false, "display the diff of formatting changes")
flags.BoolVar(&va.Write, "write", true, "overwrite source files instead of writing to stdout")
flags.BoolVar(&va.Recursive, "recursive", false, "Also process files in subdirectories")
va.MetaArgs.AddFlagSets(flags)
}
// FormatArgs represents a parsed cli line for `packer fmt`
type FormatArgs struct {
MetaArgs
Check, Diff, Write, Recursive bool
}