diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index 34bab5b98..8092ec560 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -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 +} diff --git a/builder/vmware/common/driver_config.go b/builder/vmware/common/driver_config.go index d72320451..35f9d7187 100644 --- a/builder/vmware/common/driver_config.go +++ b/builder/vmware/common/driver_config.go @@ -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 } diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esx5.go index b3ef21cf0..cb4b2352d 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esx5.go @@ -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" { diff --git a/builder/vmware/common/driver_mock.go b/builder/vmware/common/driver_mock.go index c298294be..ba5ecc57f 100644 --- a/builder/vmware/common/driver_mock.go +++ b/builder/vmware/common/driver_mock.go @@ -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 { diff --git a/builder/vmware/common/export_config.go b/builder/vmware/common/export_config.go index 3584ef4cc..0d38e6176 100644 --- a/builder/vmware/common/export_config.go +++ b/builder/vmware/common/export_config.go @@ -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 } diff --git a/builder/vmware/common/step_export.go b/builder/vmware/common/step_export.go index 91d3a9552..52ff1a210 100644 --- a/builder/vmware/common/step_export.go +++ b/builder/vmware/common/step_export.go @@ -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,58 +64,73 @@ 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) + err := fmt.Errorf("Couldn't generate ovftool export args: %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()) + + 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 } - ui.Message(out.String()) - return multistep.ActionContinue } diff --git a/builder/vmware/common/step_export_test.go b/builder/vmware/common/step_export_test.go index 70197c827..9c8b3f740 100644 --- a/builder/vmware/common/step_export_test.go +++ b/builder/vmware/common/step_export_test.go @@ -2,22 +2,144 @@ 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) + + step.SkipExport = true + + 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") + } + + // 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_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) - var config DriverConfig - config.RemoteType = "foo" - state.Put("driverConfig", &config) + 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) @@ -26,11 +148,86 @@ func testStepExport_wrongtype_impl(t *testing.T, remoteType string) { 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_wrongtype_impl(t *testing.T) { - testStepExport_wrongtype_impl(t, "foo") - testStepExport_wrongtype_impl(t, "") +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) } diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index d8cc57cbd..353cd0199 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -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, }, } diff --git a/builder/vmware/iso/builder_test.go b/builder/vmware/iso/builder_test.go index 899525789..cd39df5d3 100644 --- a/builder/vmware/iso/builder_test.go +++ b/builder/vmware/iso/builder_test.go @@ -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 { diff --git a/builder/vmware/iso/config.go b/builder/vmware/iso/config.go index 0d51dc871..253b578ff 100644 --- a/builder/vmware/iso/config.go +++ b/builder/vmware/iso/config.go @@ -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) diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index 11aa59d35..f9ba881d5 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -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, }, } diff --git a/builder/vmware/vmx/builder_test.go b/builder/vmware/vmx/builder_test.go index 4e2a165a7..66926b289 100644 --- a/builder/vmware/vmx/builder_test.go +++ b/builder/vmware/vmx/builder_test.go @@ -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) diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index 5fd826d4a..cc7b31531 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -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 diff --git a/website/pages/partials/builder/vmware/common/ExportConfig-not-required.mdx b/website/pages/partials/builder/vmware/common/ExportConfig-not-required.mdx index 4866f61ed..43288200e 100644 --- a/website/pages/partials/builder/vmware/common/ExportConfig-not-required.mdx +++ b/website/pages/partials/builder/vmware/common/ExportConfig-not-required.mdx @@ -1,33 +1,30 @@ - `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