Commit 6a3dd16a authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer: template now handles user var logic

parent eeadafc4
...@@ -40,7 +40,7 @@ type Build interface { ...@@ -40,7 +40,7 @@ type Build interface {
// Prepare configures the various components of this build and reports // Prepare configures the various components of this build and reports
// any errors in doing so (such as syntax errors, validation errors, etc.). // any errors in doing so (such as syntax errors, validation errors, etc.).
// It also reports any warnings. // It also reports any warnings.
Prepare(v map[string]string) ([]string, error) Prepare() ([]string, error)
// Run runs the actual builder, returning an artifact implementation // Run runs the actual builder, returning an artifact implementation
// of what is built. If anything goes wrong, an error is returned. // of what is built. If anything goes wrong, an error is returned.
...@@ -78,7 +78,7 @@ type coreBuild struct { ...@@ -78,7 +78,7 @@ type coreBuild struct {
hooks map[string][]Hook hooks map[string][]Hook
postProcessors [][]coreBuildPostProcessor postProcessors [][]coreBuildPostProcessor
provisioners []coreBuildProvisioner provisioners []coreBuildProvisioner
variables map[string]coreBuildVariable variables map[string]string
debug bool debug bool
force bool force bool
...@@ -102,12 +102,6 @@ type coreBuildProvisioner struct { ...@@ -102,12 +102,6 @@ type coreBuildProvisioner struct {
config []interface{} config []interface{}
} }
// A user-variable that is part of a single build.
type coreBuildVariable struct {
Default string
Required bool
}
// Returns the name of the build. // Returns the name of the build.
func (b *coreBuild) Name() string { func (b *coreBuild) Name() string {
return b.name return b.name
...@@ -116,7 +110,7 @@ func (b *coreBuild) Name() string { ...@@ -116,7 +110,7 @@ func (b *coreBuild) Name() string {
// Prepare prepares the build by doing some initialization for the builder // Prepare prepares the build by doing some initialization for the builder
// and any hooks. This _must_ be called prior to Run. The parameter is the // and any hooks. This _must_ be called prior to Run. The parameter is the
// overrides for the variables within the template (if any). // overrides for the variables within the template (if any).
func (b *coreBuild) Prepare(userVars map[string]string) (warn []string, err error) { func (b *coreBuild) Prepare() (warn []string, err error) {
b.l.Lock() b.l.Lock()
defer b.l.Unlock() defer b.l.Unlock()
...@@ -126,46 +120,12 @@ func (b *coreBuild) Prepare(userVars map[string]string) (warn []string, err erro ...@@ -126,46 +120,12 @@ func (b *coreBuild) Prepare(userVars map[string]string) (warn []string, err erro
b.prepareCalled = true b.prepareCalled = true
// Compile the variables
varErrs := make([]error, 0)
variables := make(map[string]string)
for k, v := range b.variables {
variables[k] = v.Default
if v.Required {
if _, ok := userVars[k]; !ok {
varErrs = append(varErrs,
fmt.Errorf("Required user variable '%s' not set", k))
}
}
}
if userVars != nil {
for k, v := range userVars {
if _, ok := variables[k]; !ok {
varErrs = append(
varErrs, fmt.Errorf("Unknown user variable: %s", k))
continue
}
variables[k] = v
}
}
// If there were any problem with variables, return an error right
// away because we can't be certain anything else will actually work.
if len(varErrs) > 0 {
return nil, &MultiError{
Errors: varErrs,
}
}
packerConfig := map[string]interface{}{ packerConfig := map[string]interface{}{
BuildNameConfigKey: b.name, BuildNameConfigKey: b.name,
BuilderTypeConfigKey: b.builderType, BuilderTypeConfigKey: b.builderType,
DebugConfigKey: b.debug, DebugConfigKey: b.debug,
ForceConfigKey: b.force, ForceConfigKey: b.force,
UserVariablesConfigKey: variables, UserVariablesConfigKey: b.variables,
} }
// Prepare the builder // Prepare the builder
......
...@@ -22,7 +22,7 @@ func testBuild() *coreBuild { ...@@ -22,7 +22,7 @@ func testBuild() *coreBuild {
coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp"}, "testPP", make(map[string]interface{}), true}, coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp"}, "testPP", make(map[string]interface{}), true},
}, },
}, },
variables: make(map[string]coreBuildVariable), variables: make(map[string]string),
} }
} }
...@@ -48,7 +48,7 @@ func TestBuild_Prepare(t *testing.T) { ...@@ -48,7 +48,7 @@ func TestBuild_Prepare(t *testing.T) {
build := testBuild() build := testBuild()
builder := build.builder.(*MockBuilder) builder := build.builder.(*MockBuilder)
build.Prepare(nil) build.Prepare()
if !builder.PrepareCalled { if !builder.PrepareCalled {
t.Fatal("should be called") t.Fatal("should be called")
} }
...@@ -77,7 +77,7 @@ func TestBuild_Prepare(t *testing.T) { ...@@ -77,7 +77,7 @@ func TestBuild_Prepare(t *testing.T) {
func TestBuild_Prepare_Twice(t *testing.T) { func TestBuild_Prepare_Twice(t *testing.T) {
build := testBuild() build := testBuild()
warn, err := build.Prepare(nil) warn, err := build.Prepare()
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("bad: %#v", warn) t.Fatalf("bad: %#v", warn)
} }
...@@ -96,7 +96,7 @@ func TestBuild_Prepare_Twice(t *testing.T) { ...@@ -96,7 +96,7 @@ func TestBuild_Prepare_Twice(t *testing.T) {
} }
}() }()
build.Prepare(nil) build.Prepare()
} }
func TestBuildPrepare_BuilderWarniings(t *testing.T) { func TestBuildPrepare_BuilderWarniings(t *testing.T) {
...@@ -106,7 +106,7 @@ func TestBuildPrepare_BuilderWarniings(t *testing.T) { ...@@ -106,7 +106,7 @@ func TestBuildPrepare_BuilderWarniings(t *testing.T) {
builder := build.builder.(*MockBuilder) builder := build.builder.(*MockBuilder)
builder.PrepareWarnings = expected builder.PrepareWarnings = expected
warn, err := build.Prepare(nil) warn, err := build.Prepare()
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
...@@ -123,7 +123,7 @@ func TestBuild_Prepare_Debug(t *testing.T) { ...@@ -123,7 +123,7 @@ func TestBuild_Prepare_Debug(t *testing.T) {
builder := build.builder.(*MockBuilder) builder := build.builder.(*MockBuilder)
build.SetDebug(true) build.SetDebug(true)
build.Prepare(nil) build.Prepare()
if !builder.PrepareCalled { if !builder.PrepareCalled {
t.Fatalf("should be called") t.Fatalf("should be called")
} }
...@@ -148,10 +148,10 @@ func TestBuildPrepare_variables_default(t *testing.T) { ...@@ -148,10 +148,10 @@ func TestBuildPrepare_variables_default(t *testing.T) {
} }
build := testBuild() build := testBuild()
build.variables["foo"] = coreBuildVariable{Default: "bar"} build.variables["foo"] = "bar"
builder := build.builder.(*MockBuilder) builder := build.builder.(*MockBuilder)
warn, err := build.Prepare(nil) warn, err := build.Prepare()
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("bad: %#v", warn) t.Fatalf("bad: %#v", warn)
} }
...@@ -168,76 +168,12 @@ func TestBuildPrepare_variables_default(t *testing.T) { ...@@ -168,76 +168,12 @@ func TestBuildPrepare_variables_default(t *testing.T) {
} }
} }
func TestBuildPrepare_variables_nonexist(t *testing.T) {
build := testBuild()
build.variables["foo"] = coreBuildVariable{Default: "bar"}
warn, err := build.Prepare(map[string]string{"bar": "baz"})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err == nil {
t.Fatal("should have had error")
}
}
func TestBuildPrepare_variables_override(t *testing.T) {
packerConfig := testDefaultPackerConfig()
packerConfig[UserVariablesConfigKey] = map[string]string{
"foo": "baz",
}
build := testBuild()
build.variables["foo"] = coreBuildVariable{Default: "bar"}
builder := build.builder.(*MockBuilder)
warn, err := build.Prepare(map[string]string{"foo": "baz"})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err != nil {
t.Fatalf("err: %s", err)
}
if !builder.PrepareCalled {
t.Fatal("prepare should be called")
}
if !reflect.DeepEqual(builder.PrepareConfig[1], packerConfig) {
t.Fatalf("prepare bad: %#v", builder.PrepareConfig[1])
}
}
func TestBuildPrepare_variablesRequired(t *testing.T) {
build := testBuild()
build.variables["foo"] = coreBuildVariable{Required: true}
warn, err := build.Prepare(map[string]string{})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err == nil {
t.Fatal("should have had error")
}
// Test with setting the value
build = testBuild()
build.variables["foo"] = coreBuildVariable{Required: true}
warn, err = build.Prepare(map[string]string{"foo": ""})
if len(warn) > 0 {
t.Fatalf("bad: %#v", warn)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuild_Run(t *testing.T) { func TestBuild_Run(t *testing.T) {
cache := &TestCache{} cache := &TestCache{}
ui := testUi() ui := testUi()
build := testBuild() build := testBuild()
build.Prepare(nil) build.Prepare()
artifacts, err := build.Run(ui, cache) artifacts, err := build.Run(ui, cache)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
...@@ -287,7 +223,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { ...@@ -287,7 +223,7 @@ func TestBuild_Run_Artifacts(t *testing.T) {
build := testBuild() build := testBuild()
build.postProcessors = [][]coreBuildPostProcessor{} build.postProcessors = [][]coreBuildPostProcessor{}
build.Prepare(nil) build.Prepare()
artifacts, err := build.Run(ui, cache) artifacts, err := build.Run(ui, cache)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
...@@ -312,7 +248,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { ...@@ -312,7 +248,7 @@ func TestBuild_Run_Artifacts(t *testing.T) {
}, },
} }
build.Prepare(nil) build.Prepare()
artifacts, err = build.Run(ui, cache) artifacts, err = build.Run(ui, cache)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
...@@ -340,7 +276,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { ...@@ -340,7 +276,7 @@ func TestBuild_Run_Artifacts(t *testing.T) {
}, },
} }
build.Prepare(nil) build.Prepare()
artifacts, err = build.Run(ui, cache) artifacts, err = build.Run(ui, cache)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
...@@ -370,7 +306,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { ...@@ -370,7 +306,7 @@ func TestBuild_Run_Artifacts(t *testing.T) {
}, },
} }
build.Prepare(nil) build.Prepare()
artifacts, err = build.Run(ui, cache) artifacts, err = build.Run(ui, cache)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
...@@ -397,7 +333,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { ...@@ -397,7 +333,7 @@ func TestBuild_Run_Artifacts(t *testing.T) {
}, },
} }
build.Prepare(nil) build.Prepare()
artifacts, err = build.Run(ui, cache) artifacts, err = build.Run(ui, cache)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
......
...@@ -75,8 +75,10 @@ type RawProvisionerConfig struct { ...@@ -75,8 +75,10 @@ type RawProvisionerConfig struct {
// RawVariable represents a variable configuration within a template. // RawVariable represents a variable configuration within a template.
type RawVariable struct { type RawVariable struct {
Default string Default string // The default value for this variable
Required bool Required bool // If the variable is required or not
Value string // The set value for this variable
HasValue bool // True if the value was set
} }
// ParseTemplate takes a byte slice and parses a Template from it, returning // ParseTemplate takes a byte slice and parses a Template from it, returning
...@@ -84,7 +86,9 @@ type RawVariable struct { ...@@ -84,7 +86,9 @@ type RawVariable struct {
// could potentially be a MultiError, representing multiple errors. Knowing // could potentially be a MultiError, representing multiple errors. Knowing
// and checking for this can be useful, if you wish to format it in a certain // and checking for this can be useful, if you wish to format it in a certain
// way. // way.
func ParseTemplate(data []byte) (t *Template, err error) { //
// The second parameter, vars, are the values for a set of user variables.
func ParseTemplate(data []byte, vars map[string]string) (t *Template, err error) {
var rawTplInterface interface{} var rawTplInterface interface{}
err = jsonutil.Unmarshal(data, &rawTplInterface) err = jsonutil.Unmarshal(data, &rawTplInterface)
if err != nil { if err != nil {
...@@ -152,6 +156,13 @@ func ParseTemplate(data []byte) (t *Template, err error) { ...@@ -152,6 +156,13 @@ func ParseTemplate(data []byte) (t *Template, err error) {
continue continue
} }
// Set the value of this variable if we have it
if val, ok := vars[k]; ok {
variable.HasValue = true
variable.Value = val
delete(vars, k)
}
t.Variables[k] = variable t.Variables[k] = variable
} }
...@@ -313,6 +324,11 @@ func ParseTemplate(data []byte) (t *Template, err error) { ...@@ -313,6 +324,11 @@ func ParseTemplate(data []byte) (t *Template, err error) {
errors = append(errors, fmt.Errorf("No builders are defined in the template.")) errors = append(errors, fmt.Errorf("No builders are defined in the template."))
} }
// Verify that all the variable sets were for real variables.
for k, _ := range vars {
errors = append(errors, fmt.Errorf("Unknown user variables: %s", k))
}
// If there were errors, we put it into a MultiError and return // If there were errors, we put it into a MultiError and return
if len(errors) > 0 { if len(errors) > 0 {
err = &MultiError{errors} err = &MultiError{errors}
...@@ -325,7 +341,7 @@ func ParseTemplate(data []byte) (t *Template, err error) { ...@@ -325,7 +341,7 @@ func ParseTemplate(data []byte) (t *Template, err error) {
// ParseTemplateFile takes the given template file and parses it into // ParseTemplateFile takes the given template file and parses it into
// a single template. // a single template.
func ParseTemplateFile(path string) (*Template, error) { func ParseTemplateFile(path string, vars map[string]string) (*Template, error) {
var data []byte var data []byte
if path == "-" { if path == "-" {
...@@ -345,7 +361,7 @@ func ParseTemplateFile(path string) (*Template, error) { ...@@ -345,7 +361,7 @@ func ParseTemplateFile(path string) (*Template, error) {
} }
} }
return ParseTemplate(data) return ParseTemplate(data, vars)
} }
func parsePostProcessor(i int, rawV interface{}) (result []map[string]interface{}, errors []error) { func parsePostProcessor(i int, rawV interface{}) (result []map[string]interface{}, errors []error) {
...@@ -527,12 +543,24 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err ...@@ -527,12 +543,24 @@ func (t *Template) Build(name string, components *ComponentFinder) (b Build, err
} }
// Prepare the variables // Prepare the variables
variables := make(map[string]coreBuildVariable) var varErrors []error
variables := make(map[string]string)
for k, v := range t.Variables { for k, v := range t.Variables {
variables[k] = coreBuildVariable{ if v.Required && !v.HasValue {
Default: v.Default, varErrors = append(varErrors,
Required: v.Required, fmt.Errorf("Required user variable '%s' not set", k))
} }
var val string = v.Default
if v.HasValue {
val = v.Value
}
variables[k] = val
}
if len(varErrors) > 0 {
return nil, &MultiError{varErrors}
} }
b = &coreBuild{ b = &coreBuild{
......
This diff is collapsed.
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