builder/amazonebs: Timeouts while waiting for SSH to connect
This commit is contained in:
parent
59b5902619
commit
de9c4ace9e
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
"github.com/mitchellh/packer/packer"
|
"github.com/mitchellh/packer/packer"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The unique ID for this builder
|
// The unique ID for this builder
|
||||||
|
@ -30,9 +31,12 @@ type config struct {
|
||||||
InstanceType string `mapstructure:"instance_type"`
|
InstanceType string `mapstructure:"instance_type"`
|
||||||
SSHUsername string `mapstructure:"ssh_username"`
|
SSHUsername string `mapstructure:"ssh_username"`
|
||||||
SSHPort int `mapstructure:"ssh_port"`
|
SSHPort int `mapstructure:"ssh_port"`
|
||||||
|
SSHTimeout time.Duration
|
||||||
|
|
||||||
// Configuration of the resulting AMI
|
// Configuration of the resulting AMI
|
||||||
AMIName string `mapstructure:"ami_name"`
|
AMIName string `mapstructure:"ami_name"`
|
||||||
|
|
||||||
|
RawSSHTimeout string `mapstructure:"ssh_timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Builder struct {
|
type Builder struct {
|
||||||
|
@ -40,16 +44,20 @@ type Builder struct {
|
||||||
runner multistep.Runner
|
runner multistep.Runner
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) Prepare(raw interface{}) (err error) {
|
func (b *Builder) Prepare(raw interface{}) error {
|
||||||
err = mapstructure.Decode(raw, &b.config)
|
err := mapstructure.Decode(raw, &b.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.config.SSHPort == 0 {
|
if b.config.SSHPort == 0 {
|
||||||
b.config.SSHPort = 22
|
b.config.SSHPort = 22
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.config.RawSSHTimeout == "" {
|
||||||
|
b.config.RawSSHTimeout = "1m"
|
||||||
|
}
|
||||||
|
|
||||||
// Accumulate any errors
|
// Accumulate any errors
|
||||||
errs := make([]error, 0)
|
errs := make([]error, 0)
|
||||||
|
|
||||||
|
@ -79,12 +87,17 @@ func (b *Builder) Prepare(raw interface{}) (err error) {
|
||||||
errs = append(errs, errors.New("An ssh_username must be specified"))
|
errs = append(errs, errors.New("An ssh_username must be specified"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.config.SSHTimeout, err = time.ParseDuration(b.config.RawSSHTimeout)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("Failed parsing ssh_timeout: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return &packer.MultiError{errs}
|
return &packer.MultiError{errs}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Config: %+v", b.config)
|
log.Printf("Config: %+v", b.config)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) packer.Artifact {
|
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) packer.Artifact {
|
||||||
|
|
|
@ -191,6 +191,25 @@ func TestBuilderPrepare_SSHPort(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_SSHTimeout(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
|
||||||
|
// Test with a bad value
|
||||||
|
config["ssh_timeout"] = "this is not good"
|
||||||
|
err := b.Prepare(config)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with a good one
|
||||||
|
config["ssh_timeout"] = "5s"
|
||||||
|
err = b.Prepare(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not have error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuilderPrepare_SSHUsername(t *testing.T) {
|
func TestBuilderPrepare_SSHUsername(t *testing.T) {
|
||||||
var b Builder
|
var b Builder
|
||||||
config := testConfig()
|
config := testConfig()
|
||||||
|
|
|
@ -39,17 +39,50 @@ func (s *stepConnectSSH) Run(state map[string]interface{}) multistep.StepAction
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to connect for SSH a few times
|
// Start trying to connect to SSH
|
||||||
ui.Say("Connecting to the instance via SSH...")
|
connected := make(chan bool, 1)
|
||||||
for i := 0; i < 5; i++ {
|
connectQuit := make(chan bool, 1)
|
||||||
time.Sleep(time.Duration(i) * time.Second)
|
defer func() {
|
||||||
|
connectQuit <- true
|
||||||
|
}()
|
||||||
|
|
||||||
log.Printf(
|
go func() {
|
||||||
"Opening TCP conn for SSH to %s:%d (attempt %d)",
|
var err error
|
||||||
instance.DNSName, config.SSHPort, i+1)
|
|
||||||
s.conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", instance.DNSName, config.SSHPort))
|
ui.Say("Connecting to the instance via SSH...")
|
||||||
if err != nil {
|
attempts := 0
|
||||||
continue
|
for {
|
||||||
|
select {
|
||||||
|
case <-connectQuit:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts += 1
|
||||||
|
log.Printf(
|
||||||
|
"Opening TCP conn for SSH to %s:%d (attempt %d)",
|
||||||
|
instance.DNSName, config.SSHPort, attempts)
|
||||||
|
s.conn, err = net.Dial("tcp", fmt.Sprintf("%s:%d", instance.DNSName, config.SSHPort))
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connected <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Printf("Waiting up to %s for SSH connection", config.SSHTimeout)
|
||||||
|
timeout := time.After(config.SSHTimeout)
|
||||||
|
|
||||||
|
ConnectWaitLoop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-connected:
|
||||||
|
// We connected. Just break the loop.
|
||||||
|
break ConnectWaitLoop
|
||||||
|
case <-timeout:
|
||||||
|
ui.Error("Timeout while waiting to connect to SSH.")
|
||||||
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue