mirror of
https://github.com/hashicorp/packer.git
synced 2026-09-18 22:11:39 -04: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
```
337 lines
9.8 KiB
Go
337 lines
9.8 KiB
Go
package command
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
"github.com/hashicorp/packer/packer"
|
|
)
|
|
|
|
func TestValidateCommand(t *testing.T) {
|
|
tt := []struct {
|
|
path string
|
|
exitCode int
|
|
}{
|
|
{path: filepath.Join(testFixture("validate"), "build.json")},
|
|
{path: filepath.Join(testFixture("validate"), "build.pkr.hcl")},
|
|
{path: filepath.Join(testFixture("validate"), "build_with_vars.pkr.hcl")},
|
|
{path: filepath.Join(testFixture("validate-invalid"), "bad_provisioner.json"), exitCode: 1},
|
|
{path: filepath.Join(testFixture("validate-invalid"), "missing_build_block.pkr.hcl"), exitCode: 1},
|
|
{path: filepath.Join(testFixture("validate"), "null_var.json"), exitCode: 1},
|
|
{path: filepath.Join(testFixture("validate"), "var_foo_with_no_default.pkr.hcl"), exitCode: 1},
|
|
|
|
{path: testFixture("hcl", "validation", "wrong_pause_before.pkr.hcl"), exitCode: 1},
|
|
|
|
// wrong version fails
|
|
{path: filepath.Join(testFixture("version_req", "base_failure")), exitCode: 1},
|
|
{path: filepath.Join(testFixture("version_req", "base_success")), exitCode: 0},
|
|
|
|
// wrong version field
|
|
{path: filepath.Join(testFixture("version_req", "wrong_field_name")), exitCode: 1},
|
|
|
|
// wrong packer block
|
|
{path: filepath.Join(testFixture("validate", "invalid_packer_block.pkr.hcl")), exitCode: 1},
|
|
|
|
// Should return multiple errors,
|
|
{path: filepath.Join(testFixture("validate", "circular_error.pkr.hcl")), exitCode: 1},
|
|
|
|
// datasource could be unknown at that moment
|
|
{path: filepath.Join(testFixture("hcl", "data-source-validation.pkr.hcl")), exitCode: 0},
|
|
}
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.path, func(t *testing.T) {
|
|
c := &ValidateCommand{
|
|
Meta: TestMetaFile(t),
|
|
}
|
|
tc := tc
|
|
args := []string{tc.path}
|
|
if code := c.Run(args); code != tc.exitCode {
|
|
fatalCommand(t, c.Meta)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateCommand_SkipDatasourceExecution(t *testing.T) {
|
|
datasourceMock := &packersdk.MockDatasource{}
|
|
meta := TestMetaFile(t)
|
|
meta.CoreConfig.Components.PluginConfig.DataSources = packer.MapOfDatasource{
|
|
"mock": func() (packersdk.Datasource, error) {
|
|
return datasourceMock, nil
|
|
},
|
|
}
|
|
c := &ValidateCommand{
|
|
Meta: meta,
|
|
}
|
|
args := []string{filepath.Join(testFixture("validate"), "datasource.pkr.hcl")}
|
|
if code := c.Run(args); code != 0 {
|
|
fatalCommand(t, c.Meta)
|
|
}
|
|
if datasourceMock.ExecuteCalled {
|
|
t.Fatalf("Datasource should not be executed on validation")
|
|
}
|
|
if !datasourceMock.OutputSpecCalled {
|
|
t.Fatalf("Datasource OutPutSpec should be called on validation")
|
|
}
|
|
}
|
|
|
|
func TestValidateCommand_SyntaxOnly(t *testing.T) {
|
|
tt := []struct {
|
|
path string
|
|
exitCode int
|
|
}{
|
|
{path: filepath.Join(testFixture("validate"), "build.json")},
|
|
{path: filepath.Join(testFixture("validate"), "build.pkr.hcl")},
|
|
{path: filepath.Join(testFixture("validate"), "build_with_vars.pkr.hcl")},
|
|
{path: filepath.Join(testFixture("validate-invalid"), "bad_provisioner.json")},
|
|
{path: filepath.Join(testFixture("validate-invalid"), "missing_build_block.pkr.hcl")},
|
|
{path: filepath.Join(testFixture("validate-invalid"), "broken.json"), exitCode: 1},
|
|
{path: filepath.Join(testFixture("validate"), "null_var.json")},
|
|
{path: filepath.Join(testFixture("validate"), "var_foo_with_no_default.pkr.hcl")},
|
|
}
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.path, func(t *testing.T) {
|
|
c := &ValidateCommand{
|
|
Meta: TestMetaFile(t),
|
|
}
|
|
c.CoreConfig.Version = "102.0.0"
|
|
tc := tc
|
|
args := []string{"-syntax-only", tc.path}
|
|
if code := c.Run(args); code != tc.exitCode {
|
|
fatalCommand(t, c.Meta)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateCommandOKVersion(t *testing.T) {
|
|
c := &ValidateCommand{
|
|
Meta: TestMetaFile(t),
|
|
}
|
|
args := []string{
|
|
filepath.Join(testFixture("validate"), "template.json"),
|
|
}
|
|
|
|
// This should pass with a valid configuration version
|
|
c.CoreConfig.Version = "102.0.0"
|
|
if code := c.Run(args); code != 0 {
|
|
fatalCommand(t, c.Meta)
|
|
}
|
|
}
|
|
|
|
func TestValidateCommandBadVersion(t *testing.T) {
|
|
c := &ValidateCommand{
|
|
Meta: TestMetaFile(t),
|
|
}
|
|
args := []string{
|
|
filepath.Join(testFixture("validate"), "template.json"),
|
|
}
|
|
|
|
// This should fail with an invalid configuration version
|
|
c.CoreConfig.Version = "100.0.0"
|
|
if code := c.Run(args); code != 1 {
|
|
t.Errorf("Expected exit code 1")
|
|
}
|
|
|
|
stdout, stderr := GetStdoutAndErrFromTestMeta(t, c.Meta)
|
|
expected := `Error:
|
|
|
|
This template requires Packer version 101.0.0 or higher; using 100.0.0
|
|
|
|
|
|
`
|
|
|
|
if diff := cmp.Diff(expected, stderr); diff != "" {
|
|
t.Errorf("Unexpected output: %s", diff)
|
|
}
|
|
t.Log(stdout)
|
|
}
|
|
|
|
func TestValidateCommandExcept(t *testing.T) {
|
|
tt := []struct {
|
|
name string
|
|
args []string
|
|
exitCode int
|
|
}{
|
|
{
|
|
name: "JSON: validate except build and post-processor",
|
|
args: []string{
|
|
"-except=vanilla,pear",
|
|
filepath.Join(testFixture("validate"), "validate_except.json"),
|
|
},
|
|
},
|
|
{
|
|
name: "JSON: fail validate except build and post-processor",
|
|
args: []string{
|
|
"-except=chocolate,apple",
|
|
filepath.Join(testFixture("validate"), "validate_except.json"),
|
|
},
|
|
exitCode: 1,
|
|
},
|
|
{
|
|
name: "HCL2: validate except build and post-processor",
|
|
args: []string{
|
|
"-except=file.vanilla,pear",
|
|
filepath.Join(testFixture("validate"), "validate_except.pkr.hcl"),
|
|
},
|
|
},
|
|
{
|
|
name: "HCL2: fail validation except build and post-processor",
|
|
args: []string{
|
|
"-except=file.chocolate,apple",
|
|
filepath.Join(testFixture("validate"), "validate_except.pkr.hcl"),
|
|
},
|
|
exitCode: 1,
|
|
},
|
|
}
|
|
|
|
c := &ValidateCommand{
|
|
Meta: TestMetaFile(t),
|
|
}
|
|
c.CoreConfig.Version = "102.0.0"
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
defer cleanup()
|
|
|
|
tc := tc
|
|
if code := c.Run(tc.args); code != tc.exitCode {
|
|
fatalCommand(t, c.Meta)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateCommand_VarFiles(t *testing.T) {
|
|
tt := []struct {
|
|
name string
|
|
path string
|
|
varfile string
|
|
exitCode int
|
|
}{
|
|
{name: "with basic HCL var-file definition",
|
|
path: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "basic.pkr.hcl"),
|
|
varfile: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "basic.pkrvars.hcl"),
|
|
exitCode: 0,
|
|
},
|
|
{name: "with unused variable in var-file definition",
|
|
path: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "basic.pkr.hcl"),
|
|
varfile: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "undeclared.pkrvars.hcl"),
|
|
exitCode: 0,
|
|
},
|
|
{name: "with unused variable in JSON var-file definition",
|
|
path: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "basic.pkr.hcl"),
|
|
varfile: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "undeclared.json"),
|
|
exitCode: 0,
|
|
},
|
|
}
|
|
for _, tc := range tt {
|
|
t.Run(tc.path, func(t *testing.T) {
|
|
c := &ValidateCommand{
|
|
Meta: TestMetaFile(t),
|
|
}
|
|
tc := tc
|
|
args := []string{"-var-file", tc.varfile, tc.path}
|
|
if code := c.Run(args); code != tc.exitCode {
|
|
fatalCommand(t, c.Meta)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateCommand_VarFilesWarnOnUndeclared(t *testing.T) {
|
|
tt := []struct {
|
|
name string
|
|
path string
|
|
varfile string
|
|
exitCode int
|
|
}{
|
|
{name: "default warning with unused variable in HCL var-file definition",
|
|
path: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "basic.pkr.hcl"),
|
|
varfile: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "undeclared.pkrvars.hcl"),
|
|
exitCode: 0,
|
|
},
|
|
{name: "default warning with unused variable in JSON var-file definition",
|
|
path: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "basic.pkr.hcl"),
|
|
varfile: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "undeclared.json"),
|
|
exitCode: 0,
|
|
},
|
|
}
|
|
for _, tc := range tt {
|
|
t.Run(tc.path, func(t *testing.T) {
|
|
c := &ValidateCommand{
|
|
Meta: TestMetaFile(t),
|
|
}
|
|
tc := tc
|
|
args := []string{"-var-file", tc.varfile, tc.path}
|
|
if code := c.Run(args); code != tc.exitCode {
|
|
fatalCommand(t, c.Meta)
|
|
}
|
|
|
|
stdout, stderr := GetStdoutAndErrFromTestMeta(t, c.Meta)
|
|
expected := `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.
|
|
`
|
|
if diff := cmp.Diff(expected, stdout); diff != "" {
|
|
t.Errorf("Unexpected output: %s", diff)
|
|
}
|
|
t.Log(stderr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateCommand_VarFilesDisableWarnOnUndeclared(t *testing.T) {
|
|
tt := []struct {
|
|
name string
|
|
path string
|
|
varfile string
|
|
exitCode int
|
|
}{
|
|
{name: "no-warn-undeclared-var with unused variable in HCL var-file definition",
|
|
path: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "basic.pkr.hcl"),
|
|
varfile: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "undeclared.pkrvars.hcl"),
|
|
exitCode: 0,
|
|
},
|
|
{name: "no-warn-undeclared-var with unused variable in JSON var-file definition",
|
|
path: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "basic.pkr.hcl"),
|
|
varfile: filepath.Join(testFixture(filepath.Join("validate", "var-file-tests")), "undeclared.json"),
|
|
exitCode: 0,
|
|
},
|
|
}
|
|
for _, tc := range tt {
|
|
t.Run(tc.path, func(t *testing.T) {
|
|
c := &ValidateCommand{
|
|
Meta: TestMetaFile(t),
|
|
}
|
|
tc := tc
|
|
args := []string{"-no-warn-undeclared-var", "-var-file", tc.varfile, tc.path}
|
|
if code := c.Run(args); code != tc.exitCode {
|
|
fatalCommand(t, c.Meta)
|
|
}
|
|
|
|
stdout, stderr := GetStdoutAndErrFromTestMeta(t, c.Meta)
|
|
expected := `The configuration is valid.
|
|
`
|
|
if diff := cmp.Diff(expected, stdout); diff != "" {
|
|
t.Errorf("Unexpected output: %s", diff)
|
|
}
|
|
t.Log(stderr)
|
|
})
|
|
}
|
|
}
|