Merge pull request #9825 from hashicorp/do_7165
Allow "export" to ovf/ova for local vmware builds in addition to esx …
This commit is contained in:
commit
60d124dcaf
|
@ -77,6 +77,9 @@ type Driver interface {
|
|||
|
||||
// Get the host ip address for the vm
|
||||
HostIP(multistep.StateBag) (string, error)
|
||||
|
||||
// Export the vm to ovf or ova format using ovftool
|
||||
Export([]string) error
|
||||
}
|
||||
|
||||
// NewDriver returns a new driver implementation for this operating
|
||||
|
@ -600,3 +603,28 @@ func (d *VmwareDriver) HostIP(state multistep.StateBag) (string, error) {
|
|||
}
|
||||
return "", fmt.Errorf("Unable to find host IP from devices %v, last error: %s", devices, lastError)
|
||||
}
|
||||
|
||||
func GetOVFTool() string {
|
||||
ovftool := "ovftool"
|
||||
if runtime.GOOS == "windows" {
|
||||
ovftool = "ovftool.exe"
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath(ovftool); err != nil {
|
||||
return ""
|
||||
}
|
||||
return ovftool
|
||||
}
|
||||
|
||||
func (d *VmwareDriver) Export(args []string) error {
|
||||
ovftool := GetOVFTool()
|
||||
if ovftool == "" {
|
||||
return fmt.Errorf("Error: ovftool not found")
|
||||
}
|
||||
cmd := exec.Command(ovftool, args...)
|
||||
if _, _, err := runAndLog(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -84,13 +84,28 @@ func (c *DriverConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
}
|
||||
|
||||
func (c *DriverConfig) Validate(SkipExport bool) error {
|
||||
if c.RemoteType == "" || SkipExport == true {
|
||||
if SkipExport {
|
||||
return nil
|
||||
}
|
||||
if c.RemotePassword == "" {
|
||||
return fmt.Errorf("exporting the vm (with ovftool) requires that " +
|
||||
"you set a value for remote_password")
|
||||
|
||||
if c.RemoteType != "" && c.RemotePassword == "" {
|
||||
return fmt.Errorf("exporting the vm from esxi with ovftool requires " +
|
||||
"that you set a value for remote_password")
|
||||
}
|
||||
|
||||
if c.RemoteType == "" {
|
||||
// Validate that tool exists, but no need to validate credentials.
|
||||
ovftool := GetOVFTool()
|
||||
if ovftool != "" {
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Couldn't find ovftool in path! Please either " +
|
||||
"set `skip_export = true` and remove the `format` option " +
|
||||
"from your template, or make sure ovftool is installed on " +
|
||||
"your build system. ")
|
||||
}
|
||||
}
|
||||
|
||||
if c.SkipValidateCredentials {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -699,6 +699,10 @@ func (d *ESX5Driver) Download(src, dst string) error {
|
|||
return d.comm.Download(d.datastorePath(src), file)
|
||||
}
|
||||
|
||||
func (d *ESX5Driver) Export(args []string) error {
|
||||
return d.base.Export(args)
|
||||
}
|
||||
|
||||
// VerifyChecksum checks that file on the esxi instance matches hash
|
||||
func (d *ESX5Driver) VerifyChecksum(hash string, file string) bool {
|
||||
if hash == "none" {
|
||||
|
|
|
@ -27,6 +27,9 @@ type DriverMock struct {
|
|||
CreateDiskTypeId string
|
||||
CreateDiskErr error
|
||||
|
||||
ExportCalled bool
|
||||
ExportArgs []string
|
||||
|
||||
IsRunningCalled bool
|
||||
IsRunningPath string
|
||||
IsRunningResult bool
|
||||
|
@ -254,6 +257,12 @@ func (d *DriverMock) Verify() error {
|
|||
return d.VerifyErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) Export(args []string) error {
|
||||
d.ExportCalled = true
|
||||
d.ExportArgs = args
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DriverMock) GetVmwareDriver() VmwareDriver {
|
||||
var state VmwareDriver
|
||||
state.DhcpLeasesPath = func(string) string {
|
||||
|
|
|
@ -10,33 +10,30 @@ import (
|
|||
|
||||
type ExportConfig struct {
|
||||
// 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. This option
|
||||
// currently only works when option remote_type is set to "esx5".
|
||||
// format of the exported virtual machine. This defaults to "ovf" for
|
||||
// remote (esx) builds, and "vmx" for local builds.
|
||||
// Before using this option, you need to install ovftool.
|
||||
// Since ovftool is only capable of password based authentication
|
||||
// remote_password must be set when exporting the VM.
|
||||
// remote_password must be set when exporting the VM from a remote instance.
|
||||
// If you are building locally, Packer will create a vmx and then
|
||||
// export that vm to an ovf or ova. Packer will not delete the vmx and vmdk
|
||||
// files; this is left up to the user if you don't want to keep those
|
||||
// files.
|
||||
Format string `mapstructure:"format" required:"false"`
|
||||
// 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. Currently, exporting the build VM (with ovftool) is only
|
||||
// supported when building on ESXi e.g. when `remote_type` is set to
|
||||
// `esx5`. See the [Building on a Remote vSphere
|
||||
// Hypervisor](/docs/builders/vmware-iso#building-on-a-remote-vsphere-hypervisor)
|
||||
// section below for more info.
|
||||
// and `--targetType` are used by Packer for remote exports, and should not
|
||||
// be passed to this argument. For ovf/ova exports from local builds, Packer
|
||||
// does not automatically set any ovftool options.
|
||||
OVFToolOptions []string `mapstructure:"ovftool_options" required:"false"`
|
||||
// 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. Currently, exporting the build VM is only supported when building on
|
||||
// ESXi e.g. when `remote_type` is set to `esx5`. See the [Building on a
|
||||
// Remote vSphere
|
||||
// Hypervisor](/docs/builders/vmware-iso#building-on-a-remote-vsphere-hypervisor)
|
||||
// section below for more info.
|
||||
// Defaults to `false`. When true, Packer will not export the VM. This can
|
||||
// be useful if the build output is not the resultant image, but created
|
||||
// inside the VM.
|
||||
SkipExport bool `mapstructure:"skip_export" required:"false"`
|
||||
// Set this to true if you would like to keep
|
||||
// the VM registered with the remote ESXi server. If you do not need to export
|
||||
// the vm, then also set skip_export: true in order to avoid an unnecessary
|
||||
// step of using ovftool to export the vm. Defaults to false.
|
||||
// Set this to true if you would like to keep a remotely-built
|
||||
// VM registered with the remote ESXi server. If you do not need to export
|
||||
// the vm, then also set `skip_export: true` in order to avoid unnecessarily
|
||||
// using ovftool to export the vm. Defaults to false.
|
||||
KeepRegistered bool `mapstructure:"keep_registered" required:"false"`
|
||||
// VMware-created disks are defragmented and
|
||||
// compacted at the end of the build process using vmware-vdiskmanager or
|
||||
|
@ -56,5 +53,6 @@ func (c *ExportConfig) Prepare(ctx *interpolate.Context) []error {
|
|||
errs, fmt.Errorf("format must be one of ova, ovf, or vmx"))
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
|
@ -15,30 +13,15 @@ import (
|
|||
)
|
||||
|
||||
// This step exports a VM built on ESXi using ovftool
|
||||
//
|
||||
// Uses:
|
||||
// display_name string
|
||||
type StepExport struct {
|
||||
Format string
|
||||
SkipExport bool
|
||||
VMName string
|
||||
OVFToolOptions []string
|
||||
OutputDir string
|
||||
OutputDir *string
|
||||
}
|
||||
|
||||
func GetOVFTool() string {
|
||||
ovftool := "ovftool"
|
||||
if runtime.GOOS == "windows" {
|
||||
ovftool = "ovftool.exe"
|
||||
}
|
||||
|
||||
if _, err := exec.LookPath(ovftool); err != nil {
|
||||
return ""
|
||||
}
|
||||
return ovftool
|
||||
}
|
||||
|
||||
func (s *StepExport) generateArgs(c *DriverConfig, displayName string, hidePassword bool) ([]string, error) {
|
||||
func (s *StepExport) generateRemoteExportArgs(c *DriverConfig, displayName string, hidePassword bool, exportOutputPath string) ([]string, error) {
|
||||
|
||||
ovftool_uri := fmt.Sprintf("vi://%s/%s", c.RemoteHost, displayName)
|
||||
u, err := url.Parse(ovftool_uri)
|
||||
|
@ -57,7 +40,15 @@ func (s *StepExport) generateArgs(c *DriverConfig, displayName string, hidePassw
|
|||
"--skipManifestCheck",
|
||||
"-tt=" + s.Format,
|
||||
u.String(),
|
||||
s.OutputDir,
|
||||
filepath.Join(exportOutputPath, s.VMName+"."+s.Format),
|
||||
}
|
||||
return append(s.OVFToolOptions, args...), nil
|
||||
}
|
||||
|
||||
func (s *StepExport) generateLocalExportArgs(exportOutputPath string) ([]string, error) {
|
||||
args := []string{
|
||||
filepath.Join(exportOutputPath, s.VMName+".vmx"),
|
||||
filepath.Join(exportOutputPath, s.VMName+"."+s.Format),
|
||||
}
|
||||
return append(s.OVFToolOptions, args...), nil
|
||||
}
|
||||
|
@ -65,6 +56,7 @@ func (s *StepExport) generateArgs(c *DriverConfig, displayName string, hidePassw
|
|||
func (s *StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
c := state.Get("driverConfig").(*DriverConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
driver := state.Get("driver").(Driver)
|
||||
|
||||
// Skip export if requested
|
||||
if s.SkipExport {
|
||||
|
@ -72,57 +64,72 @@ func (s *StepExport) Run(ctx context.Context, state multistep.StateBag) multiste
|
|||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
if c.RemoteType != "esx5" {
|
||||
ui.Say("Skipping export of virtual machine (export is allowed only for ESXi)...")
|
||||
return multistep.ActionContinue
|
||||
// load output path from state. If it doesn't exist, just use the local
|
||||
// outputdir.
|
||||
exportOutputPath, ok := state.Get("export_output_path").(string)
|
||||
if !ok || exportOutputPath == "" {
|
||||
if *s.OutputDir != "" {
|
||||
exportOutputPath = *s.OutputDir
|
||||
} else {
|
||||
exportOutputPath = s.VMName
|
||||
}
|
||||
}
|
||||
|
||||
ovftool := GetOVFTool()
|
||||
if ovftool == "" {
|
||||
err := fmt.Errorf("Error ovftool not found")
|
||||
state.Put("error", err)
|
||||
err := os.MkdirAll(exportOutputPath, 0755)
|
||||
if err != nil {
|
||||
state.Put("error creating export directory", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Export the VM
|
||||
if s.OutputDir == "" {
|
||||
s.OutputDir = s.VMName + "." + s.Format
|
||||
}
|
||||
|
||||
os.MkdirAll(s.OutputDir, 0755)
|
||||
|
||||
ui.Say("Exporting virtual machine...")
|
||||
var displayName string
|
||||
if v, ok := state.GetOk("display_name"); ok {
|
||||
displayName = v.(string)
|
||||
}
|
||||
ui_args, err := s.generateArgs(c, displayName, true)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Couldn't generate ovftool uri: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
|
||||
var args, ui_args []string
|
||||
|
||||
ovftool := GetOVFTool()
|
||||
if c.RemoteType == "esx5" {
|
||||
// Generate arguments for the ovftool command, but obfuscating the
|
||||
// password that we can log the command to the UI for debugging.
|
||||
ui_args, err := s.generateRemoteExportArgs(c, displayName, true, exportOutputPath)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Couldn't generate ovftool export args: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Message(fmt.Sprintf("Executing: %s %s", ovftool,
|
||||
strings.Join(ui_args, " ")))
|
||||
// Re-run the generate command, this time without obfuscating the
|
||||
// password, so we can actually use it.
|
||||
args, err = s.generateRemoteExportArgs(c, displayName, false, exportOutputPath)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Couldn't generate ovftool export args: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
} else {
|
||||
args, err = s.generateLocalExportArgs(exportOutputPath)
|
||||
ui.Message(fmt.Sprintf("Executing: %s %s", ovftool,
|
||||
strings.Join(ui_args, " ")))
|
||||
}
|
||||
ui.Message(fmt.Sprintf("Executing: %s %s", ovftool, strings.Join(ui_args, " ")))
|
||||
var out bytes.Buffer
|
||||
args, err := s.generateArgs(c, displayName, false)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Couldn't generate ovftool uri: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
cmd := exec.Command(ovftool, args...)
|
||||
cmd.Stdout = &out
|
||||
if err := cmd.Run(); err != nil {
|
||||
err := fmt.Errorf("Error exporting virtual machine: %s\n%s\n", err, out.String())
|
||||
err := fmt.Errorf("Couldn't generate ovftool export args: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message(out.String())
|
||||
if err := driver.Export(args); err != nil {
|
||||
err := fmt.Errorf("Error performing ovftool export: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
|
|
@ -2,22 +2,41 @@ package common
|
|||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStepExport_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepExport)
|
||||
}
|
||||
|
||||
func testStepExport_wrongtype_impl(t *testing.T, remoteType string) {
|
||||
func stringPointer(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func remoteExportTestState(t *testing.T) multistep.StateBag {
|
||||
state := testState(t)
|
||||
driverConfig := &DriverConfig{
|
||||
RemoteHost: "123.45.67.8",
|
||||
RemotePassword: "password",
|
||||
RemoteUser: "user",
|
||||
RemoteType: "esx5",
|
||||
}
|
||||
state.Put("driverConfig", driverConfig)
|
||||
state.Put("display_name", "vm_name")
|
||||
return state
|
||||
}
|
||||
|
||||
func TestStepExport_ReturnIfSkip(t *testing.T) {
|
||||
state := testState(t)
|
||||
driverConfig := &DriverConfig{}
|
||||
state.Put("driverConfig", driverConfig)
|
||||
step := new(StepExport)
|
||||
|
||||
var config DriverConfig
|
||||
config.RemoteType = "foo"
|
||||
state.Put("driverConfig", &config)
|
||||
step.SkipExport = true
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
|
@ -26,11 +45,189 @@ func testStepExport_wrongtype_impl(t *testing.T, remoteType string) {
|
|||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
// We told step to skip so it should not have reached the driver's Export
|
||||
// func.
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
if d.ExportCalled {
|
||||
t.Fatal("Should not have called the driver export func")
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
}
|
||||
|
||||
func TestStepExport_wrongtype_impl(t *testing.T) {
|
||||
testStepExport_wrongtype_impl(t, "foo")
|
||||
testStepExport_wrongtype_impl(t, "")
|
||||
func TestStepExport_localArgs(t *testing.T) {
|
||||
// even though we aren't overriding the remote args and they are present,
|
||||
// test shouldn't use them since remoteType is not set to esx.
|
||||
state := testState(t)
|
||||
driverConfig := &DriverConfig{}
|
||||
state.Put("driverConfig", driverConfig)
|
||||
step := new(StepExport)
|
||||
|
||||
step.SkipExport = false
|
||||
step.OutputDir = stringPointer("test-output")
|
||||
step.VMName = "test-name"
|
||||
step.Format = "ova"
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
// Check that step ran, and called Export with the expected args.
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
if !d.ExportCalled {
|
||||
t.Fatal("Should have called the driver export func")
|
||||
}
|
||||
|
||||
assert.Equal(t, d.ExportArgs,
|
||||
[]string{
|
||||
filepath.Join("test-output", "test-name.vmx"),
|
||||
filepath.Join("test-output", "test-name.ova")})
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
}
|
||||
|
||||
func TestStepExport_localArgsExportOutputPath(t *testing.T) {
|
||||
// even though we aren't overriding the remote args and they are present,
|
||||
// test shouldn't use them since remoteType is not set to esx.
|
||||
state := testState(t)
|
||||
driverConfig := &DriverConfig{}
|
||||
state.Put("driverConfig", driverConfig)
|
||||
state.Put("export_output_path", "local_output")
|
||||
step := new(StepExport)
|
||||
|
||||
step.SkipExport = false
|
||||
step.OutputDir = stringPointer("test-output")
|
||||
step.VMName = "test-name"
|
||||
step.Format = "ova"
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
// Check that step ran, and called Export with the expected args.
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
if !d.ExportCalled {
|
||||
t.Fatal("Should have called the driver export func")
|
||||
}
|
||||
|
||||
assert.Equal(t, d.ExportArgs,
|
||||
[]string{
|
||||
filepath.Join("local_output", "test-name.vmx"),
|
||||
filepath.Join("local_output", "test-name.ova")})
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
}
|
||||
|
||||
func TestStepExport_localArgs_OvftoolOptions(t *testing.T) {
|
||||
// even though we aren't overriding the remote args and they are present,
|
||||
// test shouldn't use them since remoteType is not set to esx.
|
||||
state := testState(t)
|
||||
driverConfig := &DriverConfig{}
|
||||
state.Put("driverConfig", driverConfig)
|
||||
step := new(StepExport)
|
||||
|
||||
step.SkipExport = false
|
||||
step.OutputDir = stringPointer("test-output")
|
||||
step.VMName = "test-name"
|
||||
step.Format = "ova"
|
||||
step.OVFToolOptions = []string{"--option=value", "--second-option=\"quoted value\""}
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
// Check that step ran, and called Export with the expected args.
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
if !d.ExportCalled {
|
||||
t.Fatal("Should have called the driver export func")
|
||||
}
|
||||
|
||||
assert.Equal(t, d.ExportArgs, []string{"--option=value",
|
||||
"--second-option=\"quoted value\"",
|
||||
filepath.Join("test-output", "test-name.vmx"),
|
||||
filepath.Join("test-output", "test-name.ova")})
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
}
|
||||
|
||||
func TestStepExport_RemoteArgs(t *testing.T) {
|
||||
// Even though we aren't overriding the remote args and they are present,
|
||||
// test shouldn't use them since remoteType is not set to esx.
|
||||
state := remoteExportTestState(t)
|
||||
step := new(StepExport)
|
||||
|
||||
step.SkipExport = false
|
||||
step.OutputDir = stringPointer("test-output")
|
||||
step.VMName = "test-name"
|
||||
step.Format = "ova"
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
// Check that step ran, and called Export with the expected args.
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
if !d.ExportCalled {
|
||||
t.Fatal("Should have called the driver export func")
|
||||
}
|
||||
|
||||
assert.Equal(t, d.ExportArgs, []string{"--noSSLVerify=true",
|
||||
"--skipManifestCheck",
|
||||
"-tt=ova",
|
||||
"vi://user:password@123.45.67.8/vm_name",
|
||||
filepath.Join("test-output", "test-name.ova")})
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
}
|
||||
|
||||
func TestStepExport_RemoteArgsWithExportOutputPath(t *testing.T) {
|
||||
// Even though we aren't overriding the remote args and they are present,
|
||||
// test shouldn't use them since remoteType is not set to esx.
|
||||
state := remoteExportTestState(t)
|
||||
state.Put("export_output_path", "local_output")
|
||||
step := new(StepExport)
|
||||
|
||||
step.SkipExport = false
|
||||
step.OutputDir = stringPointer("test-output")
|
||||
step.VMName = "test-name"
|
||||
step.Format = "ova"
|
||||
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
// Check that step ran, and called Export with the expected args.
|
||||
d := state.Get("driver").(*DriverMock)
|
||||
if !d.ExportCalled {
|
||||
t.Fatal("Should have called the driver export func")
|
||||
}
|
||||
|
||||
assert.Equal(t, d.ExportArgs, []string{"--noSSLVerify=true",
|
||||
"--skipManifestCheck",
|
||||
"-tt=ova",
|
||||
"vi://user:password@123.45.67.8/vm_name",
|
||||
filepath.Join("local_output", "test-name.ova")})
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
}
|
||||
|
|
|
@ -171,6 +171,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
SkipExport: b.config.SkipExport,
|
||||
VMName: b.config.VMName,
|
||||
OVFToolOptions: b.config.OVFToolOptions,
|
||||
OutputDir: &b.config.OutputConfig.OutputDir,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,7 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"}
|
||||
|
||||
b = Builder{}
|
||||
_, _, errs := b.Prepare(config)
|
||||
if errs == nil {
|
||||
|
|
|
@ -176,18 +176,19 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if c.Format != "" {
|
||||
if c.Format == "" {
|
||||
if c.RemoteType != "esx5" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("format is only valid when RemoteType=esx5"))
|
||||
c.Format = "vmx"
|
||||
} else {
|
||||
c.Format = "ovf"
|
||||
}
|
||||
} else {
|
||||
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"))
|
||||
if c.RemoteType != "esx5" && c.Format == "vmx" {
|
||||
// if we're building locally and want a vmx, there's nothing to export.
|
||||
// Set skip export flag here to keep the export step from attempting
|
||||
// an unneded export
|
||||
c.SkipExport = true
|
||||
}
|
||||
|
||||
err = c.DriverConfig.Validate(c.SkipExport)
|
||||
|
|
|
@ -170,6 +170,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
SkipExport: b.config.SkipExport,
|
||||
VMName: b.config.VMName,
|
||||
OVFToolOptions: b.config.OVFToolOptions,
|
||||
OutputDir: &b.config.OutputConfig.OutputDir,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
expected := []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)}
|
||||
if !reflect.DeepEqual(b.config.FloppyFiles, expected) {
|
||||
t.Fatalf("bad: %#v", b.config.FloppyFiles)
|
||||
|
|
|
@ -123,23 +123,32 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if c.Format == "" {
|
||||
if c.RemoteType != "esx5" {
|
||||
c.Format = "vmx"
|
||||
} else {
|
||||
c.Format = "ovf"
|
||||
}
|
||||
}
|
||||
|
||||
if c.RemoteType != "esx5" && c.Format == "vmx" {
|
||||
// if we're building locally and want a vmx, there's nothing to export.
|
||||
// Set skip export flag here to keep the export step from attempting
|
||||
// an unneded export
|
||||
c.SkipExport = true
|
||||
}
|
||||
|
||||
err = c.DriverConfig.Validate(c.SkipExport)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
if c.Format != "" {
|
||||
if c.Format == "" {
|
||||
if c.RemoteType != "esx5" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("format is only valid when remote_type=esx5"))
|
||||
c.Format = "vmx"
|
||||
} else {
|
||||
c.Format = "ovf"
|
||||
}
|
||||
} else {
|
||||
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
|
||||
|
|
|
@ -1,33 +1,30 @@
|
|||
<!-- Code generated from the comments of the ExportConfig struct in builder/vmware/common/export_config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `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. This option
|
||||
currently only works when option remote_type is set to "esx5".
|
||||
format of the exported virtual machine. This defaults to "ovf" for
|
||||
remote (esx) builds, and "vmx" for local builds.
|
||||
Before using this option, you need to install ovftool.
|
||||
Since ovftool is only capable of password based authentication
|
||||
remote_password must be set when exporting the VM.
|
||||
remote_password must be set when exporting the VM from a remote instance.
|
||||
If you are building locally, Packer will create a vmx and then
|
||||
export that vm to an ovf or ova. Packer will not delete the vmx and vmdk
|
||||
files; this is left up to the user if you don't want to keep those
|
||||
files.
|
||||
|
||||
- `ovftool_options` ([]string) - 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. Currently, exporting the build VM (with ovftool) is only
|
||||
supported when building on ESXi e.g. when `remote_type` is set to
|
||||
`esx5`. See the [Building on a Remote vSphere
|
||||
Hypervisor](/docs/builders/vmware-iso#building-on-a-remote-vsphere-hypervisor)
|
||||
section below for more info.
|
||||
and `--targetType` are used by Packer for remote exports, and should not
|
||||
be passed to this argument. For ovf/ova exports from local builds, Packer
|
||||
does not automatically set any ovftool options.
|
||||
|
||||
- `skip_export` (bool) - 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. Currently, exporting the build VM is only supported when building on
|
||||
ESXi e.g. when `remote_type` is set to `esx5`. See the [Building on a
|
||||
Remote vSphere
|
||||
Hypervisor](/docs/builders/vmware-iso#building-on-a-remote-vsphere-hypervisor)
|
||||
section below for more info.
|
||||
- `skip_export` (bool) - Defaults to `false`. When true, Packer will not export the VM. This can
|
||||
be useful if the build output is not the resultant image, but created
|
||||
inside the VM.
|
||||
|
||||
- `keep_registered` (bool) - Set this to true if you would like to keep
|
||||
the VM registered with the remote ESXi server. If you do not need to export
|
||||
the vm, then also set skip_export: true in order to avoid an unnecessary
|
||||
step of using ovftool to export the vm. Defaults to false.
|
||||
- `keep_registered` (bool) - Set this to true if you would like to keep a remotely-built
|
||||
VM registered with the remote ESXi server. If you do not need to export
|
||||
the vm, then also set `skip_export: true` in order to avoid unnecessarily
|
||||
using ovftool to export the vm. Defaults to false.
|
||||
|
||||
- `skip_compaction` (bool) - VMware-created disks are defragmented and
|
||||
compacted at the end of the build process using vmware-vdiskmanager or
|
||||
|
|
Loading…
Reference in New Issue