Commit 21869295 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer: Build can return multiple artifacts

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