Merge pull request #6927 from hashicorp/rebased_4591
Extend vmware-vmx builder to allow esxi builds. (Rebase of PR #4591)
This commit is contained in:
commit
8567be43d9
|
@ -2,68 +2,87 @@ 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 (
|
||||
// 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"
|
||||
)
|
||||
|
||||
// Artifact is the result of running the VMware builder, namely a set
|
||||
// of files associated with the resulting machine.
|
||||
type localArtifact struct {
|
||||
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.
|
||||
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 &localArtifact{
|
||||
id: id,
|
||||
dir: dir,
|
||||
f: files,
|
||||
}, nil
|
||||
func (a *artifact) BuilderId() string {
|
||||
return a.builderId
|
||||
}
|
||||
|
||||
func (a *localArtifact) BuilderId() string {
|
||||
return BuilderId
|
||||
}
|
||||
|
||||
func (a *localArtifact) Files() []string {
|
||||
func (a *artifact) Files() []string {
|
||||
return a.f
|
||||
}
|
||||
|
||||
func (a *localArtifact) Id() string {
|
||||
func (a *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{} {
|
||||
return nil
|
||||
func (a *artifact) State(name string) interface{} {
|
||||
return a.config[name]
|
||||
}
|
||||
|
||||
func (a *localArtifact) Destroy() error {
|
||||
return os.RemoveAll(a.dir)
|
||||
func (a *artifact) Destroy() error {
|
||||
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 != "" && !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
|
||||
}
|
||||
|
|
|
@ -1,46 +1,11 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func TestLocalArtifact_impl(t *testing.T) {
|
||||
var _ packer.Artifact = new(localArtifact)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
a, err := NewLocalArtifact("vm1", td)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
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()))
|
||||
}
|
||||
var _ packer.Artifact = new(artifact)
|
||||
}
|
||||
|
|
|
@ -81,9 +81,26 @@ 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, vmName string) (Driver, error) {
|
||||
drivers := []Driver{}
|
||||
|
||||
if dconfig.RemoteType != "" {
|
||||
drivers = []Driver{
|
||||
&ESX5Driver{
|
||||
Host: dconfig.RemoteHost,
|
||||
Port: dconfig.RemotePort,
|
||||
Username: dconfig.RemoteUser,
|
||||
Password: dconfig.RemotePassword,
|
||||
PrivateKeyFile: dconfig.RemotePrivateKey,
|
||||
Datastore: dconfig.RemoteDatastore,
|
||||
CacheDatastore: dconfig.RemoteCacheDatastore,
|
||||
CacheDirectory: dconfig.RemoteCacheDirectory,
|
||||
VMName: vmName,
|
||||
CommConfig: config.Comm,
|
||||
},
|
||||
}
|
||||
|
||||
} else {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
drivers = []Driver{
|
||||
|
@ -122,6 +139,7 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig) (Driver, error) {
|
|||
default:
|
||||
return nil, fmt.Errorf("can't find driver for OS: %s", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
errs := ""
|
||||
for _, driver := range drivers {
|
||||
|
|
|
@ -8,6 +8,15 @@ import (
|
|||
|
||||
type DriverConfig struct {
|
||||
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 {
|
||||
|
@ -17,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
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package iso
|
||||
package common
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
|
@ -10,13 +10,14 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"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"
|
||||
|
@ -26,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
|
||||
|
@ -36,6 +37,8 @@ type ESX5Driver struct {
|
|||
Datastore string
|
||||
CacheDatastore string
|
||||
CacheDirectory string
|
||||
VMName string
|
||||
CommConfig communicator.Config
|
||||
|
||||
comm packer.Communicator
|
||||
outputDir string
|
||||
|
@ -43,7 +46,61 @@ type ESX5Driver struct {
|
|||
}
|
||||
|
||||
func (d *ESX5Driver) Clone(dst, src string, linked bool) error {
|
||||
return errors.New("Cloning is not supported with the ESX driver.")
|
||||
|
||||
linesToArray := func(lines string) []string { return strings.Split(strings.Trim(lines, "\n"), "\n") }
|
||||
|
||||
d.SetOutputDir(path.Dir(filepath.ToSlash(dst)))
|
||||
srcVmx := d.datastorePath(src)
|
||||
dstVmx := d.datastorePath(dst)
|
||||
srcDir := path.Dir(srcVmx)
|
||||
dstDir := path.Dir(dstVmx)
|
||||
|
||||
log.Printf("Source: %s\n", srcVmx)
|
||||
log.Printf("Dest: %s\n", dstVmx)
|
||||
|
||||
err := d.MkdirAll()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create the destination directory %s: %s", d.outputDir, err)
|
||||
}
|
||||
|
||||
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", 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) {
|
||||
// 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'", strconv.Quote(srcVmx))
|
||||
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", strconv.Quote(srcDisk), strconv.Quote(destDisk))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failing to clone disk %s: %s", srcDisk, err)
|
||||
}
|
||||
}
|
||||
log.Printf("Successfully cloned %s to %s\n", src, dst)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *ESX5Driver) CompactDisk(diskPathLocal string) error {
|
||||
|
@ -65,7 +122,11 @@ func (d *ESX5Driver) IsRunning(string) (bool, error) {
|
|||
}
|
||||
|
||||
func (d *ESX5Driver) ReloadVM() error {
|
||||
if d.vmId != "" {
|
||||
return d.sh("vim-cmd", "vmsvc/reload", d.vmId)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *ESX5Driver) Start(vmxPathLocal string, headless bool) error {
|
||||
|
@ -122,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
|
||||
}
|
||||
|
@ -141,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))
|
||||
}
|
||||
|
@ -375,14 +436,18 @@ 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
|
||||
sshc := state.Get("sshConfig").(*SSHConfig).Comm
|
||||
port := sshc.SSHPort
|
||||
if sshc.Type == "winrm" {
|
||||
port = sshc.WinRMPort
|
||||
}
|
||||
|
||||
if address := config.CommConfig.Host(); address != "" {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -396,7 +461,12 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) {
|
|||
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", displayName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -432,14 +502,12 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) {
|
|||
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"]
|
||||
state.Put("vm_address", address)
|
||||
return address, nil
|
||||
}
|
||||
}
|
||||
|
@ -456,7 +524,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
|
||||
}
|
||||
|
@ -503,7 +571,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)))
|
||||
}
|
||||
|
||||
|
@ -592,7 +660,16 @@ func (d *ESX5Driver) upload(dst, src string) error {
|
|||
return d.comm.Upload(dst, f, nil)
|
||||
}
|
||||
|
||||
func (d *ESX5Driver) verifyChecksum(ctype string, hash string, file string) bool {
|
||||
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 {
|
||||
return false
|
||||
|
@ -661,7 +738,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
|
||||
}
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
package iso
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestESX5Driver_implDriver(t *testing.T) {
|
||||
var _ vmwcommon.Driver = new(ESX5Driver)
|
||||
var _ Driver = new(ESX5Driver)
|
||||
}
|
||||
|
||||
func TestESX5Driver_UpdateVMX(t *testing.T) {
|
||||
|
@ -30,11 +31,11 @@ func TestESX5Driver_UpdateVMX(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestESX5Driver_implOutputDir(t *testing.T) {
|
||||
var _ vmwcommon.OutputDir = new(ESX5Driver)
|
||||
var _ OutputDir = new(ESX5Driver)
|
||||
}
|
||||
|
||||
func TestESX5Driver_implVNCAddressFinder(t *testing.T) {
|
||||
var _ vmwcommon.VNCAddressFinder = new(ESX5Driver)
|
||||
var _ VNCAddressFinder = new(ESX5Driver)
|
||||
}
|
||||
|
||||
func TestESX5Driver_implRemoteDriver(t *testing.T) {
|
||||
|
@ -60,28 +61,19 @@ func TestESX5Driver_HostIP(t *testing.T) {
|
|||
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)
|
||||
}
|
||||
conf := make(map[string]interface{})
|
||||
conf["communicator"] = "winrm"
|
||||
conf["winrm_username"] = "username"
|
||||
conf["winrm_password"] = "password"
|
||||
conf["winrm_host"] = expected_host
|
||||
|
||||
var commConfig communicator.Config
|
||||
err := config.Decode(&commConfig, nil, conf)
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", &b.config)
|
||||
sshConfig := SSHConfig{Comm: commConfig}
|
||||
state.Put("sshConfig", &sshConfig)
|
||||
driver := ESX5Driver{CommConfig: *(&sshConfig.Comm)}
|
||||
|
||||
var driver ESX5Driver
|
||||
host, err := driver.CommHost(state)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
|
@ -89,4 +81,11 @@ func TestESX5Driver_CommHost(t *testing.T) {
|
|||
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))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/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"`
|
||||
SkipCompaction bool `mapstructure:"skip_compaction"`
|
||||
}
|
||||
|
||||
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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
@ -30,6 +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
|
||||
}
|
|
@ -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
|
||||
|
@ -27,7 +23,8 @@ type RemoteDriverMock struct {
|
|||
IsDestroyedResult bool
|
||||
IsDestroyedErr error
|
||||
|
||||
uploadErr error
|
||||
UploadErr error
|
||||
DownloadErr error
|
||||
|
||||
ReloadVMErr error
|
||||
}
|
||||
|
@ -61,7 +58,11 @@ func (d *RemoteDriverMock) IsDestroyed() (bool, error) {
|
|||
}
|
||||
|
||||
func (d *RemoteDriverMock) upload(dst, src string) error {
|
||||
return d.uploadErr
|
||||
return d.UploadErr
|
||||
}
|
||||
|
||||
func (d *RemoteDriverMock) Download(src, dst string) error {
|
||||
return d.DownloadErr
|
||||
}
|
||||
|
||||
func (d *RemoteDriverMock) RemoveCache(localPath string) error {
|
|
@ -0,0 +1,10 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRemoteDriverMock_impl(t *testing.T) {
|
||||
var _ Driver = new(RemoteDriverMock)
|
||||
var _ RemoteDriver = new(RemoteDriverMock)
|
||||
}
|
|
@ -22,12 +22,16 @@ import (
|
|||
type StepConfigureVMX struct {
|
||||
CustomData map[string]string
|
||||
SkipFloppy bool
|
||||
VMName string
|
||||
}
|
||||
|
||||
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 err error
|
||||
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 +73,9 @@ func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) mult
|
|||
}
|
||||
}
|
||||
|
||||
if err := WriteVMX(vmxPath, vmxData); err != nil {
|
||||
err = WriteVMX(vmxPath, vmxData)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error writing VMX file: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package iso
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -21,10 +21,12 @@ import (
|
|||
type StepExport struct {
|
||||
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, displayName 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,
|
||||
}
|
||||
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
|
||||
|
@ -68,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" {
|
|
@ -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)
|
|
@ -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,11 +12,14 @@ import (
|
|||
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").(vmwcommon.Driver)
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
vmxPath := state.Get("vmx_path").(string)
|
||||
|
||||
if remoteDriver, ok := driver.(RemoteDriver); ok {
|
||||
|
@ -40,19 +42,18 @@ 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.(RemoteDriver); ok {
|
||||
if s.Format == "" || config.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))
|
||||
|
@ -70,7 +71,7 @@ func (s *StepRegister) Cleanup(state multistep.StateBag) {
|
|||
if destroyed {
|
||||
break
|
||||
}
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package iso
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -31,12 +31,12 @@ func TestStepRegister_regularDriver(t *testing.T) {
|
|||
|
||||
func TestStepRegister_remoteDriver(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepRegister)
|
||||
step := &StepRegister{
|
||||
KeepRegistered: false,
|
||||
SkipExport: true,
|
||||
}
|
||||
|
||||
driver := new(RemoteDriverMock)
|
||||
var config Config
|
||||
config.KeepRegistered = false
|
||||
state.Put("config", &config)
|
||||
|
||||
state.Put("driver", driver)
|
||||
state.Put("vmx_path", "foo")
|
||||
|
@ -71,12 +71,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")
|
|
@ -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)
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package iso
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
@ -24,7 +23,7 @@ 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)
|
|
@ -1,45 +0,0 @@
|
|||
package iso
|
||||
|
||||
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 Artifact struct {
|
||||
builderId string
|
||||
id string
|
||||
dir OutputDir
|
||||
f []string
|
||||
config map[string]string
|
||||
}
|
||||
|
||||
func (a *Artifact) BuilderId() string {
|
||||
return a.builderId
|
||||
}
|
||||
|
||||
func (a *Artifact) Files() []string {
|
||||
return a.f
|
||||
}
|
||||
|
||||
func (a *Artifact) Id() string {
|
||||
return a.id
|
||||
}
|
||||
|
||||
func (a *Artifact) String() string {
|
||||
return fmt.Sprintf("VM files in directory: %s", a.dir)
|
||||
}
|
||||
|
||||
func (a *Artifact) State(name string) interface{} {
|
||||
return a.config[name]
|
||||
}
|
||||
|
||||
func (a *Artifact) Destroy() error {
|
||||
return a.dir.RemoveAll()
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package iso
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func TestArtifact_Impl(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = &Artifact{}
|
||||
if _, ok := raw.(packer.Artifact); !ok {
|
||||
t.Fatal("Artifact must be a proper artifact")
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import (
|
|||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
||||
|
@ -19,8 +18,6 @@ import (
|
|||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
const BuilderIdESX = "mitchellh.vmware-esx"
|
||||
|
||||
type Builder struct {
|
||||
config Config
|
||||
runner multistep.Runner
|
||||
|
@ -39,6 +36,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"`
|
||||
|
@ -68,27 +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"`
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -125,6 +105,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"
|
||||
|
@ -175,26 +156,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(
|
||||
|
@ -221,6 +182,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"))
|
||||
|
@ -262,20 +224,22 @@ 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.VMName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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 != "" {
|
||||
|
@ -292,6 +256,8 @@ 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("driverConfig", &b.config.DriverConfig)
|
||||
|
||||
steps := []multistep.Step{
|
||||
&vmwcommon.StepPrepareTools{
|
||||
|
@ -327,6 +293,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{
|
||||
|
@ -341,8 +308,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
VNCPortMax: b.config.VNCPortMax,
|
||||
VNCDisablePassword: b.config.VNCDisablePassword,
|
||||
},
|
||||
&StepRegister{
|
||||
&vmwcommon.StepRegister{
|
||||
Format: b.config.Format,
|
||||
KeepRegistered: b.config.KeepRegistered,
|
||||
SkipExport: b.config.SkipExport,
|
||||
},
|
||||
&vmwcommon.StepRun{
|
||||
DurationBeforeStop: 5 * time.Second,
|
||||
|
@ -382,17 +351,20 @@ 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,
|
||||
VNCEnabled: !b.config.DisableVNC,
|
||||
},
|
||||
&StepUploadVMX{
|
||||
&vmwcommon.StepUploadVMX{
|
||||
RemoteType: b.config.RemoteType,
|
||||
},
|
||||
&StepExport{
|
||||
&vmwcommon.StepExport{
|
||||
Format: b.config.Format,
|
||||
SkipExport: b.config.SkipExport,
|
||||
VMName: b.config.VMName,
|
||||
OVFToolOptions: b.config.OVFToolOptions,
|
||||
OutputDir: exportOutputPath,
|
||||
},
|
||||
}
|
||||
|
@ -416,36 +388,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").(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(b.config.RemoteType, b.config.Format, exportOutputPath,
|
||||
b.config.VMName, b.config.SkipExport, b.config.KeepRegistered, state)
|
||||
}
|
||||
|
||||
func (b *Builder) Cancel() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +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) {
|
||||
drivers := []vmwcommon.Driver{}
|
||||
|
||||
if config.RemoteType == "" {
|
||||
return vmwcommon.NewDriver(&config.DriverConfig, &config.SSHConfig)
|
||||
}
|
||||
|
||||
drivers = []vmwcommon.Driver{
|
||||
&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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -34,13 +34,27 @@ 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.VMName)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// Set up the state.
|
||||
|
@ -51,6 +65,8 @@ 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("driverConfig", &b.config.DriverConfig)
|
||||
|
||||
// Build the steps.
|
||||
steps := []multistep.Step{
|
||||
|
@ -73,6 +89,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{
|
||||
|
@ -80,6 +97,9 @@ 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.StepConfigureVNC{
|
||||
Enabled: !b.config.DisableVNC,
|
||||
VNCBindAddress: b.config.VNCBindAddress,
|
||||
|
@ -87,6 +107,11 @@ 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: b.config.Format,
|
||||
KeepRegistered: b.config.KeepRegistered,
|
||||
SkipExport: b.config.SkipExport,
|
||||
},
|
||||
&vmwcommon.StepRun{
|
||||
DurationBeforeStop: 5 * time.Second,
|
||||
Headless: b.config.Headless,
|
||||
|
@ -125,11 +150,22 @@ 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,
|
||||
VNCEnabled: !b.config.DisableVNC,
|
||||
},
|
||||
&vmwcommon.StepUploadVMX{
|
||||
RemoteType: b.config.RemoteType,
|
||||
},
|
||||
&vmwcommon.StepExport{
|
||||
Format: b.config.Format,
|
||||
SkipExport: b.config.SkipExport,
|
||||
VMName: b.config.VMName,
|
||||
OVFToolOptions: b.config.OVFToolOptions,
|
||||
OutputDir: exportOutputPath,
|
||||
},
|
||||
}
|
||||
|
||||
// Run the steps.
|
||||
|
@ -150,7 +186,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
return nil, errors.New("Build was halted.")
|
||||
}
|
||||
|
||||
return vmwcommon.NewLocalArtifact(b.config.VMName, b.config.OutputDir)
|
||||
// Artifact
|
||||
log.Printf("Generating artifact...")
|
||||
return vmwcommon.NewArtifact(b.config.RemoteType, b.config.Format, exportOutputPath,
|
||||
b.config.VMName, b.config.SkipExport, b.config.KeepRegistered, state)
|
||||
}
|
||||
|
||||
// Cancel.
|
||||
|
|
|
@ -25,10 +25,10 @@ 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"`
|
||||
SkipCompaction bool `mapstructure:"skip_compaction"`
|
||||
SourcePath string `mapstructure:"source_path"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
|
||||
|
@ -69,7 +69,9 @@ 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.RemoteType == "" {
|
||||
if c.SourcePath == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is blank, but is required"))
|
||||
} else {
|
||||
|
@ -78,6 +80,27 @@ 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
|
||||
var warnings []string
|
||||
|
|
|
@ -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,9 +37,11 @@ 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
|
||||
return halt(err)
|
||||
|
||||
}
|
||||
|
||||
// Read in the machine configuration from the cloned VMX file
|
||||
|
@ -40,10 +50,22 @@ 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 {
|
||||
remoteVmxPath := vmxPath
|
||||
tempDir, err := ioutil.TempDir("", "packer-vmx")
|
||||
if err != nil {
|
||||
return halt(err)
|
||||
}
|
||||
s.tempDir = tempDir
|
||||
vmxPath = filepath.Join(tempDir, s.VMName+".vmx")
|
||||
if err = remoteDriver.Download(remoteVmxPath, vmxPath); 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 +126,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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
@ -20,7 +20,7 @@ import (
|
|||
|
||||
var builtins = map[string]string{
|
||||
vsphere.BuilderId: "vmware",
|
||||
iso.BuilderIdESX: "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")
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
@ -124,6 +125,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
|
||||
|
@ -157,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.
|
||||
|
@ -230,6 +284,46 @@ contention. You can tune this delay on a per-builder basis by specifying
|
|||
}
|
||||
```
|
||||
|
||||
- `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
- `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
- `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
- `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
- `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
- `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
- `<leftAlt>` `<rightAlt>` - Simulates pressing the alt key.
|
||||
|
||||
- `<leftCtrl>` `<rightCtrl>` - Simulates pressing the ctrl key.
|
||||
|
||||
- `<leftShift>` `<rightShift>` - Simulates pressing the shift key.
|
||||
|
||||
- `<leftAltOn>` `<rightAltOn>` - Simulates pressing and holding the alt key.
|
||||
|
||||
- `<leftCtrlOn>` `<rightCtrlOn>` - Simulates pressing and holding the ctrl
|
||||
key.
|
||||
|
||||
- `<leftShiftOn>` `<rightShiftOn>` - Simulates pressing and holding the
|
||||
shift key.
|
||||
|
||||
- `<leftAltOff>` `<rightAltOff>` - Simulates releasing a held alt key.
|
||||
|
||||
- `<leftCtrlOff>` `<rightCtrlOff>` - Simulates releasing a held ctrl key.
|
||||
|
||||
- `<leftShiftOff>` `<rightShiftOff>` - Simulates releasing a held shift key.
|
||||
|
||||
- `<wait>` `<wait5>` `<wait10>` - 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:
|
||||
|
||||
<%= partial "partials/builders/boot-command" %>
|
||||
|
||||
Example boot command. This is actually a working boot command used to start an
|
||||
|
|
Loading…
Reference in New Issue