Add VirtualBox post shutdown delay to address floppy controller delete error per issue #2401

This commit is contained in:
Mike Stankavich 2016-10-02 12:08:32 -05:00
parent ad5f2a108e
commit a187b1cf92
6 changed files with 118 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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