From 642ed07476081536d3ee5d37f05b957c8d375601 Mon Sep 17 00:00:00 2001 From: sylviamoss Date: Mon, 19 Apr 2021 16:28:12 +0200 Subject: [PATCH] remote qemu plugin --- builder/qemu/artifact.go | 38 - builder/qemu/builder.go | 229 ------ builder/qemu/builder_test.go | 15 - builder/qemu/comm_config.go | 73 -- builder/qemu/comm_config_test.go | 145 ---- builder/qemu/config.go | 642 ---------------- builder/qemu/config.hcl2spec.go | 298 -------- builder/qemu/config_test.go | 695 ----------------- builder/qemu/driver.go | 247 ------ builder/qemu/driver_mock.go | 74 -- builder/qemu/qmp.go | 135 ---- builder/qemu/ssh.go | 30 - builder/qemu/step_configure_qmp.go | 92 --- builder/qemu/step_configure_vnc.go | 89 --- builder/qemu/step_convert_disk.go | 104 --- builder/qemu/step_convert_disk_test.go | 52 -- builder/qemu/step_copy_disk.go | 78 -- builder/qemu/step_copy_disk_test.go | 122 --- builder/qemu/step_create_disk.go | 91 --- builder/qemu/step_create_disk_test.go | 176 ----- builder/qemu/step_http_ip_discover.go | 68 -- builder/qemu/step_http_ip_discover_test.go | 34 - builder/qemu/step_port_forward.go | 74 -- builder/qemu/step_prepare_output_dir.go | 51 -- builder/qemu/step_resize_disk.go | 60 -- builder/qemu/step_resize_disk_test.go | 77 -- builder/qemu/step_run.go | 400 ---------- builder/qemu/step_run_test.go | 710 ------------------ builder/qemu/step_set_iso.go | 54 -- builder/qemu/step_shutdown.go | 94 --- builder/qemu/step_shutdown_test.go | 88 --- builder/qemu/step_test.go | 19 - builder/qemu/step_type_boot_command.go | 139 ---- builder/qemu/step_wait_guest_address.go | 136 ---- builder/qemu/version/version.go | 13 - command/plugin.go | 2 - website/content/docs/builders/qemu.mdx | 219 ------ .../builder/qemu/CommConfig-not-required.mdx | 15 - .../builder/qemu/Config-not-required.mdx | 325 -------- .../builder/qemu/QemuImgArgs-not-required.mdx | 9 - website/data/docs-nav-data.json | 4 - 41 files changed, 6016 deletions(-) delete mode 100644 builder/qemu/artifact.go delete mode 100644 builder/qemu/builder.go delete mode 100644 builder/qemu/builder_test.go delete mode 100644 builder/qemu/comm_config.go delete mode 100644 builder/qemu/comm_config_test.go delete mode 100644 builder/qemu/config.go delete mode 100644 builder/qemu/config.hcl2spec.go delete mode 100644 builder/qemu/config_test.go delete mode 100644 builder/qemu/driver.go delete mode 100644 builder/qemu/driver_mock.go delete mode 100644 builder/qemu/qmp.go delete mode 100644 builder/qemu/ssh.go delete mode 100644 builder/qemu/step_configure_qmp.go delete mode 100644 builder/qemu/step_configure_vnc.go delete mode 100644 builder/qemu/step_convert_disk.go delete mode 100644 builder/qemu/step_convert_disk_test.go delete mode 100644 builder/qemu/step_copy_disk.go delete mode 100644 builder/qemu/step_copy_disk_test.go delete mode 100644 builder/qemu/step_create_disk.go delete mode 100644 builder/qemu/step_create_disk_test.go delete mode 100644 builder/qemu/step_http_ip_discover.go delete mode 100644 builder/qemu/step_http_ip_discover_test.go delete mode 100644 builder/qemu/step_port_forward.go delete mode 100644 builder/qemu/step_prepare_output_dir.go delete mode 100644 builder/qemu/step_resize_disk.go delete mode 100644 builder/qemu/step_resize_disk_test.go delete mode 100644 builder/qemu/step_run.go delete mode 100644 builder/qemu/step_run_test.go delete mode 100644 builder/qemu/step_set_iso.go delete mode 100644 builder/qemu/step_shutdown.go delete mode 100644 builder/qemu/step_shutdown_test.go delete mode 100644 builder/qemu/step_test.go delete mode 100644 builder/qemu/step_type_boot_command.go delete mode 100644 builder/qemu/step_wait_guest_address.go delete mode 100644 builder/qemu/version/version.go delete mode 100644 website/content/docs/builders/qemu.mdx delete mode 100644 website/content/partials/builder/qemu/CommConfig-not-required.mdx delete mode 100644 website/content/partials/builder/qemu/Config-not-required.mdx delete mode 100644 website/content/partials/builder/qemu/QemuImgArgs-not-required.mdx diff --git a/builder/qemu/artifact.go b/builder/qemu/artifact.go deleted file mode 100644 index fe7674a47..000000000 --- a/builder/qemu/artifact.go +++ /dev/null @@ -1,38 +0,0 @@ -package qemu - -import ( - "fmt" - "os" -) - -// Artifact is the result of running the Qemu builder, namely a set -// of files associated with the resulting machine. -type Artifact struct { - dir string - f []string - state map[string]interface{} -} - -func (*Artifact) BuilderId() string { - return BuilderId -} - -func (a *Artifact) Files() []string { - return a.f -} - -func (*Artifact) Id() string { - return "VM" -} - -func (a *Artifact) String() string { - return fmt.Sprintf("VM files in directory: %s", a.dir) -} - -func (a *Artifact) State(name string) interface{} { - return a.state[name] -} - -func (a *Artifact) Destroy() error { - return os.RemoveAll(a.dir) -} diff --git a/builder/qemu/builder.go b/builder/qemu/builder.go deleted file mode 100644 index c9e63a312..000000000 --- a/builder/qemu/builder.go +++ /dev/null @@ -1,229 +0,0 @@ -package qemu - -import ( - "context" - "errors" - "fmt" - "log" - "os" - "os/exec" - "path/filepath" - - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/packer-plugin-sdk/communicator" - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -const BuilderId = "transcend.qemu" - -type Builder struct { - config Config - runner multistep.Runner -} - -func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } - -func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { - warnings, errs := b.config.Prepare(raws...) - if errs != nil { - return nil, warnings, errs - } - - return nil, warnings, nil -} - -func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) { - // Create the driver that we'll use to communicate with Qemu - driver, err := b.newDriver(b.config.QemuBinary) - if err != nil { - return nil, fmt.Errorf("Failed creating Qemu driver: %s", err) - } - - steps := []multistep.Step{} - if !b.config.ISOSkipCache { - steps = append(steps, &commonsteps.StepDownload{ - Checksum: b.config.ISOChecksum, - Description: "ISO", - Extension: b.config.TargetExtension, - ResultKey: "iso_path", - TargetPath: b.config.TargetPath, - Url: b.config.ISOUrls, - }) - } else { - steps = append(steps, &stepSetISO{ - ResultKey: "iso_path", - Url: b.config.ISOUrls, - }) - } - - steps = append(steps, new(stepPrepareOutputDir), - &commonsteps.StepCreateFloppy{ - Files: b.config.FloppyConfig.FloppyFiles, - Directories: b.config.FloppyConfig.FloppyDirectories, - Label: b.config.FloppyConfig.FloppyLabel, - }, - &commonsteps.StepCreateCD{ - Files: b.config.CDConfig.CDFiles, - Label: b.config.CDConfig.CDLabel, - }, - &stepCreateDisk{ - AdditionalDiskSize: b.config.AdditionalDiskSize, - DiskImage: b.config.DiskImage, - DiskSize: b.config.DiskSize, - Format: b.config.Format, - OutputDir: b.config.OutputDir, - UseBackingFile: b.config.UseBackingFile, - VMName: b.config.VMName, - QemuImgArgs: b.config.QemuImgArgs, - }, - &stepCopyDisk{ - DiskImage: b.config.DiskImage, - Format: b.config.Format, - OutputDir: b.config.OutputDir, - UseBackingFile: b.config.UseBackingFile, - VMName: b.config.VMName, - }, - &stepResizeDisk{ - DiskCompression: b.config.DiskCompression, - DiskImage: b.config.DiskImage, - Format: b.config.Format, - OutputDir: b.config.OutputDir, - SkipResizeDisk: b.config.SkipResizeDisk, - VMName: b.config.VMName, - DiskSize: b.config.DiskSize, - QemuImgArgs: b.config.QemuImgArgs, - }, - new(stepHTTPIPDiscover), - commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), - &stepPortForward{ - CommunicatorType: b.config.CommConfig.Comm.Type, - NetBridge: b.config.NetBridge, - }, - new(stepConfigureVNC), - &stepRun{ - DiskImage: b.config.DiskImage, - }, - &stepConfigureQMP{ - QMPSocketPath: b.config.QMPSocketPath, - }, - &stepTypeBootCommand{}, - &stepWaitGuestAddress{ - CommunicatorType: b.config.CommConfig.Comm.Type, - NetBridge: b.config.NetBridge, - timeout: b.config.CommConfig.Comm.SSHTimeout, - }, - &communicator.StepConnect{ - Config: &b.config.CommConfig.Comm, - Host: commHost(b.config.CommConfig.Comm.Host()), - SSHConfig: b.config.CommConfig.Comm.SSHConfigFunc(), - SSHPort: commPort, - WinRMPort: commPort, - }, - new(commonsteps.StepProvision), - &commonsteps.StepCleanupTempKeys{ - Comm: &b.config.CommConfig.Comm, - }, - &stepShutdown{ - ShutdownTimeout: b.config.ShutdownTimeout, - ShutdownCommand: b.config.ShutdownCommand, - Comm: &b.config.CommConfig.Comm, - }, - &stepConvertDisk{ - DiskCompression: b.config.DiskCompression, - Format: b.config.Format, - OutputDir: b.config.OutputDir, - SkipCompaction: b.config.SkipCompaction, - VMName: b.config.VMName, - QemuImgArgs: b.config.QemuImgArgs, - }, - ) - - // Setup the state bag - state := new(multistep.BasicStateBag) - state.Put("config", &b.config) - state.Put("debug", b.config.PackerDebug) - state.Put("driver", driver) - state.Put("hook", hook) - state.Put("ui", ui) - - // Run - b.runner = commonsteps.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state) - b.runner.Run(ctx, state) - - // If there was an error, return that - if rawErr, ok := state.GetOk("error"); ok { - return nil, rawErr.(error) - } - - // If we were interrupted or cancelled, then just exit. - if _, ok := state.GetOk(multistep.StateCancelled); ok { - return nil, errors.New("Build was cancelled.") - } - - if _, ok := state.GetOk(multistep.StateHalted); ok { - return nil, errors.New("Build was halted.") - } - - // Compile the artifact list - 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(b.config.OutputDir, visit); err != nil { - return nil, err - } - - artifact := &Artifact{ - dir: b.config.OutputDir, - f: files, - state: make(map[string]interface{}), - } - - artifact.state["generated_data"] = state.Get("generated_data") - artifact.state["diskName"] = b.config.VMName - - // placed in state in step_create_disk.go - diskpaths, ok := state.Get("qemu_disk_paths").([]string) - if ok { - artifact.state["diskPaths"] = diskpaths - } - artifact.state["diskType"] = b.config.Format - artifact.state["diskSize"] = b.config.DiskSize - artifact.state["domainType"] = b.config.Accelerator - - return artifact, nil -} - -func (b *Builder) newDriver(qemuBinary string) (Driver, error) { - qemuPath, err := exec.LookPath(qemuBinary) - if err != nil { - return nil, err - } - - qemuImgPath, err := exec.LookPath("qemu-img") - if err != nil { - return nil, err - } - - log.Printf("Qemu path: %s, Qemu Image page: %s", qemuPath, qemuImgPath) - driver := &QemuDriver{ - QemuPath: qemuPath, - QemuImgPath: qemuImgPath, - } - - if err := driver.Verify(); err != nil { - return nil, err - } - - return driver, nil -} diff --git a/builder/qemu/builder_test.go b/builder/qemu/builder_test.go deleted file mode 100644 index 904f0c549..000000000 --- a/builder/qemu/builder_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package qemu - -import ( - "testing" - - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -func TestBuilder_ImplementsBuilder(t *testing.T) { - var raw interface{} - raw = &Builder{} - if _, ok := raw.(packersdk.Builder); !ok { - t.Error("Builder must implement builder.") - } -} diff --git a/builder/qemu/comm_config.go b/builder/qemu/comm_config.go deleted file mode 100644 index 99b945507..000000000 --- a/builder/qemu/comm_config.go +++ /dev/null @@ -1,73 +0,0 @@ -//go:generate packer-sdc struct-markdown -package qemu - -import ( - "errors" - - "github.com/hashicorp/packer-plugin-sdk/communicator" - "github.com/hashicorp/packer-plugin-sdk/template/interpolate" -) - -type CommConfig struct { - Comm communicator.Config `mapstructure:",squash"` - // The minimum port to use for the Communicator port on the host machine which is forwarded - // to the SSH or WinRM port on the guest machine. By default this is 2222. - HostPortMin int `mapstructure:"host_port_min" required:"false"` - // The maximum port to use for the Communicator port on the host machine which is forwarded - // to the SSH or WinRM port on the guest machine. Because Packer often runs in parallel, - // Packer will choose a randomly available port in this range to use as the - // host port. By default this is 4444. - HostPortMax int `mapstructure:"host_port_max" required:"false"` - // Defaults to false. When enabled, Packer - // does not setup forwarded port mapping for communicator (SSH or WinRM) requests and uses ssh_port or winrm_port - // on the host to communicate to the virtual machine. - SkipNatMapping bool `mapstructure:"skip_nat_mapping" required:"false"` - - // These are deprecated, but we keep them around for backwards compatibility - // TODO: remove later - SSHHostPortMin int `mapstructure:"ssh_host_port_min" required:"false"` - // TODO: remove later - SSHHostPortMax int `mapstructure:"ssh_host_port_max"` -} - -func (c *CommConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs []error) { - - // Backwards compatibility - if c.SSHHostPortMin != 0 { - warnings = append(warnings, "ssh_host_port_min is deprecated and is being replaced by host_port_min. "+ - "Please, update your template to use host_port_min. In future versions of Packer, inclusion of ssh_host_port_min will error your builds.") - c.HostPortMin = c.SSHHostPortMin - } - - // Backwards compatibility - if c.SSHHostPortMax != 0 { - warnings = append(warnings, "ssh_host_port_max is deprecated and is being replaced by host_port_max. "+ - "Please, update your template to use host_port_max. In future versions of Packer, inclusion of ssh_host_port_max will error your builds.") - c.HostPortMax = c.SSHHostPortMax - } - - if c.Comm.SSHHost == "" && c.SkipNatMapping { - c.Comm.SSHHost = "127.0.0.1" - c.Comm.WinRMHost = "127.0.0.1" - } - - if c.HostPortMin == 0 { - c.HostPortMin = 2222 - } - - if c.HostPortMax == 0 { - c.HostPortMax = 4444 - } - - errs = c.Comm.Prepare(ctx) - if c.HostPortMin > c.HostPortMax { - errs = append(errs, - errors.New("host_port_min must be less than host_port_max")) - } - - if c.HostPortMin < 0 { - errs = append(errs, errors.New("host_port_min must be positive")) - } - - return -} diff --git a/builder/qemu/comm_config_test.go b/builder/qemu/comm_config_test.go deleted file mode 100644 index a5854cf59..000000000 --- a/builder/qemu/comm_config_test.go +++ /dev/null @@ -1,145 +0,0 @@ -package qemu - -import ( - "io/ioutil" - "os" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/communicator" - "github.com/hashicorp/packer-plugin-sdk/template/interpolate" -) - -func testCommConfig() *CommConfig { - return &CommConfig{ - Comm: communicator.Config{ - SSH: communicator.SSH{ - SSHUsername: "foo", - }, - }, - } -} - -func TestCommConfigPrepare(t *testing.T) { - c := testCommConfig() - warns, errs := c.Prepare(interpolate.NewContext()) - if len(errs) > 0 { - t.Fatalf("err: %#v", errs) - } - if len(warns) != 0 { - t.Fatal("should not have any warnings") - } - - if c.HostPortMin != 2222 { - t.Errorf("bad min communicator host port: %d", c.HostPortMin) - } - - if c.HostPortMax != 4444 { - t.Errorf("bad max communicator host port: %d", c.HostPortMax) - } - - if c.Comm.SSHPort != 22 { - t.Errorf("bad communicator port: %d", c.Comm.SSHPort) - } -} - -func TestCommConfigPrepare_SSHHostPort(t *testing.T) { - var c *CommConfig - var errs []error - var warns []string - - // Bad - c = testCommConfig() - c.HostPortMin = 1000 - c.HostPortMax = 500 - warns, errs = c.Prepare(interpolate.NewContext()) - if len(errs) == 0 { - t.Fatalf("bad: %#v", errs) - } - if len(warns) != 0 { - t.Fatal("should not have any warnings") - } - - // Good - c = testCommConfig() - c.HostPortMin = 50 - c.HostPortMax = 500 - warns, errs = c.Prepare(interpolate.NewContext()) - if len(errs) > 0 { - t.Fatalf("should not have error: %s", errs) - } - if len(warns) != 0 { - t.Fatal("should not have any warnings") - } -} - -func TestCommConfigPrepare_SSHPrivateKey(t *testing.T) { - var c *CommConfig - var errs []error - var warns []string - - c = testCommConfig() - c.Comm.SSHPrivateKeyFile = "" - warns, errs = c.Prepare(interpolate.NewContext()) - if len(errs) > 0 { - t.Fatalf("should not have error: %#v", errs) - } - if len(warns) != 0 { - t.Fatal("should not have any warnings") - } - - c = testCommConfig() - c.Comm.SSHPrivateKeyFile = "/i/dont/exist" - warns, errs = c.Prepare(interpolate.NewContext()) - if len(errs) == 0 { - t.Fatal("should have error") - } - if len(warns) != 0 { - t.Fatal("should not have any warnings") - } - - // Test bad contents - tf, err := ioutil.TempFile("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - defer os.Remove(tf.Name()) - defer tf.Close() - - if _, err := tf.Write([]byte("HELLO!")); err != nil { - t.Fatalf("err: %s", err) - } - - c = testCommConfig() - c.Comm.SSHPrivateKeyFile = tf.Name() - warns, errs = c.Prepare(interpolate.NewContext()) - if len(errs) == 0 { - t.Fatal("should have error") - } - if len(warns) != 0 { - t.Fatal("should not have any warnings") - } - - // Test good contents - _, err = tf.Seek(0, 0) - if err != nil { - t.Fatalf("err: %s", err) - } - err = tf.Truncate(0) - if err != nil { - t.Fatalf("err: %s", err) - } - _, err = tf.Write([]byte(testPem)) - if err != nil { - t.Fatalf("err: %s", err) - } - - c = testCommConfig() - c.Comm.SSHPrivateKeyFile = tf.Name() - warns, errs = c.Prepare(interpolate.NewContext()) - if len(errs) > 0 { - t.Fatalf("should not have error: %#v", errs) - } - if len(warns) != 0 { - t.Fatal("should not have any warnings") - } -} diff --git a/builder/qemu/config.go b/builder/qemu/config.go deleted file mode 100644 index 4a8cf487e..000000000 --- a/builder/qemu/config.go +++ /dev/null @@ -1,642 +0,0 @@ -//go:generate packer-sdc struct-markdown -//go:generate packer-sdc mapstructure-to-hcl2 -type Config,QemuImgArgs - -package qemu - -import ( - "errors" - "fmt" - "log" - "os" - "path/filepath" - "regexp" - "runtime" - "strings" - - "github.com/hashicorp/packer-plugin-sdk/bootcommand" - "github.com/hashicorp/packer-plugin-sdk/common" - "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/shutdowncommand" - "github.com/hashicorp/packer-plugin-sdk/template/config" - "github.com/hashicorp/packer-plugin-sdk/template/interpolate" -) - -var accels = map[string]struct{}{ - "none": {}, - "kvm": {}, - "tcg": {}, - "xen": {}, - "hax": {}, - "hvf": {}, - "whpx": {}, -} - -var diskInterface = map[string]bool{ - "ide": true, - "scsi": true, - "virtio": true, - "virtio-scsi": true, -} - -var diskCache = map[string]bool{ - "writethrough": true, - "writeback": true, - "none": true, - "unsafe": true, - "directsync": true, -} - -var diskDiscard = map[string]bool{ - "unmap": true, - "ignore": true, -} - -var diskDZeroes = map[string]bool{ - "unmap": true, - "on": true, - "off": true, -} - -type QemuImgArgs struct { - Convert []string `mapstructure:"convert" required:"false"` - Create []string `mapstructure:"create" required:"false"` - Resize []string `mapstructure:"resize" required:"false"` -} - -type Config struct { - common.PackerConfig `mapstructure:",squash"` - commonsteps.HTTPConfig `mapstructure:",squash"` - commonsteps.ISOConfig `mapstructure:",squash"` - bootcommand.VNCConfig `mapstructure:",squash"` - shutdowncommand.ShutdownConfig `mapstructure:",squash"` - CommConfig CommConfig `mapstructure:",squash"` - commonsteps.FloppyConfig `mapstructure:",squash"` - commonsteps.CDConfig `mapstructure:",squash"` - // Use iso from provided url. Qemu must support - // curl block device. This defaults to `false`. - ISOSkipCache bool `mapstructure:"iso_skip_cache" required:"false"` - // The accelerator type to use when running the VM. - // This may be `none`, `kvm`, `tcg`, `hax`, `hvf`, `whpx`, or `xen`. The appropriate - // software must have already been installed on your build machine to use the - // accelerator you specified. When no accelerator is specified, Packer will try - // to use `kvm` if it is available but will default to `tcg` otherwise. - // - // ~> The `hax` accelerator has issues attaching CDROM ISOs. This is an - // upstream issue which can be tracked - // [here](https://github.com/intel/haxm/issues/20). - // - // ~> The `hvf` and `whpx` accelerator are new and experimental as of - // [QEMU 2.12.0](https://wiki.qemu.org/ChangeLog/2.12#Host_support). - // You may encounter issues unrelated to Packer when using these. You may need to - // add [ "-global", "virtio-pci.disable-modern=on" ] to `qemuargs` depending on the - // guest operating system. - // - // ~> For `whpx`, note that [Stefan Weil's QEMU for Windows distribution](https://qemu.weilnetz.de/w64/) - // does not include WHPX support and users may need to compile or source a - // build of QEMU for Windows themselves with WHPX support. - Accelerator string `mapstructure:"accelerator" required:"false"` - // Additional disks to create. Uses `vm_name` as the disk name template and - // appends `-#` where `#` is the position in the array. `#` starts at 1 since 0 - // is the default disk. Each string represents the disk image size in bytes. - // Optional suffixes 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' - // (gigabyte, 1024M), 'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' - // (exabyte, 1024P) are supported. 'b' is ignored. Per qemu-img documentation. - // Each additional disk uses the same disk parameters as the default disk. - // Unset by default. - AdditionalDiskSize []string `mapstructure:"disk_additional_size" required:"false"` - // The number of cpus to use when building the VM. - // The default is `1` CPU. - CpuCount int `mapstructure:"cpus" required:"false"` - // The firmware file to be used by QEMU, which is to be set by the -bios - // option of QEMU. Particularly, this option can be set to use EFI instead - // of BIOS, by using "OVMF.fd" from OpenFirmware. - // If unset, no -bios option is passed to QEMU, using the default of QEMU. - // Also see the QEMU documentation. - Firmware string `mapstructure:"firmware" required:"false"` - // The interface to use for the disk. Allowed values include any of `ide`, - // `scsi`, `virtio` or `virtio-scsi`^\*. Note also that any boot commands - // or kickstart type scripts must have proper adjustments for resulting - // device names. The Qemu builder uses `virtio` by default. - // - // ^\* Please be aware that use of the `scsi` disk interface has been - // disabled by Red Hat due to a bug described - // [here](https://bugzilla.redhat.com/show_bug.cgi?id=1019220). If you are - // running Qemu on RHEL or a RHEL variant such as CentOS, you *must* choose - // one of the other listed interfaces. Using the `scsi` interface under - // these circumstances will cause the build to fail. - DiskInterface string `mapstructure:"disk_interface" required:"false"` - // The size in bytes of the hard disk of the VM. Suffix with the first - // letter of common byte types. Use "k" or "K" for kilobytes, "M" for - // megabytes, G for gigabytes, and T for terabytes. If no value is provided - // for disk_size, Packer uses a default of `40960M` (40 GB). If a disk_size - // number is provided with no units, Packer will default to Megabytes. - DiskSize string `mapstructure:"disk_size" required:"false"` - // Packer resizes the QCOW2 image using - // qemu-img resize. Set this option to true to disable resizing. - // Defaults to false. - SkipResizeDisk bool `mapstructure:"skip_resize_disk" required:"false"` - // The cache mode to use for disk. Allowed values include any of - // `writethrough`, `writeback`, `none`, `unsafe` or `directsync`. By - // default, this is set to `writeback`. - DiskCache string `mapstructure:"disk_cache" required:"false"` - // The discard mode to use for disk. Allowed values - // include any of unmap or ignore. By default, this is set to ignore. - DiskDiscard string `mapstructure:"disk_discard" required:"false"` - // The detect-zeroes mode to use for disk. - // Allowed values include any of unmap, on or off. Defaults to off. - // When the value is "off" we don't set the flag in the qemu command, so that - // Packer still works with old versions of QEMU that don't have this option. - DetectZeroes string `mapstructure:"disk_detect_zeroes" required:"false"` - // Packer compacts the QCOW2 image using - // qemu-img convert. Set this option to true to disable compacting. - // Defaults to false. - SkipCompaction bool `mapstructure:"skip_compaction" required:"false"` - // Apply compression to the QCOW2 disk file - // using qemu-img convert. Defaults to false. - DiskCompression bool `mapstructure:"disk_compression" required:"false"` - // Either `qcow2` or `raw`, this specifies the output format of the virtual - // machine image. This defaults to `qcow2`. Due to a long-standing bug with - // `qemu-img convert` on OSX, sometimes the qemu-img convert call will - // create a corrupted image. If this is an issue for you, make sure that the - // the output format matches the input file's format, and Packer will - // perform a simple copy operation instead. See - // https://bugs.launchpad.net/qemu/+bug/1776920 for more details. - Format string `mapstructure:"format" required:"false"` - // Packer defaults to building QEMU virtual machines by - // launching a GUI that shows the console of the machine being built. When this - // value is set to `true`, the machine will start without a console. - // - // You can still see the console if you make a note of the VNC display - // number chosen, and then connect using `vncviewer -Shared :` - Headless bool `mapstructure:"headless" required:"false"` - // Packer defaults to building from an ISO file, this parameter controls - // whether the ISO URL supplied is actually a bootable QEMU image. When - // this value is set to `true`, the machine will either clone the source or - // use it as a backing file (if `use_backing_file` is `true`); then, it - // will resize the image according to `disk_size` and boot it. - DiskImage bool `mapstructure:"disk_image" required:"false"` - // Only applicable when disk_image is true - // and format is qcow2, set this option to true to create a new QCOW2 - // file that uses the file located at iso_url as a backing file. The new file - // will only contain blocks that have changed compared to the backing file, so - // enabling this option can significantly reduce disk usage. If true, Packer - // will force the `skip_compaction` also to be true as well to skip disk - // conversion which would render the backing file feature useless. - UseBackingFile bool `mapstructure:"use_backing_file" required:"false"` - // The type of machine emulation to use. Run your qemu binary with the - // flags `-machine help` to list available types for your system. This - // defaults to `pc`. - MachineType string `mapstructure:"machine_type" required:"false"` - // The amount of memory to use when building the VM - // in megabytes. This defaults to 512 megabytes. - MemorySize int `mapstructure:"memory" required:"false"` - // The driver to use for the network interface. Allowed values `ne2k_pci`, - // `i82551`, `i82557b`, `i82559er`, `rtl8139`, `e1000`, `pcnet`, `virtio`, - // `virtio-net`, `virtio-net-pci`, `usb-net`, `i82559a`, `i82559b`, - // `i82559c`, `i82550`, `i82562`, `i82557a`, `i82557c`, `i82801`, - // `vmxnet3`, `i82558a` or `i82558b`. The Qemu builder uses `virtio-net` by - // default. - NetDevice string `mapstructure:"net_device" required:"false"` - // Connects the network to this bridge instead of using the user mode - // networking. - // - // **NB** This bridge must already exist. You can use the `virbr0` bridge - // as created by vagrant-libvirt. - // - // **NB** This will automatically enable the QMP socket (see QMPEnable). - // - // **NB** This only works in Linux based OSes. - NetBridge string `mapstructure:"net_bridge" required:"false"` - // 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 - // 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. - OutputDir string `mapstructure:"output_directory" required:"false"` - // Allows complete control over the qemu command line (though not qemu-img). - // Each array of strings makes up a command line switch - // that overrides matching default switch/value pairs. Any value specified - // as an empty string is ignored. All values after the switch are - // concatenated with no separator. - // - // ~> **Warning:** The qemu command line allows extreme flexibility, so - // beware of conflicting arguments causing failures of your run. - // For instance adding a "--drive" or "--device" override will mean that - // none of the default configuration Packer sets will be used. To see the - // defaults that Packer sets, look in your packer.log - // file (set PACKER_LOG=1 to get verbose logging) and search for the - // qemu-system-x86 command. The arguments are all printed for review, and - // you can use those arguments along with the template engines allowed - // by qemu-args to set up a working configuration that includes both the - // Packer defaults and your extra arguments. - // - // Another pitfall could be setting arguments like --no-acpi, which could - // break the ability to send power signal type commands - // (e.g., shutdown -P now) to the virtual machine, thus preventing proper - // shutdown. - // - // The following shows a sample usage: - // - // In JSON: - // ```json - // "qemuargs": [ - // [ "-m", "1024M" ], - // [ "--no-acpi", "" ], - // [ - // "-netdev", - // "user,id=mynet0,", - // "hostfwd=hostip:hostport-guestip:guestport", - // "" - // ], - // [ "-device", "virtio-net,netdev=mynet0" ] - // ] - // ``` - // - // In HCL2: - // ```hcl - // qemuargs = [ - // [ "-m", "1024M" ], - // [ "--no-acpi", "" ], - // [ - // "-netdev", - // "user,id=mynet0,", - // "hostfwd=hostip:hostport-guestip:guestport", - // "" - // ], - // [ "-device", "virtio-net,netdev=mynet0" ] - // ] - // ``` - // - // would produce the following (not including other defaults supplied by - // the builder and not otherwise conflicting with the qemuargs): - // - // ```text - // qemu-system-x86 -m 1024m --no-acpi -netdev - // user,id=mynet0,hostfwd=hostip:hostport-guestip:guestport -device - // virtio-net,netdev=mynet0" - // ``` - // - // ~> **Windows Users:** [QEMU for Windows](https://qemu.weilnetz.de/) - // builds are available though an environmental variable does need to be - // set for QEMU for Windows to redirect stdout to the console instead of - // stdout.txt. - // - // The following shows the environment variable that needs to be set for - // Windows QEMU support: - // - // ```text - // setx SDL_STDIO_REDIRECT=0 - // ``` - // - // You can also use the `SSHHostPort` template variable to produce a packer - // template that can be invoked by `make` in parallel: - // - // In JSON: - // ```json - // "qemuargs": [ - // [ "-netdev", "user,hostfwd=tcp::{{ .SSHHostPort }}-:22,id=forward"], - // [ "-device", "virtio-net,netdev=forward,id=net0"] - // ] - // ``` - // - // In HCL2: - // ```hcl - // qemuargs = [ - // [ "-netdev", "user,hostfwd=tcp::{{ .SSHHostPort }}-:22,id=forward"], - // [ "-device", "virtio-net,netdev=forward,id=net0"] - // ] - // - // `make -j 3 my-awesome-packer-templates` spawns 3 packer processes, each - // of which will bind to their own SSH port as determined by each process. - // This will also work with WinRM, just change the port forward in - // `qemuargs` to map to WinRM's default port of `5985` or whatever value - // you have the service set to listen on. - // - // This is a template engine and allows access to the following variables: - // `{{ .HTTPIP }}`, `{{ .HTTPPort }}`, `{{ .HTTPDir }}`, - // `{{ .OutputDir }}`, `{{ .Name }}`, and `{{ .SSHHostPort }}` - QemuArgs [][]string `mapstructure:"qemuargs" required:"false"` - // A map of custom arguments to pass to qemu-img commands, where the key - // is the subcommand, and the values are lists of strings for each flag. - // Example: - // - // In JSON: - // ```json - // { - // "qemu_img_args": { - // "convert": ["-o", "preallocation=full"], - // "resize": ["-foo", "bar"] - // } - // ``` - // Please note - // that unlike qemuargs, these commands are not split into switch-value - // sub-arrays, because the basic elements in qemu-img calls are unlikely - // to need an actual override. - // The arguments will be constructed as follows: - // - Convert: - // Default is `qemu-img convert -O $format $sourcepath $targetpath`. Adding - // arguments ["-foo", "bar"] to qemu_img_args.convert will change this to - // `qemu-img convert -foo bar -O $format $sourcepath $targetpath` - // - Create: - // Default is `create -f $format $targetpath $size`. Adding arguments - // ["-foo", "bar"] to qemu_img_args.create will change this to - // "create -f qcow2 -foo bar target.qcow2 1234M" - // - Resize: - // Default is `qemu-img resize -f $format $sourcepath $size`. Adding - // arguments ["-foo", "bar"] to qemu_img_args.resize will change this to - // `qemu-img resize -f $format -foo bar $sourcepath $size` - QemuImgArgs QemuImgArgs `mapstructure:"qemu_img_args" required:"false"` - // The name of the Qemu binary to look for. This - // defaults to qemu-system-x86_64, but may need to be changed for - // some platforms. For example qemu-kvm, or qemu-system-i386 may be a - // better choice for some systems. - QemuBinary string `mapstructure:"qemu_binary" required:"false"` - // Enable QMP socket. Location is specified by `qmp_socket_path`. Defaults - // to false. - QMPEnable bool `mapstructure:"qmp_enable" required:"false"` - // QMP Socket Path when `qmp_enable` is true. Defaults to - // `output_directory`/`vm_name`.monitor. - QMPSocketPath string `mapstructure:"qmp_socket_path" required:"false"` - // If true, do not pass a -display option - // to qemu, allowing it to choose the default. This may be needed when running - // under macOS, and getting errors about sdl not being available. - UseDefaultDisplay bool `mapstructure:"use_default_display" required:"false"` - // What QEMU -display option to use. Defaults to gtk, use none to not pass the - // -display option allowing QEMU to choose the default. This may be needed when - // running under macOS, and getting errors about sdl not being available. - Display string `mapstructure:"display" required:"false"` - // The IP address that should be - // binded to for VNC. By default packer will use 127.0.0.1 for this. If you - // wish to bind to all interfaces use 0.0.0.0. - VNCBindAddress string `mapstructure:"vnc_bind_address" required:"false"` - // Whether or not to set a password on the VNC server. This option - // automatically enables the QMP socket. See `qmp_socket_path`. Defaults to - // `false`. - VNCUsePassword bool `mapstructure:"vnc_use_password" required:"false"` - // The minimum and maximum port - // to use for VNC access to the virtual machine. The builder uses VNC to type - // the initial boot_command. Because Packer generally runs in parallel, - // Packer uses a randomly chosen port in this range that appears available. By - // default this is 5900 to 6000. The minimum and maximum ports are inclusive. - // The minimum port cannot be set below 5900 due to a quirk in how QEMU parses - // vnc display address. - VNCPortMin int `mapstructure:"vnc_port_min" required:"false"` - VNCPortMax int `mapstructure:"vnc_port_max"` - // This is the name of the image (QCOW2 or IMG) file for - // the new virtual machine. By default this is packer-BUILDNAME, where - // "BUILDNAME" is the name of the build. Currently, no file extension will be - // used unless it is specified in this option. - VMName string `mapstructure:"vm_name" required:"false"` - // The interface to use for the CDROM device which contains the ISO image. - // Allowed values include any of `ide`, `scsi`, `virtio` or - // `virtio-scsi`. The Qemu builder uses `virtio` by default. - // Some ARM64 images require `virtio-scsi`. - CDROMInterface string `mapstructure:"cdrom_interface" required:"false"` - - // TODO(mitchellh): deprecate - RunOnce bool `mapstructure:"run_once"` - - ctx interpolate.Context -} - -func (c *Config) Prepare(raws ...interface{}) ([]string, error) { - err := config.Decode(c, &config.DecodeOpts{ - PluginType: BuilderId, - Interpolate: true, - InterpolateContext: &c.ctx, - InterpolateFilter: &interpolate.RenderFilter{ - Exclude: []string{ - "boot_command", - "qemuargs", - }, - }, - }, raws...) - if err != nil { - return nil, err - } - - // Accumulate any errors and warnings - var errs *packersdk.MultiError - warnings := make([]string, 0) - - errs = packersdk.MultiErrorAppend(errs, c.ShutdownConfig.Prepare(&c.ctx)...) - - if c.DiskSize == "" || c.DiskSize == "0" { - c.DiskSize = "40960M" - } else { - // Make sure supplied disk size is valid - // (digits, plus an optional valid unit character). e.g. 5000, 40G, 1t - re := regexp.MustCompile(`^[\d]+(b|k|m|g|t){0,1}$`) - matched := re.MatchString(strings.ToLower(c.DiskSize)) - if !matched { - errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Invalid disk size.")) - } else { - // Okay, it's valid -- if it doesn't alreay have a suffix, then - // append "M" as the default unit. - re = regexp.MustCompile(`^[\d]+$`) - matched = re.MatchString(strings.ToLower(c.DiskSize)) - if matched { - // Needs M added. - c.DiskSize = fmt.Sprintf("%sM", c.DiskSize) - } - } - } - - if c.DiskCache == "" { - c.DiskCache = "writeback" - } - - if c.DiskDiscard == "" { - c.DiskDiscard = "ignore" - } - - if c.DetectZeroes == "" { - c.DetectZeroes = "off" - } - - if c.Accelerator == "" { - if runtime.GOOS == "windows" { - c.Accelerator = "tcg" - } else { - // /dev/kvm is a kernel module that may be loaded if kvm is - // installed and the host supports VT-x extensions. To make sure - // this will actually work we need to os.Open() it. If os.Open fails - // the kernel module was not installed or loaded correctly. - if fp, err := os.Open("/dev/kvm"); err != nil { - c.Accelerator = "tcg" - } else { - fp.Close() - c.Accelerator = "kvm" - } - } - log.Printf("use detected accelerator: %s", c.Accelerator) - } else { - log.Printf("use specified accelerator: %s", c.Accelerator) - } - - if c.MachineType == "" { - c.MachineType = "pc" - } - - if c.OutputDir == "" { - c.OutputDir = fmt.Sprintf("output-%s", c.PackerBuildName) - } - - if c.QemuBinary == "" { - c.QemuBinary = "qemu-system-x86_64" - } - - if c.MemorySize < 10 { - log.Printf("MemorySize %d is too small, using default: 512", c.MemorySize) - c.MemorySize = 512 - } - - if c.CpuCount < 1 { - log.Printf("CpuCount %d too small, using default: 1", c.CpuCount) - c.CpuCount = 1 - } - - if c.VNCBindAddress == "" { - c.VNCBindAddress = "127.0.0.1" - } - - if c.VNCPortMin == 0 { - c.VNCPortMin = 5900 - } - - if c.VNCPortMax == 0 { - c.VNCPortMax = 6000 - } - - if c.VMName == "" { - c.VMName = fmt.Sprintf("packer-%s", c.PackerBuildName) - } - - if c.Format == "" { - c.Format = "qcow2" - } - - errs = packersdk.MultiErrorAppend(errs, c.FloppyConfig.Prepare(&c.ctx)...) - errs = packersdk.MultiErrorAppend(errs, c.CDConfig.Prepare(&c.ctx)...) - errs = packersdk.MultiErrorAppend(errs, c.VNCConfig.Prepare(&c.ctx)...) - - if c.NetDevice == "" { - c.NetDevice = "virtio-net" - } - - if c.DiskInterface == "" { - c.DiskInterface = "virtio" - } - - if c.ISOSkipCache { - c.ISOChecksum = "none" - } - isoWarnings, isoErrs := c.ISOConfig.Prepare(&c.ctx) - warnings = append(warnings, isoWarnings...) - errs = packersdk.MultiErrorAppend(errs, isoErrs...) - - errs = packersdk.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...) - commConfigWarnings, es := c.CommConfig.Prepare(&c.ctx) - if len(es) > 0 { - errs = packersdk.MultiErrorAppend(errs, es...) - } - warnings = append(warnings, commConfigWarnings...) - - if !(c.Format == "qcow2" || c.Format == "raw") { - errs = packersdk.MultiErrorAppend( - errs, errors.New("invalid format, only 'qcow2' or 'raw' are allowed")) - } - - if c.Format != "qcow2" { - c.SkipCompaction = true - c.DiskCompression = false - } - - if c.UseBackingFile { - c.SkipCompaction = true - if !(c.DiskImage && c.Format == "qcow2") { - errs = packersdk.MultiErrorAppend( - errs, errors.New("use_backing_file can only be enabled for QCOW2 images and when disk_image is true")) - } - } - - if c.SkipResizeDisk && !(c.DiskImage) { - errs = packersdk.MultiErrorAppend( - errs, errors.New("skip_resize_disk can only be used when disk_image is true")) - } - - if _, ok := accels[c.Accelerator]; !ok { - errs = packersdk.MultiErrorAppend( - errs, errors.New("invalid accelerator, only 'kvm', 'tcg', 'xen', 'hax', 'hvf', 'whpx', or 'none' are allowed")) - } - - if _, ok := diskInterface[c.DiskInterface]; !ok { - errs = packersdk.MultiErrorAppend( - errs, errors.New("unrecognized disk interface type")) - } - - if _, ok := diskCache[c.DiskCache]; !ok { - errs = packersdk.MultiErrorAppend( - errs, errors.New("unrecognized disk cache type")) - } - - if _, ok := diskDiscard[c.DiskDiscard]; !ok { - errs = packersdk.MultiErrorAppend( - errs, errors.New("unrecognized disk discard type")) - } - - if _, ok := diskDZeroes[c.DetectZeroes]; !ok { - errs = packersdk.MultiErrorAppend( - errs, errors.New("unrecognized disk detect zeroes setting")) - } - - if !c.PackerForce { - if _, err := os.Stat(c.OutputDir); err == nil { - errs = packersdk.MultiErrorAppend( - errs, - fmt.Errorf("Output directory '%s' already exists. It must not exist.", c.OutputDir)) - } - } - - if c.VNCPortMin < 5900 { - errs = packersdk.MultiErrorAppend( - errs, fmt.Errorf("vnc_port_min cannot be below 5900")) - } - - if c.VNCPortMin > 65535 || c.VNCPortMax > 65535 { - errs = packersdk.MultiErrorAppend( - errs, fmt.Errorf("vmc_port_min and vnc_port_max must both be below 65535 to be valid TCP ports")) - } - - if c.VNCPortMin > c.VNCPortMax { - errs = packersdk.MultiErrorAppend( - errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max")) - } - - if c.NetBridge != "" && runtime.GOOS != "linux" { - errs = packersdk.MultiErrorAppend( - errs, fmt.Errorf("net_bridge is only supported in Linux based OSes")) - } - - if c.NetBridge != "" || c.VNCUsePassword { - c.QMPEnable = true - } - - if c.QMPEnable && c.QMPSocketPath == "" { - socketName := fmt.Sprintf("%s.monitor", c.VMName) - c.QMPSocketPath = filepath.Join(c.OutputDir, socketName) - } - - if c.QemuArgs == nil { - c.QemuArgs = make([][]string, 0) - } - - if errs != nil && len(errs.Errors) > 0 { - return warnings, errs - } - - return warnings, nil - -} diff --git a/builder/qemu/config.hcl2spec.go b/builder/qemu/config.hcl2spec.go deleted file mode 100644 index 899b95885..000000000 --- a/builder/qemu/config.hcl2spec.go +++ /dev/null @@ -1,298 +0,0 @@ -// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. - -package qemu - -import ( - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" -) - -// FlatConfig is an auto-generated flat version of Config. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatConfig struct { - PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` - PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` - PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` - PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` - PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` - PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` - PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` - PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` - HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` - HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"` - HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` - HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` - HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` - HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"` - ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"` - RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"` - ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"` - TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"` - TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"` - 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"` - DisableVNC *bool `mapstructure:"disable_vnc" cty:"disable_vnc" hcl:"disable_vnc"` - BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"` - ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command" hcl:"shutdown_command"` - ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"` - Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` - PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` - SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` - SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"` - SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"` - SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"` - SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"` - SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` - SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"` - SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"` - SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"` - SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"` - SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"` - SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` - SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"` - SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"` - SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"` - SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"` - SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"` - SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"` - SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"` - SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"` - SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"` - SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"` - SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"` - SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"` - SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"` - SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"` - SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"` - SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"` - SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"` - SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"` - SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"` - SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"` - SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"` - SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"` - SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"` - SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"` - SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"` - SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"` - WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"` - WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` - WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"` - WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"` - WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"` - WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"` - WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` - WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` - WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` - HostPortMin *int `mapstructure:"host_port_min" required:"false" cty:"host_port_min" hcl:"host_port_min"` - HostPortMax *int `mapstructure:"host_port_max" required:"false" cty:"host_port_max" hcl:"host_port_max"` - SkipNatMapping *bool `mapstructure:"skip_nat_mapping" required:"false" cty:"skip_nat_mapping" hcl:"skip_nat_mapping"` - SSHHostPortMin *int `mapstructure:"ssh_host_port_min" required:"false" cty:"ssh_host_port_min" hcl:"ssh_host_port_min"` - SSHHostPortMax *int `mapstructure:"ssh_host_port_max" cty:"ssh_host_port_max" hcl:"ssh_host_port_max"` - FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"` - FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"` - FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"` - CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"` - CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"` - ISOSkipCache *bool `mapstructure:"iso_skip_cache" required:"false" cty:"iso_skip_cache" hcl:"iso_skip_cache"` - Accelerator *string `mapstructure:"accelerator" required:"false" cty:"accelerator" hcl:"accelerator"` - AdditionalDiskSize []string `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"` - CpuCount *int `mapstructure:"cpus" required:"false" cty:"cpus" hcl:"cpus"` - Firmware *string `mapstructure:"firmware" required:"false" cty:"firmware" hcl:"firmware"` - DiskInterface *string `mapstructure:"disk_interface" required:"false" cty:"disk_interface" hcl:"disk_interface"` - DiskSize *string `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"` - SkipResizeDisk *bool `mapstructure:"skip_resize_disk" required:"false" cty:"skip_resize_disk" hcl:"skip_resize_disk"` - DiskCache *string `mapstructure:"disk_cache" required:"false" cty:"disk_cache" hcl:"disk_cache"` - DiskDiscard *string `mapstructure:"disk_discard" required:"false" cty:"disk_discard" hcl:"disk_discard"` - DetectZeroes *string `mapstructure:"disk_detect_zeroes" required:"false" cty:"disk_detect_zeroes" hcl:"disk_detect_zeroes"` - SkipCompaction *bool `mapstructure:"skip_compaction" required:"false" cty:"skip_compaction" hcl:"skip_compaction"` - DiskCompression *bool `mapstructure:"disk_compression" required:"false" cty:"disk_compression" hcl:"disk_compression"` - Format *string `mapstructure:"format" required:"false" cty:"format" hcl:"format"` - Headless *bool `mapstructure:"headless" required:"false" cty:"headless" hcl:"headless"` - DiskImage *bool `mapstructure:"disk_image" required:"false" cty:"disk_image" hcl:"disk_image"` - UseBackingFile *bool `mapstructure:"use_backing_file" required:"false" cty:"use_backing_file" hcl:"use_backing_file"` - MachineType *string `mapstructure:"machine_type" required:"false" cty:"machine_type" hcl:"machine_type"` - MemorySize *int `mapstructure:"memory" required:"false" cty:"memory" hcl:"memory"` - NetDevice *string `mapstructure:"net_device" required:"false" cty:"net_device" hcl:"net_device"` - NetBridge *string `mapstructure:"net_bridge" required:"false" cty:"net_bridge" hcl:"net_bridge"` - OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"` - QemuArgs [][]string `mapstructure:"qemuargs" required:"false" cty:"qemuargs" hcl:"qemuargs"` - QemuImgArgs *FlatQemuImgArgs `mapstructure:"qemu_img_args" required:"false" cty:"qemu_img_args" hcl:"qemu_img_args"` - QemuBinary *string `mapstructure:"qemu_binary" required:"false" cty:"qemu_binary" hcl:"qemu_binary"` - QMPEnable *bool `mapstructure:"qmp_enable" required:"false" cty:"qmp_enable" hcl:"qmp_enable"` - QMPSocketPath *string `mapstructure:"qmp_socket_path" required:"false" cty:"qmp_socket_path" hcl:"qmp_socket_path"` - UseDefaultDisplay *bool `mapstructure:"use_default_display" required:"false" cty:"use_default_display" hcl:"use_default_display"` - Display *string `mapstructure:"display" required:"false" cty:"display" hcl:"display"` - VNCBindAddress *string `mapstructure:"vnc_bind_address" required:"false" cty:"vnc_bind_address" hcl:"vnc_bind_address"` - VNCUsePassword *bool `mapstructure:"vnc_use_password" required:"false" cty:"vnc_use_password" hcl:"vnc_use_password"` - VNCPortMin *int `mapstructure:"vnc_port_min" required:"false" cty:"vnc_port_min" hcl:"vnc_port_min"` - VNCPortMax *int `mapstructure:"vnc_port_max" cty:"vnc_port_max" hcl:"vnc_port_max"` - VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"` - CDROMInterface *string `mapstructure:"cdrom_interface" required:"false" cty:"cdrom_interface" hcl:"cdrom_interface"` - RunOnce *bool `mapstructure:"run_once" cty:"run_once" hcl:"run_once"` -} - -// FlatMapstructure returns a new FlatConfig. -// FlatConfig is an auto-generated flat version of Config. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatConfig) -} - -// HCL2Spec returns the hcl spec of a Config. -// This spec is used by HCL to read the fields of Config. -// The decoded values from this spec will then be applied to a FlatConfig. -func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, - "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, - "packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false}, - "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, - "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, - "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, - "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, - "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, - "http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false}, - "http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false}, - "http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false}, - "http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false}, - "http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false}, - "http_interface": &hcldec.AttrSpec{Name: "http_interface", Type: cty.String, Required: false}, - "iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false}, - "iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false}, - "iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false}, - "iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false}, - "iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", 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}, - "disable_vnc": &hcldec.AttrSpec{Name: "disable_vnc", Type: cty.Bool, Required: false}, - "boot_key_interval": &hcldec.AttrSpec{Name: "boot_key_interval", Type: cty.String, Required: false}, - "shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false}, - "shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false}, - "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, - "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, - "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, - "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, - "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, - "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, - "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, - "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, - "temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false}, - "temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false}, - "ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false}, - "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, - "ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false}, - "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, - "ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false}, - "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, - "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, - "ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false}, - "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, - "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, - "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, - "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, - "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, - "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, - "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, - "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, - "ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false}, - "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, - "ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false}, - "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, - "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, - "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, - "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, - "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, - "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, - "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, - "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, - "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, - "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, - "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, - "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, - "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, - "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, - "winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false}, - "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, - "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, - "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, - "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, - "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, - "host_port_min": &hcldec.AttrSpec{Name: "host_port_min", Type: cty.Number, Required: false}, - "host_port_max": &hcldec.AttrSpec{Name: "host_port_max", Type: cty.Number, Required: false}, - "skip_nat_mapping": &hcldec.AttrSpec{Name: "skip_nat_mapping", Type: cty.Bool, Required: false}, - "ssh_host_port_min": &hcldec.AttrSpec{Name: "ssh_host_port_min", Type: cty.Number, Required: false}, - "ssh_host_port_max": &hcldec.AttrSpec{Name: "ssh_host_port_max", Type: cty.Number, Required: false}, - "floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false}, - "floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(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_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false}, - "iso_skip_cache": &hcldec.AttrSpec{Name: "iso_skip_cache", Type: cty.Bool, Required: false}, - "accelerator": &hcldec.AttrSpec{Name: "accelerator", Type: cty.String, Required: false}, - "disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.String), Required: false}, - "cpus": &hcldec.AttrSpec{Name: "cpus", Type: cty.Number, Required: false}, - "firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false}, - "disk_interface": &hcldec.AttrSpec{Name: "disk_interface", Type: cty.String, Required: false}, - "disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.String, Required: false}, - "skip_resize_disk": &hcldec.AttrSpec{Name: "skip_resize_disk", Type: cty.Bool, Required: false}, - "disk_cache": &hcldec.AttrSpec{Name: "disk_cache", Type: cty.String, Required: false}, - "disk_discard": &hcldec.AttrSpec{Name: "disk_discard", Type: cty.String, Required: false}, - "disk_detect_zeroes": &hcldec.AttrSpec{Name: "disk_detect_zeroes", Type: cty.String, Required: false}, - "skip_compaction": &hcldec.AttrSpec{Name: "skip_compaction", Type: cty.Bool, Required: false}, - "disk_compression": &hcldec.AttrSpec{Name: "disk_compression", Type: cty.Bool, Required: false}, - "format": &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false}, - "headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false}, - "disk_image": &hcldec.AttrSpec{Name: "disk_image", Type: cty.Bool, Required: false}, - "use_backing_file": &hcldec.AttrSpec{Name: "use_backing_file", Type: cty.Bool, Required: false}, - "machine_type": &hcldec.AttrSpec{Name: "machine_type", Type: cty.String, Required: false}, - "memory": &hcldec.AttrSpec{Name: "memory", Type: cty.Number, Required: false}, - "net_device": &hcldec.AttrSpec{Name: "net_device", Type: cty.String, Required: false}, - "net_bridge": &hcldec.AttrSpec{Name: "net_bridge", Type: cty.String, Required: false}, - "output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false}, - "qemuargs": &hcldec.AttrSpec{Name: "qemuargs", Type: cty.List(cty.List(cty.String)), Required: false}, - "qemu_img_args": &hcldec.BlockSpec{TypeName: "qemu_img_args", Nested: hcldec.ObjectSpec((*FlatQemuImgArgs)(nil).HCL2Spec())}, - "qemu_binary": &hcldec.AttrSpec{Name: "qemu_binary", Type: cty.String, Required: false}, - "qmp_enable": &hcldec.AttrSpec{Name: "qmp_enable", Type: cty.Bool, Required: false}, - "qmp_socket_path": &hcldec.AttrSpec{Name: "qmp_socket_path", Type: cty.String, Required: false}, - "use_default_display": &hcldec.AttrSpec{Name: "use_default_display", Type: cty.Bool, Required: false}, - "display": &hcldec.AttrSpec{Name: "display", Type: cty.String, Required: false}, - "vnc_bind_address": &hcldec.AttrSpec{Name: "vnc_bind_address", Type: cty.String, Required: false}, - "vnc_use_password": &hcldec.AttrSpec{Name: "vnc_use_password", Type: cty.Bool, Required: false}, - "vnc_port_min": &hcldec.AttrSpec{Name: "vnc_port_min", Type: cty.Number, Required: false}, - "vnc_port_max": &hcldec.AttrSpec{Name: "vnc_port_max", Type: cty.Number, Required: false}, - "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, - "cdrom_interface": &hcldec.AttrSpec{Name: "cdrom_interface", Type: cty.String, Required: false}, - "run_once": &hcldec.AttrSpec{Name: "run_once", Type: cty.Bool, Required: false}, - } - return s -} - -// FlatQemuImgArgs is an auto-generated flat version of QemuImgArgs. -// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. -type FlatQemuImgArgs struct { - Convert []string `mapstructure:"convert" required:"false" cty:"convert" hcl:"convert"` - Create []string `mapstructure:"create" required:"false" cty:"create" hcl:"create"` - Resize []string `mapstructure:"resize" required:"false" cty:"resize" hcl:"resize"` -} - -// FlatMapstructure returns a new FlatQemuImgArgs. -// FlatQemuImgArgs is an auto-generated flat version of QemuImgArgs. -// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*QemuImgArgs) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { - return new(FlatQemuImgArgs) -} - -// HCL2Spec returns the hcl spec of a QemuImgArgs. -// This spec is used by HCL to read the fields of QemuImgArgs. -// The decoded values from this spec will then be applied to a FlatQemuImgArgs. -func (*FlatQemuImgArgs) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - "convert": &hcldec.AttrSpec{Name: "convert", Type: cty.List(cty.String), Required: false}, - "create": &hcldec.AttrSpec{Name: "create", Type: cty.List(cty.String), Required: false}, - "resize": &hcldec.AttrSpec{Name: "resize", Type: cty.List(cty.String), Required: false}, - } - return s -} diff --git a/builder/qemu/config_test.go b/builder/qemu/config_test.go deleted file mode 100644 index 1f9969e10..000000000 --- a/builder/qemu/config_test.go +++ /dev/null @@ -1,695 +0,0 @@ -package qemu - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "testing" - "time" - - "github.com/hashicorp/packer-plugin-sdk/common" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/stretchr/testify/assert" -) - -var testPem = ` ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEAxd4iamvrwRJvtNDGQSIbNvvIQN8imXTRWlRY62EvKov60vqu -hh+rDzFYAIIzlmrJopvOe0clqmi3mIP9dtkjPFrYflq52a2CF5q+BdwsJXuRHbJW -LmStZUwW1khSz93DhvhmK50nIaczW63u4EO/jJb3xj+wxR1Nkk9bxi3DDsYFt8SN -AzYx9kjlEYQ/+sI4/ATfmdV9h78SVotjScupd9KFzzi76gWq9gwyCBLRynTUWlyD -2UOfJRkOvhN6/jKzvYfVVwjPSfA9IMuooHdScmC4F6KBKJl/zf/zETM0XyzIDNmH -uOPbCiljq2WoRM+rY6ET84EO0kVXbfx8uxUsqQIDAQABAoIBAQCkPj9TF0IagbM3 -5BSs/CKbAWS4dH/D4bPlxx4IRCNirc8GUg+MRb04Xz0tLuajdQDqeWpr6iLZ0RKV -BvreLF+TOdV7DNQ4XE4gSdJyCtCaTHeort/aordL3l0WgfI7mVk0L/yfN1PEG4YG -E9q1TYcyrB3/8d5JwIkjabxERLglCcP+geOEJp+QijbvFIaZR/n2irlKW4gSy6ko -9B0fgUnhkHysSg49ChHQBPQ+o5BbpuLrPDFMiTPTPhdfsvGGcyCGeqfBA56oHcSF -K02Fg8OM+Bd1lb48LAN9nWWY4WbwV+9bkN3Ym8hO4c3a/Dxf2N7LtAQqWZzFjvM3 -/AaDvAgBAoGBAPLD+Xn1IYQPMB2XXCXfOuJewRY7RzoVWvMffJPDfm16O7wOiW5+ -2FmvxUDayk4PZy6wQMzGeGKnhcMMZTyaq2g/QtGfrvy7q1Lw2fB1VFlVblvqhoJa -nMJojjC4zgjBkXMHsRLeTmgUKyGs+fdFbfI6uejBnnf+eMVUMIdJ+6I9AoGBANCn -kWO9640dttyXURxNJ3lBr2H3dJOkmD6XS+u+LWqCSKQe691Y/fZ/ZL0Oc4Mhy7I6 -hsy3kDQ5k2V0fkaNODQIFJvUqXw2pMewUk8hHc9403f4fe9cPrL12rQ8WlQw4yoC -v2B61vNczCCUDtGxlAaw8jzSRaSI5s6ax3K7enbdAoGBAJB1WYDfA2CoAQO6y9Sl -b07A/7kQ8SN5DbPaqrDrBdJziBQxukoMJQXJeGFNUFD/DXFU5Fp2R7C86vXT7HIR -v6m66zH+CYzOx/YE6EsUJms6UP9VIVF0Rg/RU7teXQwM01ZV32LQ8mswhTH20o/3 -uqMHmxUMEhZpUMhrfq0isyApAoGAe1UxGTXfj9AqkIVYylPIq2HqGww7+jFmVEj1 -9Wi6S6Sq72ffnzzFEPkIQL/UA4TsdHMnzsYKFPSbbXLIWUeMGyVTmTDA5c0e5XIR -lPhMOKCAzv8w4VUzMnEkTzkFY5JqFCD/ojW57KvDdNZPVB+VEcdxyAW6aKELXMAc -eHLc1nkCgYEApm/motCTPN32nINZ+Vvywbv64ZD+gtpeMNP3CLrbe1X9O+H52AXa -1jCoOldWR8i2bs2NVPcKZgdo6fFULqE4dBX7Te/uYEIuuZhYLNzRO1IKU/YaqsXG -3bfQ8hKYcSnTfE0gPtLDnqCIxTocaGLSHeG3TH9fTw+dA8FvWpUztI4= ------END RSA PRIVATE KEY----- -` - -func testConfig() map[string]interface{} { - return map[string]interface{}{ - "iso_checksum": "md5:0B0F137F17AC10944716020B018F8126", - "iso_url": "http://www.google.com/", - "ssh_username": "foo", - common.BuildNameConfigKey: "foo", - } -} - -func TestBuilderPrepare_Defaults(t *testing.T) { - var c Config - config := testConfig() - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - if c.OutputDir != "output-foo" { - t.Errorf("bad output dir: %s", c.OutputDir) - } - - if c.CommConfig.HostPortMin != 2222 { - t.Errorf("bad min ssh host port: %d", c.CommConfig.HostPortMin) - } - - if c.CommConfig.HostPortMax != 4444 { - t.Errorf("bad max ssh host port: %d", c.CommConfig.HostPortMax) - } - - if c.CommConfig.Comm.SSHPort != 22 { - t.Errorf("bad ssh port: %d", c.CommConfig.Comm.SSHPort) - } - - if c.VMName != "packer-foo" { - t.Errorf("bad vm name: %s", c.VMName) - } - - if c.Format != "qcow2" { - t.Errorf("bad format: %s", c.Format) - } -} - -func TestBuilderPrepare_VNCBindAddress(t *testing.T) { - var c Config - config := testConfig() - - // Test a default boot_wait - delete(config, "vnc_bind_address") - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("err: %s", err) - } - - if c.VNCBindAddress != "127.0.0.1" { - t.Fatalf("bad value: %s", c.VNCBindAddress) - } -} - -func TestBuilderPrepare_DiskCompaction(t *testing.T) { - var c Config - config := testConfig() - - // Bad - config["skip_compaction"] = false - config["disk_compression"] = true - config["format"] = "img" - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - if c.SkipCompaction != true { - t.Fatalf("SkipCompaction should be true") - } - if c.DiskCompression != false { - t.Fatalf("DiskCompression should be false") - } - - // Good - config["skip_compaction"] = false - config["disk_compression"] = true - config["format"] = "qcow2" - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - if c.SkipCompaction != false { - t.Fatalf("SkipCompaction should be false") - } - if c.DiskCompression != true { - t.Fatalf("DiskCompression should be true") - } -} - -func TestBuilderPrepare_DiskSize(t *testing.T) { - type testcase struct { - InputSize string - OutputSize string - ErrExpected bool - } - - testCases := []testcase{ - {"", "40960M", false}, // not provided - {"12345", "12345M", false}, // no unit given, defaults to M - {"12345x", "12345x", true}, // invalid unit - {"12345T", "12345T", false}, // terabytes - {"12345b", "12345b", false}, // bytes get preserved when set. - {"60000M", "60000M", false}, // Original test case - } - for _, tc := range testCases { - // Set input disk size - var c Config - config := testConfig() - delete(config, "disk_size") - config["disk_size"] = tc.InputSize - - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if (err == nil) == tc.ErrExpected { - t.Fatalf("bad: error when providing disk size %s; Err expected: %t; err recieved: %v", tc.InputSize, tc.ErrExpected, err) - } - - if c.DiskSize != tc.OutputSize { - t.Fatalf("bad size: received: %s but expected %s", c.DiskSize, tc.OutputSize) - } - } -} - -func TestBuilderPrepare_AdditionalDiskSize(t *testing.T) { - var c Config - config := testConfig() - - config["disk_additional_size"] = []string{"1M"} - config["disk_image"] = true - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error") - } - - delete(config, "disk_image") - config["disk_additional_size"] = []string{"1M"} - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - if c.AdditionalDiskSize[0] != "1M" { - t.Fatalf("bad size: %s", c.AdditionalDiskSize) - } -} - -func TestBuilderPrepare_Format(t *testing.T) { - var c Config - config := testConfig() - - // Bad - config["format"] = "illegal value" - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Good - config["format"] = "qcow2" - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - // Good - config["format"] = "raw" - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - -func TestBuilderPrepare_UseBackingFile(t *testing.T) { - var c Config - config := testConfig() - - config["use_backing_file"] = true - - // Bad: iso_url is not a disk_image - config["disk_image"] = false - config["format"] = "qcow2" - c = Config{} - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Bad: format is not 'qcow2' - config["disk_image"] = true - config["format"] = "raw" - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Good: iso_url is a disk image and format is 'qcow2' - config["disk_image"] = true - config["format"] = "qcow2" - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - -func TestBuilderPrepare_SkipResizeDisk(t *testing.T) { - config := testConfig() - config["skip_resize_disk"] = true - config["disk_image"] = false - - var c Config - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Errorf("unexpected warns when calling prepare with skip_resize_disk set to true: %#v", warns) - } - if err == nil { - t.Errorf("setting skip_resize_disk to true when disk_image is false should have error") - } -} - -func TestBuilderPrepare_FloppyFiles(t *testing.T) { - var c Config - config := testConfig() - - delete(config, "floppy_files") - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("bad err: %s", err) - } - - if len(c.FloppyFiles) != 0 { - t.Fatalf("bad: %#v", c.FloppyFiles) - } - - floppies_path := "../test-fixtures/floppies" - config["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - 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(c.FloppyFiles, expected) { - t.Fatalf("bad: %#v", c.FloppyFiles) - } -} - -func TestBuilderPrepare_InvalidFloppies(t *testing.T) { - var c Config - config := testConfig() - config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} - c = Config{} - _, errs := c.Prepare(config) - if errs == nil { - t.Fatalf("Nonexistent floppies should trigger multierror") - } - - if len(errs.(*packersdk.MultiError).Errors) != 2 { - t.Fatalf("Multierror should work and report 2 errors") - } -} - -func TestBuilderPrepare_InvalidKey(t *testing.T) { - var c Config - config := testConfig() - - // Add a random key - config["i_should_not_be_valid"] = true - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } -} - -func TestBuilderPrepare_OutputDir(t *testing.T) { - var c Config - config := testConfig() - - // Test with existing dir - dir, err := ioutil.TempDir("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - defer os.RemoveAll(dir) - - config["output_directory"] = dir - c = Config{} - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test with a good one - config["output_directory"] = "i-hope-i-dont-exist" - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - -func TestBuilderPrepare_ShutdownTimeout(t *testing.T) { - var c Config - config := testConfig() - - // Test with a bad value - config["shutdown_timeout"] = "this is not good" - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test with a good one - config["shutdown_timeout"] = "5s" - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - -func TestBuilderPrepare_SSHHostPort(t *testing.T) { - var c Config - config := testConfig() - - // Bad - config["host_port_min"] = 1000 - config["host_port_max"] = 500 - c = Config{} - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Bad - config["host_port_min"] = -500 - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Good - config["host_port_min"] = 500 - config["host_port_max"] = 1000 - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - -func TestBuilderPrepare_SSHPrivateKey(t *testing.T) { - var c Config - config := testConfig() - - config["ssh_private_key_file"] = "" - c = Config{} - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - config["ssh_private_key_file"] = "/i/dont/exist" - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test bad contents - tf, err := ioutil.TempFile("", "packer") - if err != nil { - t.Fatalf("err: %s", err) - } - defer os.Remove(tf.Name()) - defer tf.Close() - - if _, err := tf.Write([]byte("HELLO!")); err != nil { - t.Fatalf("err: %s", err) - } - - config["ssh_private_key_file"] = tf.Name() - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test good contents - if _, err := tf.Seek(0, 0); err != nil { - t.Fatalf("errorf getting key") - } - if err := tf.Truncate(0); err != nil { - t.Fatalf("errorf getting key") - } - if _, err := tf.Write([]byte(testPem)); err != nil { - t.Fatalf("errorf getting key") - } - config["ssh_private_key_file"] = tf.Name() - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestBuilderPrepare_SSHWaitTimeout(t *testing.T) { - var c Config - config := testConfig() - - // Test a default boot_wait - delete(config, "ssh_timeout") - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("err: %s", err) - } - - // Test with a bad value - config["ssh_timeout"] = "this is not good" - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err == nil { - t.Fatal("should have error") - } - - // Test with a good one - config["ssh_timeout"] = "5s" - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } -} - -func TestBuilderPrepare_QemuArgs(t *testing.T) { - var c Config - config := testConfig() - - // Test with empty - delete(config, "qemuargs") - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("err: %s", err) - } - - if !reflect.DeepEqual(c.QemuArgs, [][]string{}) { - t.Fatalf("bad: %#v", c.QemuArgs) - } - - // Test with a good one - config["qemuargs"] = [][]interface{}{ - {"foo", "bar", "baz"}, - } - - c = Config{} - warns, err = c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - expected := [][]string{ - {"foo", "bar", "baz"}, - } - - if !reflect.DeepEqual(c.QemuArgs, expected) { - t.Fatalf("bad: %#v", c.QemuArgs) - } -} - -func TestBuilderPrepare_VNCPassword(t *testing.T) { - var c Config - config := testConfig() - config["vnc_use_password"] = true - config["output_directory"] = "not-a-real-directory" - - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - expected := filepath.Join("not-a-real-directory", "packer-foo.monitor") - if !reflect.DeepEqual(c.QMPSocketPath, expected) { - t.Fatalf("Bad QMP socket Path: %s", c.QMPSocketPath) - } -} - -func TestCommConfigPrepare_BackwardsCompatibility(t *testing.T) { - var c Config - config := testConfig() - hostPortMin := 1234 - hostPortMax := 4321 - sshTimeout := 2 * time.Minute - - config["ssh_wait_timeout"] = sshTimeout - config["ssh_host_port_min"] = hostPortMin - config["ssh_host_port_max"] = hostPortMax - - warns, err := c.Prepare(config) - if len(warns) == 0 { - t.Fatalf("should have deprecation warn") - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - - if c.CommConfig.Comm.SSHTimeout != sshTimeout { - t.Fatalf("SSHTimeout should be %s for backwards compatibility, but it was %s", sshTimeout.String(), c.CommConfig.Comm.SSHTimeout.String()) - } - - if c.CommConfig.HostPortMin != hostPortMin { - t.Fatalf("HostPortMin should be %d for backwards compatibility, but it was %d", hostPortMin, c.CommConfig.HostPortMin) - } - - if c.CommConfig.HostPortMax != hostPortMax { - t.Fatalf("HostPortMax should be %d for backwards compatibility, but it was %d", hostPortMax, c.CommConfig.HostPortMax) - } -} - -func TestBuilderPrepare_LoadQemuImgArgs(t *testing.T) { - var c Config - config := testConfig() - config["qemu_img_args"] = map[string][]string{ - "convert": []string{"-o", "preallocation=full"}, - "resize": []string{"-foo", "bar"}, - "create": []string{"-baz", "bang"}, - } - warns, err := c.Prepare(config) - if len(warns) > 0 { - t.Fatalf("bad: %#v", warns) - } - if err != nil { - t.Fatalf("should not have error: %s", err) - } - assert.Equal(t, []string{"-o", "preallocation=full"}, - c.QemuImgArgs.Convert, "Convert args not loaded properly") - assert.Equal(t, []string{"-foo", "bar"}, - c.QemuImgArgs.Resize, "Resize args not loaded properly") - assert.Equal(t, []string{"-baz", "bang"}, - c.QemuImgArgs.Create, "Create args not loaded properly") -} diff --git a/builder/qemu/driver.go b/builder/qemu/driver.go deleted file mode 100644 index 3cbcf6ecf..000000000 --- a/builder/qemu/driver.go +++ /dev/null @@ -1,247 +0,0 @@ -package qemu - -import ( - "bufio" - "bytes" - "fmt" - "io" - "log" - "os" - "os/exec" - "regexp" - "strings" - "sync" - "syscall" - "time" - "unicode" - - "github.com/hashicorp/packer-plugin-sdk/multistep" -) - -type DriverCancelCallback func(state multistep.StateBag) bool - -// A driver is able to talk to qemu-system-x86_64 and perform certain -// operations with it. -type Driver interface { - // Copy bypasses qemu-img convert and directly copies an image - // that doesn't need converting. - Copy(string, string) error - - // Stop stops a running machine, forcefully. - Stop() error - - // Qemu executes the given command via qemu-system-x86_64 - Qemu(qemuArgs ...string) error - - // wait on shutdown of the VM with option to cancel - WaitForShutdown(<-chan struct{}) bool - - // Qemu executes the given command via qemu-img - QemuImg(...string) error - - // Verify checks to make sure that this driver should function - // properly. If there is any indication the driver can't function, - // this will return an error. - Verify() error - - // Version reads the version of Qemu that is installed. - Version() (string, error) -} - -type QemuDriver struct { - QemuPath string - QemuImgPath string - - vmCmd *exec.Cmd - vmEndCh <-chan int - lock sync.Mutex -} - -func (d *QemuDriver) Stop() error { - d.lock.Lock() - defer d.lock.Unlock() - - if d.vmCmd != nil { - if err := d.vmCmd.Process.Kill(); err != nil { - return err - } - } - - return nil -} - -func (d *QemuDriver) Copy(sourceName, targetName string) error { - source, err := os.Open(sourceName) - if err != nil { - err = fmt.Errorf("Error opening iso for copy: %s", err) - return err - } - defer source.Close() - - // Create will truncate an existing file - target, err := os.Create(targetName) - if err != nil { - err = fmt.Errorf("Error creating hard drive in output dir: %s", err) - return err - } - defer target.Close() - - log.Printf("Copying %s to %s", source.Name(), target.Name()) - bytes, err := io.Copy(target, source) - if err != nil { - err = fmt.Errorf("Error copying iso to output dir: %s", err) - return err - } - log.Printf(fmt.Sprintf("Copied %d bytes", bytes)) - - return nil -} - -func (d *QemuDriver) Qemu(qemuArgs ...string) error { - d.lock.Lock() - defer d.lock.Unlock() - - if d.vmCmd != nil { - panic("Existing VM state found") - } - - stdout_r, stdout_w := io.Pipe() - stderr_r, stderr_w := io.Pipe() - - log.Printf("Executing %s: %#v", d.QemuPath, qemuArgs) - cmd := exec.Command(d.QemuPath, qemuArgs...) - cmd.Stdout = stdout_w - cmd.Stderr = stderr_w - - err := cmd.Start() - if err != nil { - err = fmt.Errorf("Error starting VM: %s", err) - return err - } - - go logReader("Qemu stdout", stdout_r) - go logReader("Qemu stderr", stderr_r) - - log.Printf("Started Qemu. Pid: %d", cmd.Process.Pid) - - // Wait for Qemu to complete in the background, and mark when its done - endCh := make(chan int, 1) - go func() { - defer stderr_w.Close() - defer stdout_w.Close() - - var exitCode int = 0 - if err := cmd.Wait(); err != nil { - if exiterr, ok := err.(*exec.ExitError); ok { - // The program has exited with an exit code != 0 - if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { - exitCode = status.ExitStatus() - } else { - exitCode = 254 - } - } - } - - endCh <- exitCode - - d.lock.Lock() - defer d.lock.Unlock() - d.vmCmd = nil - d.vmEndCh = nil - }() - - // Wait at least a couple seconds for an early fail from Qemu so - // we can report that. - select { - case exit := <-endCh: - if exit != 0 { - return fmt.Errorf("Qemu failed to start. Please run with PACKER_LOG=1 to get more info.") - } - case <-time.After(2 * time.Second): - } - - // Setup our state so we know we are running - d.vmCmd = cmd - d.vmEndCh = endCh - - return nil -} - -func (d *QemuDriver) WaitForShutdown(cancelCh <-chan struct{}) bool { - d.lock.Lock() - endCh := d.vmEndCh - d.lock.Unlock() - - if endCh == nil { - return true - } - - select { - case <-endCh: - return true - case <-cancelCh: - return false - } -} - -func (d *QemuDriver) QemuImg(args ...string) error { - var stdout, stderr bytes.Buffer - - log.Printf("Executing qemu-img: %#v", args) - cmd := exec.Command(d.QemuImgPath, args...) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() - - stdoutString := strings.TrimSpace(stdout.String()) - stderrString := strings.TrimSpace(stderr.String()) - - if _, ok := err.(*exec.ExitError); ok { - err = fmt.Errorf("QemuImg error: %s", stderrString) - } - - log.Printf("stdout: %s", stdoutString) - log.Printf("stderr: %s", stderrString) - - return err -} - -func (d *QemuDriver) Verify() error { - return nil -} - -func (d *QemuDriver) Version() (string, error) { - var stdout bytes.Buffer - - cmd := exec.Command(d.QemuPath, "-version") - cmd.Stdout = &stdout - if err := cmd.Run(); err != nil { - return "", err - } - - versionOutput := strings.TrimSpace(stdout.String()) - log.Printf("Qemu --version output: %s", versionOutput) - versionRe := regexp.MustCompile(`[\.[0-9]+]*`) - matches := versionRe.FindStringSubmatch(versionOutput) - if len(matches) == 0 { - return "", fmt.Errorf("No version found: %s", versionOutput) - } - - log.Printf("Qemu version: %s", matches[0]) - return matches[0], nil -} - -func logReader(name string, r io.Reader) { - bufR := bufio.NewReader(r) - for { - line, err := bufR.ReadString('\n') - if line != "" { - line = strings.TrimRightFunc(line, unicode.IsSpace) - log.Printf("%s: %s", name, line) - } - - if err == io.EOF { - break - } - } -} diff --git a/builder/qemu/driver_mock.go b/builder/qemu/driver_mock.go deleted file mode 100644 index 7fa1a7df7..000000000 --- a/builder/qemu/driver_mock.go +++ /dev/null @@ -1,74 +0,0 @@ -package qemu - -import "sync" - -type DriverMock struct { - sync.Mutex - - CopyCalled bool - CopyErr error - - StopCalled bool - StopErr error - - QemuCalls [][]string - QemuErrs []error - - WaitForShutdownCalled bool - WaitForShutdownState bool - - QemuImgCalled bool - QemuImgCalls []string - QemuImgErrs []error - - VerifyCalled bool - VerifyErr error - - VersionCalled bool - VersionResult string - VersionErr error -} - -func (d *DriverMock) Copy(source, dst string) error { - d.CopyCalled = true - return d.CopyErr -} - -func (d *DriverMock) Stop() error { - d.StopCalled = true - return d.StopErr -} - -func (d *DriverMock) Qemu(args ...string) error { - d.QemuCalls = append(d.QemuCalls, args) - - if len(d.QemuErrs) >= len(d.QemuCalls) { - return d.QemuErrs[len(d.QemuCalls)-1] - } - return nil -} - -func (d *DriverMock) WaitForShutdown(cancelCh <-chan struct{}) bool { - d.WaitForShutdownCalled = true - return d.WaitForShutdownState -} - -func (d *DriverMock) QemuImg(args ...string) error { - d.QemuImgCalled = true - d.QemuImgCalls = append(d.QemuImgCalls, args...) - - if len(d.QemuImgErrs) >= len(d.QemuImgCalls) { - return d.QemuImgErrs[len(d.QemuImgCalls)-1] - } - return nil -} - -func (d *DriverMock) Verify() error { - d.VerifyCalled = true - return d.VerifyErr -} - -func (d *DriverMock) Version() (string, error) { - d.VersionCalled = true - return d.VersionResult, d.VersionErr -} diff --git a/builder/qemu/qmp.go b/builder/qemu/qmp.go deleted file mode 100644 index 04c4bea14..000000000 --- a/builder/qemu/qmp.go +++ /dev/null @@ -1,135 +0,0 @@ -package qemu - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/digitalocean/go-qemu/qmp" -) - -type qomListRequest struct { - Execute string `json:"execute"` - Arguments qomListRequestArguments `json:"arguments"` -} - -type qomListRequestArguments struct { - Path string `json:"path"` -} - -type qomListResponse struct { - Return []qomListReturn `json:"return"` -} - -type qomListReturn struct { - Name string `json:"name"` - Type string `json:"type"` -} - -func qmpQomList(qmpMonitor *qmp.SocketMonitor, path string) ([]qomListReturn, error) { - request, _ := json.Marshal(qomListRequest{ - Execute: "qom-list", - Arguments: qomListRequestArguments{ - Path: path, - }, - }) - result, err := qmpMonitor.Run(request) - if err != nil { - return nil, err - } - var response qomListResponse - if err := json.Unmarshal(result, &response); err != nil { - return nil, err - } - return response.Return, nil -} - -type qomGetRequest struct { - Execute string `json:"execute"` - Arguments qomGetRequestArguments `json:"arguments"` -} - -type qomGetRequestArguments struct { - Path string `json:"path"` - Property string `json:"property"` -} - -type qomGetResponse struct { - Return string `json:"return"` -} - -func qmpQomGet(qmpMonitor *qmp.SocketMonitor, path string, property string) (string, error) { - request, _ := json.Marshal(qomGetRequest{ - Execute: "qom-get", - Arguments: qomGetRequestArguments{ - Path: path, - Property: property, - }, - }) - result, err := qmpMonitor.Run(request) - if err != nil { - return "", err - } - var response qomGetResponse - if err := json.Unmarshal(result, &response); err != nil { - return "", err - } - return response.Return, nil -} - -type netDevice struct { - Path string - Name string - Type string - MacAddress string -} - -func getNetDevices(qmpMonitor *qmp.SocketMonitor) ([]netDevice, error) { - devices := []netDevice{} - for _, parentPath := range []string{"/machine/peripheral", "/machine/peripheral-anon"} { - listResponse, err := qmpQomList(qmpMonitor, parentPath) - if err != nil { - return nil, fmt.Errorf("failed to get qmp qom list %v: %w", parentPath, err) - } - for _, p := range listResponse { - if strings.HasPrefix(p.Type, "child<") { - path := fmt.Sprintf("%s/%s", parentPath, p.Name) - r, err := qmpQomList(qmpMonitor, path) - if err != nil { - return nil, fmt.Errorf("failed to get qmp qom list %v: %w", path, err) - } - isNetdev := false - for _, d := range r { - if d.Name == "netdev" { - isNetdev = true - break - } - } - if isNetdev { - device := netDevice{ - Path: path, - } - for _, d := range r { - if d.Name != "type" && d.Name != "netdev" && d.Name != "mac" { - continue - } - value, err := qmpQomGet(qmpMonitor, path, d.Name) - if err != nil { - return nil, fmt.Errorf("failed to get qmp qom property %v %v: %w", path, d.Name, err) - } - switch d.Name { - case "type": - device.Type = value - case "netdev": - device.Name = value - case "mac": - device.MacAddress = value - } - } - devices = append(devices, device) - } - } - } - } - return devices, nil -} diff --git a/builder/qemu/ssh.go b/builder/qemu/ssh.go deleted file mode 100644 index 62ed86a41..000000000 --- a/builder/qemu/ssh.go +++ /dev/null @@ -1,30 +0,0 @@ -package qemu - -import ( - "log" - - "github.com/hashicorp/packer-plugin-sdk/multistep" -) - -func commHost(host string) func(multistep.StateBag) (string, error) { - return func(state multistep.StateBag) (string, error) { - if host != "" { - log.Printf("Using host value: %s", host) - return host, nil - } - - if guestAddress, ok := state.Get("guestAddress").(string); ok { - return guestAddress, nil - } - - return "127.0.0.1", nil - } -} - -func commPort(state multistep.StateBag) (int, error) { - commHostPort, ok := state.Get("commHostPort").(int) - if !ok { - commHostPort = 22 - } - return commHostPort, nil -} diff --git a/builder/qemu/step_configure_qmp.go b/builder/qemu/step_configure_qmp.go deleted file mode 100644 index ccd867d6e..000000000 --- a/builder/qemu/step_configure_qmp.go +++ /dev/null @@ -1,92 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "log" - "os" - "time" - - "github.com/digitalocean/go-qemu/qmp" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// This step configures the VM to enable the QMP listener. -// -// Uses: -// config *config -// ui packersdk.Ui -// -// Produces: -type stepConfigureQMP struct { - monitor *qmp.SocketMonitor - QMPSocketPath string -} - -func (s *stepConfigureQMP) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) - ui := state.Get("ui").(packersdk.Ui) - - if !config.QMPEnable { - return multistep.ActionContinue - } - - ui.Say(fmt.Sprintf("QMP socket at: %s", s.QMPSocketPath)) - - // Only initialize and open QMP when we have a use for it. - // Open QMP socket - var err error - var cmd []byte - var result []byte - s.monitor, err = qmp.NewSocketMonitor("unix", s.QMPSocketPath, 2*time.Second) - if err != nil { - err := fmt.Errorf("Error opening QMP socket: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - // Connect to QMP - // function automatically calls capabilities so is immediately ready for commands - err = s.monitor.Connect() - if err != nil { - err := fmt.Errorf("Error connecting to QMP socket: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - log.Printf("QMP socket open SUCCESS") - - vncPassword := state.Get("vnc_password") - if vncPassword != "" { - cmd = []byte(fmt.Sprintf("{ \"execute\": \"change-vnc-password\", \"arguments\": { \"password\": \"%s\" } }", - vncPassword)) - result, err = s.monitor.Run(cmd) - if err != nil { - err := fmt.Errorf("Error connecting to QMP socket: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - log.Printf("QMP Command: %s\nResult: %s", cmd, result) - } - - // make the qmp_monitor available to other steps. - state.Put("qmp_monitor", s.monitor) - - return multistep.ActionContinue -} - -func (s *stepConfigureQMP) Cleanup(multistep.StateBag) { - if s.monitor != nil { - err := s.monitor.Disconnect() - if err != nil { - log.Printf("failed to disconnect QMP: %v", err) - } - // Delete file associated with qmp socket. - if err := os.Remove(s.QMPSocketPath); err != nil { - log.Printf("Failed to delete the qmp socket file: %s", err) - } - } -} diff --git a/builder/qemu/step_configure_vnc.go b/builder/qemu/step_configure_vnc.go deleted file mode 100644 index 776958c37..000000000 --- a/builder/qemu/step_configure_vnc.go +++ /dev/null @@ -1,89 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "log" - "math/rand" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer-plugin-sdk/net" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// This step configures the VM to enable the VNC server. -// -// Uses: -// config *config -// ui packersdk.Ui -// -// Produces: -// vnc_port int - The port that VNC is configured to listen on. -type stepConfigureVNC struct { - l *net.Listener -} - -func VNCPassword() string { - length := int(8) - - charSet := []byte("012345689abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - charSetLength := len(charSet) - - password := make([]byte, length) - - for i := 0; i < length; i++ { - password[i] = charSet[rand.Intn(charSetLength)] - } - - return string(password) -} - -func (s *stepConfigureVNC) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) - ui := state.Get("ui").(packersdk.Ui) - - // Find an open VNC port. Note that this can still fail later on - // because we have to release the port at some point. But this does its - // best. - msg := fmt.Sprintf("Looking for available port between %d and %d on %s", config.VNCPortMin, config.VNCPortMax, config.VNCBindAddress) - ui.Say(msg) - log.Print(msg) - - var vncPassword string - var err error - s.l, err = net.ListenRangeConfig{ - Addr: config.VNCBindAddress, - Min: config.VNCPortMin, - Max: config.VNCPortMax, - Network: "tcp", - }.Listen(ctx) - if err != nil { - err := fmt.Errorf("Error finding VNC port: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - s.l.Listener.Close() // free port, but don't unlock lock file - vncPort := s.l.Port - - if config.VNCUsePassword { - vncPassword = VNCPassword() - } else { - vncPassword = "" - } - - log.Printf("Found available VNC port: %d on IP: %s", vncPort, config.VNCBindAddress) - state.Put("vnc_port", vncPort) - state.Put("vnc_password", vncPassword) - - return multistep.ActionContinue -} - -func (s *stepConfigureVNC) Cleanup(multistep.StateBag) { - if s.l != nil { - err := s.l.Close() - if err != nil { - log.Printf("failed to unlock port lockfile: %v", err) - } - } -} diff --git a/builder/qemu/step_convert_disk.go b/builder/qemu/step_convert_disk.go deleted file mode 100644 index 0d16ed982..000000000 --- a/builder/qemu/step_convert_disk.go +++ /dev/null @@ -1,104 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "path/filepath" - "strings" - "time" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/retry" - - "os" -) - -// This step converts the virtual disk that was used as the -// hard drive for the virtual machine. -type stepConvertDisk struct { - DiskCompression bool - Format string - OutputDir string - SkipCompaction bool - VMName string - - QemuImgArgs QemuImgArgs -} - -func (s *stepConvertDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) - ui := state.Get("ui").(packersdk.Ui) - - diskName := s.VMName - - if s.SkipCompaction && !s.DiskCompression { - return multistep.ActionContinue - } - - name := diskName + ".convert" - - sourcePath := filepath.Join(s.OutputDir, diskName) - targetPath := filepath.Join(s.OutputDir, name) - - command := s.buildConvertCommand(sourcePath, targetPath) - - ui.Say("Converting hard drive...") - // Retry the conversion a few times in case it takes the qemu process a - // moment to release the lock - err := retry.Config{ - Tries: 10, - ShouldRetry: func(err error) bool { - if strings.Contains(err.Error(), `Failed to get shared "write" lock`) { - ui.Say("Error getting file lock for conversion; retrying...") - return true - } - return false - }, - RetryDelay: (&retry.Backoff{InitialBackoff: 1 * time.Second, MaxBackoff: 10 * time.Second, Multiplier: 2}).Linear, - }.Run(ctx, func(ctx context.Context) error { - return driver.QemuImg(command...) - }) - - if err != nil { - switch err.(type) { - case *retry.RetryExhaustedError: - err = fmt.Errorf("Exhausted retries for getting file lock: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - default: - err := fmt.Errorf("Error converting hard drive: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - if err := os.Rename(targetPath, sourcePath); err != nil { - err := fmt.Errorf("Error moving converted hard drive: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (s *stepConvertDisk) buildConvertCommand(sourcePath, targetPath string) []string { - command := []string{"convert"} - - if s.DiskCompression { - command = append(command, "-c") - } - - // Add user-provided convert args - command = append(command, s.QemuImgArgs.Convert...) - - // Add format, and paths. - command = append(command, "-O", s.Format, sourcePath, targetPath) - - return command -} - -func (s *stepConvertDisk) Cleanup(state multistep.StateBag) {} diff --git a/builder/qemu/step_convert_disk_test.go b/builder/qemu/step_convert_disk_test.go deleted file mode 100644 index e6ff4b0c4..000000000 --- a/builder/qemu/step_convert_disk_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package qemu - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" -) - -func Test_buildConvertCommand(t *testing.T) { - type testCase struct { - Step *stepConvertDisk - Expected []string - Reason string - } - testcases := []testCase{ - { - &stepConvertDisk{ - Format: "qcow2", - DiskCompression: false, - }, - []string{"convert", "-O", "qcow2", "source.qcow", "target.qcow2"}, - "Basic, happy path, no compression, no extra args", - }, - { - &stepConvertDisk{ - Format: "qcow2", - DiskCompression: true, - }, - []string{"convert", "-c", "-O", "qcow2", "source.qcow", "target.qcow2"}, - "Basic, happy path, with compression, no extra args", - }, - { - &stepConvertDisk{ - Format: "qcow2", - DiskCompression: true, - QemuImgArgs: QemuImgArgs{ - Convert: []string{"-o", "preallocation=full"}, - }, - }, - []string{"convert", "-c", "-o", "preallocation=full", "-O", "qcow2", "source.qcow", "target.qcow2"}, - "Basic, happy path, with compression, one set of extra args", - }, - } - - for _, tc := range testcases { - command := tc.Step.buildConvertCommand("source.qcow", "target.qcow2") - - assert.Equal(t, command, tc.Expected, - fmt.Sprintf("%s. Expected %#v", tc.Reason, tc.Expected)) - } -} diff --git a/builder/qemu/step_copy_disk.go b/builder/qemu/step_copy_disk.go deleted file mode 100644 index 3fbc4e66b..000000000 --- a/builder/qemu/step_copy_disk.go +++ /dev/null @@ -1,78 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "path/filepath" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// This step copies the virtual disk that will be used as the -// hard drive for the virtual machine. -type stepCopyDisk struct { - DiskImage bool - Format string - OutputDir string - UseBackingFile bool - VMName string - - QemuImgArgs QemuImgArgs -} - -func (s *stepCopyDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) - isoPath := state.Get("iso_path").(string) - ui := state.Get("ui").(packersdk.Ui) - path := filepath.Join(s.OutputDir, s.VMName) - - // Only make a copy of the ISO as the 'main' or 'default' disk if is a disk - // image and not using a backing file. The create disk step (step_create_disk.go) - // would already have made the disk otherwise - if !s.DiskImage || s.UseBackingFile { - return multistep.ActionContinue - } - - // In some cases, the file formats provided are equivalent by comparing the - // file extensions. Skip the conversion step - // This also serves as a workaround for a QEMU bug: https://bugs.launchpad.net/qemu/+bug/1776920 - ext := filepath.Ext(isoPath) - if len(ext) >= 1 && ext[1:] == s.Format && len(s.QemuImgArgs.Convert) == 0 { - ui.Message("File extension already matches desired output format. " + - "Skipping qemu-img convert step") - err := driver.Copy(isoPath, path) - if err != nil { - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - return multistep.ActionContinue - } - - command := s.buildConvertCommand(isoPath, path) - - ui.Say("Copying hard drive...") - if err := driver.QemuImg(command...); err != nil { - err := fmt.Errorf("Error creating hard drive: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (s *stepCopyDisk) buildConvertCommand(sourcePath, targetPath string) []string { - command := []string{"convert"} - - // Add user-provided convert args - command = append(command, s.QemuImgArgs.Convert...) - - // Add format, and paths. - command = append(command, "-O", s.Format, sourcePath, targetPath) - - return command -} - -func (s *stepCopyDisk) Cleanup(state multistep.StateBag) {} diff --git a/builder/qemu/step_copy_disk_test.go b/builder/qemu/step_copy_disk_test.go deleted file mode 100644 index cbcd93452..000000000 --- a/builder/qemu/step_copy_disk_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package qemu - -import ( - "context" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/stretchr/testify/assert" -) - -func copyTestState(t *testing.T, d *DriverMock) multistep.StateBag { - state := new(multistep.BasicStateBag) - state.Put("ui", packersdk.TestUi(t)) - state.Put("driver", d) - state.Put("iso_path", "example_source.qcow2") - - return state -} - -func Test_StepCopySkip(t *testing.T) { - testcases := []stepCopyDisk{ - stepCopyDisk{ - DiskImage: false, - UseBackingFile: false, - }, - stepCopyDisk{ - DiskImage: true, - UseBackingFile: true, - }, - stepCopyDisk{ - DiskImage: false, - UseBackingFile: true, - }, - } - - for _, tc := range testcases { - d := new(DriverMock) - state := copyTestState(t, d) - action := tc.Run(context.TODO(), state) - if action != multistep.ActionContinue { - t.Fatalf("Should have gotten an ActionContinue") - } - - if d.CopyCalled || d.QemuImgCalled { - t.Fatalf("Should have skipped step since DiskImage and UseBackingFile are not set") - } - } -} - -func Test_StepCopyCalled(t *testing.T) { - step := stepCopyDisk{ - DiskImage: true, - Format: "qcow2", - VMName: "output.qcow2", - } - - d := new(DriverMock) - state := copyTestState(t, d) - action := step.Run(context.TODO(), state) - if action != multistep.ActionContinue { - t.Fatalf("Should have gotten an ActionContinue") - } - - if !d.CopyCalled { - t.Fatalf("Should have copied since all extensions are qcow2") - } - if d.QemuImgCalled { - t.Fatalf("Should not have called qemu-img when formats match") - } -} - -func Test_StepQemuImgCalled(t *testing.T) { - step := stepCopyDisk{ - DiskImage: true, - Format: "raw", - VMName: "output.qcow2", - } - - d := new(DriverMock) - state := copyTestState(t, d) - action := step.Run(context.TODO(), state) - if action != multistep.ActionContinue { - t.Fatalf("Should have gotten an ActionContinue") - } - if d.CopyCalled { - t.Fatalf("Should not have copied since extensions don't match") - } - if !d.QemuImgCalled { - t.Fatalf("Should have called qemu-img since extensions don't match") - } -} - -func Test_StepQemuImgCalledWithExtraArgs(t *testing.T) { - step := &stepCopyDisk{ - DiskImage: true, - Format: "raw", - VMName: "output.qcow2", - QemuImgArgs: QemuImgArgs{ - Convert: []string{"-o", "preallocation=full"}, - }, - } - - d := new(DriverMock) - state := copyTestState(t, d) - action := step.Run(context.TODO(), state) - if action != multistep.ActionContinue { - t.Fatalf("Should have gotten an ActionContinue") - } - if d.CopyCalled { - t.Fatalf("Should not have copied since extensions don't match") - } - if !d.QemuImgCalled { - t.Fatalf("Should have called qemu-img since extensions don't match") - } - assert.Equal( - t, - d.QemuImgCalls, - []string{"convert", "-o", "preallocation=full", "-O", "raw", - "example_source.qcow2", "output.qcow2"}, - "should have added user extra args") -} diff --git a/builder/qemu/step_create_disk.go b/builder/qemu/step_create_disk.go deleted file mode 100644 index e86ee49dc..000000000 --- a/builder/qemu/step_create_disk.go +++ /dev/null @@ -1,91 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "log" - "path/filepath" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// This step creates the virtual disk that will be used as the -// hard drive for the virtual machine. -type stepCreateDisk struct { - AdditionalDiskSize []string - DiskImage bool - DiskSize string - Format string - OutputDir string - UseBackingFile bool - VMName string - QemuImgArgs QemuImgArgs -} - -func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) - ui := state.Get("ui").(packersdk.Ui) - name := s.VMName - - if len(s.AdditionalDiskSize) > 0 || s.UseBackingFile { - ui.Say("Creating required virtual machine disks") - } - - // The 'main' or 'default' disk - diskFullPaths := []string{filepath.Join(s.OutputDir, name)} - diskSizes := []string{s.DiskSize} - - // Additional disks - if len(s.AdditionalDiskSize) > 0 { - for i, diskSize := range s.AdditionalDiskSize { - path := filepath.Join(s.OutputDir, fmt.Sprintf("%s-%d", name, i+1)) - diskFullPaths = append(diskFullPaths, path) - diskSizes = append(diskSizes, diskSize) - } - } - - // Create all required disks - for i, diskFullPath := range diskFullPaths { - if s.DiskImage && !s.UseBackingFile && i == 0 { - // Let the copy disk step (step_copy_disk.go) create the 'main' or - // 'default' disk. - continue - } - log.Printf("[INFO] Creating disk with Path: %s and Size: %s", diskFullPath, diskSizes[i]) - - command := s.buildCreateCommand(diskFullPath, diskSizes[i], i, state) - - if err := driver.QemuImg(command...); err != nil { - err := fmt.Errorf("Error creating hard drive: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - // Stash the disk paths so we can retrieve later - state.Put("qemu_disk_paths", diskFullPaths) - - return multistep.ActionContinue -} - -func (s *stepCreateDisk) buildCreateCommand(path string, size string, i int, state multistep.StateBag) []string { - command := []string{"create", "-f", s.Format} - - if s.DiskImage && s.UseBackingFile && i == 0 { - // Use a backing file for the 'main' or 'default' disk - isoPath := state.Get("iso_path").(string) - command = append(command, "-b", isoPath) - } - - // add user-provided convert args - command = append(command, s.QemuImgArgs.Create...) - - // add target path and size. - command = append(command, path, size) - - return command -} - -func (s *stepCreateDisk) Cleanup(state multistep.StateBag) {} diff --git a/builder/qemu/step_create_disk_test.go b/builder/qemu/step_create_disk_test.go deleted file mode 100644 index 0af353652..000000000 --- a/builder/qemu/step_create_disk_test.go +++ /dev/null @@ -1,176 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/stretchr/testify/assert" -) - -func Test_buildCreateCommand(t *testing.T) { - type testCase struct { - Step *stepCreateDisk - I int - Expected []string - Reason string - } - testcases := []testCase{ - { - &stepCreateDisk{ - Format: "qcow2", - UseBackingFile: false, - }, - 0, - []string{"create", "-f", "qcow2", "target.qcow2", "1234M"}, - "Basic, happy path, no backing store, no extra args", - }, - { - &stepCreateDisk{ - Format: "qcow2", - DiskImage: true, - UseBackingFile: true, - AdditionalDiskSize: []string{"1M", "2M"}, - }, - 0, - []string{"create", "-f", "qcow2", "-b", "source.qcow2", "target.qcow2", "1234M"}, - "Basic, happy path, backing store, additional disks", - }, - { - &stepCreateDisk{ - Format: "qcow2", - UseBackingFile: true, - DiskImage: true, - }, - 1, - []string{"create", "-f", "qcow2", "target.qcow2", "1234M"}, - "Basic, happy path, backing store set but not at first index, no extra args", - }, - { - &stepCreateDisk{ - Format: "qcow2", - UseBackingFile: true, - DiskImage: true, - QemuImgArgs: QemuImgArgs{ - Create: []string{"-foo", "bar"}, - }, - }, - 0, - []string{"create", "-f", "qcow2", "-b", "source.qcow2", "-foo", "bar", "target.qcow2", "1234M"}, - "Basic, happy path, backing store set, extra args", - }, - { - &stepCreateDisk{ - Format: "qcow2", - UseBackingFile: true, - QemuImgArgs: QemuImgArgs{ - Create: []string{"-foo", "bar"}, - }, - }, - 1, - []string{"create", "-f", "qcow2", "-foo", "bar", "target.qcow2", "1234M"}, - "Basic, happy path, backing store set but not at first index, extra args", - }, - } - - for _, tc := range testcases { - state := new(multistep.BasicStateBag) - state.Put("iso_path", "source.qcow2") - command := tc.Step.buildCreateCommand("target.qcow2", "1234M", tc.I, state) - - assert.Equal(t, command, tc.Expected, - fmt.Sprintf("%s. Expected %#v", tc.Reason, tc.Expected)) - } -} - -func Test_StepCreateCalled(t *testing.T) { - type testCase struct { - Step *stepCreateDisk - Expected []string - Reason string - } - testcases := []testCase{ - { - &stepCreateDisk{ - Format: "qcow2", - DiskImage: true, - DiskSize: "1M", - VMName: "target", - UseBackingFile: true, - }, - []string{ - "create", "-f", "qcow2", "-b", "source.qcow2", "target", "1M", - }, - "Basic, happy path, backing store, no additional disks", - }, - { - &stepCreateDisk{ - Format: "raw", - DiskImage: false, - DiskSize: "4M", - VMName: "target", - UseBackingFile: false, - }, - []string{ - "create", "-f", "raw", "target", "4M", - }, - "Basic, happy path, raw, no additional disks", - }, - { - &stepCreateDisk{ - Format: "qcow2", - DiskImage: true, - DiskSize: "4M", - VMName: "target", - UseBackingFile: false, - AdditionalDiskSize: []string{"3M", "8M"}, - }, - []string{ - "create", "-f", "qcow2", "target-1", "3M", - "create", "-f", "qcow2", "target-2", "8M", - }, - "Skips disk creation when disk can be copied", - }, - { - &stepCreateDisk{ - Format: "qcow2", - DiskImage: true, - DiskSize: "4M", - VMName: "target", - UseBackingFile: false, - }, - nil, - "Skips disk creation when disk can be copied", - }, - { - &stepCreateDisk{ - Format: "qcow2", - DiskImage: true, - DiskSize: "1M", - VMName: "target", - UseBackingFile: true, - AdditionalDiskSize: []string{"3M", "8M"}, - }, - []string{ - "create", "-f", "qcow2", "-b", "source.qcow2", "target", "1M", - "create", "-f", "qcow2", "target-1", "3M", - "create", "-f", "qcow2", "target-2", "8M", - }, - "Basic, happy path, backing store, additional disks", - }, - } - - for _, tc := range testcases { - d := new(DriverMock) - state := copyTestState(t, d) - state.Put("iso_path", "source.qcow2") - action := tc.Step.Run(context.TODO(), state) - if action != multistep.ActionContinue { - t.Fatalf("Should have gotten an ActionContinue") - } - - assert.Equal(t, d.QemuImgCalls, tc.Expected, - fmt.Sprintf("%s. Expected %#v", tc.Reason, tc.Expected)) - } -} diff --git a/builder/qemu/step_http_ip_discover.go b/builder/qemu/step_http_ip_discover.go deleted file mode 100644 index 543fc111f..000000000 --- a/builder/qemu/step_http_ip_discover.go +++ /dev/null @@ -1,68 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "net" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// Step to discover the http ip -// which guests use to reach the vm host -// To make sure the IP is set before boot command and http server steps -type stepHTTPIPDiscover struct{} - -func (s *stepHTTPIPDiscover) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) - ui := state.Get("ui").(packersdk.Ui) - - hostIP := "" - - if config.NetBridge == "" { - hostIP = "10.0.2.2" - } else { - bridgeInterface, err := net.InterfaceByName(config.NetBridge) - if err != nil { - err := fmt.Errorf("Error getting the bridge %s interface: %s", config.NetBridge, err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - addrs, err := bridgeInterface.Addrs() - if err != nil { - err := fmt.Errorf("Error getting the bridge %s interface addresses: %s", config.NetBridge, err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - for _, addr := range addrs { - var ip net.IP - switch v := addr.(type) { - case *net.IPNet: - ip = v.IP - case *net.IPAddr: - ip = v.IP - } - if ip == nil { - continue - } - ip = ip.To4() - if ip == nil { - continue - } - hostIP = ip.String() - break - } - if hostIP == "" { - err := fmt.Errorf("Error getting an IPv4 address from the bridge %s: cannot find any IPv4 address", config.NetBridge) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - state.Put("http_ip", hostIP) - - return multistep.ActionContinue -} - -func (s *stepHTTPIPDiscover) Cleanup(state multistep.StateBag) {} diff --git a/builder/qemu/step_http_ip_discover_test.go b/builder/qemu/step_http_ip_discover_test.go deleted file mode 100644 index 6118ec259..000000000 --- a/builder/qemu/step_http_ip_discover_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package qemu - -import ( - "bytes" - "context" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -func TestStepHTTPIPDiscover_Run(t *testing.T) { - state := new(multistep.BasicStateBag) - state.Put("ui", &packersdk.BasicUi{ - Reader: new(bytes.Buffer), - Writer: new(bytes.Buffer), - }) - config := &Config{} - state.Put("config", config) - step := new(stepHTTPIPDiscover) - hostIp := "10.0.2.2" - - // 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") - } - httpIp := state.Get("http_ip").(string) - if httpIp != hostIp { - t.Fatalf("bad: Http ip is %s but was supposed to be %s", httpIp, hostIp) - } -} diff --git a/builder/qemu/step_port_forward.go b/builder/qemu/step_port_forward.go deleted file mode 100644 index 5282cfdf9..000000000 --- a/builder/qemu/step_port_forward.go +++ /dev/null @@ -1,74 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "log" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer-plugin-sdk/net" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// This step adds a NAT port forwarding definition so that SSH or WinRM is available -// on the guest machine. -type stepPortForward struct { - CommunicatorType string - NetBridge string - - l *net.Listener -} - -func (s *stepPortForward) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) - ui := state.Get("ui").(packersdk.Ui) - - if s.CommunicatorType == "none" { - ui.Message("No communicator is set; skipping port forwarding setup.") - return multistep.ActionContinue - } - if s.NetBridge != "" { - ui.Message("net_bridge is set; skipping port forwarding setup.") - return multistep.ActionContinue - } - - commHostPort := config.CommConfig.Comm.Port() - - if config.CommConfig.SkipNatMapping { - log.Printf("Skipping NAT port forwarding. Using communicator (SSH, WinRM, etc) port %d", commHostPort) - state.Put("commHostPort", commHostPort) - return multistep.ActionContinue - } - - log.Printf("Looking for available communicator (SSH, WinRM, etc) port between %d and %d", config.CommConfig.HostPortMin, config.CommConfig.HostPortMax) - var err error - s.l, err = net.ListenRangeConfig{ - Addr: config.VNCBindAddress, - Min: config.CommConfig.HostPortMin, - Max: config.CommConfig.HostPortMax, - Network: "tcp", - }.Listen(ctx) - if err != nil { - err := fmt.Errorf("Error finding port: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - s.l.Listener.Close() // free port, but don't unlock lock file - commHostPort = s.l.Port - ui.Say(fmt.Sprintf("Found port for communicator (SSH, WinRM, etc): %d.", commHostPort)) - - // Save the port we're using so that future steps can use it - state.Put("commHostPort", commHostPort) - - return multistep.ActionContinue -} - -func (s *stepPortForward) Cleanup(state multistep.StateBag) { - if s.l != nil { - err := s.l.Close() - if err != nil { - log.Printf("failed to unlock port lockfile: %v", err) - } - } -} diff --git a/builder/qemu/step_prepare_output_dir.go b/builder/qemu/step_prepare_output_dir.go deleted file mode 100644 index 26e9b69cf..000000000 --- a/builder/qemu/step_prepare_output_dir.go +++ /dev/null @@ -1,51 +0,0 @@ -package qemu - -import ( - "context" - "log" - "os" - "time" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -type stepPrepareOutputDir struct{} - -func (stepPrepareOutputDir) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) - ui := state.Get("ui").(packersdk.Ui) - - if _, err := os.Stat(config.OutputDir); err == nil && config.PackerForce { - ui.Say("Deleting previous output directory...") - os.RemoveAll(config.OutputDir) - } - - if err := os.MkdirAll(config.OutputDir, 0755); err != nil { - state.Put("error", err) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (stepPrepareOutputDir) Cleanup(state multistep.StateBag) { - _, cancelled := state.GetOk(multistep.StateCancelled) - _, halted := state.GetOk(multistep.StateHalted) - - if cancelled || halted { - config := state.Get("config").(*Config) - ui := state.Get("ui").(packersdk.Ui) - - ui.Say("Deleting output directory...") - for i := 0; i < 5; i++ { - err := os.RemoveAll(config.OutputDir) - if err == nil { - break - } - - log.Printf("Error removing output dir: %s", err) - time.Sleep(2 * time.Second) - } - } -} diff --git a/builder/qemu/step_resize_disk.go b/builder/qemu/step_resize_disk.go deleted file mode 100644 index 65b9592af..000000000 --- a/builder/qemu/step_resize_disk.go +++ /dev/null @@ -1,60 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "path/filepath" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// This step resizes the virtual disk that will be used as the -// hard drive for the virtual machine. -type stepResizeDisk struct { - DiskCompression bool - DiskImage bool - Format string - OutputDir string - SkipResizeDisk bool - VMName string - DiskSize string - - QemuImgArgs QemuImgArgs -} - -func (s *stepResizeDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) - ui := state.Get("ui").(packersdk.Ui) - path := filepath.Join(s.OutputDir, s.VMName) - - command := s.buildResizeCommand(path) - - if s.DiskImage == false || s.SkipResizeDisk == true { - return multistep.ActionContinue - } - - ui.Say("Resizing hard drive...") - if err := driver.QemuImg(command...); err != nil { - err := fmt.Errorf("Error creating hard drive: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (s *stepResizeDisk) buildResizeCommand(path string) []string { - command := []string{"resize", "-f", s.Format} - - // add user-provided convert args - command = append(command, s.QemuImgArgs.Resize...) - - // Add file and size - command = append(command, path, s.DiskSize) - - return command -} - -func (s *stepResizeDisk) Cleanup(state multistep.StateBag) {} diff --git a/builder/qemu/step_resize_disk_test.go b/builder/qemu/step_resize_disk_test.go deleted file mode 100644 index e0e1a23b7..000000000 --- a/builder/qemu/step_resize_disk_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/stretchr/testify/assert" -) - -func TestStepResizeDisk_Skips(t *testing.T) { - testConfigs := []*Config{ - &Config{ - DiskImage: false, - SkipResizeDisk: false, - }, - &Config{ - DiskImage: false, - SkipResizeDisk: true, - }, - } - for _, config := range testConfigs { - state := testState(t) - driver := state.Get("driver").(*DriverMock) - - state.Put("config", config) - step := new(stepResizeDisk) - - // 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 len(driver.QemuImgCalls) > 0 { - t.Fatal("should NOT have called qemu-img") - } - } -} - -func Test_buildResizeCommand(t *testing.T) { - type testCase struct { - Step *stepResizeDisk - Expected []string - Reason string - } - testcases := []testCase{ - { - &stepResizeDisk{ - Format: "qcow2", - DiskSize: "1234M", - }, - []string{"resize", "-f", "qcow2", "source.qcow", "1234M"}, - "no extra args", - }, - { - &stepResizeDisk{ - Format: "qcow2", - DiskSize: "1234M", - QemuImgArgs: QemuImgArgs{ - Resize: []string{"-foo", "bar"}, - }, - }, - []string{"resize", "-f", "qcow2", "-foo", "bar", "source.qcow", "1234M"}, - "one set of extra args", - }, - } - - for _, tc := range testcases { - command := tc.Step.buildResizeCommand("source.qcow") - - assert.Equal(t, command, tc.Expected, - fmt.Sprintf("%s. Expected %#v", tc.Reason, tc.Expected)) - } -} diff --git a/builder/qemu/step_run.go b/builder/qemu/step_run.go deleted file mode 100644 index 37239396f..000000000 --- a/builder/qemu/step_run.go +++ /dev/null @@ -1,400 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "log" - "path/filepath" - "strings" - - "github.com/hashicorp/go-version" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/template/interpolate" -) - -// stepRun runs the virtual machine -type stepRun struct { - DiskImage bool - - atLeastVersion2 bool - ui packersdk.Ui -} - -func (s *stepRun) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) - driver := state.Get("driver").(Driver) - s.ui = state.Get("ui").(packersdk.Ui) - - // Figure out version of qemu; store on step for later use - rawVersion, err := driver.Version() - if err != nil { - err := fmt.Errorf("Error determining qemu version: %s", err) - s.ui.Error(err.Error()) - return multistep.ActionHalt - } - qemuVersion, err := version.NewVersion(rawVersion) - if err != nil { - err := fmt.Errorf("Error parsing qemu version: %s", err) - s.ui.Error(err.Error()) - return multistep.ActionHalt - } - v2 := version.Must(version.NewVersion("2.0")) - - s.atLeastVersion2 = qemuVersion.GreaterThanOrEqual(v2) - - // Generate the qemu command - command, err := s.getCommandArgs(config, state) - if err != nil { - err := fmt.Errorf("Error processing QemuArgs: %s", err) - s.ui.Error(err.Error()) - return multistep.ActionHalt - } - - // run the qemu command - if err := driver.Qemu(command...); err != nil { - err := fmt.Errorf("Error launching VM: %s", err) - s.ui.Error(err.Error()) - return multistep.ActionHalt - } - - return multistep.ActionContinue -} - -func (s *stepRun) Cleanup(state multistep.StateBag) { - driver := state.Get("driver").(Driver) - ui := state.Get("ui").(packersdk.Ui) - - if err := driver.Stop(); err != nil { - ui.Error(fmt.Sprintf("Error shutting down VM: %s", err)) - } -} - -func (s *stepRun) getDefaultArgs(config *Config, state multistep.StateBag) map[string]interface{} { - - defaultArgs := make(map[string]interface{}) - - // Configure "boot" arguement - // Run command is different depending whether we're booting from an - // installation CD or a pre-baked image - bootDrive := "once=d" - message := "Starting VM, booting from CD-ROM" - if s.DiskImage { - bootDrive = "c" - message = "Starting VM, booting disk image" - } - s.ui.Say(message) - defaultArgs["-boot"] = bootDrive - - // configure "-qmp" arguments - if config.QMPEnable { - defaultArgs["-qmp"] = fmt.Sprintf("unix:%s,server,nowait", config.QMPSocketPath) - } - - // configure "-name" arguments - defaultArgs["-name"] = config.VMName - - // Configure "-machine" arguments - if config.Accelerator == "none" { - defaultArgs["-machine"] = fmt.Sprintf("type=%s", config.MachineType) - s.ui.Message("WARNING: The VM will be started with no hardware acceleration.\n" + - "The installation may take considerably longer to finish.\n") - } else { - defaultArgs["-machine"] = fmt.Sprintf("type=%s,accel=%s", - config.MachineType, config.Accelerator) - } - - // Firmware - if config.Firmware != "" { - defaultArgs["-bios"] = config.Firmware - } - - // Configure "-netdev" arguments - defaultArgs["-netdev"] = fmt.Sprintf("bridge,id=user.0,br=%s", config.NetBridge) - if config.NetBridge == "" { - defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0") - if config.CommConfig.Comm.Type != "none" { - commHostPort := state.Get("commHostPort").(int) - defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0,hostfwd=tcp::%v-:%d", commHostPort, config.CommConfig.Comm.Port()) - } - } - - // Configure "-vnc" arguments - // vncPort is always set in stepConfigureVNC, so we don't need to - // defensively assert - vncPort := state.Get("vnc_port").(int) - vncIP := config.VNCBindAddress - - vncRealAddress := fmt.Sprintf("%s:%d", vncIP, vncPort) - vncPort = vncPort - 5900 - vncArgs := fmt.Sprintf("%s:%d", vncIP, vncPort) - if config.VNCUsePassword { - vncArgs = fmt.Sprintf("%s:%d,password", vncIP, vncPort) - } - defaultArgs["-vnc"] = vncArgs - - // Track the connection for the user - vncPass, _ := state.Get("vnc_password").(string) - - message = getVncConnectionMessage(config.Headless, vncRealAddress, vncPass) - if message != "" { - s.ui.Message(message) - } - - // Configure "-m" memory argument - defaultArgs["-m"] = fmt.Sprintf("%dM", config.MemorySize) - - // Configure "-smp" processor hardware arguments - if config.CpuCount > 1 { - defaultArgs["-smp"] = fmt.Sprintf("cpus=%d,sockets=%d", config.CpuCount, config.CpuCount) - } - - // Configure "-fda" floppy disk attachment - if floppyPathRaw, ok := state.GetOk("floppy_path"); ok { - defaultArgs["-fda"] = floppyPathRaw.(string) - } else { - log.Println("Qemu Builder has no floppy files, not attaching a floppy.") - } - - // Configure GUI display - if !config.Headless { - if s.atLeastVersion2 { - // FIXME: "none" is a valid display option in qemu but we have - // departed from the qemu usage here to instaed mean "let qemu - // set a reasonable default". We need to deprecate this behavior - // and let users just set "UseDefaultDisplay" if they want to let - // qemu do its thing. - if len(config.Display) > 0 && config.Display != "none" { - defaultArgs["-display"] = config.Display - } else if !config.UseDefaultDisplay { - defaultArgs["-display"] = "gtk" - } - } else { - s.ui.Message("WARNING: The version of qemu on your host doesn't support display mode.\n" + - "The display parameter will be ignored.") - } - } - - deviceArgs, driveArgs := s.getDeviceAndDriveArgs(config, state) - defaultArgs["-device"] = deviceArgs - defaultArgs["-drive"] = driveArgs - - return defaultArgs -} - -func getVncConnectionMessage(headless bool, vnc string, vncPass string) string { - // Configure GUI display - if headless { - if vnc == "" { - return "The VM will be run headless, without a GUI, as configured.\n" + - "If the run isn't succeeding as you expect, please enable the GUI\n" + - "to inspect the progress of the build." - } - - if vncPass != "" { - return fmt.Sprintf( - "The VM will be run headless, without a GUI. If you want to\n"+ - "view the screen of the VM, connect via VNC to vnc://%s\n"+ - "with the password: %s", vnc, vncPass) - } - - return fmt.Sprintf( - "The VM will be run headless, without a GUI. If you want to\n"+ - "view the screen of the VM, connect via VNC without a password to\n"+ - "vnc://%s", vnc) - } - return "" -} - -func (s *stepRun) getDeviceAndDriveArgs(config *Config, state multistep.StateBag) ([]string, []string) { - var deviceArgs []string - var driveArgs []string - - vmName := config.VMName - imgPath := filepath.Join(config.OutputDir, vmName) - - // Configure virtual hard drives - if s.atLeastVersion2 { - drivesToAttach := []string{} - - if v, ok := state.GetOk("qemu_disk_paths"); ok { - diskFullPaths := v.([]string) - drivesToAttach = append(drivesToAttach, diskFullPaths...) - } - - for i, drivePath := range drivesToAttach { - driveArgumentString := fmt.Sprintf("file=%s,if=%s,cache=%s,discard=%s,format=%s", drivePath, config.DiskInterface, config.DiskCache, config.DiskDiscard, config.Format) - if config.DiskInterface == "virtio-scsi" { - // TODO: Megan: Remove this conditional. This, and the code - // under the TODO below, reproduce the old behavior. While it - // may be broken, the goal of this commit is to refactor in a way - // that creates a result that is testably the same as the old - // code. A pr will follow fixing this broken behavior. - if i == 0 { - deviceArgs = append(deviceArgs, fmt.Sprintf("virtio-scsi-pci,id=scsi%d", i)) - } - // TODO: Megan: When you remove above conditional, - // set deviceArgs = append(deviceArgs, fmt.Sprintf("scsi-hd,bus=scsi%d.0,drive=drive%d", i, i)) - deviceArgs = append(deviceArgs, fmt.Sprintf("scsi-hd,bus=scsi0.0,drive=drive%d", i)) - driveArgumentString = fmt.Sprintf("if=none,file=%s,id=drive%d,cache=%s,discard=%s,format=%s", drivePath, i, config.DiskCache, config.DiskDiscard, config.Format) - } - if config.DetectZeroes != "off" { - driveArgumentString = fmt.Sprintf("%s,detect-zeroes=%s", driveArgumentString, config.DetectZeroes) - } - driveArgs = append(driveArgs, driveArgumentString) - } - } else { - driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=%s,cache=%s,format=%s", imgPath, config.DiskInterface, config.DiskCache, config.Format)) - } - - deviceArgs = append(deviceArgs, fmt.Sprintf("%s,netdev=user.0", config.NetDevice)) - - // Configure virtual CDs - cdPaths := []string{} - // Add the installation CD to the run command - if !config.DiskImage { - isoPath := state.Get("iso_path").(string) - cdPaths = append(cdPaths, isoPath) - } - // Add our custom CD created from cd_files, if it exists - cdFilesPath, ok := state.Get("cd_path").(string) - if ok { - if cdFilesPath != "" { - cdPaths = append(cdPaths, cdFilesPath) - } - } - for i, cdPath := range cdPaths { - if config.CDROMInterface == "" { - driveArgs = append(driveArgs, fmt.Sprintf("file=%s,media=cdrom", cdPath)) - } else if config.CDROMInterface == "virtio-scsi" { - driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=none,index=%d,id=cdrom%d,media=cdrom", cdPath, i, i)) - deviceArgs = append(deviceArgs, "virtio-scsi-device", fmt.Sprintf("scsi-cd,drive=cdrom%d", i)) - } else { - driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=%s,index=%d,id=cdrom%d,media=cdrom", cdPath, config.CDROMInterface, i, i)) - } - } - - return deviceArgs, driveArgs -} - -func (s *stepRun) applyUserOverrides(defaultArgs map[string]interface{}, config *Config, state multistep.StateBag) ([]string, error) { - // Done setting up defaults; time to process user args and defaults together - // and generate output args - - inArgs := make(map[string][]string) - if len(config.QemuArgs) > 0 { - s.ui.Say("Overriding default Qemu arguments with qemuargs template option...") - - commHostPort := 0 - if config.CommConfig.Comm.Type != "none" { - if v, ok := state.GetOk("commHostPort"); ok { - commHostPort = v.(int) - } - } - httpIp := state.Get("http_ip").(string) - httpPort := state.Get("http_port").(int) - - type qemuArgsTemplateData struct { - HTTPIP string - HTTPPort int - HTTPDir string - HTTPContent map[string]string - OutputDir string - Name string - SSHHostPort int - } - - ictx := config.ctx - ictx.Data = qemuArgsTemplateData{ - HTTPIP: httpIp, - HTTPPort: httpPort, - HTTPDir: config.HTTPDir, - HTTPContent: config.HTTPContent, - OutputDir: config.OutputDir, - Name: config.VMName, - SSHHostPort: commHostPort, - } - - // Interpolate each string in qemuargs - newQemuArgs, err := processArgs(config.QemuArgs, &ictx) - if err != nil { - return nil, err - } - - // Qemu supports multiple appearances of the same switch. This means - // each key in the args hash will have an array of string values - for _, qemuArgs := range newQemuArgs { - key := qemuArgs[0] - val := strings.Join(qemuArgs[1:], "") - if _, ok := inArgs[key]; !ok { - inArgs[key] = make([]string, 0) - } - if len(val) > 0 { - inArgs[key] = append(inArgs[key], val) - } - } - } - - // get any remaining missing default args from the default settings - for key := range defaultArgs { - if _, ok := inArgs[key]; !ok { - arg := make([]string, 1) - switch defaultArgs[key].(type) { - case string: - arg[0] = defaultArgs[key].(string) - case []string: - arg = defaultArgs[key].([]string) - } - inArgs[key] = arg - } - } - - // Check if we are missing the netDevice #6804 - if x, ok := inArgs["-device"]; ok { - if !strings.Contains(strings.Join(x, ""), config.NetDevice) { - inArgs["-device"] = append(inArgs["-device"], fmt.Sprintf("%s,netdev=user.0", config.NetDevice)) - } - } - - // Flatten to array of strings - outArgs := make([]string, 0) - for key, values := range inArgs { - if len(values) > 0 { - for idx := range values { - outArgs = append(outArgs, key, values[idx]) - } - } else { - outArgs = append(outArgs, key) - } - } - - return outArgs, nil -} - -func (s *stepRun) getCommandArgs(config *Config, state multistep.StateBag) ([]string, error) { - defaultArgs := s.getDefaultArgs(config, state) - - return s.applyUserOverrides(defaultArgs, config, state) -} - -func processArgs(args [][]string, ctx *interpolate.Context) ([][]string, error) { - var err error - - if args == nil { - return make([][]string, 0), err - } - - newArgs := make([][]string, len(args)) - for argsIdx, rowArgs := range args { - parms := make([]string, len(rowArgs)) - newArgs[argsIdx] = parms - for i, parm := range rowArgs { - parms[i], err = interpolate.Render(parm, ctx) - if err != nil { - return nil, err - } - } - } - - return newArgs, err -} diff --git a/builder/qemu/step_run_test.go b/builder/qemu/step_run_test.go deleted file mode 100644 index 2139c8b8a..000000000 --- a/builder/qemu/step_run_test.go +++ /dev/null @@ -1,710 +0,0 @@ -package qemu - -import ( - "fmt" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/communicator" - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/stretchr/testify/assert" -) - -func runTestState(t *testing.T, config *Config) multistep.StateBag { - state := new(multistep.BasicStateBag) - state.Put("config", config) - - d := new(DriverMock) - d.VersionResult = "3.0.0" - state.Put("driver", d) - - state.Put("commHostPort", 5000) - state.Put("floppy_path", "fake_floppy_path") - state.Put("http_ip", "127.0.0.1") - state.Put("http_port", 1234) - state.Put("iso_path", "/path/to/test.iso") - state.Put("qemu_disk_paths", []string{}) - state.Put("vnc_port", 5905) - state.Put("vnc_password", "fake_vnc_password") - - return state -} - -func Test_UserOverrides(t *testing.T) { - type testCase struct { - Config *Config - Expected []string - Reason string - } - testcases := []testCase{ - { - &Config{ - HTTPConfig: commonsteps.HTTPConfig{ - HTTPDir: "http/directory", - }, - OutputDir: "output/directory", - VMName: "myvm", - QemuArgs: [][]string{ - {"-randomflag1", "{{.HTTPIP}}-{{.HTTPPort}}-{{.HTTPDir}}"}, - {"-randomflag2", "{{.OutputDir}}-{{.Name}}"}, - }, - }, - []string{ - "-display", "gtk", - "-drive", "file=/path/to/test.iso,media=cdrom", - "-randomflag1", "127.0.0.1-1234-http/directory", - "-randomflag2", "output/directory-myvm", - "-device", ",netdev=user.0", - }, - "Test that interpolation overrides work.", - }, - { - &Config{ - VMName: "myvm", - QemuArgs: [][]string{{"-display", "partydisplay"}}, - }, - []string{ - "-display", "partydisplay", - "-drive", "file=/path/to/test.iso,media=cdrom", - "-device", ",netdev=user.0", - }, - "User input overrides default, rest is populated as normal", - }, - { - &Config{ - VMName: "myvm", - NetDevice: "mynetdevice", - QemuArgs: [][]string{{"-device", "somerandomdevice"}}, - }, - []string{ - "-display", "gtk", - "-device", "somerandomdevice", - "-device", "mynetdevice,netdev=user.0", - "-drive", "file=/path/to/test.iso,media=cdrom", - }, - "Net device gets added", - }, - } - - for _, tc := range testcases { - state := runTestState(t, tc.Config) - - step := &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - } - args, err := step.getCommandArgs(tc.Config, state) - if err != nil { - t.Fatalf("should not have an error getting args. Error: %s", err) - } - - expected := append([]string{ - "-m", "0M", - "-boot", "once=d", - "-fda", "fake_floppy_path", - "-name", "myvm", - "-netdev", "user,id=user.0,hostfwd=tcp::5000-:0", - "-vnc", ":5", - "-machine", "type=,accel="}, - tc.Expected...) - - assert.ElementsMatch(t, args, expected, - fmt.Sprintf("%s, \nRecieved: %#v", tc.Reason, args)) - } - -} - -func Test_DriveAndDeviceArgs(t *testing.T) { - type testCase struct { - Config *Config - ExtraState map[string]interface{} - Step *stepRun - Expected []string - Reason string - } - - testcases := []testCase{ - { - &Config{}, - map[string]interface{}{}, - &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{ - "-display", "gtk", - "-boot", "once=d", - "-drive", "file=/path/to/test.iso,media=cdrom", - }, - "Boot value should default to once=d when diskImage isn't set", - }, - { - &Config{ - DiskImage: true, - DiskInterface: "virtio-scsi", - - OutputDir: "path_to_output", - DiskCache: "writeback", - Format: "qcow2", - DetectZeroes: "off", - }, - map[string]interface{}{ - "cd_path": "fake_cd_path.iso", - "qemu_disk_paths": []string{"path_to_output"}, - }, - &stepRun{ - DiskImage: true, - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{ - "-display", "gtk", - "-boot", "c", - "-device", "virtio-scsi-pci,id=scsi0", - "-device", "scsi-hd,bus=scsi0.0,drive=drive0", - "-drive", "if=none,file=path_to_output,id=drive0,cache=writeback,discard=,format=qcow2", - "-drive", "file=fake_cd_path.iso,media=cdrom", - }, - "virtio-scsi interface, DiskImage true, extra cdrom, detectZeroes off", - }, - { - &Config{ - DiskImage: true, - DiskInterface: "virtio-scsi", - - OutputDir: "path_to_output", - DiskCache: "writeback", - Format: "qcow2", - DetectZeroes: "on", - }, - map[string]interface{}{ - "cd_path": "fake_cd_path.iso", - "qemu_disk_paths": []string{"path_to_output"}, - }, - &stepRun{ - DiskImage: true, - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{ - "-display", "gtk", - "-boot", "c", - "-device", "virtio-scsi-pci,id=scsi0", - "-device", "scsi-hd,bus=scsi0.0,drive=drive0", - "-drive", "if=none,file=path_to_output,id=drive0,cache=writeback,discard=,format=qcow2,detect-zeroes=on", - "-drive", "file=fake_cd_path.iso,media=cdrom", - }, - "virtio-scsi interface, DiskImage true, extra cdrom, detectZeroes on", - }, - { - &Config{ - DiskInterface: "virtio-scsi", - - OutputDir: "path_to_output", - DiskCache: "writeback", - Format: "qcow2", - DetectZeroes: "off", - }, - map[string]interface{}{ - "cd_path": "fake_cd_path.iso", - // when disk image is false, we will always have at least one - // disk path: the one we create to be the main disk. - "qemu_disk_paths": []string{"qemupath1", "qemupath2"}, - }, - &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{ - "-display", "gtk", - "-boot", "once=d", - "-device", "virtio-scsi-pci,id=scsi0", - "-device", "scsi-hd,bus=scsi0.0,drive=drive0", - "-device", "scsi-hd,bus=scsi0.0,drive=drive1", - "-drive", "if=none,file=qemupath1,id=drive0,cache=writeback,discard=,format=qcow2", - "-drive", "if=none,file=qemupath2,id=drive1,cache=writeback,discard=,format=qcow2", - "-drive", "file=/path/to/test.iso,media=cdrom", - "-drive", "file=fake_cd_path.iso,media=cdrom", - }, - "virtio-scsi interface, bootable iso, cdrom", - }, - { - &Config{ - DiskInterface: "virtio-scsi", - - OutputDir: "path_to_output", - DiskCache: "writeback", - Format: "qcow2", - DetectZeroes: "on", - }, - map[string]interface{}{ - "cd_path": "fake_cd_path.iso", - // when disk image is false, we will always have at least one - // disk path: the one we create to be the main disk. - "qemu_disk_paths": []string{"qemupath1", "qemupath2"}, - }, - &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{ - "-display", "gtk", - "-boot", "once=d", - "-device", "virtio-scsi-pci,id=scsi0", - "-device", "scsi-hd,bus=scsi0.0,drive=drive0", - "-device", "scsi-hd,bus=scsi0.0,drive=drive1", - "-drive", "if=none,file=qemupath1,id=drive0,cache=writeback,discard=,format=qcow2,detect-zeroes=on", - "-drive", "if=none,file=qemupath2,id=drive1,cache=writeback,discard=,format=qcow2,detect-zeroes=on", - "-drive", "file=/path/to/test.iso,media=cdrom", - "-drive", "file=fake_cd_path.iso,media=cdrom", - }, - "virtio-scsi interface, DiskImage false, extra cdrom, detect zeroes on", - }, - { - &Config{ - DiskInterface: "virtio-scsi", - - OutputDir: "path_to_output", - DiskCache: "writeback", - Format: "qcow2", - }, - map[string]interface{}{ - // when disk image is false, we will always have at least one - // disk path: the one we create to be the main disk. - "qemu_disk_paths": []string{"output/dir/path/mydisk.qcow2"}, - }, - &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{ - "-display", "gtk", - "-boot", "once=d", - "-device", "virtio-scsi-pci,id=scsi0", - "-device", "scsi-hd,bus=scsi0.0,drive=drive0", - "-drive", "if=none,file=output/dir/path/mydisk.qcow2,id=drive0,cache=writeback,discard=,format=qcow2,detect-zeroes=", - "-drive", "file=/path/to/test.iso,media=cdrom", - }, - "virtio-scsi interface, DiskImage false, no extra disks or cds", - }, - { - &Config{}, - map[string]interface{}{ - "cd_path": "fake_cd_path.iso", - "qemu_disk_paths": []string{"output/dir/path/mydisk.qcow2"}, - }, - &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{ - "-display", "gtk", - "-boot", "once=d", - "-drive", "file=output/dir/path/mydisk.qcow2,if=,cache=,discard=,format=,detect-zeroes=", - "-drive", "file=/path/to/test.iso,media=cdrom", - "-drive", "file=fake_cd_path.iso,media=cdrom", - }, - "cd_path is set and DiskImage is false", - }, - { - &Config{}, - map[string]interface{}{ - // when disk image is false, we will always have at least one - // disk path: the one we create to be the main disk. - "qemu_disk_paths": []string{"output/dir/path/mydisk.qcow2"}, - }, - &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{ - "-display", "gtk", - "-boot", "once=d", - "-drive", "file=output/dir/path/mydisk.qcow2,if=,cache=,discard=,format=,detect-zeroes=", - "-drive", "file=/path/to/test.iso,media=cdrom", - }, - "empty config", - }, - { - &Config{ - OutputDir: "path_to_output", - DiskInterface: "virtio", - DiskCache: "writeback", - Format: "qcow2", - }, - map[string]interface{}{ - // when disk image is false, we will always have at least one - // disk path: the one we create to be the main disk. - "qemu_disk_paths": []string{"output/dir/path/mydisk.qcow2"}, - }, - &stepRun{ - atLeastVersion2: false, - ui: packersdk.TestUi(t), - }, - []string{ - "-boot", "once=d", - "-drive", "file=path_to_output,if=virtio,cache=writeback,format=qcow2", - "-drive", "file=/path/to/test.iso,media=cdrom", - }, - "version less than 2", - }, - { - &Config{ - OutputDir: "path_to_output", - DiskInterface: "virtio", - DiskCache: "writeback", - Format: "qcow2", - }, - map[string]interface{}{ - "cd_path": "fake_cd_path.iso", - "qemu_disk_paths": []string{"qemupath1", "qemupath2"}, - }, - &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{ - "-display", "gtk", - "-boot", "once=d", - "-drive", "file=qemupath1,if=virtio,cache=writeback,discard=,format=qcow2,detect-zeroes=", - "-drive", "file=qemupath2,if=virtio,cache=writeback,discard=,format=qcow2,detect-zeroes=", - "-drive", "file=fake_cd_path.iso,media=cdrom", - "-drive", "file=/path/to/test.iso,media=cdrom", - }, - "virtio interface with extra disks", - }, - { - &Config{ - DiskImage: true, - OutputDir: "path_to_output", - DiskInterface: "virtio", - DiskCache: "writeback", - Format: "qcow2", - }, - map[string]interface{}{ - "cd_path": "fake_cd_path.iso", - "qemu_disk_paths": []string{"path_to_output"}, - }, - &stepRun{ - DiskImage: true, - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{ - "-display", "gtk", - "-boot", "c", - "-drive", "file=path_to_output,if=virtio,cache=writeback,discard=,format=qcow2,detect-zeroes=", - "-drive", "file=fake_cd_path.iso,media=cdrom", - }, - "virtio interface with disk image", - }, - } - for _, tc := range testcases { - state := runTestState(t, &Config{}) - for k, v := range tc.ExtraState { - state.Put(k, v) - } - - args, err := tc.Step.getCommandArgs(tc.Config, state) - if err != nil { - t.Fatalf("should not have an error getting args. Error: %s", err) - } - - expected := append([]string{ - "-m", "0M", - "-fda", "fake_floppy_path", - "-name", "", - "-netdev", "user,id=user.0,hostfwd=tcp::5000-:0", - "-vnc", ":5", - "-machine", "type=,accel=", - "-device", ",netdev=user.0"}, - tc.Expected...) - - assert.ElementsMatch(t, args, expected, - fmt.Sprintf("%s, \nRecieved: %#v", tc.Reason, args)) - } -} - -func Test_OptionalConfigOptionsGetSet(t *testing.T) { - c := &Config{ - VNCUsePassword: true, - QMPEnable: true, - QMPSocketPath: "qmp_path", - VMName: "MyFancyName", - MachineType: "pc", - Accelerator: "hvf", - } - - state := runTestState(t, c) - step := &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - } - args, err := step.getCommandArgs(c, state) - if err != nil { - t.Fatalf("should not have an error getting args. Error: %s", err) - } - - expected := []string{ - "-display", "gtk", - "-m", "0M", - "-boot", "once=d", - "-fda", "fake_floppy_path", - "-name", "MyFancyName", - "-netdev", "user,id=user.0,hostfwd=tcp::5000-:0", - "-vnc", ":5,password", - "-machine", "type=pc,accel=hvf", - "-device", ",netdev=user.0", - "-drive", "file=/path/to/test.iso,media=cdrom", - "-qmp", "unix:qmp_path,server,nowait", - } - - assert.ElementsMatch(t, args, expected, "password flag should be set, and d drive should be set: %s", args) -} - -// Tests for presence of Packer-generated arguments. Doesn't test that -// arguments which shouldn't be there are absent. -func Test_Defaults(t *testing.T) { - type testCase struct { - Config *Config - ExtraState map[string]interface{} - Step *stepRun - Expected []string - Reason string - } - - testcases := []testCase{ - { - &Config{}, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-boot", "once=d"}, - "Boot value should default to once=d", - }, - { - &Config{}, - map[string]interface{}{}, - &stepRun{ - DiskImage: true, - ui: packersdk.TestUi(t), - }, - []string{"-boot", "c"}, - "Boot value should be set to c when DiskImage is set on step", - }, - { - &Config{ - QMPEnable: true, - QMPSocketPath: "/path/to/socket", - }, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-qmp", "unix:/path/to/socket,server,nowait"}, - "Args should contain -qmp when qmp_enable is set", - }, - { - &Config{ - QMPEnable: true, - }, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-qmp", "unix:,server,nowait"}, - "Args contain -qmp even when socket path isn't set, if qmp enabled", - }, - { - &Config{ - VMName: "partyname", - }, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-name", "partyname"}, - "Name is set from config", - }, - { - &Config{}, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-name", ""}, - "Name is set from config, even when name is blank (which won't " + - "happen for real thanks to defaulting in build prepare)", - }, - { - &Config{ - Accelerator: "none", - MachineType: "fancymachine", - }, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-machine", "type=fancymachine"}, - "Don't add accelerator tag when no accelerator is set.", - }, - { - &Config{ - Accelerator: "kvm", - MachineType: "fancymachine", - }, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-machine", "type=fancymachine,accel=kvm"}, - "Add accelerator tag when accelerator is set.", - }, - { - &Config{ - NetBridge: "fakebridge", - }, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-netdev", "bridge,id=user.0,br=fakebridge"}, - "Add netbridge tag when netbridge is set.", - }, - { - &Config{ - CommConfig: CommConfig{ - Comm: communicator.Config{ - Type: "none", - }, - }, - }, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-netdev", "user,id=user.0"}, - "No host forwarding when no net bridge and no communicator", - }, - { - &Config{ - CommConfig: CommConfig{ - Comm: communicator.Config{ - Type: "ssh", - SSH: communicator.SSH{ - SSHPort: 4567, - }, - }, - }, - }, - map[string]interface{}{ - "commHostPort": 1111, - }, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-netdev", "user,id=user.0,hostfwd=tcp::1111-:4567"}, - "Host forwarding when a communicator is configured", - }, - { - &Config{ - VNCBindAddress: "1.1.1.1", - }, - map[string]interface{}{ - "vnc_port": 5959, - }, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-vnc", "1.1.1.1:59"}, - "no VNC password should be set", - }, - { - &Config{ - VNCBindAddress: "1.1.1.1", - VNCUsePassword: true, - }, - map[string]interface{}{ - "vnc_port": 5959, - }, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-vnc", "1.1.1.1:59,password"}, - "VNC password should be set", - }, - { - &Config{ - MemorySize: 2345, - }, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-m", "2345M"}, - "Memory is set, with unit M", - }, - { - &Config{ - CpuCount: 2, - }, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-smp", "cpus=2,sockets=2"}, - "both cpus and sockets are set to config's CpuCount", - }, - { - &Config{ - CpuCount: 2, - }, - map[string]interface{}{}, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-smp", "cpus=2,sockets=2"}, - "both cpus and sockets are set to config's CpuCount", - }, - { - &Config{ - CpuCount: 2, - }, - map[string]interface{}{ - "floppy_path": "/path/to/floppy", - }, - &stepRun{ui: packersdk.TestUi(t)}, - []string{"-fda", "/path/to/floppy"}, - "floppy path should be set under fda flag, when it exists", - }, - { - &Config{ - Headless: false, - Display: "fakedisplay", - UseDefaultDisplay: false, - }, - map[string]interface{}{}, - &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{"-display", "fakedisplay"}, - "Display option should value config display", - }, - { - &Config{ - Headless: false, - }, - map[string]interface{}{}, - &stepRun{ - atLeastVersion2: true, - ui: packersdk.TestUi(t), - }, - []string{"-display", "gtk"}, - "Display option should default to gtk", - }, - } - - for _, tc := range testcases { - state := runTestState(t, &Config{}) - for k, v := range tc.ExtraState { - state.Put(k, v) - } - - args, err := tc.Step.getCommandArgs(tc.Config, state) - if err != nil { - t.Fatalf("should not have an error getting args. Error: %s", err) - } - if !matchArgument(args, tc.Expected) { - t.Fatalf("Couldn't find %#v in result. Got: %#v, Reason: %s", - tc.Expected, args, tc.Reason) - } - } -} - -// This test makes sure that arguments don't end up in the final boot command -// if they aren't configured in the config. -// func TestDefaultsAbsentValues(t *testing.T) {} -func matchArgument(actual []string, expected []string) bool { - key := expected[0] - for i, k := range actual { - if key == k { - if expected[1] == actual[i+1] { - return true - } - } - } - return false -} diff --git a/builder/qemu/step_set_iso.go b/builder/qemu/step_set_iso.go deleted file mode 100644 index cc4054d46..000000000 --- a/builder/qemu/step_set_iso.go +++ /dev/null @@ -1,54 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "net/http" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - "github.com/hashicorp/packer-plugin-sdk/net" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// This step set iso_patch to available url -type stepSetISO struct { - ResultKey string - Url []string -} - -func (s *stepSetISO) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packersdk.Ui) - - iso_path := "" - - for _, url := range s.Url { - req, err := http.NewRequest("HEAD", url, nil) - if err != nil { - continue - } - - req.Header.Set("User-Agent", "Packer") - - httpClient := net.HttpClientWithEnvironmentProxy() - - res, err := httpClient.Do(req) - if err == nil && (res.StatusCode >= 200 && res.StatusCode < 300) { - if res.Header.Get("Accept-Ranges") == "bytes" { - iso_path = url - } - } - } - - if iso_path == "" { - err := fmt.Errorf("No byte serving support. The HTTP server must support Accept-Ranges=bytes") - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - state.Put(s.ResultKey, iso_path) - - return multistep.ActionContinue -} - -func (s *stepSetISO) Cleanup(state multistep.StateBag) {} diff --git a/builder/qemu/step_shutdown.go b/builder/qemu/step_shutdown.go deleted file mode 100644 index b7cd17777..000000000 --- a/builder/qemu/step_shutdown.go +++ /dev/null @@ -1,94 +0,0 @@ -package qemu - -import ( - "context" - "errors" - "fmt" - "log" - "time" - - "github.com/hashicorp/packer-plugin-sdk/communicator" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -// This step shuts down the machine. It first attempts to do so gracefully, -// but ultimately forcefully shuts it down if that fails. -// -// Uses: -// communicator packersdk.Communicator -// config *config -// driver Driver -// ui packersdk.Ui -// -// Produces: -// -type stepShutdown struct { - ShutdownCommand string - ShutdownTimeout time.Duration - Comm *communicator.Config -} - -func (s *stepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - driver := state.Get("driver").(Driver) - ui := state.Get("ui").(packersdk.Ui) - - if s.Comm.Type == "none" { - cancelCh := make(chan struct{}, 1) - go func() { - defer close(cancelCh) - <-time.After(s.ShutdownTimeout) - }() - ui.Say("Waiting for shutdown...") - if ok := driver.WaitForShutdown(cancelCh); ok { - log.Println("VM shut down.") - return multistep.ActionContinue - } else { - err := fmt.Errorf("Failed to shutdown") - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - if s.ShutdownCommand != "" { - comm := state.Get("communicator").(packersdk.Communicator) - ui.Say("Gracefully halting virtual machine...") - log.Printf("Executing shutdown command: %s", s.ShutdownCommand) - cmd := &packersdk.RemoteCmd{Command: s.ShutdownCommand} - if err := cmd.RunWithUi(ctx, comm, ui); err != nil { - err := fmt.Errorf("Failed to send shutdown command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - // Start the goroutine that will time out our graceful attempt - cancelCh := make(chan struct{}, 1) - go func() { - defer close(cancelCh) - <-time.After(s.ShutdownTimeout) - }() - - log.Printf("Waiting max %s for shutdown to complete", s.ShutdownTimeout) - if ok := driver.WaitForShutdown(cancelCh); !ok { - err := errors.New("Timeout while waiting for machine to shut down.") - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } else { - ui.Say("Halting the virtual machine...") - if err := driver.Stop(); err != nil { - err := fmt.Errorf("Error stopping VM: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - } - - log.Println("VM shut down.") - return multistep.ActionContinue -} - -func (s *stepShutdown) Cleanup(state multistep.StateBag) {} diff --git a/builder/qemu/step_shutdown_test.go b/builder/qemu/step_shutdown_test.go deleted file mode 100644 index 26f20cc32..000000000 --- a/builder/qemu/step_shutdown_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package qemu - -import ( - "context" - "testing" - "time" - - "github.com/hashicorp/packer-plugin-sdk/communicator" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -func Test_Shutdown_Null_success(t *testing.T) { - state := new(multistep.BasicStateBag) - state.Put("ui", packersdk.TestUi(t)) - driverMock := new(DriverMock) - driverMock.WaitForShutdownState = true - state.Put("driver", driverMock) - - step := &stepShutdown{ - ShutdownCommand: "", - ShutdownTimeout: 5 * time.Minute, - Comm: &communicator.Config{ - Type: "none", - }, - } - action := step.Run(context.TODO(), state) - if action != multistep.ActionContinue { - t.Fatalf("Should have successfully shut down.") - } - err := state.Get("error") - if err != nil { - err = err.(error) - t.Fatalf("Shutdown shouldn't have errored; err: %v", err) - } -} - -func Test_Shutdown_Null_failure(t *testing.T) { - state := new(multistep.BasicStateBag) - state.Put("ui", packersdk.TestUi(t)) - driverMock := new(DriverMock) - driverMock.WaitForShutdownState = false - state.Put("driver", driverMock) - - step := &stepShutdown{ - ShutdownCommand: "", - ShutdownTimeout: 5 * time.Minute, - Comm: &communicator.Config{ - Type: "none", - }, - } - action := step.Run(context.TODO(), state) - if action != multistep.ActionHalt { - t.Fatalf("Shouldn't have successfully shut down.") - } - err := state.Get("error") - if err == nil { - t.Fatalf("Shutdown should have errored") - } -} - -func Test_Shutdown_NoShutdownCommand(t *testing.T) { - state := new(multistep.BasicStateBag) - state.Put("ui", packersdk.TestUi(t)) - driverMock := new(DriverMock) - state.Put("driver", driverMock) - - step := &stepShutdown{ - ShutdownCommand: "", - ShutdownTimeout: 5 * time.Minute, - Comm: &communicator.Config{ - Type: "ssh", - }, - } - action := step.Run(context.TODO(), state) - if action != multistep.ActionContinue { - t.Fatalf("Should have successfully shut down.") - } - - if !driverMock.StopCalled { - t.Fatalf("should have called Stop through the driver.") - } - err := state.Get("error") - if err != nil { - err = err.(error) - t.Fatalf("Shutdown shouldn't have errored; err: %v", err) - } -} diff --git a/builder/qemu/step_test.go b/builder/qemu/step_test.go deleted file mode 100644 index 3273a4c44..000000000 --- a/builder/qemu/step_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package qemu - -import ( - "bytes" - "testing" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" -) - -func testState(t *testing.T) multistep.StateBag { - state := new(multistep.BasicStateBag) - state.Put("driver", new(DriverMock)) - state.Put("ui", &packersdk.BasicUi{ - Reader: new(bytes.Buffer), - Writer: new(bytes.Buffer), - }) - return state -} diff --git a/builder/qemu/step_type_boot_command.go b/builder/qemu/step_type_boot_command.go deleted file mode 100644 index 038f18d62..000000000 --- a/builder/qemu/step_type_boot_command.go +++ /dev/null @@ -1,139 +0,0 @@ -package qemu - -import ( - "context" - "fmt" - "log" - "net" - "time" - - "github.com/hashicorp/packer-plugin-sdk/bootcommand" - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - "github.com/hashicorp/packer-plugin-sdk/template/interpolate" - "github.com/mitchellh/go-vnc" -) - -const KeyLeftShift uint32 = 0xFFE1 - -type bootCommandTemplateData struct { - HTTPIP string - HTTPPort int - Name string -} - -// This step "types" the boot command into the VM over VNC. -// -// Uses: -// config *config -// http_port int -// ui packersdk.Ui -// vnc_port int -// -// Produces: -// -type stepTypeBootCommand struct{} - -func (s *stepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(*Config) - debug := state.Get("debug").(bool) - httpPort := state.Get("http_port").(int) - ui := state.Get("ui").(packersdk.Ui) - vncPort := state.Get("vnc_port").(int) - vncIP := config.VNCBindAddress - vncPassword := state.Get("vnc_password") - - if config.VNCConfig.DisableVNC { - log.Println("Skipping boot command step...") - return multistep.ActionContinue - } - - // Wait the for the vm to boot. - if int64(config.BootWait) > 0 { - ui.Say(fmt.Sprintf("Waiting %s for boot...", config.BootWait)) - select { - case <-time.After(config.BootWait): - break - case <-ctx.Done(): - return multistep.ActionHalt - } - } - - var pauseFn multistep.DebugPauseFn - if debug { - pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn) - } - - // Connect to VNC - ui.Say(fmt.Sprintf("Connecting to VM via VNC (%s:%d)", vncIP, vncPort)) - - nc, err := net.Dial("tcp", fmt.Sprintf("%s:%d", vncIP, vncPort)) - if err != nil { - err := fmt.Errorf("Error connecting to VNC: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - defer nc.Close() - - var auth []vnc.ClientAuth - - if vncPassword != nil && len(vncPassword.(string)) > 0 { - auth = []vnc.ClientAuth{&vnc.PasswordAuth{Password: vncPassword.(string)}} - } else { - auth = []vnc.ClientAuth{new(vnc.ClientAuthNone)} - } - - c, err := vnc.Client(nc, &vnc.ClientConfig{Auth: auth, Exclusive: false}) - if err != nil { - err := fmt.Errorf("Error handshaking with VNC: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - defer c.Close() - - log.Printf("Connected to VNC desktop: %s", c.DesktopName) - - hostIP := state.Get("http_ip").(string) - configCtx := config.ctx - configCtx.Data = &bootCommandTemplateData{ - hostIP, - httpPort, - config.VMName, - } - - d := bootcommand.NewVNCDriver(c, config.VNCConfig.BootKeyInterval) - - ui.Say("Typing the boot command over VNC...") - command, err := interpolate.Render(config.VNCConfig.FlatBootCommand(), &configCtx) - if err != nil { - err := fmt.Errorf("Error preparing boot command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - seq, err := bootcommand.GenerateExpressionSequence(command) - if err != nil { - err := fmt.Errorf("Error generating boot command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - if err := seq.Do(ctx, d); err != nil { - err := fmt.Errorf("Error running boot command: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - if pauseFn != nil { - pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command: %s", command), state) - } - - return multistep.ActionContinue -} - -func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {} diff --git a/builder/qemu/step_wait_guest_address.go b/builder/qemu/step_wait_guest_address.go deleted file mode 100644 index c15da17f0..000000000 --- a/builder/qemu/step_wait_guest_address.go +++ /dev/null @@ -1,136 +0,0 @@ -package qemu - -import ( - "bufio" - "context" - "fmt" - "log" - "os" - "strconv" - "strings" - "time" - - "github.com/hashicorp/packer-plugin-sdk/multistep" - packersdk "github.com/hashicorp/packer-plugin-sdk/packer" - - "github.com/digitalocean/go-qemu/qmp" -) - -// This step waits for the guest address to become available in the network -// bridge, then it sets the guestAddress state property. -type stepWaitGuestAddress struct { - CommunicatorType string - NetBridge string - - timeout time.Duration -} - -func (s *stepWaitGuestAddress) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packersdk.Ui) - - if s.CommunicatorType == "none" { - ui.Message("No communicator is configured -- skipping StepWaitGuestAddress") - return multistep.ActionContinue - } - if s.NetBridge == "" { - ui.Message("Not using a NetBridge -- skipping StepWaitGuestAddress") - return multistep.ActionContinue - } - - qmpMonitor := state.Get("qmp_monitor").(*qmp.SocketMonitor) - ctx, cancel := context.WithTimeout(ctx, s.timeout) - defer cancel() - - ui.Say(fmt.Sprintf("Waiting for the guest address to become available in the %s network bridge...", s.NetBridge)) - for { - guestAddress := getGuestAddress(qmpMonitor, s.NetBridge, "user.0") - if guestAddress != "" { - log.Printf("Found guest address %s", guestAddress) - state.Put("guestAddress", guestAddress) - return multistep.ActionContinue - } - select { - case <-time.After(10 * time.Second): - continue - case <-ctx.Done(): - return multistep.ActionHalt - } - } -} - -func (s *stepWaitGuestAddress) Cleanup(state multistep.StateBag) { -} - -func getGuestAddress(qmpMonitor *qmp.SocketMonitor, bridgeName string, deviceName string) string { - devices, err := getNetDevices(qmpMonitor) - if err != nil { - log.Printf("Could not retrieve QEMU QMP network device list: %v", err) - return "" - } - - for _, device := range devices { - if device.Name == deviceName { - ipAddress, _ := getDeviceIPAddress(bridgeName, device.MacAddress) - return ipAddress - } - } - - log.Printf("QEMU QMP network device %s was not found", deviceName) - return "" -} - -func getDeviceIPAddress(device string, macAddress string) (string, error) { - // this parses /proc/net/arp to retrieve the given device IP address. - // - // /proc/net/arp is normally someting alike: - // - // IP address HW type Flags HW address Mask Device - // 192.168.121.111 0x1 0x2 52:54:00:12:34:56 * virbr0 - // - - const ( - IPAddressIndex int = iota - HWTypeIndex - FlagsIndex - HWAddressIndex - MaskIndex - DeviceIndex - ) - - // see ARP flags at https://github.com/torvalds/linux/blob/v5.4/include/uapi/linux/if_arp.h#L132 - const ( - AtfCom int = 0x02 // ATF_COM (complete) - ) - - f, err := os.Open("/proc/net/arp") - if err != nil { - return "", fmt.Errorf("failed to open /proc/net/arp: %w", err) - } - defer f.Close() - - s := bufio.NewScanner(f) - s.Scan() - - for s.Scan() { - fields := strings.Fields(s.Text()) - - if device != "" && fields[DeviceIndex] != device { - continue - } - - if fields[HWAddressIndex] != macAddress { - continue - } - - flags, err := strconv.ParseInt(fields[FlagsIndex], 0, 32) - if err != nil { - return "", fmt.Errorf("failed to parse /proc/net/arp flags field %s: %w", fields[FlagsIndex], err) - } - - if int(flags)&AtfCom == AtfCom { - return fields[IPAddressIndex], nil - } - } - - return "", fmt.Errorf("could not find %s", macAddress) -} diff --git a/builder/qemu/version/version.go b/builder/qemu/version/version.go deleted file mode 100644 index 4e0039bbf..000000000 --- a/builder/qemu/version/version.go +++ /dev/null @@ -1,13 +0,0 @@ -package version - -import ( - "github.com/hashicorp/packer-plugin-sdk/version" - packerVersion "github.com/hashicorp/packer/version" -) - -var QemuPluginVersion *version.PluginVersion - -func init() { - QemuPluginVersion = version.InitializePluginVersion( - packerVersion.Version, packerVersion.VersionPrerelease) -} diff --git a/command/plugin.go b/command/plugin.go index b99a67e5d..cc93357e5 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -44,7 +44,6 @@ import ( proxmoxbuilder "github.com/hashicorp/packer/builder/proxmox" proxmoxclonebuilder "github.com/hashicorp/packer/builder/proxmox/clone" proxmoxisobuilder "github.com/hashicorp/packer/builder/proxmox/iso" - qemubuilder "github.com/hashicorp/packer/builder/qemu" scalewaybuilder "github.com/hashicorp/packer/builder/scaleway" tencentcloudcvmbuilder "github.com/hashicorp/packer/builder/tencentcloud/cvm" tritonbuilder "github.com/hashicorp/packer/builder/triton" @@ -117,7 +116,6 @@ var Builders = map[string]packersdk.Builder{ "proxmox": new(proxmoxbuilder.Builder), "proxmox-clone": new(proxmoxclonebuilder.Builder), "proxmox-iso": new(proxmoxisobuilder.Builder), - "qemu": new(qemubuilder.Builder), "scaleway": new(scalewaybuilder.Builder), "tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder), "triton": new(tritonbuilder.Builder), diff --git a/website/content/docs/builders/qemu.mdx b/website/content/docs/builders/qemu.mdx deleted file mode 100644 index cd32119d0..000000000 --- a/website/content/docs/builders/qemu.mdx +++ /dev/null @@ -1,219 +0,0 @@ ---- -modeline: | - vim: set ft=pandoc: -description: | - The Qemu Packer builder is able to create KVM virtual machine images. -page_title: QEMU - Builders ---- - -# QEMU Builder - -Type: `qemu` -Artifact BuilderId: `transcend.qemu` - -The Qemu Packer builder is able to create [KVM](http://www.linux-kvm.org) virtual -machine images. - -The builder builds a virtual machine by creating a new virtual machine from -scratch, booting it, installing an OS, rebooting the machine with the boot media -as the virtual hard drive, provisioning software within the OS, then shutting it -down. The result of the Qemu builder is a directory containing the image file -necessary to run the virtual machine on KVM. - -## Basic Example - -Here is a basic example. This example is functional so long as you fixup paths -to files, URLS for ISOs and checksums. - - - - -```json -{ - "builders": [ - { - "type": "qemu", - "iso_url": "http://mirror.raystedman.net/centos/6/isos/x86_64/CentOS-6.9-x86_64-minimal.iso", - "iso_checksum": "md5:af4a1640c0c6f348c6c41f1ea9e192a2", - "output_directory": "output_centos_tdhtest", - "shutdown_command": "echo 'packer' | sudo -S shutdown -P now", - "disk_size": "5000M", - "format": "qcow2", - "accelerator": "kvm", - "http_directory": "path/to/httpdir", - "ssh_username": "root", - "ssh_password": "s0m3password", - "ssh_timeout": "20m", - "vm_name": "tdhtest", - "net_device": "virtio-net", - "disk_interface": "virtio", - "boot_wait": "10s", - "boot_command": [ - " text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos6-ks.cfg" - ] - } - ] -} -``` - - - - -```hcl -source "qemu" "example" { - iso_url = "http://mirror.raystedman.net/centos/6/isos/x86_64/CentOS-6.9-x86_64-minimal.iso" - iso_checksum = "md5:af4a1640c0c6f348c6c41f1ea9e192a2" - output_directory = "output_centos_tdhtest" - shutdown_command = "echo 'packer' | sudo -S shutdown -P now" - disk_size = "5000M" - format = "qcow2" - accelerator = "kvm" - http_directory = "path/to/httpdir" - ssh_username = "root" - ssh_password = "s0m3password" - ssh_timeout = "20m" - vm_name = "tdhtest" - net_device = "virtio-net" - disk_interface = "virtio" - boot_wait = "10s" - boot_command = [" text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos6-ks.cfg"] -} - -build { - sources = ["source.qemu.example"] -} -``` - - - - -This is an example only, and will time out waiting for SSH because we have not -provided a kickstart file. You must add a valid kickstart file to the -"http_directory" and then provide the file in the "boot_command" in order for -this build to run. We recommend you check out the -[Community Templates](/community-tools#templates) -for a practical usage example. - -Note that you will need to set `"headless": true` if you are running Packer -on a Linux server without X11; or if you are connected via SSH to a remote -Linux server and have not enabled X11 forwarding (`ssh -X`). - -## Qemu Specific Configuration Reference - -There are many configuration options available for the builder. In addition to -the items listed here, you will want to look at the general configuration -references for [ISO](#iso-configuration), -[HTTP](#http-directory-configuration), -[Floppy](#floppy-configuration), -[Boot](#boot-configuration), -[Shutdown](#shutdown-configuration), -[Communicator](#communicator-configuration) -configuration references, which are -necessary for this build to succeed and can be found further down the page. - -### Optional: - -@include 'builder/qemu/Config-not-required.mdx' - -## ISO Configuration - -@include 'packer-plugin-sdk/multistep/commonsteps/ISOConfig.mdx' - -### Required: - -@include 'packer-plugin-sdk/multistep/commonsteps/ISOConfig-required.mdx' - -### Optional: - -@include 'packer-plugin-sdk/multistep/commonsteps/ISOConfig-not-required.mdx' - -## Http directory configuration - -@include 'packer-plugin-sdk/multistep/commonsteps/HTTPConfig.mdx' - -### Optional: - -@include 'packer-plugin-sdk/multistep/commonsteps/HTTPConfig-not-required.mdx' - -## Floppy configuration - -@include 'packer-plugin-sdk/multistep/commonsteps/FloppyConfig.mdx' - -### Optional: - -@include 'packer-plugin-sdk/multistep/commonsteps/FloppyConfig-not-required.mdx' - -### CD configuration - -@include 'packer-plugin-sdk/multistep/commonsteps/CDConfig.mdx' - -#### Optional: - -@include 'packer-plugin-sdk/multistep/commonsteps/CDConfig-not-required.mdx' - -## Shutdown configuration - -### Optional: - -@include 'packer-plugin-sdk/shutdowncommand/ShutdownConfig-not-required.mdx' - -## Communicator configuration - -### Optional common fields: - -@include 'packer-plugin-sdk/communicator/Config-not-required.mdx' - -@include 'builder/qemu/CommConfig-not-required.mdx' - -### Optional SSH fields: - -@include 'packer-plugin-sdk/communicator/SSH-not-required.mdx' - -@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx' - -### Optional WinRM fields: - -@include 'packer-plugin-sdk/communicator/WinRM-not-required.mdx' - -## Boot Configuration - -@include 'packer-plugin-sdk/bootcommand/VNCConfig.mdx' - -@include 'packer-plugin-sdk/bootcommand/BootConfig.mdx' - -### Optional: - -@include 'packer-plugin-sdk/bootcommand/VNCConfig-not-required.mdx' - -@include 'packer-plugin-sdk/bootcommand/BootConfig-not-required.mdx' - -### Communicator Configuration - -#### Optional: - -@include 'packer-plugin-sdk/communicator/Config-not-required.mdx' - -### Troubleshooting - -#### Invalid Keymaps - -Some users have experienced errors complaining about invalid keymaps. This -seems to be related to having a `common` directory or file in the directory -they've run Packer in, like the Packer source directory. This appears to be an -upstream bug with qemu, and the best solution for now is to remove the -file/directory or run in another directory. - -Some users have reported issues with incorrect keymaps using qemu version 2.11. -This is a bug with qemu, and the solution is to upgrade, or downgrade to 2.10.1 -or earlier. - -#### Corrupted image after Packer calls qemu-img convert on OSX - -Due to an upstream bug with `qemu-img convert` on OSX, sometimes the qemu-img -convert call will create a corrupted image. If this is an issue for you, make -sure that the the output format (provided using the option `format`) matches -the input file's format and file extension, and Packer will -perform a simple copy operation instead. You will also want to set -`"skip_compaction": true,` and `"disk_compression": false` to skip a final -image conversion at the end of the build. See -https://bugs.launchpad.net/qemu/+bug/1776920 for more details. diff --git a/website/content/partials/builder/qemu/CommConfig-not-required.mdx b/website/content/partials/builder/qemu/CommConfig-not-required.mdx deleted file mode 100644 index b908939aa..000000000 --- a/website/content/partials/builder/qemu/CommConfig-not-required.mdx +++ /dev/null @@ -1,15 +0,0 @@ - - -- `host_port_min` (int) - The minimum port to use for the Communicator port on the host machine which is forwarded - to the SSH or WinRM port on the guest machine. By default this is 2222. - -- `host_port_max` (int) - The maximum port to use for the Communicator port on the host machine which is forwarded - to the SSH or WinRM port on the guest machine. Because Packer often runs in parallel, - Packer will choose a randomly available port in this range to use as the - host port. By default this is 4444. - -- `skip_nat_mapping` (bool) - Defaults to false. When enabled, Packer - does not setup forwarded port mapping for communicator (SSH or WinRM) requests and uses ssh_port or winrm_port - on the host to communicate to the virtual machine. - - diff --git a/website/content/partials/builder/qemu/Config-not-required.mdx b/website/content/partials/builder/qemu/Config-not-required.mdx deleted file mode 100644 index edff6ae59..000000000 --- a/website/content/partials/builder/qemu/Config-not-required.mdx +++ /dev/null @@ -1,325 +0,0 @@ - - -- `iso_skip_cache` (bool) - Use iso from provided url. Qemu must support - curl block device. This defaults to `false`. - -- `accelerator` (string) - The accelerator type to use when running the VM. - This may be `none`, `kvm`, `tcg`, `hax`, `hvf`, `whpx`, or `xen`. The appropriate - software must have already been installed on your build machine to use the - accelerator you specified. When no accelerator is specified, Packer will try - to use `kvm` if it is available but will default to `tcg` otherwise. - - ~> The `hax` accelerator has issues attaching CDROM ISOs. This is an - upstream issue which can be tracked - [here](https://github.com/intel/haxm/issues/20). - - ~> The `hvf` and `whpx` accelerator are new and experimental as of - [QEMU 2.12.0](https://wiki.qemu.org/ChangeLog/2.12#Host_support). - You may encounter issues unrelated to Packer when using these. You may need to - add [ "-global", "virtio-pci.disable-modern=on" ] to `qemuargs` depending on the - guest operating system. - - ~> For `whpx`, note that [Stefan Weil's QEMU for Windows distribution](https://qemu.weilnetz.de/w64/) - does not include WHPX support and users may need to compile or source a - build of QEMU for Windows themselves with WHPX support. - -- `disk_additional_size` ([]string) - Additional disks to create. Uses `vm_name` as the disk name template and - appends `-#` where `#` is the position in the array. `#` starts at 1 since 0 - is the default disk. Each string represents the disk image size in bytes. - Optional suffixes 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' - (gigabyte, 1024M), 'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' - (exabyte, 1024P) are supported. 'b' is ignored. Per qemu-img documentation. - Each additional disk uses the same disk parameters as the default disk. - Unset by default. - -- `cpus` (int) - The number of cpus to use when building the VM. - The default is `1` CPU. - -- `firmware` (string) - The firmware file to be used by QEMU, which is to be set by the -bios - option of QEMU. Particularly, this option can be set to use EFI instead - of BIOS, by using "OVMF.fd" from OpenFirmware. - If unset, no -bios option is passed to QEMU, using the default of QEMU. - Also see the QEMU documentation. - -- `disk_interface` (string) - The interface to use for the disk. Allowed values include any of `ide`, - `scsi`, `virtio` or `virtio-scsi`^\*. Note also that any boot commands - or kickstart type scripts must have proper adjustments for resulting - device names. The Qemu builder uses `virtio` by default. - - ^\* Please be aware that use of the `scsi` disk interface has been - disabled by Red Hat due to a bug described - [here](https://bugzilla.redhat.com/show_bug.cgi?id=1019220). If you are - running Qemu on RHEL or a RHEL variant such as CentOS, you *must* choose - one of the other listed interfaces. Using the `scsi` interface under - these circumstances will cause the build to fail. - -- `disk_size` (string) - The size in bytes of the hard disk of the VM. Suffix with the first - letter of common byte types. Use "k" or "K" for kilobytes, "M" for - megabytes, G for gigabytes, and T for terabytes. If no value is provided - for disk_size, Packer uses a default of `40960M` (40 GB). If a disk_size - number is provided with no units, Packer will default to Megabytes. - -- `skip_resize_disk` (bool) - Packer resizes the QCOW2 image using - qemu-img resize. Set this option to true to disable resizing. - Defaults to false. - -- `disk_cache` (string) - The cache mode to use for disk. Allowed values include any of - `writethrough`, `writeback`, `none`, `unsafe` or `directsync`. By - default, this is set to `writeback`. - -- `disk_discard` (string) - The discard mode to use for disk. Allowed values - include any of unmap or ignore. By default, this is set to ignore. - -- `disk_detect_zeroes` (string) - The detect-zeroes mode to use for disk. - Allowed values include any of unmap, on or off. Defaults to off. - When the value is "off" we don't set the flag in the qemu command, so that - Packer still works with old versions of QEMU that don't have this option. - -- `skip_compaction` (bool) - Packer compacts the QCOW2 image using - qemu-img convert. Set this option to true to disable compacting. - Defaults to false. - -- `disk_compression` (bool) - Apply compression to the QCOW2 disk file - using qemu-img convert. Defaults to false. - -- `format` (string) - Either `qcow2` or `raw`, this specifies the output format of the virtual - machine image. This defaults to `qcow2`. Due to a long-standing bug with - `qemu-img convert` on OSX, sometimes the qemu-img convert call will - create a corrupted image. If this is an issue for you, make sure that the - the output format matches the input file's format, and Packer will - perform a simple copy operation instead. See - https://bugs.launchpad.net/qemu/+bug/1776920 for more details. - -- `headless` (bool) - Packer defaults to building QEMU virtual machines by - launching a GUI that shows the console of the machine being built. When this - value is set to `true`, the machine will start without a console. - - You can still see the console if you make a note of the VNC display - number chosen, and then connect using `vncviewer -Shared :` - -- `disk_image` (bool) - Packer defaults to building from an ISO file, this parameter controls - whether the ISO URL supplied is actually a bootable QEMU image. When - this value is set to `true`, the machine will either clone the source or - use it as a backing file (if `use_backing_file` is `true`); then, it - will resize the image according to `disk_size` and boot it. - -- `use_backing_file` (bool) - Only applicable when disk_image is true - and format is qcow2, set this option to true to create a new QCOW2 - file that uses the file located at iso_url as a backing file. The new file - will only contain blocks that have changed compared to the backing file, so - enabling this option can significantly reduce disk usage. If true, Packer - will force the `skip_compaction` also to be true as well to skip disk - conversion which would render the backing file feature useless. - -- `machine_type` (string) - The type of machine emulation to use. Run your qemu binary with the - flags `-machine help` to list available types for your system. This - defaults to `pc`. - -- `memory` (int) - The amount of memory to use when building the VM - in megabytes. This defaults to 512 megabytes. - -- `net_device` (string) - The driver to use for the network interface. Allowed values `ne2k_pci`, - `i82551`, `i82557b`, `i82559er`, `rtl8139`, `e1000`, `pcnet`, `virtio`, - `virtio-net`, `virtio-net-pci`, `usb-net`, `i82559a`, `i82559b`, - `i82559c`, `i82550`, `i82562`, `i82557a`, `i82557c`, `i82801`, - `vmxnet3`, `i82558a` or `i82558b`. The Qemu builder uses `virtio-net` by - default. - -- `net_bridge` (string) - Connects the network to this bridge instead of using the user mode - networking. - - **NB** This bridge must already exist. You can use the `virbr0` bridge - as created by vagrant-libvirt. - - **NB** This will automatically enable the QMP socket (see QMPEnable). - - **NB** This only works in Linux based OSes. - -- `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 - 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. - -- `qemuargs` ([][]string) - Allows complete control over the qemu command line (though not qemu-img). - Each array of strings makes up a command line switch - that overrides matching default switch/value pairs. Any value specified - as an empty string is ignored. All values after the switch are - concatenated with no separator. - - ~> **Warning:** The qemu command line allows extreme flexibility, so - beware of conflicting arguments causing failures of your run. - For instance adding a "--drive" or "--device" override will mean that - none of the default configuration Packer sets will be used. To see the - defaults that Packer sets, look in your packer.log - file (set PACKER_LOG=1 to get verbose logging) and search for the - qemu-system-x86 command. The arguments are all printed for review, and - you can use those arguments along with the template engines allowed - by qemu-args to set up a working configuration that includes both the - Packer defaults and your extra arguments. - - Another pitfall could be setting arguments like --no-acpi, which could - break the ability to send power signal type commands - (e.g., shutdown -P now) to the virtual machine, thus preventing proper - shutdown. - - The following shows a sample usage: - - In JSON: - ```json - "qemuargs": [ - [ "-m", "1024M" ], - [ "--no-acpi", "" ], - [ - "-netdev", - "user,id=mynet0,", - "hostfwd=hostip:hostport-guestip:guestport", - "" - ], - [ "-device", "virtio-net,netdev=mynet0" ] - ] - ``` - - In HCL2: - ```hcl - qemuargs = [ - [ "-m", "1024M" ], - [ "--no-acpi", "" ], - [ - "-netdev", - "user,id=mynet0,", - "hostfwd=hostip:hostport-guestip:guestport", - "" - ], - [ "-device", "virtio-net,netdev=mynet0" ] - ] - ``` - - would produce the following (not including other defaults supplied by - the builder and not otherwise conflicting with the qemuargs): - - ```text - qemu-system-x86 -m 1024m --no-acpi -netdev - user,id=mynet0,hostfwd=hostip:hostport-guestip:guestport -device - virtio-net,netdev=mynet0" - ``` - - ~> **Windows Users:** [QEMU for Windows](https://qemu.weilnetz.de/) - builds are available though an environmental variable does need to be - set for QEMU for Windows to redirect stdout to the console instead of - stdout.txt. - - The following shows the environment variable that needs to be set for - Windows QEMU support: - - ```text - setx SDL_STDIO_REDIRECT=0 - ``` - - You can also use the `SSHHostPort` template variable to produce a packer - template that can be invoked by `make` in parallel: - - In JSON: - ```json - "qemuargs": [ - [ "-netdev", "user,hostfwd=tcp::{{ .SSHHostPort }}-:22,id=forward"], - [ "-device", "virtio-net,netdev=forward,id=net0"] - ] - ``` - - In HCL2: - ```hcl - qemuargs = [ - [ "-netdev", "user,hostfwd=tcp::{{ .SSHHostPort }}-:22,id=forward"], - [ "-device", "virtio-net,netdev=forward,id=net0"] - ] - - `make -j 3 my-awesome-packer-templates` spawns 3 packer processes, each - of which will bind to their own SSH port as determined by each process. - This will also work with WinRM, just change the port forward in - `qemuargs` to map to WinRM's default port of `5985` or whatever value - you have the service set to listen on. - - This is a template engine and allows access to the following variables: - `{{ .HTTPIP }}`, `{{ .HTTPPort }}`, `{{ .HTTPDir }}`, - `{{ .OutputDir }}`, `{{ .Name }}`, and `{{ .SSHHostPort }}` - -- `qemu_img_args` (QemuImgArgs) - A map of custom arguments to pass to qemu-img commands, where the key - is the subcommand, and the values are lists of strings for each flag. - Example: - - In JSON: - ```json - { - "qemu_img_args": { - "convert": ["-o", "preallocation=full"], - "resize": ["-foo", "bar"] - } - ``` - Please note - that unlike qemuargs, these commands are not split into switch-value - sub-arrays, because the basic elements in qemu-img calls are unlikely - to need an actual override. - The arguments will be constructed as follows: - - Convert: - Default is `qemu-img convert -O $format $sourcepath $targetpath`. Adding - arguments ["-foo", "bar"] to qemu_img_args.convert will change this to - `qemu-img convert -foo bar -O $format $sourcepath $targetpath` - - Create: - Default is `create -f $format $targetpath $size`. Adding arguments - ["-foo", "bar"] to qemu_img_args.create will change this to - "create -f qcow2 -foo bar target.qcow2 1234M" - - Resize: - Default is `qemu-img resize -f $format $sourcepath $size`. Adding - arguments ["-foo", "bar"] to qemu_img_args.resize will change this to - `qemu-img resize -f $format -foo bar $sourcepath $size` - -- `qemu_binary` (string) - The name of the Qemu binary to look for. This - defaults to qemu-system-x86_64, but may need to be changed for - some platforms. For example qemu-kvm, or qemu-system-i386 may be a - better choice for some systems. - -- `qmp_enable` (bool) - Enable QMP socket. Location is specified by `qmp_socket_path`. Defaults - to false. - -- `qmp_socket_path` (string) - QMP Socket Path when `qmp_enable` is true. Defaults to - `output_directory`/`vm_name`.monitor. - -- `use_default_display` (bool) - If true, do not pass a -display option - to qemu, allowing it to choose the default. This may be needed when running - under macOS, and getting errors about sdl not being available. - -- `display` (string) - What QEMU -display option to use. Defaults to gtk, use none to not pass the - -display option allowing QEMU to choose the default. This may be needed when - running under macOS, and getting errors about sdl not being available. - -- `vnc_bind_address` (string) - The IP address that should be - binded to for VNC. By default packer will use 127.0.0.1 for this. If you - wish to bind to all interfaces use 0.0.0.0. - -- `vnc_use_password` (bool) - Whether or not to set a password on the VNC server. This option - automatically enables the QMP socket. See `qmp_socket_path`. Defaults to - `false`. - -- `vnc_port_min` (int) - The minimum and maximum port - to use for VNC access to the virtual machine. The builder uses VNC to type - the initial boot_command. Because Packer generally runs in parallel, - Packer uses a randomly chosen port in this range that appears available. By - default this is 5900 to 6000. The minimum and maximum ports are inclusive. - The minimum port cannot be set below 5900 due to a quirk in how QEMU parses - vnc display address. - -- `vnc_port_max` (int) - VNC Port Max - -- `vm_name` (string) - This is the name of the image (QCOW2 or IMG) file for - the new virtual machine. By default this is packer-BUILDNAME, where - "BUILDNAME" is the name of the build. Currently, no file extension will be - used unless it is specified in this option. - -- `cdrom_interface` (string) - The interface to use for the CDROM device which contains the ISO image. - Allowed values include any of `ide`, `scsi`, `virtio` or - `virtio-scsi`. The Qemu builder uses `virtio` by default. - Some ARM64 images require `virtio-scsi`. - - diff --git a/website/content/partials/builder/qemu/QemuImgArgs-not-required.mdx b/website/content/partials/builder/qemu/QemuImgArgs-not-required.mdx deleted file mode 100644 index ddac43643..000000000 --- a/website/content/partials/builder/qemu/QemuImgArgs-not-required.mdx +++ /dev/null @@ -1,9 +0,0 @@ - - -- `convert` ([]string) - Convert - -- `create` ([]string) - Create - -- `resize` ([]string) - Resize - - diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index 7b12f5b5d..5ff658273 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -845,10 +845,6 @@ } ] }, - { - "title": "QEMU", - "path": "builders/qemu" - }, { "title": "Scaleway", "path": "builders/scaleway"