github.com/jerryclinesmith/packer@v0.3.7/packer/rpc/communicator.go (about) 1 package rpc 2 3 import ( 4 "encoding/gob" 5 "errors" 6 "github.com/mitchellh/packer/packer" 7 "io" 8 "log" 9 "net" 10 "net/rpc" 11 ) 12 13 // An implementation of packer.Communicator where the communicator is actually 14 // executed over an RPC connection. 15 type communicator struct { 16 client *rpc.Client 17 } 18 19 // CommunicatorServer wraps a packer.Communicator implementation and makes 20 // it exportable as part of a Golang RPC server. 21 type CommunicatorServer struct { 22 c packer.Communicator 23 } 24 25 type CommandFinished struct { 26 ExitStatus int 27 } 28 29 type CommunicatorStartArgs struct { 30 Command string 31 StdinAddress string 32 StdoutAddress string 33 StderrAddress string 34 ResponseAddress string 35 } 36 37 type CommunicatorDownloadArgs struct { 38 Path string 39 WriterAddress string 40 } 41 42 type CommunicatorUploadArgs struct { 43 Path string 44 ReaderAddress string 45 } 46 47 type CommunicatorUploadDirArgs struct { 48 Dst string 49 Src string 50 Exclude []string 51 } 52 53 func Communicator(client *rpc.Client) *communicator { 54 return &communicator{client} 55 } 56 57 func (c *communicator) Start(cmd *packer.RemoteCmd) (err error) { 58 var args CommunicatorStartArgs 59 args.Command = cmd.Command 60 61 if cmd.Stdin != nil { 62 stdinL := netListenerInRange(portRangeMin, portRangeMax) 63 args.StdinAddress = stdinL.Addr().String() 64 go serveSingleCopy("stdin", stdinL, nil, cmd.Stdin) 65 } 66 67 if cmd.Stdout != nil { 68 stdoutL := netListenerInRange(portRangeMin, portRangeMax) 69 args.StdoutAddress = stdoutL.Addr().String() 70 go serveSingleCopy("stdout", stdoutL, cmd.Stdout, nil) 71 } 72 73 if cmd.Stderr != nil { 74 stderrL := netListenerInRange(portRangeMin, portRangeMax) 75 args.StderrAddress = stderrL.Addr().String() 76 go serveSingleCopy("stderr", stderrL, cmd.Stderr, nil) 77 } 78 79 responseL := netListenerInRange(portRangeMin, portRangeMax) 80 args.ResponseAddress = responseL.Addr().String() 81 82 go func() { 83 defer responseL.Close() 84 85 conn, err := responseL.Accept() 86 if err != nil { 87 cmd.SetExited(123) 88 return 89 } 90 91 defer conn.Close() 92 93 decoder := gob.NewDecoder(conn) 94 95 var finished CommandFinished 96 if err := decoder.Decode(&finished); err != nil { 97 cmd.SetExited(123) 98 return 99 } 100 101 cmd.SetExited(finished.ExitStatus) 102 }() 103 104 err = c.client.Call("Communicator.Start", &args, new(interface{})) 105 return 106 } 107 108 func (c *communicator) Upload(path string, r io.Reader) (err error) { 109 // We need to create a server that can proxy the reader data 110 // over because we can't simply gob encode an io.Reader 111 readerL := netListenerInRange(portRangeMin, portRangeMax) 112 if readerL == nil { 113 err = errors.New("couldn't allocate listener for upload reader") 114 return 115 } 116 117 // Make sure at the end of this call, we close the listener 118 defer readerL.Close() 119 120 // Pipe the reader through to the connection 121 go serveSingleCopy("uploadReader", readerL, nil, r) 122 123 args := CommunicatorUploadArgs{ 124 path, 125 readerL.Addr().String(), 126 } 127 128 err = c.client.Call("Communicator.Upload", &args, new(interface{})) 129 return 130 } 131 132 func (c *communicator) UploadDir(dst string, src string, exclude []string) error { 133 args := &CommunicatorUploadDirArgs{ 134 Dst: dst, 135 Src: src, 136 Exclude: exclude, 137 } 138 139 var reply error 140 err := c.client.Call("Communicator.UploadDir", args, &reply) 141 if err == nil { 142 err = reply 143 } 144 145 return err 146 } 147 148 func (c *communicator) Download(path string, w io.Writer) (err error) { 149 // We need to create a server that can proxy that data downloaded 150 // into the writer because we can't gob encode a writer directly. 151 writerL := netListenerInRange(portRangeMin, portRangeMax) 152 if writerL == nil { 153 err = errors.New("couldn't allocate listener for download writer") 154 return 155 } 156 157 // Make sure we close the listener once we're done because we'll be done 158 defer writerL.Close() 159 160 // Serve a single connection and a single copy 161 go serveSingleCopy("downloadWriter", writerL, w, nil) 162 163 args := CommunicatorDownloadArgs{ 164 path, 165 writerL.Addr().String(), 166 } 167 168 err = c.client.Call("Communicator.Download", &args, new(interface{})) 169 return 170 } 171 172 func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface{}) (err error) { 173 // Build the RemoteCmd on this side so that it all pipes over 174 // to the remote side. 175 var cmd packer.RemoteCmd 176 cmd.Command = args.Command 177 178 toClose := make([]net.Conn, 0) 179 if args.StdinAddress != "" { 180 stdinC, err := net.Dial("tcp", args.StdinAddress) 181 if err != nil { 182 return err 183 } 184 185 toClose = append(toClose, stdinC) 186 cmd.Stdin = stdinC 187 } 188 189 if args.StdoutAddress != "" { 190 stdoutC, err := net.Dial("tcp", args.StdoutAddress) 191 if err != nil { 192 return err 193 } 194 195 toClose = append(toClose, stdoutC) 196 cmd.Stdout = stdoutC 197 } 198 199 if args.StderrAddress != "" { 200 stderrC, err := net.Dial("tcp", args.StderrAddress) 201 if err != nil { 202 return err 203 } 204 205 toClose = append(toClose, stderrC) 206 cmd.Stderr = stderrC 207 } 208 209 // Connect to the response address so we can write our result to it 210 // when ready. 211 responseC, err := net.Dial("tcp", args.ResponseAddress) 212 if err != nil { 213 return err 214 } 215 216 responseWriter := gob.NewEncoder(responseC) 217 218 // Start the actual command 219 err = c.c.Start(&cmd) 220 221 // Start a goroutine to spin and wait for the process to actual 222 // exit. When it does, report it back to caller... 223 go func() { 224 defer responseC.Close() 225 for _, conn := range toClose { 226 defer conn.Close() 227 } 228 229 cmd.Wait() 230 responseWriter.Encode(&CommandFinished{cmd.ExitStatus}) 231 }() 232 233 return 234 } 235 236 func (c *CommunicatorServer) Upload(args *CommunicatorUploadArgs, reply *interface{}) (err error) { 237 readerC, err := net.Dial("tcp", args.ReaderAddress) 238 if err != nil { 239 return 240 } 241 242 defer readerC.Close() 243 244 err = c.c.Upload(args.Path, readerC) 245 return 246 } 247 248 func (c *CommunicatorServer) UploadDir(args *CommunicatorUploadDirArgs, reply *error) error { 249 return c.c.UploadDir(args.Dst, args.Src, args.Exclude) 250 } 251 252 func (c *CommunicatorServer) Download(args *CommunicatorDownloadArgs, reply *interface{}) (err error) { 253 writerC, err := net.Dial("tcp", args.WriterAddress) 254 if err != nil { 255 return 256 } 257 258 defer writerC.Close() 259 260 err = c.c.Download(args.Path, writerC) 261 return 262 } 263 264 func serveSingleCopy(name string, l net.Listener, dst io.Writer, src io.Reader) { 265 defer l.Close() 266 267 conn, err := l.Accept() 268 if err != nil { 269 log.Printf("'%s' accept error: %s", name, err) 270 return 271 } 272 273 // Be sure to close the connection after we're done copying so 274 // that an EOF will successfully be sent to the remote side 275 defer conn.Close() 276 277 // The connection is the destination/source that is nil 278 if dst == nil { 279 dst = conn 280 } else { 281 src = conn 282 } 283 284 written, err := io.Copy(dst, src) 285 log.Printf("%d bytes written for '%s'", written, name) 286 if err != nil { 287 log.Printf("'%s' copy error: %s", name, err) 288 } 289 }