From bb2384c56aeb85aaf71cf7f91752d9c97560f89d Mon Sep 17 00:00:00 2001 From: Lucas Bajolet Date: Thu, 10 Nov 2022 14:56:56 -0500 Subject: [PATCH] command/validate: add option to eval datasources When packer validate is invoked, it does not try to evaluate the datasources before attempting to decide if the template is valid. In many cases, this works, but sometimes it will fail as the value is unknown by the validation code. Since the validation code for all the elements of a Packer template is left to be implemented by plugins, we cannot rely on checking for unknown values everywhere, especially since the unknown references are replaced automatically by a value of the right type for the configuration expected. So, in order for such configurations to be validable, we add an extra option to packer validate, that will let users evaluate the datasources from a template. --- command/cli.go | 2 ++ .../test-fixtures/hcl/local-ds-validate.pkr.hcl | 17 +++++++++++++++++ command/validate.go | 2 +- command/validate_test.go | 13 ++++++++++--- website/content/docs/commands/validate.mdx | 8 ++++++++ 5 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 command/test-fixtures/hcl/local-ds-validate.pkr.hcl diff --git a/command/cli.go b/command/cli.go index 826835ef2..e1eb1ffbc 100644 --- a/command/cli.go +++ b/command/cli.go @@ -136,6 +136,7 @@ type FixArgs struct { 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.") + flags.BoolVar(&va.EvaluateDatasources, "evaluate-datasources", false, "evaluate datasources for validation (HCL2 only, may incur costs)") va.MetaArgs.AddFlagSets(flags) } @@ -144,6 +145,7 @@ func (va *ValidateArgs) AddFlagSets(flags *flag.FlagSet) { type ValidateArgs struct { MetaArgs SyntaxOnly, NoWarnUndeclaredVar bool + EvaluateDatasources bool } func (va *InspectArgs) AddFlagSets(flags *flag.FlagSet) { diff --git a/command/test-fixtures/hcl/local-ds-validate.pkr.hcl b/command/test-fixtures/hcl/local-ds-validate.pkr.hcl new file mode 100644 index 000000000..47a2f9fd1 --- /dev/null +++ b/command/test-fixtures/hcl/local-ds-validate.pkr.hcl @@ -0,0 +1,17 @@ +data "null" "dep" { + input = "upload" +} + +source "null" "test" { + communicator = "none" +} + +build { + sources = ["sources.null.test"] + + provisioner "file" { + source = "test-fixtures/hcl/force.pkr.hcl" + destination = "dest" + direction = "${data.null.dep.output}" + } +} diff --git a/command/validate.go b/command/validate.go index 304db1a38..718fcc121 100644 --- a/command/validate.go +++ b/command/validate.go @@ -63,7 +63,7 @@ func (c *ValidateCommand) RunContext(ctx context.Context, cla *ValidateArgs) int } diags := packerStarter.Initialize(packer.InitializeOptions{ - SkipDatasourcesExecution: true, + SkipDatasourcesExecution: !cla.EvaluateDatasources, }) ret = writeDiags(c.Ui, nil, diags) if ret != 0 { diff --git a/command/validate_test.go b/command/validate_test.go index 5b9b346aa..3a2c6b20a 100644 --- a/command/validate_test.go +++ b/command/validate_test.go @@ -11,8 +11,9 @@ import ( func TestValidateCommand(t *testing.T) { tt := []struct { - path string - exitCode int + path string + exitCode int + extraArgs []string }{ {path: filepath.Join(testFixture("validate"), "build.json")}, {path: filepath.Join(testFixture("validate"), "build.pkr.hcl")}, @@ -39,6 +40,11 @@ func TestValidateCommand(t *testing.T) { // datasource could be unknown at that moment {path: filepath.Join(testFixture("hcl", "data-source-validation.pkr.hcl")), exitCode: 0}, + + // datasource unknown at validation-time without datasource evaluation -> fail on provisioner + {path: filepath.Join(testFixture("hcl", "local-ds-validate.pkr.hcl")), exitCode: 1}, + // datasource unknown at validation-time with datasource evaluation -> success + {path: filepath.Join(testFixture("hcl", "local-ds-validate.pkr.hcl")), exitCode: 0, extraArgs: []string{"--evaluate-datasources"}}, } for _, tc := range tt { @@ -47,7 +53,8 @@ func TestValidateCommand(t *testing.T) { Meta: TestMetaFile(t), } tc := tc - args := []string{tc.path} + args := tc.extraArgs + args = append(args, tc.path) if code := c.Run(args); code != tc.exitCode { fatalCommand(t, c.Meta) } diff --git a/website/content/docs/commands/validate.mdx b/website/content/docs/commands/validate.mdx index 57026315e..8b06c274f 100644 --- a/website/content/docs/commands/validate.mdx +++ b/website/content/docs/commands/validate.mdx @@ -31,6 +31,14 @@ Errors validating build 'vmware'. 1 error(s) occurred: - `-syntax-only` - Only the syntax of the template is checked. The configuration is not validated. +- `-evaluate-datasources` - Evaluate all data sources when validating a template. + This is only valid on HCL2 templates, since JSON templates do not feature + datasources, this option will be ignored. + + ~> **Warning:** Data sources may rely on external services for fetching data, + which can incur some costs at validation if the services being contacted are + billing per operation. + - `-except=foo,bar,baz` - Validates all the builds except those with the comma-separated names. In legacy JSON templates, build names default to the types of their builders (e.g. `docker` or