Merge pull request #9956 from hashicorp/do_6734
builder/qemu: Add qemu_img_args option to set special cli flags for our calls to qemu-img
This commit is contained in:
commit
77817f80a2
|
@ -68,7 +68,16 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||||
Files: b.config.CDConfig.CDFiles,
|
Files: b.config.CDConfig.CDFiles,
|
||||||
Label: b.config.CDConfig.CDLabel,
|
Label: b.config.CDConfig.CDLabel,
|
||||||
},
|
},
|
||||||
new(stepCreateDisk),
|
&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{
|
&stepCopyDisk{
|
||||||
DiskImage: b.config.DiskImage,
|
DiskImage: b.config.DiskImage,
|
||||||
Format: b.config.Format,
|
Format: b.config.Format,
|
||||||
|
@ -76,7 +85,16 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||||
UseBackingFile: b.config.UseBackingFile,
|
UseBackingFile: b.config.UseBackingFile,
|
||||||
VMName: b.config.VMName,
|
VMName: b.config.VMName,
|
||||||
},
|
},
|
||||||
new(stepResizeDisk),
|
&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),
|
new(stepHTTPIPDiscover),
|
||||||
&common.StepHTTPServer{
|
&common.StepHTTPServer{
|
||||||
HTTPDir: b.config.HTTPDir,
|
HTTPDir: b.config.HTTPDir,
|
||||||
|
@ -113,7 +131,13 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||||
Comm: &b.config.CommConfig.Comm,
|
Comm: &b.config.CommConfig.Comm,
|
||||||
},
|
},
|
||||||
new(stepShutdown),
|
new(stepShutdown),
|
||||||
new(stepConvertDisk),
|
&stepConvertDisk{
|
||||||
|
DiskCompression: b.config.DiskCompression,
|
||||||
|
Format: b.config.Format,
|
||||||
|
OutputDir: b.config.OutputDir,
|
||||||
|
SkipCompaction: b.config.SkipCompaction,
|
||||||
|
VMName: b.config.VMName,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// Setup the state bag
|
// Setup the state bag
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//go:generate struct-markdown
|
//go:generate struct-markdown
|
||||||
//go:generate mapstructure-to-hcl2 -type Config
|
//go:generate mapstructure-to-hcl2 -type Config,QemuImgArgs
|
||||||
|
|
||||||
package qemu
|
package qemu
|
||||||
|
|
||||||
|
@ -57,6 +57,12 @@ var diskDZeroes = map[string]bool{
|
||||||
"off": 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 {
|
type Config struct {
|
||||||
common.PackerConfig `mapstructure:",squash"`
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
common.HTTPConfig `mapstructure:",squash"`
|
common.HTTPConfig `mapstructure:",squash"`
|
||||||
|
@ -297,6 +303,36 @@ type Config struct {
|
||||||
// `{{ .HTTPIP }}`, `{{ .HTTPPort }}`, `{{ .HTTPDir }}`,
|
// `{{ .HTTPIP }}`, `{{ .HTTPPort }}`, `{{ .HTTPDir }}`,
|
||||||
// `{{ .OutputDir }}`, `{{ .Name }}`, and `{{ .SSHHostPort }}`
|
// `{{ .OutputDir }}`, `{{ .Name }}`, and `{{ .SSHHostPort }}`
|
||||||
QemuArgs [][]string `mapstructure:"qemuargs" required:"false"`
|
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
|
// The name of the Qemu binary to look for. This
|
||||||
// defaults to qemu-system-x86_64, but may need to be changed for
|
// 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
|
// some platforms. For example qemu-kvm, or qemu-system-i386 may be a
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
// Code generated by "mapstructure-to-hcl2 -type Config,QemuImgArgs"; DO NOT EDIT.
|
||||||
package qemu
|
package qemu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -112,6 +112,7 @@ type FlatConfig struct {
|
||||||
NetBridge *string `mapstructure:"net_bridge" required:"false" cty:"net_bridge" hcl:"net_bridge"`
|
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"`
|
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"`
|
||||||
QemuArgs [][]string `mapstructure:"qemuargs" required:"false" cty:"qemuargs" hcl:"qemuargs"`
|
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"`
|
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"`
|
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"`
|
QMPSocketPath *string `mapstructure:"qmp_socket_path" required:"false" cty:"qmp_socket_path" hcl:"qmp_socket_path"`
|
||||||
|
@ -241,6 +242,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
"net_bridge": &hcldec.AttrSpec{Name: "net_bridge", 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},
|
"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},
|
"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},
|
"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_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},
|
"qmp_socket_path": &hcldec.AttrSpec{Name: "qmp_socket_path", Type: cty.String, Required: false},
|
||||||
|
@ -256,3 +258,30 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
}
|
}
|
||||||
return s
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testPem = `
|
var testPem = `
|
||||||
|
@ -668,3 +669,26 @@ func TestCommConfigPrepare_BackwardsCompatibility(t *testing.T) {
|
||||||
t.Fatalf("HostPortMax should be %d for backwards compatibility, but it was %d", hostPortMax, c.CommConfig.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")
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ type DriverMock struct {
|
||||||
WaitForShutdownState bool
|
WaitForShutdownState bool
|
||||||
|
|
||||||
QemuImgCalled bool
|
QemuImgCalled bool
|
||||||
QemuImgCalls [][]string
|
QemuImgCalls []string
|
||||||
QemuImgErrs []error
|
QemuImgErrs []error
|
||||||
|
|
||||||
VerifyCalled bool
|
VerifyCalled bool
|
||||||
|
@ -55,7 +55,7 @@ func (d *DriverMock) WaitForShutdown(cancelCh <-chan struct{}) bool {
|
||||||
|
|
||||||
func (d *DriverMock) QemuImg(args ...string) error {
|
func (d *DriverMock) QemuImg(args ...string) error {
|
||||||
d.QemuImgCalled = true
|
d.QemuImgCalled = true
|
||||||
d.QemuImgCalls = append(d.QemuImgCalls, args)
|
d.QemuImgCalls = append(d.QemuImgCalls, args...)
|
||||||
|
|
||||||
if len(d.QemuImgErrs) >= len(d.QemuImgCalls) {
|
if len(d.QemuImgErrs) >= len(d.QemuImgCalls) {
|
||||||
return d.QemuImgErrs[len(d.QemuImgCalls)-1]
|
return d.QemuImgErrs[len(d.QemuImgCalls)-1]
|
||||||
|
|
|
@ -17,37 +17,32 @@ import (
|
||||||
|
|
||||||
// This step converts the virtual disk that was used as the
|
// This step converts the virtual disk that was used as the
|
||||||
// hard drive for the virtual machine.
|
// hard drive for the virtual machine.
|
||||||
type stepConvertDisk struct{}
|
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 {
|
func (s *stepConvertDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(*Config)
|
|
||||||
driver := state.Get("driver").(Driver)
|
driver := state.Get("driver").(Driver)
|
||||||
diskName := config.VMName
|
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
if config.SkipCompaction && !config.DiskCompression {
|
diskName := s.VMName
|
||||||
|
|
||||||
|
if s.SkipCompaction && !s.DiskCompression {
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
name := diskName + ".convert"
|
name := diskName + ".convert"
|
||||||
|
|
||||||
sourcePath := filepath.Join(config.OutputDir, diskName)
|
sourcePath := filepath.Join(s.OutputDir, diskName)
|
||||||
targetPath := filepath.Join(config.OutputDir, name)
|
targetPath := filepath.Join(s.OutputDir, name)
|
||||||
|
|
||||||
command := []string{
|
command := s.buildConvertCommand(sourcePath, targetPath)
|
||||||
"convert",
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.DiskCompression {
|
|
||||||
command = append(command, "-c")
|
|
||||||
}
|
|
||||||
|
|
||||||
command = append(command, []string{
|
|
||||||
"-O", config.Format,
|
|
||||||
sourcePath,
|
|
||||||
targetPath,
|
|
||||||
}...,
|
|
||||||
)
|
|
||||||
|
|
||||||
ui.Say("Converting hard drive...")
|
ui.Say("Converting hard drive...")
|
||||||
// Retry the conversion a few times in case it takes the qemu process a
|
// Retry the conversion a few times in case it takes the qemu process a
|
||||||
|
@ -90,4 +85,20 @@ func (s *stepConvertDisk) Run(ctx context.Context, state multistep.StateBag) mul
|
||||||
return multistep.ActionContinue
|
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) {}
|
func (s *stepConvertDisk) Cleanup(state multistep.StateBag) {}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,8 @@ type stepCopyDisk struct {
|
||||||
OutputDir string
|
OutputDir string
|
||||||
UseBackingFile bool
|
UseBackingFile bool
|
||||||
VMName string
|
VMName string
|
||||||
|
|
||||||
|
QemuImgArgs QemuImgArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stepCopyDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
func (s *stepCopyDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
@ -43,12 +45,7 @@ func (s *stepCopyDisk) Run(ctx context.Context, state multistep.StateBag) multis
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
command := []string{
|
command := s.buildConvertCommand(isoPath, path)
|
||||||
"convert",
|
|
||||||
"-O", s.Format,
|
|
||||||
isoPath,
|
|
||||||
path,
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Say("Copying hard drive...")
|
ui.Say("Copying hard drive...")
|
||||||
if err := driver.QemuImg(command...); err != nil {
|
if err := driver.QemuImg(command...); err != nil {
|
||||||
|
@ -61,4 +58,16 @@ func (s *stepCopyDisk) Run(ctx context.Context, state multistep.StateBag) multis
|
||||||
return multistep.ActionContinue
|
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) {}
|
func (s *stepCopyDisk) Cleanup(state multistep.StateBag) {}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func copyTestState(t *testing.T, d *DriverMock) multistep.StateBag {
|
func copyTestState(t *testing.T, d *DriverMock) multistep.StateBag {
|
||||||
|
@ -89,3 +90,33 @@ func Test_StepQemuImgCalled(t *testing.T) {
|
||||||
t.Fatalf("Should have called qemu-img since extensions don't match")
|
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")
|
||||||
|
}
|
||||||
|
|
|
@ -12,15 +12,23 @@ import (
|
||||||
|
|
||||||
// This step creates the virtual disk that will be used as the
|
// This step creates the virtual disk that will be used as the
|
||||||
// hard drive for the virtual machine.
|
// hard drive for the virtual machine.
|
||||||
type stepCreateDisk struct{}
|
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 {
|
func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
config := state.Get("config").(*Config)
|
|
||||||
driver := state.Get("driver").(Driver)
|
driver := state.Get("driver").(Driver)
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
name := config.VMName
|
name := s.VMName
|
||||||
|
|
||||||
if config.DiskImage && !config.UseBackingFile {
|
if s.DiskImage && !s.UseBackingFile {
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,12 +36,12 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||||
|
|
||||||
ui.Say("Creating required virtual machine disks")
|
ui.Say("Creating required virtual machine disks")
|
||||||
// The 'main' or 'default' disk
|
// The 'main' or 'default' disk
|
||||||
diskFullPaths = append(diskFullPaths, filepath.Join(config.OutputDir, name))
|
diskFullPaths = append(diskFullPaths, filepath.Join(s.OutputDir, name))
|
||||||
diskSizes = append(diskSizes, config.DiskSize)
|
diskSizes = append(diskSizes, s.DiskSize)
|
||||||
// Additional disks
|
// Additional disks
|
||||||
if len(config.AdditionalDiskSize) > 0 {
|
if len(s.AdditionalDiskSize) > 0 {
|
||||||
for i, diskSize := range config.AdditionalDiskSize {
|
for i, diskSize := range s.AdditionalDiskSize {
|
||||||
path := filepath.Join(config.OutputDir, fmt.Sprintf("%s-%d", name, i+1))
|
path := filepath.Join(s.OutputDir, fmt.Sprintf("%s-%d", name, i+1))
|
||||||
diskFullPaths = append(diskFullPaths, path)
|
diskFullPaths = append(diskFullPaths, path)
|
||||||
size := diskSize
|
size := diskSize
|
||||||
diskSizes = append(diskSizes, size)
|
diskSizes = append(diskSizes, size)
|
||||||
|
@ -43,19 +51,8 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||||
// Create all required disks
|
// Create all required disks
|
||||||
for i, diskFullPath := range diskFullPaths {
|
for i, diskFullPath := range diskFullPaths {
|
||||||
log.Printf("[INFO] Creating disk with Path: %s and Size: %s", diskFullPath, diskSizes[i])
|
log.Printf("[INFO] Creating disk with Path: %s and Size: %s", diskFullPath, diskSizes[i])
|
||||||
command := []string{
|
|
||||||
"create",
|
|
||||||
"-f", config.Format,
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.UseBackingFile && i == 0 {
|
command := s.buildCreateCommand(diskFullPath, diskSizes[i], i, state)
|
||||||
isoPath := state.Get("iso_path").(string)
|
|
||||||
command = append(command, "-b", isoPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
command = append(command,
|
|
||||||
diskFullPath,
|
|
||||||
diskSizes[i])
|
|
||||||
|
|
||||||
if err := driver.QemuImg(command...); err != nil {
|
if err := driver.QemuImg(command...); err != nil {
|
||||||
err := fmt.Errorf("Error creating hard drive: %s", err)
|
err := fmt.Errorf("Error creating hard drive: %s", err)
|
||||||
|
@ -71,4 +68,21 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||||
return multistep.ActionContinue
|
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.UseBackingFile && i == 0 {
|
||||||
|
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) {}
|
func (s *stepCreateDisk) Cleanup(state multistep.StateBag) {}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
package qemu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/helper/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",
|
||||||
|
UseBackingFile: true,
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
[]string{"create", "-f", "qcow2", "-b", "source.qcow2", "target.qcow2", "1234M"},
|
||||||
|
"Basic, happy path, backing store, no extra args",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&stepCreateDisk{
|
||||||
|
Format: "qcow2",
|
||||||
|
UseBackingFile: 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,
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,22 +11,26 @@ import (
|
||||||
|
|
||||||
// This step resizes the virtual disk that will be used as the
|
// This step resizes the virtual disk that will be used as the
|
||||||
// hard drive for the virtual machine.
|
// hard drive for the virtual machine.
|
||||||
type stepResizeDisk struct{}
|
type stepResizeDisk struct {
|
||||||
|
DiskCompression bool
|
||||||
|
DiskImage bool
|
||||||
|
Format string
|
||||||
|
OutputDir string
|
||||||
|
SkipResizeDisk bool
|
||||||
|
VMName string
|
||||||
|
DiskSize string
|
||||||
|
|
||||||
func (s *stepResizeDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
QemuImgArgs QemuImgArgs
|
||||||
config := state.Get("config").(*Config)
|
|
||||||
driver := state.Get("driver").(Driver)
|
|
||||||
ui := state.Get("ui").(packer.Ui)
|
|
||||||
path := filepath.Join(config.OutputDir, config.VMName)
|
|
||||||
|
|
||||||
command := []string{
|
|
||||||
"resize",
|
|
||||||
"-f", config.Format,
|
|
||||||
path,
|
|
||||||
config.DiskSize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.DiskImage == false || config.SkipResizeDisk == true {
|
func (s *stepResizeDisk) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
driver := state.Get("driver").(Driver)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
path := filepath.Join(s.OutputDir, s.VMName)
|
||||||
|
|
||||||
|
command := s.buildResizeCommand(path)
|
||||||
|
|
||||||
|
if s.DiskImage == false || s.SkipResizeDisk == true {
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,4 +45,16 @@ func (s *stepResizeDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||||
return multistep.ActionContinue
|
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) {}
|
func (s *stepResizeDisk) Cleanup(state multistep.StateBag) {}
|
||||||
|
|
|
@ -2,70 +2,28 @@ package qemu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/helper/multistep"
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStepResizeDisk_Run(t *testing.T) {
|
func TestStepResizeDisk_Skips(t *testing.T) {
|
||||||
state := testState(t)
|
testConfigs := []*Config{
|
||||||
driver := state.Get("driver").(*DriverMock)
|
&Config{
|
||||||
|
|
||||||
config := &Config{
|
|
||||||
DiskImage: true,
|
|
||||||
SkipResizeDisk: false,
|
|
||||||
DiskSize: "4096M",
|
|
||||||
Format: "qcow2",
|
|
||||||
OutputDir: "/test/",
|
|
||||||
VMName: "test",
|
|
||||||
}
|
|
||||||
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 qemu-img called")
|
|
||||||
}
|
|
||||||
if len(driver.QemuImgCalls[0]) != 5 {
|
|
||||||
t.Fatal("should 5 qemu-img parameters")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepResizeDisk_SkipIso(t *testing.T) {
|
|
||||||
state := testState(t)
|
|
||||||
driver := state.Get("driver").(*DriverMock)
|
|
||||||
config := &Config{
|
|
||||||
DiskImage: false,
|
DiskImage: false,
|
||||||
SkipResizeDisk: false,
|
SkipResizeDisk: false,
|
||||||
}
|
},
|
||||||
state.Put("config", 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 qemu-img called")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepResizeDisk_SkipOption(t *testing.T) {
|
|
||||||
state := testState(t)
|
|
||||||
driver := state.Get("driver").(*DriverMock)
|
|
||||||
config := &Config{
|
|
||||||
DiskImage: false,
|
DiskImage: false,
|
||||||
SkipResizeDisk: true,
|
SkipResizeDisk: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
for _, config := range testConfigs {
|
||||||
|
state := testState(t)
|
||||||
|
driver := state.Get("driver").(*DriverMock)
|
||||||
|
|
||||||
state.Put("config", config)
|
state.Put("config", config)
|
||||||
step := new(stepResizeDisk)
|
step := new(stepResizeDisk)
|
||||||
|
|
||||||
|
@ -77,6 +35,43 @@ func TestStepResizeDisk_SkipOption(t *testing.T) {
|
||||||
t.Fatal("should NOT have error")
|
t.Fatal("should NOT have error")
|
||||||
}
|
}
|
||||||
if len(driver.QemuImgCalls) > 0 {
|
if len(driver.QemuImgCalls) > 0 {
|
||||||
t.Fatal("should NOT qemu-img called")
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,7 +247,7 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
|
||||||
|
|
||||||
inArgs := make(map[string][]string)
|
inArgs := make(map[string][]string)
|
||||||
if len(config.QemuArgs) > 0 {
|
if len(config.QemuArgs) > 0 {
|
||||||
ui.Say("Overriding defaults Qemu arguments with QemuArgs...")
|
ui.Say("Overriding default Qemu arguments with QemuArgs...")
|
||||||
|
|
||||||
httpIp := state.Get("http_ip").(string)
|
httpIp := state.Get("http_ip").(string)
|
||||||
httpPort := state.Get("http_port").(int)
|
httpPort := state.Get("http_port").(int)
|
||||||
|
|
|
@ -231,6 +231,36 @@
|
||||||
`{{ .HTTPIP }}`, `{{ .HTTPPort }}`, `{{ .HTTPDir }}`,
|
`{{ .HTTPIP }}`, `{{ .HTTPPort }}`, `{{ .HTTPDir }}`,
|
||||||
`{{ .OutputDir }}`, `{{ .Name }}`, and `{{ .SSHHostPort }}`
|
`{{ .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
|
- `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
|
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
|
some platforms. For example qemu-kvm, or qemu-system-i386 may be a
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<!-- Code generated from the comments of the QemuImgArgs struct in builder/qemu/config.go; DO NOT EDIT MANUALLY -->
|
||||||
|
|
||||||
|
- `convert` ([]string) - Convert
|
||||||
|
|
||||||
|
- `create` ([]string) - Create
|
||||||
|
|
||||||
|
- `resize` ([]string) - Resize
|
Loading…
Reference in New Issue