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