From 46cfb5a30ce14c99db7896b5ae2bcdc9a8bee48c Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Mon, 23 Jan 2017 15:58:39 +0200 Subject: [PATCH 01/32] Move driver_esx5 to commons and allow it to be instantiated wtih NewDriver() --- builder/vmware/common/driver.go | 75 +++++---- builder/vmware/common/driver_config.go | 11 +- builder/vmware/{iso => common}/driver_esx5.go | 150 ++++++++++-------- builder/vmware/common/driver_esx5_test.go | 97 +++++++++++ builder/vmware/iso/driver.go | 2 +- 5 files changed, 234 insertions(+), 101 deletions(-) rename builder/vmware/{iso => common}/driver_esx5.go (89%) create mode 100644 builder/vmware/common/driver_esx5_test.go diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index ec34e70b5..e65e429b9 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -83,44 +83,61 @@ type Driver interface { // system, or an error if the driver couldn't be initialized. func NewDriver(dconfig *DriverConfig, config *SSHConfig) (Driver, error) { drivers := []Driver{} - - switch runtime.GOOS { - case "darwin": + log.Printf("**** NewDriver()") + if dconfig.RemoteType != "" { + log.Printf("**** Creating the remote driver.") drivers = []Driver{ - &Fusion6Driver{ - Fusion5Driver: Fusion5Driver{ + &ESX5Driver{ + Host: dconfig.RemoteHost, + Port: dconfig.RemotePort, + Username: dconfig.RemoteUser, + Password: dconfig.RemotePassword, + PrivateKey: dconfig.RemotePrivateKey, + Datastore: dconfig.RemoteDatastore, + CacheDatastore: dconfig.RemoteCacheDatastore, + CacheDirectory: dconfig.RemoteCacheDirectory, + }, + } + + } else { + switch runtime.GOOS { + case "darwin": + drivers = []Driver{ + &Fusion6Driver{ + Fusion5Driver: Fusion5Driver{ + AppPath: dconfig.FusionAppPath, + SSHConfig: config, + }, + }, + &Fusion5Driver{ AppPath: dconfig.FusionAppPath, SSHConfig: config, }, - }, - &Fusion5Driver{ - AppPath: dconfig.FusionAppPath, - SSHConfig: config, - }, - } - case "linux": - fallthrough - case "windows": - drivers = []Driver{ - &Workstation10Driver{ - Workstation9Driver: Workstation9Driver{ + } + case "linux": + fallthrough + case "windows": + drivers = []Driver{ + &Workstation10Driver{ + Workstation9Driver: Workstation9Driver{ + SSHConfig: config, + }, + }, + &Workstation9Driver{ SSHConfig: config, }, - }, - &Workstation9Driver{ - SSHConfig: config, - }, - &Player6Driver{ - Player5Driver: Player5Driver{ + &Player6Driver{ + Player5Driver: Player5Driver{ + SSHConfig: config, + }, + }, + &Player5Driver{ SSHConfig: config, }, - }, - &Player5Driver{ - SSHConfig: config, - }, + } + default: + return nil, fmt.Errorf("can't find driver for OS: %s", runtime.GOOS) } - default: - return nil, fmt.Errorf("can't find driver for OS: %s", runtime.GOOS) } errs := "" diff --git a/builder/vmware/common/driver_config.go b/builder/vmware/common/driver_config.go index 13c9e96ac..a58070502 100644 --- a/builder/vmware/common/driver_config.go +++ b/builder/vmware/common/driver_config.go @@ -7,7 +7,16 @@ import ( ) type DriverConfig struct { - FusionAppPath string `mapstructure:"fusion_app_path"` + FusionAppPath string `mapstructure:"fusion_app_path"` + RemoteType string `mapstructure:"remote_type"` + RemoteDatastore string `mapstructure:"remote_datastore"` + RemoteCacheDatastore string `mapstructure:"remote_cache_datastore"` + RemoteCacheDirectory string `mapstructure:"remote_cache_directory"` + RemoteHost string `mapstructure:"remote_host"` + RemotePort uint `mapstructure:"remote_port"` + RemoteUser string `mapstructure:"remote_username"` + RemotePassword string `mapstructure:"remote_password"` + RemotePrivateKey string `mapstructure:"remote_private_key_file"` } func (c *DriverConfig) Prepare(ctx *interpolate.Context) []error { diff --git a/builder/vmware/iso/driver_esx5.go b/builder/vmware/common/driver_esx5.go similarity index 89% rename from builder/vmware/iso/driver_esx5.go rename to builder/vmware/common/driver_esx5.go index 2f37f60b9..8269dd8f3 100644 --- a/builder/vmware/iso/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -1,4 +1,4 @@ -package iso +package common import ( "bufio" @@ -43,7 +43,20 @@ type ESX5Driver struct { } func (d *ESX5Driver) Clone(dst, src string, linked bool) error { - return errors.New("Cloning is not supported with the ESX driver.") + ret, err := d.sh("test -r %s", src) + if err != nil { + return errors.New("Source VMX not found") + } + + ret, err = d.run(nil, "ls") + files := strings.Split(ret, "\n") + if err != nil { + return errors.New("Error running cmd") + } + for _, f := range files { + log.Printf("One file is: %s", f) + } + log.Printf("Return was: %s", ret) } func (d *ESX5Driver) CompactDisk(diskPathLocal string) error { @@ -375,74 +388,71 @@ func (ESX5Driver) UpdateVMX(_, password string, port uint, data map[string]strin } func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { - config := state.Get("config").(*Config) - sshc := config.SSHConfig.Comm - port := sshc.SSHPort - if sshc.Type == "winrm" { - port = sshc.WinRMPort - } - - if address := config.CommConfig.Host(); address != "" { - return address, nil - } - - r, err := d.esxcli("network", "vm", "list") - if err != nil { - return "", err - } - - // The value in the Name field returned by 'esxcli network vm list' - // corresponds directly to the value of displayName set in the VMX file - var displayName string - if v, ok := state.GetOk("display_name"); ok { - displayName = v.(string) - } - record, err := r.find("Name", displayName) - if err != nil { - return "", err - } - wid := record["WorldID"] - if wid == "" { - return "", errors.New("VM WorldID not found") - } - - r, err = d.esxcli("network", "vm", "port", "list", "-w", wid) - if err != nil { - return "", err - } - - // Loop through interfaces - for { - record, err = r.read() - if err == io.EOF { - break - } - if err != nil { - return "", err - } - - if record["IPAddress"] == "0.0.0.0" { - continue - } - // When multiple NICs are connected to the same network, choose - // one that has a route back. This Dial should ensure that. - conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", record["IPAddress"], port), 2*time.Second) - if err != nil { - if e, ok := err.(*net.OpError); ok { - if e.Timeout() { - log.Printf("Timeout connecting to %s", record["IPAddress"]) - continue - } else if strings.Contains(e.Error(), "connection refused") { - log.Printf("Connection refused when connecting to: %s", record["IPAddress"]) - continue - } - } - } else { - defer conn.Close() - address := record["IPAddress"] - return address, nil - } - } + // config := state.Get("config").(*Config) + // sshc := config.SSHConfig.Comm + // port := sshc.SSHPort + // if sshc.Type == "winrm" { + // port = sshc.WinRMPort + // } + // + // if address, ok := state.GetOk("vm_address"); ok { + // return address.(string), nil + // } + // + // if address := config.CommConfig.Host(); address != "" { + // state.Put("vm_address", address) + // return address, nil + // } + // + // r, err := d.esxcli("network", "vm", "list") + // if err != nil { + // return "", err + // } + // + // record, err := r.find("Name", config.VMName) + // if err != nil { + // return "", err + // } + // wid := record["WorldID"] + // if wid == "" { + // return "", errors.New("VM WorldID not found") + // } + // + // r, err = d.esxcli("network", "vm", "port", "list", "-w", wid) + // if err != nil { + // return "", err + // } + // + // // Loop through interfaces + // for { + // record, err = r.read() + // if err == io.EOF { + // break + // } + // if err != nil { + // return "", err + // } + // + // if record["IPAddress"] == "0.0.0.0" { + // continue + // } + // // When multiple NICs are connected to the same network, choose + // // one that has a route back. This Dial should ensure that. + // conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", record["IPAddress"], port), 2*time.Second) + // if err != nil { + // if e, ok := err.(*net.OpError); ok { + // if e.Timeout() { + // log.Printf("Timeout connecting to %s", record["IPAddress"]) + // continue + // } + // } + // } else { + // defer conn.Close() + // address := record["IPAddress"] + // state.Put("vm_address", address) + // return address, nil + // } + // } return "", errors.New("No interface on the VM has an IP address ready") } diff --git a/builder/vmware/common/driver_esx5_test.go b/builder/vmware/common/driver_esx5_test.go new file mode 100644 index 000000000..2fec8ee7f --- /dev/null +++ b/builder/vmware/common/driver_esx5_test.go @@ -0,0 +1,97 @@ +package common + +import ( + "fmt" + "net" + "testing" + + "github.com/mitchellh/multistep" +) + +func TestESX5Driver_implDriver(t *testing.T) { + var _ Driver = new(ESX5Driver) +} + +func TestESX5Driver_UpdateVMX(t *testing.T) { + var driver ESX5Driver + data := make(map[string]string) + driver.UpdateVMX("0.0.0.0", "", 5900, data) + if _, ok := data["remotedisplay.vnc.ip"]; ok { + // Do not add the remotedisplay.vnc.ip on ESXi + t.Fatal("invalid VMX data key: remotedisplay.vnc.ip") + } + if enabled := data["remotedisplay.vnc.enabled"]; enabled != "TRUE" { + t.Errorf("bad VMX data for key remotedisplay.vnc.enabled: %v", enabled) + } + if port := data["remotedisplay.vnc.port"]; port != fmt.Sprint(port) { + t.Errorf("bad VMX data for key remotedisplay.vnc.port: %v", port) + } +} + +func TestESX5Driver_implOutputDir(t *testing.T) { + var _ OutputDir = new(ESX5Driver) +} + +func TestESX5Driver_implVNCAddressFinder(t *testing.T) { + var _ VNCAddressFinder = new(ESX5Driver) +} + +func TestESX5Driver_implRemoteDriver(t *testing.T) { + var _ RemoteDriver = new(ESX5Driver) +} + +func TestESX5Driver_HostIP(t *testing.T) { + expected_host := "127.0.0.1" + + //create mock SSH server + listen, _ := net.Listen("tcp", fmt.Sprintf("%s:0", expected_host)) + port := listen.Addr().(*net.TCPAddr).Port + defer listen.Close() + + driver := ESX5Driver{Host: "localhost", Port: uint(port)} + + if host, _ := driver.HostIP(); host != expected_host { + t.Error(fmt.Sprintf("Expected string, %s but got %s", expected_host, host)) + } +} + +func TestESX5Driver_CommHost(t *testing.T) { + const expected_host = "127.0.0.1" + + config := testConfig() + config["communicator"] = "winrm" + config["winrm_username"] = "username" + config["winrm_password"] = "password" + config["winrm_host"] = expected_host + + var b Builder + warns, err := b.Prepare(config) + if len(warns) > 0 { + t.Fatalf("bad: %#v", warns) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + if host := b.config.CommConfig.Host(); host != expected_host { + t.Fatalf("setup failed, bad host name: %s", host) + } + + state := new(multistep.BasicStateBag) + state.Put("config", &b.config) + + var driver ESX5Driver + host, err := driver.CommHost(state) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + if host != expected_host { + t.Errorf("bad host name: %s", host) + } + address, ok := state.GetOk("vm_address") + if !ok { + t.Error("state not updated with vm_address") + } + if address.(string) != expected_host { + t.Errorf("bad vm_address: %s", address.(string)) + } +} diff --git a/builder/vmware/iso/driver.go b/builder/vmware/iso/driver.go index f461c0927..6580c3108 100644 --- a/builder/vmware/iso/driver.go +++ b/builder/vmware/iso/driver.go @@ -16,7 +16,7 @@ func NewDriver(config *Config) (vmwcommon.Driver, error) { } drivers = []vmwcommon.Driver{ - &ESX5Driver{ + &vmwcommon.ESX5Driver{ Host: config.RemoteHost, Port: config.RemotePort, Username: config.RemoteUser, From 91c70894555dbaa5ea415e7f3abde3dedd8cf4eb Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Tue, 24 Jan 2017 10:24:53 +0200 Subject: [PATCH 02/32] Implement the first version of the remote Clone(). --- builder/vmware/common/driver_esx5.go | 92 +++++++++++++++++++++++++--- builder/vmware/vmx/config.go | 7 ++- 2 files changed, 90 insertions(+), 9 deletions(-) diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index 8269dd8f3..1d389e434 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -7,9 +7,11 @@ import ( "errors" "fmt" "io" + "io/ioutil" "log" "net" "os" + "path" "path/filepath" "strconv" "strings" @@ -43,20 +45,87 @@ type ESX5Driver struct { } func (d *ESX5Driver) Clone(dst, src string, linked bool) error { - ret, err := d.sh("test -r %s", src) + + linesToArray := func(lines string) []string { return strings.Split(strings.Trim(lines, "\n"), "\n") } + + err := d.sh("test -r", src) if err != nil { return errors.New("Source VMX not found") } - ret, err = d.run(nil, "ls") - files := strings.Split(ret, "\n") + vmName := strings.TrimSuffix(path.Base(src), ".vmx") + srcDir := path.Dir(src) + dstDir := path.Join(path.Dir(srcDir), path.Dir(dst)) + dstVmx := path.Join(dstDir, path.Base(dst)) + + log.Printf("Source: %s\n", src) + log.Printf("Dest: %s\n", dstVmx) + + err = d.sh("mkdir", dstDir) if err != nil { - return errors.New("Error running cmd") + return fmt.Errorf("Failed to create the destination directory %s: %s", dstDir, err) } - for _, f := range files { - log.Printf("One file is: %s", f) + + err = d.sh("cp", src, dstVmx) + if err != nil { + return fmt.Errorf("Failed to copy the vmx file %s: %s", src, err) } - log.Printf("Return was: %s", ret) + + filesToClone, err := d.run(nil, "find", srcDir, "! -name '*.vmdk' ! -name '*.vmx' -type f") + if err != nil { + return fmt.Errorf("Failed to get the file list to copy: %s", err) + } + + for _, f := range linesToArray(filesToClone) { + log.Printf("Copying file %s\n", f) + err := d.sh("cp", f, dstDir) + if err != nil { + return fmt.Errorf("Failing to copy %s to %s: %s", f, dstDir, err) + } + } + + disksToClone, err := d.run(nil, "sed -ne 's/.*file[Nn]ame = \"\\(.*vmdk\\)\"/\\1/p'", src) + if err != nil { + return fmt.Errorf("Failing to get the vmdk list to clone %s", err) + } + for _, disk := range linesToArray(disksToClone) { + srcDisk := path.Join(srcDir, disk) + if path.IsAbs(disk) { + srcDisk = disk + } + destDisk := path.Join(dstDir, path.Base(disk)) + err := d.sh("vmkfstools", "-d thin", "-i", srcDisk, destDisk) + if err != nil { + return fmt.Errorf("Failing to clone disk %s: %s", srcDisk, err) + } + } + + vmxDir, err = ioutil.TempDir("", "packer-vmx") + if err != nil { + err := fmt.Errorf("Error preparing VMX template: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + // Set the tempDir so we clean it up + s.tempDir = vmxDir + + // FIXME: VMName should be taken from the config. + vmxEdits := []string{ + "s/\\(display[Nn]ame = \\).*/\\1\"" + vmName + "\"/", + "/ethernet..generated[aA]ddress =/d", + "/uuid.bios =/d", + "/uuid.location =/d", + "/vc.uuid =/d", + } + for _, edit := range vmxEdits { + err := d.sh("sed -i -e", "'", edit, "'", dstVmx) + if err != nil { + return fmt.Errorf("Failed to edit the destination file %s: %s", dstVmx, err) + } + } + return nil } func (d *ESX5Driver) CompactDisk(diskPathLocal string) error { @@ -602,6 +671,15 @@ func (d *ESX5Driver) upload(dst, src string) error { return d.comm.Upload(dst, f, nil) } +func (d *ESX5Driver) download(dst, src string) error { + f, err := os.Open(dst) + if err != nil { + return err + } + defer f.Close() + return d.comm.Download(dst, f, nil) +} + func (d *ESX5Driver) verifyChecksum(ctype string, hash string, file string) bool { if ctype == "none" { if err := d.sh("stat", strconv.Quote(file)); err != nil { diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index 1e32ac7b7..bd25dc92a 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -2,6 +2,7 @@ package vmx import ( "fmt" + "log" "os" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" @@ -74,8 +75,10 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is blank, but is required")) } else { if _, err := os.Stat(c.SourcePath); err != nil { - errs = packer.MultiErrorAppend(errs, - fmt.Errorf("source_path is invalid: %s", err)) + // FIXME: + log.Printf("source_path is invalid: %s", err) + // errs = packer.MultiErrorAppend(errs, + // fmt.Errorf("source_path is invalid: %s", err)) } } From b5298464c5c99698d7d4d34c222be262c6a85cbc Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Fri, 27 Jan 2017 16:11:55 +0200 Subject: [PATCH 03/32] Move the remote_driver from iso to common. --- builder/vmware/common/driver_esx5.go | 98 +++++++++---------- .../vmware/{iso => common}/remote_driver.go | 16 +-- .../{iso => common}/remote_driver_mock.go | 8 +- .../vmware/common/remote_driver_mock_test.go | 10 ++ builder/vmware/common/step_configure_vmx.go | 20 +++- builder/vmware/iso/remote_driver_mock_test.go | 12 --- builder/vmware/iso/step_register.go | 4 +- builder/vmware/iso/step_remote_upload.go | 2 +- builder/vmware/iso/step_upload_vmx.go | 4 +- builder/vmware/vmx/step_clone_vmx.go | 32 +++++- 10 files changed, 118 insertions(+), 88 deletions(-) rename builder/vmware/{iso => common}/remote_driver.go (78%) rename builder/vmware/{iso => common}/remote_driver_mock.go (92%) create mode 100644 builder/vmware/common/remote_driver_mock_test.go delete mode 100644 builder/vmware/iso/remote_driver_mock_test.go diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index 1d389e434..deb5f5634 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "log" "net" "os" @@ -48,27 +47,23 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { linesToArray := func(lines string) []string { return strings.Split(strings.Trim(lines, "\n"), "\n") } - err := d.sh("test -r", src) - if err != nil { - return errors.New("Source VMX not found") - } + d.SetOutputDir(dst) + srcVmx := d.datastorePath(path.Dir(src)) + dstVmx := d.datastorePath(dst) + srcDir := path.Dir(srcVmx) + dstDir := path.Dir(dstVmx) - vmName := strings.TrimSuffix(path.Base(src), ".vmx") - srcDir := path.Dir(src) - dstDir := path.Join(path.Dir(srcDir), path.Dir(dst)) - dstVmx := path.Join(dstDir, path.Base(dst)) - - log.Printf("Source: %s\n", src) + log.Printf("Source: %s\n", srcVmx) log.Printf("Dest: %s\n", dstVmx) - err = d.sh("mkdir", dstDir) + err := d.sh("mkdir", dstDir) if err != nil { return fmt.Errorf("Failed to create the destination directory %s: %s", dstDir, err) } - err = d.sh("cp", src, dstVmx) + err = d.sh("cp", srcVmx, dstVmx) if err != nil { - return fmt.Errorf("Failed to copy the vmx file %s: %s", src, err) + return fmt.Errorf("Failed to copy the vmx file %s: %s", srcVmx, err) } filesToClone, err := d.run(nil, "find", srcDir, "! -name '*.vmdk' ! -name '*.vmx' -type f") @@ -84,7 +79,7 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { } } - disksToClone, err := d.run(nil, "sed -ne 's/.*file[Nn]ame = \"\\(.*vmdk\\)\"/\\1/p'", src) + disksToClone, err := d.run(nil, "sed -ne 's/.*file[Nn]ame = \"\\(.*vmdk\\)\"/\\1/p'", srcVmx) if err != nil { return fmt.Errorf("Failing to get the vmdk list to clone %s", err) } @@ -94,37 +89,28 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { srcDisk = disk } destDisk := path.Join(dstDir, path.Base(disk)) - err := d.sh("vmkfstools", "-d thin", "-i", srcDisk, destDisk) + err = d.sh("vmkfstools", "-d thin", "-i", srcDisk, destDisk) if err != nil { return fmt.Errorf("Failing to clone disk %s: %s", srcDisk, err) } } - vmxDir, err = ioutil.TempDir("", "packer-vmx") - if err != nil { - err := fmt.Errorf("Error preparing VMX template: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - // Set the tempDir so we clean it up - s.tempDir = vmxDir - - // FIXME: VMName should be taken from the config. - vmxEdits := []string{ - "s/\\(display[Nn]ame = \\).*/\\1\"" + vmName + "\"/", - "/ethernet..generated[aA]ddress =/d", - "/uuid.bios =/d", - "/uuid.location =/d", - "/vc.uuid =/d", - } - for _, edit := range vmxEdits { - err := d.sh("sed -i -e", "'", edit, "'", dstVmx) - if err != nil { - return fmt.Errorf("Failed to edit the destination file %s: %s", dstVmx, err) - } - } + // + // // FIXME: VMName should be taken from the config. + // vmxEdits := []string{ + // "s/\\(display[Nn]ame = \\).*/\\1\"" + vmName + "\"/", + // "/ethernet..generated[aA]ddress =/d", + // "/uuid.bios =/d", + // "/uuid.location =/d", + // "/vc.uuid =/d", + // } + // for _, edit := range vmxEdits { + // err := d.sh("sed -i -e", "'", edit, "'", dstVmx) + // if err != nil { + // return fmt.Errorf("Failed to edit the destination file %s: %s", dstVmx, err) + // } + // } + log.Printf("Successfully cloned %s to %s\n", src, dst) return nil } @@ -150,6 +136,21 @@ func (d *ESX5Driver) ReloadVM() error { return d.sh("vim-cmd", "vmsvc/reload", d.vmId) } +func (d *ESX5Driver) ReadFile(name string) ([]byte, error) { + var b bytes.Buffer + writer := bufio.NewWriter(&b) + err := d.comm.Download(d.datastorePath(name), writer) + if err != nil { + return nil, fmt.Errorf("Cant read remote file %s: %s", name, err) + } + writer.Flush() + return b.Bytes(), nil +} + +func (d *ESX5Driver) WriteFile(name string, content []byte) error { + return d.comm.Upload(d.datastorePath(name), bytes.NewReader(content), nil) +} + func (d *ESX5Driver) Start(vmxPathLocal string, headless bool) error { for i := 0; i < 20; i++ { //intentionally not checking for error since poweron may fail specially after initial VM registration @@ -172,7 +173,7 @@ func (d *ESX5Driver) Stop(vmxPathLocal string) error { func (d *ESX5Driver) Register(vmxPathLocal string) error { vmxPath := filepath.ToSlash(filepath.Join(d.outputDir, filepath.Base(vmxPathLocal))) - if err := d.upload(vmxPath, vmxPathLocal); err != nil { + if err := d.Upload(vmxPath, vmxPathLocal); err != nil { return err } r, err := d.run(nil, "vim-cmd", "solo/registervm", strconv.Quote(vmxPath)) @@ -215,7 +216,7 @@ func (d *ESX5Driver) UploadISO(localPath string, checksum string, checksumType s return finalPath, nil } - if err := d.upload(finalPath, localPath); err != nil { + if err := d.Upload(finalPath, localPath); err != nil { return "", err } @@ -662,7 +663,7 @@ func (d *ESX5Driver) mkdir(path string) error { return d.sh("mkdir", "-p", strconv.Quote(path)) } -func (d *ESX5Driver) upload(dst, src string) error { +func (d *ESX5Driver) Upload(dst, src string) error { f, err := os.Open(src) if err != nil { return err @@ -671,15 +672,6 @@ func (d *ESX5Driver) upload(dst, src string) error { return d.comm.Upload(dst, f, nil) } -func (d *ESX5Driver) download(dst, src string) error { - f, err := os.Open(dst) - if err != nil { - return err - } - defer f.Close() - return d.comm.Download(dst, f, nil) -} - func (d *ESX5Driver) verifyChecksum(ctype string, hash string, file string) bool { if ctype == "none" { if err := d.sh("stat", strconv.Quote(file)); err != nil { diff --git a/builder/vmware/iso/remote_driver.go b/builder/vmware/common/remote_driver.go similarity index 78% rename from builder/vmware/iso/remote_driver.go rename to builder/vmware/common/remote_driver.go index 6e830042c..5257d245f 100644 --- a/builder/vmware/iso/remote_driver.go +++ b/builder/vmware/common/remote_driver.go @@ -1,11 +1,7 @@ -package iso - -import ( - vmwcommon "github.com/hashicorp/packer/builder/vmware/common" -) +package common type RemoteDriver interface { - vmwcommon.Driver + Driver // UploadISO uploads a local ISO to the remote side and returns the // new path that should be used in the VMX along with an error if it @@ -28,8 +24,14 @@ type RemoteDriver interface { IsDestroyed() (bool, error) // Uploads a local file to remote side. - upload(dst, src string) error + Upload(dst, src string) error // Reload VM on remote side. ReloadVM() error + + // Read bytes from of a remote file. + ReadFile(string) ([]byte, error) + + // Write bytes to a remote file. + WriteFile(string, []byte) error } diff --git a/builder/vmware/iso/remote_driver_mock.go b/builder/vmware/common/remote_driver_mock.go similarity index 92% rename from builder/vmware/iso/remote_driver_mock.go rename to builder/vmware/common/remote_driver_mock.go index 03830ddcf..94b4778a7 100644 --- a/builder/vmware/iso/remote_driver_mock.go +++ b/builder/vmware/common/remote_driver_mock.go @@ -1,11 +1,7 @@ -package iso - -import ( - vmwcommon "github.com/hashicorp/packer/builder/vmware/common" -) +package common type RemoteDriverMock struct { - vmwcommon.DriverMock + DriverMock UploadISOCalled bool UploadISOPath string diff --git a/builder/vmware/common/remote_driver_mock_test.go b/builder/vmware/common/remote_driver_mock_test.go new file mode 100644 index 000000000..110e4990b --- /dev/null +++ b/builder/vmware/common/remote_driver_mock_test.go @@ -0,0 +1,10 @@ +package common + +import ( + "testing" +) + +func TestRemoteDriverMock_impl(t *testing.T) { + var _ Driver = new(RemoteDriverMock) + var _ RemoteDriver = new(RemoteDriverMock) +} diff --git a/builder/vmware/common/step_configure_vmx.go b/builder/vmware/common/step_configure_vmx.go index c3f63b2b2..5a4f61b2a 100644 --- a/builder/vmware/common/step_configure_vmx.go +++ b/builder/vmware/common/step_configure_vmx.go @@ -1,6 +1,7 @@ package common import ( + "bytes" "context" "fmt" "log" @@ -25,9 +26,14 @@ type StepConfigureVMX struct { } func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - vmxPath := state.Get("vmx_path").(string) + log.Printf("Configuring VMX...\n") + var vmxContents []byte + var err error + driver := state.Get("driver").(Driver) + ui := state.Get("ui").(packer.Ui) + + vmxPath := state.Get("vmx_path").(string) vmxData, err := ReadVMX(vmxPath) if err != nil { err := fmt.Errorf("Error reading VMX file: %s", err) @@ -69,7 +75,15 @@ func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) mult } } - if err := WriteVMX(vmxPath, vmxData); err != nil { + if remoteDriver, ok := driver.(RemoteDriver); ok { + var buf bytes.Buffer + buf.WriteString(EncodeVMX(vmxData)) + err = remoteDriver.WriteFile(vmxPath, buf.Bytes()) + } else { + err = WriteVMX(vmxPath, vmxData) + } + + if err != nil { err := fmt.Errorf("Error writing VMX file: %s", err) state.Put("error", err) ui.Error(err.Error()) diff --git a/builder/vmware/iso/remote_driver_mock_test.go b/builder/vmware/iso/remote_driver_mock_test.go deleted file mode 100644 index dd7a26e0a..000000000 --- a/builder/vmware/iso/remote_driver_mock_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package iso - -import ( - "testing" - - vmwcommon "github.com/hashicorp/packer/builder/vmware/common" -) - -func TestRemoteDriverMock_impl(t *testing.T) { - var _ vmwcommon.Driver = new(RemoteDriverMock) - var _ RemoteDriver = new(RemoteDriverMock) -} diff --git a/builder/vmware/iso/step_register.go b/builder/vmware/iso/step_register.go index c97800910..46b713c79 100644 --- a/builder/vmware/iso/step_register.go +++ b/builder/vmware/iso/step_register.go @@ -20,7 +20,7 @@ func (s *StepRegister) Run(_ context.Context, state multistep.StateBag) multiste ui := state.Get("ui").(packer.Ui) vmxPath := state.Get("vmx_path").(string) - if remoteDriver, ok := driver.(RemoteDriver); ok { + if remoteDriver, ok := driver.(vmwcommon.RemoteDriver); ok { ui.Say("Registering remote VM...") if err := remoteDriver.Register(vmxPath); err != nil { err := fmt.Errorf("Error registering VM: %s", err) @@ -51,7 +51,7 @@ func (s *StepRegister) Cleanup(state multistep.StateBag) { return } - if remoteDriver, ok := driver.(RemoteDriver); ok { + if remoteDriver, ok := driver.(vmwcommon.RemoteDriver); ok { if s.Format == "" || config.SkipExport { ui.Say("Unregistering virtual machine...") if err := remoteDriver.Unregister(s.registeredPath); err != nil { diff --git a/builder/vmware/iso/step_remote_upload.go b/builder/vmware/iso/step_remote_upload.go index 05050326d..8391753a6 100644 --- a/builder/vmware/iso/step_remote_upload.go +++ b/builder/vmware/iso/step_remote_upload.go @@ -22,7 +22,7 @@ func (s *stepRemoteUpload) Run(_ context.Context, state multistep.StateBag) mult driver := state.Get("driver").(vmwcommon.Driver) ui := state.Get("ui").(packer.Ui) - remote, ok := driver.(RemoteDriver) + remote, ok := driver.(vmwcommon.RemoteDriver) if !ok { return multistep.ActionContinue } diff --git a/builder/vmware/iso/step_upload_vmx.go b/builder/vmware/iso/step_upload_vmx.go index f0fec436c..b05795508 100644 --- a/builder/vmware/iso/step_upload_vmx.go +++ b/builder/vmware/iso/step_upload_vmx.go @@ -30,10 +30,10 @@ func (c *StepUploadVMX) Run(_ context.Context, state multistep.StateBag) multist vmxPath := state.Get("vmx_path").(string) if c.RemoteType == "esx5" { - remoteDriver, ok := driver.(RemoteDriver) + remoteDriver, ok := driver.(vmwcommon.RemoteDriver) if ok { remoteVmxPath := filepath.ToSlash(filepath.Join(fmt.Sprintf("%s", remoteDriver), filepath.Base(vmxPath))) - if err := remoteDriver.upload(remoteVmxPath, vmxPath); err != nil { + if err := remoteDriver.Upload(remoteVmxPath, vmxPath); err != nil { state.Put("error", fmt.Errorf("Error writing VMX: %s", err)) return multistep.ActionHalt } diff --git a/builder/vmware/vmx/step_clone_vmx.go b/builder/vmware/vmx/step_clone_vmx.go index 848476e9a..9c150e75f 100644 --- a/builder/vmware/vmx/step_clone_vmx.go +++ b/builder/vmware/vmx/step_clone_vmx.go @@ -3,7 +3,9 @@ package vmx import ( "context" "fmt" + "io/ioutil" "log" + "os" "path/filepath" "regexp" @@ -18,9 +20,15 @@ type StepCloneVMX struct { Path string VMName string Linked bool + tempDir string } func (s *StepCloneVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + halt := func(err error) multistep.StepAction { + state.Put("error", err) + return multistep.ActionHalt + } + driver := state.Get("driver").(vmwcommon.Driver) ui := state.Get("ui").(packer.Ui) @@ -29,6 +37,7 @@ func (s *StepCloneVMX) Run(_ context.Context, state multistep.StateBag) multiste ui.Say("Cloning source VM...") log.Printf("Cloning from: %s", s.Path) log.Printf("Cloning to: %s", vmxPath) + if err := driver.Clone(vmxPath, s.Path, s.Linked); err != nil { state.Put("error", err) return multistep.ActionHalt @@ -40,10 +49,26 @@ func (s *StepCloneVMX) Run(_ context.Context, state multistep.StateBag) multiste // network type so that it can work out things like IP's and MAC // addresses // * The disk compaction step needs the paths to all attached disks + if remoteDriver, ok := driver.(vmwcommon.RemoteDriver); ok { + tempDir, err := ioutil.TempDir("", "packer-vmx") + if err != nil { + return halt(err) + } + s.tempDir = tempDir + content, err := remoteDriver.ReadFile(vmxPath) + if err != nil { + return halt(err) + } + vmxPath = filepath.Join(tempDir, s.VMName+".vmx") + err = ioutil.WriteFile(vmxPath, content, 0600) + if err != nil { + return halt(err) + } + } + vmxData, err := vmwcommon.ReadVMX(vmxPath) if err != nil { - state.Put("error", err) - return multistep.ActionHalt + return halt(err) } var diskFilenames []string @@ -104,4 +129,7 @@ func (s *StepCloneVMX) Run(_ context.Context, state multistep.StateBag) multiste } func (s *StepCloneVMX) Cleanup(state multistep.StateBag) { + if s.tempDir != "" { + os.RemoveAll(s.tempDir) + } } From ba22090bc9eabeeb0377cc8f27815abb6ce1960c Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Tue, 31 Jan 2017 12:05:49 +0200 Subject: [PATCH 04/32] Move step_register and step_uload_vmx from iso to common. --- builder/vmware/common/driver_esx5.go | 4 ++-- builder/vmware/common/step_configure_vmx.go | 9 +-------- builder/vmware/{iso => common}/step_register.go | 15 +++++++-------- .../vmware/{iso => common}/step_register_test.go | 2 +- builder/vmware/{iso => common}/step_upload_vmx.go | 9 +++++---- builder/vmware/iso/builder.go | 7 ++++--- builder/vmware/vmx/builder.go | 7 +++++++ 7 files changed, 27 insertions(+), 26 deletions(-) rename builder/vmware/{iso => common}/step_register.go (80%) rename builder/vmware/{iso => common}/step_register_test.go (99%) rename builder/vmware/{iso => common}/step_upload_vmx.go (85%) diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index deb5f5634..7519c586f 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -47,8 +47,8 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { linesToArray := func(lines string) []string { return strings.Split(strings.Trim(lines, "\n"), "\n") } - d.SetOutputDir(dst) - srcVmx := d.datastorePath(path.Dir(src)) + d.SetOutputDir(path.Dir(dst)) + srcVmx := d.datastorePath(src) dstVmx := d.datastorePath(dst) srcDir := path.Dir(srcVmx) dstDir := path.Dir(dstVmx) diff --git a/builder/vmware/common/step_configure_vmx.go b/builder/vmware/common/step_configure_vmx.go index 5a4f61b2a..719a96902 100644 --- a/builder/vmware/common/step_configure_vmx.go +++ b/builder/vmware/common/step_configure_vmx.go @@ -30,7 +30,6 @@ func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) mult var vmxContents []byte var err error - driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmxPath := state.Get("vmx_path").(string) @@ -75,13 +74,7 @@ func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) mult } } - if remoteDriver, ok := driver.(RemoteDriver); ok { - var buf bytes.Buffer - buf.WriteString(EncodeVMX(vmxData)) - err = remoteDriver.WriteFile(vmxPath, buf.Bytes()) - } else { - err = WriteVMX(vmxPath, vmxData) - } + err = WriteVMX(vmxPath, vmxData) if err != nil { err := fmt.Errorf("Error writing VMX file: %s", err) diff --git a/builder/vmware/iso/step_register.go b/builder/vmware/common/step_register.go similarity index 80% rename from builder/vmware/iso/step_register.go rename to builder/vmware/common/step_register.go index 46b713c79..0fbc1cc95 100644 --- a/builder/vmware/iso/step_register.go +++ b/builder/vmware/common/step_register.go @@ -1,11 +1,10 @@ -package iso +package common import ( "context" "fmt" "time" - vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -13,14 +12,15 @@ import ( type StepRegister struct { registeredPath string Format string + KeepRegistered bool } func (s *StepRegister) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(vmwcommon.Driver) + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmxPath := state.Get("vmx_path").(string) - if remoteDriver, ok := driver.(vmwcommon.RemoteDriver); ok { + if remoteDriver, ok := driver.(RemoteDriver); ok { ui.Say("Registering remote VM...") if err := remoteDriver.Register(vmxPath); err != nil { err := fmt.Errorf("Error registering VM: %s", err) @@ -40,18 +40,17 @@ func (s *StepRegister) Cleanup(state multistep.StateBag) { return } - driver := state.Get("driver").(vmwcommon.Driver) + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) - config := state.Get("config").(*Config) _, cancelled := state.GetOk(multistep.StateCancelled) _, halted := state.GetOk(multistep.StateHalted) - if (config.KeepRegistered) && (!cancelled && !halted) { + if (s.KeepRegistered) && (!cancelled && !halted) { ui.Say("Keeping virtual machine registered with ESX host (keep_registered = true)") return } - if remoteDriver, ok := driver.(vmwcommon.RemoteDriver); ok { + if remoteDriver, ok := driver.(RemoteDriver); ok { if s.Format == "" || config.SkipExport { ui.Say("Unregistering virtual machine...") if err := remoteDriver.Unregister(s.registeredPath); err != nil { diff --git a/builder/vmware/iso/step_register_test.go b/builder/vmware/common/step_register_test.go similarity index 99% rename from builder/vmware/iso/step_register_test.go rename to builder/vmware/common/step_register_test.go index 099e067ab..f3148a18a 100644 --- a/builder/vmware/iso/step_register_test.go +++ b/builder/vmware/common/step_register_test.go @@ -1,4 +1,4 @@ -package iso +package common import ( "context" diff --git a/builder/vmware/iso/step_upload_vmx.go b/builder/vmware/common/step_upload_vmx.go similarity index 85% rename from builder/vmware/iso/step_upload_vmx.go rename to builder/vmware/common/step_upload_vmx.go index b05795508..646225a7c 100644 --- a/builder/vmware/iso/step_upload_vmx.go +++ b/builder/vmware/common/step_upload_vmx.go @@ -1,11 +1,11 @@ -package iso +package common import ( "context" "fmt" + "log" "path/filepath" - vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -24,15 +24,16 @@ type StepUploadVMX struct { } func (c *StepUploadVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(vmwcommon.Driver) + driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmxPath := state.Get("vmx_path").(string) if c.RemoteType == "esx5" { - remoteDriver, ok := driver.(vmwcommon.RemoteDriver) + remoteDriver, ok := driver.(RemoteDriver) if ok { remoteVmxPath := filepath.ToSlash(filepath.Join(fmt.Sprintf("%s", remoteDriver), filepath.Base(vmxPath))) + log.Printf("Uploading VMX file from %s to %s", vmxPath, remoteVmxPath) if err := remoteDriver.Upload(remoteVmxPath, vmxPath); err != nil { state.Put("error", fmt.Errorf("Error writing VMX: %s", err)) return multistep.ActionHalt diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 054ff4664..8f175560d 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -341,8 +341,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe VNCPortMax: b.config.VNCPortMax, VNCDisablePassword: b.config.VNCDisablePassword, }, - &StepRegister{ - Format: b.config.Format, + &vmwcommon.StepRegister{ + Format: b.config.Format, + KeepRegistered: b.config.KeepRegistered, }, &vmwcommon.StepRun{ DurationBeforeStop: 5 * time.Second, @@ -387,7 +388,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe RemoveEthernetInterfaces: b.config.VMXConfig.VMXRemoveEthernet, VNCEnabled: !b.config.DisableVNC, }, - &StepUploadVMX{ + &vmwcommon.StepUploadVMX{ RemoteType: b.config.RemoteType, }, &StepExport{ diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 42a550f29..64d89d887 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -80,6 +80,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe HTTPPortMin: b.config.HTTPPortMin, HTTPPortMax: b.config.HTTPPortMax, }, + &vmwcommon.StepUploadVMX{ + RemoteType: b.config.RemoteType, + }, + &vmwcommon.StepRegister{ + Format: "foo", + KeepRegistered: false, + }, &vmwcommon.StepConfigureVNC{ Enabled: !b.config.DisableVNC, VNCBindAddress: b.config.VNCBindAddress, From 7b5943160bb88e2bd87de1f0f9fe1049165ea9a0 Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Wed, 1 Feb 2017 13:32:02 +0200 Subject: [PATCH 05/32] Re-enable the CommHost() call. Use common's driver factory. --- builder/vmware/common/driver.go | 7 +- builder/vmware/common/driver_esx5.go | 133 +++++++++--------- .../vmware/common/step_type_boot_command.go | 1 + builder/vmware/iso/builder.go | 11 -- builder/vmware/iso/driver.go | 33 +---- builder/vmware/vmx/builder.go | 10 +- builder/vmware/vmx/config.go | 3 + 7 files changed, 79 insertions(+), 119 deletions(-) diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index e65e429b9..ed37ff207 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" ) @@ -81,11 +82,9 @@ type Driver interface { // NewDriver returns a new driver implementation for this operating // system, or an error if the driver couldn't be initialized. -func NewDriver(dconfig *DriverConfig, config *SSHConfig) (Driver, error) { +func NewDriver(dconfig *DriverConfig, config *SSHConfig, commConfig *communicator.Config, vmName string) (Driver, error) { drivers := []Driver{} - log.Printf("**** NewDriver()") if dconfig.RemoteType != "" { - log.Printf("**** Creating the remote driver.") drivers = []Driver{ &ESX5Driver{ Host: dconfig.RemoteHost, @@ -96,6 +95,8 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig) (Driver, error) { Datastore: dconfig.RemoteDatastore, CacheDatastore: dconfig.RemoteCacheDatastore, CacheDirectory: dconfig.RemoteCacheDirectory, + VMName: vmName, + CommConfig: *commConfig, }, } diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index 7519c586f..6c6b02c13 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -16,8 +16,8 @@ import ( "strings" "time" - vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/communicator/ssh" + "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" helperssh "github.com/hashicorp/packer/helper/ssh" "github.com/hashicorp/packer/packer" @@ -37,6 +37,8 @@ type ESX5Driver struct { Datastore string CacheDatastore string CacheDirectory string + VMName string + CommConfig communicator.Config comm packer.Communicator outputDir string @@ -56,9 +58,9 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { log.Printf("Source: %s\n", srcVmx) log.Printf("Dest: %s\n", dstVmx) - err := d.sh("mkdir", dstDir) + err := d.MkdirAll() if err != nil { - return fmt.Errorf("Failed to create the destination directory %s: %s", dstDir, err) + return fmt.Errorf("Failed to create the destination directory %s: %s", d.outputDir, err) } err = d.sh("cp", srcVmx, dstVmx) @@ -458,71 +460,66 @@ func (ESX5Driver) UpdateVMX(_, password string, port uint, data map[string]strin } func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { - // config := state.Get("config").(*Config) - // sshc := config.SSHConfig.Comm - // port := sshc.SSHPort - // if sshc.Type == "winrm" { - // port = sshc.WinRMPort - // } - // - // if address, ok := state.GetOk("vm_address"); ok { - // return address.(string), nil - // } - // - // if address := config.CommConfig.Host(); address != "" { - // state.Put("vm_address", address) - // return address, nil - // } - // - // r, err := d.esxcli("network", "vm", "list") - // if err != nil { - // return "", err - // } - // - // record, err := r.find("Name", config.VMName) - // if err != nil { - // return "", err - // } - // wid := record["WorldID"] - // if wid == "" { - // return "", errors.New("VM WorldID not found") - // } - // - // r, err = d.esxcli("network", "vm", "port", "list", "-w", wid) - // if err != nil { - // return "", err - // } - // - // // Loop through interfaces - // for { - // record, err = r.read() - // if err == io.EOF { - // break - // } - // if err != nil { - // return "", err - // } - // - // if record["IPAddress"] == "0.0.0.0" { - // continue - // } - // // When multiple NICs are connected to the same network, choose - // // one that has a route back. This Dial should ensure that. - // conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", record["IPAddress"], port), 2*time.Second) - // if err != nil { - // if e, ok := err.(*net.OpError); ok { - // if e.Timeout() { - // log.Printf("Timeout connecting to %s", record["IPAddress"]) - // continue - // } - // } - // } else { - // defer conn.Close() - // address := record["IPAddress"] - // state.Put("vm_address", address) - // return address, nil - // } - // } + port := d.CommConfig.Port() + + if address, ok := state.GetOk("vm_address"); ok { + return address.(string), nil + } + + if address := d.CommConfig.Host(); address != "" { + state.Put("vm_address", address) + return address, nil + } + + r, err := d.esxcli("network", "vm", "list") + if err != nil { + return "", err + } + + record, err := r.find("Name", d.VMName) + if err != nil { + return "", err + } + wid := record["WorldID"] + if wid == "" { + return "", errors.New("VM WorldID not found") + } + + r, err = d.esxcli("network", "vm", "port", "list", "-w", wid) + if err != nil { + return "", err + } + + // Loop through interfaces + for { + record, err = r.read() + if err == io.EOF { + break + } + if err != nil { + return "", err + } + + if record["IPAddress"] == "0.0.0.0" { + continue + } + // When multiple NICs are connected to the same network, choose + // one that has a route back. This Dial should ensure that. + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", record["IPAddress"], port), 2*time.Second) + if err != nil { + if e, ok := err.(*net.OpError); ok { + if e.Timeout() { + log.Printf("Timeout connecting to %s", record["IPAddress"]) + continue + } + } + } else { + defer conn.Close() + address := record["IPAddress"] + state.Put("vm_address", address) + return address, nil + } + } return "", errors.New("No interface on the VM has an IP address ready") } diff --git a/builder/vmware/common/step_type_boot_command.go b/builder/vmware/common/step_type_boot_command.go index ac33fd24c..b241d57ba 100644 --- a/builder/vmware/common/step_type_boot_command.go +++ b/builder/vmware/common/step_type_boot_command.go @@ -70,6 +70,7 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) // Connect to VNC ui.Say(fmt.Sprintf("Connecting to VM via VNC (%s:%d)", vncIp, vncPort)) + nc, err := net.Dial("tcp", fmt.Sprintf("%s:%d", vncIp, vncPort)) if err != nil { err := fmt.Errorf("Error connecting to VNC: %s", err) diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 8f175560d..751799d42 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -76,17 +76,6 @@ type Config struct { VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"` VMXTemplatePath string `mapstructure:"vmx_template_path"` - // remote vsphere - RemoteType string `mapstructure:"remote_type"` - RemoteDatastore string `mapstructure:"remote_datastore"` - RemoteCacheDatastore string `mapstructure:"remote_cache_datastore"` - RemoteCacheDirectory string `mapstructure:"remote_cache_directory"` - RemoteHost string `mapstructure:"remote_host"` - RemotePort uint `mapstructure:"remote_port"` - RemoteUser string `mapstructure:"remote_username"` - RemotePassword string `mapstructure:"remote_password"` - RemotePrivateKey string `mapstructure:"remote_private_key_file"` - CommConfig communicator.Config `mapstructure:",squash"` ctx interpolate.Context diff --git a/builder/vmware/iso/driver.go b/builder/vmware/iso/driver.go index 6580c3108..947ff4c16 100644 --- a/builder/vmware/iso/driver.go +++ b/builder/vmware/iso/driver.go @@ -9,36 +9,5 @@ import ( // NewDriver returns a new driver implementation for this operating // system, or an error if the driver couldn't be initialized. func NewDriver(config *Config) (vmwcommon.Driver, error) { - drivers := []vmwcommon.Driver{} - - if config.RemoteType == "" { - return vmwcommon.NewDriver(&config.DriverConfig, &config.SSHConfig) - } - - drivers = []vmwcommon.Driver{ - &vmwcommon.ESX5Driver{ - Host: config.RemoteHost, - Port: config.RemotePort, - Username: config.RemoteUser, - Password: config.RemotePassword, - PrivateKeyFile: config.RemotePrivateKey, - Datastore: config.RemoteDatastore, - CacheDatastore: config.RemoteCacheDatastore, - CacheDirectory: config.RemoteCacheDirectory, - }, - } - - errs := "" - for _, driver := range drivers { - err := driver.Verify() - if err == nil { - return driver, nil - } - errs += "* " + err.Error() + "\n" - } - - return nil, fmt.Errorf( - "Unable to initialize any driver for this platform. The errors\n"+ - "from each driver are shown below. Please fix at least one driver\n"+ - "to continue:\n%s", errs) + return vmwcommon.NewDriver(&config.DriverConfig, &config.SSHConfig, &config.CommConfig, config.VMName) } diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 64d89d887..35ef9068b 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -34,7 +34,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { // Run executes a Packer build and returns a packer.Artifact representing // a VMware image. func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { - driver, err := vmwcommon.NewDriver(&b.config.DriverConfig, &b.config.SSHConfig) + driver, err := vmwcommon.NewDriver(&b.config.DriverConfig, &b.config.SSHConfig, &b.config.CommConfig, b.config.VMName) if err != nil { return nil, fmt.Errorf("Failed creating VMware driver: %s", err) } @@ -83,10 +83,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vmwcommon.StepUploadVMX{ RemoteType: b.config.RemoteType, }, - &vmwcommon.StepRegister{ - Format: "foo", - KeepRegistered: false, - }, &vmwcommon.StepConfigureVNC{ Enabled: !b.config.DisableVNC, VNCBindAddress: b.config.VNCBindAddress, @@ -94,6 +90,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe VNCPortMax: b.config.VNCPortMax, VNCDisablePassword: b.config.VNCDisablePassword, }, + &vmwcommon.StepRegister{ + Format: "foo", + KeepRegistered: false, + }, &vmwcommon.StepRun{ DurationBeforeStop: 5 * time.Second, Headless: b.config.Headless, diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index bd25dc92a..b5babbb4c 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -8,6 +8,7 @@ import ( vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" + "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" @@ -33,6 +34,8 @@ type Config struct { SourcePath string `mapstructure:"source_path"` VMName string `mapstructure:"vm_name"` + CommConfig communicator.Config `mapstructure:",squash"` + ctx interpolate.Context } From 2d00dc67569e74f8344518f99b05cd834fad5f4b Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Fri, 3 Feb 2017 09:00:30 +0200 Subject: [PATCH 06/32] Add VMName to the vmx mods. --- builder/vmware/common/step_configure_vmx.go | 2 ++ builder/vmware/iso/builder.go | 1 + builder/vmware/vmx/builder.go | 1 + 3 files changed, 4 insertions(+) diff --git a/builder/vmware/common/step_configure_vmx.go b/builder/vmware/common/step_configure_vmx.go index 719a96902..03162ed35 100644 --- a/builder/vmware/common/step_configure_vmx.go +++ b/builder/vmware/common/step_configure_vmx.go @@ -23,6 +23,7 @@ import ( type StepConfigureVMX struct { CustomData map[string]string SkipFloppy bool + VMName string } func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { @@ -47,6 +48,7 @@ func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) mult // Create a new UUID for this VM, since it is a new VM vmxData["uuid.action"] = "create" + vmxData["displayname"] = s.VMName // Delete any generated addresses since we want to regenerate // them. Conflicting MAC addresses is a bad time. addrRegex := regexp.MustCompile(`(?i)^ethernet\d+\.generatedAddress`) diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 751799d42..0573e6c9c 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -316,6 +316,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &stepCreateVMX{}, &vmwcommon.StepConfigureVMX{ CustomData: b.config.VMXData, + VMName: b.config.VMName, }, &vmwcommon.StepSuppressMessages{}, &common.StepHTTPServer{ diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 35ef9068b..2034e57d9 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -73,6 +73,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &vmwcommon.StepConfigureVMX{ CustomData: b.config.VMXData, + VMName: b.config.VMName, }, &vmwcommon.StepSuppressMessages{}, &common.StepHTTPServer{ From 4d9cb19012de17dbc6e76a2f9f971a6a431d9522 Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Fri, 3 Feb 2017 15:56:13 +0200 Subject: [PATCH 07/32] Refactor artifacts and output_dir implementations. --- builder/vmware/common/artifact.go | 47 +++++++++++++------ builder/vmware/common/driver_esx5.go | 24 +++------- .../artifact.go => common/remote_artifact.go} | 16 +++---- .../remote_artifact_test.go} | 4 +- builder/vmware/iso/builder.go | 28 ++--------- builder/vmware/iso/output_dir.go | 14 ------ builder/vmware/vmx/builder.go | 23 +++++++-- 7 files changed, 73 insertions(+), 83 deletions(-) rename builder/vmware/{iso/artifact.go => common/remote_artifact.go} (65%) rename builder/vmware/{iso/artifact_test.go => common/remote_artifact_test.go} (84%) delete mode 100644 builder/vmware/iso/output_dir.go diff --git a/builder/vmware/common/artifact.go b/builder/vmware/common/artifact.go index e0b3e875e..cb61cd2f4 100644 --- a/builder/vmware/common/artifact.go +++ b/builder/vmware/common/artifact.go @@ -3,22 +3,26 @@ package common import ( "fmt" "os" - "path/filepath" "github.com/hashicorp/packer/packer" ) // BuilderId for the local artifacts const BuilderId = "mitchellh.vmware" +const BuilderIdESX = "mitchellh.vmware-esx" // Artifact is the result of running the VMware builder, namely a set // of files associated with the resulting machine. -type localArtifact struct { - id string - dir string - f []string +type artifact struct { + builderId string + id string + dir string + f []string + config map[string]string } +// NewLocalArtifact returns a VMware artifact containing the files +// in the given directory. // NewLocalArtifact returns a VMware artifact containing the files // in the given directory. func NewLocalArtifact(id string, dir string) (packer.Artifact, error) { @@ -37,33 +41,46 @@ func NewLocalArtifact(id string, dir string) (packer.Artifact, error) { return nil, err } - return &localArtifact{ - id: id, - dir: dir, - f: files, + return &artifact{ + builderId: id, + dir: dir, + f: files, }, nil } -func (a *localArtifact) BuilderId() string { +func NewArtifact(dir OutputDir, files []string, esxi bool) (packer.Artifact, err) { + builderID := BuilderId + if esxi { + builderID = BuilderIdESX + } + + return &artifact{ + builderId: builderID, + dir: dir.String(), + f: files, + }, nil +} + +func (a *artifact) BuilderId() string { return BuilderId } -func (a *localArtifact) Files() []string { +func (a *artifact) Files() []string { return a.f } -func (a *localArtifact) Id() string { +func (*artifact) Id() string { return a.id } -func (a *localArtifact) String() string { +func (a *artifact) String() string { return fmt.Sprintf("VM files in directory: %s", a.dir) } -func (a *localArtifact) State(name string) interface{} { +func (a *artifact) State(name string) interface{} { return nil } -func (a *localArtifact) Destroy() error { +func (a *artifact) Destroy() error { return os.RemoveAll(a.dir) } diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index 6c6b02c13..876ea7001 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -68,7 +68,7 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { return fmt.Errorf("Failed to copy the vmx file %s: %s", srcVmx, err) } - filesToClone, err := d.run(nil, "find", srcDir, "! -name '*.vmdk' ! -name '*.vmx' -type f") + filesToClone, err := d.run(nil, "find", srcDir, "! -name '*.vmdk' ! -name '*.vmx' -type f ! -size 0") if err != nil { return fmt.Errorf("Failed to get the file list to copy: %s", err) } @@ -96,22 +96,6 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { return fmt.Errorf("Failing to clone disk %s: %s", srcDisk, err) } } - - // - // // FIXME: VMName should be taken from the config. - // vmxEdits := []string{ - // "s/\\(display[Nn]ame = \\).*/\\1\"" + vmName + "\"/", - // "/ethernet..generated[aA]ddress =/d", - // "/uuid.bios =/d", - // "/uuid.location =/d", - // "/vc.uuid =/d", - // } - // for _, edit := range vmxEdits { - // err := d.sh("sed -i -e", "'", edit, "'", dstVmx) - // if err != nil { - // return fmt.Errorf("Failed to edit the destination file %s: %s", dstVmx, err) - // } - // } log.Printf("Successfully cloned %s to %s\n", src, dst) return nil } @@ -460,7 +444,11 @@ func (ESX5Driver) UpdateVMX(_, password string, port uint, data map[string]strin } func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { - port := d.CommConfig.Port() + sshc := state.Get("sshConfig").(SSHConfig).Comm + port := sshc.SSHPort + if sshc.Type == "winrm" { + port = sshc.WinRMPort + } if address, ok := state.GetOk("vm_address"); ok { return address.(string), nil diff --git a/builder/vmware/iso/artifact.go b/builder/vmware/common/remote_artifact.go similarity index 65% rename from builder/vmware/iso/artifact.go rename to builder/vmware/common/remote_artifact.go index 026b4580f..37dbc1812 100644 --- a/builder/vmware/iso/artifact.go +++ b/builder/vmware/common/remote_artifact.go @@ -1,4 +1,4 @@ -package iso +package common import ( "fmt" @@ -12,7 +12,7 @@ const ( // Artifact is the result of running the VMware builder, namely a set // of files associated with the resulting machine. -type Artifact struct { +type RemoteArtifact struct { builderId string id string dir OutputDir @@ -20,26 +20,26 @@ type Artifact struct { config map[string]string } -func (a *Artifact) BuilderId() string { +func (a *RemoteArtifact) BuilderId() string { return a.builderId } -func (a *Artifact) Files() []string { +func (a *RemoteArtifact) Files() []string { return a.f } -func (a *Artifact) Id() string { +func (*RemoteArtifact) Id() string { return a.id } -func (a *Artifact) String() string { +func (a *RemoteArtifact) String() string { return fmt.Sprintf("VM files in directory: %s", a.dir) } -func (a *Artifact) State(name string) interface{} { +func (a *RemoteArtifact) State(name string) interface{} { return a.config[name] } -func (a *Artifact) Destroy() error { +func (a *RemoteArtifact) Destroy() error { return a.dir.RemoveAll() } diff --git a/builder/vmware/iso/artifact_test.go b/builder/vmware/common/remote_artifact_test.go similarity index 84% rename from builder/vmware/iso/artifact_test.go rename to builder/vmware/common/remote_artifact_test.go index ea4bab42b..292a87b54 100644 --- a/builder/vmware/iso/artifact_test.go +++ b/builder/vmware/common/remote_artifact_test.go @@ -1,4 +1,4 @@ -package iso +package common import ( "testing" @@ -8,7 +8,7 @@ import ( func TestArtifact_Impl(t *testing.T) { var raw interface{} - raw = &Artifact{} + raw = &RemoteArtifact{} if _, ok := raw.(packer.Artifact); !ok { t.Fatal("Artifact must be a proper artifact") } diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 0573e6c9c..e81431fcf 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -19,8 +19,6 @@ import ( "github.com/hashicorp/packer/template/interpolate" ) -const BuilderIdESX = "mitchellh.vmware-esx" - type Builder struct { config Config runner multistep.Runner @@ -257,9 +255,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } // Determine the output dir implementation - var dir OutputDir + var dir vmwcommon.OutputDir switch d := driver.(type) { - case OutputDir: + case vmwcommon.OutputDir: dir = d default: dir = new(vmwcommon.LocalOutputDir) @@ -281,6 +279,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe state.Put("driver", driver) state.Put("hook", hook) state.Put("ui", ui) + state.Put("sshConfig", &b.config.SSHConfig) steps := []multistep.Step{ &vmwcommon.StepPrepareTools{ @@ -413,30 +412,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe dir.SetOutputDir(exportOutputPath) files, err = dir.ListFiles() } else { - files, err = state.Get("dir").(OutputDir).ListFiles() + files, err = state.Get("dir").(vmwcommon.OutputDir).ListFiles() } if err != nil { return nil, err } - // Set the proper builder ID - builderId := vmwcommon.BuilderId - if b.config.RemoteType != "" { - builderId = BuilderIdESX - } - - config := make(map[string]string) - config[ArtifactConfKeepRegistered] = strconv.FormatBool(b.config.KeepRegistered) - config[ArtifactConfFormat] = b.config.Format - config[ArtifactConfSkipExport] = strconv.FormatBool(b.config.SkipExport) - - return &Artifact{ - builderId: builderId, - id: b.config.VMName, - dir: dir, - f: files, - config: config, - }, nil + return vmwcommon.NewArtifact(dir, files, b.config.RemoteType != ""), nil } func (b *Builder) Cancel() { diff --git a/builder/vmware/iso/output_dir.go b/builder/vmware/iso/output_dir.go deleted file mode 100644 index 30eab2e31..000000000 --- a/builder/vmware/iso/output_dir.go +++ /dev/null @@ -1,14 +0,0 @@ -package iso - -// OutputDir is an interface type that abstracts the creation and handling -// of the output directory for VMware-based products. The abstraction is made -// so that the output directory can be properly made on remote (ESXi) based -// VMware products as well as local. -type OutputDir interface { - DirExists() (bool, error) - ListFiles() ([]string, error) - MkdirAll() error - Remove(string) error - RemoveAll() error - SetOutputDir(string) -} diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 2034e57d9..088868eda 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -39,8 +39,17 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, fmt.Errorf("Failed creating VMware driver: %s", err) } - // Setup the directory - dir := new(vmwcommon.LocalOutputDir) + // Determine the output dir implementation + var dir vmwcommon.OutputDir + switch d := driver.(type) { + case vmwcommon.OutputDir: + dir = d + default: + dir = new(vmwcommon.LocalOutputDir) + } + if b.config.RemoteType != "" { + b.config.OutputDir = b.config.VMName + } dir.SetOutputDir(b.config.OutputDir) // Set up the state. @@ -51,6 +60,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe state.Put("driver", driver) state.Put("hook", hook) state.Put("ui", ui) + state.Put("sshConfig", b.config.SSHConfig) // Build the steps. steps := []multistep.Step{ @@ -138,6 +148,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe RemoveEthernetInterfaces: b.config.VMXConfig.VMXRemoveEthernet, VNCEnabled: !b.config.DisableVNC, }, + &vmwcommon.StepUploadVMX{ + RemoteType: b.config.RemoteType, + }, } // Run the steps. @@ -157,8 +170,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe if _, ok := state.GetOk(multistep.StateHalted); ok { return nil, errors.New("Build was halted.") } + files, err := state.Get("dir").(vmwcommon.OutputDir).ListFiles() + if err != nil { + return nil, err + } - return vmwcommon.NewLocalArtifact(b.config.VMName, b.config.OutputDir) + return vmwcommon.NewArtifact(dir, files, b.config.RemoteType != ""), nil } // Cancel. From 5e74bcfb889f401b4324c6f11ac2e582f085c76c Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Mon, 6 Feb 2017 09:13:37 +0200 Subject: [PATCH 08/32] Fix tests, move remote_ default values to driver_config. --- builder/vmware/common/artifact_test.go | 10 +-- builder/vmware/common/driver_config.go | 15 ++++ builder/vmware/common/driver_esx5.go | 26 +++--- builder/vmware/common/driver_esx5_test.go | 62 +++++++------- builder/vmware/common/remote_driver.go | 9 +- builder/vmware/common/remote_driver_mock.go | 11 ++- builder/vmware/common/step_register_test.go | 10 +-- builder/vmware/iso/builder.go | 20 ----- builder/vmware/iso/driver_esx5_test.go | 92 --------------------- builder/vmware/vmx/builder.go | 4 +- builder/vmware/vmx/config.go | 28 +++---- builder/vmware/vmx/step_clone_vmx.go | 11 +-- 12 files changed, 93 insertions(+), 205 deletions(-) delete mode 100644 builder/vmware/iso/driver_esx5_test.go diff --git a/builder/vmware/common/artifact_test.go b/builder/vmware/common/artifact_test.go index 53b8364f1..3e6a1cd23 100644 --- a/builder/vmware/common/artifact_test.go +++ b/builder/vmware/common/artifact_test.go @@ -10,7 +10,7 @@ import ( ) func TestLocalArtifact_impl(t *testing.T) { - var _ packer.Artifact = new(localArtifact) + var _ packer.Artifact = new(artifact) } func TestNewLocalArtifact(t *testing.T) { @@ -28,11 +28,11 @@ func TestNewLocalArtifact(t *testing.T) { if err := os.Mkdir(filepath.Join(td, "b"), 0755); err != nil { t.Fatalf("err: %s", err) } + dir := new(LocalOutputDir) + dir.SetOutputDir(td) + files, err := dir.ListFiles() - a, err := NewLocalArtifact("vm1", td) - if err != nil { - t.Fatalf("err: %s", err) - } + a := NewArtifact(dir, files, false) if a.BuilderId() != BuilderId { t.Fatalf("bad: %#v", a.BuilderId()) diff --git a/builder/vmware/common/driver_config.go b/builder/vmware/common/driver_config.go index a58070502..6367b1734 100644 --- a/builder/vmware/common/driver_config.go +++ b/builder/vmware/common/driver_config.go @@ -26,6 +26,21 @@ func (c *DriverConfig) Prepare(ctx *interpolate.Context) []error { if c.FusionAppPath == "" { c.FusionAppPath = "/Applications/VMware Fusion.app" } + if c.RemoteUser == "" { + c.RemoteUser = "root" + } + if c.RemoteDatastore == "" { + c.RemoteDatastore = "datastore1" + } + if c.RemoteCacheDatastore == "" { + c.RemoteCacheDatastore = c.RemoteDatastore + } + if c.RemoteCacheDirectory == "" { + c.RemoteCacheDirectory = "packer_cache" + } + if c.RemotePort == 0 { + c.RemotePort = 22 + } return nil } diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index 876ea7001..e0d089535 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -122,21 +122,6 @@ func (d *ESX5Driver) ReloadVM() error { return d.sh("vim-cmd", "vmsvc/reload", d.vmId) } -func (d *ESX5Driver) ReadFile(name string) ([]byte, error) { - var b bytes.Buffer - writer := bufio.NewWriter(&b) - err := d.comm.Download(d.datastorePath(name), writer) - if err != nil { - return nil, fmt.Errorf("Cant read remote file %s: %s", name, err) - } - writer.Flush() - return b.Bytes(), nil -} - -func (d *ESX5Driver) WriteFile(name string, content []byte) error { - return d.comm.Upload(d.datastorePath(name), bytes.NewReader(content), nil) -} - func (d *ESX5Driver) Start(vmxPathLocal string, headless bool) error { for i := 0; i < 20; i++ { //intentionally not checking for error since poweron may fail specially after initial VM registration @@ -444,7 +429,7 @@ func (ESX5Driver) UpdateVMX(_, password string, port uint, data map[string]strin } func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { - sshc := state.Get("sshConfig").(SSHConfig).Comm + sshc := state.Get("sshConfig").(*SSHConfig).Comm port := sshc.SSHPort if sshc.Type == "winrm" { port = sshc.WinRMPort @@ -657,6 +642,15 @@ func (d *ESX5Driver) Upload(dst, src string) error { return d.comm.Upload(dst, f, nil) } +func (d *ESX5Driver) Download(src, dst string) error { + file, err := os.Create(dst) + if err != nil { + return err + } + defer file.Close() + return d.comm.Download(d.datastorePath(src), file) +} + func (d *ESX5Driver) verifyChecksum(ctype string, hash string, file string) bool { if ctype == "none" { if err := d.sh("stat", strconv.Quote(file)); err != nil { diff --git a/builder/vmware/common/driver_esx5_test.go b/builder/vmware/common/driver_esx5_test.go index 2fec8ee7f..8d3d067f2 100644 --- a/builder/vmware/common/driver_esx5_test.go +++ b/builder/vmware/common/driver_esx5_test.go @@ -4,8 +4,6 @@ import ( "fmt" "net" "testing" - - "github.com/mitchellh/multistep" ) func TestESX5Driver_implDriver(t *testing.T) { @@ -58,40 +56,40 @@ func TestESX5Driver_HostIP(t *testing.T) { func TestESX5Driver_CommHost(t *testing.T) { const expected_host = "127.0.0.1" - config := testConfig() + config := make(map[string]interface{}) config["communicator"] = "winrm" config["winrm_username"] = "username" config["winrm_password"] = "password" config["winrm_host"] = expected_host - var b Builder - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - if host := b.config.CommConfig.Host(); host != expected_host { - t.Fatalf("setup failed, bad host name: %s", host) - } + // var b Builder + // warns, err := b.Prepare(config) + // if len(warns) > 0 { + // t.Fatalf("bad: %#v", warns) + // } + // if err != nil { + // t.Fatalf("should not have error: %s", err) + // } + // if host := b.config.CommConfig.Host(); host != expected_host { + // t.Fatalf("setup failed, bad host name: %s", host) + // } - state := new(multistep.BasicStateBag) - state.Put("config", &b.config) - - var driver ESX5Driver - host, err := driver.CommHost(state) - if err != nil { - t.Fatalf("should not have error: %s", err) - } - if host != expected_host { - t.Errorf("bad host name: %s", host) - } - address, ok := state.GetOk("vm_address") - if !ok { - t.Error("state not updated with vm_address") - } - if address.(string) != expected_host { - t.Errorf("bad vm_address: %s", address.(string)) - } + // state := new(multistep.BasicStateBag) + // state.Put("config", &b.config) + // + // var driver ESX5Driver{} + // host, err := driver.CommHost(state) + // if err != nil { + // t.Fatalf("should not have error: %s", err) + // } + // if host != expected_host { + // t.Errorf("bad host name: %s", host) + // } + // address, ok := state.GetOk("vm_address") + // if !ok { + // t.Error("state not updated with vm_address") + // } + // if address.(string) != expected_host { + // t.Errorf("bad vm_address: %s", address.(string)) + // } } diff --git a/builder/vmware/common/remote_driver.go b/builder/vmware/common/remote_driver.go index 5257d245f..bc2454297 100644 --- a/builder/vmware/common/remote_driver.go +++ b/builder/vmware/common/remote_driver.go @@ -26,12 +26,9 @@ type RemoteDriver interface { // Uploads a local file to remote side. Upload(dst, src string) error + // Download a remote file to a local file. + Download(src, dst string) error + // Reload VM on remote side. ReloadVM() error - - // Read bytes from of a remote file. - ReadFile(string) ([]byte, error) - - // Write bytes to a remote file. - WriteFile(string, []byte) error } diff --git a/builder/vmware/common/remote_driver_mock.go b/builder/vmware/common/remote_driver_mock.go index 94b4778a7..10338c86c 100644 --- a/builder/vmware/common/remote_driver_mock.go +++ b/builder/vmware/common/remote_driver_mock.go @@ -23,7 +23,8 @@ type RemoteDriverMock struct { IsDestroyedResult bool IsDestroyedErr error - uploadErr error + UploadErr error + DownloadErr error ReloadVMErr error } @@ -56,8 +57,12 @@ func (d *RemoteDriverMock) IsDestroyed() (bool, error) { return d.IsDestroyedResult, d.IsDestroyedErr } -func (d *RemoteDriverMock) upload(dst, src string) error { - return d.uploadErr +func (d *RemoteDriverMock) Upload(dst, src string) error { + return d.UploadErr +} + +func (d *RemoteDriverMock) Download(src, dst string) error { + return d.DownloadErr } func (d *RemoteDriverMock) RemoveCache(localPath string) error { diff --git a/builder/vmware/common/step_register_test.go b/builder/vmware/common/step_register_test.go index f3148a18a..be3c0b5e0 100644 --- a/builder/vmware/common/step_register_test.go +++ b/builder/vmware/common/step_register_test.go @@ -31,12 +31,9 @@ func TestStepRegister_regularDriver(t *testing.T) { func TestStepRegister_remoteDriver(t *testing.T) { state := testState(t) - step := new(StepRegister) + step := &StepRegister{KeepRegistered: false} driver := new(RemoteDriverMock) - var config Config - config.KeepRegistered = false - state.Put("config", &config) state.Put("driver", driver) state.Put("vmx_path", "foo") @@ -71,12 +68,9 @@ func TestStepRegister_remoteDriver(t *testing.T) { } func TestStepRegister_WithoutUnregister_remoteDriver(t *testing.T) { state := testState(t) - step := new(StepRegister) + step := &StepRegister{KeepRegistered: true} driver := new(RemoteDriverMock) - var config Config - config.KeepRegistered = true - state.Put("config", &config) state.Put("driver", driver) state.Put("vmx_path", "foo") diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index e81431fcf..236bd454b 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -162,26 +162,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.Version = "9" } - if b.config.RemoteUser == "" { - b.config.RemoteUser = "root" - } - - if b.config.RemoteDatastore == "" { - b.config.RemoteDatastore = "datastore1" - } - - if b.config.RemoteCacheDatastore == "" { - b.config.RemoteCacheDatastore = b.config.RemoteDatastore - } - - if b.config.RemoteCacheDirectory == "" { - b.config.RemoteCacheDirectory = "packer_cache" - } - - if b.config.RemotePort == 0 { - b.config.RemotePort = 22 - } - if b.config.VMXTemplatePath != "" { if err := b.validateVMXTemplatePath(); err != nil { errs = packer.MultiErrorAppend( diff --git a/builder/vmware/iso/driver_esx5_test.go b/builder/vmware/iso/driver_esx5_test.go deleted file mode 100644 index 097d6e186..000000000 --- a/builder/vmware/iso/driver_esx5_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package iso - -import ( - "fmt" - "net" - "testing" - - vmwcommon "github.com/hashicorp/packer/builder/vmware/common" - "github.com/hashicorp/packer/helper/multistep" -) - -func TestESX5Driver_implDriver(t *testing.T) { - var _ vmwcommon.Driver = new(ESX5Driver) -} - -func TestESX5Driver_UpdateVMX(t *testing.T) { - var driver ESX5Driver - data := make(map[string]string) - driver.UpdateVMX("0.0.0.0", "", 5900, data) - if _, ok := data["remotedisplay.vnc.ip"]; ok { - // Do not add the remotedisplay.vnc.ip on ESXi - t.Fatal("invalid VMX data key: remotedisplay.vnc.ip") - } - if enabled := data["remotedisplay.vnc.enabled"]; enabled != "TRUE" { - t.Errorf("bad VMX data for key remotedisplay.vnc.enabled: %v", enabled) - } - if port := data["remotedisplay.vnc.port"]; port != fmt.Sprint(port) { - t.Errorf("bad VMX data for key remotedisplay.vnc.port: %v", port) - } -} - -func TestESX5Driver_implOutputDir(t *testing.T) { - var _ vmwcommon.OutputDir = new(ESX5Driver) -} - -func TestESX5Driver_implVNCAddressFinder(t *testing.T) { - var _ vmwcommon.VNCAddressFinder = new(ESX5Driver) -} - -func TestESX5Driver_implRemoteDriver(t *testing.T) { - var _ RemoteDriver = new(ESX5Driver) -} - -func TestESX5Driver_HostIP(t *testing.T) { - expected_host := "127.0.0.1" - - //create mock SSH server - listen, _ := net.Listen("tcp", fmt.Sprintf("%s:0", expected_host)) - port := listen.Addr().(*net.TCPAddr).Port - defer listen.Close() - - driver := ESX5Driver{Host: "localhost", Port: uint(port)} - state := new(multistep.BasicStateBag) - - if host, _ := driver.HostIP(state); host != expected_host { - t.Error(fmt.Sprintf("Expected string, %s but got %s", expected_host, host)) - } -} - -func TestESX5Driver_CommHost(t *testing.T) { - const expected_host = "127.0.0.1" - - config := testConfig() - config["communicator"] = "winrm" - config["winrm_username"] = "username" - config["winrm_password"] = "password" - config["winrm_host"] = expected_host - - var b Builder - warns, err := b.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - if host := b.config.CommConfig.Host(); host != expected_host { - t.Fatalf("setup failed, bad host name: %s", host) - } - - state := new(multistep.BasicStateBag) - state.Put("config", &b.config) - - var driver ESX5Driver - host, err := driver.CommHost(state) - if err != nil { - t.Fatalf("should not have error: %s", err) - } - if host != expected_host { - t.Errorf("bad host name: %s", host) - } -} diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 088868eda..260099657 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -60,7 +60,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe state.Put("driver", driver) state.Put("hook", hook) state.Put("ui", ui) - state.Put("sshConfig", b.config.SSHConfig) + state.Put("sshConfig", &b.config.SSHConfig) // Build the steps. steps := []multistep.Step{ @@ -102,7 +102,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe VNCDisablePassword: b.config.VNCDisablePassword, }, &vmwcommon.StepRegister{ - Format: "foo", + Format: "", KeepRegistered: false, }, &vmwcommon.StepRun{ diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index b5babbb4c..9a52c8ea9 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -2,7 +2,6 @@ package vmx import ( "fmt" - "log" "os" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" @@ -28,11 +27,12 @@ type Config struct { vmwcommon.ToolsConfig `mapstructure:",squash"` vmwcommon.VMXConfig `mapstructure:",squash"` - Linked bool `mapstructure:"linked"` - RemoteType string `mapstructure:"remote_type"` - SkipCompaction bool `mapstructure:"skip_compaction"` - SourcePath string `mapstructure:"source_path"` - VMName string `mapstructure:"vm_name"` + Linked bool `mapstructure:"linked"` + RemoteType string `mapstructure:"remote_type"` + SkipCompaction bool `mapstructure:"skip_compaction"` + BootCommand []string `mapstructure:"boot_command"` + SourcePath string `mapstructure:"source_path"` + VMName string `mapstructure:"vm_name"` CommConfig communicator.Config `mapstructure:",squash"` @@ -74,14 +74,14 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { 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")) - } else { - if _, err := os.Stat(c.SourcePath); err != nil { - // FIXME: - log.Printf("source_path is invalid: %s", err) - // errs = packer.MultiErrorAppend(errs, - // fmt.Errorf("source_path is invalid: %s", err)) + if c.DriverConfig.RemoteType == "" { + if c.SourcePath == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is blank, but is required")) + } else { + if _, err := os.Stat(c.SourcePath); err != nil { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("source_path is invalid: %s", err)) + } } } diff --git a/builder/vmware/vmx/step_clone_vmx.go b/builder/vmware/vmx/step_clone_vmx.go index 9c150e75f..2028b5d3e 100644 --- a/builder/vmware/vmx/step_clone_vmx.go +++ b/builder/vmware/vmx/step_clone_vmx.go @@ -40,7 +40,8 @@ func (s *StepCloneVMX) Run(_ context.Context, state multistep.StateBag) multiste if err := driver.Clone(vmxPath, s.Path, s.Linked); err != nil { state.Put("error", err) - return multistep.ActionHalt + return halt(err) + } // Read in the machine configuration from the cloned VMX file @@ -50,18 +51,14 @@ func (s *StepCloneVMX) Run(_ context.Context, state multistep.StateBag) multiste // addresses // * The disk compaction step needs the paths to all attached disks if remoteDriver, ok := driver.(vmwcommon.RemoteDriver); ok { + remoteVmxPath := vmxPath tempDir, err := ioutil.TempDir("", "packer-vmx") if err != nil { return halt(err) } s.tempDir = tempDir - content, err := remoteDriver.ReadFile(vmxPath) - if err != nil { - return halt(err) - } vmxPath = filepath.Join(tempDir, s.VMName+".vmx") - err = ioutil.WriteFile(vmxPath, content, 0600) - if err != nil { + if err = remoteDriver.Download(remoteVmxPath, vmxPath); err != nil { return halt(err) } } From 53fef09696fa90a2989d62fd9fcbfe32d6763bfe Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Thu, 9 Feb 2017 08:01:41 +0200 Subject: [PATCH 09/32] Update the vmware-vmx documentation. --- .../docs/builders/vmware-vmx.html.md.erb | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/website/source/docs/builders/vmware-vmx.html.md.erb b/website/source/docs/builders/vmware-vmx.html.md.erb index d597bb98d..31f4a7627 100644 --- a/website/source/docs/builders/vmware-vmx.html.md.erb +++ b/website/source/docs/builders/vmware-vmx.html.md.erb @@ -124,6 +124,40 @@ builder. the builder. By default this is `output-BUILDNAME` where "BUILDNAME" is the name of the build. +- `remote_cache_datastore` (string) - The path to the datastore where + supporting files will be stored during the build on the remote machine. By + default this is the same as the `remote_datastore` option. This only has an + effect if `remote_type` is enabled. + +- `remote_cache_directory` (string) - The path where the ISO and/or floppy + files will be stored during the build on the remote machine. The path is + relative to the `remote_cache_datastore` on the remote machine. By default + this is "packer\_cache". This only has an effect if `remote_type` + is enabled. + +- `remote_datastore` (string) - The path to the datastore where the resulting + VM will be stored when it is built on the remote machine. By default this + is "datastore1". This only has an effect if `remote_type` is enabled. + +- `remote_host` (string) - The host of the remote machine used for access. + This is only required if `remote_type` is enabled. + +- `remote_password` (string) - The SSH password for the user used to access + the remote machine. By default this is empty. This only has an effect if + `remote_type` is enabled. + +- `remote_private_key_file` (string) - The path to the PEM encoded private key + file for the user used to access the remote machine. By default this is empty. + This only has an effect if `remote_type` is enabled. + +- `remote_type` (string) - The type of remote machine that will be used to + build this VM rather than a local desktop product. The only value accepted + for this currently is "esx5". If this is not set, a desktop product will + be used. By default, this is not set. + +- `remote_username` (string) - The username for the SSH user that will access + the remote machine. This is required if `remote_type` is enabled. + - `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 @@ -230,6 +264,47 @@ contention. You can tune this delay on a per-builder basis by specifying } ``` +- `` - `` - Simulates pressing a function key. + +- `` `` `` `` - Simulates pressing an arrow key. + +- `` - Simulates pressing the spacebar. + +- `` - Simulates pressing the insert key. + +- `` `` - Simulates pressing the home and end keys. + +- `` `` - Simulates pressing the page up and page down keys. + +- `` `` - Simulates pressing the alt key. + +- `` `` - Simulates pressing the ctrl key. + +- `` `` - Simulates pressing the shift key. + +- `` `` - Simulates pressing and holding the alt key. + +- `` `` - Simulates pressing and holding the ctrl + key. + +- `` `` - Simulates pressing and holding the + shift key. + +- `` `` - Simulates releasing a held alt key. + +- `` `` - Simulates releasing a held ctrl key. + +- `` `` - Simulates releasing a held shift key. + +- `` `` `` - Adds a 1, 5 or 10 second pause before + sending any additional keys. This is useful if you have to generally wait + for the UI to update before typing more. + +In addition to the special keys, each command to type is treated as a +[configuration template](/docs/templates/configuration-templates.html). The +available variables are: +>>>>>>> Update the vmware-vmx documentation.:website/source/docs/builders/vmware-vmx.html.md + <%= partial "partials/builders/boot-command" %> Example boot command. This is actually a working boot command used to start an From 17845beadcdbb8740bed5fe9eb965eb461feb52b Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Thu, 23 Feb 2017 21:55:51 +0200 Subject: [PATCH 10/32] Fix the CommHost tests --- builder/vmware/common/driver_esx5_test.go | 63 +++++++++++------------ 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/builder/vmware/common/driver_esx5_test.go b/builder/vmware/common/driver_esx5_test.go index 8d3d067f2..793a0b62d 100644 --- a/builder/vmware/common/driver_esx5_test.go +++ b/builder/vmware/common/driver_esx5_test.go @@ -4,6 +4,10 @@ import ( "fmt" "net" "testing" + + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/helper/communicator" + "github.com/mitchellh/packer/helper/config" ) func TestESX5Driver_implDriver(t *testing.T) { @@ -56,40 +60,31 @@ func TestESX5Driver_HostIP(t *testing.T) { func TestESX5Driver_CommHost(t *testing.T) { const expected_host = "127.0.0.1" - config := make(map[string]interface{}) - config["communicator"] = "winrm" - config["winrm_username"] = "username" - config["winrm_password"] = "password" - config["winrm_host"] = expected_host + conf := make(map[string]interface{}) + conf["communicator"] = "winrm" + conf["winrm_username"] = "username" + conf["winrm_password"] = "password" + conf["winrm_host"] = expected_host - // var b Builder - // warns, err := b.Prepare(config) - // if len(warns) > 0 { - // t.Fatalf("bad: %#v", warns) - // } - // if err != nil { - // t.Fatalf("should not have error: %s", err) - // } - // if host := b.config.CommConfig.Host(); host != expected_host { - // t.Fatalf("setup failed, bad host name: %s", host) - // } + var commConfig communicator.Config + err := config.Decode(&commConfig, nil, conf) + state := new(multistep.BasicStateBag) + sshConfig := SSHConfig{Comm: commConfig} + state.Put("sshConfig", &sshConfig) + driver := ESX5Driver{CommConfig: commConfig} - // state := new(multistep.BasicStateBag) - // state.Put("config", &b.config) - // - // var driver ESX5Driver{} - // host, err := driver.CommHost(state) - // if err != nil { - // t.Fatalf("should not have error: %s", err) - // } - // if host != expected_host { - // t.Errorf("bad host name: %s", host) - // } - // address, ok := state.GetOk("vm_address") - // if !ok { - // t.Error("state not updated with vm_address") - // } - // if address.(string) != expected_host { - // t.Errorf("bad vm_address: %s", address.(string)) - // } + host, err := driver.CommHost(state) + if err != nil { + t.Fatalf("should not have error: %s", err) + } + if host != expected_host { + t.Errorf("bad host name: %s", host) + } + address, ok := state.GetOk("vm_address") + if !ok { + t.Error("state not updated with vm_address") + } + if address.(string) != expected_host { + t.Errorf("bad vm_address: %s", address.(string)) + } } From c940f7e7543423a463156514d1254322f5199383 Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Thu, 23 Feb 2017 22:08:11 +0200 Subject: [PATCH 11/32] Conditional reload. --- builder/vmware/common/driver_esx5.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index e0d089535..c841e6639 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -119,7 +119,11 @@ func (d *ESX5Driver) IsRunning(string) (bool, error) { } func (d *ESX5Driver) ReloadVM() error { - return d.sh("vim-cmd", "vmsvc/reload", d.vmId) + if d.vmId != "" { + return d.sh("vim-cmd", "vmsvc/reload", d.vmId) + } else { + return nil + } } func (d *ESX5Driver) Start(vmxPathLocal string, headless bool) error { From 2ed6c3ef5efa4895a1b94dedd3ca2f915193c584 Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Thu, 23 Feb 2017 22:08:57 +0200 Subject: [PATCH 12/32] Set output dir always according to the config. --- builder/vmware/vmx/builder.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 260099657..b63216318 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -47,9 +47,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe default: dir = new(vmwcommon.LocalOutputDir) } - if b.config.RemoteType != "" { - b.config.OutputDir = b.config.VMName - } dir.SetOutputDir(b.config.OutputDir) // Set up the state. From c294e65c36bb7d27c510b28981c678ca87c258fd Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Thu, 23 Feb 2017 22:15:09 +0200 Subject: [PATCH 13/32] A separate driver file is not really needed anymore --- builder/vmware/iso/builder.go | 2 +- builder/vmware/iso/driver.go | 13 ------------- website/source/docs/builders/vmware-vmx.html.md.erb | 1 - 3 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 builder/vmware/iso/driver.go diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 236bd454b..c9cd16bce 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -229,7 +229,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { - driver, err := NewDriver(&b.config) + driver, err := vmwcommon.NewDriver(&b.config.DriverConfig, &b.config.SSHConfig, &b.config.CommConfig, b.config.VMName) if err != nil { return nil, fmt.Errorf("Failed creating VMware driver: %s", err) } diff --git a/builder/vmware/iso/driver.go b/builder/vmware/iso/driver.go deleted file mode 100644 index 947ff4c16..000000000 --- a/builder/vmware/iso/driver.go +++ /dev/null @@ -1,13 +0,0 @@ -package iso - -import ( - "fmt" - - vmwcommon "github.com/hashicorp/packer/builder/vmware/common" -) - -// NewDriver returns a new driver implementation for this operating -// system, or an error if the driver couldn't be initialized. -func NewDriver(config *Config) (vmwcommon.Driver, error) { - return vmwcommon.NewDriver(&config.DriverConfig, &config.SSHConfig, &config.CommConfig, config.VMName) -} diff --git a/website/source/docs/builders/vmware-vmx.html.md.erb b/website/source/docs/builders/vmware-vmx.html.md.erb index 31f4a7627..55b56f89f 100644 --- a/website/source/docs/builders/vmware-vmx.html.md.erb +++ b/website/source/docs/builders/vmware-vmx.html.md.erb @@ -303,7 +303,6 @@ contention. You can tune this delay on a per-builder basis by specifying In addition to the special keys, each command to type is treated as a [configuration template](/docs/templates/configuration-templates.html). The available variables are: ->>>>>>> Update the vmware-vmx documentation.:website/source/docs/builders/vmware-vmx.html.md <%= partial "partials/builders/boot-command" %> From e7e6a80587a33a2f22aab219c1656b2214dfc15e Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Thu, 23 Feb 2017 22:29:53 +0200 Subject: [PATCH 14/32] No need to make upload public. --- builder/vmware/common/driver_esx5.go | 6 +++--- builder/vmware/common/remote_driver.go | 2 +- builder/vmware/common/remote_driver_mock.go | 2 +- builder/vmware/common/step_upload_vmx.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index c841e6639..d17f9404a 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -148,7 +148,7 @@ func (d *ESX5Driver) Stop(vmxPathLocal string) error { func (d *ESX5Driver) Register(vmxPathLocal string) error { vmxPath := filepath.ToSlash(filepath.Join(d.outputDir, filepath.Base(vmxPathLocal))) - if err := d.Upload(vmxPath, vmxPathLocal); err != nil { + if err := d.upload(vmxPath, vmxPathLocal); err != nil { return err } r, err := d.run(nil, "vim-cmd", "solo/registervm", strconv.Quote(vmxPath)) @@ -191,7 +191,7 @@ func (d *ESX5Driver) UploadISO(localPath string, checksum string, checksumType s return finalPath, nil } - if err := d.Upload(finalPath, localPath); err != nil { + if err := d.upload(finalPath, localPath); err != nil { return "", err } @@ -637,7 +637,7 @@ func (d *ESX5Driver) mkdir(path string) error { return d.sh("mkdir", "-p", strconv.Quote(path)) } -func (d *ESX5Driver) Upload(dst, src string) error { +func (d *ESX5Driver) upload(dst, src string) error { f, err := os.Open(src) if err != nil { return err diff --git a/builder/vmware/common/remote_driver.go b/builder/vmware/common/remote_driver.go index bc2454297..c0c6ffb60 100644 --- a/builder/vmware/common/remote_driver.go +++ b/builder/vmware/common/remote_driver.go @@ -24,7 +24,7 @@ type RemoteDriver interface { IsDestroyed() (bool, error) // Uploads a local file to remote side. - Upload(dst, src string) error + upload(dst, src string) error // Download a remote file to a local file. Download(src, dst string) error diff --git a/builder/vmware/common/remote_driver_mock.go b/builder/vmware/common/remote_driver_mock.go index 10338c86c..773c7103e 100644 --- a/builder/vmware/common/remote_driver_mock.go +++ b/builder/vmware/common/remote_driver_mock.go @@ -57,7 +57,7 @@ func (d *RemoteDriverMock) IsDestroyed() (bool, error) { return d.IsDestroyedResult, d.IsDestroyedErr } -func (d *RemoteDriverMock) Upload(dst, src string) error { +func (d *RemoteDriverMock) upload(dst, src string) error { return d.UploadErr } diff --git a/builder/vmware/common/step_upload_vmx.go b/builder/vmware/common/step_upload_vmx.go index 646225a7c..cdf64b56a 100644 --- a/builder/vmware/common/step_upload_vmx.go +++ b/builder/vmware/common/step_upload_vmx.go @@ -34,7 +34,7 @@ func (c *StepUploadVMX) Run(_ context.Context, state multistep.StateBag) multist if ok { remoteVmxPath := filepath.ToSlash(filepath.Join(fmt.Sprintf("%s", remoteDriver), filepath.Base(vmxPath))) log.Printf("Uploading VMX file from %s to %s", vmxPath, remoteVmxPath) - if err := remoteDriver.Upload(remoteVmxPath, vmxPath); err != nil { + if err := remoteDriver.upload(remoteVmxPath, vmxPath); err != nil { state.Put("error", fmt.Errorf("Error writing VMX: %s", err)) return multistep.ActionHalt } From f42f5a8ad3578e3a7465f7d4906dceaef5a6f774 Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Fri, 24 Feb 2017 16:05:57 +0200 Subject: [PATCH 15/32] Remove some extra logging. --- builder/vmware/common/driver_esx5.go | 1 - builder/vmware/common/step_upload_vmx.go | 2 -- 2 files changed, 3 deletions(-) diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index d17f9404a..440203ed0 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -74,7 +74,6 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { } for _, f := range linesToArray(filesToClone) { - log.Printf("Copying file %s\n", f) err := d.sh("cp", f, dstDir) if err != nil { return fmt.Errorf("Failing to copy %s to %s: %s", f, dstDir, err) diff --git a/builder/vmware/common/step_upload_vmx.go b/builder/vmware/common/step_upload_vmx.go index cdf64b56a..d0c7b1043 100644 --- a/builder/vmware/common/step_upload_vmx.go +++ b/builder/vmware/common/step_upload_vmx.go @@ -3,7 +3,6 @@ package common import ( "context" "fmt" - "log" "path/filepath" "github.com/hashicorp/packer/helper/multistep" @@ -33,7 +32,6 @@ func (c *StepUploadVMX) Run(_ context.Context, state multistep.StateBag) multist remoteDriver, ok := driver.(RemoteDriver) if ok { remoteVmxPath := filepath.ToSlash(filepath.Join(fmt.Sprintf("%s", remoteDriver), filepath.Base(vmxPath))) - log.Printf("Uploading VMX file from %s to %s", vmxPath, remoteVmxPath) if err := remoteDriver.upload(remoteVmxPath, vmxPath); err != nil { state.Put("error", fmt.Errorf("Error writing VMX: %s", err)) return multistep.ActionHalt From da5f3224845c736d676955c73a7bb9d6590f9575 Mon Sep 17 00:00:00 2001 From: Stuart Miller Date: Fri, 3 Mar 2017 10:46:23 +1300 Subject: [PATCH 16/32] Add quotes around all filenames to prevent issues with spaces --- builder/vmware/common/driver_esx5.go | 25 ++++++++++++++++--------- communicator/ssh/communicator.go | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index 440203ed0..16b67e166 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -16,6 +16,7 @@ import ( "strings" "time" + commonssh "github.com/hashicorp/packer/common/ssh" "github.com/hashicorp/packer/communicator/ssh" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -49,7 +50,7 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { linesToArray := func(lines string) []string { return strings.Split(strings.Trim(lines, "\n"), "\n") } - d.SetOutputDir(path.Dir(dst)) + d.SetOutputDir(path.Dir(filepath.ToSlash(dst))) srcVmx := d.datastorePath(src) dstVmx := d.datastorePath(dst) srcDir := path.Dir(srcVmx) @@ -63,24 +64,28 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { return fmt.Errorf("Failed to create the destination directory %s: %s", d.outputDir, err) } - err = d.sh("cp", srcVmx, dstVmx) + err = d.sh("cp", strconv.Quote(srcVmx), strconv.Quote(dstVmx)) if err != nil { return fmt.Errorf("Failed to copy the vmx file %s: %s", srcVmx, err) } - filesToClone, err := d.run(nil, "find", srcDir, "! -name '*.vmdk' ! -name '*.vmx' -type f ! -size 0") + filesToClone, err := d.run(nil, "find", strconv.Quote(srcDir), "! -name '*.vmdk' ! -name '*.vmx' -type f ! -size 0") if err != nil { return fmt.Errorf("Failed to get the file list to copy: %s", err) } for _, f := range linesToArray(filesToClone) { - err := d.sh("cp", f, dstDir) + // TODO: linesToArray should really return [] if the string is empty. Instead it returns [""] + if f == "" { + continue + } + err := d.sh("cp", strconv.Quote(f), strconv.Quote(dstDir)) if err != nil { return fmt.Errorf("Failing to copy %s to %s: %s", f, dstDir, err) } } - disksToClone, err := d.run(nil, "sed -ne 's/.*file[Nn]ame = \"\\(.*vmdk\\)\"/\\1/p'", srcVmx) + disksToClone, err := d.run(nil, "sed -ne 's/.*file[Nn]ame = \"\\(.*vmdk\\)\"/\\1/p'", strconv.Quote(srcVmx)) if err != nil { return fmt.Errorf("Failing to get the vmdk list to clone %s", err) } @@ -90,7 +95,7 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool) error { srcDisk = disk } destDisk := path.Join(dstDir, path.Base(disk)) - err = d.sh("vmkfstools", "-d thin", "-i", srcDisk, destDisk) + err = d.sh("vmkfstools", "-d thin", "-i", strconv.Quote(srcDisk), strconv.Quote(destDisk)) if err != nil { return fmt.Errorf("Failing to clone disk %s: %s", srcDisk, err) } @@ -443,7 +448,6 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { } if address := d.CommConfig.Host(); address != "" { - state.Put("vm_address", address) return address, nil } @@ -452,7 +456,10 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { return "", err } - record, err := r.find("Name", d.VMName) + spacesToUnderscores := func(string string) string { + return strings.Replace(string, " ", "_", -1) + } + record, err := r.find("Name", spacesToUnderscores(d.VMName)) if err != nil { return "", err } @@ -509,7 +516,7 @@ func (d *ESX5Driver) DirExists() (bool, error) { } func (d *ESX5Driver) ListFiles() ([]string, error) { - stdout, err := d.ssh("ls -1p "+d.outputDir, nil) + stdout, err := d.ssh("ls -1p "+strconv.Quote(d.outputDir), nil) if err != nil { return nil, err } diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index a6ebeefa2..46cbd51d7 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -576,7 +576,7 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e return scpUploadFile(target_file, input, w, stdoutR, fi) } - return c.scpSession("scp -vt "+target_dir, scpFunc) + return c.scpSession("scp -vt "+ strconv.Quote(target_dir), scpFunc) } func (c *comm) scpUploadDirSession(dst string, src string, excl []string) error { From 50a17cfc4e87172d8214a432f694466470173122 Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Fri, 3 Mar 2017 11:13:57 +0200 Subject: [PATCH 17/32] Add a comment about rource_path on remote builders. --- website/source/docs/builders/vmware-vmx.html.md.erb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/source/docs/builders/vmware-vmx.html.md.erb b/website/source/docs/builders/vmware-vmx.html.md.erb index 55b56f89f..c7be55d3f 100644 --- a/website/source/docs/builders/vmware-vmx.html.md.erb +++ b/website/source/docs/builders/vmware-vmx.html.md.erb @@ -56,7 +56,8 @@ builder. ### Required: -- `source_path` (string) - Path to the source VMX file to clone. +- `source_path` (string) - Path to the source VMX file to clone. If + `remote_type` is enabled then this specifies a path on the `remote_host`. ### Optional: From 50a36be5d1238af9702790b3e8acbc576771f3fb Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Fri, 3 Mar 2017 21:53:43 +0200 Subject: [PATCH 18/32] Fix formating. --- communicator/ssh/communicator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 46cbd51d7..5c9be02bc 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -576,7 +576,7 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e return scpUploadFile(target_file, input, w, stdoutR, fi) } - return c.scpSession("scp -vt "+ strconv.Quote(target_dir), scpFunc) + return c.scpSession("scp -vt "+strconv.Quote(target_dir), scpFunc) } func (c *comm) scpUploadDirSession(dst string, src string, excl []string) error { From 63054fb808a200e5582a8d635eaebb44b401f18e Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Fri, 3 Mar 2017 21:54:48 +0200 Subject: [PATCH 19/32] Fix the test. --- builder/vmware/common/driver_esx5.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index 16b67e166..edb74d051 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -448,6 +448,7 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { } if address := d.CommConfig.Host(); address != "" { + state.Put("vm_address", address) return address, nil } From a68a639a1ad4a44e0543423f3c74a6f8f19eb857 Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Sun, 5 Mar 2017 22:15:53 +0200 Subject: [PATCH 20/32] Move step_export to common. --- builder/vmware/{iso => common}/step_export.go | 25 +++++++++++-------- .../{iso => common}/step_export_test.go | 6 ++--- builder/vmware/iso/builder.go | 10 +++++--- 3 files changed, 23 insertions(+), 18 deletions(-) rename builder/vmware/{iso => common}/step_export.go (82%) rename builder/vmware/{iso => common}/step_export_test.go (90%) diff --git a/builder/vmware/iso/step_export.go b/builder/vmware/common/step_export.go similarity index 82% rename from builder/vmware/iso/step_export.go rename to builder/vmware/common/step_export.go index c39443c8f..3b0cf5920 100644 --- a/builder/vmware/iso/step_export.go +++ b/builder/vmware/common/step_export.go @@ -1,4 +1,4 @@ -package iso +package common import ( "bytes" @@ -19,12 +19,14 @@ import ( // Uses: // display_name string type StepExport struct { - Format string - SkipExport bool - OutputDir string + Format string + SkipExport bool + VMName string + OVFToolOptions []string + OutputDir string } -func (s *StepExport) generateArgs(c *Config, displayName string, hidePassword bool) []string { +func (s *StepExport) generateArgs(c *DriverConfig, outputPath string, hidePassword bool) []string { password := url.QueryEscape(c.RemotePassword) if hidePassword { password = "****" @@ -33,18 +35,19 @@ func (s *StepExport) generateArgs(c *Config, displayName string, hidePassword bo "--noSSLVerify=true", "--skipManifestCheck", "-tt=" + s.Format, - "vi://" + c.RemoteUser + ":" + password + "@" + c.RemoteHost + "/" + displayName, - s.OutputDir, + + "vi://" + c.RemoteUser + ":" + password + "@" + c.RemoteHost + "/" + s.VMName, + outputPath, } - return append(c.OVFToolOptions, args...) + return append(s.OVFToolOptions, args...) } func (s *StepExport) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - c := state.Get("config").(*Config) + c := state.Get("driverConfig").(*DriverConfig) ui := state.Get("ui").(packer.Ui) // Skip export if requested - if c.SkipExport { + if s.SkipExport { ui.Say("Skipping export of virtual machine...") return multistep.ActionContinue } @@ -60,7 +63,7 @@ func (s *StepExport) Run(_ context.Context, state multistep.StateBag) multistep. } if _, err := exec.LookPath(ovftool); err != nil { - err := fmt.Errorf("Error %s not found: %s", ovftool, err) + err = fmt.Errorf("Error %s not found: %s", ovftool, err) state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt diff --git a/builder/vmware/iso/step_export_test.go b/builder/vmware/common/step_export_test.go similarity index 90% rename from builder/vmware/iso/step_export_test.go rename to builder/vmware/common/step_export_test.go index 27c2a0c36..70197c827 100644 --- a/builder/vmware/iso/step_export_test.go +++ b/builder/vmware/common/step_export_test.go @@ -1,4 +1,4 @@ -package iso +package common import ( "context" @@ -15,9 +15,9 @@ func testStepExport_wrongtype_impl(t *testing.T, remoteType string) { state := testState(t) step := new(StepExport) - var config Config + var config DriverConfig config.RemoteType = "foo" - state.Put("config", &config) + state.Put("driverConfig", &config) if action := step.Run(context.Background(), state); action != multistep.ActionContinue { t.Fatalf("bad action: %#v", action) diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index c9cd16bce..03b4cfc5d 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -260,6 +260,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe state.Put("hook", hook) state.Put("ui", ui) state.Put("sshConfig", &b.config.SSHConfig) + state.Put("driverConfig", &b.config.DriverConfig) steps := []multistep.Step{ &vmwcommon.StepPrepareTools{ @@ -360,10 +361,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vmwcommon.StepUploadVMX{ RemoteType: b.config.RemoteType, }, - &StepExport{ - Format: b.config.Format, - SkipExport: b.config.SkipExport, - OutputDir: exportOutputPath, + &vmwcommon.StepExport{ + Format: b.config.Format, + SkipExport: b.config.SkipExport, + VMName: b.config.VMName, + OVFToolOptions: b.config.OVFToolOptions, }, } From 3193f50f1725dfa7079a35f512c29acd1923b76f Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Tue, 7 Mar 2017 09:03:04 +0200 Subject: [PATCH 21/32] Support export with the vmx builder. --- builder/vmware/common/export_config.go | 25 +++++++++++++++++++++++++ builder/vmware/iso/builder.go | 17 +++++++++++------ builder/vmware/vmx/builder.go | 25 ++++++++++++++++++++++--- builder/vmware/vmx/config.go | 2 ++ 4 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 builder/vmware/common/export_config.go diff --git a/builder/vmware/common/export_config.go b/builder/vmware/common/export_config.go new file mode 100644 index 000000000..fae944cb9 --- /dev/null +++ b/builder/vmware/common/export_config.go @@ -0,0 +1,25 @@ +package common + +import ( + "fmt" + + "github.com/mitchellh/packer/template/interpolate" +) + +type ExportConfig struct { + Format string `mapstructure:"format"` + OVFToolOptions []string `mapstructure:"ovftool_options"` + SkipExport bool `mapstructure:"skip_export"` + KeepRegistered bool `mapstructure:"keep_registered"` +} + +func (c *ExportConfig) Prepare(ctx *interpolate.Context) []error { + var errs []error + if c.Format != "" { + if !(c.Format == "ova" || c.Format == "ovf" || c.Format == "vmx") { + errs = append( + errs, fmt.Errorf("format must be one of ova, ovf, or vmx")) + } + } + return errs +} diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 03b4cfc5d..91050d0f2 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -37,6 +37,7 @@ type Config struct { vmwcommon.SSHConfig `mapstructure:",squash"` vmwcommon.ToolsConfig `mapstructure:",squash"` vmwcommon.VMXConfig `mapstructure:",squash"` + vmwcommon.ExportConfig `mapstructure:",squash"` // disk drives AdditionalDiskSize []uint `mapstructure:"disk_additional_size"` @@ -67,12 +68,13 @@ type Config struct { Parallel string `mapstructure:"parallel"` // booting a guest - KeepRegistered bool `mapstructure:"keep_registered"` - OVFToolOptions []string `mapstructure:"ovftool_options"` - SkipCompaction bool `mapstructure:"skip_compaction"` - SkipExport bool `mapstructure:"skip_export"` - VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"` - VMXTemplatePath string `mapstructure:"vmx_template_path"` + KeepRegistered bool `mapstructure:"keep_registered"` + OVFToolOptions []string `mapstructure:"ovftool_options"` + SkipCompaction bool `mapstructure:"skip_compaction"` + SkipExport bool `mapstructure:"skip_export"` + + VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"` + VMXTemplatePath string `mapstructure:"vmx_template_path"` CommConfig communicator.Config `mapstructure:",squash"` @@ -112,6 +114,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 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)...) + errs = packer.MultiErrorAppend(errs, b.config.ExportConfig.Prepare(&b.config.ctx)...) if b.config.DiskName == "" { b.config.DiskName = "disk" @@ -188,6 +191,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("remote_host must be specified")) } + if b.config.RemoteType != "esx5" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("Only 'esx5' value is accepted for remote_type")) @@ -353,6 +357,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vmwcommon.StepConfigureVMX{ CustomData: b.config.VMXDataPost, SkipFloppy: true, + VMName: b.config.VMName, }, &vmwcommon.StepCleanVMX{ RemoveEthernetInterfaces: b.config.VMXConfig.VMXRemoveEthernet, diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index b63216318..f5cab8ae4 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -47,6 +47,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe default: dir = new(vmwcommon.LocalOutputDir) } + if b.config.RemoteType != "" && b.config.Format != "" { + b.config.OutputDir = b.config.VMName + } dir.SetOutputDir(b.config.OutputDir) // Set up the state. @@ -58,6 +61,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe state.Put("hook", hook) state.Put("ui", ui) state.Put("sshConfig", &b.config.SSHConfig) + state.Put("driverConfig", &b.config.DriverConfig) // Build the steps. steps := []multistep.Step{ @@ -99,8 +103,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe VNCDisablePassword: b.config.VNCDisablePassword, }, &vmwcommon.StepRegister{ - Format: "", - KeepRegistered: false, + Format: b.config.Format, + KeepRegistered: b.config.KeepRegistered, }, &vmwcommon.StepRun{ DurationBeforeStop: 5 * time.Second, @@ -140,6 +144,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vmwcommon.StepConfigureVMX{ CustomData: b.config.VMXDataPost, SkipFloppy: true, + VMName: b.config.VMName, }, &vmwcommon.StepCleanVMX{ RemoveEthernetInterfaces: b.config.VMXConfig.VMXRemoveEthernet, @@ -148,6 +153,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vmwcommon.StepUploadVMX{ RemoteType: b.config.RemoteType, }, + &vmwcommon.StepExport{ + Format: b.config.Format, + SkipExport: b.config.SkipExport, + VMName: b.config.VMName, + OVFToolOptions: b.config.OVFToolOptions, + }, } // Run the steps. @@ -167,7 +178,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe if _, ok := state.GetOk(multistep.StateHalted); ok { return nil, errors.New("Build was halted.") } - files, err := state.Get("dir").(vmwcommon.OutputDir).ListFiles() + // Compile the artifact list + var files []string + if b.config.RemoteType != "" && b.config.Format != "" { + dir = new(vmwcommon.LocalOutputDir) + dir.SetOutputDir(b.config.OutputDir) + files, err = dir.ListFiles() + } else { + files, err = state.Get("dir").(vmwcommon.OutputDir).ListFiles() + } if err != nil { return nil, err } diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index 9a52c8ea9..e21daba2f 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -26,6 +26,7 @@ type Config struct { vmwcommon.SSHConfig `mapstructure:",squash"` vmwcommon.ToolsConfig `mapstructure:",squash"` vmwcommon.VMXConfig `mapstructure:",squash"` + vmwcommon.ExportConfig `mapstructure:",squash"` Linked bool `mapstructure:"linked"` RemoteType string `mapstructure:"remote_type"` @@ -73,6 +74,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { 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)...) + errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(&c.ctx)...) if c.DriverConfig.RemoteType == "" { if c.SourcePath == "" { From 77abb3afd4861842fd152b234b4bb32db024242a Mon Sep 17 00:00:00 2001 From: Alexander Laamanen Date: Tue, 7 Mar 2017 09:07:35 +0200 Subject: [PATCH 22/32] Copy and paste export related documentation. --- .../docs/builders/vmware-vmx.html.md.erb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/website/source/docs/builders/vmware-vmx.html.md.erb b/website/source/docs/builders/vmware-vmx.html.md.erb index c7be55d3f..0fe3a6734 100644 --- a/website/source/docs/builders/vmware-vmx.html.md.erb +++ b/website/source/docs/builders/vmware-vmx.html.md.erb @@ -192,6 +192,25 @@ builder. slightly larger. If you find this to be the case, you can disable compaction using this configuration value. Defaults to `false`. +- `skip_export` (boolean) - Defaults to `false`. When enabled, Packer will + not export the VM. Useful if the build output is not the resultant image, + but created inside the VM. + +- `keep_registered` (boolean) - Set this to `true` if you would like to keep + the VM registered with the remote ESXi server. This is convenient if you + use packer to provision VMs on ESXi and don't want to use ovftool to + deploy the resulting artifact (VMX or OVA or whatever you used as `format`). + Defaults to `false`. + +- `ovftool_options` (array of strings) - Extra options to pass to ovftool + during export. Each item in the array is a new argument. The options + `--noSSLVerify`, `--skipManifestCheck`, and `--targetType` are reserved, + and should not be passed to this argument. + +- `format` (string) - Either "ovf", "ova" or "vmx", this specifies the output + format of the exported virtual machine. This defaults to "ovf". + Before using this option, you need to install `ovftool`. + - `tools_upload_flavor` (string) - The flavor of the VMware Tools ISO to upload into the VM. Valid values are `darwin`, `linux`, and `windows`. By default, this is empty, which means VMware tools won't be uploaded. From 290f4a7c4c342fa3bd45900308bb6e3189f7481d Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 25 Oct 2018 14:17:35 -0700 Subject: [PATCH 23/32] cleanup to make it actually build after that monstrous rebase --- builder/vmware/common/artifact.go | 13 ++++++++++--- builder/vmware/common/driver.go | 3 ++- builder/vmware/common/driver_esx5.go | 15 +++++++-------- builder/vmware/common/export_config.go | 2 +- builder/vmware/common/remote_artifact.go | 8 +------- builder/vmware/common/step_configure_vmx.go | 2 -- builder/vmware/common/step_export.go | 2 +- builder/vmware/common/step_register.go | 4 +++- builder/vmware/iso/builder.go | 8 +++++++- builder/vmware/iso/step_remote_upload.go | 8 ++++---- builder/vmware/vmx/builder.go | 9 ++++++++- post-processor/vsphere-template/post-processor.go | 12 ++++++------ 12 files changed, 50 insertions(+), 36 deletions(-) diff --git a/builder/vmware/common/artifact.go b/builder/vmware/common/artifact.go index cb61cd2f4..b6d3c4cd6 100644 --- a/builder/vmware/common/artifact.go +++ b/builder/vmware/common/artifact.go @@ -3,6 +3,7 @@ package common import ( "fmt" "os" + "path/filepath" "github.com/hashicorp/packer/packer" ) @@ -11,6 +12,12 @@ import ( const BuilderId = "mitchellh.vmware" const BuilderIdESX = "mitchellh.vmware-esx" +const ( + ArtifactConfFormat = "artifact.conf.format" + ArtifactConfKeepRegistered = "artifact.conf.keep_registered" + ArtifactConfSkipExport = "artifact.conf.skip_export" +) + // Artifact is the result of running the VMware builder, namely a set // of files associated with the resulting machine. type artifact struct { @@ -48,7 +55,7 @@ func NewLocalArtifact(id string, dir string) (packer.Artifact, error) { }, nil } -func NewArtifact(dir OutputDir, files []string, esxi bool) (packer.Artifact, err) { +func NewArtifact(dir OutputDir, files []string, config map[string]string, esxi bool) (packer.Artifact, error) { builderID := BuilderId if esxi { builderID = BuilderIdESX @@ -69,7 +76,7 @@ func (a *artifact) Files() []string { return a.f } -func (*artifact) Id() string { +func (a *artifact) Id() string { return a.id } @@ -78,7 +85,7 @@ func (a *artifact) String() string { } func (a *artifact) State(name string) interface{} { - return nil + return a.config[name] } func (a *artifact) Destroy() error { diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index ed37ff207..b230d84b9 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -84,6 +84,7 @@ type Driver interface { // system, or an error if the driver couldn't be initialized. func NewDriver(dconfig *DriverConfig, config *SSHConfig, commConfig *communicator.Config, vmName string) (Driver, error) { drivers := []Driver{} + if dconfig.RemoteType != "" { drivers = []Driver{ &ESX5Driver{ @@ -91,7 +92,7 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig, commConfig *communicato Port: dconfig.RemotePort, Username: dconfig.RemoteUser, Password: dconfig.RemotePassword, - PrivateKey: dconfig.RemotePrivateKey, + PrivateKeyFile: dconfig.RemotePrivateKey, Datastore: dconfig.RemoteDatastore, CacheDatastore: dconfig.RemoteCacheDatastore, CacheDirectory: dconfig.RemoteCacheDirectory, diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index edb74d051..e53a73d54 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -16,7 +16,6 @@ import ( "strings" "time" - commonssh "github.com/hashicorp/packer/common/ssh" "github.com/hashicorp/packer/communicator/ssh" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -28,7 +27,7 @@ import ( // ESX5 driver talks to an ESXi5 hypervisor remotely over SSH to build // virtual machines. This driver can only manage one machine at a time. type ESX5Driver struct { - base vmwcommon.VmwareDriver + base VmwareDriver Host string Port uint @@ -184,13 +183,13 @@ func (d *ESX5Driver) IsDestroyed() (bool, error) { } func (d *ESX5Driver) UploadISO(localPath string, checksum string, checksumType string) (string, error) { - finalPath := d.cachePath(localPath) + finalPath := d.CachePath(localPath) if err := d.mkdir(filepath.ToSlash(filepath.Dir(finalPath))); err != nil { return "", err } log.Printf("Verifying checksum of %s", finalPath) - if d.verifyChecksum(checksumType, checksum, finalPath) { + if d.VerifyChecksum(checksumType, checksum, finalPath) { log.Println("Initial checksum matched, no upload needed.") return finalPath, nil } @@ -203,7 +202,7 @@ func (d *ESX5Driver) UploadISO(localPath string, checksum string, checksumType s } func (d *ESX5Driver) RemoveCache(localPath string) error { - finalPath := d.cachePath(localPath) + finalPath := d.CachePath(localPath) log.Printf("Removing remote cache path %s (local %s)", finalPath, localPath) return d.sh("rm", "-f", strconv.Quote(finalPath)) } @@ -564,7 +563,7 @@ func (d *ESX5Driver) datastorePath(path string) string { return filepath.ToSlash(filepath.Join("/vmfs/volumes", d.Datastore, dirPath, filepath.Base(path))) } -func (d *ESX5Driver) cachePath(path string) string { +func (d *ESX5Driver) CachePath(path string) string { return filepath.ToSlash(filepath.Join("/vmfs/volumes", d.CacheDatastore, d.CacheDirectory, filepath.Base(path))) } @@ -662,7 +661,7 @@ func (d *ESX5Driver) Download(src, dst string) error { return d.comm.Download(d.datastorePath(src), file) } -func (d *ESX5Driver) verifyChecksum(ctype string, hash string, file string) bool { +func (d *ESX5Driver) VerifyChecksum(ctype string, hash string, file string) bool { if ctype == "none" { if err := d.sh("stat", strconv.Quote(file)); err != nil { return false @@ -731,7 +730,7 @@ func (d *ESX5Driver) esxcli(args ...string) (*esxcliReader, error) { return &esxcliReader{r, header}, nil } -func (d *ESX5Driver) GetVmwareDriver() vmwcommon.VmwareDriver { +func (d *ESX5Driver) GetVmwareDriver() VmwareDriver { return d.base } diff --git a/builder/vmware/common/export_config.go b/builder/vmware/common/export_config.go index fae944cb9..663972847 100644 --- a/builder/vmware/common/export_config.go +++ b/builder/vmware/common/export_config.go @@ -3,7 +3,7 @@ package common import ( "fmt" - "github.com/mitchellh/packer/template/interpolate" + "github.com/hashicorp/packer/template/interpolate" ) type ExportConfig struct { diff --git a/builder/vmware/common/remote_artifact.go b/builder/vmware/common/remote_artifact.go index 37dbc1812..4cc1b2a2f 100644 --- a/builder/vmware/common/remote_artifact.go +++ b/builder/vmware/common/remote_artifact.go @@ -4,12 +4,6 @@ import ( "fmt" ) -const ( - ArtifactConfFormat = "artifact.conf.format" - ArtifactConfKeepRegistered = "artifact.conf.keep_registered" - ArtifactConfSkipExport = "artifact.conf.skip_export" -) - // Artifact is the result of running the VMware builder, namely a set // of files associated with the resulting machine. type RemoteArtifact struct { @@ -28,7 +22,7 @@ func (a *RemoteArtifact) Files() []string { return a.f } -func (*RemoteArtifact) Id() string { +func (a *RemoteArtifact) Id() string { return a.id } diff --git a/builder/vmware/common/step_configure_vmx.go b/builder/vmware/common/step_configure_vmx.go index 03162ed35..2c47590c1 100644 --- a/builder/vmware/common/step_configure_vmx.go +++ b/builder/vmware/common/step_configure_vmx.go @@ -1,7 +1,6 @@ package common import ( - "bytes" "context" "fmt" "log" @@ -29,7 +28,6 @@ type StepConfigureVMX struct { func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { log.Printf("Configuring VMX...\n") - var vmxContents []byte var err error ui := state.Get("ui").(packer.Ui) diff --git a/builder/vmware/common/step_export.go b/builder/vmware/common/step_export.go index 3b0cf5920..fb891151a 100644 --- a/builder/vmware/common/step_export.go +++ b/builder/vmware/common/step_export.go @@ -71,7 +71,7 @@ func (s *StepExport) Run(_ context.Context, state multistep.StateBag) multistep. // Export the VM if s.OutputDir == "" { - s.OutputDir = c.VMName + "." + s.Format + s.OutputDir = s.VMName + "." + s.Format } if s.Format == "ova" { diff --git a/builder/vmware/common/step_register.go b/builder/vmware/common/step_register.go index 0fbc1cc95..0d9b93b7b 100644 --- a/builder/vmware/common/step_register.go +++ b/builder/vmware/common/step_register.go @@ -13,11 +13,13 @@ type StepRegister struct { registeredPath string Format string KeepRegistered bool + SkipExport bool } func (s *StepRegister) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) + vmxPath := state.Get("vmx_path").(string) if remoteDriver, ok := driver.(RemoteDriver); ok { @@ -51,7 +53,7 @@ func (s *StepRegister) Cleanup(state multistep.StateBag) { } if remoteDriver, ok := driver.(RemoteDriver); ok { - if s.Format == "" || config.SkipExport { + if s.Format == "" || s.SkipExport { ui.Say("Unregistering virtual machine...") if err := remoteDriver.Unregister(s.registeredPath); err != nil { ui.Error(fmt.Sprintf("Error unregistering VM: %s", err)) diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 91050d0f2..0ad07a055 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -318,6 +318,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vmwcommon.StepRegister{ Format: b.config.Format, KeepRegistered: b.config.KeepRegistered, + SkipExport: b.config.SkipExport, }, &vmwcommon.StepRun{ DurationBeforeStop: 5 * time.Second, @@ -405,7 +406,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, err } - return vmwcommon.NewArtifact(dir, files, b.config.RemoteType != ""), nil + config := make(map[string]string) + config[vmwcommon.ArtifactConfKeepRegistered] = strconv.FormatBool(b.config.KeepRegistered) + config[vmwcommon.ArtifactConfFormat] = b.config.Format + config[vmwcommon.ArtifactConfSkipExport] = strconv.FormatBool(b.config.SkipExport) + + return vmwcommon.NewArtifact(dir, files, config, b.config.RemoteType != "") } func (b *Builder) Cancel() { diff --git a/builder/vmware/iso/step_remote_upload.go b/builder/vmware/iso/step_remote_upload.go index 8391753a6..f034d1fc3 100644 --- a/builder/vmware/iso/step_remote_upload.go +++ b/builder/vmware/iso/step_remote_upload.go @@ -36,10 +36,10 @@ func (s *stepRemoteUpload) Run(_ context.Context, state multistep.StateBag) mult checksum := config.ISOChecksum checksumType := config.ISOChecksumType - if esx5, ok := remote.(*ESX5Driver); ok { - remotePath := esx5.cachePath(path) + if esx5, ok := remote.(*vmwcommon.ESX5Driver); ok { + remotePath := esx5.CachePath(path) - if esx5.verifyChecksum(checksumType, checksum, remotePath) { + if esx5.VerifyChecksum(checksumType, checksum, remotePath) { ui.Say("Remote cache was verified skipping remote upload...") state.Put(s.Key, remotePath) return multistep.ActionContinue @@ -68,7 +68,7 @@ func (s *stepRemoteUpload) Cleanup(state multistep.StateBag) { driver := state.Get("driver").(vmwcommon.Driver) - remote, ok := driver.(RemoteDriver) + remote, ok := driver.(vmwcommon.RemoteDriver) if !ok { return } diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index f5cab8ae4..eb4ee7342 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "log" + "strconv" "time" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" @@ -105,6 +106,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &vmwcommon.StepRegister{ Format: b.config.Format, KeepRegistered: b.config.KeepRegistered, + SkipExport: b.config.SkipExport, }, &vmwcommon.StepRun{ DurationBeforeStop: 5 * time.Second, @@ -191,7 +193,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, err } - return vmwcommon.NewArtifact(dir, files, b.config.RemoteType != ""), nil + config := make(map[string]string) + config[vmwcommon.ArtifactConfKeepRegistered] = strconv.FormatBool(b.config.KeepRegistered) + config[vmwcommon.ArtifactConfFormat] = b.config.Format + config[vmwcommon.ArtifactConfSkipExport] = strconv.FormatBool(b.config.SkipExport) + + return vmwcommon.NewArtifact(dir, files, config, b.config.RemoteType != "") } // Cancel. diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index deed5c2d7..d6b4fa769 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -8,7 +8,7 @@ import ( "strings" "time" - "github.com/hashicorp/packer/builder/vmware/iso" + vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/multistep" @@ -19,8 +19,8 @@ import ( ) var builtins = map[string]string{ - vsphere.BuilderId: "vmware", - iso.BuilderIdESX: "vmware", + vsphere.BuilderId: "vmware", + vmwcommon.BuilderIdESX: "vmware", } type Config struct { @@ -96,9 +96,9 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac "Artifact type %s does not fit this requirement", artifact.BuilderId()) } - f := artifact.State(iso.ArtifactConfFormat) - k := artifact.State(iso.ArtifactConfKeepRegistered) - s := artifact.State(iso.ArtifactConfSkipExport) + f := artifact.State(vmwcommon.ArtifactConfFormat) + k := artifact.State(vmwcommon.ArtifactConfKeepRegistered) + s := artifact.State(vmwcommon.ArtifactConfSkipExport) if f != "" && k != "true" && s == "false" { return nil, false, errors.New("To use this post-processor with exporting behavior you need set keep_registered as true") From 2ae235a28a45e0f2d812f48729affeae842b8b28 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 25 Oct 2018 14:41:01 -0700 Subject: [PATCH 24/32] fix tests --- builder/vmware/common/artifact.go | 5 +++-- builder/vmware/common/artifact_test.go | 3 ++- builder/vmware/common/driver_esx5_test.go | 9 +++++---- builder/vmware/common/step_configure_vmx.go | 1 - builder/vmware/iso/builder.go | 2 +- builder/vmware/vmx/builder.go | 2 +- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/builder/vmware/common/artifact.go b/builder/vmware/common/artifact.go index b6d3c4cd6..bab8478ae 100644 --- a/builder/vmware/common/artifact.go +++ b/builder/vmware/common/artifact.go @@ -55,7 +55,7 @@ func NewLocalArtifact(id string, dir string) (packer.Artifact, error) { }, nil } -func NewArtifact(dir OutputDir, files []string, config map[string]string, esxi bool) (packer.Artifact, error) { +func NewArtifact(vmname string, dir OutputDir, files []string, config map[string]string, esxi bool) (packer.Artifact, error) { builderID := BuilderId if esxi { builderID = BuilderIdESX @@ -63,13 +63,14 @@ func NewArtifact(dir OutputDir, files []string, config map[string]string, esxi b return &artifact{ builderId: builderID, + id: vmname, dir: dir.String(), f: files, }, nil } func (a *artifact) BuilderId() string { - return BuilderId + return a.builderId } func (a *artifact) Files() []string { diff --git a/builder/vmware/common/artifact_test.go b/builder/vmware/common/artifact_test.go index 3e6a1cd23..c2bbac5dc 100644 --- a/builder/vmware/common/artifact_test.go +++ b/builder/vmware/common/artifact_test.go @@ -32,7 +32,8 @@ func TestNewLocalArtifact(t *testing.T) { dir.SetOutputDir(td) files, err := dir.ListFiles() - a := NewArtifact(dir, files, false) + config := make(map[string]string) + a, err := NewArtifact("vm1", dir, files, config, false) if a.BuilderId() != BuilderId { t.Fatalf("bad: %#v", a.BuilderId()) diff --git a/builder/vmware/common/driver_esx5_test.go b/builder/vmware/common/driver_esx5_test.go index 793a0b62d..15b823737 100644 --- a/builder/vmware/common/driver_esx5_test.go +++ b/builder/vmware/common/driver_esx5_test.go @@ -5,9 +5,9 @@ import ( "net" "testing" - "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/helper/communicator" - "github.com/mitchellh/packer/helper/config" + "github.com/hashicorp/packer/helper/communicator" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/helper/multistep" ) func TestESX5Driver_implDriver(t *testing.T) { @@ -51,8 +51,9 @@ func TestESX5Driver_HostIP(t *testing.T) { defer listen.Close() driver := ESX5Driver{Host: "localhost", Port: uint(port)} + state := new(multistep.BasicStateBag) - if host, _ := driver.HostIP(); host != expected_host { + if host, _ := driver.HostIP(state); host != expected_host { t.Error(fmt.Sprintf("Expected string, %s but got %s", expected_host, host)) } } diff --git a/builder/vmware/common/step_configure_vmx.go b/builder/vmware/common/step_configure_vmx.go index 2c47590c1..3692a2c0c 100644 --- a/builder/vmware/common/step_configure_vmx.go +++ b/builder/vmware/common/step_configure_vmx.go @@ -46,7 +46,6 @@ func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) mult // Create a new UUID for this VM, since it is a new VM vmxData["uuid.action"] = "create" - vmxData["displayname"] = s.VMName // Delete any generated addresses since we want to regenerate // them. Conflicting MAC addresses is a bad time. addrRegex := regexp.MustCompile(`(?i)^ethernet\d+\.generatedAddress`) diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 0ad07a055..2b410533f 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -411,7 +411,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe config[vmwcommon.ArtifactConfFormat] = b.config.Format config[vmwcommon.ArtifactConfSkipExport] = strconv.FormatBool(b.config.SkipExport) - return vmwcommon.NewArtifact(dir, files, config, b.config.RemoteType != "") + return vmwcommon.NewArtifact(b.config.VMName, dir, files, config, b.config.RemoteType != "") } func (b *Builder) Cancel() { diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index eb4ee7342..1bd470f37 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -198,7 +198,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe config[vmwcommon.ArtifactConfFormat] = b.config.Format config[vmwcommon.ArtifactConfSkipExport] = strconv.FormatBool(b.config.SkipExport) - return vmwcommon.NewArtifact(dir, files, config, b.config.RemoteType != "") + return vmwcommon.NewArtifact(b.config.VMName, dir, files, config, b.config.RemoteType != "") } // Cancel. From fa12113eaf07483c8c3ae2640b60e8389e0ae536 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 25 Oct 2018 15:07:07 -0700 Subject: [PATCH 25/32] remove unused NewLocalArtifact consolidate artifacts entirely remove local artifact object and get artifacts in the vmx builder the same way we do in the vmware iso builder --- builder/vmware/common/artifact.go | 92 +++++++++---------- builder/vmware/common/artifact_test.go | 36 -------- builder/vmware/common/output_dir.go | 1 + builder/vmware/common/remote_artifact.go | 39 -------- builder/vmware/common/remote_artifact_test.go | 15 --- builder/vmware/common/step_export.go | 4 +- builder/vmware/iso/builder.go | 23 +---- builder/vmware/vmx/builder.go | 22 +---- 8 files changed, 51 insertions(+), 181 deletions(-) delete mode 100644 builder/vmware/common/remote_artifact.go delete mode 100644 builder/vmware/common/remote_artifact_test.go diff --git a/builder/vmware/common/artifact.go b/builder/vmware/common/artifact.go index bab8478ae..729dc0e17 100644 --- a/builder/vmware/common/artifact.go +++ b/builder/vmware/common/artifact.go @@ -2,17 +2,17 @@ package common import ( "fmt" - "os" - "path/filepath" + "strconv" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) -// BuilderId for the local artifacts -const BuilderId = "mitchellh.vmware" -const BuilderIdESX = "mitchellh.vmware-esx" - const ( + // BuilderId for the local artifacts + BuilderId = "mitchellh.vmware" + BuilderIdESX = "mitchellh.vmware-esx" + ArtifactConfFormat = "artifact.conf.format" ArtifactConfKeepRegistered = "artifact.conf.keep_registered" ArtifactConfSkipExport = "artifact.conf.skip_export" @@ -23,52 +23,11 @@ const ( type artifact struct { builderId string id string - dir string + dir OutputDir f []string config map[string]string } -// NewLocalArtifact returns a VMware artifact containing the files -// in the given directory. -// NewLocalArtifact returns a VMware artifact containing the files -// in the given directory. -func NewLocalArtifact(id string, dir string) (packer.Artifact, error) { - files := make([]string, 0, 5) - visit := func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() { - files = append(files, path) - } - return nil - } - - if err := filepath.Walk(dir, visit); err != nil { - return nil, err - } - - return &artifact{ - builderId: id, - dir: dir, - f: files, - }, nil -} - -func NewArtifact(vmname string, dir OutputDir, files []string, config map[string]string, esxi bool) (packer.Artifact, error) { - builderID := BuilderId - if esxi { - builderID = BuilderIdESX - } - - return &artifact{ - builderId: builderID, - id: vmname, - dir: dir.String(), - f: files, - }, nil -} - func (a *artifact) BuilderId() string { return a.builderId } @@ -90,5 +49,40 @@ func (a *artifact) State(name string) interface{} { } func (a *artifact) Destroy() error { - return os.RemoveAll(a.dir) + return a.dir.RemoveAll() +} + +func NewArtifact(remoteType string, format string, exportOutputPath string, vmName string, skipExport bool, keepRegistered bool, state multistep.StateBag) (packer.Artifact, error) { + var files []string + var dir OutputDir + var err error + if remoteType != "" && format != "" && !skipExport { + dir = new(LocalOutputDir) + dir.SetOutputDir(exportOutputPath) + files, err = dir.ListFiles() + } else { + files, err = state.Get("dir").(OutputDir).ListFiles() + } + if err != nil { + return nil, err + } + + // Set the proper builder ID + builderId := BuilderId + if remoteType != "" { + builderId = BuilderIdESX + } + + config := make(map[string]string) + config[ArtifactConfKeepRegistered] = strconv.FormatBool(keepRegistered) + config[ArtifactConfFormat] = format + config[ArtifactConfSkipExport] = strconv.FormatBool(skipExport) + + return &artifact{ + builderId: builderId, + id: vmName, + dir: dir, + f: files, + config: config, + }, nil } diff --git a/builder/vmware/common/artifact_test.go b/builder/vmware/common/artifact_test.go index c2bbac5dc..dd2c64b74 100644 --- a/builder/vmware/common/artifact_test.go +++ b/builder/vmware/common/artifact_test.go @@ -1,9 +1,6 @@ package common import ( - "io/ioutil" - "os" - "path/filepath" "testing" "github.com/hashicorp/packer/packer" @@ -12,36 +9,3 @@ import ( func TestLocalArtifact_impl(t *testing.T) { var _ packer.Artifact = new(artifact) } - -func TestNewLocalArtifact(t *testing.T) { - td, err := ioutil.TempDir("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - defer os.RemoveAll(td) - - err = ioutil.WriteFile(filepath.Join(td, "a"), []byte("foo"), 0644) - if err != nil { - t.Fatalf("err: %s", err) - } - - if err := os.Mkdir(filepath.Join(td, "b"), 0755); err != nil { - t.Fatalf("err: %s", err) - } - dir := new(LocalOutputDir) - dir.SetOutputDir(td) - files, err := dir.ListFiles() - - config := make(map[string]string) - a, err := NewArtifact("vm1", dir, files, config, false) - - if a.BuilderId() != BuilderId { - t.Fatalf("bad: %#v", a.BuilderId()) - } - if a.Id() != "vm1" { - t.Fatalf("bad: %#v", a.Id()) - } - if len(a.Files()) != 1 { - t.Fatalf("should length 1: %d", len(a.Files())) - } -} diff --git a/builder/vmware/common/output_dir.go b/builder/vmware/common/output_dir.go index 8af513a19..85b690f31 100644 --- a/builder/vmware/common/output_dir.go +++ b/builder/vmware/common/output_dir.go @@ -4,6 +4,7 @@ package common // of the output directory for VMware-based products. The abstraction is made // so that the output directory can be properly made on remote (ESXi) based // VMware products as well as local. +// For remote builds, OutputDir interface is satisfied by the ESX5Driver. type OutputDir interface { DirExists() (bool, error) ListFiles() ([]string, error) diff --git a/builder/vmware/common/remote_artifact.go b/builder/vmware/common/remote_artifact.go deleted file mode 100644 index 4cc1b2a2f..000000000 --- a/builder/vmware/common/remote_artifact.go +++ /dev/null @@ -1,39 +0,0 @@ -package common - -import ( - "fmt" -) - -// Artifact is the result of running the VMware builder, namely a set -// of files associated with the resulting machine. -type RemoteArtifact struct { - builderId string - id string - dir OutputDir - f []string - config map[string]string -} - -func (a *RemoteArtifact) BuilderId() string { - return a.builderId -} - -func (a *RemoteArtifact) Files() []string { - return a.f -} - -func (a *RemoteArtifact) Id() string { - return a.id -} - -func (a *RemoteArtifact) String() string { - return fmt.Sprintf("VM files in directory: %s", a.dir) -} - -func (a *RemoteArtifact) State(name string) interface{} { - return a.config[name] -} - -func (a *RemoteArtifact) Destroy() error { - return a.dir.RemoveAll() -} diff --git a/builder/vmware/common/remote_artifact_test.go b/builder/vmware/common/remote_artifact_test.go deleted file mode 100644 index 292a87b54..000000000 --- a/builder/vmware/common/remote_artifact_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package common - -import ( - "testing" - - "github.com/hashicorp/packer/packer" -) - -func TestArtifact_Impl(t *testing.T) { - var raw interface{} - raw = &RemoteArtifact{} - if _, ok := raw.(packer.Artifact); !ok { - t.Fatal("Artifact must be a proper artifact") - } -} diff --git a/builder/vmware/common/step_export.go b/builder/vmware/common/step_export.go index fb891151a..088541d0d 100644 --- a/builder/vmware/common/step_export.go +++ b/builder/vmware/common/step_export.go @@ -26,7 +26,7 @@ type StepExport struct { OutputDir string } -func (s *StepExport) generateArgs(c *DriverConfig, outputPath string, hidePassword bool) []string { +func (s *StepExport) generateArgs(c *DriverConfig, displayName string, outputPath string, hidePassword bool) []string { password := url.QueryEscape(c.RemotePassword) if hidePassword { password = "****" @@ -36,7 +36,7 @@ func (s *StepExport) generateArgs(c *DriverConfig, outputPath string, hidePasswo "--skipManifestCheck", "-tt=" + s.Format, - "vi://" + c.RemoteUser + ":" + password + "@" + c.RemoteHost + "/" + s.VMName, + "vi://" + c.RemoteUser + ":" + password + "@" + c.RemoteHost + "/" + displayName, outputPath, } return append(s.OVFToolOptions, args...) diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 2b410533f..9ec662dca 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "log" "os" - "strconv" "time" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" @@ -247,8 +246,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe dir = new(vmwcommon.LocalOutputDir) } - exportOutputPath := b.config.OutputDir - if b.config.RemoteType != "" { b.config.OutputDir = b.config.VMName } @@ -394,24 +391,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } // Compile the artifact list - var files []string - if b.config.RemoteType != "" && b.config.Format != "" && !b.config.SkipExport { - dir = new(vmwcommon.LocalOutputDir) - dir.SetOutputDir(exportOutputPath) - files, err = dir.ListFiles() - } else { - files, err = state.Get("dir").(vmwcommon.OutputDir).ListFiles() - } - if err != nil { - return nil, err - } - - config := make(map[string]string) - config[vmwcommon.ArtifactConfKeepRegistered] = strconv.FormatBool(b.config.KeepRegistered) - config[vmwcommon.ArtifactConfFormat] = b.config.Format - config[vmwcommon.ArtifactConfSkipExport] = strconv.FormatBool(b.config.SkipExport) - - return vmwcommon.NewArtifact(b.config.VMName, dir, files, config, b.config.RemoteType != "") + return vmwcommon.NewArtifact(b.config.RemoteType, b.config.Format, b.config.OutputDir, + b.config.VMName, b.config.SkipExport, b.config.KeepRegistered, state) } func (b *Builder) Cancel() { diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 1bd470f37..4f0a5258c 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "strconv" "time" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" @@ -180,25 +179,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe if _, ok := state.GetOk(multistep.StateHalted); ok { return nil, errors.New("Build was halted.") } - // Compile the artifact list - var files []string - if b.config.RemoteType != "" && b.config.Format != "" { - dir = new(vmwcommon.LocalOutputDir) - dir.SetOutputDir(b.config.OutputDir) - files, err = dir.ListFiles() - } else { - files, err = state.Get("dir").(vmwcommon.OutputDir).ListFiles() - } - if err != nil { - return nil, err - } - config := make(map[string]string) - config[vmwcommon.ArtifactConfKeepRegistered] = strconv.FormatBool(b.config.KeepRegistered) - config[vmwcommon.ArtifactConfFormat] = b.config.Format - config[vmwcommon.ArtifactConfSkipExport] = strconv.FormatBool(b.config.SkipExport) - - return vmwcommon.NewArtifact(b.config.VMName, dir, files, config, b.config.RemoteType != "") + // Artifact + return vmwcommon.NewArtifact(b.config.RemoteType, b.config.Format, b.config.OutputDir, + b.config.VMName, b.config.SkipExport, b.config.KeepRegistered, state) } // Cancel. From f18bb19f96a9aee115b944de2aacfbde40f352ab Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 29 Oct 2018 11:24:26 -0700 Subject: [PATCH 26/32] remove redundant config; CommConfig was already a part of the SSSHConfig struct --- builder/vmware/common/driver.go | 5 ++--- builder/vmware/common/export_config.go | 1 + builder/vmware/common/step_export.go | 4 ++-- builder/vmware/iso/builder.go | 11 ++--------- builder/vmware/iso/builder_test.go | 20 ++++++++++---------- builder/vmware/vmx/builder.go | 2 +- builder/vmware/vmx/config.go | 13 ++++--------- 7 files changed, 22 insertions(+), 34 deletions(-) diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index b230d84b9..a8a898a21 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -15,7 +15,6 @@ import ( "strings" "time" - "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" ) @@ -82,7 +81,7 @@ type Driver interface { // NewDriver returns a new driver implementation for this operating // system, or an error if the driver couldn't be initialized. -func NewDriver(dconfig *DriverConfig, config *SSHConfig, commConfig *communicator.Config, vmName string) (Driver, error) { +func NewDriver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Driver, error) { drivers := []Driver{} if dconfig.RemoteType != "" { @@ -97,7 +96,7 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig, commConfig *communicato CacheDatastore: dconfig.RemoteCacheDatastore, CacheDirectory: dconfig.RemoteCacheDirectory, VMName: vmName, - CommConfig: *commConfig, + CommConfig: *(&config.Comm), }, } diff --git a/builder/vmware/common/export_config.go b/builder/vmware/common/export_config.go index 663972847..79c4eaeea 100644 --- a/builder/vmware/common/export_config.go +++ b/builder/vmware/common/export_config.go @@ -11,6 +11,7 @@ type ExportConfig struct { OVFToolOptions []string `mapstructure:"ovftool_options"` SkipExport bool `mapstructure:"skip_export"` KeepRegistered bool `mapstructure:"keep_registered"` + SkipCompaction bool `mapstructure:"skip_compaction"` } func (c *ExportConfig) Prepare(ctx *interpolate.Context) []error { diff --git a/builder/vmware/common/step_export.go b/builder/vmware/common/step_export.go index 088541d0d..173de488b 100644 --- a/builder/vmware/common/step_export.go +++ b/builder/vmware/common/step_export.go @@ -26,7 +26,7 @@ type StepExport struct { OutputDir string } -func (s *StepExport) generateArgs(c *DriverConfig, displayName string, outputPath string, hidePassword bool) []string { +func (s *StepExport) generateArgs(c *DriverConfig, displayName string, hidePassword bool) []string { password := url.QueryEscape(c.RemotePassword) if hidePassword { password = "****" @@ -37,7 +37,7 @@ func (s *StepExport) generateArgs(c *DriverConfig, displayName string, outputPat "-tt=" + s.Format, "vi://" + c.RemoteUser + ":" + password + "@" + c.RemoteHost + "/" + displayName, - outputPath, + s.OutputDir, } return append(s.OVFToolOptions, args...) } diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 9ec662dca..129180d1f 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -66,17 +66,9 @@ type Config struct { Serial string `mapstructure:"serial"` Parallel string `mapstructure:"parallel"` - // booting a guest - KeepRegistered bool `mapstructure:"keep_registered"` - OVFToolOptions []string `mapstructure:"ovftool_options"` - SkipCompaction bool `mapstructure:"skip_compaction"` - SkipExport bool `mapstructure:"skip_export"` - VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"` VMXTemplatePath string `mapstructure:"vmx_template_path"` - CommConfig communicator.Config `mapstructure:",squash"` - ctx interpolate.Context } @@ -232,7 +224,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { - driver, err := vmwcommon.NewDriver(&b.config.DriverConfig, &b.config.SSHConfig, &b.config.CommConfig, b.config.VMName) + driver, err := vmwcommon.NewDriver(&b.config.DriverConfig, &b.config.SSHConfig, b.config.VMName) if err != nil { return nil, fmt.Errorf("Failed creating VMware driver: %s", err) } @@ -369,6 +361,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SkipExport: b.config.SkipExport, VMName: b.config.VMName, OVFToolOptions: b.config.OVFToolOptions, + OutputDir: b.config.OutputDir, }, } diff --git a/builder/vmware/iso/builder_test.go b/builder/vmware/iso/builder_test.go index 59e18907d..b17fb95ab 100644 --- a/builder/vmware/iso/builder_test.go +++ b/builder/vmware/iso/builder_test.go @@ -459,13 +459,13 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { t.Fatalf("should not have error: %s", err) } - if b.config.CommConfig.WinRMUser != "username" { - t.Errorf("bad winrm_username: %s", b.config.CommConfig.WinRMUser) + if b.config.SSHConfig.Comm.WinRMUser != "username" { + t.Errorf("bad winrm_username: %s", b.config.SSHConfig.Comm.WinRMUser) } - if b.config.CommConfig.WinRMPassword != "password" { - t.Errorf("bad winrm_password: %s", b.config.CommConfig.WinRMPassword) + if b.config.SSHConfig.Comm.WinRMPassword != "password" { + t.Errorf("bad winrm_password: %s", b.config.SSHConfig.Comm.WinRMPassword) } - if host := b.config.CommConfig.Host(); host != "1.2.3.4" { + if host := b.config.SSHConfig.Comm.Host(); host != "1.2.3.4" { t.Errorf("bad host: %s", host) } } @@ -487,13 +487,13 @@ func TestBuilderPrepare_CommConfig(t *testing.T) { t.Fatalf("should not have error: %s", err) } - if b.config.CommConfig.SSHUsername != "username" { - t.Errorf("bad ssh_username: %s", b.config.CommConfig.SSHUsername) + if b.config.SSHConfig.Comm.SSHUsername != "username" { + t.Errorf("bad ssh_username: %s", b.config.SSHConfig.Comm.SSHUsername) } - if b.config.CommConfig.SSHPassword != "password" { - t.Errorf("bad ssh_password: %s", b.config.CommConfig.SSHPassword) + if b.config.SSHConfig.Comm.SSHPassword != "password" { + t.Errorf("bad ssh_password: %s", b.config.SSHConfig.Comm.SSHPassword) } - if host := b.config.CommConfig.Host(); host != "1.2.3.4" { + if host := b.config.SSHConfig.Comm.Host(); host != "1.2.3.4" { t.Errorf("bad host: %s", host) } } diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 4f0a5258c..017e32918 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -34,7 +34,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { // Run executes a Packer build and returns a packer.Artifact representing // a VMware image. func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { - driver, err := vmwcommon.NewDriver(&b.config.DriverConfig, &b.config.SSHConfig, &b.config.CommConfig, b.config.VMName) + driver, err := vmwcommon.NewDriver(&b.config.DriverConfig, &b.config.SSHConfig, b.config.VMName) if err != nil { return nil, fmt.Errorf("Failed creating VMware driver: %s", err) } diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index e21daba2f..0c726f590 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -7,7 +7,6 @@ import ( vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" - "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" @@ -28,14 +27,10 @@ type Config struct { vmwcommon.VMXConfig `mapstructure:",squash"` vmwcommon.ExportConfig `mapstructure:",squash"` - Linked bool `mapstructure:"linked"` - RemoteType string `mapstructure:"remote_type"` - SkipCompaction bool `mapstructure:"skip_compaction"` - BootCommand []string `mapstructure:"boot_command"` - SourcePath string `mapstructure:"source_path"` - VMName string `mapstructure:"vm_name"` - - CommConfig communicator.Config `mapstructure:",squash"` + Linked bool `mapstructure:"linked"` + RemoteType string `mapstructure:"remote_type"` + SourcePath string `mapstructure:"source_path"` + VMName string `mapstructure:"vm_name"` ctx interpolate.Context } From 7d290a5df1587df1055aae83b616a4a1c8697784 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 29 Oct 2018 13:50:07 -0700 Subject: [PATCH 27/32] dont change ssh communicator as part of this pr --- communicator/ssh/communicator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/communicator/ssh/communicator.go b/communicator/ssh/communicator.go index 5c9be02bc..a6ebeefa2 100644 --- a/communicator/ssh/communicator.go +++ b/communicator/ssh/communicator.go @@ -576,7 +576,7 @@ func (c *comm) scpUploadSession(path string, input io.Reader, fi *os.FileInfo) e return scpUploadFile(target_file, input, w, stdoutR, fi) } - return c.scpSession("scp -vt "+strconv.Quote(target_dir), scpFunc) + return c.scpSession("scp -vt "+target_dir, scpFunc) } func (c *comm) scpUploadDirSession(dst string, src string, excl []string) error { From c3d6d456960a639ef1c6ce3617c3e9fef55e59ab Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 29 Oct 2018 14:42:19 -0700 Subject: [PATCH 28/32] modify test so that it accesses CommConfig the same way we really do within the driver --- builder/vmware/common/driver_esx5_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/vmware/common/driver_esx5_test.go b/builder/vmware/common/driver_esx5_test.go index 15b823737..38232bbe7 100644 --- a/builder/vmware/common/driver_esx5_test.go +++ b/builder/vmware/common/driver_esx5_test.go @@ -72,7 +72,7 @@ func TestESX5Driver_CommHost(t *testing.T) { state := new(multistep.BasicStateBag) sshConfig := SSHConfig{Comm: commConfig} state.Put("sshConfig", &sshConfig) - driver := ESX5Driver{CommConfig: commConfig} + driver := ESX5Driver{CommConfig: *(&sshConfig.Comm)} host, err := driver.CommHost(state) if err != nil { From c3144c2d0ab872acae9dd02c8b230760daf48f07 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 1 Nov 2018 16:37:54 -0700 Subject: [PATCH 29/32] add some missing template validation (copied from iso builder) and revert to current use of displayName for parsing vm ssh IP; using VMName was breaking chained builds that hadn't updated the display name from the previous build. --- builder/vmware/common/driver_esx5.go | 14 +++++++++++--- builder/vmware/vmx/config.go | 22 +++++++++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index e53a73d54..602680ddc 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -456,10 +456,18 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { return "", err } - spacesToUnderscores := func(string string) string { - return strings.Replace(string, " ", "_", -1) + // The value in the Name field returned by 'esxcli network vm list' + // corresponds directly to the value of displayName set in the VMX file + var displayName string + if v, ok := state.GetOk("display_name"); ok { + displayName = v.(string) + } else { + displayName = strings.Replace(d.VMName, " ", "_", -1) + log.Printf("No display_name set; falling back to using VMName %s "+ + "to look for SSH IP", displayName) } - record, err := r.find("Name", spacesToUnderscores(d.VMName)) + + record, err := r.find("Name", displayName) if err != nil { return "", err } diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index 0c726f590..38a252787 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -71,7 +71,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { errs = packer.MultiErrorAppend(errs, c.VNCConfig.Prepare(&c.ctx)...) errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(&c.ctx)...) - if c.DriverConfig.RemoteType == "" { + if c.RemoteType == "" { if c.SourcePath == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is blank, but is required")) } else { @@ -80,6 +80,26 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { fmt.Errorf("source_path is invalid: %s", err)) } } + } else { + // Remote configuration validation + if c.RemoteHost == "" { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("remote_host must be specified")) + } + + if c.RemoteType != "esx5" { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("Only 'esx5' value is accepted for remote_type")) + } + } + + if c.Format == "" { + c.Format = "ovf" + } + + if !(c.Format == "ova" || c.Format == "ovf" || c.Format == "vmx") { + errs = packer.MultiErrorAppend(errs, + fmt.Errorf("format must be one of ova, ovf, or vmx")) } // Warnings From 5815b376b45793839c5d675adcc91a63281bc386 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 2 Nov 2018 11:10:51 -0700 Subject: [PATCH 30/32] remove unnecessary checks; we always default format to ovf if it is unset. fix exportoutputdir --- builder/vmware/common/artifact.go | 2 +- builder/vmware/common/step_register.go | 4 ++-- builder/vmware/iso/builder.go | 8 ++++++-- builder/vmware/vmx/builder.go | 11 +++++++++-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/builder/vmware/common/artifact.go b/builder/vmware/common/artifact.go index 729dc0e17..b8056e544 100644 --- a/builder/vmware/common/artifact.go +++ b/builder/vmware/common/artifact.go @@ -56,7 +56,7 @@ func NewArtifact(remoteType string, format string, exportOutputPath string, vmNa var files []string var dir OutputDir var err error - if remoteType != "" && format != "" && !skipExport { + if remoteType != "" && !skipExport { dir = new(LocalOutputDir) dir.SetOutputDir(exportOutputPath) files, err = dir.ListFiles() diff --git a/builder/vmware/common/step_register.go b/builder/vmware/common/step_register.go index 0d9b93b7b..6866e6703 100644 --- a/builder/vmware/common/step_register.go +++ b/builder/vmware/common/step_register.go @@ -53,7 +53,7 @@ func (s *StepRegister) Cleanup(state multistep.StateBag) { } if remoteDriver, ok := driver.(RemoteDriver); ok { - if s.Format == "" || s.SkipExport { + if s.SkipExport { ui.Say("Unregistering virtual machine...") if err := remoteDriver.Unregister(s.registeredPath); err != nil { ui.Error(fmt.Sprintf("Error unregistering VM: %s", err)) @@ -71,7 +71,7 @@ func (s *StepRegister) Cleanup(state multistep.StateBag) { if destroyed { break } - time.Sleep(150 * time.Millisecond) + time.Sleep(1 * time.Second) } } } diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 129180d1f..1a6b66171 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -238,6 +238,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe dir = new(vmwcommon.LocalOutputDir) } + // The OutputDir will track remote esxi output; exportOutputPath preserves + // the path to the output on the machine running Packer. + exportOutputPath := b.config.OutputDir + if b.config.RemoteType != "" { b.config.OutputDir = b.config.VMName } @@ -361,7 +365,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SkipExport: b.config.SkipExport, VMName: b.config.VMName, OVFToolOptions: b.config.OVFToolOptions, - OutputDir: b.config.OutputDir, + OutputDir: exportOutputPath, }, } @@ -384,7 +388,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } // Compile the artifact list - return vmwcommon.NewArtifact(b.config.RemoteType, b.config.Format, b.config.OutputDir, + return vmwcommon.NewArtifact(b.config.RemoteType, b.config.Format, exportOutputPath, b.config.VMName, b.config.SkipExport, b.config.KeepRegistered, state) } diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 017e32918..9a633a40b 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -47,7 +47,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe default: dir = new(vmwcommon.LocalOutputDir) } - if b.config.RemoteType != "" && b.config.Format != "" { + + // The OutputDir will track remote esxi output; exportOutputPath preserves + // the path to the output on the machine running Packer. + exportOutputPath := b.config.OutputDir + + if b.config.RemoteType != "" { b.config.OutputDir = b.config.VMName } dir.SetOutputDir(b.config.OutputDir) @@ -159,6 +164,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SkipExport: b.config.SkipExport, VMName: b.config.VMName, OVFToolOptions: b.config.OVFToolOptions, + OutputDir: exportOutputPath, }, } @@ -181,7 +187,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe } // Artifact - return vmwcommon.NewArtifact(b.config.RemoteType, b.config.Format, b.config.OutputDir, + log.Printf("Generating artifact...") + return vmwcommon.NewArtifact(b.config.RemoteType, b.config.Format, exportOutputPath, b.config.VMName, b.config.SkipExport, b.config.KeepRegistered, state) } From 5c2ca450bdef7bf252d01dbcf5c2ef7693aeb7e8 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 2 Nov 2018 15:44:27 -0700 Subject: [PATCH 31/32] fix tests --- builder/vmware/common/step_register_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/builder/vmware/common/step_register_test.go b/builder/vmware/common/step_register_test.go index be3c0b5e0..a9bcc7b61 100644 --- a/builder/vmware/common/step_register_test.go +++ b/builder/vmware/common/step_register_test.go @@ -31,7 +31,10 @@ func TestStepRegister_regularDriver(t *testing.T) { func TestStepRegister_remoteDriver(t *testing.T) { state := testState(t) - step := &StepRegister{KeepRegistered: false} + step := &StepRegister{ + KeepRegistered: false, + SkipExport: true, + } driver := new(RemoteDriverMock) From 96d628eef1c8981ec8a893bebacaa48e71311ae2 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 6 Nov 2018 09:50:28 -0800 Subject: [PATCH 32/32] fix silliness with pointers --- builder/vmware/common/driver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index a8a898a21..525c13d2e 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -96,7 +96,7 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Driver, CacheDatastore: dconfig.RemoteCacheDatastore, CacheDirectory: dconfig.RemoteCacheDirectory, VMName: vmName, - CommConfig: *(&config.Comm), + CommConfig: config.Comm, }, }