packer: Add Cancel method to Provisioner

This commit is contained in:
Mitchell Hashimoto 2013-08-30 23:21:15 -07:00
parent 6312f680da
commit 47cd6df85b
11 changed files with 84 additions and 73 deletions

View File

@ -16,7 +16,7 @@ func testBuild() *coreBuild {
"foo": []Hook{&MockHook{}},
},
provisioners: []coreBuildProvisioner{
coreBuildProvisioner{&TestProvisioner{}, []interface{}{42}},
coreBuildProvisioner{&MockProvisioner{}, []interface{}{42}},
},
postProcessors: [][]coreBuildPostProcessor{
[]coreBuildPostProcessor{
@ -59,9 +59,9 @@ func TestBuild_Prepare(t *testing.T) {
assert.Equal(builder.prepareConfig, []interface{}{42, packerConfig}, "prepare config should be 42")
coreProv := build.provisioners[0]
prov := coreProv.provisioner.(*TestProvisioner)
assert.True(prov.prepCalled, "prepare should be called")
assert.Equal(prov.prepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
prov := coreProv.provisioner.(*MockProvisioner)
assert.True(prov.PrepCalled, "prepare should be called")
assert.Equal(prov.PrepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
corePP := build.postProcessors[0][0]
pp := corePP.processor.(*TestPostProcessor)
@ -104,9 +104,9 @@ func TestBuild_Prepare_Debug(t *testing.T) {
assert.Equal(builder.prepareConfig, []interface{}{42, packerConfig}, "prepare config should be 42")
coreProv := build.provisioners[0]
prov := coreProv.provisioner.(*TestProvisioner)
assert.True(prov.prepCalled, "prepare should be called")
assert.Equal(prov.prepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
prov := coreProv.provisioner.(*MockProvisioner)
assert.True(prov.PrepCalled, "prepare should be called")
assert.Equal(prov.PrepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
}
func TestBuildPrepare_variables_default(t *testing.T) {
@ -193,8 +193,8 @@ func TestBuild_Run(t *testing.T) {
// Verify provisioners run
dispatchHook.Run(HookProvision, nil, nil, 42)
prov := build.provisioners[0].provisioner.(*TestProvisioner)
assert.True(prov.provCalled, "provision should be called")
prov := build.provisioners[0].provisioner.(*MockProvisioner)
assert.True(prov.ProvCalled, "provision should be called")
// Verify post-processor was run
pp := build.postProcessors[0][0].processor.(*TestPostProcessor)

View File

@ -20,7 +20,7 @@ func init() {
func testComponentFinder() *ComponentFinder {
builderFactory := func(n string) (Builder, error) { return testBuilder(), nil }
ppFactory := func(n string) (PostProcessor, error) { return new(TestPostProcessor), nil }
provFactory := func(n string) (Provisioner, error) { return new(TestProvisioner), nil }
provFactory := func(n string) (Provisioner, error) { return new(MockProvisioner), nil }
return &ComponentFinder{
Builder: builderFactory,
PostProcessor: ppFactory,
@ -309,7 +309,7 @@ func TestEnvironment_PostProcessor_Error(t *testing.T) {
func TestEnvironmentProvisioner(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
p := &TestProvisioner{}
p := &MockProvisioner{}
ps := make(map[string]Provisioner)
ps["foo"] = p

View File

@ -64,7 +64,7 @@ func TestHelperProcess(*testing.T) {
case "post-processor":
ServePostProcessor(new(helperPostProcessor))
case "provisioner":
ServeProvisioner(new(helperProvisioner))
ServeProvisioner(new(packer.MockProvisioner))
case "start-timeout":
time.Sleep(1 * time.Minute)
os.Exit(1)

View File

@ -28,6 +28,15 @@ func (c *cmdProvisioner) Provision(ui packer.Ui, comm packer.Communicator) error
return c.p.Provision(ui, comm)
}
func (c *cmdProvisioner) Cancel() {
defer func() {
r := recover()
c.checkExit(r, nil)
}()
c.p.Cancel()
}
func (c *cmdProvisioner) checkExit(p interface{}, cb func()) {
if c.client.Exited() && cb != nil {
cb()

View File

@ -1,21 +1,10 @@
package plugin
import (
"github.com/mitchellh/packer/packer"
"os/exec"
"testing"
)
type helperProvisioner byte
func (helperProvisioner) Prepare(...interface{}) error {
return nil
}
func (helperProvisioner) Provision(packer.Ui, packer.Communicator) error {
return nil
}
func TestProvisioner_NoExist(t *testing.T) {
c := NewClient(&ClientConfig{Cmd: exec.Command("i-should-not-exist")})
defer c.Kill()

View File

@ -13,6 +13,11 @@ type Provisioner interface {
// is guaranteed to be connected to some machine so that provisioning
// can be done.
Provision(Ui, Communicator) error
// Cancel is called to cancel the provisioning. This is usually called
// while Provision is still being called. The Provisioner should act
// to stop its execution as quickly as possible in a race-free way.
Cancel()
}
// A Hook implementation that runs the given provisioners.

View File

@ -0,0 +1,27 @@
package packer
// MockProvisioner is an implementation of Provisioner that can be
// used for tests.
type MockProvisioner struct {
PrepCalled bool
PrepConfigs []interface{}
ProvCalled bool
ProvUi Ui
CancelCalled bool
}
func (t *MockProvisioner) Prepare(configs ...interface{}) error {
t.PrepCalled = true
t.PrepConfigs = configs
return nil
}
func (t *MockProvisioner) Provision(ui Ui, comm Communicator) error {
t.ProvCalled = true
t.ProvUi = ui
return nil
}
func (t *MockProvisioner) Cancel() {
t.CancelCalled = true
}

View File

@ -2,23 +2,6 @@ package packer
import "testing"
type TestProvisioner struct {
prepCalled bool
prepConfigs []interface{}
provCalled bool
}
func (t *TestProvisioner) Prepare(configs ...interface{}) error {
t.prepCalled = true
t.prepConfigs = configs
return nil
}
func (t *TestProvisioner) Provision(Ui, Communicator) error {
t.provCalled = true
return nil
}
func TestProvisionHook_Impl(t *testing.T) {
var raw interface{}
raw = &ProvisionHook{}
@ -28,8 +11,8 @@ func TestProvisionHook_Impl(t *testing.T) {
}
func TestProvisionHook(t *testing.T) {
pA := &TestProvisioner{}
pB := &TestProvisioner{}
pA := &MockProvisioner{}
pB := &MockProvisioner{}
ui := testUi()
var comm Communicator = nil
@ -38,11 +21,11 @@ func TestProvisionHook(t *testing.T) {
hook := &ProvisionHook{[]Provisioner{pA, pB}}
hook.Run("foo", ui, comm, data)
if !pA.provCalled {
if !pA.ProvCalled {
t.Error("provision should be called on pA")
}
if !pB.provCalled {
if !pB.ProvCalled {
t.Error("provision should be called on pB")
}
}

View File

@ -2,6 +2,7 @@ package rpc
import (
"github.com/mitchellh/packer/packer"
"log"
"net/rpc"
)
@ -47,6 +48,13 @@ func (p *provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
return p.client.Call("Provisioner.Provision", args, new(interface{}))
}
func (p *provisioner) Cancel() {
err := p.client.Call("Provisioner.Cancel", new(interface{}), new(interface{}))
if err != nil {
log.Printf("Provisioner.Cancel err: %s", err)
}
}
func (p *ProvisionerServer) Prepare(args *ProvisionerPrepareArgs, reply *error) error {
*reply = p.p.Prepare(args.Configs...)
if *reply != nil {
@ -71,3 +79,8 @@ func (p *ProvisionerServer) Provision(args *ProvisionerProvisionArgs, reply *int
return nil
}
func (p *ProvisionerServer) Cancel(args *interface{}, reply *interface{}) error {
p.p.Cancel()
return nil
}

View File

@ -7,32 +7,11 @@ import (
"testing"
)
type testProvisioner struct {
prepareCalled bool
prepareConfigs []interface{}
provCalled bool
provComm packer.Communicator
provUi packer.Ui
}
func (p *testProvisioner) Prepare(configs ...interface{}) error {
p.prepareCalled = true
p.prepareConfigs = configs
return nil
}
func (p *testProvisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
p.provCalled = true
p.provComm = comm
p.provUi = ui
return nil
}
func TestProvisionerRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the interface to test
p := new(testProvisioner)
p := new(packer.MockProvisioner)
// Start the server
server := rpc.NewServer()
@ -47,17 +26,23 @@ func TestProvisionerRPC(t *testing.T) {
config := 42
pClient := Provisioner(client)
pClient.Prepare(config)
assert.True(p.prepareCalled, "prepare should be called")
assert.Equal(p.prepareConfigs, []interface{}{42}, "prepare should be called with right arg")
assert.True(p.PrepCalled, "prepare should be called")
assert.Equal(p.PrepConfigs, []interface{}{42}, "prepare should be called with right arg")
// Test Provision
ui := &testUi{}
comm := new(packer.MockCommunicator)
comm := &packer.MockCommunicator{}
pClient.Provision(ui, comm)
assert.True(p.provCalled, "provision should be called")
assert.True(p.ProvCalled, "provision should be called")
p.provUi.Say("foo")
p.ProvUi.Say("foo")
assert.True(ui.sayCalled, "say should be called")
// Test Cancel
pClient.Cancel()
if !p.CancelCalled {
t.Fatal("cancel should be called")
}
}
func TestProvisioner_Implements(t *testing.T) {

View File

@ -589,7 +589,7 @@ func TestTemplate_Build(t *testing.T) {
"test-builder": builder,
}
provisioner := &TestProvisioner{}
provisioner := &MockProvisioner{}
provisionerMap := map[string]Provisioner{
"test-prov": provisioner,
}
@ -677,7 +677,7 @@ func TestTemplate_Build_ProvisionerOverride(t *testing.T) {
"test-builder": builder,
}
provisioner := &TestProvisioner{}
provisioner := &MockProvisioner{}
provisionerMap := map[string]Provisioner{
"test-prov": provisioner,
}