builder/vmware: Have an overall ssh wait timeout
This commit is contained in:
parent
ca39d23636
commit
a3800625ac
|
@ -5,6 +5,7 @@ import (
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const BuilderId = "mitchellh.vmware"
|
const BuilderId = "mitchellh.vmware"
|
||||||
|
@ -15,15 +16,18 @@ type Builder struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
DiskName string `mapstructure:"vmdk_name"`
|
DiskName string `mapstructure:"vmdk_name"`
|
||||||
ISOUrl string `mapstructure:"iso_url"`
|
ISOUrl string `mapstructure:"iso_url"`
|
||||||
VMName string `mapstructure:"vm_name"`
|
VMName string `mapstructure:"vm_name"`
|
||||||
OutputDir string `mapstructure:"output_directory"`
|
OutputDir string `mapstructure:"output_directory"`
|
||||||
HTTPDir string `mapstructure:"http_directory"`
|
HTTPDir string `mapstructure:"http_directory"`
|
||||||
BootCommand []string `mapstructure:"boot_command"`
|
BootCommand []string `mapstructure:"boot_command"`
|
||||||
BootWait uint `mapstructure:"boot_wait"`
|
BootWait uint `mapstructure:"boot_wait"`
|
||||||
SSHUser string `mapstructure:"ssh_user"`
|
SSHUser string `mapstructure:"ssh_user"`
|
||||||
SSHPassword string `mapstructure:"ssh_password"`
|
SSHPassword string `mapstructure:"ssh_password"`
|
||||||
|
SSHWaitTimeout time.Duration
|
||||||
|
|
||||||
|
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) Prepare(raw interface{}) (err error) {
|
func (b *Builder) Prepare(raw interface{}) (err error) {
|
||||||
|
@ -44,6 +48,15 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
|
||||||
b.config.OutputDir = "vmware"
|
b.config.OutputDir = "vmware"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.config.RawSSHWaitTimeout == "" {
|
||||||
|
b.config.RawSSHWaitTimeout = "20m"
|
||||||
|
}
|
||||||
|
|
||||||
|
b.config.SSHWaitTimeout, err = time.ParseDuration(b.config.RawSSHWaitTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,64 +24,47 @@ import (
|
||||||
//
|
//
|
||||||
// Produces:
|
// Produces:
|
||||||
// communicator packer.Communicator
|
// communicator packer.Communicator
|
||||||
type stepWaitForSSH struct{}
|
type stepWaitForSSH struct {
|
||||||
|
cancel bool
|
||||||
|
conn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stepWaitForSSH) Run(state map[string]interface{}) multistep.StepAction {
|
func (s *stepWaitForSSH) Run(state map[string]interface{}) multistep.StepAction {
|
||||||
config := state["config"].(*config)
|
config := state["config"].(*config)
|
||||||
ui := state["ui"].(packer.Ui)
|
ui := state["ui"].(packer.Ui)
|
||||||
vmxPath := state["vmx_path"].(string)
|
|
||||||
|
|
||||||
ui.Say("Waiting for SSH to become available...")
|
|
||||||
var comm packer.Communicator
|
var comm packer.Communicator
|
||||||
for {
|
var err error
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
// First we wait for the IP to become available...
|
waitDone := make(chan bool, 1)
|
||||||
log.Println("Lookup up IP information...")
|
go func() {
|
||||||
ipLookup, err := s.dhcpLeaseLookup(vmxPath)
|
comm, err = s.waitForSSH(state)
|
||||||
|
waitDone <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-waitDone:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Can't lookup via DHCP lease: %s", err)
|
ui.Error(fmt.Sprintf("Error waiting for SSH: %s", err))
|
||||||
}
|
|
||||||
|
|
||||||
ip, err := ipLookup.GuestIP()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("IP lookup failed: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Detected IP: %s", ip)
|
|
||||||
|
|
||||||
// Attempt to connect to SSH port
|
|
||||||
nc, err := net.Dial("tcp", fmt.Sprintf("%s:22", ip))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("TCP connection to SSH ip/port failed: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then we attempt to connect via SSH
|
|
||||||
sshConfig := &gossh.ClientConfig{
|
|
||||||
User: config.SSHUser,
|
|
||||||
Auth: []gossh.ClientAuth{
|
|
||||||
gossh.ClientAuthPassword(ssh.Password(config.SSHPassword)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
comm, err = ssh.New(nc, sshConfig)
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf("Error connecting via SSH: %s", err))
|
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Say("Connected via SSH!")
|
state["communicator"] = comm
|
||||||
break
|
case <-time.After(config.SSHWaitTimeout):
|
||||||
|
ui.Error("Timeout waiting for SSH.")
|
||||||
|
s.cancel = true
|
||||||
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
state["communicator"] = comm
|
|
||||||
|
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stepWaitForSSH) Cleanup(map[string]interface{}) {}
|
func (s *stepWaitForSSH) Cleanup(map[string]interface{}) {
|
||||||
|
if s.conn != nil {
|
||||||
|
s.conn.Close()
|
||||||
|
s.conn = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reads the network information for lookup via DHCP.
|
// Reads the network information for lookup via DHCP.
|
||||||
func (s *stepWaitForSSH) dhcpLeaseLookup(vmxPath string) (GuestIPFinder, error) {
|
func (s *stepWaitForSSH) dhcpLeaseLookup(vmxPath string) (GuestIPFinder, error) {
|
||||||
|
@ -108,3 +91,69 @@ func (s *stepWaitForSSH) dhcpLeaseLookup(vmxPath string) (GuestIPFinder, error)
|
||||||
|
|
||||||
return &DHCPLeaseGuestLookup{"vmnet8", macAddress}, nil
|
return &DHCPLeaseGuestLookup{"vmnet8", macAddress}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This blocks until SSH becomes available, and sends the communicator
|
||||||
|
// on the given channel.
|
||||||
|
func (s *stepWaitForSSH) waitForSSH(state map[string]interface{}) (packer.Communicator, error) {
|
||||||
|
config := state["config"].(*config)
|
||||||
|
ui := state["ui"].(packer.Ui)
|
||||||
|
vmxPath := state["vmx_path"].(string)
|
||||||
|
|
||||||
|
ui.Say("Waiting for SSH to become available...")
|
||||||
|
var comm packer.Communicator
|
||||||
|
var nc net.Conn
|
||||||
|
for {
|
||||||
|
if nc != nil {
|
||||||
|
nc.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
|
if s.cancel {
|
||||||
|
log.Println("SSH wait cancelled. Exiting loop.")
|
||||||
|
return nil, errors.New("SSH wait cancelled")
|
||||||
|
}
|
||||||
|
|
||||||
|
// First we wait for the IP to become available...
|
||||||
|
log.Println("Lookup up IP information...")
|
||||||
|
ipLookup, err := s.dhcpLeaseLookup(vmxPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Can't lookup via DHCP lease: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := ipLookup.GuestIP()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("IP lookup failed: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Detected IP: %s", ip)
|
||||||
|
|
||||||
|
// Attempt to connect to SSH port
|
||||||
|
nc, err = net.Dial("tcp", fmt.Sprintf("%s:22", ip))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("TCP connection to SSH ip/port failed: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then we attempt to connect via SSH
|
||||||
|
sshConfig := &gossh.ClientConfig{
|
||||||
|
User: config.SSHUser,
|
||||||
|
Auth: []gossh.ClientAuth{
|
||||||
|
gossh.ClientAuthPassword(ssh.Password(config.SSHPassword)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
comm, err = ssh.New(nc, sshConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Connected via SSH!")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the connection so we can close it later
|
||||||
|
s.conn = nc
|
||||||
|
return comm, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue