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