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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"strconv"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuilderId for the local artifacts
|
const (
|
||||||
const BuilderId = "mitchellh.vmware"
|
// 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
|
// Artifact is the result of running the VMware builder, namely a set
|
||||||
// of files associated with the resulting machine.
|
// of files associated with the resulting machine.
|
||||||
type localArtifact struct {
|
type artifact struct {
|
||||||
|
builderId string
|
||||||
id string
|
id string
|
||||||
dir string
|
dir OutputDir
|
||||||
f []string
|
f []string
|
||||||
|
config map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLocalArtifact returns a VMware artifact containing the files
|
func (a *artifact) BuilderId() string {
|
||||||
// in the given directory.
|
return a.builderId
|
||||||
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 *localArtifact) BuilderId() string {
|
func (a *artifact) Files() []string {
|
||||||
return BuilderId
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *localArtifact) Files() []string {
|
|
||||||
return a.f
|
return a.f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *localArtifact) Id() string {
|
func (a *artifact) Id() string {
|
||||||
return a.id
|
return a.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *localArtifact) String() string {
|
func (a *artifact) String() string {
|
||||||
return fmt.Sprintf("VM files in directory: %s", a.dir)
|
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
|
return a.config[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *localArtifact) Destroy() error {
|
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 != "" && !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
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLocalArtifact_impl(t *testing.T) {
|
func TestLocalArtifact_impl(t *testing.T) {
|
||||||
var _ packer.Artifact = new(localArtifact)
|
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,9 +81,26 @@ type Driver interface {
|
||||||
|
|
||||||
// NewDriver returns a new driver implementation for this operating
|
// NewDriver returns a new driver implementation for this operating
|
||||||
// system, or an error if the driver couldn't be initialized.
|
// 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{}
|
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 {
|
switch runtime.GOOS {
|
||||||
case "darwin":
|
case "darwin":
|
||||||
drivers = []Driver{
|
drivers = []Driver{
|
||||||
|
@ -122,6 +139,7 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig) (Driver, error) {
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("can't find driver for OS: %s", runtime.GOOS)
|
return nil, fmt.Errorf("can't find driver for OS: %s", runtime.GOOS)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
errs := ""
|
errs := ""
|
||||||
for _, driver := range drivers {
|
for _, driver := range drivers {
|
||||||
|
|
|
@ -8,6 +8,15 @@ import (
|
||||||
|
|
||||||
type DriverConfig struct {
|
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 {
|
func (c *DriverConfig) Prepare(ctx *interpolate.Context) []error {
|
||||||
|
@ -17,6 +26,21 @@ func (c *DriverConfig) Prepare(ctx *interpolate.Context) []error {
|
||||||
if c.FusionAppPath == "" {
|
if c.FusionAppPath == "" {
|
||||||
c.FusionAppPath = "/Applications/VMware Fusion.app"
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package iso
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
@ -10,13 +10,14 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
|
||||||
"github.com/hashicorp/packer/communicator/ssh"
|
"github.com/hashicorp/packer/communicator/ssh"
|
||||||
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
helperssh "github.com/hashicorp/packer/helper/ssh"
|
helperssh "github.com/hashicorp/packer/helper/ssh"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
@ -26,7 +27,7 @@ import (
|
||||||
// ESX5 driver talks to an ESXi5 hypervisor remotely over SSH to build
|
// ESX5 driver talks to an ESXi5 hypervisor remotely over SSH to build
|
||||||
// virtual machines. This driver can only manage one machine at a time.
|
// virtual machines. This driver can only manage one machine at a time.
|
||||||
type ESX5Driver struct {
|
type ESX5Driver struct {
|
||||||
base vmwcommon.VmwareDriver
|
base VmwareDriver
|
||||||
|
|
||||||
Host string
|
Host string
|
||||||
Port uint
|
Port uint
|
||||||
|
@ -36,6 +37,8 @@ type ESX5Driver struct {
|
||||||
Datastore string
|
Datastore string
|
||||||
CacheDatastore string
|
CacheDatastore string
|
||||||
CacheDirectory string
|
CacheDirectory string
|
||||||
|
VMName string
|
||||||
|
CommConfig communicator.Config
|
||||||
|
|
||||||
comm packer.Communicator
|
comm packer.Communicator
|
||||||
outputDir string
|
outputDir string
|
||||||
|
@ -43,7 +46,61 @@ type ESX5Driver struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ESX5Driver) Clone(dst, src string, linked bool) error {
|
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 {
|
func (d *ESX5Driver) CompactDisk(diskPathLocal string) error {
|
||||||
|
@ -65,7 +122,11 @@ func (d *ESX5Driver) IsRunning(string) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ESX5Driver) ReloadVM() error {
|
func (d *ESX5Driver) ReloadVM() error {
|
||||||
|
if d.vmId != "" {
|
||||||
return d.sh("vim-cmd", "vmsvc/reload", d.vmId)
|
return d.sh("vim-cmd", "vmsvc/reload", d.vmId)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ESX5Driver) Start(vmxPathLocal string, headless bool) error {
|
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) {
|
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 {
|
if err := d.mkdir(filepath.ToSlash(filepath.Dir(finalPath))); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Verifying checksum of %s", finalPath)
|
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.")
|
log.Println("Initial checksum matched, no upload needed.")
|
||||||
return finalPath, nil
|
return finalPath, nil
|
||||||
}
|
}
|
||||||
|
@ -141,7 +202,7 @@ func (d *ESX5Driver) UploadISO(localPath string, checksum string, checksumType s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ESX5Driver) RemoveCache(localPath string) error {
|
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)
|
log.Printf("Removing remote cache path %s (local %s)", finalPath, localPath)
|
||||||
return d.sh("rm", "-f", strconv.Quote(finalPath))
|
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) {
|
func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) {
|
||||||
config := state.Get("config").(*Config)
|
sshc := state.Get("sshConfig").(*SSHConfig).Comm
|
||||||
sshc := config.SSHConfig.Comm
|
|
||||||
port := sshc.SSHPort
|
port := sshc.SSHPort
|
||||||
if sshc.Type == "winrm" {
|
if sshc.Type == "winrm" {
|
||||||
port = sshc.WinRMPort
|
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
|
return address, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,7 +461,12 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) {
|
||||||
var displayName string
|
var displayName string
|
||||||
if v, ok := state.GetOk("display_name"); ok {
|
if v, ok := state.GetOk("display_name"); ok {
|
||||||
displayName = v.(string)
|
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)
|
record, err := r.find("Name", displayName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -432,14 +502,12 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) {
|
||||||
if e.Timeout() {
|
if e.Timeout() {
|
||||||
log.Printf("Timeout connecting to %s", record["IPAddress"])
|
log.Printf("Timeout connecting to %s", record["IPAddress"])
|
||||||
continue
|
continue
|
||||||
} else if strings.Contains(e.Error(), "connection refused") {
|
|
||||||
log.Printf("Connection refused when connecting to: %s", record["IPAddress"])
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
address := record["IPAddress"]
|
address := record["IPAddress"]
|
||||||
|
state.Put("vm_address", address)
|
||||||
return address, nil
|
return address, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,7 +524,7 @@ func (d *ESX5Driver) DirExists() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ESX5Driver) ListFiles() ([]string, 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 {
|
if err != nil {
|
||||||
return nil, err
|
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)))
|
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)))
|
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)
|
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 ctype == "none" {
|
||||||
if err := d.sh("stat", strconv.Quote(file)); err != nil {
|
if err := d.sh("stat", strconv.Quote(file)); err != nil {
|
||||||
return false
|
return false
|
||||||
|
@ -661,7 +738,7 @@ func (d *ESX5Driver) esxcli(args ...string) (*esxcliReader, error) {
|
||||||
return &esxcliReader{r, header}, nil
|
return &esxcliReader{r, header}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *ESX5Driver) GetVmwareDriver() vmwcommon.VmwareDriver {
|
func (d *ESX5Driver) GetVmwareDriver() VmwareDriver {
|
||||||
return d.base
|
return d.base
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
package iso
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"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"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestESX5Driver_implDriver(t *testing.T) {
|
func TestESX5Driver_implDriver(t *testing.T) {
|
||||||
var _ vmwcommon.Driver = new(ESX5Driver)
|
var _ Driver = new(ESX5Driver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestESX5Driver_UpdateVMX(t *testing.T) {
|
func TestESX5Driver_UpdateVMX(t *testing.T) {
|
||||||
|
@ -30,11 +31,11 @@ func TestESX5Driver_UpdateVMX(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestESX5Driver_implOutputDir(t *testing.T) {
|
func TestESX5Driver_implOutputDir(t *testing.T) {
|
||||||
var _ vmwcommon.OutputDir = new(ESX5Driver)
|
var _ OutputDir = new(ESX5Driver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestESX5Driver_implVNCAddressFinder(t *testing.T) {
|
func TestESX5Driver_implVNCAddressFinder(t *testing.T) {
|
||||||
var _ vmwcommon.VNCAddressFinder = new(ESX5Driver)
|
var _ VNCAddressFinder = new(ESX5Driver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestESX5Driver_implRemoteDriver(t *testing.T) {
|
func TestESX5Driver_implRemoteDriver(t *testing.T) {
|
||||||
|
@ -60,28 +61,19 @@ func TestESX5Driver_HostIP(t *testing.T) {
|
||||||
func TestESX5Driver_CommHost(t *testing.T) {
|
func TestESX5Driver_CommHost(t *testing.T) {
|
||||||
const expected_host = "127.0.0.1"
|
const expected_host = "127.0.0.1"
|
||||||
|
|
||||||
config := testConfig()
|
conf := make(map[string]interface{})
|
||||||
config["communicator"] = "winrm"
|
conf["communicator"] = "winrm"
|
||||||
config["winrm_username"] = "username"
|
conf["winrm_username"] = "username"
|
||||||
config["winrm_password"] = "password"
|
conf["winrm_password"] = "password"
|
||||||
config["winrm_host"] = expected_host
|
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)
|
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)
|
host, err := driver.CommHost(state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("should not have error: %s", err)
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
@ -89,4 +81,11 @@ func TestESX5Driver_CommHost(t *testing.T) {
|
||||||
if host != expected_host {
|
if host != expected_host {
|
||||||
t.Errorf("bad host name: %s", 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
|
// 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
|
// so that the output directory can be properly made on remote (ESXi) based
|
||||||
// VMware products as well as local.
|
// VMware products as well as local.
|
||||||
|
// For remote builds, OutputDir interface is satisfied by the ESX5Driver.
|
||||||
type OutputDir interface {
|
type OutputDir interface {
|
||||||
DirExists() (bool, error)
|
DirExists() (bool, error)
|
||||||
ListFiles() ([]string, error)
|
ListFiles() ([]string, error)
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
package iso
|
package common
|
||||||
|
|
||||||
import (
|
|
||||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RemoteDriver interface {
|
type RemoteDriver interface {
|
||||||
vmwcommon.Driver
|
Driver
|
||||||
|
|
||||||
// UploadISO uploads a local ISO to the remote side and returns the
|
// 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
|
// 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.
|
// 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
|
||||||
|
|
||||||
// Reload VM on remote side.
|
// Reload VM on remote side.
|
||||||
ReloadVM() error
|
ReloadVM() error
|
||||||
}
|
}
|
|
@ -1,11 +1,7 @@
|
||||||
package iso
|
package common
|
||||||
|
|
||||||
import (
|
|
||||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RemoteDriverMock struct {
|
type RemoteDriverMock struct {
|
||||||
vmwcommon.DriverMock
|
DriverMock
|
||||||
|
|
||||||
UploadISOCalled bool
|
UploadISOCalled bool
|
||||||
UploadISOPath string
|
UploadISOPath string
|
||||||
|
@ -27,7 +23,8 @@ type RemoteDriverMock struct {
|
||||||
IsDestroyedResult bool
|
IsDestroyedResult bool
|
||||||
IsDestroyedErr error
|
IsDestroyedErr error
|
||||||
|
|
||||||
uploadErr error
|
UploadErr error
|
||||||
|
DownloadErr error
|
||||||
|
|
||||||
ReloadVMErr error
|
ReloadVMErr error
|
||||||
}
|
}
|
||||||
|
@ -61,7 +58,11 @@ func (d *RemoteDriverMock) IsDestroyed() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *RemoteDriverMock) upload(dst, src string) 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 {
|
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 {
|
type StepConfigureVMX struct {
|
||||||
CustomData map[string]string
|
CustomData map[string]string
|
||||||
SkipFloppy bool
|
SkipFloppy bool
|
||||||
|
VMName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *StepConfigureVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
log.Printf("Configuring VMX...\n")
|
||||||
vmxPath := state.Get("vmx_path").(string)
|
|
||||||
|
|
||||||
|
var err error
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
vmxPath := state.Get("vmx_path").(string)
|
||||||
vmxData, err := ReadVMX(vmxPath)
|
vmxData, err := ReadVMX(vmxPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error reading VMX file: %s", err)
|
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)
|
err := fmt.Errorf("Error writing VMX file: %s", err)
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package iso
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -21,10 +21,12 @@ import (
|
||||||
type StepExport struct {
|
type StepExport struct {
|
||||||
Format string
|
Format string
|
||||||
SkipExport bool
|
SkipExport bool
|
||||||
|
VMName string
|
||||||
|
OVFToolOptions []string
|
||||||
OutputDir 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)
|
password := url.QueryEscape(c.RemotePassword)
|
||||||
if hidePassword {
|
if hidePassword {
|
||||||
password = "****"
|
password = "****"
|
||||||
|
@ -33,18 +35,19 @@ func (s *StepExport) generateArgs(c *Config, displayName string, hidePassword bo
|
||||||
"--noSSLVerify=true",
|
"--noSSLVerify=true",
|
||||||
"--skipManifestCheck",
|
"--skipManifestCheck",
|
||||||
"-tt=" + s.Format,
|
"-tt=" + s.Format,
|
||||||
|
|
||||||
"vi://" + c.RemoteUser + ":" + password + "@" + c.RemoteHost + "/" + displayName,
|
"vi://" + c.RemoteUser + ":" + password + "@" + c.RemoteHost + "/" + displayName,
|
||||||
s.OutputDir,
|
s.OutputDir,
|
||||||
}
|
}
|
||||||
return append(c.OVFToolOptions, args...)
|
return append(s.OVFToolOptions, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepExport) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
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)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
// Skip export if requested
|
// Skip export if requested
|
||||||
if c.SkipExport {
|
if s.SkipExport {
|
||||||
ui.Say("Skipping export of virtual machine...")
|
ui.Say("Skipping export of virtual machine...")
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
@ -60,7 +63,7 @@ func (s *StepExport) Run(_ context.Context, state multistep.StateBag) multistep.
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := exec.LookPath(ovftool); err != nil {
|
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)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
|
@ -68,7 +71,7 @@ func (s *StepExport) Run(_ context.Context, state multistep.StateBag) multistep.
|
||||||
|
|
||||||
// Export the VM
|
// Export the VM
|
||||||
if s.OutputDir == "" {
|
if s.OutputDir == "" {
|
||||||
s.OutputDir = c.VMName + "." + s.Format
|
s.OutputDir = s.VMName + "." + s.Format
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Format == "ova" {
|
if s.Format == "ova" {
|
|
@ -1,4 +1,4 @@
|
||||||
package iso
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -15,9 +15,9 @@ func testStepExport_wrongtype_impl(t *testing.T, remoteType string) {
|
||||||
state := testState(t)
|
state := testState(t)
|
||||||
step := new(StepExport)
|
step := new(StepExport)
|
||||||
|
|
||||||
var config Config
|
var config DriverConfig
|
||||||
config.RemoteType = "foo"
|
config.RemoteType = "foo"
|
||||||
state.Put("config", &config)
|
state.Put("driverConfig", &config)
|
||||||
|
|
||||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||||
t.Fatalf("bad action: %#v", action)
|
t.Fatalf("bad action: %#v", action)
|
|
@ -1,11 +1,10 @@
|
||||||
package iso
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
)
|
)
|
||||||
|
@ -13,11 +12,14 @@ import (
|
||||||
type StepRegister struct {
|
type StepRegister struct {
|
||||||
registeredPath string
|
registeredPath string
|
||||||
Format string
|
Format string
|
||||||
|
KeepRegistered bool
|
||||||
|
SkipExport bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepRegister) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
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)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
vmxPath := state.Get("vmx_path").(string)
|
vmxPath := state.Get("vmx_path").(string)
|
||||||
|
|
||||||
if remoteDriver, ok := driver.(RemoteDriver); ok {
|
if remoteDriver, ok := driver.(RemoteDriver); ok {
|
||||||
|
@ -40,19 +42,18 @@ func (s *StepRegister) Cleanup(state multistep.StateBag) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
driver := state.Get("driver").(vmwcommon.Driver)
|
driver := state.Get("driver").(Driver)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
config := state.Get("config").(*Config)
|
|
||||||
|
|
||||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||||
_, halted := state.GetOk(multistep.StateHalted)
|
_, 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)")
|
ui.Say("Keeping virtual machine registered with ESX host (keep_registered = true)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if remoteDriver, ok := driver.(RemoteDriver); ok {
|
if remoteDriver, ok := driver.(RemoteDriver); ok {
|
||||||
if s.Format == "" || config.SkipExport {
|
if s.SkipExport {
|
||||||
ui.Say("Unregistering virtual machine...")
|
ui.Say("Unregistering virtual machine...")
|
||||||
if err := remoteDriver.Unregister(s.registeredPath); err != nil {
|
if err := remoteDriver.Unregister(s.registeredPath); err != nil {
|
||||||
ui.Error(fmt.Sprintf("Error unregistering VM: %s", err))
|
ui.Error(fmt.Sprintf("Error unregistering VM: %s", err))
|
||||||
|
@ -70,7 +71,7 @@ func (s *StepRegister) Cleanup(state multistep.StateBag) {
|
||||||
if destroyed {
|
if destroyed {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
time.Sleep(150 * time.Millisecond)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package iso
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -31,12 +31,12 @@ func TestStepRegister_regularDriver(t *testing.T) {
|
||||||
|
|
||||||
func TestStepRegister_remoteDriver(t *testing.T) {
|
func TestStepRegister_remoteDriver(t *testing.T) {
|
||||||
state := testState(t)
|
state := testState(t)
|
||||||
step := new(StepRegister)
|
step := &StepRegister{
|
||||||
|
KeepRegistered: false,
|
||||||
|
SkipExport: true,
|
||||||
|
}
|
||||||
|
|
||||||
driver := new(RemoteDriverMock)
|
driver := new(RemoteDriverMock)
|
||||||
var config Config
|
|
||||||
config.KeepRegistered = false
|
|
||||||
state.Put("config", &config)
|
|
||||||
|
|
||||||
state.Put("driver", driver)
|
state.Put("driver", driver)
|
||||||
state.Put("vmx_path", "foo")
|
state.Put("vmx_path", "foo")
|
||||||
|
@ -71,12 +71,9 @@ func TestStepRegister_remoteDriver(t *testing.T) {
|
||||||
}
|
}
|
||||||
func TestStepRegister_WithoutUnregister_remoteDriver(t *testing.T) {
|
func TestStepRegister_WithoutUnregister_remoteDriver(t *testing.T) {
|
||||||
state := testState(t)
|
state := testState(t)
|
||||||
step := new(StepRegister)
|
step := &StepRegister{KeepRegistered: true}
|
||||||
|
|
||||||
driver := new(RemoteDriverMock)
|
driver := new(RemoteDriverMock)
|
||||||
var config Config
|
|
||||||
config.KeepRegistered = true
|
|
||||||
state.Put("config", &config)
|
|
||||||
|
|
||||||
state.Put("driver", driver)
|
state.Put("driver", driver)
|
||||||
state.Put("vmx_path", "foo")
|
state.Put("vmx_path", "foo")
|
|
@ -70,6 +70,7 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag)
|
||||||
|
|
||||||
// Connect to VNC
|
// Connect to VNC
|
||||||
ui.Say(fmt.Sprintf("Connecting to VM via VNC (%s:%d)", vncIp, vncPort))
|
ui.Say(fmt.Sprintf("Connecting to VM via VNC (%s:%d)", vncIp, vncPort))
|
||||||
|
|
||||||
nc, err := net.Dial("tcp", fmt.Sprintf("%s:%d", vncIp, vncPort))
|
nc, err := net.Dial("tcp", fmt.Sprintf("%s:%d", vncIp, vncPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error connecting to VNC: %s", err)
|
err := fmt.Errorf("Error connecting to VNC: %s", err)
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
package iso
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
)
|
)
|
||||||
|
@ -24,7 +23,7 @@ type StepUploadVMX struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StepUploadVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
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)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
vmxPath := state.Get("vmx_path").(string)
|
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"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
|
||||||
|
@ -19,8 +18,6 @@ import (
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
)
|
)
|
||||||
|
|
||||||
const BuilderIdESX = "mitchellh.vmware-esx"
|
|
||||||
|
|
||||||
type Builder struct {
|
type Builder struct {
|
||||||
config Config
|
config Config
|
||||||
runner multistep.Runner
|
runner multistep.Runner
|
||||||
|
@ -39,6 +36,7 @@ type Config struct {
|
||||||
vmwcommon.SSHConfig `mapstructure:",squash"`
|
vmwcommon.SSHConfig `mapstructure:",squash"`
|
||||||
vmwcommon.ToolsConfig `mapstructure:",squash"`
|
vmwcommon.ToolsConfig `mapstructure:",squash"`
|
||||||
vmwcommon.VMXConfig `mapstructure:",squash"`
|
vmwcommon.VMXConfig `mapstructure:",squash"`
|
||||||
|
vmwcommon.ExportConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
// disk drives
|
// disk drives
|
||||||
AdditionalDiskSize []uint `mapstructure:"disk_additional_size"`
|
AdditionalDiskSize []uint `mapstructure:"disk_additional_size"`
|
||||||
|
@ -68,27 +66,9 @@ type Config struct {
|
||||||
Serial string `mapstructure:"serial"`
|
Serial string `mapstructure:"serial"`
|
||||||
Parallel string `mapstructure:"parallel"`
|
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"`
|
VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"`
|
||||||
VMXTemplatePath string `mapstructure:"vmx_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
|
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.VMXConfig.Prepare(&b.config.ctx)...)
|
||||||
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.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.VNCConfig.Prepare(&b.config.ctx)...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, b.config.ExportConfig.Prepare(&b.config.ctx)...)
|
||||||
|
|
||||||
if b.config.DiskName == "" {
|
if b.config.DiskName == "" {
|
||||||
b.config.DiskName = "disk"
|
b.config.DiskName = "disk"
|
||||||
|
@ -175,26 +156,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
b.config.Version = "9"
|
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 b.config.VMXTemplatePath != "" {
|
||||||
if err := b.validateVMXTemplatePath(); err != nil {
|
if err := b.validateVMXTemplatePath(); err != nil {
|
||||||
errs = packer.MultiErrorAppend(
|
errs = packer.MultiErrorAppend(
|
||||||
|
@ -221,6 +182,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
errs = packer.MultiErrorAppend(errs,
|
errs = packer.MultiErrorAppend(errs,
|
||||||
fmt.Errorf("remote_host must be specified"))
|
fmt.Errorf("remote_host must be specified"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.config.RemoteType != "esx5" {
|
if b.config.RemoteType != "esx5" {
|
||||||
errs = packer.MultiErrorAppend(errs,
|
errs = packer.MultiErrorAppend(errs,
|
||||||
fmt.Errorf("Only 'esx5' value is accepted for remote_type"))
|
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) {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)
|
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the output dir implementation
|
// Determine the output dir implementation
|
||||||
var dir OutputDir
|
var dir vmwcommon.OutputDir
|
||||||
switch d := driver.(type) {
|
switch d := driver.(type) {
|
||||||
case OutputDir:
|
case vmwcommon.OutputDir:
|
||||||
dir = d
|
dir = d
|
||||||
default:
|
default:
|
||||||
dir = new(vmwcommon.LocalOutputDir)
|
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
|
exportOutputPath := b.config.OutputDir
|
||||||
|
|
||||||
if b.config.RemoteType != "" {
|
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("driver", driver)
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
state.Put("sshConfig", &b.config.SSHConfig)
|
||||||
|
state.Put("driverConfig", &b.config.DriverConfig)
|
||||||
|
|
||||||
steps := []multistep.Step{
|
steps := []multistep.Step{
|
||||||
&vmwcommon.StepPrepareTools{
|
&vmwcommon.StepPrepareTools{
|
||||||
|
@ -327,6 +293,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
&stepCreateVMX{},
|
&stepCreateVMX{},
|
||||||
&vmwcommon.StepConfigureVMX{
|
&vmwcommon.StepConfigureVMX{
|
||||||
CustomData: b.config.VMXData,
|
CustomData: b.config.VMXData,
|
||||||
|
VMName: b.config.VMName,
|
||||||
},
|
},
|
||||||
&vmwcommon.StepSuppressMessages{},
|
&vmwcommon.StepSuppressMessages{},
|
||||||
&common.StepHTTPServer{
|
&common.StepHTTPServer{
|
||||||
|
@ -341,8 +308,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
VNCPortMax: b.config.VNCPortMax,
|
VNCPortMax: b.config.VNCPortMax,
|
||||||
VNCDisablePassword: b.config.VNCDisablePassword,
|
VNCDisablePassword: b.config.VNCDisablePassword,
|
||||||
},
|
},
|
||||||
&StepRegister{
|
&vmwcommon.StepRegister{
|
||||||
Format: b.config.Format,
|
Format: b.config.Format,
|
||||||
|
KeepRegistered: b.config.KeepRegistered,
|
||||||
|
SkipExport: b.config.SkipExport,
|
||||||
},
|
},
|
||||||
&vmwcommon.StepRun{
|
&vmwcommon.StepRun{
|
||||||
DurationBeforeStop: 5 * time.Second,
|
DurationBeforeStop: 5 * time.Second,
|
||||||
|
@ -382,17 +351,20 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
&vmwcommon.StepConfigureVMX{
|
&vmwcommon.StepConfigureVMX{
|
||||||
CustomData: b.config.VMXDataPost,
|
CustomData: b.config.VMXDataPost,
|
||||||
SkipFloppy: true,
|
SkipFloppy: true,
|
||||||
|
VMName: b.config.VMName,
|
||||||
},
|
},
|
||||||
&vmwcommon.StepCleanVMX{
|
&vmwcommon.StepCleanVMX{
|
||||||
RemoveEthernetInterfaces: b.config.VMXConfig.VMXRemoveEthernet,
|
RemoveEthernetInterfaces: b.config.VMXConfig.VMXRemoveEthernet,
|
||||||
VNCEnabled: !b.config.DisableVNC,
|
VNCEnabled: !b.config.DisableVNC,
|
||||||
},
|
},
|
||||||
&StepUploadVMX{
|
&vmwcommon.StepUploadVMX{
|
||||||
RemoteType: b.config.RemoteType,
|
RemoteType: b.config.RemoteType,
|
||||||
},
|
},
|
||||||
&StepExport{
|
&vmwcommon.StepExport{
|
||||||
Format: b.config.Format,
|
Format: b.config.Format,
|
||||||
SkipExport: b.config.SkipExport,
|
SkipExport: b.config.SkipExport,
|
||||||
|
VMName: b.config.VMName,
|
||||||
|
OVFToolOptions: b.config.OVFToolOptions,
|
||||||
OutputDir: exportOutputPath,
|
OutputDir: exportOutputPath,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -416,36 +388,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the artifact list
|
// Compile the artifact list
|
||||||
var files []string
|
return vmwcommon.NewArtifact(b.config.RemoteType, b.config.Format, exportOutputPath,
|
||||||
if b.config.RemoteType != "" && b.config.Format != "" && !b.config.SkipExport {
|
b.config.VMName, b.config.SkipExport, b.config.KeepRegistered, state)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) Cancel() {
|
func (b *Builder) Cancel() {
|
||||||
|
|
|
@ -459,13 +459,13 @@ func TestBuilderPrepare_CommConfig(t *testing.T) {
|
||||||
t.Fatalf("should not have error: %s", err)
|
t.Fatalf("should not have error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.config.CommConfig.WinRMUser != "username" {
|
if b.config.SSHConfig.Comm.WinRMUser != "username" {
|
||||||
t.Errorf("bad winrm_username: %s", b.config.CommConfig.WinRMUser)
|
t.Errorf("bad winrm_username: %s", b.config.SSHConfig.Comm.WinRMUser)
|
||||||
}
|
}
|
||||||
if b.config.CommConfig.WinRMPassword != "password" {
|
if b.config.SSHConfig.Comm.WinRMPassword != "password" {
|
||||||
t.Errorf("bad winrm_password: %s", b.config.CommConfig.WinRMPassword)
|
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)
|
t.Errorf("bad host: %s", host)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,13 +487,13 @@ func TestBuilderPrepare_CommConfig(t *testing.T) {
|
||||||
t.Fatalf("should not have error: %s", err)
|
t.Fatalf("should not have error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.config.CommConfig.SSHUsername != "username" {
|
if b.config.SSHConfig.Comm.SSHUsername != "username" {
|
||||||
t.Errorf("bad ssh_username: %s", b.config.CommConfig.SSHUsername)
|
t.Errorf("bad ssh_username: %s", b.config.SSHConfig.Comm.SSHUsername)
|
||||||
}
|
}
|
||||||
if b.config.CommConfig.SSHPassword != "password" {
|
if b.config.SSHConfig.Comm.SSHPassword != "password" {
|
||||||
t.Errorf("bad ssh_password: %s", b.config.CommConfig.SSHPassword)
|
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)
|
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)
|
driver := state.Get("driver").(vmwcommon.Driver)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
remote, ok := driver.(RemoteDriver)
|
remote, ok := driver.(vmwcommon.RemoteDriver)
|
||||||
if !ok {
|
if !ok {
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
@ -36,10 +36,10 @@ func (s *stepRemoteUpload) Run(_ context.Context, state multistep.StateBag) mult
|
||||||
checksum := config.ISOChecksum
|
checksum := config.ISOChecksum
|
||||||
checksumType := config.ISOChecksumType
|
checksumType := config.ISOChecksumType
|
||||||
|
|
||||||
if esx5, ok := remote.(*ESX5Driver); ok {
|
if esx5, ok := remote.(*vmwcommon.ESX5Driver); ok {
|
||||||
remotePath := esx5.cachePath(path)
|
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...")
|
ui.Say("Remote cache was verified skipping remote upload...")
|
||||||
state.Put(s.Key, remotePath)
|
state.Put(s.Key, remotePath)
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
|
@ -68,7 +68,7 @@ func (s *stepRemoteUpload) Cleanup(state multistep.StateBag) {
|
||||||
|
|
||||||
driver := state.Get("driver").(vmwcommon.Driver)
|
driver := state.Get("driver").(vmwcommon.Driver)
|
||||||
|
|
||||||
remote, ok := driver.(RemoteDriver)
|
remote, ok := driver.(vmwcommon.RemoteDriver)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,13 +34,27 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
// Run executes a Packer build and returns a packer.Artifact representing
|
// Run executes a Packer build and returns a packer.Artifact representing
|
||||||
// a VMware image.
|
// a VMware image.
|
||||||
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, 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)
|
driver, err := vmwcommon.NewDriver(&b.config.DriverConfig, &b.config.SSHConfig, b.config.VMName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)
|
return nil, fmt.Errorf("Failed creating VMware driver: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the directory
|
// Determine the output dir implementation
|
||||||
dir := new(vmwcommon.LocalOutputDir)
|
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)
|
dir.SetOutputDir(b.config.OutputDir)
|
||||||
|
|
||||||
// Set up the state.
|
// 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("driver", driver)
|
||||||
state.Put("hook", hook)
|
state.Put("hook", hook)
|
||||||
state.Put("ui", ui)
|
state.Put("ui", ui)
|
||||||
|
state.Put("sshConfig", &b.config.SSHConfig)
|
||||||
|
state.Put("driverConfig", &b.config.DriverConfig)
|
||||||
|
|
||||||
// Build the steps.
|
// Build the steps.
|
||||||
steps := []multistep.Step{
|
steps := []multistep.Step{
|
||||||
|
@ -73,6 +89,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
},
|
},
|
||||||
&vmwcommon.StepConfigureVMX{
|
&vmwcommon.StepConfigureVMX{
|
||||||
CustomData: b.config.VMXData,
|
CustomData: b.config.VMXData,
|
||||||
|
VMName: b.config.VMName,
|
||||||
},
|
},
|
||||||
&vmwcommon.StepSuppressMessages{},
|
&vmwcommon.StepSuppressMessages{},
|
||||||
&common.StepHTTPServer{
|
&common.StepHTTPServer{
|
||||||
|
@ -80,6 +97,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
HTTPPortMin: b.config.HTTPPortMin,
|
HTTPPortMin: b.config.HTTPPortMin,
|
||||||
HTTPPortMax: b.config.HTTPPortMax,
|
HTTPPortMax: b.config.HTTPPortMax,
|
||||||
},
|
},
|
||||||
|
&vmwcommon.StepUploadVMX{
|
||||||
|
RemoteType: b.config.RemoteType,
|
||||||
|
},
|
||||||
&vmwcommon.StepConfigureVNC{
|
&vmwcommon.StepConfigureVNC{
|
||||||
Enabled: !b.config.DisableVNC,
|
Enabled: !b.config.DisableVNC,
|
||||||
VNCBindAddress: b.config.VNCBindAddress,
|
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,
|
VNCPortMax: b.config.VNCPortMax,
|
||||||
VNCDisablePassword: b.config.VNCDisablePassword,
|
VNCDisablePassword: b.config.VNCDisablePassword,
|
||||||
},
|
},
|
||||||
|
&vmwcommon.StepRegister{
|
||||||
|
Format: b.config.Format,
|
||||||
|
KeepRegistered: b.config.KeepRegistered,
|
||||||
|
SkipExport: b.config.SkipExport,
|
||||||
|
},
|
||||||
&vmwcommon.StepRun{
|
&vmwcommon.StepRun{
|
||||||
DurationBeforeStop: 5 * time.Second,
|
DurationBeforeStop: 5 * time.Second,
|
||||||
Headless: b.config.Headless,
|
Headless: b.config.Headless,
|
||||||
|
@ -125,11 +150,22 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
&vmwcommon.StepConfigureVMX{
|
&vmwcommon.StepConfigureVMX{
|
||||||
CustomData: b.config.VMXDataPost,
|
CustomData: b.config.VMXDataPost,
|
||||||
SkipFloppy: true,
|
SkipFloppy: true,
|
||||||
|
VMName: b.config.VMName,
|
||||||
},
|
},
|
||||||
&vmwcommon.StepCleanVMX{
|
&vmwcommon.StepCleanVMX{
|
||||||
RemoveEthernetInterfaces: b.config.VMXConfig.VMXRemoveEthernet,
|
RemoveEthernetInterfaces: b.config.VMXConfig.VMXRemoveEthernet,
|
||||||
VNCEnabled: !b.config.DisableVNC,
|
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.
|
// 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 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.
|
// Cancel.
|
||||||
|
|
|
@ -25,10 +25,10 @@ type Config struct {
|
||||||
vmwcommon.SSHConfig `mapstructure:",squash"`
|
vmwcommon.SSHConfig `mapstructure:",squash"`
|
||||||
vmwcommon.ToolsConfig `mapstructure:",squash"`
|
vmwcommon.ToolsConfig `mapstructure:",squash"`
|
||||||
vmwcommon.VMXConfig `mapstructure:",squash"`
|
vmwcommon.VMXConfig `mapstructure:",squash"`
|
||||||
|
vmwcommon.ExportConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
Linked bool `mapstructure:"linked"`
|
Linked bool `mapstructure:"linked"`
|
||||||
RemoteType string `mapstructure:"remote_type"`
|
RemoteType string `mapstructure:"remote_type"`
|
||||||
SkipCompaction bool `mapstructure:"skip_compaction"`
|
|
||||||
SourcePath string `mapstructure:"source_path"`
|
SourcePath string `mapstructure:"source_path"`
|
||||||
VMName string `mapstructure:"vm_name"`
|
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.VMXConfig.Prepare(&c.ctx)...)
|
||||||
errs = packer.MultiErrorAppend(errs, c.FloppyConfig.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.VNCConfig.Prepare(&c.ctx)...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.ExportConfig.Prepare(&c.ctx)...)
|
||||||
|
|
||||||
|
if c.RemoteType == "" {
|
||||||
if c.SourcePath == "" {
|
if c.SourcePath == "" {
|
||||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is blank, but is required"))
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("source_path is blank, but is required"))
|
||||||
} else {
|
} else {
|
||||||
|
@ -78,6 +80,27 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
||||||
fmt.Errorf("source_path is invalid: %s", err))
|
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
|
// Warnings
|
||||||
var warnings []string
|
var warnings []string
|
||||||
|
|
|
@ -3,7 +3,9 @@ package vmx
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
@ -18,9 +20,15 @@ type StepCloneVMX struct {
|
||||||
Path string
|
Path string
|
||||||
VMName string
|
VMName string
|
||||||
Linked bool
|
Linked bool
|
||||||
|
tempDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepCloneVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
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)
|
driver := state.Get("driver").(vmwcommon.Driver)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
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...")
|
ui.Say("Cloning source VM...")
|
||||||
log.Printf("Cloning from: %s", s.Path)
|
log.Printf("Cloning from: %s", s.Path)
|
||||||
log.Printf("Cloning to: %s", vmxPath)
|
log.Printf("Cloning to: %s", vmxPath)
|
||||||
|
|
||||||
if err := driver.Clone(vmxPath, s.Path, s.Linked); err != nil {
|
if err := driver.Clone(vmxPath, s.Path, s.Linked); err != nil {
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
return multistep.ActionHalt
|
return halt(err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read in the machine configuration from the cloned VMX file
|
// 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
|
// network type so that it can work out things like IP's and MAC
|
||||||
// addresses
|
// addresses
|
||||||
// * The disk compaction step needs the paths to all attached disks
|
// * 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)
|
vmxData, err := vmwcommon.ReadVMX(vmxPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Put("error", err)
|
return halt(err)
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var diskFilenames []string
|
var diskFilenames []string
|
||||||
|
@ -104,4 +126,7 @@ func (s *StepCloneVMX) Run(_ context.Context, state multistep.StateBag) multiste
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepCloneVMX) Cleanup(state multistep.StateBag) {
|
func (s *StepCloneVMX) Cleanup(state multistep.StateBag) {
|
||||||
|
if s.tempDir != "" {
|
||||||
|
os.RemoveAll(s.tempDir)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/common"
|
||||||
"github.com/hashicorp/packer/helper/config"
|
"github.com/hashicorp/packer/helper/config"
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
@ -20,7 +20,7 @@ import (
|
||||||
|
|
||||||
var builtins = map[string]string{
|
var builtins = map[string]string{
|
||||||
vsphere.BuilderId: "vmware",
|
vsphere.BuilderId: "vmware",
|
||||||
iso.BuilderIdESX: "vmware",
|
vmwcommon.BuilderIdESX: "vmware",
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
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())
|
"Artifact type %s does not fit this requirement", artifact.BuilderId())
|
||||||
}
|
}
|
||||||
|
|
||||||
f := artifact.State(iso.ArtifactConfFormat)
|
f := artifact.State(vmwcommon.ArtifactConfFormat)
|
||||||
k := artifact.State(iso.ArtifactConfKeepRegistered)
|
k := artifact.State(vmwcommon.ArtifactConfKeepRegistered)
|
||||||
s := artifact.State(iso.ArtifactConfSkipExport)
|
s := artifact.State(vmwcommon.ArtifactConfSkipExport)
|
||||||
|
|
||||||
if f != "" && k != "true" && s == "false" {
|
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")
|
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:
|
### 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:
|
### Optional:
|
||||||
|
|
||||||
|
@ -124,6 +125,40 @@ builder.
|
||||||
the builder. By default this is `output-BUILDNAME` where "BUILDNAME" is the
|
the builder. By default this is `output-BUILDNAME` where "BUILDNAME" is the
|
||||||
name of the build.
|
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
|
- `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
|
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
|
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
|
slightly larger. If you find this to be the case, you can disable compaction
|
||||||
using this configuration value. Defaults to `false`.
|
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
|
- `tools_upload_flavor` (string) - The flavor of the VMware Tools ISO to
|
||||||
upload into the VM. Valid values are `darwin`, `linux`, and `windows`. By
|
upload into the VM. Valid values are `darwin`, `linux`, and `windows`. By
|
||||||
default, this is empty, which means VMware tools won't be uploaded.
|
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" %>
|
<%= partial "partials/builders/boot-command" %>
|
||||||
|
|
||||||
Example boot command. This is actually a working boot command used to start an
|
Example boot command. This is actually a working boot command used to start an
|
||||||
|
|
Loading…
Reference in New Issue