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