Commit 80e8e09e authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

packer: Add Cancel() method to hook

parent d5c6b9fa
...@@ -13,7 +13,7 @@ func testBuild() *coreBuild { ...@@ -13,7 +13,7 @@ func testBuild() *coreBuild {
builderConfig: 42, builderConfig: 42,
builderType: "foo", builderType: "foo",
hooks: map[string][]Hook{ hooks: map[string][]Hook{
"foo": []Hook{&TestHook{}}, "foo": []Hook{&MockHook{}},
}, },
provisioners: []coreBuildProvisioner{ provisioners: []coreBuildProvisioner{
coreBuildProvisioner{&TestProvisioner{}, []interface{}{42}}, coreBuildProvisioner{&TestProvisioner{}, []interface{}{42}},
...@@ -187,9 +187,9 @@ func TestBuild_Run(t *testing.T) { ...@@ -187,9 +187,9 @@ func TestBuild_Run(t *testing.T) {
dispatchHook := builder.runHook dispatchHook := builder.runHook
dispatchHook.Run("foo", nil, nil, 42) dispatchHook.Run("foo", nil, nil, 42)
hook := build.hooks["foo"][0].(*TestHook) hook := build.hooks["foo"][0].(*MockHook)
assert.True(hook.runCalled, "run should be called") assert.True(hook.RunCalled, "run should be called")
assert.Equal(hook.runData, 42, "should have correct data") assert.Equal(hook.RunData, 42, "should have correct data")
// Verify provisioners run // Verify provisioners run
dispatchHook.Run(HookProvision, nil, nil, 42) dispatchHook.Run(HookProvision, nil, nil, 42)
......
...@@ -227,7 +227,7 @@ func TestEnvironment_DefaultCli_Version(t *testing.T) { ...@@ -227,7 +227,7 @@ func TestEnvironment_DefaultCli_Version(t *testing.T) {
func TestEnvironment_Hook(t *testing.T) { func TestEnvironment_Hook(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true) assert := asserts.NewTestingAsserts(t, true)
hook := &TestHook{} hook := &MockHook{}
hooks := make(map[string]Hook) hooks := make(map[string]Hook)
hooks["foo"] = hook hooks["foo"] = hook
......
...@@ -11,8 +11,14 @@ const HookProvision = "packer_provision" ...@@ -11,8 +11,14 @@ const HookProvision = "packer_provision"
// you must reference the documentation for the specific hook you're interested // you must reference the documentation for the specific hook you're interested
// in. In addition to that, the Hook is given access to a UI so that it can // in. In addition to that, the Hook is given access to a UI so that it can
// output things to the user. // output things to the user.
//
// Cancel is called when the hook needs to be cancelled. This will usually
// be called when Run is still in progress so the mechanism that handles this
// must be race-free. Cancel should attempt to cancel the hook in the
// quickest, safest way possible.
type Hook interface { type Hook interface {
Run(string, Ui, Communicator, interface{}) error Run(string, Ui, Communicator, interface{}) error
Cancel()
} }
// A Hook implementation that dispatches based on an internal mapping. // A Hook implementation that dispatches based on an internal mapping.
...@@ -38,3 +44,5 @@ func (h *DispatchHook) Run(name string, ui Ui, comm Communicator, data interface ...@@ -38,3 +44,5 @@ func (h *DispatchHook) Run(name string, ui Ui, comm Communicator, data interface
return nil return nil
} }
func (h *DispatchHook) Cancel() {}
package packer
// MockHook is an implementation of Hook that can be used for tests.
type MockHook struct {
RunCalled bool
RunComm Communicator
RunData interface{}
RunName string
RunUi Ui
CancelCalled bool
}
func (t *MockHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
t.RunCalled = true
t.RunComm = comm
t.RunData = data
t.RunName = name
t.RunUi = ui
return nil
}
func (t *MockHook) Cancel() {
t.CancelCalled = true
}
...@@ -5,23 +5,6 @@ import ( ...@@ -5,23 +5,6 @@ import (
"testing" "testing"
) )
type TestHook struct {
runCalled bool
runComm Communicator
runData interface{}
runName string
runUi Ui
}
func (t *TestHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
t.runCalled = true
t.runComm = comm
t.runData = data
t.runName = name
t.runUi = ui
return nil
}
func TestDispatchHook_Implements(t *testing.T) { func TestDispatchHook_Implements(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true) assert := asserts.NewTestingAsserts(t, true)
...@@ -40,14 +23,14 @@ func TestDispatchHook_Run_NoHooks(t *testing.T) { ...@@ -40,14 +23,14 @@ func TestDispatchHook_Run_NoHooks(t *testing.T) {
func TestDispatchHook_Run(t *testing.T) { func TestDispatchHook_Run(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true) assert := asserts.NewTestingAsserts(t, true)
hook := &TestHook{} hook := &MockHook{}
mapping := make(map[string][]Hook) mapping := make(map[string][]Hook)
mapping["foo"] = []Hook{hook} mapping["foo"] = []Hook{hook}
dh := &DispatchHook{mapping} dh := &DispatchHook{mapping}
dh.Run("foo", nil, nil, 42) dh.Run("foo", nil, nil, 42)
assert.True(hook.runCalled, "run should be called") assert.True(hook.RunCalled, "run should be called")
assert.Equal(hook.runName, "foo", "should be proper event") assert.Equal(hook.RunName, "foo", "should be proper event")
assert.Equal(hook.runData, 42, "should be correct data") assert.Equal(hook.RunData, 42, "should be correct data")
} }
...@@ -19,8 +19,17 @@ func (c *cmdHook) Run(name string, ui packer.Ui, comm packer.Communicator, data ...@@ -19,8 +19,17 @@ func (c *cmdHook) Run(name string, ui packer.Ui, comm packer.Communicator, data
return c.hook.Run(name, ui, comm, data) return c.hook.Run(name, ui, comm, data)
} }
func (c *cmdHook) Cancel() {
defer func() {
r := recover()
c.checkExit(r, nil)
}()
c.hook.Cancel()
}
func (c *cmdHook) checkExit(p interface{}, cb func()) { func (c *cmdHook) checkExit(p interface{}, cb func()) {
if c.client.Exited() { if c.client.Exited() && cb != nil {
cb() cb()
} else if p != nil && !Killed { } else if p != nil && !Killed {
log.Panic(p) log.Panic(p)
......
package plugin package plugin
import ( import (
"github.com/mitchellh/packer/packer"
"os/exec" "os/exec"
"testing" "testing"
) )
type helperHook byte
func (helperHook) Run(string, packer.Ui, packer.Communicator, interface{}) error {
return nil
}
func TestHook_NoExist(t *testing.T) { func TestHook_NoExist(t *testing.T) {
c := NewClient(&ClientConfig{Cmd: exec.Command("i-should-not-exist")}) c := NewClient(&ClientConfig{Cmd: exec.Command("i-should-not-exist")})
defer c.Kill() defer c.Kill()
......
...@@ -2,6 +2,7 @@ package plugin ...@@ -2,6 +2,7 @@ package plugin
import ( import (
"fmt" "fmt"
"github.com/mitchellh/packer/packer"
"log" "log"
"os" "os"
"os/exec" "os/exec"
...@@ -54,7 +55,7 @@ func TestHelperProcess(*testing.T) { ...@@ -54,7 +55,7 @@ func TestHelperProcess(*testing.T) {
case "command": case "command":
ServeCommand(new(helperCommand)) ServeCommand(new(helperCommand))
case "hook": case "hook":
ServeHook(new(helperHook)) ServeHook(new(packer.MockHook))
case "invalid-rpc-address": case "invalid-rpc-address":
fmt.Println("lolinvalid") fmt.Println("lolinvalid")
case "mock": case "mock":
......
...@@ -32,3 +32,8 @@ func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interfac ...@@ -32,3 +32,8 @@ func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interfac
return nil return nil
} }
// Cancels the privisioners that are still running.
func (h *ProvisionHook) Cancel() {
// TODO(mitchellh): implement
}
...@@ -72,7 +72,7 @@ func TestBuilderRPC(t *testing.T) { ...@@ -72,7 +72,7 @@ func TestBuilderRPC(t *testing.T) {
// Test Run // Test Run
cache := new(testCache) cache := new(testCache)
hook := &testHook{} hook := &packer.MockHook{}
ui := &testUi{} ui := &testUi{}
artifact, err := bClient.Run(ui, hook, cache) artifact, err := bClient.Run(ui, hook, cache)
assert.Nil(err, "should have no error") assert.Nil(err, "should have no error")
...@@ -83,7 +83,7 @@ func TestBuilderRPC(t *testing.T) { ...@@ -83,7 +83,7 @@ func TestBuilderRPC(t *testing.T) {
assert.True(cache.lockCalled, "lock should be called") assert.True(cache.lockCalled, "lock should be called")
b.runHook.Run("foo", nil, nil, nil) b.runHook.Run("foo", nil, nil, nil)
assert.True(hook.runCalled, "run should be called") assert.True(hook.RunCalled, "run should be called")
b.runUi.Say("format") b.runUi.Say("format")
assert.True(ui.sayCalled, "say should be called") assert.True(ui.sayCalled, "say should be called")
......
...@@ -37,6 +37,10 @@ func (h *hook) Run(name string, ui packer.Ui, comm packer.Communicator, data int ...@@ -37,6 +37,10 @@ func (h *hook) Run(name string, ui packer.Ui, comm packer.Communicator, data int
return h.client.Call("Hook.Run", args, new(interface{})) return h.client.Call("Hook.Run", args, new(interface{}))
} }
func (h *hook) Cancel() {
// TODO(mitchellh): implement
}
func (h *HookServer) Run(args *HookRunArgs, reply *interface{}) error { func (h *HookServer) Run(args *HookRunArgs, reply *interface{}) error {
client, err := rpc.Dial("tcp", args.RPCAddress) client, err := rpc.Dial("tcp", args.RPCAddress)
if err != nil { if err != nil {
......
...@@ -7,21 +7,11 @@ import ( ...@@ -7,21 +7,11 @@ import (
"testing" "testing"
) )
type testHook struct {
runCalled bool
runUi packer.Ui
}
func (h *testHook) Run(name string, ui packer.Ui, comm packer.Communicator, data interface{}) error {
h.runCalled = true
return nil
}
func TestHookRPC(t *testing.T) { func TestHookRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true) assert := asserts.NewTestingAsserts(t, true)
// Create the UI to test // Create the UI to test
h := new(testHook) h := new(packer.MockHook)
// Serve // Serve
server := rpc.NewServer() server := rpc.NewServer()
...@@ -37,7 +27,7 @@ func TestHookRPC(t *testing.T) { ...@@ -37,7 +27,7 @@ func TestHookRPC(t *testing.T) {
// Test Run // Test Run
ui := &testUi{} ui := &testUi{}
hClient.Run("foo", ui, nil, 42) hClient.Run("foo", ui, nil, 42)
assert.True(h.runCalled, "run should be called") assert.True(h.RunCalled, "run should be called")
} }
func TestHook_Implements(t *testing.T) { func TestHook_Implements(t *testing.T) {
......
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