Add VirtualBox post shutdown delay to address floppy controller delete error per issue #2401
This commit is contained in:
parent
ad5f2a108e
commit
a187b1cf92
|
@ -10,8 +10,10 @@ import (
|
|||
type ShutdownConfig struct {
|
||||
ShutdownCommand string `mapstructure:"shutdown_command"`
|
||||
RawShutdownTimeout string `mapstructure:"shutdown_timeout"`
|
||||
RawPostShutdownDelay string `mapstructure:"post_shutdown_delay"`
|
||||
|
||||
ShutdownTimeout time.Duration ``
|
||||
PostShutdownDelay time.Duration ``
|
||||
}
|
||||
|
||||
func (c *ShutdownConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
|
@ -19,6 +21,10 @@ func (c *ShutdownConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
c.RawShutdownTimeout = "5m"
|
||||
}
|
||||
|
||||
if c.RawPostShutdownDelay == "" {
|
||||
c.RawPostShutdownDelay = "0s"
|
||||
}
|
||||
|
||||
var errs []error
|
||||
var err error
|
||||
c.ShutdownTimeout, err = time.ParseDuration(c.RawShutdownTimeout)
|
||||
|
@ -26,5 +32,10 @@ func (c *ShutdownConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
errs = append(errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err))
|
||||
}
|
||||
|
||||
c.PostShutdownDelay, err = time.ParseDuration(c.RawPostShutdownDelay)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("Failed parsing post_shutdown_delay: %s", err))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
|
@ -43,3 +43,38 @@ func TestShutdownConfigPrepare_ShutdownTimeout(t *testing.T) {
|
|||
t.Fatalf("bad: %s", c.ShutdownTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownConfigPrepare_PostShutdownDelay(t *testing.T) {
|
||||
var c *ShutdownConfig
|
||||
var errs []error
|
||||
|
||||
// Test with a bad value
|
||||
c = testShutdownConfig()
|
||||
c.RawPostShutdownDelay = "this is not good"
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) == 0 {
|
||||
t.Fatalf("should have error")
|
||||
}
|
||||
|
||||
// Test with default value
|
||||
c = testShutdownConfig()
|
||||
c.RawPostShutdownDelay = ""
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("err: %#v", errs)
|
||||
}
|
||||
if c.PostShutdownDelay.Nanoseconds() != 0 {
|
||||
t.Fatalf("bad: %s", c.PostShutdownDelay)
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
c = testShutdownConfig()
|
||||
c.RawPostShutdownDelay = "5s"
|
||||
errs = c.Prepare(testConfigTemplate(t))
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("err: %#v", errs)
|
||||
}
|
||||
if c.PostShutdownDelay != 5*time.Second {
|
||||
t.Fatalf("bad: %s", c.PostShutdownDelay)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
type StepShutdown struct {
|
||||
Command string
|
||||
Timeout time.Duration
|
||||
Delay time.Duration
|
||||
}
|
||||
|
||||
func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
@ -48,6 +49,12 @@ func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction {
|
|||
for {
|
||||
running, _ := driver.IsRunning(vmName)
|
||||
if !running {
|
||||
|
||||
if s.Delay.Nanoseconds() > 0 {
|
||||
log.Printf("Delay for %s after shutdown to allow locks to clear...", s.Delay)
|
||||
time.Sleep(s.Delay)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
|
|
|
@ -103,3 +103,66 @@ func TestStepShutdown_shutdownTimeout(t *testing.T) {
|
|||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepShutdown_shutdownDelay(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepShutdown)
|
||||
step.Command = "poweroff"
|
||||
step.Timeout = 5 * time.Second
|
||||
step.Delay = 2 * time.Second
|
||||
|
||||
comm := new(packer.MockCommunicator)
|
||||
state.Put("communicator", comm)
|
||||
state.Put("vmName", "foo")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
driver.IsRunningReturn = true
|
||||
start := time.Now()
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
driver.Lock()
|
||||
defer driver.Unlock()
|
||||
driver.IsRunningReturn = false
|
||||
}()
|
||||
|
||||
// Test the run
|
||||
|
||||
if action := step.Run(state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
testDuration := time.Since(start).Seconds()
|
||||
if testDuration < 2.5 || testDuration > 2.6 {
|
||||
t.Fatal("incorrect duration")
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
step.Delay = 0
|
||||
|
||||
driver.IsRunningReturn = true
|
||||
start = time.Now()
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
driver.Lock()
|
||||
defer driver.Unlock()
|
||||
driver.IsRunningReturn = false
|
||||
}()
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
testDuration = time.Since(start).Seconds()
|
||||
if testDuration > 0.6 {
|
||||
t.Fatal("incorrect duration")
|
||||
}
|
||||
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -251,6 +251,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&vboxcommon.StepShutdown{
|
||||
Command: b.config.ShutdownCommand,
|
||||
Timeout: b.config.ShutdownTimeout,
|
||||
Delay: b.config.PostShutdownDelay,
|
||||
},
|
||||
new(vboxcommon.StepRemoveDevices),
|
||||
&vboxcommon.StepVBoxManage{
|
||||
|
|
|
@ -120,6 +120,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&vboxcommon.StepShutdown{
|
||||
Command: b.config.ShutdownCommand,
|
||||
Timeout: b.config.ShutdownTimeout,
|
||||
Delay: b.config.PostShutdownDelay,
|
||||
},
|
||||
new(vboxcommon.StepRemoveDevices),
|
||||
&vboxcommon.StepVBoxManage{
|
||||
|
|
Loading…
Reference in New Issue