Interpolate boot_command when defined by user variable (#8640)
This commit is contained in:
parent
63cba2dc00
commit
328eb8ee96
|
@ -397,12 +397,17 @@ func (c *Core) init() error {
|
||||||
allVariables[k] = v
|
allVariables[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regex to exclude any build function variable or template variable
|
||||||
|
// from interpolating earlier
|
||||||
|
// E.g.: {{ .HTTPIP }} won't interpolate now
|
||||||
|
renderFilter := "{{(\\s|)\\.(.*?)(\\s|)}}"
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
shouldRetry = false
|
shouldRetry = false
|
||||||
// First, loop over the variables in the template
|
// First, loop over the variables in the template
|
||||||
for k, v := range allVariables {
|
for k, v := range allVariables {
|
||||||
// Interpolate the default
|
// Interpolate the default
|
||||||
renderedV, err := interpolate.Render(v, ctx)
|
renderedV, err := interpolate.RenderRegex(v, ctx, renderFilter)
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
// We only get here if interpolation has succeeded, so something is
|
// We only get here if interpolation has succeeded, so something is
|
||||||
|
|
|
@ -143,6 +143,36 @@ func TestCoreBuild_env(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCoreBuild_IgnoreTemplateVariables(t *testing.T) {
|
||||||
|
os.Setenv("PACKER_TEST_ENV", "test")
|
||||||
|
defer os.Setenv("PACKER_TEST_ENV", "")
|
||||||
|
|
||||||
|
config := TestCoreConfig(t)
|
||||||
|
testCoreTemplate(t, config, fixtureDir("build-ignore-template-variable.json"))
|
||||||
|
core := TestCore(t, config)
|
||||||
|
|
||||||
|
if core.variables["http_ip"] != "{{ .HTTPIP }}" {
|
||||||
|
t.Fatalf("bad: User variable http_ip={{ .HTTPIP }} should not be interpolated")
|
||||||
|
}
|
||||||
|
|
||||||
|
if core.variables["var"] != "test_{{ .PACKER_TEST_TEMP }}" {
|
||||||
|
t.Fatalf("bad: User variable var should be half interpolated to var=test_{{ .PACKER_TEST_TEMP }} but was var=%s", core.variables["var"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if core.variables["array_var"] != "us-west-1,us-west-2" {
|
||||||
|
t.Fatalf("bad: User variable array_var should be \"us-west-1,us-west-2\" but was %s", core.variables["var"])
|
||||||
|
}
|
||||||
|
|
||||||
|
build, err := core.Build("test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := build.Prepare(); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCoreBuild_buildNameVar(t *testing.T) {
|
func TestCoreBuild_buildNameVar(t *testing.T) {
|
||||||
config := TestCoreConfig(t)
|
config := TestCoreConfig(t)
|
||||||
testCoreTemplate(t, config, fixtureDir("build-var-build-name.json"))
|
testCoreTemplate(t, config, fixtureDir("build-var-build-name.json"))
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"variables": {
|
||||||
|
"var": "{{env `PACKER_TEST_ENV`}}_{{ .PACKER_TEST_TEMP }}",
|
||||||
|
"http_ip": "{{ .HTTPIP }}",
|
||||||
|
"array_var": "us-west-1,us-west-2"
|
||||||
|
},
|
||||||
|
|
||||||
|
"builders": [{
|
||||||
|
"type": "test"
|
||||||
|
}]
|
||||||
|
}
|
|
@ -2,6 +2,9 @@ package interpolate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,8 +45,44 @@ func NewContext() *Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render is shorthand for constructing an I and calling Render.
|
// Render is shorthand for constructing an I and calling Render.
|
||||||
func Render(v string, ctx *Context) (string, error) {
|
func Render(v string, ctx *Context) (rendered string, err error) {
|
||||||
return (&I{Value: v}).Render(ctx)
|
// Keep interpolating until all variables are done
|
||||||
|
// Sometimes a variable can been inside another one
|
||||||
|
for {
|
||||||
|
rendered, err = (&I{Value: v}).Render(ctx)
|
||||||
|
if err != nil || rendered == v {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v = rendered
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render is shorthand for constructing an I and calling Render.
|
||||||
|
// Use regex to filter variables that are not supposed to be interpolated now
|
||||||
|
func RenderRegex(v string, ctx *Context, regex string) (string, error) {
|
||||||
|
re := regexp.MustCompile(regex)
|
||||||
|
matches := re.FindAllStringSubmatch(v, -1)
|
||||||
|
|
||||||
|
// Replace variables to be excluded with a unique UUID
|
||||||
|
excluded := make(map[string]string)
|
||||||
|
for _, value := range matches {
|
||||||
|
id := uuid.New().String()
|
||||||
|
excluded[id] = value[0]
|
||||||
|
v = strings.ReplaceAll(v, value[0], id)
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered, err := (&I{Value: v}).Render(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return rendered, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace back by the UUID the previously excluded values
|
||||||
|
for id, value := range excluded {
|
||||||
|
rendered = strings.ReplaceAll(rendered, id, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rendered, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate is shorthand for constructing an I and calling Validate.
|
// Validate is shorthand for constructing an I and calling Validate.
|
||||||
|
|
Loading…
Reference in New Issue