From ca16f33fa39a828b7dd3c6a32d8dcfa39d5ad181 Mon Sep 17 00:00:00 2001 From: Taliesin Sisson Date: Sun, 21 Jun 2015 14:06:27 +0100 Subject: [PATCH] Use the same configuration style as existing builders Move builder into correctly named folder --- builder/hyperv/common/floppy_config.go | 19 +++ builder/hyperv/common/floppy_config_test.go | 18 +++ builder/hyperv/common/run_config.go | 29 +++++ builder/hyperv/common/run_config_test.go | 37 ++++++ builder/hyperv/common/shutdown_config.go | 30 +++++ builder/hyperv/common/shutdown_config_test.go | 45 +++++++ builder/hyperv/common/step_shutdown.go | 37 +----- builder/hyperv/iso/builder.go | 115 +++++++++++------- .../build-and-deploy.sh | 0 plugin/builder-hyperv-iso/main.go | 15 +++ .../main_test.go | 0 plugin/packer-builder-hyperv-iso/main.go | 19 --- 12 files changed, 266 insertions(+), 98 deletions(-) create mode 100644 builder/hyperv/common/floppy_config.go create mode 100644 builder/hyperv/common/floppy_config_test.go create mode 100644 builder/hyperv/common/run_config.go create mode 100644 builder/hyperv/common/run_config_test.go create mode 100644 builder/hyperv/common/shutdown_config.go create mode 100644 builder/hyperv/common/shutdown_config_test.go rename plugin/{packer-builder-hyperv-iso => builder-hyperv-iso}/build-and-deploy.sh (100%) create mode 100644 plugin/builder-hyperv-iso/main.go rename plugin/{packer-builder-hyperv-iso => builder-hyperv-iso}/main_test.go (100%) delete mode 100644 plugin/packer-builder-hyperv-iso/main.go diff --git a/builder/hyperv/common/floppy_config.go b/builder/hyperv/common/floppy_config.go new file mode 100644 index 000000000..d656e103a --- /dev/null +++ b/builder/hyperv/common/floppy_config.go @@ -0,0 +1,19 @@ +package common + +import ( + "github.com/mitchellh/packer/template/interpolate" +) + +// FloppyConfig is configuration related to created floppy disks and attaching +// them to a Parallels virtual machine. +type FloppyConfig struct { + FloppyFiles []string `mapstructure:"floppy_files"` +} + +func (c *FloppyConfig) Prepare(ctx *interpolate.Context) []error { + if c.FloppyFiles == nil { + c.FloppyFiles = make([]string, 0) + } + + return nil +} diff --git a/builder/hyperv/common/floppy_config_test.go b/builder/hyperv/common/floppy_config_test.go new file mode 100644 index 000000000..3e4fc55db --- /dev/null +++ b/builder/hyperv/common/floppy_config_test.go @@ -0,0 +1,18 @@ +package common + +import ( + "testing" +) + +func TestFloppyConfigPrepare(t *testing.T) { + c := new(FloppyConfig) + + errs := c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("err: %#v", errs) + } + + if len(c.FloppyFiles) > 0 { + t.Fatal("should not have floppy files") + } +} diff --git a/builder/hyperv/common/run_config.go b/builder/hyperv/common/run_config.go new file mode 100644 index 000000000..c755cdafb --- /dev/null +++ b/builder/hyperv/common/run_config.go @@ -0,0 +1,29 @@ +package common + +import ( + "fmt" + "time" + + "github.com/mitchellh/packer/template/interpolate" +) + +type RunConfig struct { + Headless bool `mapstructure:"headless"` + RawBootWait string `mapstructure:"boot_wait"` + + BootWait time.Duration `` +} + +func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { + if c.RawBootWait == "" { + c.RawBootWait = "10s" + } + + var err error + c.BootWait, err = time.ParseDuration(c.RawBootWait) + if err != nil { + return []error{fmt.Errorf("Failed parsing boot_wait: %s", err)} + } + + return nil +} diff --git a/builder/hyperv/common/run_config_test.go b/builder/hyperv/common/run_config_test.go new file mode 100644 index 000000000..8068fe625 --- /dev/null +++ b/builder/hyperv/common/run_config_test.go @@ -0,0 +1,37 @@ +package common + +import ( + "testing" +) + +func TestRunConfigPrepare_BootWait(t *testing.T) { + var c *RunConfig + var errs []error + + // Test a default boot_wait + c = new(RunConfig) + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", 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.Fatalf("bad: %#v", errs) + } + + // Test with a good one + c = new(RunConfig) + c.RawBootWait = "5s" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("should not have error: %s", errs) + } +} diff --git a/builder/hyperv/common/shutdown_config.go b/builder/hyperv/common/shutdown_config.go new file mode 100644 index 000000000..83d2224c3 --- /dev/null +++ b/builder/hyperv/common/shutdown_config.go @@ -0,0 +1,30 @@ +package common + +import ( + "fmt" + "time" + + "github.com/mitchellh/packer/template/interpolate" +) + +type ShutdownConfig struct { + ShutdownCommand string `mapstructure:"shutdown_command"` + RawShutdownTimeout string `mapstructure:"shutdown_timeout"` + + ShutdownTimeout time.Duration `` +} + +func (c *ShutdownConfig) Prepare(ctx *interpolate.Context) []error { + if c.RawShutdownTimeout == "" { + c.RawShutdownTimeout = "5m" + } + + var errs []error + var err error + c.ShutdownTimeout, err = time.ParseDuration(c.RawShutdownTimeout) + if err != nil { + errs = append(errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err)) + } + + return errs +} diff --git a/builder/hyperv/common/shutdown_config_test.go b/builder/hyperv/common/shutdown_config_test.go new file mode 100644 index 000000000..5da613a19 --- /dev/null +++ b/builder/hyperv/common/shutdown_config_test.go @@ -0,0 +1,45 @@ +package common + +import ( + "testing" + "time" +) + +func testShutdownConfig() *ShutdownConfig { + return &ShutdownConfig{} +} + +func TestShutdownConfigPrepare_ShutdownCommand(t *testing.T) { + var c *ShutdownConfig + var errs []error + + c = testShutdownConfig() + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("err: %#v", errs) + } +} + +func TestShutdownConfigPrepare_ShutdownTimeout(t *testing.T) { + var c *ShutdownConfig + var errs []error + + // Test with a bad value + c = testShutdownConfig() + c.RawShutdownTimeout = "this is not good" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) == 0 { + t.Fatalf("should have error") + } + + // Test with a good one + c = testShutdownConfig() + c.RawShutdownTimeout = "5s" + errs = c.Prepare(testConfigTemplate(t)) + if len(errs) > 0 { + t.Fatalf("err: %#v", errs) + } + if c.ShutdownTimeout != 5*time.Second { + t.Fatalf("bad: %s", c.ShutdownTimeout) + } +} diff --git a/builder/hyperv/common/step_shutdown.go b/builder/hyperv/common/step_shutdown.go index db80e7c56..62bbab21f 100644 --- a/builder/hyperv/common/step_shutdown.go +++ b/builder/hyperv/common/step_shutdown.go @@ -10,41 +10,6 @@ import ( "time" ) -type ShutdownConfig struct { - ShutdownCommand string `mapstructure:"shutdown_command"` - RawShutdownTimeout string `mapstructure:"shutdown_timeout"` - - ShutdownTimeout time.Duration `` -} - -func (c *ShutdownConfig) Prepare(t *packer.ConfigTemplate) []error { - if c.RawShutdownTimeout == "" { - c.RawShutdownTimeout = "5m" - } - - templates := map[string]*string{ - "shutdown_command": &c.ShutdownCommand, - "shutdown_timeout": &c.RawShutdownTimeout, - } - - errs := make([]error, 0) - for n, ptr := range templates { - var err error - *ptr, err = t.Process(*ptr, nil) - if err != nil { - errs = append(errs, fmt.Errorf("Error processing %s: %s", n, err)) - } - } - - var err error - c.ShutdownTimeout, err = time.ParseDuration(c.RawShutdownTimeout) - if err != nil { - errs = append(errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err)) - } - - return errs -} - // This step shuts down the machine. It first attempts to do so gracefully, // but ultimately forcefully shuts it down if that fails. // @@ -104,7 +69,7 @@ func (s *StepShutdown) Run(state multistep.StateBag) multistep.StepAction { if stderr.Len() > 0 { log.Printf("Shutdown stderr: %s", stderr.String()) } - + // Wait for the machine to actually shut down log.Printf("Waiting max %s for shutdown to complete", s.Timeout) shutdownTimer := time.After(s.Timeout) diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index 60bd5f77f..2e4723a91 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -11,13 +11,14 @@ import ( "github.com/mitchellh/multistep" hypervcommon "github.com/mitchellh/packer/builder/hyperv/common" "github.com/mitchellh/packer/common" - "github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/helper/communicator" + "github.com/mitchellh/packer/helper/config" + "github.com/mitchellh/packer/packer" powershell "github.com/mitchellh/packer/powershell" "github.com/mitchellh/packer/powershell/hyperv" + "github.com/mitchellh/packer/template/interpolate" "log" "os" - "regexp" "strings" "time" ) @@ -40,11 +41,18 @@ const ( // Builder implements packer.Builder and builds the actual Hyperv // images. type Builder struct { - config config + config Config runner multistep.Runner } -type config struct { +type Config struct { + common.PackerConfig `mapstructure:",squash"` + hypervcommon.FloppyConfig `mapstructure:",squash"` + hypervcommon.OutputConfig `mapstructure:",squash"` + hypervcommon.SSHConfig `mapstructure:",squash"` + hypervcommon.ShutdownConfig `mapstructure:",squash"` + hypervcommon.RunConfig `mapstructure:",squash"` + // The size, in megabytes, of the hard disk to create for the VM. // By default, this is 130048 (about 127 GB). DiskSize uint `mapstructure:"disk_size"` @@ -87,11 +95,6 @@ type config struct { // By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. VMName string `mapstructure:"vm_name"` - common.PackerConfig `mapstructure:",squash"` - hypervcommon.OutputConfig `mapstructure:",squash"` - hypervcommon.SSHConfig `mapstructure:",squash"` - hypervcommon.ShutdownConfig `mapstructure:",squash"` - SwitchName string `mapstructure:"switch_name"` Communicator string `mapstructure:"communicator"` @@ -102,32 +105,28 @@ type config struct { SSHWaitTimeout time.Duration - tpl *packer.ConfigTemplate + ctx interpolate.Context } // Prepare processes the build configuration parameters. func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - - md, err := common.DecodeConfig(&b.config, raws...) + err := config.Decode(&b.config, &config.DecodeOpts{ + Interpolate: true, + InterpolateFilter: &interpolate.RenderFilter{ + Exclude: []string{}, + }, + }, raws...) if err != nil { return nil, err } - b.config.tpl, err = packer.NewConfigTemplate() - if err != nil { - return nil, err - } - - log.Println(fmt.Sprintf("%s: %v", "PackerUserVars", b.config.PackerUserVars)) - - b.config.tpl.UserVars = b.config.PackerUserVars - // Accumulate any errors and warnings - errs := common.CheckUnusedConfig(md) - errs = packer.MultiErrorAppend(errs, b.config.OutputConfig.Prepare(b.config.tpl, &b.config.PackerConfig)...) - errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(b.config.tpl)...) - errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(b.config.tpl)...) - + var errs *packer.MultiError + errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...) + errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) + errs = packer.MultiErrorAppend(errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...) + errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...) + errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...) warnings := make([]string, 0) err = b.checkDiskSize() @@ -144,6 +143,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.VMName = fmt.Sprintf("pvm_%s", uuid.New()) } + log.Println(fmt.Sprintf("%s: %v", "VMName", b.config.VMName)) + if b.config.SwitchName == "" { // no switch name, try to get one attached to a online network adapter onlineSwitchName, err := hyperv.GetExternalOnlineVirtualSwitch() @@ -155,6 +156,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } log.Println(fmt.Sprintf("Using switch %s", b.config.SwitchName)) + log.Println(fmt.Sprintf("%s: %v", "SwitchName", b.config.SwitchName)) if b.config.Communicator == "" { b.config.Communicator = "ssh" @@ -165,22 +167,47 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, err) } - // Errors - templates := map[string]*string{ - "iso_url": &b.config.RawSingleISOUrl - } + log.Println(fmt.Sprintf("%s: %v", "Communicator", b.config.Communicator)) - for n, ptr := range templates { - var err error - *ptr, err = b.config.tpl.Process(*ptr, nil) - if err != nil { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error processing %s: %s", n, err)) + // Errors + if b.config.ISOChecksumType == "" { + errs = packer.MultiErrorAppend( + errs, errors.New("The iso_checksum_type must be specified.")) + } else { + b.config.ISOChecksumType = strings.ToLower(b.config.ISOChecksumType) + if b.config.ISOChecksumType != "none" { + if b.config.ISOChecksum == "" { + errs = packer.MultiErrorAppend( + errs, errors.New("Due to large file sizes, an iso_checksum is required")) + } else { + b.config.ISOChecksum = strings.ToLower(b.config.ISOChecksum) + } + + if h := common.HashForType(b.config.ISOChecksumType); h == nil { + errs = packer.MultiErrorAppend( + errs, + fmt.Errorf("Unsupported checksum type: %s", b.config.ISOChecksumType)) + } } } - log.Println(fmt.Sprintf("%s: %v", "VMName", b.config.VMName)) - log.Println(fmt.Sprintf("%s: %v", "SwitchName", b.config.SwitchName)) - log.Println(fmt.Sprintf("%s: %v", "Communicator", b.config.Communicator)) + if b.config.RawSingleISOUrl == "" && len(b.config.ISOUrls) == 0 { + errs = packer.MultiErrorAppend( + errs, errors.New("One of iso_url or iso_urls must be specified.")) + } else if b.config.RawSingleISOUrl != "" && len(b.config.ISOUrls) > 0 { + errs = packer.MultiErrorAppend( + errs, errors.New("Only one of iso_url or iso_urls may be specified.")) + } else if b.config.RawSingleISOUrl != "" { + b.config.ISOUrls = []string{b.config.RawSingleISOUrl} + } + + for i, url := range b.config.ISOUrls { + b.config.ISOUrls[i], err = common.DownloadableURL(url) + if err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Failed to parse iso_url %d: %s", i+1, err)) + } + } if b.config.RawSingleISOUrl == "" { errs = packer.MultiErrorAppend(errs, errors.New("iso_url: The option can't be missed and a path must be specified.")) @@ -190,14 +217,18 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { log.Println(fmt.Sprintf("%s: %v", "RawSingleISOUrl", b.config.RawSingleISOUrl)) - b.config.SSHWaitTimeout, err = time.ParseDuration(b.config.RawSSHWaitTimeout) - // Warnings warning := b.checkHostAvailableMemory() if warning != "" { warnings = appendWarnings(warnings, warning) } + if b.config.ISOChecksumType == "none" { + warnings = append(warnings, + "A checksum type of 'none' was specified. Since ISO files are so big,\n"+ + "a checksum is highly recommended.") + } + if b.config.ShutdownCommand == "" { warnings = append(warnings, "A shutdown_command was not specified. Without a shutdown command, Packer\n"+ @@ -254,7 +285,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &hypervcommon.StepMountSecondaryDvdImages{}, - &hypervcommon.StepStartVm{ Reason: "OS installation", }, @@ -277,7 +307,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Config: &b.config.SSHConfig.Comm, Host: hypervcommon.CommHost, SSHConfig: hypervcommon.SSHConfigFunc(b.config.SSHConfig), - SSHPort: hypervcommon.SSHPort, }, // provision requires communicator to be setup @@ -388,4 +417,4 @@ func (b *Builder) checkHostAvailableMemory() string { } return "" -} \ No newline at end of file +} diff --git a/plugin/packer-builder-hyperv-iso/build-and-deploy.sh b/plugin/builder-hyperv-iso/build-and-deploy.sh similarity index 100% rename from plugin/packer-builder-hyperv-iso/build-and-deploy.sh rename to plugin/builder-hyperv-iso/build-and-deploy.sh diff --git a/plugin/builder-hyperv-iso/main.go b/plugin/builder-hyperv-iso/main.go new file mode 100644 index 000000000..664615049 --- /dev/null +++ b/plugin/builder-hyperv-iso/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/mitchellh/packer/builder/hyperv/iso" + "github.com/mitchellh/packer/packer/plugin" +) + +func main() { + server, err := plugin.Server() + if err != nil { + panic(err) + } + server.RegisterBuilder(new(iso.Builder)) + server.Serve() +} diff --git a/plugin/packer-builder-hyperv-iso/main_test.go b/plugin/builder-hyperv-iso/main_test.go similarity index 100% rename from plugin/packer-builder-hyperv-iso/main_test.go rename to plugin/builder-hyperv-iso/main_test.go diff --git a/plugin/packer-builder-hyperv-iso/main.go b/plugin/packer-builder-hyperv-iso/main.go deleted file mode 100644 index c7be7a1ed..000000000 --- a/plugin/packer-builder-hyperv-iso/main.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. -// All Rights Reserved. -// Licensed under the Apache License, Version 2.0. -// See License.txt in the project root for license information. -package main - -import ( - "github.com/mitchellh/packer/builder/hyperv/iso" - "github.com/mitchellh/packer/plugin" -) - -func main() { - server, err := plugin.Server() - if err != nil { - panic(err) - } - server.RegisterBuilder(new(iso.Builder)) - server.Serve() -}