Add disable_shutdown option to VirtualBox builder (#8449)

This commit is contained in:
Sylvia Moss 2019-12-05 14:34:56 +01:00 committed by GitHub
parent 8146b39986
commit 5ff5623433
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 122 additions and 48 deletions

View File

@ -27,6 +27,13 @@ type ShutdownConfig struct {
// Error removing floppy controller, you might need to set this to 5m
// or so. By default, the delay is 0s or disabled.
PostShutdownDelay time.Duration `mapstructure:"post_shutdown_delay" required:"false"`
// Packer normally halts the virtual machine after all provisioners have
// run when no `shutdown_command` is defined. If this is set to `true`, Packer
// *will not* halt the virtual machine but will assume that you will send the stop
// signal yourself through the preseed.cfg or your final provisioner.
// Packer will wait for a default of 5 minutes until the virtual machine is shutdown.
// The timeout can be changed using `shutdown_timeout` option.
DisableShutdown bool `mapstructure:"disable_shutdown" required:"false"`
}
func (c *ShutdownConfig) Prepare(ctx *interpolate.Context) []error {

View File

@ -64,3 +64,27 @@ func TestShutdownConfigPrepare_PostShutdownDelay(t *testing.T) {
t.Fatalf("bad: %s", c.PostShutdownDelay)
}
}
func TestShutdownConfigPrepare_DisableShutdown(t *testing.T) {
var c *ShutdownConfig
var errs []error
// Test with default value
c = testShutdownConfig()
c.DisableShutdown = false
errs = c.Prepare(interpolate.NewContext())
if len(errs) > 0 {
t.Fatalf("err: %#v", errs)
}
// Test with a good one
c = testShutdownConfig()
c.DisableShutdown = true
errs = c.Prepare(interpolate.NewContext())
if len(errs) > 0 {
t.Fatalf("err: %#v", errs)
}
if !c.DisableShutdown {
t.Fatalf("bad: %t", c.DisableShutdown)
}
}

View File

@ -23,9 +23,10 @@ import (
// Produces:
// <nothing>
type StepShutdown struct {
Command string
Timeout time.Duration
Delay time.Duration
Command string
Timeout time.Duration
Delay time.Duration
DisableShutdown bool
}
func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
@ -34,25 +35,29 @@ func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multis
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
if s.Command != "" {
ui.Say("Gracefully halting virtual machine...")
log.Printf("Executing shutdown command: %s", s.Command)
cmd := &packer.RemoteCmd{Command: s.Command}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
err := fmt.Errorf("Failed to send shutdown command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if !s.DisableShutdown {
if s.Command != "" {
ui.Say("Gracefully halting virtual machine...")
log.Printf("Executing shutdown command: %s", s.Command)
cmd := &packer.RemoteCmd{Command: s.Command}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
err := fmt.Errorf("Failed to send shutdown command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
} else {
ui.Say("Halting the virtual machine...")
if err := driver.Stop(vmName); err != nil {
err := fmt.Errorf("Error stopping VM: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
} else {
ui.Say("Halting the virtual machine...")
if err := driver.Stop(vmName); err != nil {
err := fmt.Errorf("Error stopping VM: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
} else {
ui.Say("Automatic shutdown disabled. Please shutdown virtual machine.")
}
// Wait for the machine to actually shut down
@ -72,7 +77,7 @@ func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multis
select {
case <-shutdownTimer:
err := errors.New("Timeout while waiting for machine to shut down.")
err := errors.New("Timeout while waiting for machine to shutdown.")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt

View File

@ -16,6 +16,7 @@ func TestStepShutdown_impl(t *testing.T) {
func TestStepShutdown_noShutdownCommand(t *testing.T) {
state := testState(t)
step := new(StepShutdown)
step.DisableShutdown = false
comm := new(packer.MockCommunicator)
state.Put("communicator", comm)
@ -45,6 +46,7 @@ func TestStepShutdown_shutdownCommand(t *testing.T) {
step := new(StepShutdown)
step.Command = "poweroff"
step.Timeout = 1 * time.Second
step.DisableShutdown = false
comm := new(packer.MockCommunicator)
state.Put("communicator", comm)
@ -82,6 +84,7 @@ func TestStepShutdown_shutdownTimeout(t *testing.T) {
step := new(StepShutdown)
step.Command = "poweroff"
step.Timeout = 1 * time.Second
step.DisableShutdown = false
comm := new(packer.MockCommunicator)
state.Put("communicator", comm)
@ -112,6 +115,7 @@ func TestStepShutdown_shutdownDelay(t *testing.T) {
step.Command = "poweroff"
step.Timeout = 5 * time.Second
step.Delay = 2 * time.Second
step.DisableShutdown = false
comm := new(packer.MockCommunicator)
state.Put("communicator", comm)
@ -168,3 +172,32 @@ func TestStepShutdown_shutdownDelay(t *testing.T) {
}
}
func TestStepShutdown_DisableShutdown(t *testing.T) {
state := testState(t)
step := new(StepShutdown)
step.DisableShutdown = true
step.Timeout = 2 * time.Second
comm := new(packer.MockCommunicator)
state.Put("communicator", comm)
state.Put("vmName", "foo")
driver := state.Get("driver").(*DriverMock)
driver.IsRunningReturn = true
go func() {
time.Sleep(1 * time.Second)
driver.Lock()
defer driver.Unlock()
driver.IsRunningReturn = false
}()
// Test the run
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("should NOT have error")
}
}

View File

@ -370,9 +370,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Comm: &b.config.SSHConfig.Comm,
},
&vboxcommon.StepShutdown{
Command: b.config.ShutdownCommand,
Timeout: b.config.ShutdownTimeout,
Delay: b.config.PostShutdownDelay,
Command: b.config.ShutdownCommand,
Timeout: b.config.ShutdownTimeout,
Delay: b.config.PostShutdownDelay,
DisableShutdown: b.config.DisableShutdown,
},
&vboxcommon.StepRemoveDevices{
Bundling: b.config.VBoxBundleConfig,

View File

@ -42,6 +42,7 @@ type FlatConfig struct {
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command"`
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout"`
PostShutdownDelay *string `mapstructure:"post_shutdown_delay" required:"false" cty:"post_shutdown_delay"`
DisableShutdown *bool `mapstructure:"disable_shutdown" required:"false" cty:"disable_shutdown"`
Type *string `mapstructure:"communicator" cty:"communicator"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
@ -154,6 +155,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"post_shutdown_delay": &hcldec.AttrSpec{Name: "post_shutdown_delay", Type: cty.String, Required: false},
"disable_shutdown": &hcldec.AttrSpec{Name: "disable_shutdown", Type: cty.Bool, Required: false},
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},

View File

@ -139,9 +139,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Comm: &b.config.SSHConfig.Comm,
},
&vboxcommon.StepShutdown{
Command: b.config.ShutdownCommand,
Timeout: b.config.ShutdownTimeout,
Delay: b.config.PostShutdownDelay,
Command: b.config.ShutdownCommand,
Timeout: b.config.ShutdownTimeout,
Delay: b.config.PostShutdownDelay,
DisableShutdown: b.config.DisableShutdown,
},
&vboxcommon.StepRemoveDevices{
GuestAdditionsInterface: b.config.GuestAdditionsInterface,

View File

@ -79,6 +79,7 @@ type FlatConfig struct {
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command"`
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout"`
PostShutdownDelay *string `mapstructure:"post_shutdown_delay" required:"false" cty:"post_shutdown_delay"`
DisableShutdown *bool `mapstructure:"disable_shutdown" required:"false" cty:"disable_shutdown"`
VBoxManage [][]string `mapstructure:"vboxmanage" required:"false" cty:"vboxmanage"`
VBoxManagePost [][]string `mapstructure:"vboxmanage_post" required:"false" cty:"vboxmanage_post"`
VBoxVersionFile *string `mapstructure:"virtualbox_version_file" required:"false" cty:"virtualbox_version_file"`
@ -177,6 +178,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"post_shutdown_delay": &hcldec.AttrSpec{Name: "post_shutdown_delay", Type: cty.String, Required: false},
"disable_shutdown": &hcldec.AttrSpec{Name: "disable_shutdown", Type: cty.Bool, Required: false},
"vboxmanage": &hcldec.BlockListSpec{TypeName: "vboxmanage", Nested: &hcldec.AttrSpec{Name: "vboxmanage", Type: cty.List(cty.String), Required: false}},
"vboxmanage_post": &hcldec.BlockListSpec{TypeName: "vboxmanage_post", Nested: &hcldec.AttrSpec{Name: "vboxmanage_post", Type: cty.List(cty.String), Required: false}},
"virtualbox_version_file": &hcldec.AttrSpec{Name: "virtualbox_version_file", Type: cty.String, Required: false},

View File

@ -123,9 +123,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Comm: &b.config.SSHConfig.Comm,
},
&vboxcommon.StepShutdown{
Command: b.config.ShutdownCommand,
Timeout: b.config.ShutdownTimeout,
Delay: b.config.PostShutdownDelay,
Command: b.config.ShutdownCommand,
Timeout: b.config.ShutdownTimeout,
Delay: b.config.PostShutdownDelay,
DisableShutdown: b.config.DisableShutdown,
},
&vboxcommon.StepVBoxManage{
Commands: b.config.VBoxManagePost,

View File

@ -79,6 +79,7 @@ type FlatConfig struct {
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command"`
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout"`
PostShutdownDelay *string `mapstructure:"post_shutdown_delay" required:"false" cty:"post_shutdown_delay"`
DisableShutdown *bool `mapstructure:"disable_shutdown" required:"false" cty:"disable_shutdown"`
VBoxManage [][]string `mapstructure:"vboxmanage" required:"false" cty:"vboxmanage"`
VBoxManagePost [][]string `mapstructure:"vboxmanage_post" required:"false" cty:"vboxmanage_post"`
VBoxVersionFile *string `mapstructure:"virtualbox_version_file" required:"false" cty:"virtualbox_version_file"`
@ -173,6 +174,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"post_shutdown_delay": &hcldec.AttrSpec{Name: "post_shutdown_delay", Type: cty.String, Required: false},
"disable_shutdown": &hcldec.AttrSpec{Name: "disable_shutdown", Type: cty.Bool, Required: false},
"vboxmanage": &hcldec.BlockListSpec{TypeName: "vboxmanage", Nested: &hcldec.AttrSpec{Name: "vboxmanage", Type: cty.List(cty.String), Required: false}},
"vboxmanage_post": &hcldec.BlockListSpec{TypeName: "vboxmanage_post", Nested: &hcldec.AttrSpec{Name: "vboxmanage_post", Type: cty.List(cty.String), Required: false}},
"virtualbox_version_file": &hcldec.AttrSpec{Name: "virtualbox_version_file", Type: cty.String, Required: false},

View File

@ -221,24 +221,6 @@ builder.
the builder. By default this is `output-BUILDNAME` where "BUILDNAME" is the
name of the build.
- `post_shutdown_delay` (string) - The amount of time to wait after shutting
down the virtual machine. Defaults to `2s`. **Hint:** Don't specify a value
smaller than `2s` because otherwise the creation of a target snapshot might
corrupt the VM because not all locks has been released by VirtualBox.
- `shutdown_command` (string) - The command to use to gracefully shut down the
machine once all the provisioning is done. By default this is an empty
string, which tells Packer to just forcefully shut down the machine unless a
shutdown command takes place inside script so this may safely be omitted. If
one or more scripts require a reboot it is suggested to leave this blank
since reboots may fail and specify the final shutdown command in your
last script.
- `shutdown_timeout` (string) - The amount of time to wait after executing the
`shutdown_command` for the virtual machine to actually shut down. If it
doesn't shut down in this time, it is an error. By default, the timeout is
`5m` or five minutes.
- `skip_export` (boolean) - Defaults to `false`. When enabled, Packer will
not export the VM. Useful if the builder should be applied again on the created
target snapshot.
@ -292,6 +274,13 @@ builder.
port in this range that appears available. By default this is `5900` to
`6000`. The minimum and maximum ports are inclusive.
### Shutdown configuration
#### Optional:
<%= partial "partials/builder/virtualbox/common/ShutdownConfig-not-required" %>
## Boot Command
The `boot_command` configuration is very important: it specifies the keys to

View File

@ -17,4 +17,11 @@
down the virtual machine. If you get the error
Error removing floppy controller, you might need to set this to 5m
or so. By default, the delay is 0s or disabled.
- `disable_shutdown` (bool) - Packer normally halts the virtual machine after all provisioners have
run when no `shutdown_command` is defined. If this is set to `true`, Packer
*will not* halt the virtual machine but will assume that you will send the stop
signal yourself through the preseed.cfg or your final provisioner.
Packer will wait for a default of 5 minutes until the virtual machine is shutdown.
The timeout can be changed using `shutdown_timeout` option.