packer: implement Cancel in ProvisionHook
This commit is contained in:
parent
66f7f5aad5
commit
6e098d1aaf
|
@ -42,6 +42,7 @@ func (*StepProvision) Run(state map[string]interface{}) multistep.StepAction {
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
case <-time.After(1 * time.Second):
|
case <-time.After(1 * time.Second):
|
||||||
if _, ok := state[multistep.StateCancelled]; ok {
|
if _, ok := state[multistep.StateCancelled]; ok {
|
||||||
|
log.Println("Cancelling provisioning due to interrupt...")
|
||||||
hook.Cancel()
|
hook.Cancel()
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,7 +207,9 @@ func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) {
|
||||||
hooks[HookProvision] = make([]Hook, 0, 1)
|
hooks[HookProvision] = make([]Hook, 0, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
hooks[HookProvision] = append(hooks[HookProvision], &ProvisionHook{provisioners})
|
hooks[HookProvision] = append(hooks[HookProvision], &ProvisionHook{
|
||||||
|
Provisioners: provisioners,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
hook := &DispatchHook{Mapping: hooks}
|
hook := &DispatchHook{Mapping: hooks}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package packer
|
package packer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
// A provisioner is responsible for installing and configuring software
|
// A provisioner is responsible for installing and configuring software
|
||||||
// on a machine prior to building the actual image.
|
// on a machine prior to building the actual image.
|
||||||
type Provisioner interface {
|
type Provisioner interface {
|
||||||
|
@ -25,11 +29,25 @@ type ProvisionHook struct {
|
||||||
// The provisioners to run as part of the hook. These should already
|
// The provisioners to run as part of the hook. These should already
|
||||||
// be prepared (by calling Prepare) at some earlier stage.
|
// be prepared (by calling Prepare) at some earlier stage.
|
||||||
Provisioners []Provisioner
|
Provisioners []Provisioner
|
||||||
|
|
||||||
|
lock sync.Mutex
|
||||||
|
runningProvisioner Provisioner
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs the provisioners in order.
|
// Runs the provisioners in order.
|
||||||
func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
|
func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
|
||||||
|
defer func() {
|
||||||
|
h.lock.Lock()
|
||||||
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
|
h.runningProvisioner = nil
|
||||||
|
}()
|
||||||
|
|
||||||
for _, p := range h.Provisioners {
|
for _, p := range h.Provisioners {
|
||||||
|
h.lock.Lock()
|
||||||
|
h.runningProvisioner = p
|
||||||
|
h.lock.Unlock()
|
||||||
|
|
||||||
if err := p.Provision(ui, comm); err != nil {
|
if err := p.Provision(ui, comm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -40,5 +58,10 @@ func (h *ProvisionHook) Run(name string, ui Ui, comm Communicator, data interfac
|
||||||
|
|
||||||
// Cancels the privisioners that are still running.
|
// Cancels the privisioners that are still running.
|
||||||
func (h *ProvisionHook) Cancel() {
|
func (h *ProvisionHook) Cancel() {
|
||||||
// TODO(mitchellh): implement
|
h.lock.Lock()
|
||||||
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
|
if h.runningProvisioner != nil {
|
||||||
|
h.runningProvisioner.Cancel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package packer
|
||||||
// MockProvisioner is an implementation of Provisioner that can be
|
// MockProvisioner is an implementation of Provisioner that can be
|
||||||
// used for tests.
|
// used for tests.
|
||||||
type MockProvisioner struct {
|
type MockProvisioner struct {
|
||||||
|
ProvFunc func() error
|
||||||
|
|
||||||
PrepCalled bool
|
PrepCalled bool
|
||||||
PrepConfigs []interface{}
|
PrepConfigs []interface{}
|
||||||
ProvCalled bool
|
ProvCalled bool
|
||||||
|
@ -19,7 +21,12 @@ func (t *MockProvisioner) Prepare(configs ...interface{}) error {
|
||||||
func (t *MockProvisioner) Provision(ui Ui, comm Communicator) error {
|
func (t *MockProvisioner) Provision(ui Ui, comm Communicator) error {
|
||||||
t.ProvCalled = true
|
t.ProvCalled = true
|
||||||
t.ProvUi = ui
|
t.ProvUi = ui
|
||||||
return nil
|
|
||||||
|
if t.ProvFunc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.ProvFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MockProvisioner) Cancel() {
|
func (t *MockProvisioner) Cancel() {
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package packer
|
package packer
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func TestProvisionHook_Impl(t *testing.T) {
|
func TestProvisionHook_Impl(t *testing.T) {
|
||||||
var raw interface{}
|
var raw interface{}
|
||||||
|
@ -18,7 +22,10 @@ func TestProvisionHook(t *testing.T) {
|
||||||
var comm Communicator = nil
|
var comm Communicator = nil
|
||||||
var data interface{} = nil
|
var data interface{} = nil
|
||||||
|
|
||||||
hook := &ProvisionHook{[]Provisioner{pA, pB}}
|
hook := &ProvisionHook{
|
||||||
|
Provisioners: []Provisioner{pA, pB},
|
||||||
|
}
|
||||||
|
|
||||||
hook.Run("foo", ui, comm, data)
|
hook.Run("foo", ui, comm, data)
|
||||||
|
|
||||||
if !pA.ProvCalled {
|
if !pA.ProvCalled {
|
||||||
|
@ -30,4 +37,46 @@ func TestProvisionHook(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProvisionHook_cancel(t *testing.T) {
|
||||||
|
var lock sync.Mutex
|
||||||
|
order := make([]string, 0, 2)
|
||||||
|
|
||||||
|
p := &MockProvisioner{
|
||||||
|
ProvFunc: func() error {
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
order = append(order, "prov")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
hook := &ProvisionHook{
|
||||||
|
Provisioners: []Provisioner{p},
|
||||||
|
}
|
||||||
|
|
||||||
|
finished := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
hook.Run("foo", nil, nil, nil)
|
||||||
|
close(finished)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Cancel it while it is running
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
hook.Cancel()
|
||||||
|
lock.Lock()
|
||||||
|
order = append(order, "cancel")
|
||||||
|
lock.Unlock()
|
||||||
|
|
||||||
|
// Wait
|
||||||
|
<-finished
|
||||||
|
|
||||||
|
// Verify order
|
||||||
|
if order[0] != "cancel" || order[1] != "prov" {
|
||||||
|
t.Fatalf("bad: %#v", order)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(mitchellh): Test that they're run in the proper order
|
// TODO(mitchellh): Test that they're run in the proper order
|
||||||
|
|
Loading…
Reference in New Issue