From 015933b29fbbec547dfa699a6d164e9929ef3868 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 15 Aug 2013 15:02:01 -0400 Subject: [PATCH] builder/vmware: support iso_urls for multiple URLs [GH-110] --- CHANGELOG.md | 2 ++ builder/vmware/builder.go | 33 +++++++++++++++----- builder/vmware/builder_test.go | 56 ++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36d437738..0a627ed15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ FEATURES: file. * builder/virtualbox: You may now specify multiple URLs for an ISO using "iso_url" in a template. The URLs will be tried in order. +* builder/vmware: You may now specify multiple URLs for an ISO + using "iso_url" in a template. The URLs will be tried in order. IMPROVEMENTS: diff --git a/builder/vmware/builder.go b/builder/vmware/builder.go index 4ea3ccab9..236992e28 100644 --- a/builder/vmware/builder.go +++ b/builder/vmware/builder.go @@ -31,7 +31,7 @@ type config struct { GuestOSType string `mapstructure:"guest_os_type"` ISOChecksum string `mapstructure:"iso_checksum"` ISOChecksumType string `mapstructure:"iso_checksum_type"` - ISOUrl string `mapstructure:"iso_url"` + ISOUrls []string `mapstructure:"iso_urls"` VMName string `mapstructure:"vm_name"` OutputDir string `mapstructure:"output_directory"` Headless bool `mapstructure:"headless"` @@ -51,6 +51,7 @@ type config struct { VNCPortMax uint `mapstructure:"vnc_port_max"` RawBootWait string `mapstructure:"boot_wait"` + RawSingleISOUrl string `mapstructure:"iso_url"` RawShutdownTimeout string `mapstructure:"shutdown_timeout"` RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"` @@ -134,7 +135,7 @@ func (b *Builder) Prepare(raws ...interface{}) error { "http_directory": &b.config.HTTPDir, "iso_checksum": &b.config.ISOChecksum, "iso_checksum_type": &b.config.ISOChecksumType, - "iso_url": &b.config.ISOUrl, + "iso_url": &b.config.RawSingleISOUrl, "output_directory": &b.config.OutputDir, "shutdown_command": &b.config.ShutdownCommand, "ssh_password": &b.config.SSHPassword, @@ -155,6 +156,15 @@ func (b *Builder) Prepare(raws ...interface{}) error { } } + for i, url := range b.config.ISOUrls { + var err error + b.config.ISOUrls[i], err = b.config.tpl.Process(url, nil) + if err != nil { + errs = packer.MultiErrorAppend( + errs, fmt.Errorf("Error processing iso_urls[%d]: %s", i, err)) + } + } + for i, command := range b.config.BootCommand { if err := b.config.tpl.Validate(command); err != nil { errs = packer.MultiErrorAppend(errs, @@ -217,14 +227,21 @@ func (b *Builder) Prepare(raws ...interface{}) error { } } - if b.config.ISOUrl == "" { + if b.config.RawSingleISOUrl == "" && len(b.config.ISOUrls) == 0 { errs = packer.MultiErrorAppend( - errs, errors.New("An iso_url must be specified.")) - } else { - b.config.ISOUrl, err = common.DownloadableURL(b.config.ISOUrl) + 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("iso_url: %s", err)) + errs, fmt.Errorf("Failed to parse iso_url %d: %s", i+1, err)) } } @@ -303,7 +320,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ChecksumType: b.config.ISOChecksumType, Description: "ISO", ResultKey: "iso_path", - Url: []string{b.config.ISOUrl}, + Url: b.config.ISOUrls, }, &stepPrepareOutputDir{}, &common.StepCreateFloppy{ diff --git a/builder/vmware/builder_test.go b/builder/vmware/builder_test.go index 4ad6c3ee7..1e46beaec 100644 --- a/builder/vmware/builder_test.go +++ b/builder/vmware/builder_test.go @@ -52,6 +52,7 @@ func TestBuilderPrepare_BootWait(t *testing.T) { // Test with a good one config["boot_wait"] = "5s" + b = Builder{} err = b.Prepare(config) if err != nil { t.Fatalf("should not have error: %s", err) @@ -71,6 +72,7 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) { // Test good config["iso_checksum"] = "FOo" + b = Builder{} err = b.Prepare(config) if err != nil { t.Fatalf("should not have error: %s", err) @@ -94,6 +96,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) { // Test good config["iso_checksum_type"] = "mD5" + b = Builder{} err = b.Prepare(config) if err != nil { t.Fatalf("should not have error: %s", err) @@ -105,6 +108,7 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) { // Test unknown config["iso_checksum_type"] = "fake" + b = Builder{} err = b.Prepare(config) if err == nil { t.Fatal("should have error") @@ -202,6 +206,7 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) { // Bad config["http_port_min"] = -500 + b = Builder{} err = b.Prepare(config) if err == nil { t.Fatal("should have error") @@ -210,6 +215,7 @@ func TestBuilderPrepare_HTTPPort(t *testing.T) { // Good config["http_port_min"] = 500 config["http_port_max"] = 1000 + b = Builder{} err = b.Prepare(config) if err != nil { t.Fatalf("should not have error: %s", err) @@ -231,18 +237,59 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) { func TestBuilderPrepare_ISOUrl(t *testing.T) { var b Builder config := testConfig() + delete(config, "iso_url") + delete(config, "iso_urls") + // Test both epty config["iso_url"] = "" + b = Builder{} err := b.Prepare(config) if err == nil { t.Fatal("should have error") } + // Test iso_url set config["iso_url"] = "http://www.packer.io" + b = Builder{} err = b.Prepare(config) if err != nil { t.Errorf("should not have error: %s", err) } + + expected := []string{"http://www.packer.io"} + if !reflect.DeepEqual(b.config.ISOUrls, expected) { + t.Fatalf("bad: %#v", b.config.ISOUrls) + } + + // Test both set + config["iso_url"] = "http://www.packer.io" + config["iso_urls"] = []string{"http://www.packer.io"} + b = Builder{} + err = b.Prepare(config) + if err == nil { + t.Fatal("should have error") + } + + // Test just iso_urls set + delete(config, "iso_url") + config["iso_urls"] = []string{ + "http://www.packer.io", + "http://www.hashicorp.com", + } + + b = Builder{} + err = b.Prepare(config) + if err != nil { + t.Errorf("should not have error: %s", err) + } + + expected = []string{ + "http://www.packer.io", + "http://www.hashicorp.com", + } + if !reflect.DeepEqual(b.config.ISOUrls, expected) { + t.Fatalf("bad: %#v", b.config.ISOUrls) + } } func TestBuilderPrepare_OutputDir(t *testing.T) { @@ -257,6 +304,7 @@ func TestBuilderPrepare_OutputDir(t *testing.T) { defer os.RemoveAll(dir) config["output_directory"] = dir + b = Builder{} err = b.Prepare(config) if err == nil { t.Fatal("should have error") @@ -264,6 +312,7 @@ func TestBuilderPrepare_OutputDir(t *testing.T) { // Test with a good one config["output_directory"] = "i-hope-i-dont-exist" + b = Builder{} err = b.Prepare(config) if err != nil { t.Fatalf("should not have error: %s", err) @@ -283,6 +332,7 @@ func TestBuilderPrepare_ShutdownTimeout(t *testing.T) { // Test with a good one config["shutdown_timeout"] = "5s" + b = Builder{} err = b.Prepare(config) if err != nil { t.Fatalf("should not have error: %s", err) @@ -300,6 +350,7 @@ func TestBuilderPrepare_SSHUser(t *testing.T) { } config["ssh_username"] = "exists" + b = Builder{} err = b.Prepare(config) if err != nil { t.Fatalf("should not have error: %s", err) @@ -347,6 +398,7 @@ func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) { // Test with a good one config["ssh_wait_timeout"] = "5s" + b = Builder{} err = b.Prepare(config) if err != nil { t.Fatalf("should not have error: %s", err) @@ -370,6 +422,7 @@ func TestBuilderPrepare_ToolsUploadPath(t *testing.T) { // Test with a bad value config["tools_upload_path"] = "{{{nope}" + b = Builder{} err = b.Prepare(config) if err == nil { t.Fatal("should have error") @@ -377,6 +430,7 @@ func TestBuilderPrepare_ToolsUploadPath(t *testing.T) { // Test with a good one config["tools_upload_path"] = "hey" + b = Builder{} err = b.Prepare(config) if err != nil { t.Fatalf("should not have error: %s", err) @@ -397,6 +451,7 @@ func TestBuilderPrepare_VNCPort(t *testing.T) { // Bad config["vnc_port_min"] = -500 + b = Builder{} err = b.Prepare(config) if err == nil { t.Fatal("should have error") @@ -405,6 +460,7 @@ func TestBuilderPrepare_VNCPort(t *testing.T) { // Good config["vnc_port_min"] = 500 config["vnc_port_max"] = 1000 + b = Builder{} err = b.Prepare(config) if err != nil { t.Fatalf("should not have error: %s", err)