core: Decode: when in HCL2 decoding mode; reset the whole struct before preparing it. (#9622)
* core: Decode when in HCL2 decoding mode; reset the whole struct before preparing it. * HCL2: add path variables + docs & tests Co-authored-by: Megan Marsh <megan@hashicorp.com>
This commit is contained in:
parent
1f6473b4c1
commit
125178d943
|
@ -7,6 +7,8 @@ import (
|
|||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
@ -323,6 +325,44 @@ func TestBuild(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_build_output(t *testing.T) {
|
||||
|
||||
tc := []struct {
|
||||
command []string
|
||||
env []string
|
||||
expected string
|
||||
runtime string
|
||||
}{
|
||||
{[]string{"build", "--color=false", testFixture("hcl", "reprepare", "shell-local.pkr.hcl")}, nil,
|
||||
`
|
||||
null.example: hello from the NULL builder packeruser
|
||||
Build 'null.example' finished.
|
||||
`, "posix"},
|
||||
{[]string{"build", "--color=false", testFixture("hcl", "reprepare", "shell-local-windows.pkr.hcl")}, nil,
|
||||
`
|
||||
null.example: hello from the NULL builder packeruser
|
||||
Build 'null.example' finished.
|
||||
`, "windows"},
|
||||
}
|
||||
|
||||
for _, tc := range tc {
|
||||
if (runtime.GOOS == "windows") != (tc.runtime == "windows") {
|
||||
continue
|
||||
}
|
||||
t.Run(fmt.Sprintf("packer %s", tc.command), func(t *testing.T) {
|
||||
p := helperCommand(t, tc.command...)
|
||||
p.Env = append(p.Env, tc.env...)
|
||||
bs, err := p.Output()
|
||||
if err != nil {
|
||||
t.Fatalf("%v: %s", err, bs)
|
||||
}
|
||||
if !strings.Contains(string(bs), tc.expected) {
|
||||
t.Fatalf("Should have given output %s.\nReceived: %s", tc.expected, string(bs))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildOnlyFileCommaFlags(t *testing.T) {
|
||||
c := &BuildCommand{
|
||||
Meta: testMetaFile(t),
|
||||
|
|
|
@ -2,6 +2,7 @@ package command
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -12,6 +13,10 @@ import (
|
|||
)
|
||||
|
||||
func Test_console(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("Getwd: %v", err)
|
||||
}
|
||||
|
||||
tc := []struct {
|
||||
piped string
|
||||
|
@ -25,6 +30,8 @@ func Test_console(t *testing.T) {
|
|||
{"upper(var.fruit)", []string{"console", filepath.Join(testFixture("var-arg"), "fruit_builder.pkr.hcl")}, []string{"PKR_VAR_fruit=potato"}, "POTATO\n"},
|
||||
{"1 + 5", []string{"console", "--config-type=hcl2"}, nil, "6\n"},
|
||||
{"var.images", []string{"console", filepath.Join(testFixture("var-arg"), "map.pkr.hcl")}, nil, "{\n" + ` "key" = "value"` + "\n}\n"},
|
||||
{"path.cwd", []string{"console", filepath.Join(testFixture("var-arg"), "map.pkr.hcl")}, nil, strings.ReplaceAll(cwd, `\`, `/`) + "\n"},
|
||||
{"path.root", []string{"console", filepath.Join(testFixture("var-arg"), "map.pkr.hcl")}, nil, strings.ReplaceAll(testFixture("var-arg"), `\`, `/`) + "\n"},
|
||||
}
|
||||
|
||||
for _, tc := range tc {
|
||||
|
|
|
@ -87,6 +87,8 @@ func TestHelperProcess(*testing.T) {
|
|||
os.Exit((&ConsoleCommand{Meta: commandMeta()}).Run(args))
|
||||
case "inspect":
|
||||
os.Exit((&InspectCommand{Meta: commandMeta()}).Run(args))
|
||||
case "build":
|
||||
os.Exit((&BuildCommand{Meta: commandMeta()}).Run(args))
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
|
||||
os.Exit(2)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
echo hello from the ${BUILDER} builder ${USER}
|
|
@ -0,0 +1,13 @@
|
|||
source "null" "example" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = [
|
||||
"source.null.example"
|
||||
]
|
||||
provisioner "shell-local" {
|
||||
script = "./${path.root}/test_cmd.cmd"
|
||||
environment_vars = ["USER=packeruser", "BUILDER=${upper(build.ID)}"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
source "null" "example" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = [
|
||||
"source.null.example"
|
||||
]
|
||||
provisioner "shell-local" {
|
||||
script = "./${path.root}/hello.sh"
|
||||
environment_vars = ["USER=packeruser", "BUILDER=${upper(build.ID)}"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
echo hello from the %BUILDER% builder %USER%
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -70,7 +71,17 @@ func Run(ctx context.Context, ui packer.Ui, config *Config, generatedData map[st
|
|||
}
|
||||
|
||||
for _, script := range scripts {
|
||||
interpolatedCmds, err := createInterpolatedCommands(config, script, flattenedEnvVars)
|
||||
// use absolute path in case the script is linked with forward slashes
|
||||
// on windows.
|
||||
absScript, err := filepath.Abs(script)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf(
|
||||
"Error executing script: %s\n%v\n",
|
||||
absScript,
|
||||
err,
|
||||
)
|
||||
}
|
||||
interpolatedCmds, err := createInterpolatedCommands(config, absScript, flattenedEnvVars)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -91,7 +102,7 @@ func Run(ctx context.Context, ui packer.Ui, config *Config, generatedData map[st
|
|||
return false, fmt.Errorf(
|
||||
"Error executing script: %s\n\n"+
|
||||
"Please see output above for more information.",
|
||||
script)
|
||||
absScript)
|
||||
}
|
||||
|
||||
if err := config.ValidExitCode(cmd.ExitStatus()); err != nil {
|
||||
|
|
|
@ -77,6 +77,9 @@ func testParse(t *testing.T, tests []parseTest) {
|
|||
ProvisionerBlock{},
|
||||
PostProcessorBlock{},
|
||||
),
|
||||
cmpopts.IgnoreFields(PackerConfig{},
|
||||
"Cwd", // Cwd will change for every computer
|
||||
),
|
||||
cmpopts.IgnoreTypes(HCL2Ref{}),
|
||||
cmpopts.IgnoreTypes([]hcl.Range{}),
|
||||
cmpopts.IgnoreTypes(hcl.Range{}),
|
||||
|
|
|
@ -70,7 +70,7 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
|
|||
hclFiles, jsonFiles, moreDiags := GetHCL2Files(filename, hcl2FileExt, hcl2JsonFileExt)
|
||||
diags = append(diags, moreDiags...)
|
||||
if len(hclFiles)+len(jsonFiles) == 0 {
|
||||
diags = append(moreDiags, &hcl.Diagnostic{
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Could not find any config file in " + filename,
|
||||
Detail: "A config file must be suffixed with `.pkr.hcl` or " +
|
||||
|
@ -96,8 +96,17 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
|
|||
if isDir, err := isDir(basedir); err == nil && !isDir {
|
||||
basedir = filepath.Dir(basedir)
|
||||
}
|
||||
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(),
|
||||
})
|
||||
}
|
||||
cfg := &PackerConfig{
|
||||
Basedir: basedir,
|
||||
Cwd: wd,
|
||||
builderSchemas: p.BuilderSchemas,
|
||||
provisionersSchemas: p.ProvisionersSchemas,
|
||||
postProcessorsSchemas: p.PostProcessorsSchemas,
|
||||
|
|
|
@ -16,6 +16,8 @@ import (
|
|||
type PackerConfig struct {
|
||||
// Directory where the config files are defined
|
||||
Basedir string
|
||||
// directory Packer was called from
|
||||
Cwd string
|
||||
|
||||
// Available Source blocks
|
||||
Sources map[SourceRef]SourceBlock
|
||||
|
@ -49,6 +51,7 @@ type ValidationOptions struct {
|
|||
const (
|
||||
inputVariablesAccessor = "var"
|
||||
localsAccessor = "local"
|
||||
pathVariablesAccessor = "path"
|
||||
sourcesAccessor = "source"
|
||||
buildAccessor = "build"
|
||||
)
|
||||
|
@ -69,6 +72,10 @@ func (cfg *PackerConfig) EvalContext(variables map[string]cty.Value) *hcl.EvalCo
|
|||
"name": cty.UnknownVal(cty.String),
|
||||
}),
|
||||
buildAccessor: cty.UnknownVal(cty.EmptyObject),
|
||||
pathVariablesAccessor: cty.ObjectVal(map[string]cty.Value{
|
||||
"cwd": cty.StringVal(strings.ReplaceAll(cfg.Cwd, `\`, `/`)),
|
||||
"root": cty.StringVal(strings.ReplaceAll(cfg.Basedir, `\`, `/`)),
|
||||
}),
|
||||
},
|
||||
}
|
||||
for k, v := range variables {
|
||||
|
|
|
@ -72,6 +72,18 @@ func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error {
|
|||
return err
|
||||
}
|
||||
raws[i] = raw
|
||||
{
|
||||
// reset target to zero.
|
||||
// In HCL2, we need to prepare provisioners/post-processors after a
|
||||
// builder has started in order to have build values correctly
|
||||
// extrapolated. Packer plugins have never been prepared twice in
|
||||
// the past and some of them set fields during their Validation
|
||||
// steps; which end up in an invalid provisioner/post-processor,
|
||||
// like in [GH-9596]. This ensures Packer plugin will be reset
|
||||
// right before we Prepare them.
|
||||
p := reflect.ValueOf(target).Elem()
|
||||
p.Set(reflect.Zero(p.Type()))
|
||||
}
|
||||
}
|
||||
if config == nil {
|
||||
config = &DecodeOpts{Interpolate: true}
|
||||
|
|
|
@ -144,6 +144,7 @@ export default [
|
|||
'variables',
|
||||
'locals',
|
||||
'contextual-variables',
|
||||
'path-variables',
|
||||
'syntax',
|
||||
'onlyexcept',
|
||||
'expressions',
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
layout: docs
|
||||
page_title: Path Variables - HCL Configuration Language
|
||||
sidebar_title: Path Variables
|
||||
description: |-
|
||||
Special variables provide directory information. This page covers all path
|
||||
variables.
|
||||
---
|
||||
|
||||
# Path variables
|
||||
|
||||
- `path.cwd`: the directory from where Packer was started.
|
||||
|
||||
- `path.root`: the directory of the input HCL file or the input folder.
|
Loading…
Reference in New Issue