storj.io/uplink@v1.13.0/private/metaclient/client.go (about) 1 // Copyright (C) 2019 Storj Labs, Inc. 2 // See LICENSE for copying information. 3 4 package metaclient 5 6 import ( 7 "bytes" 8 "context" 9 "os" 10 "sync" 11 "time" 12 13 "github.com/klauspost/compress/zstd" 14 "github.com/spacemonkeygo/monkit/v3" 15 "github.com/zeebo/errs" 16 17 "storj.io/common/errs2" 18 "storj.io/common/macaroon" 19 "storj.io/common/pb" 20 "storj.io/common/rpc" 21 "storj.io/common/rpc/rpcstatus" 22 "storj.io/common/storj" 23 "storj.io/uplink/private/eestream" 24 ) 25 26 var ( 27 mon = monkit.Package() 28 29 // Error is the errs class of standard metainfo errors. 30 Error = errs.Class("metaclient") 31 32 zstdDecoder = func() *zstd.Decoder { 33 decoder, err := zstd.NewReader(nil, 34 zstd.WithDecoderMaxMemory(64<<20), 35 ) 36 if err != nil { 37 panic(err) 38 } 39 return decoder 40 }() 41 ) 42 43 // Client creates a grpcClient. 44 type Client struct { 45 mu sync.Mutex 46 conn *rpc.Conn 47 client pb.DRPCMetainfoClient 48 apiKeyRaw []byte 49 50 userAgent string 51 } 52 53 // DialNodeURL dials to metainfo endpoint with the specified api key. 54 func DialNodeURL(ctx context.Context, dialer rpc.Dialer, nodeURL string, apiKey *macaroon.APIKey, userAgent string) (*Client, error) { 55 url, err := storj.ParseNodeURL(nodeURL) 56 if err != nil { 57 return nil, Error.Wrap(err) 58 } 59 60 if url.ID.IsZero() { 61 return nil, Error.New("node ID is required in node URL %q", nodeURL) 62 } 63 64 conn, err := dialer.DialNode(ctx, url, rpc.DialOptions{ForceTCPFastOpenMultidialSupport: true}) 65 if err != nil { 66 return nil, Error.Wrap(err) 67 } 68 69 return &Client{ 70 conn: conn, 71 client: pb.NewDRPCMetainfoClient(conn), 72 apiKeyRaw: apiKey.SerializeRaw(), 73 74 userAgent: userAgent, 75 }, nil 76 } 77 78 // Close closes the dialed connection. 79 func (client *Client) Close() error { 80 client.mu.Lock() 81 defer client.mu.Unlock() 82 83 if client.conn != nil { 84 err := client.conn.Close() 85 client.conn = nil 86 return Error.Wrap(err) 87 } 88 89 return nil 90 } 91 92 func (client *Client) header() *pb.RequestHeader { 93 return &pb.RequestHeader{ 94 ApiKey: client.apiKeyRaw, 95 UserAgent: []byte(client.userAgent), 96 } 97 } 98 99 // GetProjectInfo gets the ProjectInfo for the api key associated with the metainfo client. 100 func (client *Client) GetProjectInfo(ctx context.Context) (response *pb.ProjectInfoResponse, err error) { 101 defer mon.Task()(&ctx)(&err) 102 103 err = WithRetry(ctx, func(ctx context.Context) error { 104 response, err = client.client.ProjectInfo(ctx, &pb.ProjectInfoRequest{ 105 Header: client.header(), 106 }) 107 return err 108 }) 109 return response, err 110 } 111 112 // CreateBucketParams parameters for CreateBucket method. 113 type CreateBucketParams struct { 114 Name []byte 115 } 116 117 func (params *CreateBucketParams) toRequest(header *pb.RequestHeader) *pb.BucketCreateRequest { 118 return &pb.BucketCreateRequest{ 119 Header: header, 120 Name: params.Name, 121 } 122 } 123 124 // BatchItem returns single item for batch request. 125 func (params *CreateBucketParams) BatchItem() *pb.BatchRequestItem { 126 return &pb.BatchRequestItem{ 127 Request: &pb.BatchRequestItem_BucketCreate{ 128 BucketCreate: params.toRequest(nil), 129 }, 130 } 131 } 132 133 // CreateBucketResponse response for CreateBucket request. 134 type CreateBucketResponse struct { 135 Bucket Bucket 136 } 137 138 func newCreateBucketResponse(response *pb.BucketCreateResponse) (CreateBucketResponse, error) { 139 bucket, err := convertProtoToBucket(response.Bucket) 140 if err != nil { 141 return CreateBucketResponse{}, err 142 } 143 return CreateBucketResponse{ 144 Bucket: bucket, 145 }, nil 146 } 147 148 // CreateBucket creates a new bucket. 149 func (client *Client) CreateBucket(ctx context.Context, params CreateBucketParams) (respBucket Bucket, err error) { 150 defer mon.Task()(&ctx)(&err) 151 152 var response *pb.BucketCreateResponse 153 err = WithRetry(ctx, func(ctx context.Context) error { 154 response, err = client.client.CreateBucket(ctx, params.toRequest(client.header())) 155 return err 156 }) 157 if err != nil { 158 return Bucket{}, Error.Wrap(err) 159 } 160 161 respBucket, err = convertProtoToBucket(response.Bucket) 162 if err != nil { 163 return Bucket{}, Error.Wrap(err) 164 } 165 return respBucket, nil 166 } 167 168 // GetBucketParams parameters for GetBucketParams method. 169 type GetBucketParams struct { 170 Name []byte 171 } 172 173 func (params *GetBucketParams) toRequest(header *pb.RequestHeader) *pb.BucketGetRequest { 174 return &pb.BucketGetRequest{ 175 Header: header, 176 Name: params.Name, 177 } 178 } 179 180 // BatchItem returns single item for batch request. 181 func (params *GetBucketParams) BatchItem() *pb.BatchRequestItem { 182 return &pb.BatchRequestItem{ 183 Request: &pb.BatchRequestItem_BucketGet{ 184 BucketGet: params.toRequest(nil), 185 }, 186 } 187 } 188 189 // GetBucketResponse response for GetBucket request. 190 type GetBucketResponse struct { 191 Bucket Bucket 192 } 193 194 func newGetBucketResponse(response *pb.BucketGetResponse) (GetBucketResponse, error) { 195 bucket, err := convertProtoToBucket(response.Bucket) 196 if err != nil { 197 return GetBucketResponse{}, err 198 } 199 return GetBucketResponse{ 200 Bucket: bucket, 201 }, nil 202 } 203 204 // GetBucket returns a bucket. 205 func (client *Client) GetBucket(ctx context.Context, params GetBucketParams) (respBucket Bucket, err error) { 206 defer mon.Task()(&ctx)(&err) 207 208 var items []BatchResponse 209 err = WithRetry(ctx, func(ctx context.Context) error { 210 // TODO(moby) make sure bucket not found is properly handled 211 items, err = client.Batch(ctx, ¶ms) 212 return err 213 }) 214 if err != nil { 215 if errs2.IsRPC(err, rpcstatus.NotFound) { 216 return Bucket{}, ErrBucketNotFound.Wrap(err) 217 } 218 return Bucket{}, Error.Wrap(err) 219 } 220 if len(items) != 1 { 221 return Bucket{}, Error.New("unexpected number of responses: %d", len(items)) 222 } 223 response, ok := items[0].pbResponse.(*pb.BatchResponseItem_BucketGet) 224 if !ok { 225 return Bucket{}, Error.New("unexpected response type: %T", items[0].pbResponse) 226 } 227 228 respBucket, err = convertProtoToBucket(response.BucketGet.Bucket) 229 if err != nil { 230 return Bucket{}, Error.Wrap(err) 231 } 232 return respBucket, nil 233 } 234 235 // GetBucketLocationParams parameters for GetBucketLocation method. 236 type GetBucketLocationParams struct { 237 Name []byte 238 } 239 240 func (params *GetBucketLocationParams) toRequest(header *pb.RequestHeader) *pb.GetBucketLocationRequest { 241 return &pb.GetBucketLocationRequest{ 242 Header: header, 243 Name: params.Name, 244 } 245 } 246 247 // BatchItem returns single item for batch request. 248 func (params *GetBucketLocationParams) BatchItem() *pb.BatchRequestItem { 249 return &pb.BatchRequestItem{ 250 Request: &pb.BatchRequestItem_BucketGetLocation{ 251 BucketGetLocation: params.toRequest(nil), 252 }, 253 } 254 } 255 256 // GetBucketLocationResponse response for GetBucketLocation request. 257 type GetBucketLocationResponse struct { 258 Location []byte 259 } 260 261 // GetBucketLocation returns a bucket location. 262 func (client *Client) GetBucketLocation(ctx context.Context, params GetBucketLocationParams) (_ GetBucketLocationResponse, err error) { 263 defer mon.Task()(&ctx)(&err) 264 265 var response *pb.GetBucketLocationResponse 266 err = WithRetry(ctx, func(ctx context.Context) error { 267 response, err = client.client.GetBucketLocation(ctx, params.toRequest(client.header())) 268 return err 269 }) 270 if err != nil { 271 return GetBucketLocationResponse{}, Error.Wrap(err) 272 } 273 274 return GetBucketLocationResponse{ 275 Location: response.Location, 276 }, nil 277 } 278 279 // GetBucketVersioningParams parameters for GetBucketVersioning method. 280 type GetBucketVersioningParams struct { 281 Name []byte 282 } 283 284 func (params *GetBucketVersioningParams) toRequest(header *pb.RequestHeader) *pb.GetBucketVersioningRequest { 285 return &pb.GetBucketVersioningRequest{ 286 Header: header, 287 Name: params.Name, 288 } 289 } 290 291 // BatchItem returns single item for batch request. 292 func (params *GetBucketVersioningParams) BatchItem() *pb.BatchRequestItem { 293 return &pb.BatchRequestItem{ 294 Request: &pb.BatchRequestItem_BucketGetVersioning{ 295 BucketGetVersioning: params.toRequest(nil), 296 }, 297 } 298 } 299 300 // GetBucketVersioningResponse response for GetBucketVersioning request. 301 type GetBucketVersioningResponse struct { 302 Versioning int32 303 } 304 305 // GetBucketVersioning returns a bucket versioning state. 306 func (client *Client) GetBucketVersioning(ctx context.Context, params GetBucketVersioningParams) (_ GetBucketVersioningResponse, err error) { 307 defer mon.Task()(&ctx)(&err) 308 309 var response *pb.GetBucketVersioningResponse 310 err = WithRetry(ctx, func(ctx context.Context) error { 311 response, err = client.client.GetBucketVersioning(ctx, params.toRequest(client.header())) 312 return err 313 }) 314 if err != nil { 315 return GetBucketVersioningResponse{}, Error.Wrap(err) 316 } 317 318 return GetBucketVersioningResponse{ 319 Versioning: response.Versioning, 320 }, nil 321 } 322 323 // SetBucketVersioningParams parameters for SetBucketVersioning method. 324 type SetBucketVersioningParams struct { 325 Name []byte 326 Versioning bool 327 } 328 329 func (params *SetBucketVersioningParams) toRequest(header *pb.RequestHeader) *pb.SetBucketVersioningRequest { 330 return &pb.SetBucketVersioningRequest{ 331 Header: header, 332 Name: params.Name, 333 Versioning: params.Versioning, 334 } 335 } 336 337 // BatchItem returns single item for batch request. 338 func (params *SetBucketVersioningParams) BatchItem() *pb.BatchRequestItem { 339 return &pb.BatchRequestItem{ 340 Request: &pb.BatchRequestItem_BucketSetVersioning{ 341 BucketSetVersioning: params.toRequest(nil), 342 }, 343 } 344 } 345 346 // SetBucketVersioning attempts to enable/disable versioning for a bucket. 347 func (client *Client) SetBucketVersioning(ctx context.Context, params SetBucketVersioningParams) (err error) { 348 defer mon.Task()(&ctx)(&err) 349 350 err = WithRetry(ctx, func(ctx context.Context) error { 351 _, err = client.client.SetBucketVersioning(ctx, params.toRequest(client.header())) 352 return err 353 }) 354 return Error.Wrap(err) 355 } 356 357 // DeleteBucketParams parameters for DeleteBucket method. 358 type DeleteBucketParams struct { 359 Name []byte 360 DeleteAll bool 361 } 362 363 func (params *DeleteBucketParams) toRequest(header *pb.RequestHeader) *pb.BucketDeleteRequest { 364 return &pb.BucketDeleteRequest{ 365 Header: header, 366 Name: params.Name, 367 DeleteAll: params.DeleteAll, 368 } 369 } 370 371 // BatchItem returns single item for batch request. 372 func (params *DeleteBucketParams) BatchItem() *pb.BatchRequestItem { 373 return &pb.BatchRequestItem{ 374 Request: &pb.BatchRequestItem_BucketDelete{ 375 BucketDelete: params.toRequest(nil), 376 }, 377 } 378 } 379 380 // DeleteBucket deletes a bucket. 381 func (client *Client) DeleteBucket(ctx context.Context, params DeleteBucketParams) (_ Bucket, err error) { 382 defer mon.Task()(&ctx)(&err) 383 384 var response *pb.BucketDeleteResponse 385 err = WithRetry(ctx, func(ctx context.Context) error { 386 // TODO(moby) make sure bucket not found is properly handled 387 response, err = client.client.DeleteBucket(ctx, params.toRequest(client.header())) 388 return err 389 }) 390 if err != nil { 391 if errs2.IsRPC(err, rpcstatus.NotFound) { 392 return Bucket{}, ErrBucketNotFound.Wrap(err) 393 } 394 return Bucket{}, Error.Wrap(err) 395 } 396 397 respBucket, err := convertProtoToBucket(response.Bucket) 398 if err != nil { 399 return Bucket{}, Error.Wrap(err) 400 } 401 return respBucket, nil 402 } 403 404 // ListBucketsParams parameters for ListBucketsParams method. 405 type ListBucketsParams struct { 406 ListOpts BucketListOptions 407 } 408 409 func (params *ListBucketsParams) toRequest(header *pb.RequestHeader) *pb.BucketListRequest { 410 return &pb.BucketListRequest{ 411 Header: header, 412 Cursor: []byte(params.ListOpts.Cursor), 413 Limit: int32(params.ListOpts.Limit), 414 Direction: params.ListOpts.Direction, 415 } 416 } 417 418 // BatchItem returns single item for batch request. 419 func (params *ListBucketsParams) BatchItem() *pb.BatchRequestItem { 420 return &pb.BatchRequestItem{ 421 Request: &pb.BatchRequestItem_BucketList{ 422 BucketList: params.toRequest(nil), 423 }, 424 } 425 } 426 427 // ListBucketsResponse response for ListBucket request. 428 type ListBucketsResponse struct { 429 BucketList BucketList 430 } 431 432 func newListBucketsResponse(response *pb.BucketListResponse) ListBucketsResponse { 433 bucketList := BucketList{ 434 More: response.More, 435 } 436 bucketList.Items = make([]Bucket, len(response.Items)) 437 for i, item := range response.GetItems() { 438 bucketList.Items[i] = Bucket{ 439 Name: string(item.Name), 440 Created: item.CreatedAt, 441 } 442 } 443 return ListBucketsResponse{ 444 BucketList: bucketList, 445 } 446 } 447 448 // ListBuckets lists buckets. 449 func (client *Client) ListBuckets(ctx context.Context, params ListBucketsParams) (_ BucketList, err error) { 450 defer mon.Task()(&ctx)(&err) 451 452 var response *pb.BucketListResponse 453 err = WithRetry(ctx, func(ctx context.Context) error { 454 response, err = client.client.ListBuckets(ctx, params.toRequest(client.header())) 455 return err 456 }) 457 if err != nil { 458 return BucketList{}, Error.Wrap(err) 459 } 460 461 resultBucketList := BucketList{ 462 More: response.GetMore(), 463 } 464 resultBucketList.Items = make([]Bucket, len(response.GetItems())) 465 for i, item := range response.GetItems() { 466 resultBucketList.Items[i] = Bucket{ 467 Name: string(item.GetName()), 468 Created: item.GetCreatedAt(), 469 Attribution: string(item.GetUserAgent()), 470 } 471 } 472 return resultBucketList, nil 473 } 474 475 func convertProtoToBucket(pbBucket *pb.Bucket) (bucket Bucket, err error) { 476 if pbBucket == nil { 477 return Bucket{}, nil 478 } 479 480 return Bucket{ 481 Name: string(pbBucket.GetName()), 482 Created: pbBucket.GetCreatedAt(), 483 }, nil 484 } 485 486 // BeginObjectParams parameters for BeginObject method. 487 type BeginObjectParams struct { 488 Bucket []byte 489 EncryptedObjectKey []byte 490 Version int32 491 Redundancy storj.RedundancyScheme 492 EncryptionParameters storj.EncryptionParameters 493 ExpiresAt time.Time 494 495 EncryptedMetadata []byte 496 EncryptedMetadataEncryptedKey []byte 497 EncryptedMetadataNonce storj.Nonce 498 } 499 500 func (params *BeginObjectParams) toRequest(header *pb.RequestHeader) *pb.ObjectBeginRequest { 501 return &pb.ObjectBeginRequest{ 502 Header: header, 503 Bucket: params.Bucket, 504 EncryptedObjectKey: params.EncryptedObjectKey, 505 Version: params.Version, 506 ExpiresAt: params.ExpiresAt, 507 RedundancyScheme: &pb.RedundancyScheme{ 508 Type: pb.RedundancyScheme_SchemeType(params.Redundancy.Algorithm), 509 ErasureShareSize: params.Redundancy.ShareSize, 510 MinReq: int32(params.Redundancy.RequiredShares), 511 RepairThreshold: int32(params.Redundancy.RepairShares), 512 SuccessThreshold: int32(params.Redundancy.OptimalShares), 513 Total: int32(params.Redundancy.TotalShares), 514 }, 515 EncryptionParameters: &pb.EncryptionParameters{ 516 CipherSuite: pb.CipherSuite(params.EncryptionParameters.CipherSuite), 517 BlockSize: int64(params.EncryptionParameters.BlockSize), 518 }, 519 520 EncryptedMetadata: params.EncryptedMetadata, 521 EncryptedMetadataEncryptedKey: params.EncryptedMetadataEncryptedKey, 522 EncryptedMetadataNonce: params.EncryptedMetadataNonce, 523 } 524 } 525 526 // BatchItem returns single item for batch request. 527 func (params *BeginObjectParams) BatchItem() *pb.BatchRequestItem { 528 return &pb.BatchRequestItem{ 529 Request: &pb.BatchRequestItem_ObjectBegin{ 530 ObjectBegin: params.toRequest(nil), 531 }, 532 } 533 } 534 535 // BeginObjectResponse response for BeginObject request. 536 type BeginObjectResponse struct { 537 StreamID storj.StreamID 538 } 539 540 func newBeginObjectResponse(response *pb.ObjectBeginResponse) BeginObjectResponse { 541 return BeginObjectResponse{ 542 StreamID: response.StreamId, 543 } 544 } 545 546 // BeginObject begins object creation. 547 func (client *Client) BeginObject(ctx context.Context, params BeginObjectParams) (_ BeginObjectResponse, err error) { 548 defer mon.Task()(&ctx)(&err) 549 550 var response *pb.ObjectBeginResponse 551 err = WithRetry(ctx, func(ctx context.Context) error { 552 response, err = client.client.BeginObject(ctx, params.toRequest(client.header())) 553 return err 554 }) 555 if err != nil { 556 return BeginObjectResponse{}, Error.Wrap(err) 557 } 558 559 return newBeginObjectResponse(response), nil 560 } 561 562 // CommitObjectParams parameters for CommitObject method. 563 type CommitObjectParams struct { 564 StreamID storj.StreamID 565 566 EncryptedMetadataNonce storj.Nonce 567 EncryptedMetadata []byte 568 EncryptedMetadataEncryptedKey []byte 569 } 570 571 func (params *CommitObjectParams) toRequest(header *pb.RequestHeader) *pb.ObjectCommitRequest { 572 return &pb.ObjectCommitRequest{ 573 Header: header, 574 StreamId: params.StreamID, 575 EncryptedMetadataNonce: params.EncryptedMetadataNonce, 576 EncryptedMetadata: params.EncryptedMetadata, 577 EncryptedMetadataEncryptedKey: params.EncryptedMetadataEncryptedKey, 578 } 579 } 580 581 // BatchItem returns single item for batch request. 582 func (params *CommitObjectParams) BatchItem() *pb.BatchRequestItem { 583 return &pb.BatchRequestItem{ 584 Request: &pb.BatchRequestItem_ObjectCommit{ 585 ObjectCommit: params.toRequest(nil), 586 }, 587 } 588 } 589 590 // CommitObjectResponse response for CommitObject request. 591 type CommitObjectResponse struct { 592 Object RawObjectItem 593 } 594 595 // CommitObject commits a created object. 596 // TODO remove when all code will be adjusted. 597 func (client *Client) CommitObject(ctx context.Context, params CommitObjectParams) (err error) { 598 defer mon.Task()(&ctx)(&err) 599 600 return WithRetry(ctx, func(ctx context.Context) error { 601 _, err = client.client.CommitObject(ctx, params.toRequest(client.header())) 602 return err 603 }) 604 } 605 606 // CommitObjectWithResponse commits a created object. 607 func (client *Client) CommitObjectWithResponse(ctx context.Context, params CommitObjectParams) (_ CommitObjectResponse, err error) { 608 defer mon.Task()(&ctx)(&err) 609 610 var response *pb.CommitObjectResponse 611 err = WithRetry(ctx, func(ctx context.Context) error { 612 response, err = client.client.CommitObject(ctx, params.toRequest(client.header())) 613 return err 614 }) 615 616 if err != nil { 617 return CommitObjectResponse{}, Error.Wrap(err) 618 } 619 620 return CommitObjectResponse{ 621 Object: newObjectInfo(response.Object), 622 }, nil 623 } 624 625 // GetObjectParams parameters for GetObject method. 626 type GetObjectParams struct { 627 Bucket []byte 628 EncryptedObjectKey []byte 629 Version []byte 630 631 RedundancySchemePerSegment bool 632 } 633 634 func (params *GetObjectParams) toRequest(header *pb.RequestHeader) *pb.ObjectGetRequest { 635 return &pb.ObjectGetRequest{ 636 Header: header, 637 Bucket: params.Bucket, 638 EncryptedObjectKey: params.EncryptedObjectKey, 639 ObjectVersion: params.Version, 640 RedundancySchemePerSegment: params.RedundancySchemePerSegment, 641 } 642 } 643 644 // BatchItem returns single item for batch request. 645 func (params *GetObjectParams) BatchItem() *pb.BatchRequestItem { 646 return &pb.BatchRequestItem{ 647 Request: &pb.BatchRequestItem_ObjectGet{ 648 ObjectGet: params.toRequest(nil), 649 }, 650 } 651 } 652 653 // GetObjectResponse response for GetObject request. 654 type GetObjectResponse struct { 655 Info RawObjectItem 656 } 657 658 func newGetObjectResponse(response *pb.ObjectGetResponse) GetObjectResponse { 659 return GetObjectResponse{ 660 Info: newObjectInfo(response.Object), 661 } 662 } 663 664 func newObjectInfo(object *pb.Object) RawObjectItem { 665 if object == nil { 666 return RawObjectItem{} 667 } 668 669 info := RawObjectItem{ 670 Bucket: string(object.Bucket), 671 EncryptedObjectKey: object.EncryptedObjectKey, 672 Version: object.ObjectVersion, 673 Status: int32(object.Status), 674 675 StreamID: object.StreamId, 676 677 Created: object.CreatedAt, 678 PlainSize: object.PlainSize, 679 Expires: object.ExpiresAt, 680 EncryptedMetadata: object.EncryptedMetadata, 681 EncryptedMetadataNonce: object.EncryptedMetadataNonce, 682 EncryptedMetadataEncryptedKey: object.EncryptedMetadataEncryptedKey, 683 } 684 685 if object.Retention != nil { 686 info.Retention = &Retention{ 687 Mode: storj.RetentionMode(object.Retention.Mode), 688 RetainUntil: object.Retention.RetainUntil, 689 } 690 } 691 692 if object.EncryptionParameters != nil { 693 info.EncryptionParameters = storj.EncryptionParameters{ 694 CipherSuite: storj.CipherSuite(object.EncryptionParameters.CipherSuite), 695 BlockSize: int32(object.EncryptionParameters.BlockSize), 696 } 697 } 698 699 pbRS := object.RedundancyScheme 700 if pbRS != nil { 701 info.RedundancyScheme = storj.RedundancyScheme{ 702 Algorithm: storj.RedundancyAlgorithm(pbRS.Type), 703 ShareSize: pbRS.ErasureShareSize, 704 RequiredShares: int16(pbRS.MinReq), 705 RepairShares: int16(pbRS.RepairThreshold), 706 OptimalShares: int16(pbRS.SuccessThreshold), 707 TotalShares: int16(pbRS.Total), 708 } 709 } 710 return info 711 } 712 713 // GetObject gets single object. 714 func (client *Client) GetObject(ctx context.Context, params GetObjectParams) (_ RawObjectItem, err error) { 715 defer mon.Task()(&ctx)(&err) 716 717 var items []BatchResponse 718 err = WithRetry(ctx, func(ctx context.Context) error { 719 items, err = client.Batch(ctx, ¶ms) 720 return err 721 }) 722 if err != nil { 723 if errs2.IsRPC(err, rpcstatus.NotFound) { 724 return RawObjectItem{}, ErrObjectNotFound.Wrap(err) 725 } 726 return RawObjectItem{}, Error.Wrap(err) 727 } 728 if len(items) != 1 { 729 return RawObjectItem{}, Error.New("unexpected number of responses: %d", len(items)) 730 } 731 response, ok := items[0].pbResponse.(*pb.BatchResponseItem_ObjectGet) 732 if !ok { 733 return RawObjectItem{}, Error.New("unexpected response type: %T", items[0].pbResponse) 734 } 735 736 getResponse := newGetObjectResponse(response.ObjectGet) 737 return getResponse.Info, nil 738 } 739 740 // GetObjectIPsParams are params for the GetObjectIPs request. 741 type GetObjectIPsParams struct { 742 Bucket []byte 743 EncryptedObjectKey []byte 744 } 745 746 // GetObjectIPsResponse is the response from GetObjectIPs. 747 type GetObjectIPsResponse struct { 748 IPPorts [][]byte 749 SegmentCount int64 750 PieceCount int64 751 ReliablePieceCount int64 752 } 753 754 func (params *GetObjectIPsParams) toRequest(header *pb.RequestHeader) *pb.ObjectGetIPsRequest { 755 return &pb.ObjectGetIPsRequest{ 756 Header: header, 757 Bucket: params.Bucket, 758 EncryptedObjectKey: params.EncryptedObjectKey, 759 } 760 } 761 762 // GetObjectIPs returns the IP addresses of the nodes which hold the object. 763 func (client *Client) GetObjectIPs(ctx context.Context, params GetObjectIPsParams) (r *GetObjectIPsResponse, err error) { 764 defer mon.Task()(&ctx)(&err) 765 766 var response *pb.ObjectGetIPsResponse 767 err = WithRetry(ctx, func(ctx context.Context) error { 768 response, err = client.client.GetObjectIPs(ctx, params.toRequest(client.header())) 769 return err 770 }) 771 if err != nil { 772 if errs2.IsRPC(err, rpcstatus.NotFound) { 773 return nil, ErrObjectNotFound.Wrap(err) 774 } 775 return nil, Error.Wrap(err) 776 } 777 778 return &GetObjectIPsResponse{ 779 IPPorts: response.Ips, 780 SegmentCount: response.SegmentCount, 781 PieceCount: response.PieceCount, 782 ReliablePieceCount: response.ReliablePieceCount, 783 }, nil 784 } 785 786 // UpdateObjectMetadataParams are params for the UpdateObjectMetadata request. 787 type UpdateObjectMetadataParams struct { 788 Bucket []byte 789 EncryptedObjectKey []byte 790 StreamID storj.StreamID 791 792 EncryptedMetadataNonce storj.Nonce 793 EncryptedMetadata []byte 794 EncryptedMetadataEncryptedKey []byte 795 } 796 797 func (params *UpdateObjectMetadataParams) toRequest(header *pb.RequestHeader) *pb.ObjectUpdateMetadataRequest { 798 return &pb.ObjectUpdateMetadataRequest{ 799 Header: header, 800 Bucket: params.Bucket, 801 EncryptedObjectKey: params.EncryptedObjectKey, 802 StreamId: params.StreamID, 803 EncryptedMetadataNonce: params.EncryptedMetadataNonce, 804 EncryptedMetadata: params.EncryptedMetadata, 805 EncryptedMetadataEncryptedKey: params.EncryptedMetadataEncryptedKey, 806 } 807 } 808 809 // UpdateObjectMetadata replaces objects metadata. 810 func (client *Client) UpdateObjectMetadata(ctx context.Context, params UpdateObjectMetadataParams) (err error) { 811 defer mon.Task()(&ctx)(&err) 812 813 err = WithRetry(ctx, func(ctx context.Context) error { 814 _, err = client.client.UpdateObjectMetadata(ctx, params.toRequest(client.header())) 815 return err 816 }) 817 if err != nil { 818 if errs2.IsRPC(err, rpcstatus.NotFound) { 819 return ErrObjectNotFound.Wrap(err) 820 } 821 } 822 823 return Error.Wrap(err) 824 } 825 826 // BeginDeleteObjectParams parameters for BeginDeleteObject method. 827 type BeginDeleteObjectParams struct { 828 Bucket []byte 829 EncryptedObjectKey []byte 830 Version []byte 831 StreamID storj.StreamID 832 Status int32 833 } 834 835 func (params *BeginDeleteObjectParams) toRequest(header *pb.RequestHeader) *pb.ObjectBeginDeleteRequest { 836 return &pb.ObjectBeginDeleteRequest{ 837 Header: header, 838 Bucket: params.Bucket, 839 EncryptedObjectKey: params.EncryptedObjectKey, 840 ObjectVersion: params.Version, 841 StreamId: ¶ms.StreamID, 842 Status: params.Status, 843 } 844 } 845 846 // BatchItem returns single item for batch request. 847 func (params *BeginDeleteObjectParams) BatchItem() *pb.BatchRequestItem { 848 return &pb.BatchRequestItem{ 849 Request: &pb.BatchRequestItem_ObjectBeginDelete{ 850 ObjectBeginDelete: params.toRequest(nil), 851 }, 852 } 853 } 854 855 // BeginDeleteObjectResponse response for BeginDeleteObject request. 856 type BeginDeleteObjectResponse struct { 857 } 858 859 func newBeginDeleteObjectResponse(response *pb.ObjectBeginDeleteResponse) BeginDeleteObjectResponse { 860 return BeginDeleteObjectResponse{} 861 } 862 863 // BeginDeleteObject begins object deletion process. 864 func (client *Client) BeginDeleteObject(ctx context.Context, params BeginDeleteObjectParams) (_ RawObjectItem, err error) { 865 defer mon.Task()(&ctx)(&err) 866 867 var response *pb.ObjectBeginDeleteResponse 868 err = WithRetry(ctx, func(ctx context.Context) error { 869 // response.StreamID is not processed because satellite will always return nil 870 response, err = client.client.BeginDeleteObject(ctx, params.toRequest(client.header())) 871 return err 872 }) 873 if err != nil { 874 if errs2.IsRPC(err, rpcstatus.NotFound) { 875 return RawObjectItem{}, ErrObjectNotFound.Wrap(err) 876 } 877 return RawObjectItem{}, Error.Wrap(err) 878 } 879 880 return newObjectInfo(response.Object), nil 881 } 882 883 // ListObjectsParams parameters for ListObjects method. 884 type ListObjectsParams struct { 885 Bucket []byte 886 EncryptedPrefix []byte 887 EncryptedCursor []byte 888 VersionCursor []byte 889 Limit int32 890 IncludeCustomMetadata bool 891 IncludeSystemMetadata bool 892 Recursive bool 893 Status int32 894 IncludeAllVersions bool 895 } 896 897 func (params *ListObjectsParams) toRequest(header *pb.RequestHeader) *pb.ObjectListRequest { 898 return &pb.ObjectListRequest{ 899 Header: header, 900 Bucket: params.Bucket, 901 EncryptedPrefix: params.EncryptedPrefix, 902 EncryptedCursor: params.EncryptedCursor, 903 Limit: params.Limit, 904 ObjectIncludes: &pb.ObjectListItemIncludes{ 905 Metadata: params.IncludeCustomMetadata, 906 ExcludeSystemMetadata: !params.IncludeSystemMetadata, 907 }, 908 UseObjectIncludes: true, 909 Recursive: params.Recursive, 910 Status: pb.Object_Status(params.Status), 911 IncludeAllVersions: params.IncludeAllVersions, 912 VersionCursor: params.VersionCursor, 913 } 914 } 915 916 // BatchItem returns single item for batch request. 917 func (params *ListObjectsParams) BatchItem() *pb.BatchRequestItem { 918 return &pb.BatchRequestItem{ 919 Request: &pb.BatchRequestItem_ObjectList{ 920 ObjectList: params.toRequest(nil), 921 }, 922 } 923 } 924 925 // ListObjectsResponse response for ListObjects request. 926 type ListObjectsResponse struct { 927 Items []RawObjectListItem 928 More bool 929 } 930 931 func newListObjectsResponse(response *pb.ObjectListResponse, encryptedPrefix []byte, recursive bool) ListObjectsResponse { 932 objects := make([]RawObjectListItem, len(response.Items)) 933 for i, object := range response.Items { 934 encryptedObjectKey := object.EncryptedObjectKey 935 isPrefix := false 936 if !recursive && len(encryptedObjectKey) != 0 && encryptedObjectKey[len(encryptedObjectKey)-1] == '/' && !bytes.Equal(encryptedObjectKey, encryptedPrefix) { 937 isPrefix = true 938 } 939 940 objects[i] = RawObjectListItem{ 941 EncryptedObjectKey: object.EncryptedObjectKey, 942 Version: object.ObjectVersion, 943 Status: int32(object.Status), 944 CreatedAt: object.CreatedAt, 945 ExpiresAt: object.ExpiresAt, 946 PlainSize: object.PlainSize, 947 EncryptedMetadataNonce: object.EncryptedMetadataNonce, 948 EncryptedMetadataEncryptedKey: object.EncryptedMetadataEncryptedKey, 949 EncryptedMetadata: object.EncryptedMetadata, 950 951 IsPrefix: isPrefix, 952 } 953 954 if object.StreamId != nil { 955 objects[i].StreamID = *object.StreamId 956 } 957 } 958 959 return ListObjectsResponse{ 960 Items: objects, 961 More: response.More, 962 } 963 } 964 965 // ListObjects lists objects according to specific parameters. 966 func (client *Client) ListObjects(ctx context.Context, params ListObjectsParams) (_ []RawObjectListItem, more bool, err error) { 967 defer mon.Task()(&ctx)(&err) 968 969 var items []BatchResponse 970 err = WithRetry(ctx, func(ctx context.Context) error { 971 items, err = client.Batch(ctx, ¶ms) 972 return err 973 }) 974 if err != nil { 975 return []RawObjectListItem{}, false, Error.Wrap(err) 976 } 977 if len(items) != 1 { 978 return []RawObjectListItem{}, false, Error.New("unexpected number of responses: %d", len(items)) 979 } 980 response, ok := items[0].pbResponse.(*pb.BatchResponseItem_ObjectList) 981 if !ok { 982 return []RawObjectListItem{}, false, Error.New("unexpected response type: %T", items[0].pbResponse) 983 } 984 985 listResponse := newListObjectsResponse(response.ObjectList, params.EncryptedPrefix, params.Recursive) 986 return listResponse.Items, listResponse.More, Error.Wrap(err) 987 } 988 989 // ListPendingObjectStreamsParams parameters for ListPendingObjectStreams method. 990 type ListPendingObjectStreamsParams struct { 991 Bucket []byte 992 EncryptedObjectKey []byte 993 EncryptedCursor []byte 994 Limit int32 995 } 996 997 func (params *ListPendingObjectStreamsParams) toRequest(header *pb.RequestHeader) *pb.ObjectListPendingStreamsRequest { 998 return &pb.ObjectListPendingStreamsRequest{ 999 Header: header, 1000 Bucket: params.Bucket, 1001 EncryptedObjectKey: params.EncryptedObjectKey, 1002 StreamIdCursor: params.EncryptedCursor, 1003 Limit: params.Limit, 1004 } 1005 } 1006 1007 // BatchItem returns single item for batch request. 1008 func (params *ListPendingObjectStreamsParams) BatchItem() *pb.BatchRequestItem { 1009 return &pb.BatchRequestItem{ 1010 Request: &pb.BatchRequestItem_ObjectListPendingStreams{ 1011 ObjectListPendingStreams: params.toRequest(nil), 1012 }, 1013 } 1014 } 1015 1016 // ListPendingObjectStreamsResponse response for ListPendingObjectStreams request. 1017 type ListPendingObjectStreamsResponse struct { 1018 Items []RawObjectListItem 1019 More bool 1020 } 1021 1022 func newListPendingObjectStreamsResponse(response *pb.ObjectListPendingStreamsResponse) ListPendingObjectStreamsResponse { 1023 objects := make([]RawObjectListItem, len(response.Items)) 1024 for i, object := range response.Items { 1025 1026 objects[i] = RawObjectListItem{ 1027 EncryptedObjectKey: object.EncryptedObjectKey, 1028 Version: object.ObjectVersion, 1029 Status: int32(object.Status), 1030 CreatedAt: object.CreatedAt, 1031 ExpiresAt: object.ExpiresAt, 1032 PlainSize: object.PlainSize, 1033 EncryptedMetadataNonce: object.EncryptedMetadataNonce, 1034 EncryptedMetadata: object.EncryptedMetadata, 1035 1036 IsPrefix: false, 1037 } 1038 1039 if object.StreamId != nil { 1040 objects[i].StreamID = *object.StreamId 1041 } 1042 } 1043 1044 return ListPendingObjectStreamsResponse{ 1045 Items: objects, 1046 More: response.More, 1047 } 1048 } 1049 1050 // ListPendingObjectStreams lists pending objects with the specified object key in the specified bucket. 1051 func (client *Client) ListPendingObjectStreams(ctx context.Context, params ListPendingObjectStreamsParams) (_ ListPendingObjectStreamsResponse, err error) { 1052 defer mon.Task()(&ctx)(&err) 1053 1054 var response *pb.ObjectListPendingStreamsResponse 1055 err = WithRetry(ctx, func(ctx context.Context) error { 1056 response, err = client.client.ListPendingObjectStreams(ctx, params.toRequest(client.header())) 1057 return err 1058 }) 1059 if err != nil { 1060 return ListPendingObjectStreamsResponse{}, Error.Wrap(err) 1061 } 1062 1063 return newListPendingObjectStreamsResponse(response), nil 1064 } 1065 1066 // SegmentListItem represents listed segment. 1067 type SegmentListItem struct { 1068 Position SegmentPosition 1069 PlainSize int64 1070 PlainOffset int64 1071 CreatedAt time.Time 1072 EncryptedETag []byte 1073 EncryptedKeyNonce storj.Nonce 1074 EncryptedKey []byte 1075 } 1076 1077 // ListSegmentsParams parameters for ListSegments method. 1078 type ListSegmentsParams struct { 1079 StreamID []byte 1080 Cursor SegmentPosition 1081 Limit int32 1082 Range StreamRange 1083 } 1084 1085 func (params *ListSegmentsParams) toRequest(header *pb.RequestHeader) *pb.SegmentListRequest { 1086 return &pb.SegmentListRequest{ 1087 Header: header, 1088 StreamId: params.StreamID, 1089 CursorPosition: &pb.SegmentPosition{ 1090 PartNumber: params.Cursor.PartNumber, 1091 Index: params.Cursor.Index, 1092 }, 1093 Limit: params.Limit, 1094 Range: params.Range.toProto(), 1095 } 1096 } 1097 1098 // BatchItem returns single item for batch request. 1099 func (params *ListSegmentsParams) BatchItem() *pb.BatchRequestItem { 1100 return &pb.BatchRequestItem{ 1101 Request: &pb.BatchRequestItem_SegmentList{ 1102 SegmentList: params.toRequest(nil), 1103 }, 1104 } 1105 } 1106 1107 // ListSegmentsResponse response for ListSegments request. 1108 type ListSegmentsResponse struct { 1109 Items []SegmentListItem 1110 More bool 1111 EncryptionParameters storj.EncryptionParameters 1112 } 1113 1114 func newListSegmentsResponse(response *pb.SegmentListResponse) ListSegmentsResponse { 1115 segments := make([]SegmentListItem, len(response.Items)) 1116 for i, segment := range response.Items { 1117 segments[i] = SegmentListItem{ 1118 Position: SegmentPosition{ 1119 PartNumber: segment.Position.PartNumber, 1120 Index: segment.Position.Index, 1121 }, 1122 PlainSize: segment.PlainSize, 1123 PlainOffset: segment.PlainOffset, 1124 CreatedAt: segment.CreatedAt, 1125 EncryptedETag: segment.EncryptedETag, 1126 EncryptedKeyNonce: segment.EncryptedKeyNonce, 1127 EncryptedKey: segment.EncryptedKey, 1128 } 1129 } 1130 1131 ep := storj.EncryptionParameters{} 1132 if response.EncryptionParameters != nil { 1133 ep = storj.EncryptionParameters{ 1134 CipherSuite: storj.CipherSuite(response.EncryptionParameters.CipherSuite), 1135 BlockSize: int32(response.EncryptionParameters.BlockSize), 1136 } 1137 } 1138 1139 return ListSegmentsResponse{ 1140 Items: segments, 1141 More: response.More, 1142 EncryptionParameters: ep, 1143 } 1144 } 1145 1146 // ListSegments lists segments according to specific parameters. 1147 func (client *Client) ListSegments(ctx context.Context, params ListSegmentsParams) (_ ListSegmentsResponse, err error) { 1148 defer mon.Task()(&ctx)(&err) 1149 1150 var response *pb.SegmentListResponse 1151 err = WithRetry(ctx, func(ctx context.Context) error { 1152 response, err = client.client.ListSegments(ctx, params.toRequest(client.header())) 1153 return err 1154 }) 1155 if err != nil { 1156 return ListSegmentsResponse{}, Error.Wrap(err) 1157 } 1158 1159 return newListSegmentsResponse(response), nil 1160 } 1161 1162 // BeginSegmentParams parameters for BeginSegment method. 1163 type BeginSegmentParams struct { 1164 StreamID storj.StreamID 1165 Position SegmentPosition 1166 MaxOrderLimit int64 1167 } 1168 1169 func (params *BeginSegmentParams) toRequest(header *pb.RequestHeader) *pb.SegmentBeginRequest { 1170 return &pb.SegmentBeginRequest{ 1171 Header: header, 1172 StreamId: params.StreamID, 1173 Position: &pb.SegmentPosition{ 1174 PartNumber: params.Position.PartNumber, 1175 Index: params.Position.Index, 1176 }, 1177 MaxOrderLimit: params.MaxOrderLimit, 1178 } 1179 } 1180 1181 // BatchItem returns single item for batch request. 1182 func (params *BeginSegmentParams) BatchItem() *pb.BatchRequestItem { 1183 return &pb.BatchRequestItem{ 1184 Request: &pb.BatchRequestItem_SegmentBegin{ 1185 SegmentBegin: params.toRequest(nil), 1186 }, 1187 } 1188 } 1189 1190 // BeginSegmentResponse response for BeginSegment request. 1191 type BeginSegmentResponse struct { 1192 SegmentID storj.SegmentID 1193 Limits []*pb.AddressedOrderLimit 1194 PiecePrivateKey storj.PiecePrivateKey 1195 RedundancyStrategy eestream.RedundancyStrategy 1196 } 1197 1198 func newBeginSegmentResponse(response *pb.SegmentBeginResponse) (BeginSegmentResponse, error) { 1199 var rs eestream.RedundancyStrategy 1200 var err error 1201 if response.RedundancyScheme != nil { 1202 rs, err = eestream.NewRedundancyStrategyFromProto(response.RedundancyScheme) 1203 if err != nil { 1204 return BeginSegmentResponse{}, err 1205 } 1206 } 1207 return BeginSegmentResponse{ 1208 SegmentID: response.SegmentId, 1209 Limits: response.AddressedLimits, 1210 PiecePrivateKey: response.PrivateKey, 1211 RedundancyStrategy: rs, 1212 }, nil 1213 } 1214 1215 // BeginSegment begins a segment upload. 1216 func (client *Client) BeginSegment(ctx context.Context, params BeginSegmentParams) (_ BeginSegmentResponse, err error) { 1217 defer mon.Task()(&ctx)(&err) 1218 1219 var response *pb.SegmentBeginResponse 1220 err = WithRetry(ctx, func(ctx context.Context) error { 1221 response, err = client.client.BeginSegment(ctx, params.toRequest(client.header())) 1222 return err 1223 }) 1224 if err != nil { 1225 return BeginSegmentResponse{}, Error.Wrap(err) 1226 } 1227 1228 return newBeginSegmentResponse(response) 1229 } 1230 1231 // RetryBeginSegmentPiecesParams parameters for RetryBeginSegmentPieces method. 1232 type RetryBeginSegmentPiecesParams struct { 1233 SegmentID storj.SegmentID 1234 RetryPieceNumbers []int 1235 } 1236 1237 func (params *RetryBeginSegmentPiecesParams) toRequest(header *pb.RequestHeader) *pb.RetryBeginSegmentPiecesRequest { 1238 retryPieceNumbers := make([]int32, len(params.RetryPieceNumbers)) 1239 for i, pieceNumber := range params.RetryPieceNumbers { 1240 retryPieceNumbers[i] = int32(pieceNumber) 1241 } 1242 return &pb.RetryBeginSegmentPiecesRequest{ 1243 Header: header, 1244 SegmentId: params.SegmentID, 1245 RetryPieceNumbers: retryPieceNumbers, 1246 } 1247 } 1248 1249 // RetryBeginSegmentPiecesResponse response for RetryBeginSegmentPieces request. 1250 type RetryBeginSegmentPiecesResponse struct { 1251 SegmentID storj.SegmentID 1252 Limits []*pb.AddressedOrderLimit 1253 } 1254 1255 func newRetryBeginSegmentPiecesResponse(response *pb.RetryBeginSegmentPiecesResponse) (RetryBeginSegmentPiecesResponse, error) { 1256 return RetryBeginSegmentPiecesResponse{ 1257 SegmentID: response.SegmentId, 1258 Limits: response.AddressedLimits, 1259 }, nil 1260 } 1261 1262 // RetryBeginSegmentPieces exchanges piece orders. 1263 func (client *Client) RetryBeginSegmentPieces(ctx context.Context, params RetryBeginSegmentPiecesParams) (_ RetryBeginSegmentPiecesResponse, err error) { 1264 defer mon.Task()(&ctx)(&err) 1265 1266 var response *pb.RetryBeginSegmentPiecesResponse 1267 err = WithRetry(ctx, func(ctx context.Context) error { 1268 response, err = client.client.RetryBeginSegmentPieces(ctx, params.toRequest(client.header())) 1269 return err 1270 }) 1271 if err != nil { 1272 return RetryBeginSegmentPiecesResponse{}, Error.Wrap(err) 1273 } 1274 1275 return newRetryBeginSegmentPiecesResponse(response) 1276 } 1277 1278 // CommitSegmentParams parameters for CommitSegment method. 1279 type CommitSegmentParams struct { 1280 SegmentID storj.SegmentID 1281 Encryption SegmentEncryption 1282 SizeEncryptedData int64 1283 PlainSize int64 1284 EncryptedTag []byte 1285 1286 UploadResult []*pb.SegmentPieceUploadResult 1287 } 1288 1289 func (params *CommitSegmentParams) toRequest(header *pb.RequestHeader) *pb.SegmentCommitRequest { 1290 return &pb.SegmentCommitRequest{ 1291 Header: header, 1292 SegmentId: params.SegmentID, 1293 1294 EncryptedKeyNonce: params.Encryption.EncryptedKeyNonce, 1295 EncryptedKey: params.Encryption.EncryptedKey, 1296 SizeEncryptedData: params.SizeEncryptedData, 1297 PlainSize: params.PlainSize, 1298 EncryptedETag: params.EncryptedTag, 1299 UploadResult: params.UploadResult, 1300 } 1301 } 1302 1303 // BatchItem returns single item for batch request. 1304 func (params *CommitSegmentParams) BatchItem() *pb.BatchRequestItem { 1305 return &pb.BatchRequestItem{ 1306 Request: &pb.BatchRequestItem_SegmentCommit{ 1307 SegmentCommit: params.toRequest(nil), 1308 }, 1309 } 1310 } 1311 1312 // CommitSegment commits an uploaded segment. 1313 func (client *Client) CommitSegment(ctx context.Context, params CommitSegmentParams) (err error) { 1314 defer mon.Task()(&ctx)(&err) 1315 1316 err = WithRetry(ctx, func(ctx context.Context) error { 1317 _, err = client.client.CommitSegment(ctx, params.toRequest(client.header())) 1318 return err 1319 }) 1320 1321 return Error.Wrap(err) 1322 } 1323 1324 // MakeInlineSegmentParams parameters for MakeInlineSegment method. 1325 type MakeInlineSegmentParams struct { 1326 StreamID storj.StreamID 1327 Position SegmentPosition 1328 Encryption SegmentEncryption 1329 EncryptedInlineData []byte 1330 PlainSize int64 1331 EncryptedTag []byte 1332 } 1333 1334 func (params *MakeInlineSegmentParams) toRequest(header *pb.RequestHeader) *pb.SegmentMakeInlineRequest { 1335 return &pb.SegmentMakeInlineRequest{ 1336 Header: header, 1337 StreamId: params.StreamID, 1338 Position: &pb.SegmentPosition{ 1339 PartNumber: params.Position.PartNumber, 1340 Index: params.Position.Index, 1341 }, 1342 EncryptedKeyNonce: params.Encryption.EncryptedKeyNonce, 1343 EncryptedKey: params.Encryption.EncryptedKey, 1344 EncryptedInlineData: params.EncryptedInlineData, 1345 PlainSize: params.PlainSize, 1346 EncryptedETag: params.EncryptedTag, 1347 } 1348 } 1349 1350 // BatchItem returns single item for batch request. 1351 func (params *MakeInlineSegmentParams) BatchItem() *pb.BatchRequestItem { 1352 return &pb.BatchRequestItem{ 1353 Request: &pb.BatchRequestItem_SegmentMakeInline{ 1354 SegmentMakeInline: params.toRequest(nil), 1355 }, 1356 } 1357 } 1358 1359 // MakeInlineSegment creates an inline segment. 1360 func (client *Client) MakeInlineSegment(ctx context.Context, params MakeInlineSegmentParams) (err error) { 1361 defer mon.Task()(&ctx)(&err) 1362 1363 err = WithRetry(ctx, func(ctx context.Context) error { 1364 _, err = client.client.MakeInlineSegment(ctx, params.toRequest(client.header())) 1365 return err 1366 }) 1367 1368 return Error.Wrap(err) 1369 } 1370 1371 // DownloadObjectParams parameters for DownloadSegment method. 1372 type DownloadObjectParams struct { 1373 Bucket []byte 1374 EncryptedObjectKey []byte 1375 Version []byte 1376 1377 Range StreamRange 1378 } 1379 1380 // StreamRange contains range specification. 1381 type StreamRange struct { 1382 Mode StreamRangeMode 1383 Start int64 1384 Limit int64 1385 Suffix int64 1386 } 1387 1388 // StreamRangeMode contains different modes for range. 1389 type StreamRangeMode byte 1390 1391 const ( 1392 // StreamRangeAll selects all. 1393 StreamRangeAll StreamRangeMode = iota 1394 // StreamRangeStart selects starting from range.Start. 1395 StreamRangeStart 1396 // StreamRangeStartLimit selects starting from range.Start to range.End (inclusive). 1397 StreamRangeStartLimit 1398 // StreamRangeSuffix selects last range.Suffix bytes. 1399 StreamRangeSuffix 1400 ) 1401 1402 func (streamRange StreamRange) toProto() *pb.Range { 1403 switch streamRange.Mode { 1404 case StreamRangeAll: 1405 case StreamRangeStart: 1406 return &pb.Range{ 1407 Range: &pb.Range_Start{ 1408 Start: &pb.RangeStart{ 1409 PlainStart: streamRange.Start, 1410 }, 1411 }, 1412 } 1413 case StreamRangeStartLimit: 1414 return &pb.Range{ 1415 Range: &pb.Range_StartLimit{ 1416 StartLimit: &pb.RangeStartLimit{ 1417 PlainStart: streamRange.Start, 1418 PlainLimit: streamRange.Limit, 1419 }, 1420 }, 1421 } 1422 case StreamRangeSuffix: 1423 return &pb.Range{ 1424 Range: &pb.Range_Suffix{ 1425 Suffix: &pb.RangeSuffix{ 1426 PlainSuffix: streamRange.Suffix, 1427 }, 1428 }, 1429 } 1430 } 1431 return nil 1432 } 1433 1434 // Normalize converts the range to a StreamRangeStartLimit or StreamRangeAll. 1435 func (streamRange StreamRange) Normalize(plainSize int64) StreamRange { 1436 switch streamRange.Mode { 1437 case StreamRangeAll: 1438 streamRange.Start = 0 1439 streamRange.Limit = plainSize 1440 case StreamRangeStart: 1441 streamRange.Mode = StreamRangeStartLimit 1442 streamRange.Limit = plainSize 1443 case StreamRangeStartLimit: 1444 case StreamRangeSuffix: 1445 streamRange.Mode = StreamRangeStartLimit 1446 streamRange.Start = plainSize - streamRange.Suffix 1447 streamRange.Limit = plainSize 1448 } 1449 1450 if streamRange.Start < 0 { 1451 streamRange.Start = 0 1452 } 1453 if streamRange.Limit > plainSize { 1454 streamRange.Limit = plainSize 1455 } 1456 streamRange.Suffix = 0 1457 1458 return streamRange 1459 } 1460 1461 func (params *DownloadObjectParams) toRequest(header *pb.RequestHeader) *pb.ObjectDownloadRequest { 1462 return &pb.ObjectDownloadRequest{ 1463 Header: header, 1464 Bucket: params.Bucket, 1465 EncryptedObjectKey: params.EncryptedObjectKey, 1466 ObjectVersion: params.Version, 1467 Range: params.Range.toProto(), 1468 } 1469 } 1470 1471 // BatchItem returns single item for batch request. 1472 func (params *DownloadObjectParams) BatchItem() *pb.BatchRequestItem { 1473 return &pb.BatchRequestItem{ 1474 Request: &pb.BatchRequestItem_ObjectDownload{ 1475 ObjectDownload: params.toRequest(nil), 1476 }, 1477 } 1478 } 1479 1480 // DownloadObjectResponse response for DownloadSegment request. 1481 type DownloadObjectResponse struct { 1482 Object RawObjectItem 1483 DownloadedSegments []DownloadSegmentWithRSResponse 1484 ListSegments ListSegmentsResponse 1485 } 1486 1487 func newDownloadObjectResponse(response *pb.ObjectDownloadResponse) DownloadObjectResponse { 1488 downloadedSegments := make([]DownloadSegmentWithRSResponse, 0, len(response.SegmentDownload)) 1489 for _, segmentDownload := range response.SegmentDownload { 1490 downloadedSegments = append(downloadedSegments, newDownloadSegmentResponseWithRS(segmentDownload)) 1491 } 1492 return DownloadObjectResponse{ 1493 Object: newObjectInfo(response.Object), 1494 DownloadedSegments: downloadedSegments, 1495 ListSegments: newListSegmentsResponse(response.SegmentList), 1496 } 1497 } 1498 1499 // DownloadObject gets object information, lists segments and downloads the first segment. 1500 func (client *Client) DownloadObject(ctx context.Context, params DownloadObjectParams) (_ DownloadObjectResponse, err error) { 1501 defer mon.Task()(&ctx)(&err) 1502 1503 if os.Getenv("STORJ_COMPRESSED_BATCH") != "false" { 1504 return client.batchDownloadObject(ctx, params) 1505 } 1506 1507 var response *pb.ObjectDownloadResponse 1508 err = WithRetry(ctx, func(ctx context.Context) error { 1509 response, err = client.client.DownloadObject(ctx, params.toRequest(client.header())) 1510 return err 1511 }) 1512 if err != nil { 1513 if errs2.IsRPC(err, rpcstatus.NotFound) { 1514 return DownloadObjectResponse{}, ErrObjectNotFound.Wrap(err) 1515 } 1516 return DownloadObjectResponse{}, Error.Wrap(err) 1517 } 1518 return newDownloadObjectResponse(response), nil 1519 } 1520 1521 // batchDownloadObject is DownloadObject but goes through the batch rpc. 1522 func (client *Client) batchDownloadObject(ctx context.Context, params DownloadObjectParams) (_ DownloadObjectResponse, err error) { 1523 defer mon.Task()(&ctx)(&err) 1524 1525 var items []BatchResponse 1526 err = WithRetry(ctx, func(ctx context.Context) error { 1527 items, err = client.Batch(ctx, ¶ms) 1528 return err 1529 }) 1530 if err != nil { 1531 if errs2.IsRPC(err, rpcstatus.NotFound) { 1532 return DownloadObjectResponse{}, ErrObjectNotFound.Wrap(err) 1533 } 1534 return DownloadObjectResponse{}, Error.Wrap(err) 1535 } 1536 if len(items) != 1 { 1537 return DownloadObjectResponse{}, Error.New("unexpected number of responses: %d", len(items)) 1538 } 1539 response, ok := items[0].pbResponse.(*pb.BatchResponseItem_ObjectDownload) 1540 if !ok { 1541 return DownloadObjectResponse{}, Error.New("unexpected response type: %T", items[0].pbResponse) 1542 } 1543 1544 return newDownloadObjectResponse(response.ObjectDownload), nil 1545 } 1546 1547 // DownloadSegmentParams parameters for DownloadSegment method. 1548 type DownloadSegmentParams struct { 1549 StreamID storj.StreamID 1550 Position SegmentPosition 1551 } 1552 1553 func (params *DownloadSegmentParams) toRequest(header *pb.RequestHeader) *pb.SegmentDownloadRequest { 1554 return &pb.SegmentDownloadRequest{ 1555 Header: header, 1556 StreamId: params.StreamID, 1557 CursorPosition: &pb.SegmentPosition{ 1558 PartNumber: params.Position.PartNumber, 1559 Index: params.Position.Index, 1560 }, 1561 } 1562 } 1563 1564 // BatchItem returns single item for batch request. 1565 func (params *DownloadSegmentParams) BatchItem() *pb.BatchRequestItem { 1566 return &pb.BatchRequestItem{ 1567 Request: &pb.BatchRequestItem_SegmentDownload{ 1568 SegmentDownload: params.toRequest(nil), 1569 }, 1570 } 1571 } 1572 1573 // DownloadSegmentResponse response for DownloadSegment request. 1574 type DownloadSegmentResponse struct { 1575 Info SegmentDownloadResponseInfo 1576 1577 Limits []*pb.AddressedOrderLimit 1578 } 1579 1580 func newDownloadSegmentResponse(response *pb.SegmentDownloadResponse) DownloadSegmentResponse { 1581 info := SegmentDownloadResponseInfo{ 1582 SegmentID: response.SegmentId, 1583 EncryptedSize: response.SegmentSize, 1584 EncryptedInlineData: response.EncryptedInlineData, 1585 PiecePrivateKey: response.PrivateKey, 1586 SegmentEncryption: SegmentEncryption{ 1587 EncryptedKeyNonce: response.EncryptedKeyNonce, 1588 EncryptedKey: response.EncryptedKey, 1589 }, 1590 } 1591 if response.Next != nil { 1592 info.Next = SegmentPosition{ 1593 PartNumber: response.Next.PartNumber, 1594 Index: response.Next.Index, 1595 } 1596 } 1597 1598 for i := range response.AddressedLimits { 1599 if response.AddressedLimits[i].Limit == nil { 1600 response.AddressedLimits[i] = nil 1601 } 1602 } 1603 return DownloadSegmentResponse{ 1604 Info: info, 1605 Limits: response.AddressedLimits, 1606 } 1607 } 1608 1609 // DownloadSegmentWithRSResponse contains information for downloading remote segment or data from an inline segment. 1610 type DownloadSegmentWithRSResponse struct { 1611 Info SegmentDownloadInfo 1612 Limits []*pb.AddressedOrderLimit 1613 } 1614 1615 // SegmentDownloadInfo represents information necessary for downloading segment (inline and remote). 1616 type SegmentDownloadInfo struct { 1617 SegmentID storj.SegmentID 1618 PlainOffset int64 1619 PlainSize int64 1620 EncryptedSize int64 1621 EncryptedInlineData []byte 1622 PiecePrivateKey storj.PiecePrivateKey 1623 SegmentEncryption SegmentEncryption 1624 RedundancyScheme storj.RedundancyScheme 1625 Position *SegmentPosition 1626 } 1627 1628 func newDownloadSegmentResponseWithRS(response *pb.SegmentDownloadResponse) DownloadSegmentWithRSResponse { 1629 info := SegmentDownloadInfo{ 1630 SegmentID: response.SegmentId, 1631 PlainOffset: response.PlainOffset, 1632 PlainSize: response.PlainSize, 1633 EncryptedSize: response.SegmentSize, 1634 EncryptedInlineData: response.EncryptedInlineData, 1635 PiecePrivateKey: response.PrivateKey, 1636 SegmentEncryption: SegmentEncryption{ 1637 EncryptedKeyNonce: response.EncryptedKeyNonce, 1638 EncryptedKey: response.EncryptedKey, 1639 }, 1640 } 1641 1642 if response.Position != nil { 1643 info.Position = &SegmentPosition{ 1644 PartNumber: response.Position.PartNumber, 1645 Index: response.Position.Index, 1646 } 1647 } 1648 1649 if response.RedundancyScheme != nil { 1650 info.RedundancyScheme = storj.RedundancyScheme{ 1651 Algorithm: storj.RedundancyAlgorithm(response.RedundancyScheme.Type), 1652 ShareSize: response.RedundancyScheme.ErasureShareSize, 1653 RequiredShares: int16(response.RedundancyScheme.MinReq), 1654 RepairShares: int16(response.RedundancyScheme.RepairThreshold), 1655 OptimalShares: int16(response.RedundancyScheme.SuccessThreshold), 1656 TotalShares: int16(response.RedundancyScheme.Total), 1657 } 1658 } 1659 1660 for i := range response.AddressedLimits { 1661 if response.AddressedLimits[i].Limit == nil { 1662 response.AddressedLimits[i] = nil 1663 } 1664 } 1665 return DownloadSegmentWithRSResponse{ 1666 Info: info, 1667 Limits: response.AddressedLimits, 1668 } 1669 } 1670 1671 // TODO replace DownloadSegment with DownloadSegmentWithRS in batch 1672 1673 // DownloadSegmentWithRS gets information for downloading remote segment or data from an inline segment. 1674 func (client *Client) DownloadSegmentWithRS(ctx context.Context, params DownloadSegmentParams) (_ DownloadSegmentWithRSResponse, err error) { 1675 defer mon.Task()(&ctx)(&err) 1676 1677 var items []BatchResponse 1678 err = WithRetry(ctx, func(ctx context.Context) error { 1679 items, err = client.Batch(ctx, ¶ms) 1680 return err 1681 }) 1682 if err != nil { 1683 if errs2.IsRPC(err, rpcstatus.NotFound) { 1684 return DownloadSegmentWithRSResponse{}, ErrObjectNotFound.Wrap(err) 1685 } 1686 return DownloadSegmentWithRSResponse{}, Error.Wrap(err) 1687 } 1688 if len(items) != 1 { 1689 return DownloadSegmentWithRSResponse{}, Error.New("unexpected number of responses: %d", len(items)) 1690 } 1691 response, ok := items[0].pbResponse.(*pb.BatchResponseItem_SegmentDownload) 1692 if !ok { 1693 return DownloadSegmentWithRSResponse{}, Error.New("unexpected response type: %T", items[0].pbResponse) 1694 } 1695 1696 return newDownloadSegmentResponseWithRS(response.SegmentDownload), nil 1697 } 1698 1699 // RevokeAPIKey revokes the APIKey provided in the params. 1700 func (client *Client) RevokeAPIKey(ctx context.Context, params RevokeAPIKeyParams) (err error) { 1701 defer mon.Task()(&ctx)(&err) 1702 err = WithRetry(ctx, func(ctx context.Context) error { 1703 _, err = client.client.RevokeAPIKey(ctx, params.toRequest(client.header())) 1704 return err 1705 }) 1706 return Error.Wrap(err) 1707 } 1708 1709 // RevokeAPIKeyParams contain params for a RevokeAPIKey request. 1710 type RevokeAPIKeyParams struct { 1711 APIKey []byte 1712 } 1713 1714 func (r RevokeAPIKeyParams) toRequest(header *pb.RequestHeader) *pb.RevokeAPIKeyRequest { 1715 return &pb.RevokeAPIKeyRequest{ 1716 Header: header, 1717 ApiKey: r.APIKey, 1718 } 1719 } 1720 1721 // Batch sends multiple requests in one batch. 1722 func (client *Client) Batch(ctx context.Context, requests ...BatchItem) (resp []BatchResponse, err error) { 1723 defer mon.Task()(&ctx)(&err) 1724 1725 if os.Getenv("STORJ_COMPRESSED_BATCH") != "false" { 1726 return client.compressedBatch(ctx, requests...) 1727 } 1728 1729 batchItems := make([]*pb.BatchRequestItem, len(requests)) 1730 for i, request := range requests { 1731 batchItems[i] = request.BatchItem() 1732 } 1733 response, err := client.client.Batch(ctx, &pb.BatchRequest{ 1734 Header: client.header(), 1735 Requests: batchItems, 1736 }) 1737 if err != nil { 1738 return []BatchResponse{}, Error.Wrap(err) 1739 } 1740 1741 resp = make([]BatchResponse, len(response.Responses)) 1742 for i, response := range response.Responses { 1743 resp[i] = MakeBatchResponse(batchItems[i], response) 1744 } 1745 1746 return resp, nil 1747 } 1748 1749 // compressedBatch sends multiple requests in one batch supporting compressed responses. 1750 func (client *Client) compressedBatch(ctx context.Context, requests ...BatchItem) (_ []BatchResponse, err error) { 1751 defer mon.Task()(&ctx)(&err) 1752 1753 batchItems := make([]*pb.BatchRequestItem, len(requests)) 1754 for i, request := range requests { 1755 batchItems[i] = request.BatchItem() 1756 } 1757 data, err := pb.Marshal(&pb.BatchRequest{ 1758 Header: client.header(), 1759 Requests: batchItems, 1760 }) 1761 compResponse, err := client.client.CompressedBatch(ctx, &pb.CompressedBatchRequest{ 1762 Supported: []pb.CompressedBatchRequest_CompressionType{pb.CompressedBatchRequest_ZSTD}, 1763 Data: data, 1764 }) 1765 if err != nil { 1766 return []BatchResponse{}, Error.Wrap(err) 1767 } 1768 1769 var respData []byte 1770 switch compResponse.Selected { 1771 case pb.CompressedBatchRequest_NONE: 1772 respData = compResponse.Data 1773 case pb.CompressedBatchRequest_ZSTD: 1774 respData, err = zstdDecoder.DecodeAll(compResponse.Data, nil) 1775 default: 1776 err = Error.New("unsupported compression type: %v", compResponse.Selected) 1777 } 1778 if err != nil { 1779 return []BatchResponse{}, Error.Wrap(err) 1780 } 1781 1782 var response pb.BatchResponse 1783 if err := pb.Unmarshal(respData, &response); err != nil { 1784 return []BatchResponse{}, Error.Wrap(err) 1785 } 1786 resp := make([]BatchResponse, len(response.Responses)) 1787 for i, response := range response.Responses { 1788 resp[i] = MakeBatchResponse(batchItems[i], response) 1789 } 1790 1791 return resp, nil 1792 } 1793 1794 // SetRawAPIKey sets the client's raw API key. Mainly used for testing. 1795 func (client *Client) SetRawAPIKey(key []byte) { 1796 client.apiKeyRaw = key 1797 }