2015-06-13 17:42:38 -04:00
|
|
|
package communicator
|
|
|
|
|
|
|
|
import (
|
2018-01-22 18:32:33 -05:00
|
|
|
"context"
|
2015-06-13 17:50:45 -04:00
|
|
|
"fmt"
|
|
|
|
"log"
|
2019-02-15 18:05:29 -05:00
|
|
|
"time"
|
2015-06-13 17:50:45 -04:00
|
|
|
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/communicator/none"
|
2018-01-19 19:18:44 -05:00
|
|
|
"github.com/hashicorp/packer/helper/multistep"
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/packer"
|
2016-02-08 20:34:06 -05:00
|
|
|
gossh "golang.org/x/crypto/ssh"
|
2015-06-13 17:42:38 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// StepConnect is a multistep Step implementation that connects to
|
|
|
|
// the proper communicator and stores it in the "communicator" key in the
|
|
|
|
// state bag.
|
|
|
|
type StepConnect struct {
|
|
|
|
// Config is the communicator config struct
|
|
|
|
Config *Config
|
|
|
|
|
2015-06-13 19:23:33 -04:00
|
|
|
// Host should return a host that can be connected to for communicator
|
|
|
|
// connections.
|
|
|
|
Host func(multistep.StateBag) (string, error)
|
|
|
|
|
2015-06-13 17:42:38 -04:00
|
|
|
// The fields below are callbacks to assist with connecting to SSH.
|
|
|
|
//
|
|
|
|
// SSHConfig should return the default configuration for
|
|
|
|
// connecting via SSH.
|
2015-06-13 19:23:33 -04:00
|
|
|
SSHConfig func(multistep.StateBag) (*gossh.ClientConfig, error)
|
|
|
|
SSHPort func(multistep.StateBag) (int, error)
|
2015-06-13 17:42:38 -04:00
|
|
|
|
2015-06-14 01:07:17 -04:00
|
|
|
// The fields below are callbacks to assist with connecting to WinRM.
|
|
|
|
//
|
|
|
|
// WinRMConfig should return the default configuration for
|
|
|
|
// connecting via WinRM.
|
|
|
|
WinRMConfig func(multistep.StateBag) (*WinRMConfig, error)
|
2015-06-30 11:39:03 -04:00
|
|
|
WinRMPort func(multistep.StateBag) (int, error)
|
2015-06-14 01:07:17 -04:00
|
|
|
|
2015-06-15 01:09:38 -04:00
|
|
|
// CustomConnect can be set to have custom connectors for specific
|
|
|
|
// types. These take highest precedence so you can also override
|
|
|
|
// existing types.
|
|
|
|
CustomConnect map[string]multistep.Step
|
|
|
|
|
2015-06-13 17:42:38 -04:00
|
|
|
substep multistep.Step
|
|
|
|
}
|
|
|
|
|
2019-02-15 18:05:29 -05:00
|
|
|
func (s *StepConnect) pause(pauseLen time.Duration, ctx context.Context) bool {
|
|
|
|
// Use a select to determine if we get cancelled during the wait
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return true
|
|
|
|
case <-time.After(pauseLen):
|
|
|
|
}
|
|
|
|
log.Printf("Pause over; connecting...")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-01-22 18:29:42 -05:00
|
|
|
func (s *StepConnect) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
2018-07-29 03:18:26 -04:00
|
|
|
ui := state.Get("ui").(packer.Ui)
|
|
|
|
|
2015-06-13 17:50:45 -04:00
|
|
|
typeMap := map[string]multistep.Step{
|
|
|
|
"none": nil,
|
|
|
|
"ssh": &StepConnectSSH{
|
2015-06-13 19:23:33 -04:00
|
|
|
Config: s.Config,
|
|
|
|
Host: s.Host,
|
|
|
|
SSHConfig: s.SSHConfig,
|
|
|
|
SSHPort: s.SSHPort,
|
2015-06-13 17:50:45 -04:00
|
|
|
},
|
2015-06-14 01:07:17 -04:00
|
|
|
"winrm": &StepConnectWinRM{
|
|
|
|
Config: s.Config,
|
|
|
|
Host: s.Host,
|
|
|
|
WinRMConfig: s.WinRMConfig,
|
2016-11-06 11:02:00 -05:00
|
|
|
WinRMPort: s.WinRMPort,
|
2015-06-14 01:07:17 -04:00
|
|
|
},
|
2015-06-13 17:50:45 -04:00
|
|
|
}
|
2015-06-15 01:09:38 -04:00
|
|
|
for k, v := range s.CustomConnect {
|
|
|
|
typeMap[k] = v
|
|
|
|
}
|
2015-06-13 17:50:45 -04:00
|
|
|
|
|
|
|
step, ok := typeMap[s.Config.Type]
|
|
|
|
if !ok {
|
|
|
|
state.Put("error", fmt.Errorf("unknown communicator type: %s", s.Config.Type))
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
|
|
|
if step == nil {
|
2018-07-29 03:18:26 -04:00
|
|
|
if comm, err := none.New("none"); err != nil {
|
2015-10-20 18:00:48 -04:00
|
|
|
err := fmt.Errorf("Failed to set communicator 'none': %s", err)
|
|
|
|
state.Put("error", err)
|
|
|
|
ui.Error(err.Error())
|
|
|
|
return multistep.ActionHalt
|
2018-07-29 03:18:26 -04:00
|
|
|
|
|
|
|
} else {
|
|
|
|
state.Put("communicator", comm)
|
|
|
|
log.Printf("[INFO] communicator disabled, will not connect")
|
2015-10-20 18:00:48 -04:00
|
|
|
}
|
2015-06-13 17:50:45 -04:00
|
|
|
return multistep.ActionContinue
|
2015-06-13 17:42:38 -04:00
|
|
|
}
|
|
|
|
|
2018-07-29 03:18:26 -04:00
|
|
|
if host, err := s.Host(state); err == nil {
|
|
|
|
ui.Say(fmt.Sprintf("Using %s communicator to connect: %s", s.Config.Type, host))
|
|
|
|
} else {
|
|
|
|
log.Printf("[DEBUG] Unable to get address during connection step: %s", err)
|
|
|
|
}
|
|
|
|
|
2015-06-13 17:50:45 -04:00
|
|
|
s.substep = step
|
2019-02-26 15:24:45 -05:00
|
|
|
action := s.substep.Run(ctx, state)
|
|
|
|
if action == multistep.ActionHalt {
|
|
|
|
return action
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.Config.PauseBeforeConnect > 0 {
|
2020-08-14 14:23:43 -04:00
|
|
|
ui.Say(fmt.Sprintf("Pausing %s before connecting...",
|
|
|
|
s.Config.PauseBeforeConnect.String()))
|
2019-02-26 15:24:45 -05:00
|
|
|
cancelled := s.pause(s.Config.PauseBeforeConnect, ctx)
|
|
|
|
if cancelled {
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
2020-08-14 14:23:43 -04:00
|
|
|
// After pause is complete, re-run the connect substep to make sure
|
|
|
|
// you've connected properly
|
|
|
|
action := s.substep.Run(ctx, state)
|
|
|
|
if action == multistep.ActionHalt {
|
|
|
|
return action
|
|
|
|
}
|
2019-02-26 15:24:45 -05:00
|
|
|
}
|
|
|
|
|
2019-10-04 14:36:57 -04:00
|
|
|
// Put communicator config into state so we can pass it to provisioners
|
|
|
|
// for specialized interpolation later
|
|
|
|
state.Put("communicator_config", s.Config)
|
|
|
|
|
2019-02-26 15:24:45 -05:00
|
|
|
return multistep.ActionContinue
|
2015-06-13 17:42:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StepConnect) Cleanup(state multistep.StateBag) {
|
|
|
|
if s.substep != nil {
|
|
|
|
s.substep.Cleanup(state)
|
|
|
|
}
|
|
|
|
}
|