diff --git a/command/build/command.go b/command/build/command.go index 4c72dd426..871f9b207 100644 --- a/command/build/command.go +++ b/command/build/command.go @@ -163,7 +163,7 @@ func (c Command) Run(env packer.Environment, args []string) int { // Run all the builds in parallel and wait for them to complete var interruptWg, wg sync.WaitGroup interrupted := false - artifacts := make(map[string]packer.Artifact) + artifacts := make(map[string][]packer.Artifact) errors := make(map[string]error) for _, b := range builds { // Increment the waitgroup so we wait for this item to finish properly @@ -191,14 +191,14 @@ func (c Command) Run(env packer.Environment, args []string) int { name := b.Name() log.Printf("Starting build run: %s", name) ui := buildUis[name] - artifact, err := b.Run(ui, env.Cache()) + runArtifacts, err := b.Run(ui, env.Cache()) if err != nil { ui.Error(fmt.Sprintf("Build errored: %s", err)) errors[name] = err } else { ui.Say("Build finished.") - artifacts[name] = artifact + artifacts[name] = runArtifacts } }(b) @@ -235,17 +235,19 @@ func (c Command) Run(env packer.Environment, args []string) int { if len(artifacts) > 0 { env.Ui().Say("\n==> Builds finished. The artifacts of successful builds are:") - for name, artifact := range artifacts { - var message bytes.Buffer - fmt.Fprintf(&message, "--> %s: ", name) + for name, buildArtifacts := range artifacts { + for _, artifact := range buildArtifacts { + var message bytes.Buffer + fmt.Fprintf(&message, "--> %s: ", name) - if artifact != nil { - fmt.Fprintf(&message, artifact.String()) - } else { - fmt.Print("") + if artifact != nil { + fmt.Fprintf(&message, artifact.String()) + } else { + fmt.Print("") + } + + env.Ui().Say(message.String()) } - - env.Ui().Say(message.String()) } } diff --git a/packer/build.go b/packer/build.go index 66987759d..a257ec65a 100644 --- a/packer/build.go +++ b/packer/build.go @@ -23,7 +23,7 @@ type Build interface { // Run runs the actual builder, returning an artifact implementation // of what is built. If anything goes wrong, an error is returned. - Run(Ui, Cache) (Artifact, error) + Run(Ui, Cache) ([]Artifact, error) // Cancel will cancel a running build. This will block until the build // is actually completely cancelled. @@ -113,7 +113,7 @@ func (b *coreBuild) Prepare() (err error) { } // Runs the actual build. Prepare must be called prior to running this. -func (b *coreBuild) Run(ui Ui, cache Cache) (Artifact, error) { +func (b *coreBuild) Run(ui Ui, cache Cache) ([]Artifact, error) { if !b.prepareCalled { panic("Prepare must be called first") } @@ -140,7 +140,14 @@ func (b *coreBuild) Run(ui Ui, cache Cache) (Artifact, error) { } hook := &DispatchHook{hooks} - return b.builder.Run(ui, hook, cache) + artifacts := make([]Artifact, 0, 1) + + artifact, err := b.builder.Run(ui, hook, cache) + if artifact != nil { + artifacts = append(artifacts, artifact) + } + + return artifacts, err } func (b *coreBuild) SetDebug(val bool) { diff --git a/packer/rpc/build.go b/packer/rpc/build.go index bd33a0482..00c9fc36d 100644 --- a/packer/rpc/build.go +++ b/packer/rpc/build.go @@ -38,24 +38,29 @@ func (b *build) Prepare() (err error) { return } -func (b *build) Run(ui packer.Ui, cache packer.Cache) (packer.Artifact, error) { +func (b *build) Run(ui packer.Ui, cache packer.Cache) ([]packer.Artifact, error) { // Create and start the server for the UI server := rpc.NewServer() RegisterCache(server, cache) RegisterUi(server, ui) args := &BuildRunArgs{serveSingleConn(server)} - var reply string - if err := b.client.Call("Build.Run", args, &reply); err != nil { + var result []string + if err := b.client.Call("Build.Run", args, &result); err != nil { return nil, err } - client, err := rpc.Dial("tcp", reply) - if err != nil { - return nil, err + artifacts := make([]packer.Artifact, len(result)) + for i, addr := range result { + client, err := rpc.Dial("tcp", addr) + if err != nil { + return nil, err + } + + artifacts[i] = Artifact(client) } - return Artifact(client), nil + return artifacts, nil } func (b *build) SetDebug(val bool) { @@ -80,22 +85,24 @@ func (b *BuildServer) Prepare(args interface{}, reply *error) error { return nil } -func (b *BuildServer) Run(args *BuildRunArgs, reply *string) error { +func (b *BuildServer) Run(args *BuildRunArgs, reply *[]string) error { client, err := rpc.Dial("tcp", args.UiRPCAddress) if err != nil { return err } - artifact, err := b.build.Run(&Ui{client}, Cache(client)) + artifacts, err := b.build.Run(&Ui{client}, Cache(client)) if err != nil { return NewBasicError(err) } - // Wrap the artifact - server := rpc.NewServer() - RegisterArtifact(server, artifact) + *reply = make([]string, len(artifacts)) + for i, artifact := range artifacts { + server := rpc.NewServer() + RegisterArtifact(server, artifact) + (*reply)[i] = serveSingleConn(server) + } - *reply = serveSingleConn(server) return nil } diff --git a/packer/rpc/build_test.go b/packer/rpc/build_test.go index 253f6c082..0d2bb74df 100644 --- a/packer/rpc/build_test.go +++ b/packer/rpc/build_test.go @@ -32,7 +32,7 @@ func (b *testBuild) Prepare() error { return nil } -func (b *testBuild) Run(ui packer.Ui, cache packer.Cache) (packer.Artifact, error) { +func (b *testBuild) Run(ui packer.Ui, cache packer.Cache) ([]packer.Artifact, error) { b.runCalled = true b.runCache = cache b.runUi = ui @@ -40,7 +40,7 @@ func (b *testBuild) Run(ui packer.Ui, cache packer.Cache) (packer.Artifact, erro if b.errRunResult { return nil, errors.New("foo") } else { - return testBuildArtifact, nil + return []packer.Artifact{testBuildArtifact}, nil } } @@ -79,9 +79,11 @@ func TestBuildRPC(t *testing.T) { // Test Run cache := new(testCache) ui := new(testUi) - _, err = bClient.Run(ui, cache) + artifacts, err := bClient.Run(ui, cache) assert.True(b.runCalled, "run should be called") assert.Nil(err, "should not error") + assert.Equal(len(artifacts), 1, "should have one artifact") + assert.Equal(artifacts[0].BuilderId(), "bid", "should have proper builder id") // Test the UI given to run, which should be fully functional if b.runCalled {