github.com/bhameyie/otto@v0.2.1-0.20160406174117-16052efa52ec/rpc/directory.go (about) 1 package rpc 2 3 import ( 4 "io" 5 "log" 6 "net/rpc" 7 8 "github.com/hashicorp/otto/directory" 9 ) 10 11 // Directory is an implementatin of directory.Backend that communicates 12 // over RPC. 13 type Directory struct { 14 Broker *muxBroker 15 Client *rpc.Client 16 Name string 17 } 18 19 func (d *Directory) PutBlob(key string, data *directory.BlobData) error { 20 // Serve the data 21 id := d.Broker.NextId() 22 doneCh := make(chan struct{}) 23 go serveSingleCopy("putBlob: "+key, d.Broker, doneCh, id, nil, data.Data) 24 25 // Run it 26 var resp ErrorResponse 27 args := &DirPutBlobArgs{ 28 Key: key, 29 Id: id, 30 } 31 err := d.Client.Call(d.Name+".PutBlob", args, &resp) 32 if err != nil { 33 return err 34 } 35 if resp.Error != nil { 36 err = resp.Error 37 return err 38 } 39 40 // NOTE: We don't wait on the doneCh because the data is being read 41 // on the other side and the contract is that it will exaust the 42 // data on success. 43 44 return nil 45 } 46 47 func (d *Directory) GetBlob(key string) (*directory.BlobData, error) { 48 // Create the result 49 pr, pw := io.Pipe() 50 result := &directory.BlobData{ 51 Key: key, 52 Data: pr, 53 } 54 55 // Download the data 56 id := d.Broker.NextId() 57 doneCh := make(chan struct{}) 58 go serveSingleCopy("getBlob: "+key, d.Broker, doneCh, id, pw, nil) 59 go func() { 60 // We wait for the data copying to be complete. When it is, we 61 // want to close the writer side of our pipe so that the result 62 // also gets an EOF. 63 <-doneCh 64 pw.Close() 65 }() 66 67 // Run it 68 var resp DirGetBlobResponse 69 args := &DirGetBlobArgs{ 70 Key: key, 71 Id: id, 72 } 73 err := d.Client.Call(d.Name+".GetBlob", args, &resp) 74 if err != nil { 75 return nil, err 76 } 77 if resp.Error != nil { 78 err = resp.Error 79 return nil, err 80 } 81 if !resp.Ok { 82 // No return value (blob key didn't exist) 83 result = nil 84 } 85 86 // NOTE: We don't wait on the doneCh because the data is put as a 87 // io.Reader into the BlobData. It is up to the end user to read 88 // until it is exausted. 89 90 return result, nil 91 } 92 93 func (d *Directory) PutInfra(v *directory.Infra) error { 94 var resp DirPutInfraResponse 95 err := d.Client.Call(d.Name+".PutInfra", v, &resp) 96 if err != nil { 97 return err 98 } 99 if resp.Error != nil { 100 err = resp.Error 101 return err 102 } 103 104 *v = *resp.Value 105 return nil 106 } 107 108 func (d *Directory) GetInfra(v *directory.Infra) (*directory.Infra, error) { 109 var resp DirGetInfraResponse 110 err := d.Client.Call(d.Name+".GetInfra", v, &resp) 111 if err != nil { 112 return nil, err 113 } 114 if resp.Error != nil { 115 err = resp.Error 116 return nil, err 117 } 118 119 return resp.Value, nil 120 } 121 122 func (d *Directory) PutDev(v *directory.Dev) error { 123 var resp DirPutDevResponse 124 err := d.Client.Call(d.Name+".PutDev", v, &resp) 125 if err != nil { 126 return err 127 } 128 if resp.Error != nil { 129 err = resp.Error 130 return err 131 } 132 133 *v = *resp.Value 134 return nil 135 } 136 137 func (d *Directory) GetDev(v *directory.Dev) (*directory.Dev, error) { 138 var resp DirGetDevResponse 139 err := d.Client.Call(d.Name+".GetDev", v, &resp) 140 if err != nil { 141 return nil, err 142 } 143 if resp.Error != nil { 144 err = resp.Error 145 return nil, err 146 } 147 148 return resp.Value, nil 149 } 150 151 func (d *Directory) DeleteDev(v *directory.Dev) error { 152 var resp ErrorResponse 153 err := d.Client.Call(d.Name+".DeleteDev", v, &resp) 154 if err != nil { 155 return err 156 } 157 if resp.Error != nil { 158 err = resp.Error 159 return err 160 } 161 162 return nil 163 } 164 165 func (d *Directory) PutBuild(v *directory.Build) error { 166 var resp DirPutBuildResponse 167 err := d.Client.Call(d.Name+".PutBuild", v, &resp) 168 if err != nil { 169 return err 170 } 171 if resp.Error != nil { 172 err = resp.Error 173 return err 174 } 175 176 *v = *resp.Value 177 return nil 178 } 179 180 func (d *Directory) GetBuild(v *directory.Build) (*directory.Build, error) { 181 var resp DirGetBuildResponse 182 err := d.Client.Call(d.Name+".GetBuild", v, &resp) 183 if err != nil { 184 return nil, err 185 } 186 if resp.Error != nil { 187 err = resp.Error 188 return nil, err 189 } 190 191 return resp.Value, nil 192 } 193 194 func (d *Directory) PutDeploy(v *directory.Deploy) error { 195 var resp DirPutDeployResponse 196 err := d.Client.Call(d.Name+".PutDeploy", v, &resp) 197 if err != nil { 198 return err 199 } 200 if resp.Error != nil { 201 err = resp.Error 202 return err 203 } 204 205 *v = *resp.Value 206 return nil 207 } 208 209 func (d *Directory) GetDeploy(v *directory.Deploy) (*directory.Deploy, error) { 210 var resp DirGetDeployResponse 211 err := d.Client.Call(d.Name+".GetDeploy", v, &resp) 212 if err != nil { 213 return nil, err 214 } 215 if resp.Error != nil { 216 err = resp.Error 217 return nil, err 218 } 219 220 return resp.Value, nil 221 } 222 223 type DirPutBlobArgs struct { 224 Key string 225 Id uint32 226 } 227 228 type DirGetBlobArgs struct { 229 Key string 230 Id uint32 231 } 232 233 type DirGetBlobResponse struct { 234 Ok bool 235 Error *BasicError 236 } 237 238 type DirGetInfraResponse struct { 239 Value *directory.Infra 240 Error *BasicError 241 } 242 243 type DirGetDevResponse struct { 244 Value *directory.Dev 245 Error *BasicError 246 } 247 248 type DirGetBuildResponse struct { 249 Value *directory.Build 250 Error *BasicError 251 } 252 253 type DirGetDeployResponse struct { 254 Value *directory.Deploy 255 Error *BasicError 256 } 257 258 type DirPutInfraResponse struct { 259 Value *directory.Infra 260 Error *BasicError 261 } 262 263 type DirPutDevResponse struct { 264 Value *directory.Dev 265 Error *BasicError 266 } 267 268 type DirPutBuildResponse struct { 269 Value *directory.Build 270 Error *BasicError 271 } 272 273 type DirPutDeployResponse struct { 274 Value *directory.Deploy 275 Error *BasicError 276 } 277 278 // DirectoryServer is a net/rpc compatible structure for serving 279 // a directory backend. This should not be used directly. 280 type DirectoryServer struct { 281 Broker *muxBroker 282 Directory directory.Backend 283 } 284 285 func (s *DirectoryServer) PutBlob( 286 args *DirPutBlobArgs, 287 reply *ErrorResponse) error { 288 // Connect to the data stream 289 conn, err := s.Broker.Dial(args.Id) 290 if err != nil { 291 *reply = ErrorResponse{Error: NewBasicError(err)} 292 return nil 293 } 294 defer conn.Close() 295 296 err = s.Directory.PutBlob(args.Key, &directory.BlobData{ 297 Data: conn, 298 }) 299 *reply = ErrorResponse{ 300 Error: NewBasicError(err), 301 } 302 return nil 303 } 304 305 func (s *DirectoryServer) GetBlob( 306 args *DirGetBlobArgs, 307 reply *DirGetBlobResponse) error { 308 // Connect to the data stream. We need to connect first because 309 // we need to at least connect and close the connection otherwise 310 // we'll leak a serveSingleCopy goroutine. This is why this cannot be 311 // below the GetBlob call below. 312 conn, err := s.Broker.Dial(args.Id) 313 if err != nil { 314 *reply = DirGetBlobResponse{Error: NewBasicError(err)} 315 return nil 316 } 317 318 // Get the blob. If we have an error we return right away 319 result, err := s.Directory.GetBlob(args.Key) 320 *reply = DirGetBlobResponse{ 321 Ok: result != nil, 322 Error: NewBasicError(err), 323 } 324 if err != nil { 325 conn.Close() 326 return nil 327 } 328 if result == nil { 329 // There is no data, so just close the data stream and return 330 conn.Close() 331 return nil 332 } 333 334 // No error! We need to copy the data from the blob into the 335 // resulting connection. We do this in a goroutine though since the 336 // data can be read at any speed. 337 go func() { 338 defer conn.Close() 339 defer result.Close() 340 if _, err := io.Copy(conn, result.Data); err != nil { 341 log.Printf( 342 "[ERR] rpc/directory: error copying getBlob data '%s': %s", 343 args.Key, 344 err) 345 } 346 }() 347 348 return nil 349 } 350 351 func (s *DirectoryServer) PutInfra( 352 args *directory.Infra, 353 reply *DirPutInfraResponse) error { 354 err := s.Directory.PutInfra(args) 355 *reply = DirPutInfraResponse{ 356 Value: args, 357 Error: NewBasicError(err), 358 } 359 return nil 360 } 361 362 func (s *DirectoryServer) GetInfra( 363 args *directory.Infra, 364 reply *DirGetInfraResponse) error { 365 result, err := s.Directory.GetInfra(args) 366 *reply = DirGetInfraResponse{ 367 Value: result, 368 Error: NewBasicError(err), 369 } 370 return nil 371 } 372 373 func (s *DirectoryServer) PutDev( 374 args *directory.Dev, 375 reply *DirPutDevResponse) error { 376 err := s.Directory.PutDev(args) 377 *reply = DirPutDevResponse{ 378 Value: args, 379 Error: NewBasicError(err), 380 } 381 return nil 382 } 383 384 func (s *DirectoryServer) GetDev( 385 args *directory.Dev, 386 reply *DirGetDevResponse) error { 387 result, err := s.Directory.GetDev(args) 388 *reply = DirGetDevResponse{ 389 Value: result, 390 Error: NewBasicError(err), 391 } 392 return nil 393 } 394 395 func (s *DirectoryServer) DeleteDev( 396 args *directory.Dev, 397 reply *ErrorResponse) error { 398 err := s.Directory.DeleteDev(args) 399 *reply = ErrorResponse{ 400 Error: NewBasicError(err), 401 } 402 return nil 403 } 404 405 func (s *DirectoryServer) PutBuild( 406 args *directory.Build, 407 reply *DirPutBuildResponse) error { 408 err := s.Directory.PutBuild(args) 409 *reply = DirPutBuildResponse{ 410 Value: args, 411 Error: NewBasicError(err), 412 } 413 return nil 414 } 415 416 func (s *DirectoryServer) GetBuild( 417 args *directory.Build, 418 reply *DirGetBuildResponse) error { 419 result, err := s.Directory.GetBuild(args) 420 *reply = DirGetBuildResponse{ 421 Value: result, 422 Error: NewBasicError(err), 423 } 424 return nil 425 } 426 427 func (s *DirectoryServer) PutDeploy( 428 args *directory.Deploy, 429 reply *DirPutDeployResponse) error { 430 err := s.Directory.PutDeploy(args) 431 *reply = DirPutDeployResponse{ 432 Value: args, 433 Error: NewBasicError(err), 434 } 435 return nil 436 } 437 438 func (s *DirectoryServer) GetDeploy( 439 args *directory.Deploy, 440 reply *DirGetDeployResponse) error { 441 result, err := s.Directory.GetDeploy(args) 442 *reply = DirGetDeployResponse{ 443 Value: result, 444 Error: NewBasicError(err), 445 } 446 return nil 447 }