Merge pull request #7423 from hashicorp/lock_ports

Lock packer ports using a lock file
This commit is contained in:
Megan Marsh 2019-03-26 10:35:18 -06:00 committed by GitHub
commit 9f1a4e0fed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 344 additions and 212 deletions

View File

@ -18,7 +18,7 @@ import (
// userDataTemplateData represents variables for user_data interpolation // userDataTemplateData represents variables for user_data interpolation
type userDataTemplateData struct { type userDataTemplateData struct {
HTTPIP string HTTPIP string
HTTPPort uint HTTPPort int
} }
// stepCreateInstance represents a Packer build step that creates CloudStack instances. // stepCreateInstance represents a Packer build step that creates CloudStack instances.
@ -86,7 +86,7 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu
} }
if config.UserData != "" { if config.UserData != "" {
httpPort := state.Get("http_port").(uint) httpPort := state.Get("http_port").(int)
httpIP, err := hostIP() httpIP, err := hostIP()
if err != nil { if err != nil {
err := fmt.Errorf("Failed to determine host IP: %s", err) err := fmt.Errorf("Failed to determine host IP: %s", err)

View File

@ -15,7 +15,7 @@ import (
type bootCommandTemplateData struct { type bootCommandTemplateData struct {
HTTPIP string HTTPIP string
HTTPPort uint HTTPPort int
Name string Name string
} }
@ -29,7 +29,7 @@ type StepTypeBootCommand struct {
} }
func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
httpPort := state.Get("http_port").(uint) httpPort := state.Get("http_port").(int)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
driver := state.Get("driver").(Driver) driver := state.Get("driver").(Driver)
vmName := state.Get("vmName").(string) vmName := state.Get("vmName").(string)

View File

@ -610,7 +610,7 @@ func TestUserVariablesInBootCommand(t *testing.T) {
state.Put("config", &b.config) state.Put("config", &b.config)
state.Put("driver", driver) state.Put("driver", driver)
state.Put("hook", hook) state.Put("hook", hook)
state.Put("http_port", uint(0)) state.Put("http_port", 0)
state.Put("ui", ui) state.Put("ui", ui)
state.Put("vmName", "packer-foo") state.Put("vmName", "packer-foo")

View File

@ -511,7 +511,7 @@ func TestUserVariablesInBootCommand(t *testing.T) {
state.Put("config", &b.config) state.Put("config", &b.config)
state.Put("driver", driver) state.Put("driver", driver)
state.Put("hook", hook) state.Put("hook", hook)
state.Put("http_port", uint(0)) state.Put("http_port", 0)
state.Put("ui", ui) state.Put("ui", ui)
state.Put("vmName", "packer-foo") state.Put("vmName", "packer-foo")

View File

@ -14,7 +14,7 @@ import (
type bootCommandTemplateData struct { type bootCommandTemplateData struct {
HTTPIP string HTTPIP string
HTTPPort uint HTTPPort int
Name string Name string
} }
@ -32,7 +32,7 @@ type StepTypeBootCommand struct {
// Run types the boot command by sending key scancodes into the VM. // Run types the boot command by sending key scancodes into the VM.
func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
debug := state.Get("debug").(bool) debug := state.Get("debug").(bool)
httpPort := state.Get("http_port").(uint) httpPort := state.Get("http_port").(int)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
driver := state.Get("driver").(Driver) driver := state.Get("driver").(Driver)

View File

@ -46,7 +46,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
state.Put("driver", driver) state.Put("driver", driver)
state.Put("hook", hook) state.Put("hook", hook)
state.Put("ui", ui) state.Put("ui", ui)
state.Put("http_port", uint(0)) state.Put("http_port", 0)
// Build the steps. // Build the steps.
steps := []multistep.Step{ steps := []multistep.Step{

View File

@ -116,12 +116,12 @@ type Config struct {
QemuArgs [][]string `mapstructure:"qemuargs"` QemuArgs [][]string `mapstructure:"qemuargs"`
QemuBinary string `mapstructure:"qemu_binary"` QemuBinary string `mapstructure:"qemu_binary"`
ShutdownCommand string `mapstructure:"shutdown_command"` ShutdownCommand string `mapstructure:"shutdown_command"`
SSHHostPortMin uint `mapstructure:"ssh_host_port_min"` SSHHostPortMin int `mapstructure:"ssh_host_port_min"`
SSHHostPortMax uint `mapstructure:"ssh_host_port_max"` SSHHostPortMax int `mapstructure:"ssh_host_port_max"`
UseDefaultDisplay bool `mapstructure:"use_default_display"` UseDefaultDisplay bool `mapstructure:"use_default_display"`
VNCBindAddress string `mapstructure:"vnc_bind_address"` VNCBindAddress string `mapstructure:"vnc_bind_address"`
VNCPortMin uint `mapstructure:"vnc_port_min"` VNCPortMin int `mapstructure:"vnc_port_min"`
VNCPortMax uint `mapstructure:"vnc_port_max"` VNCPortMax int `mapstructure:"vnc_port_max"`
VMName string `mapstructure:"vm_name"` VMName string `mapstructure:"vm_name"`
// These are deprecated, but we keep them around for BC // These are deprecated, but we keep them around for BC
@ -337,6 +337,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend( errs = packer.MultiErrorAppend(
errs, errors.New("ssh_host_port_min must be less than ssh_host_port_max")) 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 { if b.config.VNCPortMin > b.config.VNCPortMax {
errs = packer.MultiErrorAppend( errs = packer.MultiErrorAppend(

View File

@ -9,6 +9,6 @@ func commHost(state multistep.StateBag) (string, error) {
} }
func commPort(state multistep.StateBag) (int, error) { func commPort(state multistep.StateBag) (int, error) {
sshHostPort := state.Get("sshHostPort").(uint) sshHostPort := state.Get("sshHostPort").(int)
return int(sshHostPort), nil return int(sshHostPort), nil
} }

View File

@ -4,9 +4,8 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"math/rand"
"net"
"github.com/hashicorp/packer/common/net"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -18,10 +17,12 @@ import (
// ui packer.Ui // ui packer.Ui
// //
// Produces: // Produces:
// vnc_port uint - The port that VNC is configured to listen on. // vnc_port int - The port that VNC is configured to listen on.
type stepConfigureVNC struct{} type stepConfigureVNC struct {
l *net.Listener
}
func (stepConfigureVNC) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *stepConfigureVNC) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
@ -31,22 +32,22 @@ func (stepConfigureVNC) Run(_ context.Context, state multistep.StateBag) multist
msg := fmt.Sprintf("Looking for available port between %d and %d on %s", config.VNCPortMin, config.VNCPortMax, config.VNCBindAddress) msg := fmt.Sprintf("Looking for available port between %d and %d on %s", config.VNCPortMin, config.VNCPortMax, config.VNCBindAddress)
ui.Say(msg) ui.Say(msg)
log.Print(msg) log.Print(msg)
var vncPort uint
portRange := int(config.VNCPortMax - config.VNCPortMin)
for {
if portRange > 0 {
vncPort = uint(rand.Intn(portRange)) + config.VNCPortMin
} else {
vncPort = config.VNCPortMin
}
log.Printf("Trying port: %d", vncPort) var err error
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.VNCBindAddress, vncPort)) s.l, err = net.ListenRangeConfig{
if err == nil { Addr: config.VNCBindAddress,
defer l.Close() Min: config.VNCPortMin,
break Max: config.VNCPortMax,
} 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
vncPort := s.l.Port
log.Printf("Found available VNC port: %d on IP: %s", vncPort, config.VNCBindAddress) log.Printf("Found available VNC port: %d on IP: %s", vncPort, config.VNCBindAddress)
state.Put("vnc_port", vncPort) state.Put("vnc_port", vncPort)
@ -55,4 +56,11 @@ func (stepConfigureVNC) Run(_ context.Context, state multistep.StateBag) multist
return multistep.ActionContinue return multistep.ActionContinue
} }
func (stepConfigureVNC) Cleanup(multistep.StateBag) {} 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)
}
}
}

View File

@ -4,9 +4,8 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"math/rand"
"net"
"github.com/hashicorp/packer/common/net"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -17,31 +16,30 @@ import (
// Uses: // Uses:
// //
// Produces: // Produces:
type stepForwardSSH struct{} type stepForwardSSH struct {
l *net.Listener
}
func (s *stepForwardSSH) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *stepForwardSSH) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
log.Printf("Looking for available communicator (SSH, WinRM, etc) port between %d and %d", config.SSHHostPortMin, config.SSHHostPortMax) log.Printf("Looking for available communicator (SSH, WinRM, etc) port between %d and %d", config.SSHHostPortMin, config.SSHHostPortMax)
var sshHostPort uint var err error
s.l, err = net.ListenRangeConfig{
portRange := config.SSHHostPortMax - config.SSHHostPortMin + 1 Addr: config.VNCBindAddress,
offset := uint(rand.Intn(int(portRange))) Min: config.VNCPortMin,
Max: config.VNCPortMax,
for { Network: "tcp",
sshHostPort = offset + config.SSHHostPortMin }.Listen(ctx)
log.Printf("Trying port: %d", sshHostPort) if err != nil {
l, err := net.Listen("tcp", fmt.Sprintf(":%d", sshHostPort)) err := fmt.Errorf("Error finding port: %s", err)
if err == nil { state.Put("error", err)
defer l.Close() ui.Error(err.Error())
break return multistep.ActionHalt
}
offset++
if offset == portRange {
offset = 0
}
} }
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)) ui.Say(fmt.Sprintf("Found port for communicator (SSH, WinRM, etc): %d.", sshHostPort))
// Save the port we're using so that future steps can use it // Save the port we're using so that future steps can use it
@ -50,4 +48,11 @@ func (s *stepForwardSSH) Run(_ context.Context, state multistep.StateBag) multis
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *stepForwardSSH) Cleanup(state multistep.StateBag) {} func (s *stepForwardSSH) Cleanup(state multistep.StateBag) {
if s.l != nil {
err := s.l.Close()
if err != nil {
log.Printf("failed to unlock port lockfile: %v", err)
}
}
}

View File

@ -21,11 +21,11 @@ type stepRun struct {
type qemuArgsTemplateData struct { type qemuArgsTemplateData struct {
HTTPIP string HTTPIP string
HTTPPort uint HTTPPort int
HTTPDir string HTTPDir string
OutputDir string OutputDir string
Name string Name string
SSHHostPort uint SSHHostPort int
} }
func (s *stepRun) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *stepRun) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
@ -63,7 +63,7 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
isoPath := state.Get("iso_path").(string) isoPath := state.Get("iso_path").(string)
vncIP := state.Get("vnc_ip").(string) vncIP := state.Get("vnc_ip").(string)
vncPort := state.Get("vnc_port").(uint) vncPort := state.Get("vnc_port").(int)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
driver := state.Get("driver").(Driver) driver := state.Get("driver").(Driver)
@ -74,12 +74,12 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
defaultArgs := make(map[string]interface{}) defaultArgs := make(map[string]interface{})
var deviceArgs []string var deviceArgs []string
var driveArgs []string var driveArgs []string
var sshHostPort uint var sshHostPort int
defaultArgs["-name"] = vmName defaultArgs["-name"] = vmName
defaultArgs["-machine"] = fmt.Sprintf("type=%s", config.MachineType) defaultArgs["-machine"] = fmt.Sprintf("type=%s", config.MachineType)
if config.Comm.Type != "none" { if config.Comm.Type != "none" {
sshHostPort = state.Get("sshHostPort").(uint) sshHostPort = state.Get("sshHostPort").(int)
defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0,hostfwd=tcp::%v-:%d", sshHostPort, config.Comm.Port()) defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0,hostfwd=tcp::%v-:%d", sshHostPort, config.Comm.Port())
} else { } else {
defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0") defaultArgs["-netdev"] = fmt.Sprintf("user,id=user.0")
@ -121,7 +121,7 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
if vncIpOk && vncPortOk { if vncIpOk && vncPortOk {
vncIp := vncIpRaw.(string) vncIp := vncIpRaw.(string)
vncPort := vncPortRaw.(uint) vncPort := vncPortRaw.(int)
ui.Message(fmt.Sprintf( ui.Message(fmt.Sprintf(
"The VM will be run headless, without a GUI. If you want to\n"+ "The VM will be run headless, without a GUI. If you want to\n"+
@ -175,7 +175,7 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
if len(config.QemuArgs) > 0 { if len(config.QemuArgs) > 0 {
ui.Say("Overriding defaults Qemu arguments with QemuArgs...") ui.Say("Overriding defaults Qemu arguments with QemuArgs...")
httpPort := state.Get("http_port").(uint) httpPort := state.Get("http_port").(int)
ctx := config.ctx ctx := config.ctx
if config.Comm.Type != "none" { if config.Comm.Type != "none" {
ctx.Data = qemuArgsTemplateData{ ctx.Data = qemuArgsTemplateData{

View File

@ -19,7 +19,7 @@ const KeyLeftShift uint32 = 0xFFE1
type bootCommandTemplateData struct { type bootCommandTemplateData struct {
HTTPIP string HTTPIP string
HTTPPort uint HTTPPort int
Name string Name string
} }
@ -29,7 +29,7 @@ type bootCommandTemplateData struct {
// config *config // config *config
// http_port int // http_port int
// ui packer.Ui // ui packer.Ui
// vnc_port uint // vnc_port int
// //
// Produces: // Produces:
// <nothing> // <nothing>
@ -38,9 +38,9 @@ type stepTypeBootCommand struct{}
func (s *stepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *stepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
debug := state.Get("debug").(bool) debug := state.Get("debug").(bool)
httpPort := state.Get("http_port").(uint) httpPort := state.Get("http_port").(int)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vncPort := state.Get("vnc_port").(uint) vncPort := state.Get("vnc_port").(int)
vncIP := state.Get("vnc_ip").(string) vncIP := state.Get("vnc_ip").(string)
if config.VNCConfig.DisableVNC { if config.VNCConfig.DisableVNC {

View File

@ -10,8 +10,8 @@ type RunConfig struct {
Headless bool `mapstructure:"headless"` Headless bool `mapstructure:"headless"`
VRDPBindAddress string `mapstructure:"vrdp_bind_address"` VRDPBindAddress string `mapstructure:"vrdp_bind_address"`
VRDPPortMin uint `mapstructure:"vrdp_port_min"` VRDPPortMin int `mapstructure:"vrdp_port_min"`
VRDPPortMax uint `mapstructure:"vrdp_port_max"` VRDPPortMax int `mapstructure:"vrdp_port_max"`
} }
func (c *RunConfig) Prepare(ctx *interpolate.Context) (errs []error) { func (c *RunConfig) Prepare(ctx *interpolate.Context) (errs []error) {

View File

@ -11,8 +11,8 @@ import (
type SSHConfig struct { type SSHConfig struct {
Comm communicator.Config `mapstructure:",squash"` Comm communicator.Config `mapstructure:",squash"`
SSHHostPortMin uint `mapstructure:"ssh_host_port_min"` SSHHostPortMin int `mapstructure:"ssh_host_port_min"`
SSHHostPortMax uint `mapstructure:"ssh_host_port_max"` SSHHostPortMax int `mapstructure:"ssh_host_port_max"`
SSHSkipNatMapping bool `mapstructure:"ssh_skip_nat_mapping"` SSHSkipNatMapping bool `mapstructure:"ssh_skip_nat_mapping"`
// These are deprecated, but we keep them around for BC // These are deprecated, but we keep them around for BC

View File

@ -4,9 +4,8 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"math/rand"
"net"
"github.com/hashicorp/packer/common/net"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -23,33 +22,33 @@ import (
// vrdp_port unit - The port that VRDP is configured to listen on. // vrdp_port unit - The port that VRDP is configured to listen on.
type StepConfigureVRDP struct { type StepConfigureVRDP struct {
VRDPBindAddress string VRDPBindAddress string
VRDPPortMin uint VRDPPortMin int
VRDPPortMax uint VRDPPortMax int
l *net.Listener
} }
func (s *StepConfigureVRDP) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepConfigureVRDP) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver) driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string) vmName := state.Get("vmName").(string)
log.Printf("Looking for available port between %d and %d on %s", s.VRDPPortMin, s.VRDPPortMax, s.VRDPBindAddress) log.Printf("Looking for available port between %d and %d on %s", s.VRDPPortMin, s.VRDPPortMax, s.VRDPBindAddress)
var vrdpPort uint var err error
portRange := int(s.VRDPPortMax - s.VRDPPortMin) s.l, err = net.ListenRangeConfig{
Addr: s.VRDPBindAddress,
for { Min: s.VRDPPortMin,
if portRange > 0 { Max: s.VRDPPortMax,
vrdpPort = uint(rand.Intn(portRange)) + s.VRDPPortMin Network: "tcp",
} else { }.Listen(ctx)
vrdpPort = s.VRDPPortMin if err != nil {
} err := fmt.Errorf("Error finding port: %s", err)
state.Put("error", err)
log.Printf("Trying port: %d", vrdpPort) ui.Error(err.Error())
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.VRDPBindAddress, vrdpPort)) return multistep.ActionHalt
if err == nil {
defer l.Close()
break
}
} }
s.l.Listener.Close() // free port, but don't unlock lock file
vrdpPort := s.l.Port
command := []string{ command := []string{
"modifyvm", vmName, "modifyvm", vmName,
@ -72,4 +71,11 @@ func (s *StepConfigureVRDP) Run(_ context.Context, state multistep.StateBag) mul
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *StepConfigureVRDP) Cleanup(state multistep.StateBag) {} func (s *StepConfigureVRDP) Cleanup(state multistep.StateBag) {
if s.l != nil {
err := s.l.Close()
if err != nil {
log.Printf("failed to unlock port lockfile: %v", err)
}
}
}

View File

@ -4,9 +4,8 @@ import (
"context" "context"
"fmt" "fmt"
"log" "log"
"math/rand"
"net"
"github.com/hashicorp/packer/common/net"
"github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
@ -23,12 +22,14 @@ import (
// Produces: // Produces:
type StepForwardSSH struct { type StepForwardSSH struct {
CommConfig *communicator.Config CommConfig *communicator.Config
HostPortMin uint HostPortMin int
HostPortMax uint HostPortMax int
SkipNatMapping bool SkipNatMapping bool
l *net.Listener
} }
func (s *StepForwardSSH) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepForwardSSH) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver) driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string) vmName := state.Get("vmName").(string)
@ -45,22 +46,21 @@ func (s *StepForwardSSH) Run(_ context.Context, state multistep.StateBag) multis
log.Printf("Looking for available communicator (SSH, WinRM, etc) port between %d and %d", log.Printf("Looking for available communicator (SSH, WinRM, etc) port between %d and %d",
s.HostPortMin, s.HostPortMax) s.HostPortMin, s.HostPortMax)
portRange := int(s.HostPortMax - s.HostPortMin + 1) var err error
offset := rand.Intn(portRange) s.l, err = net.ListenRangeConfig{
Addr: "127.0.0.1",
for { Min: s.HostPortMin,
sshHostPort = offset + int(s.HostPortMin) Max: s.HostPortMax,
log.Printf("Trying port: %d", sshHostPort) Network: "tcp",
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", sshHostPort)) }.Listen(ctx)
if err == nil { if err != nil {
defer l.Close() err := fmt.Errorf("Error creating port forwarding rule: %s", err)
break state.Put("error", err)
} ui.Error(err.Error())
offset++ return multistep.ActionHalt
if offset == portRange {
offset = 0
}
} }
s.l.Listener.Close() // free port, but don't unlock lock file
sshHostPort = s.l.Port
// Create a forwarded port mapping to the VM // Create a forwarded port mapping to the VM
ui.Say(fmt.Sprintf("Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port %d)", sshHostPort)) ui.Say(fmt.Sprintf("Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port %d)", sshHostPort))
@ -83,4 +83,11 @@ func (s *StepForwardSSH) Run(_ context.Context, state multistep.StateBag) multis
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *StepForwardSSH) Cleanup(state multistep.StateBag) {} func (s *StepForwardSSH) Cleanup(state multistep.StateBag) {
if s.l != nil {
err := s.l.Close()
if err != nil {
log.Printf("failed to unlock port lockfile: %v", err)
}
}
}

View File

@ -35,7 +35,7 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste
if vrdpIpOk && vrdpPortOk { if vrdpIpOk && vrdpPortOk {
vrdpIp := vrdpIpRaw.(string) vrdpIp := vrdpIpRaw.(string)
vrdpPort := vrdpPortRaw.(uint) vrdpPort := vrdpPortRaw.(int)
ui.Message(fmt.Sprintf( ui.Message(fmt.Sprintf(
"The VM will be run headless, without a GUI. If you want to\n"+ "The VM will be run headless, without a GUI. If you want to\n"+

View File

@ -22,7 +22,7 @@ type bootCommandTemplateData struct {
HTTPIP string HTTPIP string
// HTTPPort is the HTTP server port. // HTTPPort is the HTTP server port.
HTTPPort uint HTTPPort int
// Name is the VM's name. // Name is the VM's name.
Name string Name string
@ -43,7 +43,7 @@ type StepTypeBootCommand struct {
func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
debug := state.Get("debug").(bool) debug := state.Get("debug").(bool)
driver := state.Get("driver").(Driver) driver := state.Get("driver").(Driver)
httpPort := state.Get("http_port").(uint) httpPort := state.Get("http_port").(int)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string) vmName := state.Get("vmName").(string)

View File

@ -20,7 +20,7 @@ type DriverConfig struct {
RemoteCacheDatastore string `mapstructure:"remote_cache_datastore"` RemoteCacheDatastore string `mapstructure:"remote_cache_datastore"`
RemoteCacheDirectory string `mapstructure:"remote_cache_directory"` RemoteCacheDirectory string `mapstructure:"remote_cache_directory"`
RemoteHost string `mapstructure:"remote_host"` RemoteHost string `mapstructure:"remote_host"`
RemotePort uint `mapstructure:"remote_port"` RemotePort int `mapstructure:"remote_port"`
RemoteUser string `mapstructure:"remote_username"` RemoteUser string `mapstructure:"remote_username"`
RemotePassword string `mapstructure:"remote_password"` RemotePassword string `mapstructure:"remote_password"`
RemotePrivateKey string `mapstructure:"remote_private_key_file"` RemotePrivateKey string `mapstructure:"remote_private_key_file"`

View File

@ -3,6 +3,7 @@ package common
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"encoding/csv" "encoding/csv"
"errors" "errors"
"fmt" "fmt"
@ -30,7 +31,7 @@ type ESX5Driver struct {
base VmwareDriver base VmwareDriver
Host string Host string
Port uint Port int
Username string Username string
Password string Password string
PrivateKeyFile string PrivateKeyFile string
@ -359,8 +360,8 @@ func (d *ESX5Driver) GuestAddress(multistep.StateBag) (string, error) {
return result, nil return result, nil
} }
func (d *ESX5Driver) VNCAddress(_ string, portMin, portMax uint) (string, uint, error) { func (d *ESX5Driver) VNCAddress(ctx context.Context, _ string, portMin, portMax int) (string, int, error) {
var vncPort uint var vncPort int
//Process ports ESXi is listening on to determine which are available //Process ports ESXi is listening on to determine which are available
//This process does best effort to detect ports that are unavailable, //This process does best effort to detect ports that are unavailable,
@ -426,7 +427,7 @@ func (d *ESX5Driver) VNCAddress(_ string, portMin, portMax uint) (string, uint,
} }
// UpdateVMX, adds the VNC port to the VMX data. // UpdateVMX, adds the VNC port to the VMX data.
func (ESX5Driver) UpdateVMX(_, password string, port uint, data map[string]string) { func (ESX5Driver) UpdateVMX(_, password string, port int, data map[string]string) {
// Do not set remotedisplay.vnc.ip - this breaks ESXi. // Do not set remotedisplay.vnc.ip - this breaks ESXi.
data["remotedisplay.vnc.enabled"] = "TRUE" data["remotedisplay.vnc.enabled"] = "TRUE"
data["remotedisplay.vnc.port"] = fmt.Sprintf("%d", port) data["remotedisplay.vnc.port"] = fmt.Sprintf("%d", port)

View File

@ -50,7 +50,7 @@ func TestESX5Driver_HostIP(t *testing.T) {
port := listen.Addr().(*net.TCPAddr).Port port := listen.Addr().(*net.TCPAddr).Port
defer listen.Close() defer listen.Close()
driver := ESX5Driver{Host: "localhost", Port: uint(port)} driver := ESX5Driver{Host: "localhost", Port: port}
state := new(multistep.BasicStateBag) state := new(multistep.BasicStateBag)
if host, _ := driver.HostIP(state); host != expected_host { if host, _ := driver.HostIP(state); host != expected_host {

View File

@ -10,8 +10,8 @@ type RunConfig struct {
Headless bool `mapstructure:"headless"` Headless bool `mapstructure:"headless"`
VNCBindAddress string `mapstructure:"vnc_bind_address"` VNCBindAddress string `mapstructure:"vnc_bind_address"`
VNCPortMin uint `mapstructure:"vnc_port_min"` VNCPortMin int `mapstructure:"vnc_port_min"`
VNCPortMax uint `mapstructure:"vnc_port_max"` VNCPortMax int `mapstructure:"vnc_port_max"`
VNCDisablePassword bool `mapstructure:"vnc_disable_password"` VNCDisablePassword bool `mapstructure:"vnc_disable_password"`
} }
@ -31,6 +31,9 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) (errs []error) {
if c.VNCPortMin > c.VNCPortMax { if c.VNCPortMin > c.VNCPortMax {
errs = append(errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max")) errs = append(errs, fmt.Errorf("vnc_port_min must be less than vnc_port_max"))
} }
if c.VNCPortMin < 0 {
errs = append(errs, fmt.Errorf("vnc_port_min must be positive"))
}
return return
} }

View File

@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"log" "log"
"math/rand" "math/rand"
"net"
"github.com/hashicorp/packer/common/net"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -18,43 +18,38 @@ import (
// vmx_path string // vmx_path string
// //
// Produces: // Produces:
// vnc_port uint - The port that VNC is configured to listen on. // vnc_port int - The port that VNC is configured to listen on.
type StepConfigureVNC struct { type StepConfigureVNC struct {
Enabled bool Enabled bool
VNCBindAddress string VNCBindAddress string
VNCPortMin uint VNCPortMin int
VNCPortMax uint VNCPortMax int
VNCDisablePassword bool VNCDisablePassword bool
l *net.Listener
} }
type VNCAddressFinder interface { type VNCAddressFinder interface {
VNCAddress(string, uint, uint) (string, uint, error) VNCAddress(context.Context, string, int, int) (string, int, error)
// UpdateVMX, sets driver specific VNC values to VMX data. // UpdateVMX, sets driver specific VNC values to VMX data.
UpdateVMX(vncAddress, vncPassword string, vncPort uint, vmxData map[string]string) UpdateVMX(vncAddress, vncPassword string, vncPort int, vmxData map[string]string)
} }
func (StepConfigureVNC) VNCAddress(vncBindAddress string, portMin, portMax uint) (string, uint, error) { func (s *StepConfigureVNC) VNCAddress(ctx context.Context, vncBindAddress string, portMin, portMax int) (string, int, error) {
// Find an open VNC port. Note that this can still fail later on var err error
// because we have to release the port at some point. But this does its s.l, err = net.ListenRangeConfig{
// best. Addr: s.VNCBindAddress,
var vncPort uint Min: s.VNCPortMin,
portRange := int(portMax - portMin) Max: s.VNCPortMax,
for { Network: "tcp",
if portRange > 0 { }.Listen(ctx)
vncPort = uint(rand.Intn(portRange)) + portMin if err != nil {
} else { return "", 0, err
vncPort = portMin
}
log.Printf("Trying port: %d", vncPort)
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", vncBindAddress, vncPort))
if err == nil {
defer l.Close()
break
}
} }
return vncBindAddress, vncPort, nil
s.l.Listener.Close() // free port, but don't unlock lock file
return s.l.Address, s.l.Port, nil
} }
func VNCPassword(skipPassword bool) string { func VNCPassword(skipPassword bool) string {
@ -75,7 +70,7 @@ func VNCPassword(skipPassword bool) string {
return string(password) return string(password)
} }
func (s *StepConfigureVNC) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepConfigureVNC) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if !s.Enabled { if !s.Enabled {
log.Println("Skipping VNC configuration step...") log.Println("Skipping VNC configuration step...")
return multistep.ActionContinue return multistep.ActionContinue
@ -99,8 +94,10 @@ func (s *StepConfigureVNC) Run(_ context.Context, state multistep.StateBag) mult
} else { } else {
vncFinder = s vncFinder = s
} }
log.Printf("Looking for available port between %d and %d", s.VNCPortMin, s.VNCPortMax) log.Printf("Looking for available port between %d and %d", s.VNCPortMin, s.VNCPortMax)
vncBindAddress, vncPort, err := vncFinder.VNCAddress(s.VNCBindAddress, s.VNCPortMin, s.VNCPortMax) vncBindAddress, vncPort, err := vncFinder.VNCAddress(ctx, s.VNCBindAddress, s.VNCPortMin, s.VNCPortMax)
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
@ -109,7 +106,7 @@ func (s *StepConfigureVNC) Run(_ context.Context, state multistep.StateBag) mult
vncPassword := VNCPassword(s.VNCDisablePassword) vncPassword := VNCPassword(s.VNCDisablePassword)
log.Printf("Found available VNC port: %d", vncPort) log.Printf("Found available VNC port: %v", s.l)
vncFinder.UpdateVMX(vncBindAddress, vncPassword, vncPort, vmxData) vncFinder.UpdateVMX(vncBindAddress, vncPassword, vncPort, vmxData)
@ -120,14 +117,14 @@ func (s *StepConfigureVNC) Run(_ context.Context, state multistep.StateBag) mult
return multistep.ActionHalt return multistep.ActionHalt
} }
state.Put("vnc_port", vncPort) state.Put("vnc_port", s.l.Port)
state.Put("vnc_ip", vncBindAddress) state.Put("vnc_ip", s.l.Address)
state.Put("vnc_password", vncPassword) state.Put("vnc_password", vncPassword)
return multistep.ActionContinue return multistep.ActionContinue
} }
func (StepConfigureVNC) UpdateVMX(address, password string, port uint, data map[string]string) { func (*StepConfigureVNC) UpdateVMX(address, password string, port int, data map[string]string) {
data["remotedisplay.vnc.enabled"] = "TRUE" data["remotedisplay.vnc.enabled"] = "TRUE"
data["remotedisplay.vnc.port"] = fmt.Sprintf("%d", port) data["remotedisplay.vnc.port"] = fmt.Sprintf("%d", port)
data["remotedisplay.vnc.ip"] = address data["remotedisplay.vnc.ip"] = address
@ -136,5 +133,10 @@ func (StepConfigureVNC) UpdateVMX(address, password string, port uint, data map[
} }
} }
func (StepConfigureVNC) Cleanup(multistep.StateBag) { func (s *StepConfigureVNC) Cleanup(multistep.StateBag) {
if s.l != nil {
if err := s.l.Close(); err != nil {
log.Printf("failed to unlock port lockfile: %v", err)
}
}
} }

View File

@ -43,7 +43,7 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste
if vncIpOk && vncPortOk && vncPasswordOk { if vncIpOk && vncPortOk && vncPasswordOk {
vncIp := vncIpRaw.(string) vncIp := vncIpRaw.(string)
vncPort := vncPortRaw.(uint) vncPort := vncPortRaw.(int)
vncPassword := vncPasswordRaw.(string) vncPassword := vncPasswordRaw.(string)
ui.Message(fmt.Sprintf( ui.Message(fmt.Sprintf(

View File

@ -20,7 +20,7 @@ import (
// Uses: // Uses:
// http_port int // http_port int
// ui packer.Ui // ui packer.Ui
// vnc_port uint // vnc_port int
// //
// Produces: // Produces:
// <nothing> // <nothing>
@ -34,7 +34,7 @@ type StepTypeBootCommand struct {
} }
type bootCommandTemplateData struct { type bootCommandTemplateData struct {
HTTPIP string HTTPIP string
HTTPPort uint HTTPPort int
Name string Name string
} }
@ -46,10 +46,10 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag)
debug := state.Get("debug").(bool) debug := state.Get("debug").(bool)
driver := state.Get("driver").(Driver) driver := state.Get("driver").(Driver)
httpPort := state.Get("http_port").(uint) httpPort := state.Get("http_port").(int)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
vncIp := state.Get("vnc_ip").(string) vncIp := state.Get("vnc_ip").(string)
vncPort := state.Get("vnc_port").(uint) vncPort := state.Get("vnc_port").(int)
vncPassword := state.Get("vnc_password") vncPassword := state.Get("vnc_password")
// Wait the for the vm to boot. // Wait the for the vm to boot.

View File

@ -9,8 +9,8 @@ import (
// HTTPConfig contains configuration for the local HTTP Server // HTTPConfig contains configuration for the local HTTP Server
type HTTPConfig struct { type HTTPConfig struct {
HTTPDir string `mapstructure:"http_directory"` HTTPDir string `mapstructure:"http_directory"`
HTTPPortMin uint `mapstructure:"http_port_min"` HTTPPortMin int `mapstructure:"http_port_min"`
HTTPPortMax uint `mapstructure:"http_port_max"` HTTPPortMax int `mapstructure:"http_port_max"`
} }
func (c *HTTPConfig) Prepare(ctx *interpolate.Context) []error { func (c *HTTPConfig) Prepare(ctx *interpolate.Context) []error {

View File

@ -24,11 +24,11 @@ func TestHTTPConfigPrepare_Bounds(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("should not have error: %s", err) t.Fatalf("should not have error: %s", err)
} }
portMin := uint(8000) portMin := 8000
if h.HTTPPortMin != portMin { if h.HTTPPortMin != portMin {
t.Fatalf("HTTPPortMin: expected %d got %d", portMin, h.HTTPPortMin) t.Fatalf("HTTPPortMin: expected %d got %d", portMin, h.HTTPPortMin)
} }
portMax := uint(9000) portMax := 9000
if h.HTTPPortMax != portMax { if h.HTTPPortMax != portMax {
t.Fatalf("HTTPPortMax: expected %d got %d", portMax, h.HTTPPortMax) t.Fatalf("HTTPPortMax: expected %d got %d", portMax, h.HTTPPortMax)
} }

View File

@ -0,0 +1,99 @@
package net
import (
"context"
"fmt"
"log"
"math/rand"
"net"
"strconv"
"github.com/gofrs/flock"
"github.com/hashicorp/packer/packer"
)
var _ net.Listener = &Listener{}
// Listener wraps a net.Lister with some magic packer capabilies. For example
// until you call Listener.Close, any call to ListenRangeConfig.Listen cannot
// bind to Port. Packer tries tells moving parts which port they can use, but
// often the port has to be released before a 3rd party is started, like a VNC
// server.
type Listener struct {
// Listener can be closed but Port will be file locked by packer until
// Close is called.
net.Listener
Port int
Address string
lock *flock.Flock
}
func (l *Listener) Close() error {
err := l.lock.Unlock()
if err != nil {
log.Printf("cannot unlock lockfile %#v: %v", l, err)
}
return l.Listener.Close()
}
// ListenRangeConfig contains options for listening to a free address [Min,Max)
// range. ListenRangeConfig wraps a net.ListenConfig.
type ListenRangeConfig struct {
// tcp", "udp"
Network string
Addr string
Min, Max int
net.ListenConfig
}
// Listen tries to Listen to a random open TCP port in the [min, max) range
// until ctx is cancelled.
// Listen uses net.ListenConfig.Listen internally.
func (lc ListenRangeConfig) Listen(ctx context.Context) (*Listener, error) {
portRange := lc.Max - lc.Min
for {
if err := ctx.Err(); err != nil {
return nil, err
}
port := lc.Min
if portRange > 0 {
port += rand.Intn(portRange)
}
log.Printf("Trying port: %d", port)
lockFilePath, err := packer.CachePath("port", strconv.Itoa(port))
if err != nil {
return nil, err
}
lock := flock.New(lockFilePath)
locked, err := lock.TryLock()
if err != nil {
return nil, err
}
if !locked {
continue // this port seems to be locked by another packer goroutine
}
l, err := lc.ListenConfig.Listen(ctx, lc.Network, fmt.Sprintf("%s:%d", lc.Addr, port))
if err != nil {
if err := lock.Unlock(); err != nil {
log.Printf("Could not unlock file lock for port %d: %v", port, err)
}
continue // this port is most likely already open
}
log.Printf("Found available port: %d on IP: %s", port, lc.Addr)
return &Listener{
Address: lc.Addr,
Port: port,
Listener: l,
lock: lock,
}, err
}
}

View File

@ -3,11 +3,10 @@ package common
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"math/rand"
"net"
"net/http" "net/http"
"github.com/hashicorp/packer/common/net"
"github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/common"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
@ -24,44 +23,38 @@ import (
// http_port int - The port the HTTP server started on. // http_port int - The port the HTTP server started on.
type StepHTTPServer struct { type StepHTTPServer struct {
HTTPDir string HTTPDir string
HTTPPortMin uint HTTPPortMin int
HTTPPortMax uint HTTPPortMax int
l net.Listener l *net.Listener
} }
func (s *StepHTTPServer) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepHTTPServer) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
var httpPort uint = 0
if s.HTTPDir == "" { if s.HTTPDir == "" {
state.Put("http_port", httpPort) state.Put("http_port", 0)
return multistep.ActionContinue return multistep.ActionContinue
} }
// Find an available TCP port for our HTTP server // Find an available TCP port for our HTTP server
var httpAddr string var httpAddr string
portRange := int(s.HTTPPortMax - s.HTTPPortMin) var err error
for { s.l, err = net.ListenRangeConfig{
var err error Min: s.HTTPPortMin,
var offset uint = 0 Max: s.HTTPPortMax,
Addr: "0.0.0.0",
Network: "tcp",
}.Listen(ctx)
if portRange > 0 { if err != nil {
// Intn will panic if portRange == 0, so we do a check. err := fmt.Errorf("Error finding port: %s", err)
// Intn is from [0, n), so add 1 to make from [0, n] state.Put("error", err)
offset = uint(rand.Intn(portRange + 1)) ui.Error(err.Error())
} return multistep.ActionHalt
httpPort = offset + s.HTTPPortMin
httpAddr = fmt.Sprintf("0.0.0.0:%d", httpPort)
log.Printf("Trying port: %d", httpPort)
s.l, err = net.Listen("tcp", httpAddr)
if err == nil {
break
}
} }
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort)) ui.Say(fmt.Sprintf("Starting HTTP server on port %d", s.l.Port))
// Start the HTTP server and run it in the background // Start the HTTP server and run it in the background
fileServer := http.FileServer(http.Dir(s.HTTPDir)) fileServer := http.FileServer(http.Dir(s.HTTPDir))
@ -69,8 +62,8 @@ func (s *StepHTTPServer) Run(_ context.Context, state multistep.StateBag) multis
go server.Serve(s.l) go server.Serve(s.l)
// Save the address into the state so it can be accessed in the future // Save the address into the state so it can be accessed in the future
state.Put("http_port", httpPort) state.Put("http_port", s.l.Port)
SetHTTPPort(fmt.Sprintf("%d", httpPort)) SetHTTPPort(fmt.Sprintf("%d", s.l.Port))
return multistep.ActionContinue return multistep.ActionContinue
} }

View File

@ -24,8 +24,8 @@ const PACKERSPACE = "-PACKERSPACE-"
type config struct { type config struct {
DisableCheckpoint bool `json:"disable_checkpoint"` DisableCheckpoint bool `json:"disable_checkpoint"`
DisableCheckpointSignature bool `json:"disable_checkpoint_signature"` DisableCheckpointSignature bool `json:"disable_checkpoint_signature"`
PluginMinPort uint PluginMinPort int
PluginMaxPort uint PluginMaxPort int
Builders map[string]string Builders map[string]string
PostProcessors map[string]string `json:"post-processors"` PostProcessors map[string]string `json:"post-processors"`

View File

@ -186,7 +186,8 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan stru
Timeout: s.Config.SSHReadWriteTimeout, Timeout: s.Config.SSHReadWriteTimeout,
} }
log.Println("[INFO] Attempting SSH connection...") log.Printf("[INFO] Attempting SSH connection to %s...", address)
log.Printf("[DEBUG] Config to %#v...", config)
comm, err = ssh.New(address, config) comm, err = ssh.New(address, config)
if err != nil { if err != nil {
log.Printf("[DEBUG] SSH handshake err: %s", err) log.Printf("[DEBUG] SSH handshake err: %s", err)

View File

@ -11,6 +11,7 @@ var DefaultCacheDir = "packer_cache"
// //
// When the directory is not absolute, CachePath will try to get // When the directory is not absolute, CachePath will try to get
// current working directory to be able to return a full path. // current working directory to be able to return a full path.
// CachePath tries to create the resulting path if it doesn't exist.
// //
// CachePath can error in case it cannot find the cwd. // CachePath can error in case it cannot find the cwd.
// //
@ -19,7 +20,11 @@ var DefaultCacheDir = "packer_cache"
// PACKER_CACHE_DIR="" CacheDir("foo") => "./packer_cache/foo // PACKER_CACHE_DIR="" CacheDir("foo") => "./packer_cache/foo
// PACKER_CACHE_DIR="bar" CacheDir("foo") => "./bar/foo // PACKER_CACHE_DIR="bar" CacheDir("foo") => "./bar/foo
// PACKER_CACHE_DIR="/home/there" CacheDir("foo", "bar") => "/home/there/foo/bar // PACKER_CACHE_DIR="/home/there" CacheDir("foo", "bar") => "/home/there/foo/bar
func CachePath(paths ...string) (string, error) { func CachePath(paths ...string) (path string, err error) {
defer func() {
// create the dir based on return path it it doesn't exist
os.MkdirAll(filepath.Base(path), os.ModePerm)
}()
cacheDir := DefaultCacheDir cacheDir := DefaultCacheDir
if cd := os.Getenv("PACKER_CACHE_DIR"); cd != "" { if cd := os.Getenv("PACKER_CACHE_DIR"); cd != "" {
cacheDir = cd cacheDir = cd

View File

@ -56,7 +56,7 @@ type ClientConfig struct {
// The minimum and maximum port to use for communicating with // The minimum and maximum port to use for communicating with
// the subprocess. If not set, this defaults to 10,000 and 25,000 // the subprocess. If not set, this defaults to 10,000 and 25,000
// respectively. // respectively.
MinPort, MaxPort uint MinPort, MaxPort int
// StartTimeout is the timeout to wait for the plugin to say it // StartTimeout is the timeout to wait for the plugin to say it
// has started successfully. // has started successfully.

View File

@ -52,7 +52,7 @@ type Config struct {
EmptyGroups []string `mapstructure:"empty_groups"` EmptyGroups []string `mapstructure:"empty_groups"`
HostAlias string `mapstructure:"host_alias"` HostAlias string `mapstructure:"host_alias"`
User string `mapstructure:"user"` User string `mapstructure:"user"`
LocalPort uint `mapstructure:"local_port"` LocalPort int `mapstructure:"local_port"`
SSHHostKeyFile string `mapstructure:"ssh_host_key_file"` SSHHostKeyFile string `mapstructure:"ssh_host_key_file"`
SSHAuthorizedKeyFile string `mapstructure:"ssh_authorized_key_file"` SSHAuthorizedKeyFile string `mapstructure:"ssh_authorized_key_file"`
SFTPCmd string `mapstructure:"sftp_command"` SFTPCmd string `mapstructure:"sftp_command"`
@ -271,12 +271,11 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
ui.Say(err.Error()) ui.Say(err.Error())
continue continue
} }
portUint64, err := strconv.ParseUint(portStr, 10, 0) p.config.LocalPort, err = strconv.Atoi(portStr)
if err != nil { if err != nil {
ui.Say(err.Error()) ui.Say(err.Error())
continue continue
} }
p.config.LocalPort = uint(portUint64)
return l, nil return l, nil
} }
return nil, errors.New("Error setting up SSH proxy connection") return nil, errors.New("Error setting up SSH proxy connection")

View File

@ -245,13 +245,13 @@ func TestProvisionerPrepare_LocalPort(t *testing.T) {
config["ssh_authorized_key_file"] = publickey_file.Name() config["ssh_authorized_key_file"] = publickey_file.Name()
config["playbook_file"] = playbook_file.Name() config["playbook_file"] = playbook_file.Name()
config["local_port"] = uint(65537) config["local_port"] = 65537
err = p.Prepare(config) err = p.Prepare(config)
if err == nil { if err == nil {
t.Fatal("should have error") t.Fatal("should have error")
} }
config["local_port"] = uint(22222) config["local_port"] = 22222
err = p.Prepare(config) err = p.Prepare(config)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)

View File

@ -52,7 +52,7 @@ type Config struct {
Backend string `mapstructure:"backend"` Backend string `mapstructure:"backend"`
User string `mapstructure:"user"` User string `mapstructure:"user"`
Host string `mapstructure:"host"` Host string `mapstructure:"host"`
LocalPort uint `mapstructure:"local_port"` LocalPort int `mapstructure:"local_port"`
SSHHostKeyFile string `mapstructure:"ssh_host_key_file"` SSHHostKeyFile string `mapstructure:"ssh_host_key_file"`
SSHAuthorizedKeyFile string `mapstructure:"ssh_authorized_key_file"` SSHAuthorizedKeyFile string `mapstructure:"ssh_authorized_key_file"`
} }
@ -264,12 +264,11 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
ui.Say(err.Error()) ui.Say(err.Error())
continue continue
} }
portUint64, err := strconv.ParseUint(portStr, 10, 0) p.config.LocalPort, err = strconv.Atoi(portStr)
if err != nil { if err != nil {
ui.Say(err.Error()) ui.Say(err.Error())
continue continue
} }
p.config.LocalPort = uint(portUint64)
return l, nil return l, nil
} }
return nil, errors.New("Error setting up SSH proxy connection") return nil, errors.New("Error setting up SSH proxy connection")
@ -338,7 +337,7 @@ func (p *Provisioner) executeInspec(ui packer.Ui, comm packer.Communicator, priv
args = append(args, "--key-files", privKeyFile) args = append(args, "--key-files", privKeyFile)
} }
args = append(args, "--user", p.config.User) args = append(args, "--user", p.config.User)
args = append(args, "--port", strconv.FormatUint(uint64(p.config.LocalPort), 10)) args = append(args, "--port", strconv.Itoa(p.config.LocalPort))
} }
args = append(args, "--attrs") args = append(args, "--attrs")

View File

@ -254,13 +254,13 @@ func TestProvisionerPrepare_LocalPort(t *testing.T) {
config["ssh_authorized_key_file"] = publickey_file.Name() config["ssh_authorized_key_file"] = publickey_file.Name()
config["profile"] = profile_file.Name() config["profile"] = profile_file.Name()
config["local_port"] = uint(65537) config["local_port"] = 65537
err = p.Prepare(config) err = p.Prepare(config)
if err == nil { if err == nil {
t.Fatal("should have error") t.Fatal("should have error")
} }
config["local_port"] = uint(22222) config["local_port"] = 22222
err = p.Prepare(config) err = p.Prepare(config)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)