From 0f2c3d7812cc4dda27d67bf6c3ef2471278fde60 Mon Sep 17 00:00:00 2001 From: Imran Hussain Date: Fri, 18 Oct 2024 13:49:26 +0100 Subject: [PATCH] Extra CD support, supply files via cd_files or cd_content (#119) * Extra CD support, supply files via cd_files or cd_content * Fix highlighted go linter errors * Add suggested tweak * Use driver for adding CDROM and add tests --------- Co-authored-by: Carlos Lapao --- .web-docs/components/builder/iso/README.md | 11 +++ .web-docs/components/builder/pvm/README.md | 11 +++ builder/parallels/common/driver_mock.go | 2 + builder/parallels/common/step_attach_cd.go | 75 ++++++++++++++++++ .../parallels/common/step_attach_cd_test.go | 76 +++++++++++++++++++ builder/parallels/iso/builder.go | 6 ++ builder/parallels/iso/builder.hcl2spec.go | 6 ++ builder/parallels/pvm/builder.go | 5 ++ builder/parallels/pvm/config.go | 1 + builder/parallels/pvm/config.hcl2spec.go | 6 ++ docs/builders/iso.mdx | 11 +++ docs/builders/pvm.mdx | 11 +++ 12 files changed, 221 insertions(+) create mode 100644 builder/parallels/common/step_attach_cd.go create mode 100644 builder/parallels/common/step_attach_cd_test.go diff --git a/.web-docs/components/builder/iso/README.md b/.web-docs/components/builder/iso/README.md index 4d51550c..e5dafcf2 100644 --- a/.web-docs/components/builder/iso/README.md +++ b/.web-docs/components/builder/iso/README.md @@ -241,6 +241,17 @@ In HCL2: Kickstart or other early initialization tools, which can benefit from labelled floppy disks. By default, the floppy label will be 'packer'. +- `cd_files` ([]string) - A list of files to place onto a CD that is attached when the VM is + booted. This can include either files or directories; any directories + will be copied onto the CD recursively, preserving directory structure + hierarchy. Symlinks will have the link's target copied into the directory + tree on the CD where the symlink was. File globbing is allowed. + +- `cd_content` (map[string]string) - Key/Values to add to the CD. The keys represent the paths, and the values + contents. It can be used alongside `cd_files`, which is useful to add large + files without loading them into memory. If any paths are specified by both, + the contents in `cd_content` will take precedence. + - `guest_os_type` (string) - The guest OS type being installed. By default this is "other", but you can get _dramatic_ performance improvements by setting this to the proper value. To view all available values for this run diff --git a/.web-docs/components/builder/pvm/README.md b/.web-docs/components/builder/pvm/README.md index 409b0c3c..12fe9096 100644 --- a/.web-docs/components/builder/pvm/README.md +++ b/.web-docs/components/builder/pvm/README.md @@ -93,6 +93,17 @@ can also be supplied to override the typical auto-generated key: Kickstart or other early initialization tools, which can benefit from labelled floppy disks. By default, the floppy label will be 'packer'. +- `cd_files` ([]string) - A list of files to place onto a CD that is attached when the VM is + booted. This can include either files or directories; any directories + will be copied onto the CD recursively, preserving directory structure + hierarchy. Symlinks will have the link's target copied into the directory + tree on the CD where the symlink was. File globbing is allowed. + +- `cd_content` (map[string]string) - Key/Values to add to the CD. The keys represent the paths, and the values + contents. It can be used alongside `cd_files`, which is useful to add large + files without loading them into memory. If any paths are specified by both, + the contents in `cd_content` will take precedence. + - `output_directory` (string) - This is the path to the directory where the resulting virtual machine will be created. This may be relative or absolute. If relative, the path is relative to the working directory when `packer` diff --git a/builder/parallels/common/driver_mock.go b/builder/parallels/common/driver_mock.go index 95ce30b5..cf72c8c6 100644 --- a/builder/parallels/common/driver_mock.go +++ b/builder/parallels/common/driver_mock.go @@ -76,6 +76,8 @@ func (d *DriverMock) DeviceAddCDROM(name string, image string) (string, error) { d.DeviceAddCDROMCalled = true d.DeviceAddCDROMName = name d.DeviceAddCDROMImage = image + d.DeviceAddCDROMResult = "cdrom0" + d.DeviceAddCDROMErr = nil return d.DeviceAddCDROMResult, d.DeviceAddCDROMErr } diff --git a/builder/parallels/common/step_attach_cd.go b/builder/parallels/common/step_attach_cd.go new file mode 100644 index 00000000..a43b6400 --- /dev/null +++ b/builder/parallels/common/step_attach_cd.go @@ -0,0 +1,75 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/packer-plugin-sdk/multistep" + packersdk "github.com/hashicorp/packer-plugin-sdk/packer" +) + +// StepAttachCD is a step that attaches a cd to the virtual machine. +// +// Uses: +// +// driver Driver +// ui packersdk.Ui +// vmName string +// +// Produces: +type StepAttachCD struct { + cdPath string + cdromDevice string +} + +// Run adds a virtual CD to the VM and attaches the image. +// If the image is not specified, then this step will be skipped. +func (s *StepAttachCD) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { + // Determine if we even have a cd to attach + var cdPath string + if cdPathRaw, ok := state.GetOk("cd_path"); ok { + cdPath = cdPathRaw.(string) + } else { + log.Println("No cd found, not attaching.") + return multistep.ActionContinue + } + + driver := state.Get("driver").(Driver) + ui := state.Get("ui").(packersdk.Ui) + vmName := state.Get("vmName").(string) + + ui.Say("Attaching cd...") + // Attaching the cd using the driver + cdrom, err := driver.DeviceAddCDROM(vmName, cdPath) + if err != nil { + err = fmt.Errorf("error attaching cd: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + // Track the device name so that we can can delete later + s.cdromDevice = cdrom + + return multistep.ActionContinue +} + +// Cleanup removes the virtual FDD device attached to the VM. +func (s *StepAttachCD) Cleanup(state multistep.StateBag) { + driver := state.Get("driver").(Driver) + vmName := state.Get("vmName").(string) + + if s.cdPath == "" { + return + } + + log.Println("Detaching cd disk...") + command := []string{ + "set", vmName, + "--device-del", s.cdromDevice, + } + _ = driver.Prlctl(command...) +} diff --git a/builder/parallels/common/step_attach_cd_test.go b/builder/parallels/common/step_attach_cd_test.go new file mode 100644 index 00000000..ff199d22 --- /dev/null +++ b/builder/parallels/common/step_attach_cd_test.go @@ -0,0 +1,76 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package common + +import ( + "context" + "io/ioutil" + "os" + "testing" + + "github.com/hashicorp/packer-plugin-sdk/multistep" +) + +func TestStepAttachCD_impl(t *testing.T) { + var _ multistep.Step = new(StepAttachCD) +} + +func TestStepAttachCD(t *testing.T) { + state := testState(t) + step := new(StepAttachCD) + + // Create a temporary file for our CD + tf, err := ioutil.TempFile("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + tf.Close() + defer os.Remove(tf.Name()) + + state.Put("cd_path", tf.Name()) + 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") + } + + if !driver.DeviceAddCDROMCalled { + t.Fatal("DeviceAddCDROM not called") + } + if driver.DeviceAddCDROMName != "foo" { + t.Fatal("DeviceAddCDROM name isn't what we specified (foo)") + } + if driver.DeviceAddCDROMResult != "cdrom0" { + t.Fatal("DeviceAddCDROM didn't return cdrom0") + } + if driver.DeviceAddCDROMErr != nil { + t.Fatal("DeviceAddCDROM returned an error") + } + +} + +func TestStepAttachCD_noCD(t *testing.T) { + state := testState(t) + step := new(StepAttachCD) + + 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") + } + + if driver.DeviceAddCDROMCalled { + t.Fatal("DeviceAddCDROM has been called") + } +} diff --git a/builder/parallels/iso/builder.go b/builder/parallels/iso/builder.go index 874790ba..797d6767 100644 --- a/builder/parallels/iso/builder.go +++ b/builder/parallels/iso/builder.go @@ -36,6 +36,7 @@ type Config struct { commonsteps.HTTPConfig `mapstructure:",squash"` commonsteps.ISOConfig `mapstructure:",squash"` commonsteps.FloppyConfig `mapstructure:",squash"` + commonsteps.CDConfig `mapstructure:",squash"` bootcommand.BootConfig `mapstructure:",squash"` parallelscommon.OutputConfig `mapstructure:",squash"` parallelscommon.HWConfig `mapstructure:",squash"` @@ -214,6 +215,10 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) Directories: b.config.FloppyConfig.FloppyDirectories, Label: b.config.FloppyConfig.FloppyLabel, }, + &commonsteps.StepCreateCD{ + Files: b.config.CDConfig.CDFiles, + Content: b.config.CDConfig.CDContent, + }, commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), new(stepCreateVM), new(stepCreateDisk), @@ -223,6 +228,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) ParallelsToolsMode: b.config.ParallelsToolsMode, }, new(parallelscommon.StepAttachFloppy), + new(parallelscommon.StepAttachCD), ¶llelscommon.StepPrlctl{ Commands: b.config.Prlctl, Ctx: b.config.ctx, diff --git a/builder/parallels/iso/builder.hcl2spec.go b/builder/parallels/iso/builder.hcl2spec.go index fa17d7e7..fb09f77a 100644 --- a/builder/parallels/iso/builder.hcl2spec.go +++ b/builder/parallels/iso/builder.hcl2spec.go @@ -33,6 +33,9 @@ type FlatConfig struct { FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"` FloppyContent map[string]string `mapstructure:"floppy_content" cty:"floppy_content" hcl:"floppy_content"` FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"` + CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"` + CDContent map[string]string `mapstructure:"cd_content" cty:"cd_content" hcl:"cd_content"` + CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"` BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"` BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"` BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"` @@ -142,6 +145,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false}, "floppy_content": &hcldec.AttrSpec{Name: "floppy_content", Type: cty.Map(cty.String), Required: false}, "floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false}, + "cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false}, + "cd_content": &hcldec.AttrSpec{Name: "cd_content", Type: cty.Map(cty.String), Required: false}, + "cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false}, "boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false}, "boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false}, "boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false}, diff --git a/builder/parallels/pvm/builder.go b/builder/parallels/pvm/builder.go index b47bc391..e64bf096 100644 --- a/builder/parallels/pvm/builder.go +++ b/builder/parallels/pvm/builder.go @@ -67,6 +67,10 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) Directories: b.config.FloppyConfig.FloppyDirectories, Label: b.config.FloppyConfig.FloppyLabel, }, + &commonsteps.StepCreateCD{ + Files: b.config.CDConfig.CDFiles, + Content: b.config.CDConfig.CDContent, + }, ¶llelscommon.StepImport{ Name: b.config.VMName, SourcePath: b.config.SourcePath, @@ -77,6 +81,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) ParallelsToolsMode: b.config.ParallelsToolsMode, }, new(parallelscommon.StepAttachFloppy), + new(parallelscommon.StepAttachCD), ¶llelscommon.StepPrlctl{ Commands: b.config.Prlctl, Ctx: b.config.ctx, diff --git a/builder/parallels/pvm/config.go b/builder/parallels/pvm/config.go index 32759204..cf04617c 100644 --- a/builder/parallels/pvm/config.go +++ b/builder/parallels/pvm/config.go @@ -24,6 +24,7 @@ import ( type Config struct { common.PackerConfig `mapstructure:",squash"` commonsteps.FloppyConfig `mapstructure:",squash"` + commonsteps.CDConfig `mapstructure:",squash"` parallelscommon.OutputConfig `mapstructure:",squash"` parallelscommon.PrlctlConfig `mapstructure:",squash"` parallelscommon.PrlctlPostConfig `mapstructure:",squash"` diff --git a/builder/parallels/pvm/config.hcl2spec.go b/builder/parallels/pvm/config.hcl2spec.go index 7a1c8c71..eb17a27d 100644 --- a/builder/parallels/pvm/config.hcl2spec.go +++ b/builder/parallels/pvm/config.hcl2spec.go @@ -22,6 +22,9 @@ type FlatConfig struct { FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"` FloppyContent map[string]string `mapstructure:"floppy_content" cty:"floppy_content" hcl:"floppy_content"` FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"` + CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"` + CDContent map[string]string `mapstructure:"cd_content" cty:"cd_content" hcl:"cd_content"` + CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"` OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"` Prlctl [][]string `mapstructure:"prlctl" required:"false" cty:"prlctl" hcl:"prlctl"` PrlctlPost [][]string `mapstructure:"prlctl_post" required:"false" cty:"prlctl_post" hcl:"prlctl_post"` @@ -113,6 +116,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false}, "floppy_content": &hcldec.AttrSpec{Name: "floppy_content", Type: cty.Map(cty.String), Required: false}, "floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false}, + "cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false}, + "cd_content": &hcldec.AttrSpec{Name: "cd_content", Type: cty.Map(cty.String), Required: false}, + "cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false}, "output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false}, "prlctl": &hcldec.AttrSpec{Name: "prlctl", Type: cty.List(cty.List(cty.String)), Required: false}, "prlctl_post": &hcldec.AttrSpec{Name: "prlctl_post", Type: cty.List(cty.List(cty.String)), Required: false}, diff --git a/docs/builders/iso.mdx b/docs/builders/iso.mdx index 9017ca33..f8a9ce0e 100644 --- a/docs/builders/iso.mdx +++ b/docs/builders/iso.mdx @@ -129,6 +129,17 @@ can also be supplied to override the typical auto-generated key: Kickstart or other early initialization tools, which can benefit from labelled floppy disks. By default, the floppy label will be 'packer'. +- `cd_files` ([]string) - A list of files to place onto a CD that is attached when the VM is + booted. This can include either files or directories; any directories + will be copied onto the CD recursively, preserving directory structure + hierarchy. Symlinks will have the link's target copied into the directory + tree on the CD where the symlink was. File globbing is allowed. + +- `cd_content` (map[string]string) - Key/Values to add to the CD. The keys represent the paths, and the values + contents. It can be used alongside `cd_files`, which is useful to add large + files without loading them into memory. If any paths are specified by both, + the contents in `cd_content` will take precedence. + - `guest_os_type` (string) - The guest OS type being installed. By default this is "other", but you can get _dramatic_ performance improvements by setting this to the proper value. To view all available values for this run diff --git a/docs/builders/pvm.mdx b/docs/builders/pvm.mdx index 3cc08122..4a0e639b 100644 --- a/docs/builders/pvm.mdx +++ b/docs/builders/pvm.mdx @@ -103,6 +103,17 @@ can also be supplied to override the typical auto-generated key: Kickstart or other early initialization tools, which can benefit from labelled floppy disks. By default, the floppy label will be 'packer'. +- `cd_files` ([]string) - A list of files to place onto a CD that is attached when the VM is + booted. This can include either files or directories; any directories + will be copied onto the CD recursively, preserving directory structure + hierarchy. Symlinks will have the link's target copied into the directory + tree on the CD where the symlink was. File globbing is allowed. + +- `cd_content` (map[string]string) - Key/Values to add to the CD. The keys represent the paths, and the values + contents. It can be used alongside `cd_files`, which is useful to add large + files without loading them into memory. If any paths are specified by both, + the contents in `cd_content` will take precedence. + - `output_directory` (string) - This is the path to the directory where the resulting virtual machine will be created. This may be relative or absolute. If relative, the path is relative to the working directory when `packer`