github.com/sl1pm4t/terraform@v0.6.4-0.20170725213156-870617d22df3/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/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 CACert: connInfo.CACert, 43 } 44 45 comm := &Communicator{ 46 connInfo: connInfo, 47 endpoint: endpoint, 48 // Seed our own rand source so that script paths are not deterministic 49 rand: rand.New(rand.NewSource(time.Now().UnixNano())), 50 } 51 52 return comm, nil 53 } 54 55 // Connect implementation of communicator.Communicator interface 56 func (c *Communicator) Connect(o terraform.UIOutput) error { 57 if c.client != nil { 58 return nil 59 } 60 61 params := winrm.DefaultParameters() 62 params.Timeout = formatDuration(c.Timeout()) 63 64 client, err := winrm.NewClientWithParameters( 65 c.endpoint, c.connInfo.User, c.connInfo.Password, params) 66 if err != nil { 67 return err 68 } 69 70 if o != nil { 71 o.Output(fmt.Sprintf( 72 "Connecting to remote host via WinRM...\n"+ 73 " Host: %s\n"+ 74 " Port: %d\n"+ 75 " User: %s\n"+ 76 " Password: %t\n"+ 77 " HTTPS: %t\n"+ 78 " Insecure: %t\n"+ 79 " CACert: %t", 80 c.connInfo.Host, 81 c.connInfo.Port, 82 c.connInfo.User, 83 c.connInfo.Password != "", 84 c.connInfo.HTTPS, 85 c.connInfo.Insecure, 86 c.connInfo.CACert != nil, 87 )) 88 } 89 90 log.Printf("connecting to remote shell using WinRM") 91 shell, err := client.CreateShell() 92 if err != nil { 93 log.Printf("connection error: %s", err) 94 return err 95 } 96 97 err = shell.Close() 98 if err != nil { 99 log.Printf("error closing connection: %s", err) 100 return err 101 } 102 103 if o != nil { 104 o.Output("Connected!") 105 } 106 107 c.client = client 108 109 return nil 110 } 111 112 // Disconnect implementation of communicator.Communicator interface 113 func (c *Communicator) Disconnect() error { 114 c.client = nil 115 return nil 116 } 117 118 // Timeout implementation of communicator.Communicator interface 119 func (c *Communicator) Timeout() time.Duration { 120 return c.connInfo.TimeoutVal 121 } 122 123 // ScriptPath implementation of communicator.Communicator interface 124 func (c *Communicator) ScriptPath() string { 125 return strings.Replace( 126 c.connInfo.ScriptPath, "%RAND%", 127 strconv.FormatInt(int64(c.rand.Int31()), 10), -1) 128 } 129 130 // Start implementation of communicator.Communicator interface 131 func (c *Communicator) Start(rc *remote.Cmd) error { 132 err := c.Connect(nil) 133 if err != nil { 134 return err 135 } 136 137 shell, err := c.client.CreateShell() 138 if err != nil { 139 return err 140 } 141 142 log.Printf("starting remote command: %s", rc.Command) 143 cmd, err := shell.Execute(rc.Command) 144 if err != nil { 145 return err 146 } 147 148 go runCommand(shell, cmd, rc) 149 return nil 150 } 151 152 func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *remote.Cmd) { 153 defer shell.Close() 154 155 var wg sync.WaitGroup 156 go func() { 157 wg.Add(1) 158 io.Copy(rc.Stdout, cmd.Stdout) 159 wg.Done() 160 }() 161 go func() { 162 wg.Add(1) 163 io.Copy(rc.Stderr, cmd.Stderr) 164 wg.Done() 165 }() 166 167 cmd.Wait() 168 wg.Wait() 169 rc.SetExited(cmd.ExitCode()) 170 } 171 172 // Upload implementation of communicator.Communicator interface 173 func (c *Communicator) Upload(path string, input io.Reader) error { 174 wcp, err := c.newCopyClient() 175 if err != nil { 176 return err 177 } 178 log.Printf("Uploading file to '%s'", path) 179 return wcp.Write(path, input) 180 } 181 182 // UploadScript implementation of communicator.Communicator interface 183 func (c *Communicator) UploadScript(path string, input io.Reader) error { 184 return c.Upload(path, input) 185 } 186 187 // UploadDir implementation of communicator.Communicator interface 188 func (c *Communicator) UploadDir(dst string, src string) error { 189 log.Printf("Uploading dir '%s' to '%s'", src, dst) 190 wcp, err := c.newCopyClient() 191 if err != nil { 192 return err 193 } 194 return wcp.Copy(src, dst) 195 } 196 197 func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) { 198 addr := fmt.Sprintf("%s:%d", c.endpoint.Host, c.endpoint.Port) 199 200 config := winrmcp.Config{ 201 Auth: winrmcp.Auth{ 202 User: c.connInfo.User, 203 Password: c.connInfo.Password, 204 }, 205 Https: c.connInfo.HTTPS, 206 Insecure: c.connInfo.Insecure, 207 OperationTimeout: c.Timeout(), 208 MaxOperationsPerShell: 15, // lowest common denominator 209 } 210 211 if c.connInfo.CACert != nil { 212 config.CACertBytes = *c.connInfo.CACert 213 } 214 215 return winrmcp.New(addr, &config) 216 }