From 39099ad3af9e2d418bb30c07958cc6e0a48eaba8 Mon Sep 17 00:00:00 2001 From: Elizaveta Tretyakova Date: Tue, 13 Jun 2017 14:11:41 +0300 Subject: [PATCH] Added template conversion (#18) * Added conversion to template without timeout * Added timeout * Extracted converting to template to a separate step --- builder.go | 6 +++++- config.go | 16 +++++++++++++++- step_configure_hw.go | 1 + step_post_process.go | 33 ++++++++++++++++++++++++++++++++ step_shutdown.go | 45 ++++++++++++++++++++++---------------------- 5 files changed, 77 insertions(+), 24 deletions(-) create mode 100644 step_post_process.go diff --git a/builder.go b/builder.go index 6dbb4b476..91e80927a 100644 --- a/builder.go +++ b/builder.go @@ -62,7 +62,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &common.StepProvision{}, &StepShutdown{ - Command: b.config.ShutdownCommand, + Command: b.config.ShutdownCommand, + ShutdownTimeout: b.config.ShutdownTimeout, + }, + &StepPostProcess{ + ToTemplate: b.config.ToTemplate, }, } diff --git a/config.go b/config.go index 217e0e456..c704e2a55 100644 --- a/config.go +++ b/config.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" "strconv" + "time" ) type Config struct { @@ -27,7 +28,12 @@ type Config struct { Host string `mapstructure:"host"` ResourcePool string `mapstructure:"resource_pool"` Datastore string `mapstructure:"datastore"` + + // Settings LinkedClone bool `mapstructure:"linked_clone"` + ToTemplate bool `mapstructure:"to_template"` + RawShutdownTimeout string `mapstructure:"shutdown_timeout"` + ShutdownTimeout time.Duration `` // Hardware Cpus string `mapstructure:"cpus"` @@ -84,11 +90,19 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("Invalid number for Ram")) } } + if c.RawShutdownTimeout == "" { + c.RawShutdownTimeout = "5m" + } + c.ShutdownTimeout, err = time.ParseDuration(c.RawShutdownTimeout) + if err != nil { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err)) + } + // Warnings var warnings []string if c.Datastore == "" { - warnings = append(warnings, "Datastore is not specified, will try to find a default one") + warnings = append(warnings, "Datastore is not specified, will try to find the default one") } if len(errs.Errors) > 0 { diff --git a/step_configure_hw.go b/step_configure_hw.go index 2e6da31e5..3f2b235e9 100644 --- a/step_configure_hw.go +++ b/step_configure_hw.go @@ -49,6 +49,7 @@ func (s *StepConfigureHW) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) if parametersFlag != (ConfigParametersFlag{}) { ui.Say("configuring virtual hardware...") + // Reconfigure hardware task, err := vm.Reconfigure(ctx, confSpec) if err != nil { state.Put("error", err) diff --git a/step_post_process.go b/step_post_process.go new file mode 100644 index 000000000..3c4c074a5 --- /dev/null +++ b/step_post_process.go @@ -0,0 +1,33 @@ +package main + +import ( + "github.com/mitchellh/multistep" + "github.com/hashicorp/packer/packer" + "github.com/vmware/govmomi/object" + "context" +) + +type StepPostProcess struct{ + ToTemplate bool +} + +func (s *StepPostProcess) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + vm := state.Get("vm").(*object.VirtualMachine) + ctx := state.Get("ctx").(context.Context) + + // Turning into template if needed + if s.ToTemplate { + ui.Say("turning into template...") + err := vm.MarkAsTemplate(ctx) + if err != nil { + state.Put("error", err) + return multistep.ActionHalt + } + ui.Say("done") + } + + return multistep.ActionContinue +} + +func (s *StepPostProcess) Cleanup(state multistep.StateBag) {} diff --git a/step_shutdown.go b/step_shutdown.go index 24bdb7ca7..5e9eaf1f3 100644 --- a/step_shutdown.go +++ b/step_shutdown.go @@ -9,10 +9,12 @@ import ( "log" "time" "bytes" + "errors" ) type StepShutdown struct{ - Command string + Command string + ShutdownTimeout time.Duration } func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction { @@ -40,38 +42,37 @@ func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction { ui.Error(err.Error()) return multistep.ActionHalt } - - // TODO: add timeout - for !cmd.Exited { - ui.Say("Waiting for remote cmd to finish...") - time.Sleep(150 * time.Millisecond) - } - if cmd.ExitStatus != 0 && cmd.ExitStatus != packer.CmdDisconnect { - err := fmt.Errorf("Cmd exit status %v, not 0", cmd.ExitStatus) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } else if cmd.ExitStatus == packer.CmdDisconnect { - ui.Say("VM disconnected") - } } else { ui.Say("Forcibly halting virtual machine...") err := vm.ShutdownGuest(ctx) + if err != nil { + state.Put("error", fmt.Errorf("Could not shutdown guest: %v", err)) + return multistep.ActionHalt + } + } + + // Wait for the machine to actually shut down + log.Printf("Waiting max %s for shutdown to complete", s.ShutdownTimeout) + shutdownTimer := time.After(s.ShutdownTimeout) + for { + powerState, err := vm.PowerState(ctx) if err != nil { state.Put("error", err) return multistep.ActionHalt } + if powerState == "poweredOff" { + break + } - task, err := vm.PowerOff(ctx) - if err != nil { - state.Put("error", err) - return multistep.ActionHalt - } - _, err = task.WaitForResult(ctx, nil) - if err != nil { + select { + case <-shutdownTimer: + err := errors.New("Timeout while waiting for machine to shut down.") state.Put("error", err) + ui.Error(err.Error()) return multistep.ActionHalt + default: + time.Sleep(150 * time.Millisecond) } }