From 99a93009ed0ff4427b5c1a67aa729482a47e0c9f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 26 May 2015 09:38:24 -0700 Subject: [PATCH] packer: remove Template --- packer/template.go | 734 --------------- packer/template_test.go | 1914 --------------------------------------- 2 files changed, 2648 deletions(-) delete mode 100644 packer/template.go delete mode 100644 packer/template_test.go diff --git a/packer/template.go b/packer/template.go deleted file mode 100644 index 717ae7682..000000000 --- a/packer/template.go +++ /dev/null @@ -1,734 +0,0 @@ -package packer - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "sort" - "text/template" - "time" - - "github.com/hashicorp/go-version" - "github.com/mitchellh/mapstructure" - jsonutil "github.com/mitchellh/packer/common/json" -) - -// The rawTemplate struct represents the structure of a template read -// directly from a file. The builders and other components map just to -// "interface{}" pointers since we actually don't know what their contents -// are until we read the "type" field. -type rawTemplate struct { - MinimumPackerVersion string `mapstructure:"min_packer_version"` - - Description string - Builders []map[string]interface{} - Hooks map[string][]string - Push PushConfig - PostProcessors []interface{} `mapstructure:"post-processors"` - Provisioners []map[string]interface{} - Variables map[string]interface{} -} - -// The Template struct represents a parsed template, parsed into the most -// completed form it can be without additional processing by the caller. -type Template struct { - RawContents []byte - Description string - Variables map[string]RawVariable - Builders map[string]RawBuilderConfig - Hooks map[string][]string - Push *PushConfig - PostProcessors [][]RawPostProcessorConfig - Provisioners []RawProvisionerConfig -} - -// PushConfig is the configuration structure for the push settings. -type PushConfig struct { - Name string - Address string - BaseDir string `mapstructure:"base_dir"` - Include []string - Exclude []string - Token string - VCS bool -} - -// The RawBuilderConfig struct represents a raw, unprocessed builder -// configuration. It contains the name of the builder as well as the -// raw configuration. If requested, this is used to compile into a full -// builder configuration at some point. -type RawBuilderConfig struct { - Name string - Type string - - RawConfig interface{} -} - -// RawPostProcessorConfig represents a raw, unprocessed post-processor -// configuration. It contains the type of the post processor as well as the -// raw configuration that is handed to the post-processor for it to process. -type RawPostProcessorConfig struct { - TemplateOnlyExcept `mapstructure:",squash"` - - Type string - KeepInputArtifact bool `mapstructure:"keep_input_artifact"` - RawConfig map[string]interface{} -} - -// RawProvisionerConfig represents a raw, unprocessed provisioner configuration. -// It contains the type of the provisioner as well as the raw configuration -// that is handed to the provisioner for it to process. -type RawProvisionerConfig struct { - TemplateOnlyExcept `mapstructure:",squash"` - - Type string - Override map[string]interface{} - RawPauseBefore string `mapstructure:"pause_before"` - - RawConfig interface{} - - pauseBefore time.Duration -} - -// RawVariable represents a variable configuration within a template. -type RawVariable struct { - Default string // The default value for this variable - 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 -// the template and possibly errors while loading the template. The error -// 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 -// way. -// -// 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{} - err = jsonutil.Unmarshal(data, &rawTplInterface) - if err != nil { - return - } - - // Decode the raw template interface into the actual rawTemplate - // structure, checking for any extranneous keys along the way. - var md mapstructure.Metadata - var rawTpl rawTemplate - decoderConfig := &mapstructure.DecoderConfig{ - Metadata: &md, - Result: &rawTpl, - } - - decoder, err := mapstructure.NewDecoder(decoderConfig) - if err != nil { - return - } - - err = decoder.Decode(rawTplInterface) - if err != nil { - return - } - - if rawTpl.MinimumPackerVersion != "" { - // TODO: NOPE! Replace this - Version := "1.0" - vCur, err := version.NewVersion(Version) - if err != nil { - panic(err) - } - vReq, err := version.NewVersion(rawTpl.MinimumPackerVersion) - if err != nil { - return nil, fmt.Errorf( - "'minimum_packer_version' error: %s", err) - } - - if vCur.LessThan(vReq) { - return nil, fmt.Errorf( - "Template requires Packer version %s. "+ - "Running version is %s.", - vReq, vCur) - } - } - - errors := make([]error, 0) - - if len(md.Unused) > 0 { - sort.Strings(md.Unused) - for _, unused := range md.Unused { - errors = append( - errors, fmt.Errorf("Unknown root level key in template: '%s'", unused)) - } - } - - t = &Template{} - t.RawContents = data - t.Description = rawTpl.Description - t.Variables = make(map[string]RawVariable) - t.Builders = make(map[string]RawBuilderConfig) - t.Hooks = rawTpl.Hooks - t.Push = &rawTpl.Push - t.PostProcessors = make([][]RawPostProcessorConfig, len(rawTpl.PostProcessors)) - t.Provisioners = make([]RawProvisionerConfig, len(rawTpl.Provisioners)) - - // Gather all the variables - for k, v := range rawTpl.Variables { - var variable RawVariable - variable.Required = v == nil - - // Create a new mapstructure decoder in order to decode the default - // value since this is the only value in the regular template that - // can be weakly typed. - decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - Result: &variable.Default, - WeaklyTypedInput: true, - }) - if err != nil { - // This should never happen. - panic(err) - } - - err = decoder.Decode(v) - if err != nil { - errors = append(errors, - fmt.Errorf("Error decoding default value for user var '%s': %s", k, err)) - 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 - } - - // Gather all the builders - for i, v := range rawTpl.Builders { - var raw RawBuilderConfig - if err := mapstructure.Decode(v, &raw); err != nil { - if merr, ok := err.(*mapstructure.Error); ok { - for _, err := range merr.Errors { - errors = append(errors, fmt.Errorf("builder %d: %s", i+1, err)) - } - } else { - errors = append(errors, fmt.Errorf("builder %d: %s", i+1, err)) - } - - continue - } - - if raw.Type == "" { - errors = append(errors, fmt.Errorf("builder %d: missing 'type'", i+1)) - continue - } - - // Attempt to get the name of the builder. If the "name" key - // missing, use the "type" field, which is guaranteed to exist - // at this point. - if raw.Name == "" { - raw.Name = raw.Type - } - - // Check if we already have a builder with this name and error if so - if _, ok := t.Builders[raw.Name]; ok { - errors = append(errors, fmt.Errorf("builder with name '%s' already exists", raw.Name)) - continue - } - - // Now that we have the name, remove it from the config - as the builder - // itself doesn't know about, and it will cause a validation error. - delete(v, "name") - - raw.RawConfig = v - - t.Builders[raw.Name] = raw - } - - // Gather all the post-processors. This is a complicated process since there - // are actually three different formats that the user can use to define - // a post-processor. - for i, rawV := range rawTpl.PostProcessors { - rawPP, err := parsePostProcessor(i, rawV) - if err != nil { - errors = append(errors, err...) - continue - } - - configs := make([]RawPostProcessorConfig, 0, len(rawPP)) - for j, pp := range rawPP { - var config RawPostProcessorConfig - if err := mapstructure.Decode(pp, &config); err != nil { - if merr, ok := err.(*mapstructure.Error); ok { - for _, err := range merr.Errors { - errors = append(errors, - fmt.Errorf("Post-processor #%d.%d: %s", i+1, j+1, err)) - } - } else { - errors = append(errors, - fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err)) - } - - continue - } - - if config.Type == "" { - errors = append(errors, - fmt.Errorf("Post-processor %d.%d: missing 'type'", i+1, j+1)) - continue - } - - // Remove the input keep_input_artifact option - config.TemplateOnlyExcept.Prune(pp) - delete(pp, "keep_input_artifact") - - // Verify that the only settings are good - if errs := config.TemplateOnlyExcept.Validate(t.Builders); len(errs) > 0 { - for _, err := range errs { - errors = append(errors, - fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err)) - } - - continue - } - - config.RawConfig = pp - - // Add it to the list of configs - configs = append(configs, config) - } - - t.PostProcessors[i] = configs - } - - // Gather all the provisioners - for i, v := range rawTpl.Provisioners { - raw := &t.Provisioners[i] - if err := mapstructure.Decode(v, raw); err != nil { - if merr, ok := err.(*mapstructure.Error); ok { - for _, err := range merr.Errors { - errors = append(errors, fmt.Errorf("provisioner %d: %s", i+1, err)) - } - } else { - errors = append(errors, fmt.Errorf("provisioner %d: %s", i+1, err)) - } - - continue - } - - if raw.Type == "" { - errors = append(errors, fmt.Errorf("provisioner %d: missing 'type'", i+1)) - continue - } - - // Delete the keys that we used - raw.TemplateOnlyExcept.Prune(v) - delete(v, "override") - - // Verify that the override keys exist... - for name, _ := range raw.Override { - if _, ok := t.Builders[name]; !ok { - errors = append( - errors, fmt.Errorf("provisioner %d: build '%s' not found for override", i+1, name)) - } - } - - // Verify that the only settings are good - if errs := raw.TemplateOnlyExcept.Validate(t.Builders); len(errs) > 0 { - for _, err := range errs { - errors = append(errors, - fmt.Errorf("provisioner %d: %s", i+1, err)) - } - } - - // Setup the pause settings - if raw.RawPauseBefore != "" { - duration, err := time.ParseDuration(raw.RawPauseBefore) - if err != nil { - errors = append( - errors, fmt.Errorf( - "provisioner %d: pause_before invalid: %s", - i+1, err)) - } - - raw.pauseBefore = duration - } - - // Remove the pause_before setting if it is there so that we don't - // get template validation errors later. - delete(v, "pause_before") - - raw.RawConfig = v - } - - if len(t.Builders) == 0 { - 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 len(errors) > 0 { - err = &MultiError{errors} - t = nil - return - } - - return -} - -// ParseTemplateFile takes the given template file and parses it into -// a single template. -func ParseTemplateFile(path string, vars map[string]string) (*Template, error) { - var data []byte - - if path == "-" { - // Read from stdin... - buf := new(bytes.Buffer) - _, err := io.Copy(buf, os.Stdin) - if err != nil { - return nil, err - } - - data = buf.Bytes() - } else { - var err error - data, err = ioutil.ReadFile(path) - if err != nil { - return nil, err - } - } - - return ParseTemplate(data, vars) -} - -func parsePostProcessor(i int, rawV interface{}) (result []map[string]interface{}, errors []error) { - switch v := rawV.(type) { - case string: - result = []map[string]interface{}{ - {"type": v}, - } - case map[string]interface{}: - result = []map[string]interface{}{v} - case []interface{}: - result = make([]map[string]interface{}, len(v)) - errors = make([]error, 0) - for j, innerRawV := range v { - switch innerV := innerRawV.(type) { - case string: - result[j] = map[string]interface{}{"type": innerV} - case map[string]interface{}: - result[j] = innerV - case []interface{}: - errors = append( - errors, - fmt.Errorf("Post-processor %d.%d: sequences not allowed to be nested in sequences", i+1, j+1)) - default: - errors = append(errors, fmt.Errorf("Post-processor %d.%d is in a bad format.", i+1, j+1)) - } - } - - if len(errors) == 0 { - errors = nil - } - default: - result = nil - errors = []error{fmt.Errorf("Post-processor %d is in a bad format.", i+1)} - } - - return -} - -// BuildNames returns a slice of the available names of builds that -// this template represents. -func (t *Template) BuildNames() []string { - names := make([]string, 0, len(t.Builders)) - for name, _ := range t.Builders { - names = append(names, name) - } - - return names -} - -// Build returns a Build for the given name. -// -// If the build does not exist as part of this template, an error is -// returned. -func (t *Template) Build(name string, components *ComponentFinder) (b Build, err error) { - // Setup the Builder - builderConfig, ok := t.Builders[name] - if !ok { - err = fmt.Errorf("No such build found in template: %s", name) - return - } - - // We panic if there is no builder function because this is really - // an internal bug that always needs to be fixed, not an error. - if components.Builder == nil { - panic("no builder function") - } - - // Panic if there are provisioners on the template but no provisioner - // component finder. This is always an internal error, so we panic. - if len(t.Provisioners) > 0 && components.Provisioner == nil { - panic("no provisioner function") - } - - builder, err := components.Builder(builderConfig.Type) - if err != nil { - return - } - - if builder == nil { - err = fmt.Errorf("Builder type not found: %s", builderConfig.Type) - return - } - - // Process the name - tpl, variables, err := t.NewConfigTemplate() - if err != nil { - return nil, err - } - - rawName := name - name, err = tpl.Process(name, nil) - if err != nil { - return nil, err - } - - // Gather the Hooks - hooks := make(map[string][]Hook) - for tplEvent, tplHooks := range t.Hooks { - curHooks := make([]Hook, 0, len(tplHooks)) - - for _, hookName := range tplHooks { - var hook Hook - hook, err = components.Hook(hookName) - if err != nil { - return - } - - if hook == nil { - err = fmt.Errorf("Hook not found: %s", hookName) - return - } - - curHooks = append(curHooks, hook) - } - - hooks[tplEvent] = curHooks - } - - // Prepare the post-processors - postProcessors := make([][]coreBuildPostProcessor, 0, len(t.PostProcessors)) - for _, rawPPs := range t.PostProcessors { - current := make([]coreBuildPostProcessor, 0, len(rawPPs)) - for _, rawPP := range rawPPs { - if rawPP.TemplateOnlyExcept.Skip(rawName) { - continue - } - - pp, err := components.PostProcessor(rawPP.Type) - if err != nil { - return nil, err - } - - if pp == nil { - return nil, fmt.Errorf("PostProcessor type not found: %s", rawPP.Type) - } - - current = append(current, coreBuildPostProcessor{ - processor: pp, - processorType: rawPP.Type, - config: rawPP.RawConfig, - keepInputArtifact: rawPP.KeepInputArtifact, - }) - } - - // If we have no post-processors in this chain, just continue. - // This can happen if the post-processors skip certain builds. - if len(current) == 0 { - continue - } - - postProcessors = append(postProcessors, current) - } - - // Prepare the provisioners - provisioners := make([]coreBuildProvisioner, 0, len(t.Provisioners)) - for _, rawProvisioner := range t.Provisioners { - if rawProvisioner.TemplateOnlyExcept.Skip(rawName) { - continue - } - - var provisioner Provisioner - provisioner, err = components.Provisioner(rawProvisioner.Type) - if err != nil { - return - } - - if provisioner == nil { - err = fmt.Errorf("Provisioner type not found: %s", rawProvisioner.Type) - return - } - - configs := make([]interface{}, 1, 2) - configs[0] = rawProvisioner.RawConfig - - if rawProvisioner.Override != nil { - if override, ok := rawProvisioner.Override[name]; ok { - configs = append(configs, override) - } - } - - if rawProvisioner.pauseBefore > 0 { - provisioner = &PausedProvisioner{ - PauseBefore: rawProvisioner.pauseBefore, - Provisioner: provisioner, - } - } - - coreProv := coreBuildProvisioner{provisioner, configs} - provisioners = append(provisioners, coreProv) - } - - b = &coreBuild{ - name: name, - builder: builder, - builderConfig: builderConfig.RawConfig, - builderType: builderConfig.Type, - hooks: hooks, - postProcessors: postProcessors, - provisioners: provisioners, - variables: variables, - } - - 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" -// meta-parameters. -type TemplateOnlyExcept struct { - Only []string - Except []string -} - -// Prune will prune out the used values from the raw map. -func (t *TemplateOnlyExcept) Prune(raw map[string]interface{}) { - delete(raw, "except") - delete(raw, "only") -} - -// Skip tests if we should skip putting this item onto a build. -func (t *TemplateOnlyExcept) Skip(name string) bool { - if len(t.Only) > 0 { - onlyFound := false - for _, n := range t.Only { - if n == name { - onlyFound = true - break - } - } - - if !onlyFound { - // Skip this provisioner - return true - } - } - - // If the name is in the except list, then skip that - for _, n := range t.Except { - if n == name { - return true - } - } - - return false -} - -// Validates the only/except parameters. -func (t *TemplateOnlyExcept) Validate(b map[string]RawBuilderConfig) (e []error) { - if len(t.Only) > 0 && len(t.Except) > 0 { - e = append(e, - fmt.Errorf("Only one of 'only' or 'except' may be specified.")) - } - - if len(t.Only) > 0 { - for _, n := range t.Only { - if _, ok := b[n]; !ok { - e = append(e, - fmt.Errorf("'only' specified builder '%s' not found", n)) - } - } - } - - for _, n := range t.Except { - if _, ok := b[n]; !ok { - e = append(e, - fmt.Errorf("'except' specified builder '%s' not found", n)) - } - } - - return -} diff --git a/packer/template_test.go b/packer/template_test.go deleted file mode 100644 index 2d0949376..000000000 --- a/packer/template_test.go +++ /dev/null @@ -1,1914 +0,0 @@ -package packer - -import ( - "io/ioutil" - "os" - "reflect" - "sort" - "testing" - "time" -) - -func testTemplateComponentFinder() *ComponentFinder { - builder := new(MockBuilder) - pp := new(MockPostProcessor) - provisioner := &MockProvisioner{} - - builderMap := map[string]Builder{ - "test-builder": builder, - } - - ppMap := map[string]PostProcessor{ - "test-pp": pp, - } - - provisionerMap := map[string]Provisioner{ - "test-prov": provisioner, - } - - builderFactory := func(n string) (Builder, error) { return builderMap[n], nil } - ppFactory := func(n string) (PostProcessor, error) { return ppMap[n], nil } - provFactory := func(n string) (Provisioner, error) { return provisionerMap[n], nil } - return &ComponentFinder{ - Builder: builderFactory, - PostProcessor: ppFactory, - Provisioner: provFactory, - } -} - -func TestParseTemplateFile_basic(t *testing.T) { - data := ` - { - "builders": [{"type": "something"}] - } - ` - - tf, err := ioutil.TempFile("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - tf.Write([]byte(data)) - tf.Close() - - result, err := ParseTemplateFile(tf.Name(), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if len(result.Builders) != 1 { - t.Fatalf("bad: %#v", result.Builders) - } - - if string(result.RawContents) != data { - t.Fatalf("expected %q to be %q", result.RawContents, data) - } -} - -func TestParseTemplateFile_minPackerVersionBad(t *testing.T) { - data := ` - { - "min_packer_version": "27.0.0", - "builders": [{"type": "something"}] - } - ` - - tf, err := ioutil.TempFile("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - tf.Write([]byte(data)) - tf.Close() - - _, err = ParseTemplateFile(tf.Name(), nil) - if err == nil { - t.Fatal("expects error") - } -} - -func TestParseTemplateFile_minPackerVersionFormat(t *testing.T) { - data := ` - { - "min_packer_version": "NOPE NOPE NOPE", - "builders": [{"type": "something"}] - } - ` - - tf, err := ioutil.TempFile("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - tf.Write([]byte(data)) - tf.Close() - - _, err = ParseTemplateFile(tf.Name(), nil) - if err == nil { - t.Fatal("expects error") - } -} - -func TestParseTemplateFile_minPackerVersionGood(t *testing.T) { - data := ` - { - "min_packer_version": "0.1", - "builders": [{"type": "something"}] - } - ` - - tf, err := ioutil.TempFile("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - tf.Write([]byte(data)) - tf.Close() - - _, err = ParseTemplateFile(tf.Name(), nil) - if err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestParseTemplateFile_stdin(t *testing.T) { - data := ` - { - "builders": [{"type": "something"}] - } - ` - - tf, err := ioutil.TempFile("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - defer tf.Close() - tf.Write([]byte(data)) - - // Sync and seek to the beginning so that we can re-read the contents - tf.Sync() - tf.Seek(0, 0) - - // Set stdin to something we control - oldStdin := os.Stdin - defer func() { os.Stdin = oldStdin }() - os.Stdin = tf - - result, err := ParseTemplateFile("-", nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if len(result.Builders) != 1 { - t.Fatalf("bad: %#v", result.Builders) - } -} - -func TestParseTemplate_Basic(t *testing.T) { - data := ` - { - "builders": [{"type": "something"}] - } - ` - - result, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - if result == nil { - t.Fatal("should have result") - } - if len(result.Builders) != 1 { - t.Fatalf("bad: %#v", result.Builders) - } -} - -func TestParseTemplate_Description(t *testing.T) { - data := ` - { - "description": "Foo", - "builders": [{"type": "something"}] - } - ` - - result, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - if result == nil { - t.Fatal("should have result") - } - if result.Description != "Foo" { - t.Fatalf("bad: %#v", result.Description) - } -} - -func TestParseTemplate_Invalid(t *testing.T) { - // Note there is an extra comma below for a purposeful - // syntax error in the JSON. - data := ` - { - "builders": [], - } - ` - - result, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("shold have error") - } - if result != nil { - t.Fatal("should not have result") - } -} - -func TestParseTemplate_InvalidKeys(t *testing.T) { - // Note there is an extra comma below for a purposeful - // syntax error in the JSON. - data := ` - { - "builders": [{"type": "foo"}], - "what is this": "" - } - ` - - result, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } - if result != nil { - t.Fatal("should not have result") - } -} - -func TestParseTemplate_BuilderWithoutType(t *testing.T) { - data := ` - { - "builders": [{}] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestParseTemplate_BuilderWithNonStringType(t *testing.T) { - data := ` - { - "builders": [{ - "type": 42 - }] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestParseTemplate_BuilderWithoutName(t *testing.T) { - data := ` - { - "builders": [ - { - "type": "amazon-ebs" - } - ] - } - ` - - result, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - if result == nil { - t.Fatal("should have result") - } - if len(result.Builders) != 1 { - t.Fatalf("bad: %#v", result.Builders) - } - - builder, ok := result.Builders["amazon-ebs"] - if !ok { - t.Fatal("should be ok") - } - if builder.Type != "amazon-ebs" { - t.Fatalf("bad: %#v", builder.Type) - } -} - -func TestParseTemplate_BuilderWithName(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "bob", - "type": "amazon-ebs" - } - ] - } - ` - - result, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - if result == nil { - t.Fatal("should have result") - } - if len(result.Builders) != 1 { - t.Fatalf("bad: %#v", result.Builders) - } - - builder, ok := result.Builders["bob"] - if !ok { - t.Fatal("should be ok") - } - if builder.Type != "amazon-ebs" { - t.Fatalf("bad: %#v", builder.Type) - } - - RawConfig := builder.RawConfig - if RawConfig == nil { - t.Fatal("missing builder raw config") - } - - expected := map[string]interface{}{ - "type": "amazon-ebs", - } - - if !reflect.DeepEqual(RawConfig, expected) { - t.Fatalf("bad raw: %#v", RawConfig) - } -} - -func TestParseTemplate_BuilderWithConflictingName(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "bob", - "type": "amazon-ebs" - }, - { - "name": "bob", - "type": "foo", - } - ] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestParseTemplate_Hooks(t *testing.T) { - data := ` - { - - "builders": [{"type": "foo"}], - - "hooks": { - "event": ["foo", "bar"] - } - } - ` - - result, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - if result == nil { - t.Fatal("should have result") - } - if len(result.Hooks) != 1 { - t.Fatalf("bad: %#v", result.Hooks) - } - - hooks, ok := result.Hooks["event"] - if !ok { - t.Fatal("should be okay") - } - if !reflect.DeepEqual(hooks, []string{"foo", "bar"}) { - t.Fatalf("bad: %#v", hooks) - } -} - -func TestParseTemplate_PostProcessors(t *testing.T) { - data := ` - { - "builders": [{"type": "foo"}], - - "post-processors": [ - "simple", - - { "type": "detailed" }, - - [ "foo", { "type": "bar" } ] - ] - } - ` - - tpl, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("error parsing: %s", err) - } - - if len(tpl.PostProcessors) != 3 { - t.Fatalf("bad number of post-processors: %d", len(tpl.PostProcessors)) - } - - pp := tpl.PostProcessors[0] - if len(pp) != 1 { - t.Fatalf("wrong number of configs in simple: %d", len(pp)) - } - - if pp[0].Type != "simple" { - t.Fatalf("wrong type for simple: %s", pp[0].Type) - } - - pp = tpl.PostProcessors[1] - if len(pp) != 1 { - t.Fatalf("wrong number of configs in detailed: %d", len(pp)) - } - - if pp[0].Type != "detailed" { - t.Fatalf("wrong type for detailed: %s", pp[0].Type) - } - - pp = tpl.PostProcessors[2] - if len(pp) != 2 { - t.Fatalf("wrong number of configs for sequence: %d", len(pp)) - } - - if pp[0].Type != "foo" { - t.Fatalf("wrong type for sequence 0: %s", pp[0].Type) - } - - if pp[1].Type != "bar" { - t.Fatalf("wrong type for sequence 1: %s", pp[1].Type) - } -} - -func TestParseTemplate_ProvisionerWithoutType(t *testing.T) { - data := ` - { - "builders": [{"type": "foo"}], - - "provisioners": [{}] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("err should not be nil") - } -} - -func TestParseTemplate_ProvisionerWithNonStringType(t *testing.T) { - data := ` - { - "builders": [{"type": "foo"}], - - "provisioners": [{ - "type": 42 - }] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestParseTemplate_Provisioners(t *testing.T) { - data := ` - { - "builders": [{"type": "foo"}], - - "provisioners": [ - { - "type": "shell" - } - ] - } - ` - - result, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - if result == nil { - t.Fatal("should have result") - } - if len(result.Provisioners) != 1 { - t.Fatalf("bad: %#v", result.Provisioners) - } - if result.Provisioners[0].Type != "shell" { - t.Fatalf("bad: %#v", result.Provisioners[0].Type) - } - if result.Provisioners[0].RawConfig == nil { - t.Fatal("should have raw config") - } -} - -func TestParseTemplate_ProvisionerPauseBefore(t *testing.T) { - data := ` - { - "builders": [{"type": "foo"}], - - "provisioners": [ - { - "type": "shell", - "pause_before": "10s" - } - ] - } - ` - - result, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - if result == nil { - t.Fatal("should have result") - } - if len(result.Provisioners) != 1 { - t.Fatalf("bad: %#v", result.Provisioners) - } - if result.Provisioners[0].Type != "shell" { - t.Fatalf("bad: %#v", result.Provisioners[0].Type) - } - if result.Provisioners[0].pauseBefore != 10*time.Second { - t.Fatalf("bad: %s", result.Provisioners[0].pauseBefore) - } -} - -func TestParseTemplateFile_push(t *testing.T) { - data := ` - { - "builders": [{"type": "something"}], - - "push": { - "name": "hello", - "include": ["one"], - "exclude": ["two"] - } - } - ` - - tf, err := ioutil.TempFile("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - tf.Write([]byte(data)) - tf.Close() - - result, err := ParseTemplateFile(tf.Name(), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := &PushConfig{ - Name: "hello", - Include: []string{"one"}, - Exclude: []string{"two"}, - } - if !reflect.DeepEqual(result.Push, expected) { - t.Fatalf("bad: %#v", result.Push) - } -} - -func TestParseTemplate_Variables(t *testing.T) { - data := ` - { - "variables": { - "foo": "bar", - "bar": null, - "baz": 27 - }, - - "builders": [{"type": "something"}] - } - ` - - result, err := ParseTemplate([]byte(data), map[string]string{ - "bar": "bar", - }) - if err != nil { - t.Fatalf("err: %s", err) - } - - if result.Variables == nil || len(result.Variables) != 3 { - t.Fatalf("bad vars: %#v", result.Variables) - } - - if result.Variables["foo"].Default != "bar" { - t.Fatal("foo default is not right") - } - if result.Variables["foo"].Required { - t.Fatal("foo should not be required") - } - if result.Variables["foo"].HasValue { - t.Fatal("foo should not have value") - } - - if result.Variables["bar"].Default != "" { - t.Fatal("default should be empty") - } - if !result.Variables["bar"].Required { - t.Fatal("bar should be required") - } - if !result.Variables["bar"].HasValue { - t.Fatal("bar should have value") - } - if result.Variables["bar"].Value != "bar" { - t.Fatal("bad value") - } - - if result.Variables["baz"].Default != "27" { - t.Fatal("default should be empty") - } - - if result.Variables["baz"].Required { - t.Fatal("baz should not be required") - } -} - -func TestParseTemplate_variablesSet(t *testing.T) { - data := ` - { - "variables": { - "foo": "bar" - }, - - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), map[string]string{ - "foo": "value", - }) - if err != nil { - t.Fatalf("err: %s", err) - } - - if len(template.Variables) != 1 { - t.Fatalf("bad vars: %#v", template.Variables) - } - if template.Variables["foo"].Value != "value" { - t.Fatalf("bad: %#v", template.Variables["foo"]) - } -} - -func TestParseTemplate_variablesSetUnknown(t *testing.T) { - data := ` - { - "variables": { - "foo": "bar" - }, - - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ] - } - ` - - _, err := ParseTemplate([]byte(data), map[string]string{ - "what": "value", - }) - if err == nil { - t.Fatal("should error") - } -} - -func TestParseTemplate_variablesBadDefault(t *testing.T) { - data := ` - { - "variables": { - "foo": 7, - }, - - "builders": [{"type": "something"}] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestTemplate_BuildNames(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "bob", - "type": "amazon-ebs" - }, - { - "name": "chris", - "type": "another" - } - ] - } - ` - - result, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - buildNames := result.BuildNames() - sort.Strings(buildNames) - if !reflect.DeepEqual(buildNames, []string{"bob", "chris"}) { - t.Fatalf("bad: %#v", buildNames) - } -} - -func TestTemplate_BuildUnknown(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("bad: %s", err) - } - - build, err := template.Build("nope", nil) - if build != nil { - t.Fatalf("build should be nil: %#v", build) - } - if err == nil { - t.Fatal("should have error") - } -} - -func TestTemplate_BuildUnknownBuilder(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - builderFactory := func(string) (Builder, error) { return nil, nil } - components := &ComponentFinder{Builder: builderFactory} - build, err := template.Build("test1", components) - if err == nil { - t.Fatal("should have error") - } - if build != nil { - t.Fatalf("bad: %#v", build) - } -} - -func TestTemplateBuild_envInVars(t *testing.T) { - data := ` - { - "variables": { - "foo": "{{env \"foo\"}}" - }, - - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ] - } - ` - - defer os.Setenv("foo", os.Getenv("foo")) - if err := os.Setenv("foo", "bar"); err != nil { - t.Fatalf("err: %s", err) - } - - template, err := ParseTemplate([]byte(data), map[string]string{}) - if err != nil { - t.Fatalf("err: %s", err) - } - - b, err := template.Build("test1", testComponentFinder()) - if err != nil { - t.Fatalf("err: %s", err) - } - - coreBuild, ok := b.(*coreBuild) - if !ok { - t.Fatal("should be ok") - } - - if coreBuild.variables["foo"] != "bar" { - t.Fatalf("bad: %#v", coreBuild.variables) - } -} - -func TestTemplateBuild_names(t *testing.T) { - data := ` - { - "variables": { - "foo": null - }, - - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2-{{user \"foo\"}}", - "type": "test-builder" - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), map[string]string{"foo": "bar"}) - if err != nil { - t.Fatalf("err: %s", err) - } - - b, err := template.Build("test1", testComponentFinder()) - if err != nil { - t.Fatalf("err: %s", err) - } - if b.Name() != "test1" { - t.Fatalf("bad: %#v", b.Name()) - } - - b, err = template.Build("test2-{{user \"foo\"}}", testComponentFinder()) - if err != nil { - t.Fatalf("err: %s", err) - } - if b.Name() != "test2-bar" { - t.Fatalf("bad: %#v", b.Name()) - } -} - -func TestTemplate_Build_NilBuilderFunc(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov" - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - defer func() { - p := recover() - if p == nil { - t.Fatal("should panic") - } - - if p.(string) != "no builder function" { - t.Fatalf("bad panic: %s", p.(string)) - } - }() - - template.Build("test1", &ComponentFinder{}) -} - -func TestTemplate_Build_NilProvisionerFunc(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov" - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - defer func() { - p := recover() - if p == nil { - t.Fatal("should panic") - } - - if p.(string) != "no provisioner function" { - t.Fatalf("bad panic: %s", p.(string)) - } - }() - - template.Build("test1", &ComponentFinder{ - Builder: func(string) (Builder, error) { return nil, nil }, - }) -} - -func TestTemplate_Build_NilProvisionerFunc_WithNoProvisioners(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ], - - "provisioners": [] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - template.Build("test1", &ComponentFinder{ - Builder: func(string) (Builder, error) { return nil, nil }, - }) -} - -func TestTemplate_Build(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov" - } - ], - - "post-processors": [ - "simple", - [ - "simple", - { "type": "simple", "keep_input_artifact": true } - ] - ] - } - ` - - expectedConfig := map[string]interface{}{ - "type": "test-builder", - } - - template, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - builder := new(MockBuilder) - builderMap := map[string]Builder{ - "test-builder": builder, - } - - provisioner := &MockProvisioner{} - provisionerMap := map[string]Provisioner{ - "test-prov": provisioner, - } - - pp := new(MockPostProcessor) - ppMap := map[string]PostProcessor{ - "simple": pp, - } - - builderFactory := func(n string) (Builder, error) { return builderMap[n], nil } - ppFactory := func(n string) (PostProcessor, error) { return ppMap[n], nil } - provFactory := func(n string) (Provisioner, error) { return provisionerMap[n], nil } - components := &ComponentFinder{ - Builder: builderFactory, - PostProcessor: ppFactory, - Provisioner: provFactory, - } - - // Get the build, verifying we can get it without issue, but also - // that the proper builder was looked up and used for the build. - build, err := template.Build("test1", components) - if err != nil { - t.Fatalf("err: %s", err) - } - - coreBuild, ok := build.(*coreBuild) - if !ok { - t.Fatal("should be ok") - } - if coreBuild.builder != builder { - t.Fatalf("bad: %#v", coreBuild.builder) - } - if !reflect.DeepEqual(coreBuild.builderConfig, expectedConfig) { - t.Fatalf("bad: %#v", coreBuild.builderConfig) - } - if len(coreBuild.provisioners) != 1 { - t.Fatalf("bad: %#v", coreBuild.provisioners) - } - if len(coreBuild.postProcessors) != 2 { - t.Fatalf("bad: %#v", coreBuild.postProcessors) - } - - if len(coreBuild.postProcessors[0]) != 1 { - t.Fatalf("bad: %#v", coreBuild.postProcessors[0]) - } - if len(coreBuild.postProcessors[1]) != 2 { - t.Fatalf("bad: %#v", coreBuild.postProcessors[1]) - } - - if coreBuild.postProcessors[1][0].keepInputArtifact { - t.Fatal("postProcessors[1][0] should not keep input artifact") - } - if !coreBuild.postProcessors[1][1].keepInputArtifact { - t.Fatal("postProcessors[1][1] should keep input artifact") - } - - config := coreBuild.postProcessors[1][1].config - if _, ok := config["keep_input_artifact"]; ok { - t.Fatal("should not have keep_input_artifact") - } -} - -func TestTemplateBuild_exceptOnlyPP(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2", - "type": "test-builder" - } - ], - - "post-processors": [ - { - "type": "test-pp", - "except": ["test1"], - "only": ["test1"] - } - ] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestTemplateBuild_exceptOnlyProv(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov", - "except": ["test1"], - "only": ["test1"] - } - ] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestTemplateBuild_exceptPPInvalid(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2", - "type": "test-builder" - } - ], - - "post-processors": [ - { - "type": "test-pp", - "except": ["test5"] - } - ] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestTemplateBuild_exceptPP(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2", - "type": "test-builder" - } - ], - - "post-processors": [ - { - "type": "test-pp", - "except": ["test1"] - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - 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", 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()) - 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_exceptProvInvalid(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov", - "except": ["test5"] - } - ] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestTemplateBuild_exceptProv(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov", - "except": ["test1"] - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - 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", 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()) - 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_onlyPPInvalid(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2", - "type": "test-builder" - } - ], - - "post-processors": [ - { - "type": "test-pp", - "only": ["test5"] - } - ] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestTemplateBuild_onlyPP(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2", - "type": "test-builder" - } - ], - - "post-processors": [ - { - "type": "test-pp", - "only": ["test2"] - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - 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", 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_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) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov", - "only": ["test5"] - } - ] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestTemplateBuild_onlyProv(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - }, - { - "name": "test2", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov", - "only": ["test2"] - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - 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", 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_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) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov", - - "override": { - "test1": {} - } - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - RawConfig := template.Provisioners[0].RawConfig - if RawConfig == nil { - t.Fatal("missing provisioner raw config") - } - - expected := map[string]interface{}{ - "type": "test-prov", - } - - if !reflect.DeepEqual(RawConfig, expected) { - t.Fatalf("bad raw: %#v", RawConfig) - } - - builder := new(MockBuilder) - builderMap := map[string]Builder{ - "test-builder": builder, - } - - provisioner := &MockProvisioner{} - provisionerMap := map[string]Provisioner{ - "test-prov": provisioner, - } - - builderFactory := func(n string) (Builder, error) { return builderMap[n], nil } - provFactory := func(n string) (Provisioner, error) { return provisionerMap[n], nil } - components := &ComponentFinder{ - Builder: builderFactory, - Provisioner: provFactory, - } - - // Get the build, verifying we can get it without issue, but also - // that the proper builder was looked up and used for the build. - build, err := template.Build("test1", components) - if err != nil { - t.Fatalf("err: %s", err) - } - - coreBuild, ok := build.(*coreBuild) - if !ok { - t.Fatal("should be okay") - } - if len(coreBuild.provisioners) != 1 { - t.Fatalf("bad: %#v", coreBuild.provisioners) - } - if len(coreBuild.provisioners[0].config) != 2 { - t.Fatalf("bad: %#v", coreBuild.provisioners[0].config) - } -} - -func TestTemplate_Build_ProvisionerOverrideBad(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov", - - "override": { - "testNope": {} - } - } - ] - } - ` - - _, err := ParseTemplate([]byte(data), nil) - if err == nil { - t.Fatal("should have error") - } -} - -func TestTemplateBuild_ProvisionerPauseBefore(t *testing.T) { - data := ` - { - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ], - - "provisioners": [ - { - "type": "test-prov", - "pause_before": "5s" - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - builder := new(MockBuilder) - builderMap := map[string]Builder{ - "test-builder": builder, - } - - provisioner := &MockProvisioner{} - provisionerMap := map[string]Provisioner{ - "test-prov": provisioner, - } - - builderFactory := func(n string) (Builder, error) { return builderMap[n], nil } - provFactory := func(n string) (Provisioner, error) { return provisionerMap[n], nil } - components := &ComponentFinder{ - Builder: builderFactory, - Provisioner: provFactory, - } - - // Get the build, verifying we can get it without issue, but also - // that the proper builder was looked up and used for the build. - build, err := template.Build("test1", components) - if err != nil { - t.Fatalf("err: %s", err) - } - - coreBuild, ok := build.(*coreBuild) - if !ok { - t.Fatal("should be okay") - } - if len(coreBuild.provisioners) != 1 { - t.Fatalf("bad: %#v", coreBuild.provisioners) - } - if pp, ok := coreBuild.provisioners[0].provisioner.(*PausedProvisioner); !ok { - t.Fatalf("should be paused provisioner") - } else { - if pp.PauseBefore != 5*time.Second { - t.Fatalf("bad: %#v", pp.PauseBefore) - } - } - - config := coreBuild.provisioners[0].config[0].(map[string]interface{}) - if _, ok := config["pause_before"]; ok { - t.Fatal("pause_before should be removed") - } -} - -func TestTemplateBuild_variables(t *testing.T) { - data := ` - { - "variables": { - "foo": "bar" - }, - - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - build, err := template.Build("test1", testComponentFinder()) - if err != nil { - t.Fatalf("err: %s", err) - } - - coreBuild, ok := build.(*coreBuild) - if !ok { - t.Fatalf("couldn't convert!") - } - - expected := map[string]string{"foo": "bar"} - if !reflect.DeepEqual(coreBuild.variables, expected) { - t.Fatalf("bad vars: %#v", coreBuild.variables) - } -} - -func TestTemplateBuild_variablesRequiredNotSet(t *testing.T) { - data := ` - { - "variables": { - "foo": null - }, - - "builders": [ - { - "name": "test1", - "type": "test-builder" - } - ] - } - ` - - template, err := ParseTemplate([]byte(data), map[string]string{}) - if err != nil { - t.Fatalf("err: %s", err) - } - - _, err = template.Build("test1", testComponentFinder()) - if err == nil { - t.Fatal("should error") - } -}