From 26c7ac2d9046459b2ea89ff9874536228f9cdead Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 26 May 2015 09:28:59 -0700 Subject: [PATCH] packer: post-processors --- packer/build_test.go | 30 ++++++++--------- packer/core.go | 53 ++++++++++++++++++++++++++---- packer/core_test.go | 38 ++++++++++++++++++++- packer/post_processor_mock.go | 33 +++++++++++++++++++ packer/post_processor_test.go | 24 -------------- packer/template_test.go | 4 +-- packer/test-fixtures/build-pp.json | 7 ++++ packer/testing.go | 35 +++++++++++++++----- 8 files changed, 167 insertions(+), 57 deletions(-) create mode 100644 packer/post_processor_mock.go delete mode 100644 packer/post_processor_test.go create mode 100644 packer/test-fixtures/build-pp.json diff --git a/packer/build_test.go b/packer/build_test.go index 5a073d39c..4f93e03a5 100644 --- a/packer/build_test.go +++ b/packer/build_test.go @@ -19,7 +19,7 @@ func testBuild() *coreBuild { }, postProcessors: [][]coreBuildPostProcessor{ []coreBuildPostProcessor{ - coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp"}, "testPP", make(map[string]interface{}), true}, + coreBuildPostProcessor{&MockPostProcessor{ArtifactId: "pp"}, "testPP", make(map[string]interface{}), true}, }, }, variables: make(map[string]string), @@ -66,12 +66,12 @@ func TestBuild_Prepare(t *testing.T) { } corePP := build.postProcessors[0][0] - pp := corePP.processor.(*TestPostProcessor) - if !pp.configCalled { + pp := corePP.processor.(*MockPostProcessor) + if !pp.ConfigureCalled { t.Fatal("should be called") } - if !reflect.DeepEqual(pp.configVal, []interface{}{make(map[string]interface{}), packerConfig}) { - t.Fatalf("bad: %#v", pp.configVal) + if !reflect.DeepEqual(pp.ConfigureConfigs, []interface{}{make(map[string]interface{}), packerConfig}) { + t.Fatalf("bad: %#v", pp.ConfigureConfigs) } } @@ -208,8 +208,8 @@ func TestBuild_Run(t *testing.T) { } // Verify post-processor was run - pp := build.postProcessors[0][0].processor.(*TestPostProcessor) - if !pp.ppCalled { + pp := build.postProcessors[0][0].processor.(*MockPostProcessor) + if !pp.PostProcessCalled { t.Fatal("should be called") } } @@ -244,7 +244,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { build = testBuild() build.postProcessors = [][]coreBuildPostProcessor{ []coreBuildPostProcessor{ - coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp"}, "pp", make(map[string]interface{}), false}, + coreBuildPostProcessor{&MockPostProcessor{ArtifactId: "pp"}, "pp", make(map[string]interface{}), false}, }, } @@ -269,10 +269,10 @@ func TestBuild_Run_Artifacts(t *testing.T) { build = testBuild() build.postProcessors = [][]coreBuildPostProcessor{ []coreBuildPostProcessor{ - coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp1"}, "pp", make(map[string]interface{}), false}, + coreBuildPostProcessor{&MockPostProcessor{ArtifactId: "pp1"}, "pp", make(map[string]interface{}), false}, }, []coreBuildPostProcessor{ - coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp2"}, "pp", make(map[string]interface{}), true}, + coreBuildPostProcessor{&MockPostProcessor{ArtifactId: "pp2"}, "pp", make(map[string]interface{}), true}, }, } @@ -297,12 +297,12 @@ func TestBuild_Run_Artifacts(t *testing.T) { build = testBuild() build.postProcessors = [][]coreBuildPostProcessor{ []coreBuildPostProcessor{ - coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp1a"}, "pp", make(map[string]interface{}), false}, - coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp1b"}, "pp", make(map[string]interface{}), true}, + coreBuildPostProcessor{&MockPostProcessor{ArtifactId: "pp1a"}, "pp", make(map[string]interface{}), false}, + coreBuildPostProcessor{&MockPostProcessor{ArtifactId: "pp1b"}, "pp", make(map[string]interface{}), true}, }, []coreBuildPostProcessor{ - coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp2a"}, "pp", make(map[string]interface{}), false}, - coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp2b"}, "pp", make(map[string]interface{}), false}, + coreBuildPostProcessor{&MockPostProcessor{ArtifactId: "pp2a"}, "pp", make(map[string]interface{}), false}, + coreBuildPostProcessor{&MockPostProcessor{ArtifactId: "pp2b"}, "pp", make(map[string]interface{}), false}, }, } @@ -328,7 +328,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { build.postProcessors = [][]coreBuildPostProcessor{ []coreBuildPostProcessor{ coreBuildPostProcessor{ - &TestPostProcessor{artifactId: "pp", keep: true}, "pp", make(map[string]interface{}), false, + &MockPostProcessor{ArtifactId: "pp", Keep: true}, "pp", make(map[string]interface{}), false, }, }, } diff --git a/packer/core.go b/packer/core.go index 4a0bf6a4d..a372c7ee8 100644 --- a/packer/core.go +++ b/packer/core.go @@ -161,13 +161,54 @@ func (c *Core) Build(n string) (Build, error) { }) } + // Setup the post-processors + postProcessors := make([][]coreBuildPostProcessor, 0, len(c.template.PostProcessors)) + for _, rawPs := range c.template.PostProcessors { + current := make([]coreBuildPostProcessor, 0, len(rawPs)) + for _, rawP := range rawPs { + // If we skip, ignore + if rawP.Skip(rawName) { + continue + } + + // Get the post-processor + postProcessor, err := c.components.PostProcessor(rawP.Type) + if err != nil { + return nil, fmt.Errorf( + "error initializing post-processor '%s': %s", + rawP.Type, err) + } + if postProcessor == nil { + return nil, fmt.Errorf( + "post-processor type not found: %s", rawP.Type) + } + + current = append(current, coreBuildPostProcessor{ + processor: postProcessor, + processorType: rawP.Type, + config: rawP.Config, + keepInputArtifact: rawP.KeepInputArtifact, + }) + } + + // If we have no post-processors in this chain, just continue. + if len(current) == 0 { + continue + } + + postProcessors = append(postProcessors, current) + } + + // TODO hooks one day + return &coreBuild{ - name: n, - builder: builder, - builderConfig: configBuilder.Config, - builderType: configBuilder.Type, - provisioners: provisioners, - variables: c.variables, + name: n, + builder: builder, + builderConfig: configBuilder.Config, + builderType: configBuilder.Type, + postProcessors: postProcessors, + provisioners: provisioners, + variables: c.variables, }, nil } diff --git a/packer/core_test.go b/packer/core_test.go index 5ef96dc96..712694766 100644 --- a/packer/core_test.go +++ b/packer/core_test.go @@ -222,6 +222,42 @@ func TestCoreBuild_provSkipInclude(t *testing.T) { } } +func TestCoreBuild_postProcess(t *testing.T) { + config := TestCoreConfig(t) + testCoreTemplate(t, config, fixtureDir("build-pp.json")) + b := TestBuilder(t, config, "test") + p := TestPostProcessor(t, config, "test") + core := TestCore(t, config) + ui := TestUi(t) + + b.ArtifactId = "hello" + p.ArtifactId = "goodbye" + + build, err := core.Build("test") + if err != nil { + t.Fatalf("err: %s", err) + } + + if _, err := build.Prepare(); err != nil { + t.Fatalf("err: %s", err) + } + + artifact, err := build.Run(ui, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + if len(artifact) != 1 { + t.Fatalf("bad: %#v", artifact) + } + + if artifact[0].Id() != p.ArtifactId { + t.Fatalf("bad: %s", artifact[0].Id()) + } + if p.PostProcessArtifact.Id() != b.ArtifactId { + t.Fatalf("bad: %s", p.PostProcessArtifact.Id()) + } +} + func TestCoreValidate(t *testing.T) { cases := []struct { File string @@ -276,7 +312,7 @@ func TestCoreValidate(t *testing.T) { func testComponentFinder() *ComponentFinder { builderFactory := func(n string) (Builder, error) { return new(MockBuilder), nil } - ppFactory := func(n string) (PostProcessor, error) { return new(TestPostProcessor), nil } + ppFactory := func(n string) (PostProcessor, error) { return new(MockPostProcessor), nil } provFactory := func(n string) (Provisioner, error) { return new(MockProvisioner), nil } return &ComponentFinder{ Builder: builderFactory, diff --git a/packer/post_processor_mock.go b/packer/post_processor_mock.go new file mode 100644 index 000000000..591e4b876 --- /dev/null +++ b/packer/post_processor_mock.go @@ -0,0 +1,33 @@ +package packer + +// MockPostProcessor is an implementation of PostProcessor that can be +// used for tests. +type MockPostProcessor struct { + ArtifactId string + Keep bool + Error error + + ConfigureCalled bool + ConfigureConfigs []interface{} + ConfigureError error + + PostProcessCalled bool + PostProcessArtifact Artifact + PostProcessUi Ui +} + +func (t *MockPostProcessor) Configure(configs ...interface{}) error { + t.ConfigureCalled = true + t.ConfigureConfigs = configs + return t.ConfigureError +} + +func (t *MockPostProcessor) PostProcess(ui Ui, a Artifact) (Artifact, bool, error) { + t.PostProcessCalled = true + t.PostProcessArtifact = a + t.PostProcessUi = ui + + return &MockArtifact{ + IdValue: t.ArtifactId, + }, t.Keep, t.Error +} diff --git a/packer/post_processor_test.go b/packer/post_processor_test.go deleted file mode 100644 index fa6dbdbf9..000000000 --- a/packer/post_processor_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package packer - -type TestPostProcessor struct { - artifactId string - keep bool - configCalled bool - configVal []interface{} - ppCalled bool - ppArtifact Artifact - ppUi Ui -} - -func (pp *TestPostProcessor) Configure(v ...interface{}) error { - pp.configCalled = true - pp.configVal = v - return nil -} - -func (pp *TestPostProcessor) PostProcess(ui Ui, a Artifact) (Artifact, bool, error) { - pp.ppCalled = true - pp.ppArtifact = a - pp.ppUi = ui - return &TestArtifact{id: pp.artifactId}, pp.keep, nil -} diff --git a/packer/template_test.go b/packer/template_test.go index b676672b1..2d0949376 100644 --- a/packer/template_test.go +++ b/packer/template_test.go @@ -11,7 +11,7 @@ import ( func testTemplateComponentFinder() *ComponentFinder { builder := new(MockBuilder) - pp := new(TestPostProcessor) + pp := new(MockPostProcessor) provisioner := &MockProvisioner{} builderMap := map[string]Builder{ @@ -1018,7 +1018,7 @@ func TestTemplate_Build(t *testing.T) { "test-prov": provisioner, } - pp := new(TestPostProcessor) + pp := new(MockPostProcessor) ppMap := map[string]PostProcessor{ "simple": pp, } diff --git a/packer/test-fixtures/build-pp.json b/packer/test-fixtures/build-pp.json new file mode 100644 index 000000000..e2b32cfac --- /dev/null +++ b/packer/test-fixtures/build-pp.json @@ -0,0 +1,7 @@ +{ + "builders": [{ + "type": "test" + }], + + "post-processors": ["test"] +} diff --git a/packer/testing.go b/packer/testing.go index 30b95c6e4..7e7ad0b53 100644 --- a/packer/testing.go +++ b/packer/testing.go @@ -8,14 +8,6 @@ import ( ) func TestCoreConfig(t *testing.T) *CoreConfig { - // Create a UI that is effectively /dev/null everywhere - var buf bytes.Buffer - ui := &BasicUi{ - Reader: &buf, - Writer: ioutil.Discard, - ErrorWriter: ioutil.Discard, - } - // Create some test components components := ComponentFinder{ Builder: func(n string) (Builder, error) { @@ -30,7 +22,7 @@ func TestCoreConfig(t *testing.T) *CoreConfig { return &CoreConfig{ Cache: &FileCache{CacheDir: os.TempDir()}, Components: components, - Ui: ui, + Ui: TestUi(t), } } @@ -43,6 +35,15 @@ func TestCore(t *testing.T, c *CoreConfig) *Core { return core } +func TestUi(t *testing.T) Ui { + var buf bytes.Buffer + return &BasicUi{ + Reader: &buf, + Writer: ioutil.Discard, + ErrorWriter: ioutil.Discard, + } +} + // TestBuilder sets the builder with the name n to the component finder // and returns the mock. func TestBuilder(t *testing.T, c *CoreConfig, n string) *MockBuilder { @@ -74,3 +75,19 @@ func TestProvisioner(t *testing.T, c *CoreConfig, n string) *MockProvisioner { return &b } + +// TestPostProcessor sets the prov. with the name n to the component finder +// and returns the mock. +func TestPostProcessor(t *testing.T, c *CoreConfig, n string) *MockPostProcessor { + var b MockPostProcessor + + c.Components.PostProcessor = func(actual string) (PostProcessor, error) { + if actual != n { + return nil, nil + } + + return &b, nil + } + + return &b +}