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