Support IP address settle down timeout

Sometimes it's possible that Windows VM starts with one address and then changes to another one. For successful communication we need latest one
This commit is contained in:
Vladislav Rassokhin 2018-05-30 14:52:27 +03:00 committed by Vladislav Rassokhin
parent f7151b4058
commit 1111c74696
6 changed files with 88 additions and 14 deletions

View file

@ -109,6 +109,7 @@ See complete Ubuntu, Windows, and macOS templates in the [examples folder](https
### Provision
* `communicator` - `ssh` (default), `winrm`, or `none` (create/clone, customize hardware, but do not boot).
* `ip_settle_timeout`(string) - Amount of time to wait for VM's IP to settle down, sometimes VM may report incorrect IP initially, then its recommended to set that parameter to apx. 2 minutes. Examples 45s and 10m. Defaults to 5s(5 seconds). See the Go Lang [ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation for full details.
* `ssh_username`(string) - Username in guest OS.
* `ssh_password`(string) - Password to access guest OS. Only specify `ssh_password` or `ssh_private_key_file`, but not both.
* `ssh_private_key_file`(string) - Path to the SSH private key file to access guest OS. Only specify `ssh_password` or `ssh_private_key_file`, but not both.

View file

@ -55,7 +55,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Config: &b.config.RunConfig,
SetOrder: false,
},
&common.StepWaitForIp{},
&common.StepWaitForIp{
&b.config.WaitIpConfig,
},
&communicator.StepConnect{
Config: &b.config.Comm,
Host: common.CommHost(b.config.Comm.SSHHost),

View file

@ -19,6 +19,7 @@ type Config struct {
common.ConfigParamsConfig `mapstructure:",squash"`
common.RunConfig `mapstructure:",squash"`
common.WaitIpConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
common.ShutdownConfig `mapstructure:",squash"`
@ -44,6 +45,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
errs = packer.MultiErrorAppend(errs, c.LocationConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)

View file

@ -6,10 +6,35 @@ import (
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/jetbrains-infra/packer-builder-vsphere/driver"
"log"
"time"
)
type StepWaitForIp struct{}
type WaitIpConfig struct {
SettleTimeout string `mapstructure:"ip_settle_timeout"`
settleTimeout time.Duration
}
type StepWaitForIp struct {
Config *WaitIpConfig
}
func (c *WaitIpConfig) Prepare() []error {
var errs []error
if c.SettleTimeout == "" {
c.SettleTimeout = "5s"
}
var err error
c.settleTimeout, err = time.ParseDuration(c.SettleTimeout)
if err != nil {
errs = append(errs, fmt.Errorf("failed parsing ip_settle_timeout: %s", err))
}
return errs
}
func (s *StepWaitForIp) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
@ -20,12 +45,7 @@ func (s *StepWaitForIp) Run(ctx context.Context, state multistep.StateBag) multi
ipChan := make(chan string)
errChan := make(chan error)
go func() {
ip, err := vm.WaitForIP(ctx)
if err != nil {
errChan <- err
} else {
ipChan <- ip
}
doGetIp(vm, ctx, s.Config, errChan, ipChan)
}()
for {
@ -47,4 +67,49 @@ func (s *StepWaitForIp) Run(ctx context.Context, state multistep.StateBag) multi
}
}
func doGetIp(vm *driver.VirtualMachine, ctx context.Context, c *WaitIpConfig, errChan chan error, ipChan chan string) {
var prevIp = ""
var stopTime time.Time
var interval time.Duration
if c.settleTimeout.Seconds() >= 120 {
interval = 30 * time.Second
} else if c.settleTimeout.Seconds() >= 60 {
interval = 15 * time.Second
} else if c.settleTimeout.Seconds() >= 10 {
interval = 5 * time.Second
} else {
interval = 1 * time.Second
}
loop:
ip, err := vm.WaitForIP(ctx)
if err != nil {
errChan <- err
return
}
if prevIp == "" || prevIp != ip {
if prevIp == "" {
log.Printf("VM IP aquired: %s", ip)
} else {
log.Printf("VM IP changed from %s to %s", prevIp, ip)
}
prevIp = ip
stopTime = time.Now().Add(c.settleTimeout)
goto loop
} else {
log.Printf("VM IP is still the same: %s", prevIp)
if time.Now().After(stopTime) {
log.Printf("VM IP seems stable enough: %s", ip)
ipChan <- ip
return
}
select {
case <-ctx.Done():
return
case <-time.After(interval):
goto loop
}
}
}
func (s *StepWaitForIp) Cleanup(state multistep.StateBag) {}

View file

@ -99,7 +99,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Ctx: b.config.ctx,
VMName: b.config.VMName,
},
&common.StepWaitForIp{},
&common.StepWaitForIp{
Config: &b.config.WaitIpConfig,
},
&communicator.StepConnect{
Config: &b.config.Comm,
Host: common.CommHost(b.config.Comm.SSHHost),

View file

@ -21,11 +21,12 @@ type Config struct {
packerCommon.ISOConfig `mapstructure:",squash"`
CDRomConfig `mapstructure:",squash"`
FloppyConfig `mapstructure:",squash"`
common.RunConfig `mapstructure:",squash"`
BootConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
CDRomConfig `mapstructure:",squash"`
FloppyConfig `mapstructure:",squash"`
common.RunConfig `mapstructure:",squash"`
BootConfig `mapstructure:",squash"`
common.WaitIpConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
common.ShutdownConfig `mapstructure:",squash"`
@ -67,6 +68,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
errs = packer.MultiErrorAppend(errs, c.CDRomConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)