packer: Add Cancel() method to hook

This commit is contained in:
Mitchell Hashimoto 2013-08-30 17:03:55 -07:00
parent d5c6b9fad3
commit 80e8e09ec7
12 changed files with 66 additions and 49 deletions

View File

@ -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) {
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)

View File

@ -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

View File

@ -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
return nil return nil
} }
func (h *DispatchHook) Cancel() {}

24
packer/hook_mock.go Normal file
View File

@ -0,0 +1,24 @@
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
}

View File

@ -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) {
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")
} }

View File

@ -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)

View File

@ -1,17 +1,10 @@
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()

View File

@ -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) {
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":

View File

@ -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
}

View File

@ -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) {
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")

View File

@ -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 {

View File

@ -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) {
// 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) {