diff --git a/builder/virtualbox/common/output_config.go b/builder/virtualbox/common/output_config.go index e99919dcb..aedfa5cf7 100644 --- a/builder/virtualbox/common/output_config.go +++ b/builder/virtualbox/common/output_config.go @@ -17,6 +17,10 @@ type OutputConfig struct { // the builder. By default this is output-BUILDNAME where "BUILDNAME" is the // name of the build. OutputDir string `mapstructure:"output_directory" required:"false"` + // This is the base name of the file (excluding the file extension) where + // the resulting virtual machine will be created. By default this is the + // `vm_name`. + OutputFilename string `mapstructure:"output_filename" required:"false"` } func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error { diff --git a/builder/virtualbox/common/step_export.go b/builder/virtualbox/common/step_export.go index 57e5bed2f..7a3dd13f4 100644 --- a/builder/virtualbox/common/step_export.go +++ b/builder/virtualbox/common/step_export.go @@ -19,6 +19,7 @@ import ( type StepExport struct { Format string OutputDir string + OutputFilename string ExportOpts []string Bundling VBoxBundleConfig SkipNatMapping bool @@ -37,6 +38,9 @@ func (s *StepExport) Run(ctx context.Context, state multistep.StateBag) multiste driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) vmName := state.Get("vmName").(string) + if s.OutputFilename == "" { + s.OutputFilename = vmName + } // Skip export if requested if s.SkipExport { @@ -61,7 +65,7 @@ func (s *StepExport) Run(ctx context.Context, state multistep.StateBag) multiste } // Export the VM to an OVF - outputPath := filepath.Join(s.OutputDir, vmName+"."+s.Format) + outputPath := filepath.Join(s.OutputDir, s.OutputFilename+"."+s.Format) command := []string{ "export", diff --git a/builder/virtualbox/common/step_export_test.go b/builder/virtualbox/common/step_export_test.go index 6d37b3150..c5adc0a45 100644 --- a/builder/virtualbox/common/step_export_test.go +++ b/builder/virtualbox/common/step_export_test.go @@ -2,6 +2,7 @@ package common import ( "context" + "path/filepath" "testing" "github.com/hashicorp/packer/helper/multistep" @@ -43,3 +44,71 @@ func TestStepExport(t *testing.T) { t.Fatal("bad") } } + +func TestStepExport_OutputPath(t *testing.T) { + type testCase struct { + Step *StepExport + Expected string + Reason string + } + tcs := []testCase{ + { + Step: &StepExport{ + Format: "ova", + OutputDir: "output-dir", + OutputFilename: "output-filename", + }, + Expected: filepath.Join("output-dir", "output-filename.ova"), + Reason: "output_filename should not be vmName if set.", + }, + { + Step: &StepExport{ + Format: "ovf", + OutputDir: "output-dir", + OutputFilename: "", + }, + Expected: filepath.Join("output-dir", "foo.ovf"), + Reason: "output_filename should default to vmName.", + }, + } + for _, tc := range tcs { + state := testState(t) + state.Put("vmName", "foo") + + // Test the run + if action := tc.Step.Run(context.Background(), state); action != multistep.ActionContinue { + t.Fatalf("bad action: %#v", action) + } + + // Test output state + path, ok := state.GetOk("exportPath") + if !ok { + t.Fatal("should set exportPath") + } + if path != tc.Expected { + t.Fatalf("Expected %s didn't match received %s: %s", tc.Expected, path, tc.Reason) + } + } +} + +func TestStepExport_SkipExport(t *testing.T) { + state := testState(t) + step := StepExport{SkipExport: true} + + state.Put("vmName", "foo") + + driver := state.Get("driver").(*DriverMock) + + // Test the run + 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") + } + // Test driver + if len(driver.VBoxManageCalls) != 0 { + t.Fatal("shouldn't have called vboxmanage; skip_export was set.") + } + +} diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index a52837203..805e4a287 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -396,6 +396,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack &vboxcommon.StepExport{ Format: b.config.Format, OutputDir: b.config.OutputDir, + OutputFilename: b.config.OutputFilename, ExportOpts: b.config.ExportConfig.ExportOpts, Bundling: b.config.VBoxBundleConfig, SkipNatMapping: b.config.SkipNatMapping, diff --git a/builder/virtualbox/iso/builder.hcl2spec.go b/builder/virtualbox/iso/builder.hcl2spec.go index 017359cdb..c633840f4 100644 --- a/builder/virtualbox/iso/builder.hcl2spec.go +++ b/builder/virtualbox/iso/builder.hcl2spec.go @@ -35,6 +35,7 @@ type FlatConfig struct { Format *string `mapstructure:"format" required:"false" cty:"format"` ExportOpts []string `mapstructure:"export_opts" required:"false" cty:"export_opts"` OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory"` + OutputFilename *string `mapstructure:"output_filename" required:"false" cty:"output_filename"` Headless *bool `mapstructure:"headless" required:"false" cty:"headless"` VRDPBindAddress *string `mapstructure:"vrdp_bind_address" required:"false" cty:"vrdp_bind_address"` VRDPPortMin *int `mapstructure:"vrdp_port_min" required:"false" cty:"vrdp_port_min"` @@ -156,6 +157,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "format": &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false}, "export_opts": &hcldec.AttrSpec{Name: "export_opts", Type: cty.List(cty.String), Required: false}, "output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false}, + "output_filename": &hcldec.AttrSpec{Name: "output_filename", Type: cty.String, Required: false}, "headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false}, "vrdp_bind_address": &hcldec.AttrSpec{Name: "vrdp_bind_address", Type: cty.String, Required: false}, "vrdp_port_min": &hcldec.AttrSpec{Name: "vrdp_port_min", Type: cty.Number, Required: false}, diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index ab4b524cc..9ee156748 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -158,6 +158,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack &vboxcommon.StepExport{ Format: b.config.Format, OutputDir: b.config.OutputDir, + OutputFilename: b.config.OutputFilename, ExportOpts: b.config.ExportConfig.ExportOpts, SkipNatMapping: b.config.SkipNatMapping, SkipExport: b.config.SkipExport, diff --git a/builder/virtualbox/ovf/config.hcl2spec.go b/builder/virtualbox/ovf/config.hcl2spec.go index f24a40a8f..d38495a9b 100644 --- a/builder/virtualbox/ovf/config.hcl2spec.go +++ b/builder/virtualbox/ovf/config.hcl2spec.go @@ -28,6 +28,7 @@ type FlatConfig struct { Format *string `mapstructure:"format" required:"false" cty:"format"` ExportOpts []string `mapstructure:"export_opts" required:"false" cty:"export_opts"` OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory"` + OutputFilename *string `mapstructure:"output_filename" required:"false" cty:"output_filename"` Headless *bool `mapstructure:"headless" required:"false" cty:"headless"` VRDPBindAddress *string `mapstructure:"vrdp_bind_address" required:"false" cty:"vrdp_bind_address"` VRDPPortMin *int `mapstructure:"vrdp_port_min" required:"false" cty:"vrdp_port_min"` @@ -135,6 +136,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "format": &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false}, "export_opts": &hcldec.AttrSpec{Name: "export_opts", Type: cty.List(cty.String), Required: false}, "output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false}, + "output_filename": &hcldec.AttrSpec{Name: "output_filename", Type: cty.String, Required: false}, "headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false}, "vrdp_bind_address": &hcldec.AttrSpec{Name: "vrdp_bind_address", Type: cty.String, Required: false}, "vrdp_port_min": &hcldec.AttrSpec{Name: "vrdp_port_min", Type: cty.Number, Required: false}, diff --git a/builder/virtualbox/vm/builder.go b/builder/virtualbox/vm/builder.go index f8f493d60..2efa4d37a 100644 --- a/builder/virtualbox/vm/builder.go +++ b/builder/virtualbox/vm/builder.go @@ -141,6 +141,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack &vboxcommon.StepExport{ Format: b.config.Format, OutputDir: b.config.OutputDir, + OutputFilename: b.config.OutputFilename, ExportOpts: b.config.ExportOpts, SkipNatMapping: b.config.SkipNatMapping, SkipExport: b.config.SkipExport, diff --git a/builder/virtualbox/vm/config.hcl2spec.go b/builder/virtualbox/vm/config.hcl2spec.go index ede4b52ef..94d0e1a54 100644 --- a/builder/virtualbox/vm/config.hcl2spec.go +++ b/builder/virtualbox/vm/config.hcl2spec.go @@ -28,6 +28,7 @@ type FlatConfig struct { Format *string `mapstructure:"format" required:"false" cty:"format"` ExportOpts []string `mapstructure:"export_opts" required:"false" cty:"export_opts"` OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory"` + OutputFilename *string `mapstructure:"output_filename" required:"false" cty:"output_filename"` Headless *bool `mapstructure:"headless" required:"false" cty:"headless"` VRDPBindAddress *string `mapstructure:"vrdp_bind_address" required:"false" cty:"vrdp_bind_address"` VRDPPortMin *int `mapstructure:"vrdp_port_min" required:"false" cty:"vrdp_port_min"` @@ -131,6 +132,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "format": &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false}, "export_opts": &hcldec.AttrSpec{Name: "export_opts", Type: cty.List(cty.String), Required: false}, "output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false}, + "output_filename": &hcldec.AttrSpec{Name: "output_filename", Type: cty.String, Required: false}, "headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false}, "vrdp_bind_address": &hcldec.AttrSpec{Name: "vrdp_bind_address", Type: cty.String, Required: false}, "vrdp_port_min": &hcldec.AttrSpec{Name: "vrdp_port_min", Type: cty.Number, Required: false}, diff --git a/website/pages/partials/builder/virtualbox/common/OutputConfig-not-required.mdx b/website/pages/partials/builder/virtualbox/common/OutputConfig-not-required.mdx index 5794e6900..6fd36c64f 100644 --- a/website/pages/partials/builder/virtualbox/common/OutputConfig-not-required.mdx +++ b/website/pages/partials/builder/virtualbox/common/OutputConfig-not-required.mdx @@ -6,4 +6,8 @@ is executed. This directory must not exist or be empty prior to running the builder. By default this is output-BUILDNAME where "BUILDNAME" is the name of the build. + +- `output_filename` (string) - This is the base name of the file (excluding the file extension) where + the resulting virtual machine will be created. By default this is the + `vm_name`. \ No newline at end of file