2019-10-14 11:02:53 -04:00
|
|
|
package hcl2template
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-05-14 19:22:51 -04:00
|
|
|
"strconv"
|
2019-10-14 11:02:53 -04:00
|
|
|
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
2020-07-02 05:07:59 -04:00
|
|
|
"github.com/hashicorp/hcl/v2/gohcl"
|
2020-12-17 16:29:25 -05:00
|
|
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
2021-01-20 04:37:16 -05:00
|
|
|
hcl2shim "github.com/hashicorp/packer/hcl2template/shim"
|
2020-07-06 10:07:29 -04:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
2019-10-14 11:02:53 -04:00
|
|
|
)
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
// SourceBlock references an HCL 'source' block to be used in a build for
|
|
|
|
// example.
|
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 SourceBlock struct {
|
2019-10-14 11:02:53 -04:00
|
|
|
// Type of source; ex: virtualbox-iso
|
|
|
|
Type string
|
|
|
|
// Given name; if any
|
|
|
|
Name string
|
|
|
|
|
2019-12-17 05:25:56 -05:00
|
|
|
block *hcl.Block
|
2020-05-25 11:09:37 -04:00
|
|
|
|
2020-07-02 05:07:59 -04:00
|
|
|
// LocalName can be set in a singular source block from a build block, it
|
|
|
|
// allows to give a special name to a build in the logs.
|
|
|
|
LocalName string
|
|
|
|
}
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
// SourceUseBlock is a SourceBlock 'usage' from a config stand point.
|
|
|
|
// For example when one uses `build.sources = ["..."]` or
|
|
|
|
// `build.source "..." {...}`.
|
|
|
|
type SourceUseBlock struct {
|
|
|
|
// reference to an actual source block definition, or SourceBlock.
|
|
|
|
SourceRef
|
|
|
|
|
|
|
|
// LocalName can be set in a singular source block from a build block, it
|
|
|
|
// allows to give a special name to a build in the logs.
|
|
|
|
LocalName string
|
|
|
|
|
|
|
|
// Rest of the body, in case the build.source block has more specific
|
|
|
|
// content
|
|
|
|
// Body can be expanded by a dynamic tag.
|
|
|
|
Body hcl.Body
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *SourceUseBlock) name() string {
|
2020-07-02 05:07:59 -04:00
|
|
|
if b.LocalName != "" {
|
2020-07-06 10:07:29 -04:00
|
|
|
return b.LocalName
|
|
|
|
}
|
|
|
|
return b.Name
|
|
|
|
}
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
func (b *SourceUseBlock) String() string {
|
2020-07-06 10:07:29 -04:00
|
|
|
return fmt.Sprintf("%s.%s", b.Type, b.name())
|
|
|
|
}
|
|
|
|
|
|
|
|
// EvalContext adds the values of the source to the passed eval context.
|
2021-02-02 12:05:04 -05:00
|
|
|
func (b *SourceUseBlock) ctyValues() map[string]cty.Value {
|
2020-07-06 10:07:29 -04:00
|
|
|
return map[string]cty.Value{
|
|
|
|
"type": cty.StringVal(b.Type),
|
|
|
|
"name": cty.StringVal(b.name()),
|
2020-07-02 05:07:59 -04:00
|
|
|
}
|
2020-05-25 11:09:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// decodeBuildSource reads a used source block from a build:
|
|
|
|
// build {
|
2020-07-02 05:07:59 -04:00
|
|
|
// source "type.example" {
|
|
|
|
// name = "local_name"
|
|
|
|
// }
|
2020-05-25 11:09:37 -04:00
|
|
|
// }
|
2021-02-02 12:05:04 -05:00
|
|
|
func (p *Parser) decodeBuildSource(block *hcl.Block) (SourceUseBlock, hcl.Diagnostics) {
|
2020-05-25 11:09:37 -04:00
|
|
|
ref := sourceRefFromString(block.Labels[0])
|
2021-02-02 12:05:04 -05:00
|
|
|
out := SourceUseBlock{SourceRef: ref}
|
2020-07-02 05:07:59 -04:00
|
|
|
var b struct {
|
|
|
|
Name string `hcl:"name,optional"`
|
|
|
|
Rest hcl.Body `hcl:",remain"`
|
|
|
|
}
|
|
|
|
diags := gohcl.DecodeBody(block.Body, nil, &b)
|
|
|
|
if diags.HasErrors() {
|
2021-02-02 12:05:04 -05:00
|
|
|
return out, diags
|
2020-07-02 05:07:59 -04:00
|
|
|
}
|
2021-02-02 12:05:04 -05:00
|
|
|
out.LocalName = b.Name
|
|
|
|
out.Body = b.Rest
|
|
|
|
return out, nil
|
2019-10-14 11:02:53 -04:00
|
|
|
}
|
|
|
|
|
2020-05-25 11:09:37 -04:00
|
|
|
func (p *Parser) decodeSource(block *hcl.Block) (SourceBlock, hcl.Diagnostics) {
|
|
|
|
source := SourceBlock{
|
2019-12-17 05:25:56 -05:00
|
|
|
Type: block.Labels[0],
|
|
|
|
Name: block.Labels[1],
|
|
|
|
block: block,
|
|
|
|
}
|
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
|
|
|
return source, diags
|
|
|
|
}
|
|
|
|
|
2021-02-05 04:57:14 -05:00
|
|
|
func (cfg *PackerConfig) startBuilder(source SourceUseBlock, ectx *hcl.EvalContext) (packersdk.Builder, hcl.Diagnostics, []string) {
|
2019-10-14 11:02:53 -04:00
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
builder, err := cfg.parser.PluginConfig.Builders.Start(source.Type)
|
2019-12-17 05:25:56 -05:00
|
|
|
if err != nil {
|
2019-10-14 11:02:53 -04:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
2021-02-05 14:06:52 -05:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Failed to load " + sourceLabel + " type",
|
|
|
|
Detail: err.Error(),
|
2019-10-14 11:02:53 -04:00
|
|
|
})
|
2019-12-17 12:42:15 -05:00
|
|
|
return builder, diags, nil
|
2019-10-14 11:02:53 -04:00
|
|
|
}
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
body := source.Body
|
2020-05-25 11:09:37 -04:00
|
|
|
decoded, moreDiags := decodeHCL2Spec(body, ectx, builder)
|
2019-10-14 11:02:53 -04:00
|
|
|
diags = append(diags, moreDiags...)
|
2019-12-17 05:25:56 -05:00
|
|
|
if moreDiags.HasErrors() {
|
2021-02-02 12:05:04 -05:00
|
|
|
return builder, diags, nil
|
2019-12-17 05:25:56 -05:00
|
|
|
}
|
2019-12-17 10:31:22 -05:00
|
|
|
|
2021-01-20 04:37:16 -05:00
|
|
|
// In case of cty.Unknown values, this will write a equivalent placeholder of the same type
|
|
|
|
// Unknown types are not recognized by the json marshal during the RPC call and we have to do this here
|
|
|
|
// to avoid json parsing failures when running the validate command.
|
|
|
|
// We don't do this before so we can validate if variable types matches correctly on decodeHCL2Spec.
|
|
|
|
decoded = hcl2shim.WriteUnknownPlaceholderValues(decoded)
|
|
|
|
|
2020-05-14 19:22:51 -04:00
|
|
|
// Note: HCL prepares inside of the Start func, but Json does not. Json
|
|
|
|
// builds are instead prepared only in command/build.go
|
|
|
|
// TODO: either make json prepare when plugins are loaded, or make HCL
|
|
|
|
// prepare at a later step, to make builds from different template types
|
|
|
|
// easier to reason about.
|
|
|
|
builderVars := source.builderVariables()
|
2021-02-05 04:57:14 -05:00
|
|
|
builderVars["packer_debug"] = strconv.FormatBool(cfg.debug)
|
|
|
|
builderVars["packer_force"] = strconv.FormatBool(cfg.force)
|
|
|
|
builderVars["packer_on_error"] = cfg.onError
|
2020-05-14 19:22:51 -04:00
|
|
|
|
|
|
|
generatedVars, warning, err := builder.Prepare(builderVars, decoded)
|
2021-02-02 12:05:04 -05:00
|
|
|
moreDiags = warningErrorsToDiags(cfg.Sources[source.SourceRef].block, warning, err)
|
2019-12-17 05:25:56 -05:00
|
|
|
diags = append(diags, moreDiags...)
|
2019-12-17 12:42:15 -05:00
|
|
|
return builder, diags, generatedVars
|
2019-10-14 11:02:53 -04:00
|
|
|
}
|
|
|
|
|
2020-05-14 19:22:51 -04:00
|
|
|
// These variables will populate the PackerConfig inside of the builders.
|
2021-02-02 12:05:04 -05:00
|
|
|
func (source *SourceUseBlock) builderVariables() map[string]string {
|
2020-04-09 05:14:37 -04:00
|
|
|
return map[string]string{
|
|
|
|
"packer_build_name": source.Name,
|
|
|
|
"packer_builder_type": source.Type,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
func (source *SourceBlock) Ref() SourceRef {
|
2019-10-14 11:02:53 -04:00
|
|
|
return SourceRef{
|
|
|
|
Type: source.Type,
|
|
|
|
Name: source.Name,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
// SourceRef is a nice way to put `virtualbox-iso.source_name`
|
2019-10-14 11:02:53 -04:00
|
|
|
type SourceRef struct {
|
2021-02-02 12:05:04 -05:00
|
|
|
// Type of the source, for example `virtualbox-iso`
|
2019-10-14 11:02:53 -04:00
|
|
|
Type string
|
2021-02-02 12:05:04 -05:00
|
|
|
// Name of the source, for example `source_name`
|
2019-10-14 11:02:53 -04:00
|
|
|
Name string
|
2020-05-25 11:09:37 -04:00
|
|
|
|
2021-02-02 12:05:04 -05:00
|
|
|
// No other field should be added to the SourceRef because we used that
|
|
|
|
// struct as a map accessor in many places.
|
2019-10-14 11:02:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// NoSource is the zero value of sourceRef, representing the absense of an
|
|
|
|
// source.
|
|
|
|
var NoSource SourceRef
|
|
|
|
|
|
|
|
func (r SourceRef) String() string {
|
|
|
|
return fmt.Sprintf("%s.%s", r.Type, r.Name)
|
|
|
|
}
|