Commit 56ec6bf7 authored by Jason A. Beranek's avatar Jason A. Beranek

common/command/template,packer/template: fix build name ConfigTemplate processing [GH-858]

parent a5bc5bec
...@@ -66,6 +66,12 @@ func (f *BuildOptions) AllUserVars() (map[string]string, error) { ...@@ -66,6 +66,12 @@ func (f *BuildOptions) AllUserVars() (map[string]string, error) {
func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([]packer.Build, error) { func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([]packer.Build, error) {
buildNames := t.BuildNames() buildNames := t.BuildNames()
// Process the name
tpl, _, err := t.NewConfigTemplate()
if err != nil {
return nil, err
}
checks := make(map[string][]string) checks := make(map[string][]string)
checks["except"] = f.Except checks["except"] = f.Except
checks["only"] = f.Only checks["only"] = f.Only
...@@ -73,7 +79,12 @@ func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([ ...@@ -73,7 +79,12 @@ func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([
for _, n := range ns { for _, n := range ns {
found := false found := false
for _, actual := range buildNames { for _, actual := range buildNames {
if actual == n { var processed string
processed, err = tpl.Process(actual, nil)
if err != nil {
return nil, err
}
if actual == n || processed == n {
found = true found = true
break break
} }
...@@ -88,17 +99,22 @@ func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([ ...@@ -88,17 +99,22 @@ func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([
builds := make([]packer.Build, 0, len(buildNames)) builds := make([]packer.Build, 0, len(buildNames))
for _, buildName := range buildNames { for _, buildName := range buildNames {
var processedBuildName string
processedBuildName, err = tpl.Process(buildName, nil)
if err != nil {
return nil, err
}
if len(f.Except) > 0 { if len(f.Except) > 0 {
found := false found := false
for _, except := range f.Except { for _, except := range f.Except {
if buildName == except { if buildName == except || processedBuildName == except {
found = true found = true
break break
} }
} }
if found { if found {
log.Printf("Skipping build '%s' because specified by -except.", buildName) log.Printf("Skipping build '%s' because specified by -except.", processedBuildName)
continue continue
} }
} }
...@@ -106,19 +122,19 @@ func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([ ...@@ -106,19 +122,19 @@ func (f *BuildOptions) Builds(t *packer.Template, cf *packer.ComponentFinder) ([
if len(f.Only) > 0 { if len(f.Only) > 0 {
found := false found := false
for _, only := range f.Only { for _, only := range f.Only {
if buildName == only { if buildName == only || processedBuildName == only {
found = true found = true
break break
} }
} }
if !found { if !found {
log.Printf("Skipping build '%s' because not specified by -only.", buildName) log.Printf("Skipping build '%s' because not specified by -only.", processedBuildName)
continue continue
} }
} }
log.Printf("Creating build: %s", buildName) log.Printf("Creating build: %s", processedBuildName)
build, err := t.Build(buildName, cf) build, err := t.Build(buildName, cf)
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to create build '%s': \n\n%s", buildName, err) return nil, fmt.Errorf("Failed to create build '%s': \n\n%s", buildName, err)
......
...@@ -7,17 +7,23 @@ import ( ...@@ -7,17 +7,23 @@ import (
func testTemplate() (*packer.Template, *packer.ComponentFinder) { func testTemplate() (*packer.Template, *packer.ComponentFinder) {
tplData := `{ tplData := `{
"builders": [ "variables": {
{ "foo": null
"type": "foo" },
},
{ "builders": [
"type": "bar" {
} "type": "foo"
] },
}` {
"name": "{{user \"foo\"}}",
tpl, err := packer.ParseTemplate([]byte(tplData), nil) "type": "bar"
}
]
}
`
tpl, err := packer.ParseTemplate([]byte(tplData), map[string]string{"foo": "bar"})
if err != nil { if err != nil {
panic(err) panic(err)
} }
...@@ -59,6 +65,44 @@ func TestBuildOptionsBuilds_except(t *testing.T) { ...@@ -59,6 +65,44 @@ func TestBuildOptionsBuilds_except(t *testing.T) {
} }
} }
//Test to make sure the build name pattern matches
func TestBuildOptionsBuilds_exceptConfigTemplateRaw(t *testing.T) {
opts := new(BuildOptions)
opts.Except = []string{"{{user \"foo\"}}"}
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 1 {
t.Fatalf("bad: %d", len(bs))
}
if bs[0].Name() != "foo" {
t.Fatalf("bad: %s", bs[0].Name())
}
}
//Test to make sure the processed build name matches
func TestBuildOptionsBuilds_exceptConfigTemplateProcessed(t *testing.T) {
opts := new(BuildOptions)
opts.Except = []string{"bar"}
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 1 {
t.Fatalf("bad: %d", len(bs))
}
if bs[0].Name() != "foo" {
t.Fatalf("bad: %s", bs[0].Name())
}
}
func TestBuildOptionsBuilds_only(t *testing.T) { func TestBuildOptionsBuilds_only(t *testing.T) {
opts := new(BuildOptions) opts := new(BuildOptions)
opts.Only = []string{"foo"} opts.Only = []string{"foo"}
...@@ -77,6 +121,44 @@ func TestBuildOptionsBuilds_only(t *testing.T) { ...@@ -77,6 +121,44 @@ func TestBuildOptionsBuilds_only(t *testing.T) {
} }
} }
//Test to make sure the build name pattern matches
func TestBuildOptionsBuilds_onlyConfigTemplateRaw(t *testing.T) {
opts := new(BuildOptions)
opts.Only = []string{"{{user \"foo\"}}"}
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 1 {
t.Fatalf("bad: %d", len(bs))
}
if bs[0].Name() != "bar" {
t.Fatalf("bad: %s", bs[0].Name())
}
}
//Test to make sure the processed build name matches
func TestBuildOptionsBuilds_onlyConfigTemplateProcessed(t *testing.T) {
opts := new(BuildOptions)
opts.Only = []string{"bar"}
bs, err := opts.Builds(testTemplate())
if err != nil {
t.Fatalf("err: %s", err)
}
if len(bs) != 1 {
t.Fatalf("bad: %d", len(bs))
}
if bs[0].Name() != "bar" {
t.Fatalf("bad: %s", bs[0].Name())
}
}
func TestBuildOptionsBuilds_exceptNonExistent(t *testing.T) { func TestBuildOptionsBuilds_exceptNonExistent(t *testing.T) {
opts := new(BuildOptions) opts := new(BuildOptions)
opts.Except = []string{"i-dont-exist"} opts.Except = []string{"i-dont-exist"}
......
...@@ -473,52 +473,13 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err ...@@ -473,52 +473,13 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
return return
} }
// Prepare the variable template processor, which is a bit unique
// because we don't allow user variable usage and we add a function
// to read from the environment.
varTpl, err := NewConfigTemplate()
if err != nil {
return nil, err
}
varTpl.Funcs(template.FuncMap{
"env": templateEnv,
"user": templateDisableUser,
})
// Prepare the variables
var varErrors []error
variables := make(map[string]string)
for k, v := range t.Variables {
if v.Required && !v.HasValue {
varErrors = append(varErrors,
fmt.Errorf("Required user variable '%s' not set", k))
}
var val string
if v.HasValue {
val = v.Value
} else {
val, err = varTpl.Process(v.Default, nil)
if err != nil {
varErrors = append(varErrors,
fmt.Errorf("Error processing user variable '%s': %s'", k, err))
}
}
variables[k] = val
}
if len(varErrors) > 0 {
return nil, &MultiError{varErrors}
}
// Process the name // Process the name
tpl, err := NewConfigTemplate() tpl, variables, err := t.NewConfigTemplate()
if err != nil { if err != nil {
return nil, err return nil, err
} }
tpl.UserVars = variables
rawName := name
name, err = tpl.Process(name, nil) name, err = tpl.Process(name, nil)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -552,7 +513,7 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err ...@@ -552,7 +513,7 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
for _, rawPPs := range t.PostProcessors { for _, rawPPs := range t.PostProcessors {
current := make([]coreBuildPostProcessor, 0, len(rawPPs)) current := make([]coreBuildPostProcessor, 0, len(rawPPs))
for _, rawPP := range rawPPs { for _, rawPP := range rawPPs {
if rawPP.TemplateOnlyExcept.Skip(name) { if rawPP.TemplateOnlyExcept.Skip(rawName) {
continue continue
} }
...@@ -585,7 +546,7 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err ...@@ -585,7 +546,7 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
// Prepare the provisioners // Prepare the provisioners
provisioners := make([]coreBuildProvisioner, 0, len(t.Provisioners)) provisioners := make([]coreBuildProvisioner, 0, len(t.Provisioners))
for _, rawProvisioner := range t.Provisioners { for _, rawProvisioner := range t.Provisioners {
if rawProvisioner.TemplateOnlyExcept.Skip(name) { if rawProvisioner.TemplateOnlyExcept.Skip(rawName) {
continue continue
} }
...@@ -634,6 +595,59 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err ...@@ -634,6 +595,59 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
return return
} }
//Build a ConfigTemplate object populated by the values within a
//parsed template
func (t *Template) NewConfigTemplate() (c *ConfigTemplate, variables map[string]string, err error) {
// Prepare the variable template processor, which is a bit unique
// because we don't allow user variable usage and we add a function
// to read from the environment.
varTpl, err := NewConfigTemplate()
if err != nil {
return nil, nil, err
}
varTpl.Funcs(template.FuncMap{
"env": templateEnv,
"user": templateDisableUser,
})
// Prepare the variables
var varErrors []error
variables = make(map[string]string)
for k, v := range t.Variables {
if v.Required && !v.HasValue {
varErrors = append(varErrors,
fmt.Errorf("Required user variable '%s' not set", k))
}
var val string
if v.HasValue {
val = v.Value
} else {
val, err = varTpl.Process(v.Default, nil)
if err != nil {
varErrors = append(varErrors,
fmt.Errorf("Error processing user variable '%s': %s'", k, err))
}
}
variables[k] = val
}
if len(varErrors) > 0 {
return nil, variables, &MultiError{varErrors}
}
// Process the name
tpl, err := NewConfigTemplate()
if err != nil {
return nil, variables, err
}
tpl.UserVars = variables
return tpl, variables, nil
}
// TemplateOnlyExcept contains the logic required for "only" and "except" // TemplateOnlyExcept contains the logic required for "only" and "except"
// meta-parameters. // meta-parameters.
type TemplateOnlyExcept struct { type TemplateOnlyExcept struct {
......
...@@ -1165,7 +1165,62 @@ func TestTemplateBuild_exceptPP(t *testing.T) { ...@@ -1165,7 +1165,62 @@ func TestTemplateBuild_exceptPP(t *testing.T) {
t.Fatal("should have no postProcessors") t.Fatal("should have no postProcessors")
} }
// Verify test2 has no post-processors // Verify test2 has one post-processors
build, err = template.Build("test2", testTemplateComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
cbuild = build.(*coreBuild)
if len(cbuild.postProcessors) != 1 {
t.Fatalf("invalid: %d", len(cbuild.postProcessors))
}
}
func TestTemplateBuild_exceptPPConfigTemplateName(t *testing.T) {
data := `
{
"variables": {
"foo": null
},
"builders": [
{
"name": "test1-{{user \"foo\"}}",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"post-processors": [
{
"type": "test-pp",
"except": ["test1-{{user \"foo\"}}"]
}
]
}
`
template, err := ParseTemplate([]byte(data), map[string]string{"foo": "bar"})
if err != nil {
t.Fatalf("err: %s", err)
}
// Verify test1 has no post-processors
build, err := template.Build("test1-{{user \"foo\"}}", testTemplateComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
cbuild := build.(*coreBuild)
if len(cbuild.postProcessors) > 0 {
t.Fatal("should have no postProcessors")
}
// Verify test2 has one post-processors
build, err = template.Build("test2", testTemplateComponentFinder()) build, err = template.Build("test2", testTemplateComponentFinder())
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
...@@ -1245,7 +1300,62 @@ func TestTemplateBuild_exceptProv(t *testing.T) { ...@@ -1245,7 +1300,62 @@ func TestTemplateBuild_exceptProv(t *testing.T) {
t.Fatal("should have no provisioners") t.Fatal("should have no provisioners")
} }
// Verify test2 has no provisioners // Verify test2 has one provisioners
build, err = template.Build("test2", testTemplateComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
cbuild = build.(*coreBuild)
if len(cbuild.provisioners) != 1 {
t.Fatalf("invalid: %d", len(cbuild.provisioners))
}
}
func TestTemplateBuild_exceptProvConfigTemplateName(t *testing.T) {
data := `
{
"variables": {
"foo": null
},
"builders": [
{
"name": "test1-{{user \"foo\"}}",
"type": "test-builder"
},
{
"name": "test2",
"type": "test-builder"
}
],
"provisioners": [
{
"type": "test-prov",
"except": ["test1-{{user \"foo\"}}"]
}
]
}
`
template, err := ParseTemplate([]byte(data), map[string]string{"foo": "bar"})
if err != nil {
t.Fatalf("err: %s", err)
}
// Verify test1 has no provisioners
build, err := template.Build("test1-{{user \"foo\"}}", testTemplateComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
cbuild := build.(*coreBuild)
if len(cbuild.provisioners) > 0 {
t.Fatal("should have no provisioners")
}
// Verify test2 has one provisioners
build, err = template.Build("test2", testTemplateComponentFinder()) build, err = template.Build("test2", testTemplateComponentFinder())
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
...@@ -1325,7 +1435,7 @@ func TestTemplateBuild_onlyPP(t *testing.T) { ...@@ -1325,7 +1435,7 @@ func TestTemplateBuild_onlyPP(t *testing.T) {
t.Fatal("should have no postProcessors") t.Fatal("should have no postProcessors")
} }
// Verify test2 has no post-processors // Verify test2 has one post-processors
build, err = template.Build("test2", testTemplateComponentFinder()) build, err = template.Build("test2", testTemplateComponentFinder())
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
...@@ -1337,6 +1447,61 @@ func TestTemplateBuild_onlyPP(t *testing.T) { ...@@ -1337,6 +1447,61 @@ func TestTemplateBuild_onlyPP(t *testing.T) {
} }
} }
func TestTemplateBuild_onlyPPConfigTemplateName(t *testing.T) {
data := `
{
"variables": {
"foo": null
},
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2-{{user \"foo\"}}",
"type": "test-builder"
}
],
"post-processors": [
{
"type": "test-pp",
"only": ["test2-{{user \"foo\"}}"]
}
]
}
`
template, err := ParseTemplate([]byte(data), map[string]string{"foo": "bar"})
if err != nil {
t.Fatalf("err: %s", err)
}
// Verify test1 has no post-processors
build, err := template.Build("test1", testTemplateComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
cbuild := build.(*coreBuild)
if len(cbuild.postProcessors) > 0 {
t.Fatal("should have no postProcessors")
}
// Verify test2 has one post-processors
build, err = template.Build("test2-{{user \"foo\"}}", testTemplateComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
cbuild = build.(*coreBuild)
if len(cbuild.postProcessors) != 1 {
t.Fatalf("invalid: %d", len(cbuild.postProcessors))
}
}
func TestTemplateBuild_onlyProvInvalid(t *testing.T) { func TestTemplateBuild_onlyProvInvalid(t *testing.T) {
data := ` data := `
{ {
...@@ -1405,7 +1570,7 @@ func TestTemplateBuild_onlyProv(t *testing.T) { ...@@ -1405,7 +1570,7 @@ func TestTemplateBuild_onlyProv(t *testing.T) {
t.Fatal("should have no provisioners") t.Fatal("should have no provisioners")
} }
// Verify test2 has no provisioners // Verify test2 has one provisioners
build, err = template.Build("test2", testTemplateComponentFinder()) build, err = template.Build("test2", testTemplateComponentFinder())
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
...@@ -1417,6 +1582,61 @@ func TestTemplateBuild_onlyProv(t *testing.T) { ...@@ -1417,6 +1582,61 @@ func TestTemplateBuild_onlyProv(t *testing.T) {
} }
} }
func TestTemplateBuild_onlyProvConfigTemplateName(t *testing.T) {
data := `
{
"variables": {
"foo": null
},
"builders": [
{
"name": "test1",
"type": "test-builder"
},
{
"name": "test2-{{user \"foo\"}}",
"type": "test-builder"
}
],
"provisioners": [
{
"type": "test-prov",
"only": ["test2-{{user \"foo\"}}"]
}
]
}
`
template, err := ParseTemplate([]byte(data), map[string]string{"foo": "bar"})
if err != nil {
t.Fatalf("err: %s", err)
}
// Verify test1 has no provisioners
build, err := template.Build("test1", testTemplateComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
cbuild := build.(*coreBuild)
if len(cbuild.provisioners) > 0 {
t.Fatal("should have no provisioners")
}
// Verify test2 has one provisioners
build, err = template.Build("test2-{{user \"foo\"}}", testTemplateComponentFinder())
if err != nil {
t.Fatalf("err: %s", err)
}
cbuild = build.(*coreBuild)
if len(cbuild.provisioners) != 1 {
t.Fatalf("invalid: %d", len(cbuild.provisioners))
}
}
func TestTemplate_Build_ProvisionerOverride(t *testing.T) { func TestTemplate_Build_ProvisionerOverride(t *testing.T) {
data := ` data := `
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment