github.com/scottwinkler/terraform@v0.11.6-0.20180329211809-05143987aea8/communicator/winrm/communicator.go (about) 1 package winrm 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "math/rand" 8 "strconv" 9 "strings" 10 "sync" 11 "time" 12 13 "github.com/hashicorp/terraform/communicator/remote" 14 "github.com/hashicorp/terraform/terraform" 15 "github.com/masterzen/winrm" 16 "github.com/packer-community/winrmcp/winrmcp" 17 18 // This import is a bit strange, but it's needed so `make updatedeps` can see and download it 19 _ "github.com/dylanmei/winrmtest" 20 ) 21 22 // Communicator represents the WinRM communicator 23 type Communicator struct { 24 connInfo *connectionInfo 25 client *winrm.Client 26 endpoint *winrm.Endpoint 27 rand *rand.Rand 28 } 29 30 // New creates a new communicator implementation over WinRM. 31 func New(s *terraform.InstanceState) (*Communicator, error) { 32 connInfo, err := parseConnectionInfo(s) 33 if err != nil { 34 return nil, err 35 } 36 37 endpoint := &winrm.Endpoint{ 38 Host: connInfo.Host, 39 Port: connInfo.Port, 40 HTTPS: connInfo.HTTPS, 41 Insecure: connInfo.Insecure, 42 } 43 if len(connInfo.CACert) > 0 { 44 endpoint.CACert = []byte(connInfo.CACert) 45 } 46 47 comm := &Communicator{ 48 connInfo: connInfo, 49 endpoint: endpoint, 50 // Seed our own rand source so that script paths are not deterministic 51 rand: rand.New(rand.NewSource(time.Now().UnixNano())), 52 } 53 54 return comm, nil 55 } 56 57 // Connect implementation of communicator.Communicator interface 58 func (c *Communicator) Connect(o terraform.UIOutput) error { 59 if c.client != nil { 60 return nil 61 } 62 63 params := winrm.DefaultParameters 64 params.Timeout = formatDuration(c.Timeout()) 65 66 client, err := winrm.NewClientWithParameters( 67 c.endpoint, c.connInfo.User, c.connInfo.Password, params) 68 if err != nil { 69 return err 70 } 71 72 if o != nil { 73 o.Output(fmt.Sprintf( 74 "Connecting to remote host via WinRM...\n"+ 75 " Host: %s\n"+ 76 " Port: %d\n"+ 77 " User: %s\n"+ 78 " Password: %t\n"+ 79 " HTTPS: %t\n"+ 80 " Insecure: %t\n"+ 81 " CACert: %t", 82 c.connInfo.Host, 83 c.connInfo.Port, 84 c.connInfo.User, 85 c.connInfo.Password != "", 86 c.connInfo.HTTPS, 87 c.connInfo.Insecure, 88 c.connInfo.CACert != "", 89 )) 90 } 91 92 log.Printf("connecting to remote shell using WinRM") 93 shell, err := client.CreateShell() 94 if err != nil { 95 log.Printf("connection error: %s", err) 96 return err 97 } 98 99 err = shell.Close() 100 if err != nil { 101 log.Printf("error closing connection: %s", err) 102 return err 103 } 104 105 if o != nil { 106 o.Output("Connected!") 107 } 108 109 c.client = client 110 111 return nil 112 } 113 114 // Disconnect implementation of communicator.Communicator interface 115 func (c *Communicator) Disconnect() error { 116 c.client = nil 117 return nil 118 } 119 120 // Timeout implementation of communicator.Communicator interface 121 func (c *Communicator) Timeout() time.Duration { 122 return c.connInfo.TimeoutVal 123 } 124 125 // ScriptPath implementation of communicator.Communicator interface 126 func (c *Communicator) ScriptPath() string { 127 return strings.Replace( 128 c.connInfo.ScriptPath, "%RAND%", 129 strconv.FormatInt(int64(c.rand.Int31()), 10), -1) 130 } 131 132 // Start implementation of communicator.Communicator interface 133 func (c *Communicator) Start(rc *remote.Cmd) error { 134 rc.Init() 135 136 err := c.Connect(nil) 137 if err != nil { 138 return err 139 } 140 141 shell, err := c.client.CreateShell() 142 if err != nil { 143 return err 144 } 145 146 log.Printf("starting remote command: %s", rc.Command) 147 cmd, err := shell.Execute(rc.Command) 148 if err != nil { 149 return err 150 } 151 152 go runCommand(shell, cmd, rc) 153 return nil 154 } 155 156 func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *remote.Cmd) { 157 defer shell.Close() 158 159 var wg sync.WaitGroup 160 go func() { 161 wg.Add(1) 162 io.Copy(rc.Stdout, cmd.Stdout) 163 wg.Done() 164 }() 165 go func() { 166 wg.Add(1) 167 io.Copy(rc.Stderr, cmd.Stderr) 168 wg.Done() 169 }() 170 171 cmd.Wait() 172 wg.Wait() 173 174 rc.SetExitStatus(cmd.ExitCode(), nil) 175 } 176 177 // Upload implementation of communicator.Communicator interface 178 func (c *Communicator) Upload(path string, input io.Reader) error { 179 wcp, err := c.newCopyClient() 180 if err != nil { 181 return err 182 } 183 log.Printf("Uploading file to '%s'", path) 184 return wcp.Write(path, input) 185 } 186 187 // UploadScript implementation of communicator.Communicator interface 188 func (c *Communicator) UploadScript(path string, input io.Reader) error { 189 return c.Upload(path, input) 190 } 191 192 // UploadDir implementation of communicator.Communicator interface 193 func (c *Communicator) UploadDir(dst string, src string) error { 194 log.Printf("Uploading dir '%s' to '%s'", src, dst) 195 wcp, err := c.newCopyClient() 196 if err != nil { 197 return err 198 } 199 return wcp.Copy(src, dst) 200 } 201 202 func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) { 203 addr := fmt.Sprintf("%s:%d", c.endpoint.Host, c.endpoint.Port) 204 205 config := winrmcp.Config{ 206 Auth: winrmcp.Auth{ 207 User: c.connInfo.User, 208 Password: c.connInfo.Password, 209 }, 210 Https: c.connInfo.HTTPS, 211 Insecure: c.connInfo.Insecure, 212 OperationTimeout: c.Timeout(), 213 MaxOperationsPerShell: 15, // lowest common denominator 214 } 215 216 if c.connInfo.CACert != "" { 217 config.CACertBytes = []byte(c.connInfo.CACert) 218 } 219 220 return winrmcp.New(addr, &config) 221 }