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/packer/packer"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
const BuilderId = "mitchellh.vmware"
|
||||
|
@ -15,15 +16,18 @@ type Builder struct {
|
|||
}
|
||||
|
||||
type config struct {
|
||||
DiskName string `mapstructure:"vmdk_name"`
|
||||
ISOUrl string `mapstructure:"iso_url"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
OutputDir string `mapstructure:"output_directory"`
|
||||
HTTPDir string `mapstructure:"http_directory"`
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
BootWait uint `mapstructure:"boot_wait"`
|
||||
SSHUser string `mapstructure:"ssh_user"`
|
||||
SSHPassword string `mapstructure:"ssh_password"`
|
||||
DiskName string `mapstructure:"vmdk_name"`
|
||||
ISOUrl string `mapstructure:"iso_url"`
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
OutputDir string `mapstructure:"output_directory"`
|
||||
HTTPDir string `mapstructure:"http_directory"`
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
BootWait uint `mapstructure:"boot_wait"`
|
||||
SSHUser string `mapstructure:"ssh_user"`
|
||||
SSHPassword string `mapstructure:"ssh_password"`
|
||||
SSHWaitTimeout time.Duration
|
||||
|
||||
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
|
||||
}
|
||||
|
||||
func (b *Builder) Prepare(raw interface{}) (err error) {
|
||||
|
@ -44,6 +48,15 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -24,64 +24,47 @@ import (
|
|||
//
|
||||
// Produces:
|
||||
// communicator packer.Communicator
|
||||
type stepWaitForSSH struct{}
|
||||
type stepWaitForSSH struct {
|
||||
cancel bool
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func (s *stepWaitForSSH) Run(state map[string]interface{}) multistep.StepAction {
|
||||
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
|
||||
for {
|
||||
time.Sleep(5 * time.Second)
|
||||
var err error
|
||||
|
||||
// First we wait for the IP to become available...
|
||||
log.Println("Lookup up IP information...")
|
||||
ipLookup, err := s.dhcpLeaseLookup(vmxPath)
|
||||
waitDone := make(chan bool, 1)
|
||||
go func() {
|
||||
comm, err = s.waitForSSH(state)
|
||||
waitDone <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-waitDone:
|
||||
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 {
|
||||
ui.Error(fmt.Sprintf("Error connecting via SSH: %s", err))
|
||||
ui.Error(fmt.Sprintf("Error waiting for SSH: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Connected via SSH!")
|
||||
break
|
||||
state["communicator"] = comm
|
||||
case <-time.After(config.SSHWaitTimeout):
|
||||
ui.Error("Timeout waiting for SSH.")
|
||||
s.cancel = true
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state["communicator"] = comm
|
||||
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
// 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