github.com/yoctocloud/packer@v0.6.2-0.20160520224004-e11a0a18423f/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 HTTPS: config.Https, 32 Insecure: config.Insecure, 33 34 /* 35 TODO 36 HTTPS: connInfo.HTTPS, 37 Insecure: connInfo.Insecure, 38 CACert: connInfo.CACert, 39 */ 40 } 41 42 // Create the client 43 params := winrm.DefaultParameters() 44 45 if config.TransportDecorator != nil { 46 params.TransportDecorator = config.TransportDecorator 47 } 48 49 params.Timeout = formatDuration(config.Timeout) 50 client, err := winrm.NewClientWithParameters( 51 endpoint, config.Username, config.Password, params) 52 if err != nil { 53 return nil, err 54 } 55 56 // Create the shell to verify the connection 57 log.Printf("[DEBUG] connecting to remote shell using WinRM") 58 shell, err := client.CreateShell() 59 if err != nil { 60 log.Printf("[ERROR] connection error: %s", err) 61 return nil, err 62 } 63 64 if err := shell.Close(); err != nil { 65 log.Printf("[ERROR] error closing connection: %s", err) 66 return nil, err 67 } 68 69 return &Communicator{ 70 config: config, 71 client: client, 72 endpoint: endpoint, 73 }, nil 74 } 75 76 // Start implementation of communicator.Communicator interface 77 func (c *Communicator) Start(rc *packer.RemoteCmd) error { 78 shell, err := c.client.CreateShell() 79 if err != nil { 80 return err 81 } 82 83 log.Printf("[INFO] starting remote command: %s", rc.Command) 84 cmd, err := shell.Execute(rc.Command) 85 if err != nil { 86 return err 87 } 88 89 go runCommand(shell, cmd, rc) 90 return nil 91 } 92 93 func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *packer.RemoteCmd) { 94 defer shell.Close() 95 var wg sync.WaitGroup 96 97 copyFunc := func(w io.Writer, r io.Reader) { 98 defer wg.Done() 99 io.Copy(w, r) 100 } 101 102 if rc.Stdout != nil && cmd.Stdout != nil { 103 wg.Add(1) 104 go copyFunc(rc.Stdout, cmd.Stdout) 105 } else { 106 log.Printf("[WARN] Failed to read stdout for command '%s'", rc.Command) 107 } 108 109 if rc.Stderr != nil && cmd.Stderr != nil { 110 wg.Add(1) 111 go copyFunc(rc.Stderr, cmd.Stderr) 112 } else { 113 log.Printf("[WARN] Failed to read stderr for command '%s'", rc.Command) 114 } 115 116 cmd.Wait() 117 wg.Wait() 118 119 code := cmd.ExitCode() 120 log.Printf("[INFO] command '%s' exited with code: %d", rc.Command, code) 121 rc.SetExited(code) 122 } 123 124 // Upload implementation of communicator.Communicator interface 125 func (c *Communicator) Upload(path string, input io.Reader, _ *os.FileInfo) error { 126 wcp, err := c.newCopyClient() 127 if err != nil { 128 return err 129 } 130 log.Printf("Uploading file to '%s'", path) 131 return wcp.Write(path, input) 132 } 133 134 // UploadDir implementation of communicator.Communicator interface 135 func (c *Communicator) UploadDir(dst string, src string, exclude []string) error { 136 log.Printf("Uploading dir '%s' to '%s'", src, dst) 137 wcp, err := c.newCopyClient() 138 if err != nil { 139 return err 140 } 141 return wcp.Copy(src, dst) 142 } 143 144 func (c *Communicator) Download(src string, dst io.Writer) error { 145 return fmt.Errorf("WinRM doesn't support download.") 146 } 147 148 func (c *Communicator) DownloadDir(src string, dst string, exclude []string) error { 149 return fmt.Errorf("WinRM doesn't support download dir.") 150 } 151 152 func (c *Communicator) newCopyClient() (*winrmcp.Winrmcp, error) { 153 addr := fmt.Sprintf("%s:%d", c.endpoint.Host, c.endpoint.Port) 154 return winrmcp.New(addr, &winrmcp.Config{ 155 Auth: winrmcp.Auth{ 156 User: c.config.Username, 157 Password: c.config.Password, 158 }, 159 Https: c.config.Https, 160 Insecure: c.config.Insecure, 161 OperationTimeout: c.config.Timeout, 162 MaxOperationsPerShell: 15, // lowest common denominator 163 TransportDecorator: c.config.TransportDecorator, 164 }) 165 }