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