package packer import ( "io/ioutil" "os" "reflect" "sort" "testing" "time" ) func testTemplateComponentFinder() *ComponentFinder { builder := new(MockBuilder) pp := new(TestPostProcessor) 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) } } 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.Fatal("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.Fatal("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(TestPostProcessor) 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") } }