2023-03-02 15:37:05 -05:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
2023-08-10 18:53:29 -04:00
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-03-02 15:37:05 -05:00
|
|
|
|
2019-10-14 11:02:53 -04:00
|
|
|
package hcl2template
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
2024-08-30 14:10:27 -04:00
|
|
|
"reflect"
|
2019-10-14 11:02:53 -04:00
|
|
|
|
2025-06-16 23:52:09 -04:00
|
|
|
"github.com/hashicorp/go-multierror"
|
2020-11-09 15:29:53 -05:00
|
|
|
"github.com/hashicorp/go-version"
|
2019-10-14 11:02:53 -04:00
|
|
|
"github.com/hashicorp/hcl/v2"
|
2020-02-20 04:51:34 -05:00
|
|
|
"github.com/hashicorp/hcl/v2/ext/dynblock"
|
2019-10-14 11:02:53 -04:00
|
|
|
"github.com/hashicorp/hcl/v2/hclparse"
|
2020-12-17 16:29:25 -05:00
|
|
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
2024-08-30 14:10:27 -04:00
|
|
|
"github.com/hashicorp/packer/internal/dag"
|
2019-12-17 05:25:56 -05:00
|
|
|
"github.com/hashicorp/packer/packer"
|
2020-10-02 04:17:07 -04:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
2019-10-14 11:02:53 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
2025-02-11 12:20:00 -05:00
|
|
|
packerLabel = "packer"
|
|
|
|
|
sourceLabel = "source"
|
|
|
|
|
variablesLabel = "variables"
|
|
|
|
|
variableLabel = "variable"
|
|
|
|
|
localsLabel = "locals"
|
|
|
|
|
localLabel = "local"
|
|
|
|
|
dataSourceLabel = "data"
|
|
|
|
|
buildLabel = "build"
|
|
|
|
|
hcpPackerRegistryLabel = "hcp_packer_registry"
|
|
|
|
|
communicatorLabel = "communicator"
|
2019-10-14 11:02:53 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var configSchema = &hcl.BodySchema{
|
|
|
|
|
Blocks: []hcl.BlockHeaderSchema{
|
2020-10-27 05:03:36 -04:00
|
|
|
{Type: packerLabel},
|
2019-10-14 11:02:53 -04:00
|
|
|
{Type: sourceLabel, LabelNames: []string{"type", "name"}},
|
|
|
|
|
{Type: variablesLabel},
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
{Type: variableLabel, LabelNames: []string{"name"}},
|
|
|
|
|
{Type: localsLabel},
|
2021-01-26 04:21:44 -05:00
|
|
|
{Type: localLabel, LabelNames: []string{"name"}},
|
2021-01-20 04:37:16 -05:00
|
|
|
{Type: dataSourceLabel, LabelNames: []string{"type", "name"}},
|
2019-10-14 11:02:53 -04:00
|
|
|
{Type: buildLabel},
|
2025-02-11 12:20:00 -05:00
|
|
|
{Type: hcpPackerRegistryLabel},
|
2019-10-14 11:02:53 -04:00
|
|
|
{Type: communicatorLabel, LabelNames: []string{"type", "name"}},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-27 05:03:36 -04:00
|
|
|
// packerBlockSchema is the schema for a top-level "packer" block in
|
|
|
|
|
// a configuration file.
|
|
|
|
|
var packerBlockSchema = &hcl.BodySchema{
|
|
|
|
|
Attributes: []hcl.AttributeSchema{
|
|
|
|
|
{Name: "required_version"},
|
|
|
|
|
},
|
2021-02-02 12:05:04 -05:00
|
|
|
Blocks: []hcl.BlockHeaderSchema{
|
|
|
|
|
{Type: "required_plugins"},
|
|
|
|
|
},
|
2020-10-27 05:03:36 -04:00
|
|
|
}
|
|
|
|
|
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
// Parser helps you parse HCL folders. It will parse an hcl file or directory
|
|
|
|
|
// and start builders, provisioners and post-processors to configure them with
|
2020-12-09 06:39:54 -05:00
|
|
|
// the parsed HCL and then return a []packersdk.Build. Packer will use that list
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
// of Builds to run everything in order.
|
2019-10-14 11:02:53 -04:00
|
|
|
type Parser struct {
|
2020-11-09 15:29:53 -05:00
|
|
|
CorePackerVersion *version.Version
|
|
|
|
|
|
|
|
|
|
CorePackerVersionString string
|
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
PluginConfig *packer.PluginConfig
|
2022-11-14 17:06:45 -05:00
|
|
|
|
|
|
|
|
ValidationOptions
|
|
|
|
|
|
|
|
|
|
*hclparse.Parser
|
2019-10-14 11:02:53 -04:00
|
|
|
}
|
|
|
|
|
|
2020-01-06 10:05:30 -05:00
|
|
|
const (
|
2020-12-14 10:29:58 -05:00
|
|
|
hcl2FileExt = ".pkr.hcl"
|
|
|
|
|
hcl2JsonFileExt = ".pkr.json"
|
|
|
|
|
hcl2VarFileExt = ".pkrvars.hcl"
|
|
|
|
|
hcl2VarJsonFileExt = ".pkrvars.json"
|
|
|
|
|
hcl2AutoVarFileExt = ".auto.pkrvars.hcl"
|
|
|
|
|
hcl2AutoVarJsonFileExt = ".auto.pkrvars.json"
|
2020-01-06 10:05:30 -05:00
|
|
|
)
|
2019-10-14 11:02:53 -04:00
|
|
|
|
2020-04-30 05:51:24 -04:00
|
|
|
// Parse will Parse all HCL files in filename. Path can be a folder or a file.
|
2020-04-29 10:36:40 -04:00
|
|
|
//
|
2021-03-30 09:53:04 -04:00
|
|
|
// Parse will first Parse packer and variables blocks, omitting the rest, which
|
|
|
|
|
// can be expanded with dynamic blocks. We need to evaluate all variables for
|
|
|
|
|
// that, so that data sources can expand dynamic blocks too.
|
2020-04-30 05:51:24 -04:00
|
|
|
//
|
|
|
|
|
// Parse returns a PackerConfig that contains configuration layout of a packer
|
|
|
|
|
// build; sources(builders)/provisioners/posts-processors will not be started
|
2021-03-30 09:53:04 -04:00
|
|
|
// and their contents wont be verified; Most syntax errors will cause an error,
|
|
|
|
|
// init should be called next to expand dynamic blocks and verify that used
|
|
|
|
|
// things do exist.
|
2020-04-29 10:36:40 -04:00
|
|
|
func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]string) (*PackerConfig, hcl.Diagnostics) {
|
2019-10-14 11:02:53 -04:00
|
|
|
var files []*hcl.File
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
|
|
|
|
|
// parse config files
|
2020-06-05 11:23:54 -04:00
|
|
|
if filename != "" {
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
hclFiles, jsonFiles, moreDiags := GetHCL2Files(filename, hcl2FileExt, hcl2JsonFileExt)
|
2020-03-12 09:27:56 -04:00
|
|
|
diags = append(diags, moreDiags...)
|
2021-03-15 09:07:07 -04:00
|
|
|
if moreDiags.HasErrors() {
|
|
|
|
|
// here this probably means that the file was not found, let's
|
|
|
|
|
// simply leave early.
|
|
|
|
|
return nil, diags
|
|
|
|
|
}
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
if len(hclFiles)+len(jsonFiles) == 0 {
|
2020-07-23 03:25:07 -04:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "Could not find any config file in " + filename,
|
|
|
|
|
Detail: "A config file must be suffixed with `.pkr.hcl` or " +
|
|
|
|
|
"`.pkr.json`. A folder can be referenced.",
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
for _, filename := range hclFiles {
|
|
|
|
|
f, moreDiags := p.ParseHCLFile(filename)
|
|
|
|
|
diags = append(diags, moreDiags...)
|
|
|
|
|
files = append(files, f)
|
|
|
|
|
}
|
|
|
|
|
for _, filename := range jsonFiles {
|
|
|
|
|
f, moreDiags := p.ParseJSONFile(filename)
|
|
|
|
|
diags = append(diags, moreDiags...)
|
|
|
|
|
files = append(files, f)
|
|
|
|
|
}
|
|
|
|
|
if diags.HasErrors() {
|
|
|
|
|
return nil, diags
|
|
|
|
|
}
|
2019-10-14 11:02:53 -04:00
|
|
|
}
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
|
|
|
|
|
basedir := filename
|
|
|
|
|
if isDir, err := isDir(basedir); err == nil && !isDir {
|
|
|
|
|
basedir = filepath.Dir(basedir)
|
2019-10-14 11:02:53 -04:00
|
|
|
}
|
2020-07-23 03:25:07 -04:00
|
|
|
wd, err := os.Getwd()
|
|
|
|
|
if err != nil {
|
|
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "Could not find current working directory",
|
|
|
|
|
Detail: err.Error(),
|
|
|
|
|
})
|
|
|
|
|
}
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
cfg := &PackerConfig{
|
2020-11-09 15:29:53 -05:00
|
|
|
Basedir: basedir,
|
|
|
|
|
Cwd: wd,
|
|
|
|
|
CorePackerVersionString: p.CorePackerVersionString,
|
2022-09-06 17:26:49 -04:00
|
|
|
HCPVars: map[string]cty.Value{},
|
2022-11-14 17:06:45 -05:00
|
|
|
ValidationOptions: p.ValidationOptions,
|
2020-11-09 15:29:53 -05:00
|
|
|
parser: p,
|
|
|
|
|
files: files,
|
2019-10-14 11:02:53 -04:00
|
|
|
}
|
|
|
|
|
|
2020-10-27 05:03:36 -04:00
|
|
|
for _, file := range files {
|
|
|
|
|
coreVersionConstraints, moreDiags := sniffCoreVersionRequirements(file.Body)
|
|
|
|
|
cfg.Packer.VersionConstraints = append(cfg.Packer.VersionConstraints, coreVersionConstraints...)
|
|
|
|
|
diags = append(diags, moreDiags...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Before we go further, we'll check to make sure this version can read
|
2021-02-02 12:05:04 -05:00
|
|
|
// all files, so we can produce a version-related error message rather than
|
2020-10-27 05:03:36 -04:00
|
|
|
// potentially-confusing downstream errors.
|
2024-04-15 15:41:26 -04:00
|
|
|
versionDiags := cfg.CheckCoreVersionRequirements(p.CorePackerVersion.Core())
|
2020-10-27 05:03:36 -04:00
|
|
|
diags = append(diags, versionDiags...)
|
|
|
|
|
if versionDiags.HasErrors() {
|
|
|
|
|
return cfg, diags
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-20 18:54:36 -05:00
|
|
|
// Looks for invalid arguments or unsupported block types
|
|
|
|
|
{
|
|
|
|
|
for _, file := range files {
|
|
|
|
|
_, moreDiags := file.Body.Content(configSchema)
|
|
|
|
|
diags = append(diags, moreDiags...)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-09 14:42:38 -04:00
|
|
|
// Decode required_plugins blocks.
|
|
|
|
|
//
|
2021-02-02 12:05:04 -05:00
|
|
|
// Note: using `latest` ( or actually an empty string ) in a config file
|
|
|
|
|
// does not work and packer will ask you to pick a version
|
|
|
|
|
{
|
|
|
|
|
for _, file := range files {
|
|
|
|
|
diags = append(diags, cfg.decodeRequiredPluginsBlock(file)...)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
// Decode variable blocks so that they are available later on. Here locals
|
2021-02-02 12:05:04 -05:00
|
|
|
// can use input variables so we decode input variables first.
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
{
|
|
|
|
|
for _, file := range files {
|
2020-02-21 06:12:30 -05:00
|
|
|
diags = append(diags, cfg.decodeInputVariables(file)...)
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
}
|
2020-02-21 06:12:30 -05:00
|
|
|
|
2021-01-20 04:37:16 -05:00
|
|
|
for _, file := range files {
|
|
|
|
|
morediags := p.decodeDatasources(file, cfg)
|
|
|
|
|
diags = append(diags, morediags...)
|
|
|
|
|
}
|
|
|
|
|
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
for _, file := range files {
|
2022-01-28 13:21:53 -05:00
|
|
|
moreLocals, morediags := parseLocalVariableBlocks(file)
|
2020-02-21 06:12:30 -05:00
|
|
|
diags = append(diags, morediags...)
|
2020-07-24 04:58:03 -04:00
|
|
|
cfg.LocalBlocks = append(cfg.LocalBlocks, moreLocals...)
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
}
|
2024-06-13 16:12:42 -04:00
|
|
|
|
|
|
|
|
diags = diags.Extend(cfg.checkForDuplicateLocalDefinition())
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parse var files
|
|
|
|
|
{
|
2020-12-14 10:29:58 -05:00
|
|
|
hclVarFiles, jsonVarFiles, moreDiags := GetHCL2Files(filename, hcl2AutoVarFileExt, hcl2AutoVarJsonFileExt)
|
2019-10-14 11:02:53 -04:00
|
|
|
diags = append(diags, moreDiags...)
|
2025-04-15 01:05:16 -04:00
|
|
|
|
|
|
|
|
// Combine all variable files into a single list, preserving the intended precedence and order.
|
|
|
|
|
// The order is: auto-loaded HCL files, auto-loaded JSON files, followed by user-specified varFiles.
|
|
|
|
|
// This ensures that user-specified files can override values from auto-loaded files,
|
|
|
|
|
// and that their relative order is preserved exactly as specified by the user.
|
|
|
|
|
variableFileNames := append(append(hclVarFiles, jsonVarFiles...), varFiles...)
|
|
|
|
|
|
|
|
|
|
var variableFiles []*hcl.File
|
|
|
|
|
|
|
|
|
|
for _, file := range variableFileNames {
|
|
|
|
|
var (
|
|
|
|
|
f *hcl.File
|
|
|
|
|
moreDiags hcl.Diagnostics
|
|
|
|
|
)
|
2020-03-12 09:27:56 -04:00
|
|
|
switch filepath.Ext(file) {
|
|
|
|
|
case ".hcl":
|
2025-04-15 01:05:16 -04:00
|
|
|
f, moreDiags = p.ParseHCLFile(file)
|
2020-03-12 09:27:56 -04:00
|
|
|
case ".json":
|
2025-04-15 01:05:16 -04:00
|
|
|
f, moreDiags = p.ParseJSONFile(file)
|
2020-03-12 09:27:56 -04:00
|
|
|
default:
|
2025-04-15 01:05:16 -04:00
|
|
|
moreDiags = hcl.Diagnostics{
|
|
|
|
|
&hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "Could not guess format of " + file,
|
|
|
|
|
Detail: "A var file must be suffixed with `.hcl` or `.json`.",
|
|
|
|
|
},
|
|
|
|
|
}
|
2020-03-12 09:27:56 -04:00
|
|
|
}
|
2025-04-15 01:05:16 -04:00
|
|
|
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
diags = append(diags, moreDiags...)
|
2020-03-19 08:57:22 -04:00
|
|
|
if moreDiags.HasErrors() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2025-04-15 01:05:16 -04:00
|
|
|
variableFiles = append(variableFiles, f)
|
|
|
|
|
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
}
|
|
|
|
|
|
2025-04-15 01:05:16 -04:00
|
|
|
diags = append(diags, cfg.collectInputVariableValues(os.Environ(), variableFiles, argVars)...)
|
Hcl2 input variables, local variables and functions (#8588)
Mainly redefine or reused what Terraform did.
* allow to used `variables`, `variable` and `local` blocks
* import the following functions and their docs from Terraform: abs, abspath, basename, base64decode, base64encode, bcrypt, can, ceil, chomp, chunklist, cidrhost, cidrnetmask, cidrsubnet, cidrsubnets, coalesce, coalescelist, compact, concat, contains, convert, csvdecode, dirname, distinct, element, file, fileexists, fileset, flatten, floor, format, formatdate, formatlist, indent, index, join, jsondecode, jsonencode, keys, length, log, lookup, lower, max, md5, merge, min, parseint, pathexpand, pow, range, reverse, rsadecrypt, setintersection, setproduct, setunion, sha1, sha256, sha512, signum, slice, sort, split, strrev, substr, timestamp, timeadd, title, trim, trimprefix, trimspace, trimsuffix, try, upper, urlencode, uuidv4, uuidv5, values, yamldecode, yamlencode, zipmap.
2020-02-06 05:49:21 -05:00
|
|
|
}
|
2021-02-02 12:05:04 -05:00
|
|
|
|
2020-07-24 04:58:03 -04:00
|
|
|
return cfg, diags
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-27 05:03:36 -04:00
|
|
|
// sniffCoreVersionRequirements does minimal parsing of the given body for
|
|
|
|
|
// "packer" blocks with "required_version" attributes, returning the
|
|
|
|
|
// requirements found.
|
|
|
|
|
//
|
|
|
|
|
// This is intended to maximize the chance that we'll be able to read the
|
|
|
|
|
// requirements (syntax errors notwithstanding) even if the config file contains
|
|
|
|
|
// constructs that might've been added in future versions
|
|
|
|
|
//
|
|
|
|
|
// This is a "best effort" sort of method which will return constraints it is
|
|
|
|
|
// able to find, but may return no constraints at all if the given body is
|
|
|
|
|
// so invalid that it cannot be decoded at all.
|
|
|
|
|
func sniffCoreVersionRequirements(body hcl.Body) ([]VersionConstraint, hcl.Diagnostics) {
|
|
|
|
|
|
|
|
|
|
var sniffRootSchema = &hcl.BodySchema{
|
|
|
|
|
Blocks: []hcl.BlockHeaderSchema{
|
|
|
|
|
{
|
|
|
|
|
Type: packerLabel,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rootContent, _, diags := body.PartialContent(sniffRootSchema)
|
|
|
|
|
|
|
|
|
|
var constraints []VersionConstraint
|
|
|
|
|
|
|
|
|
|
for _, block := range rootContent.Blocks {
|
|
|
|
|
content, blockDiags := block.Body.Content(packerBlockSchema)
|
|
|
|
|
diags = append(diags, blockDiags...)
|
|
|
|
|
|
|
|
|
|
attr, exists := content.Attributes["required_version"]
|
|
|
|
|
if !exists {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constraint, constraintDiags := decodeVersionConstraint(attr)
|
|
|
|
|
diags = append(diags, constraintDiags...)
|
|
|
|
|
if !constraintDiags.HasErrors() {
|
|
|
|
|
constraints = append(constraints, constraint)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return constraints, diags
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-26 04:21:44 -05:00
|
|
|
func filterVarsFromLogs(inputOrLocal Variables) {
|
|
|
|
|
for _, variable := range inputOrLocal {
|
2020-10-02 04:17:07 -04:00
|
|
|
if !variable.Sensitive {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2021-03-30 14:58:26 -04:00
|
|
|
value := variable.Value()
|
2020-10-02 04:53:17 -04:00
|
|
|
_ = cty.Walk(value, func(_ cty.Path, nested cty.Value) (bool, error) {
|
2020-10-02 04:17:07 -04:00
|
|
|
if nested.IsWhollyKnown() && !nested.IsNull() && nested.Type().Equals(cty.String) {
|
2020-11-19 17:03:11 -05:00
|
|
|
packersdk.LogSecretFilter.Set(nested.AsString())
|
2020-10-02 04:17:07 -04:00
|
|
|
}
|
|
|
|
|
return true, nil
|
|
|
|
|
})
|
|
|
|
|
}
|
2021-01-26 04:21:44 -05:00
|
|
|
}
|
|
|
|
|
|
2024-08-30 14:10:27 -04:00
|
|
|
func (cfg *PackerConfig) detectBuildPrereqDependencies() hcl.Diagnostics {
|
|
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
|
|
|
|
|
for _, ds := range cfg.Datasources {
|
|
|
|
|
dependencies := GetVarsByType(ds.block, "data")
|
|
|
|
|
dependencies = append(dependencies, GetVarsByType(ds.block, "local")...)
|
|
|
|
|
|
|
|
|
|
for _, dep := range dependencies {
|
|
|
|
|
// If something is locally aliased as `local` or `data`, we'll falsely
|
|
|
|
|
// report it as a local variable, which is not necessarily what we
|
|
|
|
|
// want to process here, so we continue.
|
|
|
|
|
//
|
|
|
|
|
// Note: this is kinda brittle, we should understand scopes to accurately
|
|
|
|
|
// mark something from an expression as a reference to a local variable.
|
|
|
|
|
// No real good solution for this now, besides maybe forbidding something
|
|
|
|
|
// to be locally aliased as `local`.
|
|
|
|
|
if len(dep) < 2 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
rs, err := NewRefStringFromDep(dep)
|
|
|
|
|
if err != nil {
|
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "failed to process datasource dependency",
|
|
|
|
|
Detail: fmt.Sprintf("An error occurred while processing a dependency for data source %s: %s",
|
|
|
|
|
ds.Name(), err),
|
|
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = ds.RegisterDependency(rs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "failed to register datasource dependency",
|
|
|
|
|
Detail: fmt.Sprintf("An error occurred while registering %q as a dependency for data source %s: %s",
|
|
|
|
|
rs, ds.Name(), err),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-05 14:28:22 -04:00
|
|
|
cfg.Datasources[ds.Ref()] = ds
|
2024-08-30 14:10:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, loc := range cfg.LocalBlocks {
|
|
|
|
|
dependencies := FilterTraversalsByType(loc.Expr.Variables(), "data")
|
|
|
|
|
dependencies = append(dependencies, FilterTraversalsByType(loc.Expr.Variables(), "local")...)
|
|
|
|
|
|
|
|
|
|
for _, dep := range dependencies {
|
|
|
|
|
// If something is locally aliased as `local` or `data`, we'll falsely
|
|
|
|
|
// report it as a local variable, which is not necessarily what we
|
|
|
|
|
// want to process here, so we continue.
|
|
|
|
|
//
|
|
|
|
|
// Note: this is kinda brittle, we should understand scopes to accurately
|
|
|
|
|
// mark something from an expression as a reference to a local variable.
|
|
|
|
|
// No real good solution for this now, besides maybe forbidding something
|
|
|
|
|
// to be locally aliased as `local`.
|
|
|
|
|
if len(dep) < 2 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
rs, err := NewRefStringFromDep(dep)
|
|
|
|
|
if err != nil {
|
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "failed to process local dependency",
|
|
|
|
|
Detail: fmt.Sprintf("An error occurred while processing a dependency for local variable %s: %s",
|
2024-09-03 15:28:54 -04:00
|
|
|
loc.LocalName, err),
|
2024-08-30 14:10:27 -04:00
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = loc.RegisterDependency(rs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "failed to register local dependency",
|
|
|
|
|
Detail: fmt.Sprintf("An error occurred while registering %q as a dependency for local variable %s: %s",
|
2024-09-03 15:28:54 -04:00
|
|
|
rs, loc.LocalName, err),
|
2024-08-30 14:10:27 -04:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return diags
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (cfg *PackerConfig) buildPrereqsDAG() (*dag.AcyclicGraph, error) {
|
|
|
|
|
retGraph := dag.AcyclicGraph{}
|
|
|
|
|
|
|
|
|
|
verticesMap := map[string]dag.Vertex{}
|
|
|
|
|
|
2025-06-16 23:52:09 -04:00
|
|
|
var err error
|
|
|
|
|
|
2024-08-30 14:10:27 -04:00
|
|
|
// Do a first pass to create all the vertices
|
2024-09-05 14:28:22 -04:00
|
|
|
for ref := range cfg.Datasources {
|
|
|
|
|
// We keep a reference to the datasource separately from where it
|
|
|
|
|
// is used to avoid getting bit by the loop semantics.
|
|
|
|
|
//
|
|
|
|
|
// This `ds` local variable is the same object for every loop
|
|
|
|
|
// so if we directly use the address of this object, we'll end
|
|
|
|
|
// up referencing the last node of the loop for each vertex,
|
|
|
|
|
// leading to implicit cycles.
|
|
|
|
|
//
|
|
|
|
|
// However by capturing it locally in this loop, we have a
|
|
|
|
|
// reference to the actual datasource block, so it ends-up being
|
|
|
|
|
// the right instance for each vertex.
|
|
|
|
|
ds := cfg.Datasources[ref]
|
2024-08-30 14:10:27 -04:00
|
|
|
v := retGraph.Add(&ds)
|
|
|
|
|
verticesMap[fmt.Sprintf("data.%s", ds.Name())] = v
|
|
|
|
|
}
|
2024-09-05 14:28:22 -04:00
|
|
|
// Note: locals being references to the objects already, we can safely
|
|
|
|
|
// use the reference returned by the local loop.
|
2024-08-30 14:10:27 -04:00
|
|
|
for _, local := range cfg.LocalBlocks {
|
|
|
|
|
v := retGraph.Add(local)
|
2024-09-03 15:28:54 -04:00
|
|
|
verticesMap[fmt.Sprintf("local.%s", local.LocalName)] = v
|
2024-08-30 14:10:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Connect the vertices together
|
|
|
|
|
//
|
|
|
|
|
// Vertices that don't have dependencies will be connected to the
|
|
|
|
|
// root vertex of the graph
|
|
|
|
|
for _, ds := range cfg.Datasources {
|
|
|
|
|
dsName := fmt.Sprintf("data.%s", ds.Name())
|
|
|
|
|
|
2025-06-16 23:52:09 -04:00
|
|
|
source := verticesMap[dsName]
|
|
|
|
|
if source == nil {
|
|
|
|
|
err = multierror.Append(err, fmt.Errorf("unable to find source vertex %q for dependency analysis, this is likely a Packer bug", dsName))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-30 14:10:27 -04:00
|
|
|
for _, dep := range ds.Dependencies {
|
2025-06-16 23:52:09 -04:00
|
|
|
target := verticesMap[dep.String()]
|
|
|
|
|
if target == nil {
|
|
|
|
|
err = multierror.Append(err, fmt.Errorf("could not get dependency %q for %q, %q missing in template", dep.String(), dsName, dep.String()))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retGraph.Connect(dag.BasicEdge(source, target))
|
2024-08-30 14:10:27 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, loc := range cfg.LocalBlocks {
|
2024-09-03 15:28:54 -04:00
|
|
|
locName := fmt.Sprintf("local.%s", loc.LocalName)
|
2024-08-30 14:10:27 -04:00
|
|
|
|
2025-06-16 23:52:09 -04:00
|
|
|
source := verticesMap[locName]
|
|
|
|
|
if source == nil {
|
|
|
|
|
err = multierror.Append(err, fmt.Errorf("unable to find source vertex %q for dependency analysis, this is likely a Packer bug", locName))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-30 14:10:27 -04:00
|
|
|
for _, dep := range loc.dependencies {
|
2025-06-16 23:52:09 -04:00
|
|
|
target := verticesMap[dep.String()]
|
|
|
|
|
|
|
|
|
|
if target == nil {
|
|
|
|
|
err = multierror.Append(err, fmt.Errorf("could not get dependency %q for %q, %q missing in template", dep.String(), locName, dep.String()))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retGraph.Connect(dag.BasicEdge(source, target))
|
2024-08-30 14:10:27 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 23:52:09 -04:00
|
|
|
if validateErr := retGraph.Validate(); validateErr != nil {
|
|
|
|
|
err = multierror.Append(err, validateErr)
|
2024-09-03 15:10:17 -04:00
|
|
|
}
|
|
|
|
|
|
2025-06-16 23:52:09 -04:00
|
|
|
return &retGraph, err
|
2024-08-30 14:10:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (cfg *PackerConfig) evaluateBuildPrereqs(skipDatasources bool) hcl.Diagnostics {
|
|
|
|
|
diags := cfg.detectBuildPrereqDependencies()
|
|
|
|
|
if diags.HasErrors() {
|
|
|
|
|
return diags
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
graph, err := cfg.buildPrereqsDAG()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return diags.Append(&hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "failed to prepare execution graph",
|
|
|
|
|
Detail: fmt.Sprintf("An error occurred while building the graph for datasources/locals: %s", err),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
walkFunc := func(v dag.Vertex) hcl.Diagnostics {
|
|
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
|
|
|
|
|
switch bl := v.(type) {
|
|
|
|
|
case *DatasourceBlock:
|
|
|
|
|
diags = cfg.evaluateDatasource(*bl, skipDatasources)
|
|
|
|
|
case *LocalBlock:
|
2024-09-05 14:31:42 -04:00
|
|
|
var val *Variable
|
|
|
|
|
if cfg.LocalVariables == nil {
|
|
|
|
|
cfg.LocalVariables = make(Variables)
|
|
|
|
|
}
|
|
|
|
|
val, diags = cfg.evaluateLocalVariable(bl)
|
|
|
|
|
// Note: clumsy a bit, but we won't add the variable as `nil` here
|
|
|
|
|
// unless no errors have been reported during evaluation.
|
|
|
|
|
//
|
|
|
|
|
// This prevents Packer from panicking down the line, as initialisation
|
|
|
|
|
// doesn't stop if there are diags, so if `val` is nil, it crashes.
|
|
|
|
|
if !diags.HasErrors() {
|
|
|
|
|
cfg.LocalVariables[bl.LocalName] = val
|
|
|
|
|
}
|
2024-08-30 14:10:27 -04:00
|
|
|
default:
|
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "unsupported DAG node type",
|
|
|
|
|
Detail: fmt.Sprintf("A node of type %q was added to the DAG, but cannot be "+
|
|
|
|
|
"evaluated as it is unsupported. "+
|
|
|
|
|
"This is a Packer bug, please report it so we can investigate.",
|
|
|
|
|
reflect.TypeOf(v).String()),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-05 14:31:42 -04:00
|
|
|
if diags.HasErrors() {
|
|
|
|
|
return diags
|
|
|
|
|
}
|
2024-08-30 14:10:27 -04:00
|
|
|
|
2024-09-05 14:31:42 -04:00
|
|
|
return nil
|
2024-08-30 14:10:27 -04:00
|
|
|
}
|
|
|
|
|
|
2024-09-03 15:31:28 -04:00
|
|
|
for _, vtx := range graph.ReverseTopologicalOrder() {
|
|
|
|
|
vtxDiags := walkFunc(vtx)
|
|
|
|
|
if vtxDiags.HasErrors() {
|
|
|
|
|
diags = diags.Extend(vtxDiags)
|
|
|
|
|
return diags
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
2024-08-30 14:10:27 -04:00
|
|
|
}
|
|
|
|
|
|
2021-01-26 04:21:44 -05:00
|
|
|
func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnostics {
|
2023-07-11 15:41:37 -04:00
|
|
|
diags := cfg.InputVariables.ValidateValues()
|
2024-08-30 14:13:51 -04:00
|
|
|
|
|
|
|
|
if opts.UseSequential {
|
|
|
|
|
diags = diags.Extend(cfg.evaluateDatasources(opts.SkipDatasourcesExecution))
|
|
|
|
|
diags = diags.Extend(cfg.evaluateLocalVariables(cfg.LocalBlocks))
|
|
|
|
|
} else {
|
|
|
|
|
diags = diags.Extend(cfg.evaluateBuildPrereqs(opts.SkipDatasourcesExecution))
|
|
|
|
|
}
|
2021-01-26 04:21:44 -05:00
|
|
|
|
|
|
|
|
filterVarsFromLogs(cfg.InputVariables)
|
|
|
|
|
filterVarsFromLogs(cfg.LocalVariables)
|
2020-10-02 04:17:07 -04:00
|
|
|
|
2021-03-30 09:53:04 -04:00
|
|
|
// parse the actual content // rest
|
|
|
|
|
for _, file := range cfg.files {
|
|
|
|
|
diags = append(diags, cfg.parser.parseConfig(file, cfg)...)
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
diags = append(diags, cfg.initializeBlocks()...)
|
2019-10-14 11:02:53 -04:00
|
|
|
|
2020-07-24 04:58:03 -04:00
|
|
|
return diags
|
2019-10-14 11:02:53 -04:00
|
|
|
}
|
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
// parseConfig looks in the found blocks for everything that is not a variable
|
|
|
|
|
// block.
|
|
|
|
|
func (p *Parser) parseConfig(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
2019-10-14 11:02:53 -04:00
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
body := f.Body
|
2021-03-30 09:53:04 -04:00
|
|
|
body = dynblock.Expand(body, cfg.EvalContext(DatasourceContext, nil))
|
2020-02-20 04:51:34 -05:00
|
|
|
content, moreDiags := body.Content(configSchema)
|
2019-10-14 11:02:53 -04:00
|
|
|
diags = append(diags, moreDiags...)
|
|
|
|
|
|
|
|
|
|
for _, block := range content.Blocks {
|
|
|
|
|
switch block.Type {
|
2025-02-11 12:20:00 -05:00
|
|
|
case buildHCPPackerRegistryLabel:
|
|
|
|
|
if cfg.HCPPackerRegistry != nil {
|
|
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
2025-03-03 15:22:53 -05:00
|
|
|
Summary: "Only one " + buildHCPPackerRegistryLabel + " is allowed",
|
2025-02-11 12:20:00 -05:00
|
|
|
Subject: block.DefRange.Ptr(),
|
|
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
hcpPackerRegistry, moreDiags := p.decodeHCPRegistry(block, cfg)
|
|
|
|
|
diags = append(diags, moreDiags...)
|
|
|
|
|
if moreDiags.HasErrors() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
cfg.HCPPackerRegistry = hcpPackerRegistry
|
2025-03-03 15:22:53 -05:00
|
|
|
|
2019-10-14 11:02:53 -04:00
|
|
|
case sourceLabel:
|
2019-12-17 05:25:56 -05:00
|
|
|
source, moreDiags := p.decodeSource(block)
|
2019-10-14 11:02:53 -04:00
|
|
|
diags = append(diags, moreDiags...)
|
2019-12-17 05:25:56 -05:00
|
|
|
if moreDiags.HasErrors() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2019-10-14 11:02:53 -04:00
|
|
|
|
|
|
|
|
ref := source.Ref()
|
2020-05-25 11:09:37 -04:00
|
|
|
if existing, found := cfg.Sources[ref]; found {
|
2019-10-14 11:02:53 -04:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "Duplicate " + sourceLabel + " block",
|
|
|
|
|
Detail: fmt.Sprintf("This "+sourceLabel+" block has the "+
|
|
|
|
|
"same builder type and name as a previous block declared "+
|
|
|
|
|
"at %s. Each "+sourceLabel+" must have a unique name per builder type.",
|
2019-12-17 05:25:56 -05:00
|
|
|
existing.block.DefRange.Ptr()),
|
|
|
|
|
Subject: source.block.DefRange.Ptr(),
|
2019-10-14 11:02:53 -04:00
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
2019-12-17 05:25:56 -05:00
|
|
|
|
|
|
|
|
if cfg.Sources == nil {
|
2020-05-25 11:09:37 -04:00
|
|
|
cfg.Sources = map[SourceRef]SourceBlock{}
|
2019-12-17 05:25:56 -05:00
|
|
|
}
|
2019-10-14 11:02:53 -04:00
|
|
|
cfg.Sources[ref] = source
|
|
|
|
|
|
|
|
|
|
case buildLabel:
|
2020-08-05 11:41:20 -04:00
|
|
|
build, moreDiags := p.decodeBuildConfig(block, cfg)
|
2019-10-14 11:02:53 -04:00
|
|
|
diags = append(diags, moreDiags...)
|
2019-12-17 05:25:56 -05:00
|
|
|
if moreDiags.HasErrors() {
|
2019-10-14 11:02:53 -04:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-05 09:25:19 -04:00
|
|
|
cfg.Builds = append(cfg.Builds, build)
|
2019-10-14 11:02:53 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return diags
|
|
|
|
|
}
|
2021-01-20 04:37:16 -05:00
|
|
|
|
|
|
|
|
func (p *Parser) decodeDatasources(file *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
|
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
|
2021-03-30 09:53:04 -04:00
|
|
|
body := file.Body
|
2024-12-20 18:54:36 -05:00
|
|
|
content, _ := body.Content(configSchema)
|
2021-01-20 04:37:16 -05:00
|
|
|
|
|
|
|
|
for _, block := range content.Blocks {
|
|
|
|
|
switch block.Type {
|
|
|
|
|
case dataSourceLabel:
|
|
|
|
|
datasource, moreDiags := p.decodeDataBlock(block)
|
|
|
|
|
diags = append(diags, moreDiags...)
|
|
|
|
|
if moreDiags.HasErrors() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
ref := datasource.Ref()
|
|
|
|
|
if existing, found := cfg.Datasources[ref]; found {
|
|
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
|
Summary: "Duplicate " + dataSourceLabel + " block",
|
|
|
|
|
Detail: fmt.Sprintf("This "+dataSourceLabel+" block has the "+
|
|
|
|
|
"same data type and name as a previous block declared "+
|
|
|
|
|
"at %s. Each "+dataSourceLabel+" must have a unique name per builder type.",
|
|
|
|
|
existing.block.DefRange.Ptr()),
|
|
|
|
|
Subject: datasource.block.DefRange.Ptr(),
|
|
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if cfg.Datasources == nil {
|
|
|
|
|
cfg.Datasources = Datasources{}
|
|
|
|
|
}
|
|
|
|
|
cfg.Datasources[ref] = *datasource
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return diags
|
|
|
|
|
}
|