/
step_connect.go
149 lines (128 loc) · 4.04 KB
/
step_connect.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package communicator
import (
"context"
"fmt"
"log"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/sdk-internals/communicator/none"
gossh "golang.org/x/crypto/ssh"
)
// 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
// Host should return a host that can be connected to for communicator
// connections.
Host func(multistep.StateBag) (string, error)
// The fields below are callbacks to assist with connecting to SSH.
//
// SSHConfig should return the default configuration for
// connecting via SSH.
SSHConfig func(multistep.StateBag) (*gossh.ClientConfig, error)
SSHPort func(multistep.StateBag) (int, error)
// 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)
WinRMPort func(multistep.StateBag) (int, error)
// 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
substep multistep.Step
}
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
}
func (s *StepConnect) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
typeMap := map[string]multistep.Step{
"none": nil,
"ssh": &StepConnectSSH{
Config: s.Config,
Host: s.Host,
SSHConfig: s.SSHConfig,
SSHPort: s.SSHPort,
},
"winrm": &StepConnectWinRM{
Config: s.Config,
Host: s.Host,
WinRMConfig: s.WinRMConfig,
WinRMPort: s.WinRMPort,
},
}
for k, v := range s.CustomConnect {
typeMap[k] = v
}
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 {
if comm, err := none.New("none"); err != nil {
err := fmt.Errorf("Failed to set communicator 'none': %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
} else {
state.Put("communicator", comm)
log.Printf("[INFO] communicator disabled, will not connect")
}
return multistep.ActionContinue
}
if host, err := s.Host(state); err == nil {
switch s.Config.Type {
case "ssh":
ui.Say(fmt.Sprintf("Using SSH communicator to connect: %s", host))
case "winrm":
ui.Say(fmt.Sprintf("Using WinRM communicator to connect: %s", host))
default:
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)
}
s.substep = step
action := s.substep.Run(ctx, state)
if action == multistep.ActionHalt {
return action
}
if s.Config.PauseBeforeConnect > 0 {
ui.Say(fmt.Sprintf("Pausing %s before connecting...",
s.Config.PauseBeforeConnect.String()))
cancelled := s.pause(s.Config.PauseBeforeConnect, ctx)
if cancelled {
return multistep.ActionHalt
}
// 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
}
}
// Put communicator config into state so we can pass it to provisioners
// for specialized interpolation later
state.Put("communicator_config", s.Config)
return multistep.ActionContinue
}
func (s *StepConnect) Cleanup(state multistep.StateBag) {
if s.substep != nil {
s.substep.Cleanup(state)
}
}