Refactor communicator config for Qemu and add SkipNatMapping option (#9307)

This commit is contained in:
Sylvia Moss 2020-06-02 11:56:36 +02:00 committed by GitHub
parent 374f82b978
commit ce45a1990a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 454 additions and 83 deletions

View File

@ -25,6 +25,9 @@
* builder/google: Implement iap proxy for googlecompute [GH-9105]
* builder/googlecompute: Changed default disk size. [GH-9071]
* builder/qemu: add support for using a network bridge [GH-9159]
* builder/qemu: Added `skip_nat_mapping` option to skip the
communicator (SSH or WinRM) automatic port forward and use the guest port directly. [GH-9307]
* builder/qemu: Replace deprecated `ssh_host_port_min` and `ssh_host_port_max` by `host_port_min` and `host_port_max`. [GH-9307]
* builder/virtualbox: Add `output_filename` config option to allow to set a
custom filename instead of forcing to be the same as vm_name. [GH-9174]
* builder/vsphere: floppy_label Parameter for vsphere-iso Builder [GH-9187]

View File

@ -75,7 +75,7 @@ type Config struct {
common.ISOConfig `mapstructure:",squash"`
bootcommand.VNCConfig `mapstructure:",squash"`
shutdowncommand.ShutdownConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
CommConfig CommConfig `mapstructure:",squash"`
common.FloppyConfig `mapstructure:",squash"`
// Use iso from provided url. Qemu must support
// curl block device. This defaults to `false`.
@ -288,12 +288,6 @@ type Config struct {
// QMP Socket Path when `qmp_enable` is true. Defaults to
// `output_directory`/`vm_name`.monitor.
QMPSocketPath string `mapstructure:"qmp_socket_path" required:"false"`
// The minimum and maximum port to use for the SSH port on the host machine
// which is forwarded to the SSH 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 2222 to 4444.
SSHHostPortMin int `mapstructure:"ssh_host_port_min" required:"false"`
SSHHostPortMax int `mapstructure:"ssh_host_port_max" 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.
@ -425,14 +419,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
b.config.CpuCount = 1
}
if b.config.SSHHostPortMin == 0 {
b.config.SSHHostPortMin = 2222
}
if b.config.SSHHostPortMax == 0 {
b.config.SSHHostPortMax = 4444
}
if b.config.VNCBindAddress == "" {
b.config.VNCBindAddress = "127.0.0.1"
}
@ -472,9 +458,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
errs = packer.MultiErrorAppend(errs, isoErrs...)
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
if es := b.config.Comm.Prepare(&b.config.ctx); len(es) > 0 {
commConfigWarnings, es := b.config.CommConfig.Prepare(&b.config.ctx)
if len(es) > 0 {
errs = packer.MultiErrorAppend(errs, es...)
}
warnings = append(warnings, commConfigWarnings...)
if !(b.config.Format == "qcow2" || b.config.Format == "raw") {
errs = packer.MultiErrorAppend(
@ -529,16 +517,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
}
}
if b.config.SSHHostPortMin > b.config.SSHHostPortMax {
errs = packer.MultiErrorAppend(
errs, errors.New("ssh_host_port_min must be less than ssh_host_port_max"))
}
if b.config.SSHHostPortMin < 0 {
errs = packer.MultiErrorAppend(
errs, errors.New("ssh_host_port_min must be positive"))
}
if b.config.VNCPortMin > b.config.VNCPortMax {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
@ -621,9 +599,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
},
)
if b.config.Comm.Type != "none" && b.config.NetBridge == "" {
if b.config.CommConfig.Comm.Type != "none" && b.config.NetBridge == "" {
steps = append(steps,
new(stepForwardSSH),
new(stepPortForward),
)
}
@ -636,20 +614,20 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
&stepTypeBootCommand{},
)
if b.config.Comm.Type != "none" && b.config.NetBridge != "" {
if b.config.CommConfig.Comm.Type != "none" && b.config.NetBridge != "" {
steps = append(steps,
&stepWaitGuestAddress{
timeout: b.config.Comm.SSHTimeout,
timeout: b.config.CommConfig.Comm.SSHTimeout,
},
)
}
if b.config.Comm.Type != "none" {
if b.config.CommConfig.Comm.Type != "none" {
steps = append(steps,
&communicator.StepConnect{
Config: &b.config.Comm,
Host: commHost(b.config.Comm.Host()),
SSHConfig: b.config.Comm.SSHConfigFunc(),
Config: &b.config.CommConfig.Comm,
Host: commHost(b.config.CommConfig.Comm.Host()),
SSHConfig: b.config.CommConfig.Comm.SSHConfigFunc(),
SSHPort: commPort,
WinRMPort: commPort,
},
@ -662,7 +640,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
steps = append(steps,
&common.StepCleanupTempKeys{
Comm: &b.config.Comm,
Comm: &b.config.CommConfig.Comm,
},
)
steps = append(steps,

View File

@ -73,6 +73,11 @@ type FlatConfig struct {
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"`
@ -100,8 +105,6 @@ type FlatConfig struct {
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"`
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" required:"false" cty:"ssh_host_port_max" hcl:"ssh_host_port_max"`
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"`
@ -188,6 +191,11 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"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},
@ -215,8 +223,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"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},
"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},
"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},

View File

@ -74,16 +74,16 @@ func TestBuilderPrepare_Defaults(t *testing.T) {
t.Errorf("bad output dir: %s", b.config.OutputDir)
}
if b.config.SSHHostPortMin != 2222 {
t.Errorf("bad min ssh host port: %d", b.config.SSHHostPortMin)
if b.config.CommConfig.HostPortMin != 2222 {
t.Errorf("bad min ssh host port: %d", b.config.CommConfig.HostPortMin)
}
if b.config.SSHHostPortMax != 4444 {
t.Errorf("bad max ssh host port: %d", b.config.SSHHostPortMax)
if b.config.CommConfig.HostPortMax != 4444 {
t.Errorf("bad max ssh host port: %d", b.config.CommConfig.HostPortMax)
}
if b.config.Comm.SSHPort != 22 {
t.Errorf("bad ssh port: %d", b.config.Comm.SSHPort)
if b.config.CommConfig.Comm.SSHPort != 22 {
t.Errorf("bad ssh port: %d", b.config.CommConfig.Comm.SSHPort)
}
if b.config.VMName != "packer-foo" {
@ -430,8 +430,8 @@ func TestBuilderPrepare_SSHHostPort(t *testing.T) {
config := testConfig()
// Bad
config["ssh_host_port_min"] = 1000
config["ssh_host_port_max"] = 500
config["host_port_min"] = 1000
config["host_port_max"] = 500
b = Builder{}
_, warns, err := b.Prepare(config)
if len(warns) > 0 {
@ -442,7 +442,7 @@ func TestBuilderPrepare_SSHHostPort(t *testing.T) {
}
// Bad
config["ssh_host_port_min"] = -500
config["host_port_min"] = -500
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
@ -453,8 +453,8 @@ func TestBuilderPrepare_SSHHostPort(t *testing.T) {
}
// Good
config["ssh_host_port_min"] = 500
config["ssh_host_port_max"] = 1000
config["host_port_min"] = 500
config["host_port_max"] = 1000
b = Builder{}
_, warns, err = b.Prepare(config)
if len(warns) > 0 {
@ -628,18 +628,31 @@ func TestBuilderPrepare_VNCPassword(t *testing.T) {
func TestCommConfigPrepare_BackwardsCompatibility(t *testing.T) {
var b Builder
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 := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
if len(warns) == 0 {
t.Fatalf("should have deprecation warn")
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.Comm.SSHTimeout != sshTimeout {
t.Fatalf("SSHTimeout should be %s for backwards compatibility, but it was %s", sshTimeout.String(), b.config.Comm.SSHTimeout.String())
if b.config.CommConfig.Comm.SSHTimeout != sshTimeout {
t.Fatalf("SSHTimeout should be %s for backwards compatibility, but it was %s", sshTimeout.String(), b.config.CommConfig.Comm.SSHTimeout.String())
}
if b.config.CommConfig.HostPortMin != hostPortMin {
t.Fatalf("HostPortMin should be %d for backwards compatibility, but it was %d", hostPortMin, b.config.CommConfig.HostPortMin)
}
if b.config.CommConfig.HostPortMax != hostPortMax {
t.Fatalf("HostPortMax should be %d for backwards compatibility, but it was %d", hostPortMax, b.config.CommConfig.HostPortMax)
}
}

View File

@ -0,0 +1,72 @@
//go:generate struct-markdown
package qemu
import (
"errors"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/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.Comm.SSHHost = "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
}

View File

@ -0,0 +1,145 @@
package qemu
import (
"io/ioutil"
"os"
"testing"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/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")
}
}

View File

@ -22,9 +22,9 @@ func commHost(host string) func(multistep.StateBag) (string, error) {
}
func commPort(state multistep.StateBag) (int, error) {
sshHostPort, ok := state.Get("sshHostPort").(int)
commHostPort, ok := state.Get("commHostPort").(int)
if !ok {
sshHostPort = 22
commHostPort = 22
}
return int(sshHostPort), nil
return commHostPort, nil
}

View File

@ -10,26 +10,30 @@ import (
"github.com/hashicorp/packer/packer"
)
// This step adds a NAT port forwarding definition so that SSH is available
// This step adds a NAT port forwarding definition so that SSH or WinRM is available
// on the guest machine.
//
// Uses:
//
// Produces:
type stepForwardSSH struct {
type stepPortForward struct {
l *net.Listener
}
func (s *stepForwardSSH) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
func (s *stepPortForward) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
log.Printf("Looking for available communicator (SSH, WinRM, etc) port between %d and %d", config.SSHHostPortMin, config.SSHHostPortMax)
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.SSHHostPortMin,
Max: config.SSHHostPortMax,
Min: config.CommConfig.HostPortMin,
Max: config.CommConfig.HostPortMax,
Network: "tcp",
}.Listen(ctx)
if err != nil {
@ -39,16 +43,16 @@ func (s *stepForwardSSH) Run(ctx context.Context, state multistep.StateBag) mult
return multistep.ActionHalt
}
s.l.Listener.Close() // free port, but don't unlock lock file
sshHostPort := s.l.Port
ui.Say(fmt.Sprintf("Found port for communicator (SSH, WinRM, etc): %d.", sshHostPort))
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("sshHostPort", sshHostPort)
state.Put("commHostPort", commHostPort)
return multistep.ActionContinue
}
func (s *stepForwardSSH) Cleanup(state multistep.StateBag) {
func (s *stepPortForward) Cleanup(state multistep.StateBag) {
if s.l != nil {
err := s.l.Close()
if err != nil {

View File

@ -72,7 +72,7 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
defaultArgs := make(map[string]interface{})
var deviceArgs []string
var driveArgs []string
var sshHostPort int
var commHostPort int
var vnc string
if !config.VNCUsePassword {
@ -89,9 +89,9 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
defaultArgs["-machine"] = fmt.Sprintf("type=%s", config.MachineType)
if config.NetBridge == "" {
if config.Comm.Type != "none" {
sshHostPort = state.Get("sshHostPort").(int)
defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0,hostfwd=tcp::%v-:%d", sshHostPort, config.Comm.Port())
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())
} else {
defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0")
}
@ -226,14 +226,14 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
httpIp := state.Get("http_ip").(string)
httpPort := state.Get("http_port").(int)
ictx := config.ctx
if config.Comm.Type != "none" {
if config.CommConfig.Comm.Type != "none" {
ictx.Data = qemuArgsTemplateData{
httpIp,
httpPort,
config.HTTPDir,
config.OutputDir,
config.VMName,
sshHostPort,
commHostPort,
}
} else {
ictx.Data = qemuArgsTemplateData{

View File

@ -57,6 +57,7 @@ func init() {
"docker-tag-tags": new(FixerDockerTagtoTags),
"vsphere-iso-net-disk": new(FixerVSphereNetworkDisk),
"iso-checksum-type-and-url": new(FixerISOChecksumTypeAndURL),
"qemu-host-port": new(FixerQEMUHostPort),
}
FixerOrder = []string{
@ -91,5 +92,6 @@ func init() {
"ssh-wait-timeout",
"vsphere-iso-net-disk",
"iso-checksum-type-and-url",
"qemu-host-port",
}
}

View File

@ -0,0 +1,61 @@
package fix
import (
"github.com/mitchellh/mapstructure"
)
// FixerQEMUHostPort updates ssh_host_port_min and ssh_host_port_max to host_port_min and host_port_max for QEMU builders
type FixerQEMUHostPort struct{}
func (FixerQEMUHostPort) Fix(input map[string]interface{}) (map[string]interface{}, error) {
type template struct {
Builders []map[string]interface{}
}
// Decode the input into our structure, if we can
var tpl template
if err := mapstructure.Decode(input, &tpl); err != nil {
return nil, err
}
for _, builder := range tpl.Builders {
builderTypeRaw, ok := builder["type"]
if !ok {
continue
}
builderType, ok := builderTypeRaw.(string)
if !ok {
continue
}
if builderType != "qemu" {
continue
}
// replace ssh_host_port_min with host_port_min if it exists
sshHostPortMin, ok := builder["ssh_host_port_min"]
if ok {
delete(builder, "ssh_host_port_min")
builder["host_port_min"] = sshHostPortMin
}
// replace ssh_host_port_min with host_port_min if it exists
sshHostPortMax, ok := builder["ssh_host_port_max"]
if ok {
delete(builder, "ssh_host_port_max")
builder["host_port_max"] = sshHostPortMax
}
}
input["builders"] = tpl.Builders
return input, nil
}
func (FixerQEMUHostPort) Synopsis() string {
return `Updates ssh_host_port_min and ssh_host_port_max to host_port_min and host_port_max`
}
func (FixerQEMUHostPort) DeprecatedOptions() []string {
return []string{"ssh_host_port_max", "ssh_host_port_min"}
}

View File

@ -0,0 +1,61 @@
package fix
import (
"reflect"
"testing"
)
func TestFixerQEMUHostPort_impl(t *testing.T) {
var _ Fixer = new(FixerQEMUHostPort)
}
func TestFixerQEMUHostPort(t *testing.T) {
cases := []struct {
Input map[string]interface{}
Expected map[string]interface{}
}{
{
Input: map[string]interface{}{
"type": "qemu",
"ssh_host_port_min": 2222,
},
Expected: map[string]interface{}{
"type": "qemu",
"host_port_min": 2222,
},
},
{
Input: map[string]interface{}{
"type": "qemu",
"ssh_host_port_max": 4444,
},
Expected: map[string]interface{}{
"type": "qemu",
"host_port_max": 4444,
},
},
}
for _, tc := range cases {
var f FixerQEMUHostPort
input := map[string]interface{}{
"builders": []map[string]interface{}{tc.Input},
}
expected := map[string]interface{}{
"builders": []map[string]interface{}{tc.Expected},
}
output, err := f.Fix(input)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(output, expected) {
t.Fatalf("unexpected: %#v\nexpected: %#v\n", output, expected)
}
}
}

View File

@ -38,4 +38,6 @@ var DeprecatedOptions = []string{
"disk_eagerly_scrub",
"iso_checksum_url",
"iso_checksum_type",
"ssh_host_port_max",
"ssh_host_port_min",
}

View File

@ -117,6 +117,22 @@ necessary for this build to succeed and can be found further down the page.
@include 'common/shutdowncommand/ShutdownConfig-not-required.mdx'
## Communicator configuration
### Optional common fields:
@include 'helper/communicator/Config-not-required.mdx'
@include 'builder/qemu/CommConfig-not-required.mdx'
### Optional SSH fields:
@include 'helper/communicator/SSH-not-required.mdx'
### Optional WinRM fields:
@include 'helper/communicator/WinRM-not-required.mdx'
## Boot Configuration
@include 'common/bootcommand/VNCConfig.mdx'

View File

@ -0,0 +1,14 @@
<!-- Code generated from the comments of the CommConfig struct in builder/qemu/comm_config.go; DO NOT EDIT MANUALLY -->
- `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.

View File

@ -211,12 +211,6 @@
- `qmp_socket_path` (string) - QMP Socket Path when `qmp_enable` is true. Defaults to
`output_directory`/`vm_name`.monitor.
- `ssh_host_port_min` (int) - The minimum and maximum port to use for the SSH port on the host machine
which is forwarded to the SSH 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 2222 to 4444.
- `ssh_host_port_max` (int) - SSH Host Port Max
- `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.