github.com/kimor79/packer@v0.8.7-0.20151221212622-d507b18eb4cf/communicator/winrm/communicator.go (about) 1 package winrm 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "sync" 9 10 "github.com/masterzen/winrm/winrm" 11 "github.com/mitchellh/packer/packer" 12 "github.com/packer-community/winrmcp/winrmcp" 13 14 // This import is a bit strange, but it's needed so `make updatedeps` 15 // can see and download it 16 _ "github.com/dylanmei/winrmtest" 17 ) 18 19 // Communicator represents the WinRM communicator 20 type Communicator struct { 21 config *Config 22 client *winrm.Client 23 endpoint *winrm.Endpoint 24 } 25 26 // New creates a new communicator implementation over WinRM. 27 func New(config *Config) (*Communicator, error) { 28 endpoint := &winrm.Endpoint{ 29 Host: config.Host, 30 Port: config.Port, 31 32 /* 33 TODO 34 HTTPS: connInfo.HTTPS, 35 Insecure: connInfo.Insecure, 36 CACert: connInfo.CACert, 37 */ 38 } 39 40 // Create the client 41 params := winrm.DefaultParameters() 42 params.Timeout = formatDuration(config.Timeout) 43 client, err := winrm.NewClientWithParameters( 44 endpoint, config.Username, config.Password, params) 45 if err != nil { 46 return nil, err 47 } 48 49 // Create the shell to verify the connection 50 log.Printf("[DEBUG] connecting to remote shell using WinRM") 51 shell, err := client.CreateShell() 52 if err != nil { 53 log.Printf("[ERROR] connection error: %s", err) 54 return nil, err 55 } 56 57 if err := shell.Close(); err != nil { 58 log.Printf("[ERROR] error closing connection: %s", err) 59 return nil, err 60 } 61 62 return &Communicator{ 63 config: config, 64 client: client, 65 endpoint: endpoint, 66 }, nil 67 } 68 69 // Start implementation of communicator.Communicator interface 70 func (c *Communicator) Start(rc *packer.RemoteCmd) error { 71 shell, err := c.client.CreateShell() 72 if err != nil { 73 return err 74 } 75 76 log.Printf("[INFO] starting remote command: %s", rc.Command) 77 cmd, err := shell.Execute(rc.Command) 78 if err != nil { 79 return err 80 } 81 82 go runCommand(shell, cmd, rc) 83 return nil 84 } 85 86 func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *packer.RemoteCmd) { 87 defer shell.Close() 88 var wg sync.WaitGroup 89 90 copyFunc := func(w io.Writer, r io.Reader) { 91 defer wg.Done() 92 io.Copy(w, r) 93 } 94 95 if rc.Stdout != nil && cmd.Stdout != nil { 96 wg.Add(1) 97 go copyFunc(rc.Stdout, cmd.Stdout) 98 } else { 99 log.Printf("[WARN] Failed to read stdout for command '%s'", rc.Command) 100 } 101 102 if rc.Stderr != nil && cmd.Stderr != nil { 103 wg.Add(1) 104 go copyFunc(rc.Stderr, cmd.Stderr) 105 } else { 106 log.Printf("[WARN] Failed to read stderr for command '%s'", rc.Command) 107 } 108 109 cmd.Wait() 110 wg.Wait() 111 112 code := cmd.ExitCode() 113 log.Printf("[INFO] command '%s' exited with code: %d", rc.Command, code) 114 rc.SetExited(code) 115 } 116 117 // Upload implementation of communicator.Communicator interface 118 func (c *Communicator) Upload(path string, input io.Reader, _ *os.FileInfo) error { 119 wcp, err := c.newCopyClient() 120 if err != nil { 121 return err 122 } 123 log.Printf("Uploading file to '%s'", path) 124 return wcp.Write(path, input) 125 } 126 127 // UploadDir implementation of communicator.Communicator interface 128 func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { 129 log.Printf("Uploading dir '%s' to '%s'", src, dst) 130 wcp, err := c.newCopyClient() 131 if err != nil { 132 return err 133 } 134 return wcp.Copy(src, dst) 135 } 136 137 func (c *Communicator) Download(src string, dst io.Writer) error { 138 return fmt.Errorf("WinRM doesn't support download.") 139 } 140 141 func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) { 142 addr := fmt.Sprintf("%s:%d", c.endpoint.Host, c.endpoint.Port) 143 return winrmcp.New(addr, &winrmcp.Config{ 144 Auth: winrmcp.Auth{ 145 User: c.config.Username, 146 Password: c.config.Password, 147 }, 148 OperationTimeout: c.config.Timeout, 149 MaxOperationsPerShell: 15, // lowest common denominator 150 }) 151 }