Merge branch 'cancel-next'
This implements more robust interrupt handling by propagating cancels throughout more core Packer components.
This commit is contained in:
commit
3238b65bf9
|
@ -4,6 +4,7 @@ import (
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StepProvision runs the provisioners.
|
// StepProvision runs the provisioners.
|
||||||
|
@ -22,13 +23,31 @@ func (*StepProvision) Run(state map[string]interface{}) multistep.StepAction {
|
||||||
hook := state["hook"].(packer.Hook)
|
hook := state["hook"].(packer.Hook)
|
||||||
ui := state["ui"].(packer.Ui)
|
ui := state["ui"].(packer.Ui)
|
||||||
|
|
||||||
|
// Run the provisioner in a goroutine so we can continually check
|
||||||
|
// for cancellations...
|
||||||
log.Println("Running the provision hook")
|
log.Println("Running the provision hook")
|
||||||
if err := hook.Run(packer.HookProvision, ui, comm, nil); err != nil {
|
errCh := make(chan error, 1)
|
||||||
state["error"] = err
|
go func() {
|
||||||
return multistep.ActionHalt
|
errCh <- hook.Run(packer.HookProvision, ui, comm, nil)
|
||||||
}
|
}()
|
||||||
|
|
||||||
return multistep.ActionContinue
|
for {
|
||||||
|
select {
|
||||||
|
case err := <-errCh:
|
||||||
|
if err != nil {
|
||||||
|
state["error"] = err
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
if _, ok := state[multistep.StateCancelled]; ok {
|
||||||
|
log.Println("Cancelling provisioning due to interrupt...")
|
||||||
|
hook.Cancel()
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*StepProvision) Cleanup(map[string]interface{}) {}
|
func (*StepProvision) Cleanup(map[string]interface{}) {}
|
||||||
|
|
|
@ -207,10 +207,12 @@ 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{hooks}
|
hook := &DispatchHook{Mapping: hooks}
|
||||||
artifacts := make([]Artifact, 0, 1)
|
artifacts := make([]Artifact, 0, 1)
|
||||||
|
|
||||||
// The builder just has a normal Ui, but targetted
|
// The builder just has a normal Ui, but targetted
|
||||||
|
|
|
@ -13,10 +13,10 @@ 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{&MockProvisioner{}, []interface{}{42}},
|
||||||
},
|
},
|
||||||
postProcessors: [][]coreBuildPostProcessor{
|
postProcessors: [][]coreBuildPostProcessor{
|
||||||
[]coreBuildPostProcessor{
|
[]coreBuildPostProcessor{
|
||||||
|
@ -59,9 +59,9 @@ func TestBuild_Prepare(t *testing.T) {
|
||||||
assert.Equal(builder.prepareConfig, []interface{}{42, packerConfig}, "prepare config should be 42")
|
assert.Equal(builder.prepareConfig, []interface{}{42, packerConfig}, "prepare config should be 42")
|
||||||
|
|
||||||
coreProv := build.provisioners[0]
|
coreProv := build.provisioners[0]
|
||||||
prov := coreProv.provisioner.(*TestProvisioner)
|
prov := coreProv.provisioner.(*MockProvisioner)
|
||||||
assert.True(prov.prepCalled, "prepare should be called")
|
assert.True(prov.PrepCalled, "prepare should be called")
|
||||||
assert.Equal(prov.prepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
|
assert.Equal(prov.PrepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
|
||||||
|
|
||||||
corePP := build.postProcessors[0][0]
|
corePP := build.postProcessors[0][0]
|
||||||
pp := corePP.processor.(*TestPostProcessor)
|
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")
|
assert.Equal(builder.prepareConfig, []interface{}{42, packerConfig}, "prepare config should be 42")
|
||||||
|
|
||||||
coreProv := build.provisioners[0]
|
coreProv := build.provisioners[0]
|
||||||
prov := coreProv.provisioner.(*TestProvisioner)
|
prov := coreProv.provisioner.(*MockProvisioner)
|
||||||
assert.True(prov.prepCalled, "prepare should be called")
|
assert.True(prov.PrepCalled, "prepare should be called")
|
||||||
assert.Equal(prov.prepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
|
assert.Equal(prov.PrepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildPrepare_variables_default(t *testing.T) {
|
func TestBuildPrepare_variables_default(t *testing.T) {
|
||||||
|
@ -187,14 +187,14 @@ 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)
|
||||||
prov := build.provisioners[0].provisioner.(*TestProvisioner)
|
prov := build.provisioners[0].provisioner.(*MockProvisioner)
|
||||||
assert.True(prov.provCalled, "provision should be called")
|
assert.True(prov.ProvCalled, "provision should be called")
|
||||||
|
|
||||||
// Verify post-processor was run
|
// Verify post-processor was run
|
||||||
pp := build.postProcessors[0][0].processor.(*TestPostProcessor)
|
pp := build.postProcessors[0][0].processor.(*TestPostProcessor)
|
||||||
|
|
|
@ -20,7 +20,7 @@ func init() {
|
||||||
func testComponentFinder() *ComponentFinder {
|
func testComponentFinder() *ComponentFinder {
|
||||||
builderFactory := func(n string) (Builder, error) { return testBuilder(), nil }
|
builderFactory := func(n string) (Builder, error) { return testBuilder(), nil }
|
||||||
ppFactory := func(n string) (PostProcessor, error) { return new(TestPostProcessor), 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{
|
return &ComponentFinder{
|
||||||
Builder: builderFactory,
|
Builder: builderFactory,
|
||||||
PostProcessor: ppFactory,
|
PostProcessor: ppFactory,
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -309,7 +309,7 @@ func TestEnvironment_PostProcessor_Error(t *testing.T) {
|
||||||
func TestEnvironmentProvisioner(t *testing.T) {
|
func TestEnvironmentProvisioner(t *testing.T) {
|
||||||
assert := asserts.NewTestingAsserts(t, true)
|
assert := asserts.NewTestingAsserts(t, true)
|
||||||
|
|
||||||
p := &TestProvisioner{}
|
p := &MockProvisioner{}
|
||||||
ps := make(map[string]Provisioner)
|
ps := make(map[string]Provisioner)
|
||||||
ps["foo"] = p
|
ps["foo"] = p
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package packer
|
package packer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
// This is the hook that should be fired for provisioners to run.
|
// This is the hook that should be fired for provisioners to run.
|
||||||
const HookProvision = "packer_provision"
|
const HookProvision = "packer_provision"
|
||||||
|
|
||||||
|
@ -11,19 +15,40 @@ 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.
|
||||||
type DispatchHook struct {
|
type DispatchHook struct {
|
||||||
Mapping map[string][]Hook
|
Mapping map[string][]Hook
|
||||||
|
|
||||||
|
l sync.Mutex
|
||||||
|
cancelled bool
|
||||||
|
runningHook Hook
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs the hook with the given name by dispatching it to the proper
|
// Runs the hook with the given name by dispatching it to the proper
|
||||||
// hooks if a mapping exists. If a mapping doesn't exist, then nothing
|
// hooks if a mapping exists. If a mapping doesn't exist, then nothing
|
||||||
// happens.
|
// happens.
|
||||||
func (h *DispatchHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
|
func (h *DispatchHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
|
||||||
|
h.l.Lock()
|
||||||
|
h.cancelled = false
|
||||||
|
h.l.Unlock()
|
||||||
|
|
||||||
|
// Make sure when we exit that we reset the running hook.
|
||||||
|
defer func() {
|
||||||
|
h.l.Lock()
|
||||||
|
defer h.l.Unlock()
|
||||||
|
h.runningHook = nil
|
||||||
|
}()
|
||||||
|
|
||||||
hooks, ok := h.Mapping[name]
|
hooks, ok := h.Mapping[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
// No hooks for that name. No problem.
|
// No hooks for that name. No problem.
|
||||||
|
@ -31,6 +56,15 @@ func (h *DispatchHook) Run(name string, ui Ui, comm Communicator, data interface
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, hook := range hooks {
|
for _, hook := range hooks {
|
||||||
|
h.l.Lock()
|
||||||
|
if h.cancelled {
|
||||||
|
h.l.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
h.runningHook = hook
|
||||||
|
h.l.Unlock()
|
||||||
|
|
||||||
if err := hook.Run(name, ui, comm, data); err != nil {
|
if err := hook.Run(name, ui, comm, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -38,3 +72,16 @@ func (h *DispatchHook) Run(name string, ui Ui, comm Communicator, data interface
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cancels all the hooks that are currently in-flight, if any. This will
|
||||||
|
// block until the hooks are all cancelled.
|
||||||
|
func (h *DispatchHook) Cancel() {
|
||||||
|
h.l.Lock()
|
||||||
|
defer h.l.Unlock()
|
||||||
|
|
||||||
|
if h.runningHook != nil {
|
||||||
|
h.runningHook.Cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
h.cancelled = true
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package packer
|
||||||
|
|
||||||
|
// MockHook is an implementation of Hook that can be used for tests.
|
||||||
|
type MockHook struct {
|
||||||
|
RunFunc func() error
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
if t.RunFunc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.RunFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *MockHook) Cancel() {
|
||||||
|
t.CancelCalled = true
|
||||||
|
}
|
|
@ -2,52 +2,89 @@ package packer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cgl.tideland.biz/asserts"
|
"cgl.tideland.biz/asserts"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestHook struct {
|
// A helper Hook implementation for testing cancels.
|
||||||
runCalled bool
|
type CancelHook struct {
|
||||||
runComm Communicator
|
sync.Mutex
|
||||||
runData interface{}
|
cancelCh chan struct{}
|
||||||
runName string
|
doneCh chan struct{}
|
||||||
runUi Ui
|
|
||||||
|
Cancelled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TestHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
|
func (h *CancelHook) Run(string, Ui, Communicator, interface{}) error {
|
||||||
t.runCalled = true
|
h.Lock()
|
||||||
t.runComm = comm
|
h.cancelCh = make(chan struct{})
|
||||||
t.runData = data
|
h.doneCh = make(chan struct{})
|
||||||
t.runName = name
|
h.Unlock()
|
||||||
t.runUi = ui
|
|
||||||
|
defer close(h.doneCh)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-h.cancelCh:
|
||||||
|
h.Cancelled = true
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *CancelHook) Cancel() {
|
||||||
|
h.Lock()
|
||||||
|
close(h.cancelCh)
|
||||||
|
h.Unlock()
|
||||||
|
|
||||||
|
<-h.doneCh
|
||||||
|
}
|
||||||
|
|
||||||
func TestDispatchHook_Implements(t *testing.T) {
|
func TestDispatchHook_Implements(t *testing.T) {
|
||||||
assert := asserts.NewTestingAsserts(t, true)
|
assert := asserts.NewTestingAsserts(t, true)
|
||||||
|
|
||||||
var r Hook
|
var r Hook
|
||||||
c := &DispatchHook{nil}
|
c := &DispatchHook{}
|
||||||
|
|
||||||
assert.Implementor(c, &r, "should be a Hook")
|
assert.Implementor(c, &r, "should be a Hook")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDispatchHook_Run_NoHooks(t *testing.T) {
|
func TestDispatchHook_Run_NoHooks(t *testing.T) {
|
||||||
// Just make sure nothing blows up
|
// Just make sure nothing blows up
|
||||||
dh := &DispatchHook{make(map[string][]Hook)}
|
dh := &DispatchHook{}
|
||||||
dh.Run("foo", nil, nil, nil)
|
dh.Run("foo", nil, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
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: 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDispatchHook_cancel(t *testing.T) {
|
||||||
|
hook := new(CancelHook)
|
||||||
|
|
||||||
|
dh := &DispatchHook{
|
||||||
|
Mapping: map[string][]Hook{
|
||||||
|
"foo": []Hook{hook},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
go dh.Run("foo", nil, nil, 42)
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
dh.Cancel()
|
||||||
|
|
||||||
|
if !hook.Cancelled {
|
||||||
|
t.Fatal("hook should've cancelled")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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":
|
||||||
|
@ -63,7 +64,7 @@ func TestHelperProcess(*testing.T) {
|
||||||
case "post-processor":
|
case "post-processor":
|
||||||
ServePostProcessor(new(helperPostProcessor))
|
ServePostProcessor(new(helperPostProcessor))
|
||||||
case "provisioner":
|
case "provisioner":
|
||||||
ServeProvisioner(new(helperProvisioner))
|
ServeProvisioner(new(packer.MockProvisioner))
|
||||||
case "start-timeout":
|
case "start-timeout":
|
||||||
time.Sleep(1 * time.Minute)
|
time.Sleep(1 * time.Minute)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -28,6 +28,15 @@ func (c *cmdProvisioner) Provision(ui packer.Ui, comm packer.Communicator) error
|
||||||
return c.p.Provision(ui, comm)
|
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()) {
|
func (c *cmdProvisioner) checkExit(p interface{}, cb func()) {
|
||||||
if c.client.Exited() && cb != nil {
|
if c.client.Exited() && cb != nil {
|
||||||
cb()
|
cb()
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mitchellh/packer/packer"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"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) {
|
func TestProvisioner_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()
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -13,6 +17,11 @@ type Provisioner interface {
|
||||||
// is guaranteed to be connected to some machine so that provisioning
|
// is guaranteed to be connected to some machine so that provisioning
|
||||||
// can be done.
|
// can be done.
|
||||||
Provision(Ui, Communicator) error
|
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.
|
// A Hook implementation that runs the given provisioners.
|
||||||
|
@ -20,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
|
||||||
}
|
}
|
||||||
|
@ -32,3 +55,13 @@ 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() {
|
||||||
|
h.lock.Lock()
|
||||||
|
defer h.lock.Unlock()
|
||||||
|
|
||||||
|
if h.runningProvisioner != nil {
|
||||||
|
h.runningProvisioner.Cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package packer
|
||||||
|
|
||||||
|
// MockProvisioner is an implementation of Provisioner that can be
|
||||||
|
// used for tests.
|
||||||
|
type MockProvisioner struct {
|
||||||
|
ProvFunc func() error
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
if t.ProvFunc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.ProvFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *MockProvisioner) Cancel() {
|
||||||
|
t.CancelCalled = true
|
||||||
|
}
|
|
@ -1,23 +1,10 @@
|
||||||
package packer
|
package packer
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"sync"
|
||||||
type TestProvisioner struct {
|
"testing"
|
||||||
prepCalled bool
|
"time"
|
||||||
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) {
|
func TestProvisionHook_Impl(t *testing.T) {
|
||||||
var raw interface{}
|
var raw interface{}
|
||||||
|
@ -28,23 +15,68 @@ func TestProvisionHook_Impl(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProvisionHook(t *testing.T) {
|
func TestProvisionHook(t *testing.T) {
|
||||||
pA := &TestProvisioner{}
|
pA := &MockProvisioner{}
|
||||||
pB := &TestProvisioner{}
|
pB := &MockProvisioner{}
|
||||||
|
|
||||||
ui := testUi()
|
ui := testUi()
|
||||||
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 {
|
||||||
t.Error("provision should be called on pA")
|
t.Error("provision should be called on pA")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pB.provCalled {
|
if !pB.ProvCalled {
|
||||||
t.Error("provision should be called on pB")
|
t.Error("provision should be called on pB")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -2,6 +2,7 @@ package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"log"
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,6 +38,13 @@ 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() {
|
||||||
|
err := h.client.Call("Hook.Cancel", new(interface{}), new(interface{}))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Hook.Cancel error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -50,3 +58,8 @@ func (h *HookServer) Run(args *HookRunArgs, reply *interface{}) error {
|
||||||
*reply = nil
|
*reply = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HookServer) Cancel(args *interface{}, reply *interface{}) error {
|
||||||
|
h.hook.Cancel()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -4,24 +4,17 @@ import (
|
||||||
"cgl.tideland.biz/asserts"
|
"cgl.tideland.biz/asserts"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
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 +30,11 @@ 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")
|
||||||
|
|
||||||
|
// Test Cancel
|
||||||
|
hClient.Cancel()
|
||||||
|
assert.True(h.CancelCalled, "cancel should be called")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHook_Implements(t *testing.T) {
|
func TestHook_Implements(t *testing.T) {
|
||||||
|
@ -48,3 +45,56 @@ func TestHook_Implements(t *testing.T) {
|
||||||
|
|
||||||
assert.Implementor(h, &r, "should be a Hook")
|
assert.Implementor(h, &r, "should be a Hook")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHook_cancelWhileRun(t *testing.T) {
|
||||||
|
var finishLock sync.Mutex
|
||||||
|
finishOrder := make([]string, 0, 2)
|
||||||
|
|
||||||
|
h := &packer.MockHook{
|
||||||
|
RunFunc: func() error {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
finishLock.Lock()
|
||||||
|
finishOrder = append(finishOrder, "run")
|
||||||
|
finishLock.Unlock()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve
|
||||||
|
server := rpc.NewServer()
|
||||||
|
RegisterHook(server, h)
|
||||||
|
address := serveSingleConn(server)
|
||||||
|
|
||||||
|
// Create the client over RPC and run some methods to verify it works
|
||||||
|
client, err := rpc.Dial("tcp", address)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hClient := Hook(client)
|
||||||
|
|
||||||
|
// Start the run
|
||||||
|
finished := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
hClient.Run("foo", nil, nil, nil)
|
||||||
|
close(finished)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Cancel it pretty quickly.
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
hClient.Cancel()
|
||||||
|
|
||||||
|
finishLock.Lock()
|
||||||
|
finishOrder = append(finishOrder, "cancel")
|
||||||
|
finishLock.Unlock()
|
||||||
|
|
||||||
|
// Verify things are good
|
||||||
|
<-finished
|
||||||
|
|
||||||
|
// Check the results
|
||||||
|
expected := []string{"cancel", "run"}
|
||||||
|
if !reflect.DeepEqual(finishOrder, expected) {
|
||||||
|
t.Fatalf("bad: %#v", finishOrder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"log"
|
||||||
"net/rpc"
|
"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{}))
|
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 {
|
func (p *ProvisionerServer) Prepare(args *ProvisionerPrepareArgs, reply *error) error {
|
||||||
*reply = p.p.Prepare(args.Configs...)
|
*reply = p.p.Prepare(args.Configs...)
|
||||||
if *reply != nil {
|
if *reply != nil {
|
||||||
|
@ -71,3 +79,8 @@ func (p *ProvisionerServer) Provision(args *ProvisionerProvisionArgs, reply *int
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *ProvisionerServer) Cancel(args *interface{}, reply *interface{}) error {
|
||||||
|
p.p.Cancel()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -7,32 +7,11 @@ import (
|
||||||
"testing"
|
"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) {
|
func TestProvisionerRPC(t *testing.T) {
|
||||||
assert := asserts.NewTestingAsserts(t, true)
|
assert := asserts.NewTestingAsserts(t, true)
|
||||||
|
|
||||||
// Create the interface to test
|
// Create the interface to test
|
||||||
p := new(testProvisioner)
|
p := new(packer.MockProvisioner)
|
||||||
|
|
||||||
// Start the server
|
// Start the server
|
||||||
server := rpc.NewServer()
|
server := rpc.NewServer()
|
||||||
|
@ -47,17 +26,23 @@ func TestProvisionerRPC(t *testing.T) {
|
||||||
config := 42
|
config := 42
|
||||||
pClient := Provisioner(client)
|
pClient := Provisioner(client)
|
||||||
pClient.Prepare(config)
|
pClient.Prepare(config)
|
||||||
assert.True(p.prepareCalled, "prepare should be called")
|
assert.True(p.PrepCalled, "prepare should be called")
|
||||||
assert.Equal(p.prepareConfigs, []interface{}{42}, "prepare should be called with right arg")
|
assert.Equal(p.PrepConfigs, []interface{}{42}, "prepare should be called with right arg")
|
||||||
|
|
||||||
// Test Provision
|
// Test Provision
|
||||||
ui := &testUi{}
|
ui := &testUi{}
|
||||||
comm := new(packer.MockCommunicator)
|
comm := &packer.MockCommunicator{}
|
||||||
pClient.Provision(ui, comm)
|
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")
|
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) {
|
func TestProvisioner_Implements(t *testing.T) {
|
||||||
|
|
|
@ -589,7 +589,7 @@ func TestTemplate_Build(t *testing.T) {
|
||||||
"test-builder": builder,
|
"test-builder": builder,
|
||||||
}
|
}
|
||||||
|
|
||||||
provisioner := &TestProvisioner{}
|
provisioner := &MockProvisioner{}
|
||||||
provisionerMap := map[string]Provisioner{
|
provisionerMap := map[string]Provisioner{
|
||||||
"test-prov": provisioner,
|
"test-prov": provisioner,
|
||||||
}
|
}
|
||||||
|
@ -677,7 +677,7 @@ func TestTemplate_Build_ProvisionerOverride(t *testing.T) {
|
||||||
"test-builder": builder,
|
"test-builder": builder,
|
||||||
}
|
}
|
||||||
|
|
||||||
provisioner := &TestProvisioner{}
|
provisioner := &MockProvisioner{}
|
||||||
provisionerMap := map[string]Provisioner{
|
provisionerMap := map[string]Provisioner{
|
||||||
"test-prov": provisioner,
|
"test-prov": provisioner,
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,6 +183,12 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provisioner) Cancel() {
|
||||||
|
// Just hard quit. It isn't a big deal if what we're doing keeps
|
||||||
|
// running on the other side.
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, dst string, src string) error {
|
func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, dst string, src string) error {
|
||||||
if err := p.createDir(ui, comm, dst); err != nil {
|
if err := p.createDir(ui, comm, dst); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -84,3 +84,9 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provisioner) Cancel() {
|
||||||
|
// Just hard quit. It isn't a big deal if what we're doing keeps
|
||||||
|
// running on the other side.
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
|
@ -200,6 +200,12 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provisioner) Cancel() {
|
||||||
|
// Just hard quit. It isn't a big deal if what we're doing keeps
|
||||||
|
// running on the other side.
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
func UploadLocalDirectory(localDir string, remoteDir string, comm packer.Communicator, ui packer.Ui) (err error) {
|
func UploadLocalDirectory(localDir string, remoteDir string, comm packer.Communicator, ui packer.Ui) (err error) {
|
||||||
visitPath := func(localPath string, f os.FileInfo, err error) (err2 error) {
|
visitPath := func(localPath string, f os.FileInfo, err error) (err2 error) {
|
||||||
localRelPath := strings.Replace(localPath, localDir, "", 1)
|
localRelPath := strings.Replace(localPath, localDir, "", 1)
|
||||||
|
|
|
@ -281,6 +281,12 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provisioner) Cancel() {
|
||||||
|
// Just hard quit. It isn't a big deal if what we're doing keeps
|
||||||
|
// running on the other side.
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
// retryable will retry the given function over and over until a
|
// retryable will retry the given function over and over until a
|
||||||
// non-error is returned.
|
// non-error is returned.
|
||||||
func (p *Provisioner) retryable(f func() error) error {
|
func (p *Provisioner) retryable(f func() error) error {
|
||||||
|
|
Loading…
Reference in New Issue