From 408eba88ade17311bc9d3a650c1495093beb5e80 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 18 Apr 2018 14:10:28 -0700 Subject: [PATCH] flatten boot command config and implement for vmware --- builder/vmware/common/run_config.go | 34 ++-------- builder/vmware/common/run_config_test.go | 36 ---------- .../vmware/common/step_type_boot_command.go | 50 +++++++------- builder/vmware/iso/builder.go | 5 +- builder/vmware/vmx/builder.go | 2 +- builder/vmware/vmx/config.go | 3 + common/boot_command/boot_command_ast.go | 6 -- common/boot_command/boot_command_ast_test.go | 6 +- common/boot_command/config.go | 50 ++++++++++++++ common/boot_command/config_test.go | 65 +++++++++++++++++++ 10 files changed, 154 insertions(+), 103 deletions(-) delete mode 100644 builder/vmware/common/run_config_test.go create mode 100644 common/boot_command/config.go create mode 100644 common/boot_command/config_test.go diff --git a/builder/vmware/common/run_config.go b/builder/vmware/common/run_config.go index 9463d8753..c4bfc3413 100644 --- a/builder/vmware/common/run_config.go +++ b/builder/vmware/common/run_config.go @@ -2,30 +2,20 @@ package common import ( "fmt" - "time" "github.com/hashicorp/packer/template/interpolate" ) type RunConfig struct { - Headless bool `mapstructure:"headless"` - RawBootWait string `mapstructure:"boot_wait"` - DisableVNC bool `mapstructure:"disable_vnc"` - BootCommand []string `mapstructure:"boot_command"` + Headless bool `mapstructure:"headless"` VNCBindAddress string `mapstructure:"vnc_bind_address"` VNCPortMin uint `mapstructure:"vnc_port_min"` VNCPortMax uint `mapstructure:"vnc_port_max"` VNCDisablePassword bool `mapstructure:"vnc_disable_password"` - - BootWait time.Duration `` } -func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { - if c.RawBootWait == "" { - c.RawBootWait = "10s" - } - +func (c *RunConfig) Prepare(ctx *interpolate.Context) (errs []error) { if c.VNCPortMin == 0 { c.VNCPortMin = 5900 } @@ -38,25 +28,9 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { c.VNCBindAddress = "127.0.0.1" } - var errs []error - var err error - if len(c.BootCommand) > 0 && c.DisableVNC { - errs = append(errs, - fmt.Errorf("A boot command cannot be used when vnc is disabled.")) - } - - if c.RawBootWait != "" { - c.BootWait, err = time.ParseDuration(c.RawBootWait) - if err != nil { - errs = append( - errs, fmt.Errorf("Failed parsing boot_wait: %s", err)) - } - } - if c.VNCPortMin > c.VNCPortMax { - errs = append( - errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max")) + errs = append(errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max")) } - return errs + return } diff --git a/builder/vmware/common/run_config_test.go b/builder/vmware/common/run_config_test.go deleted file mode 100644 index d94e254f7..000000000 --- a/builder/vmware/common/run_config_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package common - -import ( - "testing" -) - -func TestRunConfigPrepare(t *testing.T) { - var c *RunConfig - - // Test a default boot_wait - c = new(RunConfig) - c.RawBootWait = "" - errs := c.Prepare(testConfigTemplate(t)) - if len(errs) > 0 { - t.Fatalf("bad: %#v", errs) - } - if c.RawBootWait != "10s" { - t.Fatalf("bad value: %s", c.RawBootWait) - } - - // Test with a bad boot_wait - c = new(RunConfig) - c.RawBootWait = "this is not good" - errs = c.Prepare(testConfigTemplate(t)) - if len(errs) == 0 { - t.Fatal("should error") - } - - // Test with a good one - c = new(RunConfig) - c.RawBootWait = "5s" - errs = c.Prepare(testConfigTemplate(t)) - if len(errs) > 0 { - t.Fatalf("bad: %#v", errs) - } -} diff --git a/builder/vmware/common/step_type_boot_command.go b/builder/vmware/common/step_type_boot_command.go index 6821b8e1b..7544c4c3e 100644 --- a/builder/vmware/common/step_type_boot_command.go +++ b/builder/vmware/common/step_type_boot_command.go @@ -25,7 +25,7 @@ import ( // Produces: // type StepTypeBootCommand struct { - BootCommand []string + BootCommand string VNCEnabled bool BootWait time.Duration VMName string @@ -118,34 +118,32 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) d := bootcommand.NewVNCDriver(c) ui.Say("Typing the boot command over VNC...") - for i, command := range s.BootCommand { - command, err := interpolate.Render(command, &s.Ctx) - if err != nil { - err := fmt.Errorf("Error preparing boot command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } + command, err := interpolate.Render(s.BootCommand, &s.Ctx) + if err != nil { + err := fmt.Errorf("Error preparing boot command: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } - seq, err := bootcommand.GenerateExpressionSequence(command) - if err != nil { - err := fmt.Errorf("Error generating boot command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } + seq, err := bootcommand.GenerateExpressionSequence(command) + if err != nil { + err := fmt.Errorf("Error generating boot command: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } - if err := seq.Do(ctx, d); err != nil { - err := fmt.Errorf("Error running boot command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - if pauseFn != nil { - pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state) - } + if err := seq.Do(ctx, d); err != nil { + err := fmt.Errorf("Error running boot command: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + if pauseFn != nil { + pauseFn(multistep.DebugLocationAfterRun, + fmt.Sprintf("boot_command: %s", command), state) } return multistep.ActionContinue diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 5f1399c06..1edd778ee 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -11,6 +11,7 @@ import ( vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/common/boot_command" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/multistep" @@ -30,6 +31,7 @@ type Config struct { common.HTTPConfig `mapstructure:",squash"` common.ISOConfig `mapstructure:",squash"` common.FloppyConfig `mapstructure:",squash"` + bootcommand.VNCConfig `mapstructure:",squash"` vmwcommon.DriverConfig `mapstructure:",squash"` vmwcommon.OutputConfig `mapstructure:",squash"` vmwcommon.RunConfig `mapstructure:",squash"` @@ -122,6 +124,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, b.config.ToolsConfig.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.VMXConfig.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...) + errs = packer.MultiErrorAppend(errs, b.config.VNCConfig.Prepare(&b.config.ctx)...) if b.config.DiskName == "" { b.config.DiskName = "disk" @@ -325,7 +328,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vmwcommon.StepTypeBootCommand{ BootWait: b.config.BootWait, VNCEnabled: !b.config.DisableVNC, - BootCommand: b.config.BootCommand, + BootCommand: b.config.FlatBootCommand(), VMName: b.config.VMName, Ctx: b.config.ctx, }, diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 69b143ebe..bfe6b06c9 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -93,7 +93,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vmwcommon.StepTypeBootCommand{ BootWait: b.config.BootWait, VNCEnabled: !b.config.DisableVNC, - BootCommand: b.config.BootCommand, + BootCommand: b.config.FlatBootCommand(), VMName: b.config.VMName, Ctx: b.config.ctx, }, diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index b4d1c009f..e937136a4 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -6,6 +6,7 @@ import ( vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/common/boot_command" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" @@ -16,6 +17,7 @@ type Config struct { common.PackerConfig `mapstructure:",squash"` common.HTTPConfig `mapstructure:",squash"` common.FloppyConfig `mapstructure:",squash"` + bootcommand.VNCConfig `mapstructure:",squash"` vmwcommon.DriverConfig `mapstructure:",squash"` vmwcommon.OutputConfig `mapstructure:",squash"` vmwcommon.RunConfig `mapstructure:",squash"` @@ -65,6 +67,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, c.ToolsConfig.Prepare(&c.ctx)...) errs = packer.MultiErrorAppend(errs, c.VMXConfig.Prepare(&c.ctx)...) errs = packer.MultiErrorAppend(errs, c.FloppyConfig.Prepare(&c.ctx)...) + errs = packer.MultiErrorAppend(errs, c.VNCConfig.Prepare(&c.ctx)...) if c.SourcePath == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is blank, but is required")) diff --git a/common/boot_command/boot_command_ast.go b/common/boot_command/boot_command_ast.go index 38ce97144..16b562e95 100644 --- a/common/boot_command/boot_command_ast.go +++ b/common/boot_command/boot_command_ast.go @@ -84,12 +84,6 @@ type waitExpression struct { // Do waits the amount of time described by the expression. It is cancellable // through the context. func (w *waitExpression) Do(ctx context.Context, _ BCDriver) error { - /* - // do I want this to not parse or to error? - if w.d < 0 { - panic("Was not expecting negative wait duration.") - } - */ log.Printf("[INFO] Waiting %s", w.d) select { case <-time.After(w.d): diff --git a/common/boot_command/boot_command_ast_test.go b/common/boot_command/boot_command_ast_test.go index 60369f572..8536f58c0 100644 --- a/common/boot_command/boot_command_ast_test.go +++ b/common/boot_command/boot_command_ast_test.go @@ -109,8 +109,8 @@ func Test_negativeWait(t *testing.T) { /* gL := toIfaceSlice(got) - for _, g := range gL { - assert.Equal(t, tt.out, g.(*specialExpression).String()) - } + for _, g := range gL { + assert.Equal(t, tt.out, g.(*specialExpression).String()) + } */ } diff --git a/common/boot_command/config.go b/common/boot_command/config.go new file mode 100644 index 000000000..6a5b18cd9 --- /dev/null +++ b/common/boot_command/config.go @@ -0,0 +1,50 @@ +package bootcommand + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/packer/template/interpolate" +) + +type Config struct { + RawBootWait string `mapstructure:"boot_wait"` + BootCommand []string `mapstructure:"boot_command"` + + BootWait time.Duration `` +} + +type VNCConfig struct { + Config + DisableVNC bool `mapstructure:"disable_vnc"` +} + +func (c *Config) Prepare(ctx *interpolate.Context) (errs []error) { + if c.RawBootWait == "" { + c.RawBootWait = "10s" + } + if c.RawBootWait != "" { + bw, err := time.ParseDuration(c.RawBootWait) + if err != nil { + errs = append( + errs, fmt.Errorf("Failed parsing boot_wait: %s", err)) + } else { + c.BootWait = bw + } + } + return +} + +func (c *Config) FlatBootCommand() string { + return strings.Join(c.BootCommand, "") +} + +func (c *VNCConfig) Prepare(ctx *interpolate.Context) (errs []error) { + if len(c.BootCommand) > 0 && c.DisableVNC { + errs = append(errs, + fmt.Errorf("A boot command cannot be used when vnc is disabled.")) + } + errs = append(errs, c.Config.Prepare(ctx)...) + return +} diff --git a/common/boot_command/config_test.go b/common/boot_command/config_test.go new file mode 100644 index 000000000..608e48c0a --- /dev/null +++ b/common/boot_command/config_test.go @@ -0,0 +1,65 @@ +package bootcommand + +import ( + "testing" + + "github.com/hashicorp/packer/template/interpolate" +) + +func TestConfigPrepare(t *testing.T) { + var c *Config + + // Test a default boot_wait + c = new(Config) + c.RawBootWait = "" + errs := c.Prepare(&interpolate.Context{}) + if len(errs) > 0 { + t.Fatalf("bad: %#v", errs) + } + if c.RawBootWait != "10s" { + t.Fatalf("bad value: %s", c.RawBootWait) + } + + // Test with a bad boot_wait + c = new(Config) + c.RawBootWait = "this is not good" + errs = c.Prepare(&interpolate.Context{}) + if len(errs) == 0 { + t.Fatal("should error") + } + + // Test with a good one + c = new(Config) + c.RawBootWait = "5s" + errs = c.Prepare(&interpolate.Context{}) + if len(errs) > 0 { + t.Fatalf("bad: %#v", errs) + } +} + +func TestVNCConfigPrepare(t *testing.T) { + var c *VNCConfig + + // Test with a boot command + c = new(VNCConfig) + c.BootCommand = []string{"a", "b"} + errs := c.Prepare(&interpolate.Context{}) + if len(errs) > 0 { + t.Fatalf("bad: %#v", errs) + } + + // Test with disabled vnc + c.DisableVNC = true + errs = c.Prepare(&interpolate.Context{}) + if len(errs) == 0 { + t.Fatal("should error") + } + + // Test no boot command with no vnc + c = new(VNCConfig) + c.DisableVNC = true + errs = c.Prepare(&interpolate.Context{}) + if len(errs) > 0 { + t.Fatalf("bad: %#v", errs) + } +}