1// Copyright 2014 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package storage 16 17import ( 18 "context" 19 "fmt" 20 "net/http" 21 "reflect" 22 "time" 23 24 "cloud.google.com/go/internal/optional" 25 "cloud.google.com/go/internal/trace" 26 "google.golang.org/api/googleapi" 27 "google.golang.org/api/iterator" 28 raw "google.golang.org/api/storage/v1" 29) 30 31// BucketHandle provides operations on a Google Cloud Storage bucket. 32// Use Client.Bucket to get a handle. 33type BucketHandle struct { 34 c *Client 35 name string 36 acl ACLHandle 37 defaultObjectACL ACLHandle 38 conds *BucketConditions 39 userProject string // project for Requester Pays buckets 40} 41 42// Bucket returns a BucketHandle, which provides operations on the named bucket. 43// This call does not perform any network operations. 44// 45// The supplied name must contain only lowercase letters, numbers, dashes, 46// underscores, and dots. The full specification for valid bucket names can be 47// found at: 48// https://cloud.google.com/storage/docs/bucket-naming 49func (c *Client) Bucket(name string) *BucketHandle { 50 return &BucketHandle{ 51 c: c, 52 name: name, 53 acl: ACLHandle{ 54 c: c, 55 bucket: name, 56 }, 57 defaultObjectACL: ACLHandle{ 58 c: c, 59 bucket: name, 60 isDefault: true, 61 }, 62 } 63} 64 65// Create creates the Bucket in the project. 66// If attrs is nil the API defaults will be used. 67func (b *BucketHandle) Create(ctx context.Context, projectID string, attrs *BucketAttrs) (err error) { 68 ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Create") 69 defer func() { trace.EndSpan(ctx, err) }() 70 71 var bkt *raw.Bucket 72 if attrs != nil { 73 bkt = attrs.toRawBucket() 74 } else { 75 bkt = &raw.Bucket{} 76 } 77 bkt.Name = b.name 78 // If there is lifecycle information but no location, explicitly set 79 // the location. This is a GCS quirk/bug. 80 if bkt.Location == "" && bkt.Lifecycle != nil { 81 bkt.Location = "US" 82 } 83 req := b.c.raw.Buckets.Insert(projectID, bkt) 84 setClientHeader(req.Header()) 85 if attrs != nil && attrs.PredefinedACL != "" { 86 req.PredefinedAcl(attrs.PredefinedACL) 87 } 88 if attrs != nil && attrs.PredefinedDefaultObjectACL != "" { 89 req.PredefinedDefaultObjectAcl(attrs.PredefinedDefaultObjectACL) 90 } 91 return runWithRetry(ctx, func() error { _, err := req.Context(ctx).Do(); return err }) 92} 93 94// Delete deletes the Bucket. 95func (b *BucketHandle) Delete(ctx context.Context) (err error) { 96 ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Delete") 97 defer func() { trace.EndSpan(ctx, err) }() 98 99 req, err := b.newDeleteCall() 100 if err != nil { 101 return err 102 } 103 return runWithRetry(ctx, func() error { return req.Context(ctx).Do() }) 104} 105 106func (b *BucketHandle) newDeleteCall() (*raw.BucketsDeleteCall, error) { 107 req := b.c.raw.Buckets.Delete(b.name) 108 setClientHeader(req.Header()) 109 if err := applyBucketConds("BucketHandle.Delete", b.conds, req); err != nil { 110 return nil, err 111 } 112 if b.userProject != "" { 113 req.UserProject(b.userProject) 114 } 115 return req, nil 116} 117 118// ACL returns an ACLHandle, which provides access to the bucket's access control list. 119// This controls who can list, create or overwrite the objects in a bucket. 120// This call does not perform any network operations. 121func (b *BucketHandle) ACL() *ACLHandle { 122 return &b.acl 123} 124 125// DefaultObjectACL returns an ACLHandle, which provides access to the bucket's default object ACLs. 126// These ACLs are applied to newly created objects in this bucket that do not have a defined ACL. 127// This call does not perform any network operations. 128func (b *BucketHandle) DefaultObjectACL() *ACLHandle { 129 return &b.defaultObjectACL 130} 131 132// Object returns an ObjectHandle, which provides operations on the named object. 133// This call does not perform any network operations. 134// 135// name must consist entirely of valid UTF-8-encoded runes. The full specification 136// for valid object names can be found at: 137// https://cloud.google.com/storage/docs/naming-objects 138func (b *BucketHandle) Object(name string) *ObjectHandle { 139 return &ObjectHandle{ 140 c: b.c, 141 bucket: b.name, 142 object: name, 143 acl: ACLHandle{ 144 c: b.c, 145 bucket: b.name, 146 object: name, 147 userProject: b.userProject, 148 }, 149 gen: -1, 150 userProject: b.userProject, 151 } 152} 153 154// Attrs returns the metadata for the bucket. 155func (b *BucketHandle) Attrs(ctx context.Context) (attrs *BucketAttrs, err error) { 156 ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Attrs") 157 defer func() { trace.EndSpan(ctx, err) }() 158 159 req, err := b.newGetCall() 160 if err != nil { 161 return nil, err 162 } 163 var resp *raw.Bucket 164 err = runWithRetry(ctx, func() error { 165 resp, err = req.Context(ctx).Do() 166 return err 167 }) 168 if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound { 169 return nil, ErrBucketNotExist 170 } 171 if err != nil { 172 return nil, err 173 } 174 return newBucket(resp) 175} 176 177func (b *BucketHandle) newGetCall() (*raw.BucketsGetCall, error) { 178 req := b.c.raw.Buckets.Get(b.name).Projection("full") 179 setClientHeader(req.Header()) 180 if err := applyBucketConds("BucketHandle.Attrs", b.conds, req); err != nil { 181 return nil, err 182 } 183 if b.userProject != "" { 184 req.UserProject(b.userProject) 185 } 186 return req, nil 187} 188 189// Update updates a bucket's attributes. 190func (b *BucketHandle) Update(ctx context.Context, uattrs BucketAttrsToUpdate) (attrs *BucketAttrs, err error) { 191 ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Create") 192 defer func() { trace.EndSpan(ctx, err) }() 193 194 req, err := b.newPatchCall(&uattrs) 195 if err != nil { 196 return nil, err 197 } 198 if uattrs.PredefinedACL != "" { 199 req.PredefinedAcl(uattrs.PredefinedACL) 200 } 201 if uattrs.PredefinedDefaultObjectACL != "" { 202 req.PredefinedDefaultObjectAcl(uattrs.PredefinedDefaultObjectACL) 203 } 204 // TODO(jba): retry iff metagen is set? 205 rb, err := req.Context(ctx).Do() 206 if err != nil { 207 return nil, err 208 } 209 return newBucket(rb) 210} 211 212func (b *BucketHandle) newPatchCall(uattrs *BucketAttrsToUpdate) (*raw.BucketsPatchCall, error) { 213 rb := uattrs.toRawBucket() 214 req := b.c.raw.Buckets.Patch(b.name, rb).Projection("full") 215 setClientHeader(req.Header()) 216 if err := applyBucketConds("BucketHandle.Update", b.conds, req); err != nil { 217 return nil, err 218 } 219 if b.userProject != "" { 220 req.UserProject(b.userProject) 221 } 222 return req, nil 223} 224 225// BucketAttrs represents the metadata for a Google Cloud Storage bucket. 226// Read-only fields are ignored by BucketHandle.Create. 227type BucketAttrs struct { 228 // Name is the name of the bucket. 229 // This field is read-only. 230 Name string 231 232 // ACL is the list of access control rules on the bucket. 233 ACL []ACLRule 234 235 // BucketPolicyOnly is an alias for UniformBucketLevelAccess. Use of 236 // UniformBucketLevelAccess is recommended above the use of this field. 237 // Setting BucketPolicyOnly.Enabled OR UniformBucketLevelAccess.Enabled to 238 // true, will enable UniformBucketLevelAccess. 239 BucketPolicyOnly BucketPolicyOnly 240 241 // UniformBucketLevelAccess configures access checks to use only bucket-level IAM 242 // policies and ignore any ACL rules for the bucket. 243 // See https://cloud.google.com/storage/docs/uniform-bucket-level-access 244 // for more information. 245 UniformBucketLevelAccess UniformBucketLevelAccess 246 247 // PublicAccessPrevention is the setting for the bucket's 248 // PublicAccessPrevention policy, which can be used to prevent public access 249 // of data in the bucket. See 250 // https://cloud.google.com/storage/docs/public-access-prevention for more 251 // information. 252 PublicAccessPrevention PublicAccessPrevention 253 254 // DefaultObjectACL is the list of access controls to 255 // apply to new objects when no object ACL is provided. 256 DefaultObjectACL []ACLRule 257 258 // DefaultEventBasedHold is the default value for event-based hold on 259 // newly created objects in this bucket. It defaults to false. 260 DefaultEventBasedHold bool 261 262 // If not empty, applies a predefined set of access controls. It should be set 263 // only when creating a bucket. 264 // It is always empty for BucketAttrs returned from the service. 265 // See https://cloud.google.com/storage/docs/json_api/v1/buckets/insert 266 // for valid values. 267 PredefinedACL string 268 269 // If not empty, applies a predefined set of default object access controls. 270 // It should be set only when creating a bucket. 271 // It is always empty for BucketAttrs returned from the service. 272 // See https://cloud.google.com/storage/docs/json_api/v1/buckets/insert 273 // for valid values. 274 PredefinedDefaultObjectACL string 275 276 // Location is the location of the bucket. It defaults to "US". 277 Location string 278 279 // MetaGeneration is the metadata generation of the bucket. 280 // This field is read-only. 281 MetaGeneration int64 282 283 // StorageClass is the default storage class of the bucket. This defines 284 // how objects in the bucket are stored and determines the SLA 285 // and the cost of storage. Typical values are "STANDARD", "NEARLINE", 286 // "COLDLINE" and "ARCHIVE". Defaults to "STANDARD". 287 // See https://cloud.google.com/storage/docs/storage-classes for all 288 // valid values. 289 StorageClass string 290 291 // Created is the creation time of the bucket. 292 // This field is read-only. 293 Created time.Time 294 295 // VersioningEnabled reports whether this bucket has versioning enabled. 296 VersioningEnabled bool 297 298 // Labels are the bucket's labels. 299 Labels map[string]string 300 301 // RequesterPays reports whether the bucket is a Requester Pays bucket. 302 // Clients performing operations on Requester Pays buckets must provide 303 // a user project (see BucketHandle.UserProject), which will be billed 304 // for the operations. 305 RequesterPays bool 306 307 // Lifecycle is the lifecycle configuration for objects in the bucket. 308 Lifecycle Lifecycle 309 310 // Retention policy enforces a minimum retention time for all objects 311 // contained in the bucket. A RetentionPolicy of nil implies the bucket 312 // has no minimum data retention. 313 // 314 // This feature is in private alpha release. It is not currently available to 315 // most customers. It might be changed in backwards-incompatible ways and is not 316 // subject to any SLA or deprecation policy. 317 RetentionPolicy *RetentionPolicy 318 319 // The bucket's Cross-Origin Resource Sharing (CORS) configuration. 320 CORS []CORS 321 322 // The encryption configuration used by default for newly inserted objects. 323 Encryption *BucketEncryption 324 325 // The logging configuration. 326 Logging *BucketLogging 327 328 // The website configuration. 329 Website *BucketWebsite 330 331 // Etag is the HTTP/1.1 Entity tag for the bucket. 332 // This field is read-only. 333 Etag string 334 335 // LocationType describes how data is stored and replicated. 336 // Typical values are "multi-region", "region" and "dual-region". 337 // This field is read-only. 338 LocationType string 339} 340 341// BucketPolicyOnly is an alias for UniformBucketLevelAccess. 342// Use of UniformBucketLevelAccess is preferred above BucketPolicyOnly. 343type BucketPolicyOnly struct { 344 // Enabled specifies whether access checks use only bucket-level IAM 345 // policies. Enabled may be disabled until the locked time. 346 Enabled bool 347 // LockedTime specifies the deadline for changing Enabled from true to 348 // false. 349 LockedTime time.Time 350} 351 352// UniformBucketLevelAccess configures access checks to use only bucket-level IAM 353// policies. 354type UniformBucketLevelAccess struct { 355 // Enabled specifies whether access checks use only bucket-level IAM 356 // policies. Enabled may be disabled until the locked time. 357 Enabled bool 358 // LockedTime specifies the deadline for changing Enabled from true to 359 // false. 360 LockedTime time.Time 361} 362 363// PublicAccessPrevention configures the Public Access Prevention feature, which 364// can be used to disallow public access to any data in a bucket. See 365// https://cloud.google.com/storage/docs/public-access-prevention for more 366// information. 367type PublicAccessPrevention int 368 369const ( 370 // PublicAccessPreventionUnknown is a zero value, used only if this field is 371 // not set in a call to GCS. 372 PublicAccessPreventionUnknown PublicAccessPrevention = iota 373 374 // PublicAccessPreventionUnspecified corresponds to a value of "unspecified" 375 // and is the default for buckets. 376 PublicAccessPreventionUnspecified 377 378 // PublicAccessPreventionEnforced corresponds to a value of "enforced". This 379 // enforces Public Access Prevention on the bucket. 380 PublicAccessPreventionEnforced 381 382 publicAccessPreventionUnknown string = "" 383 publicAccessPreventionUnspecified = "unspecified" 384 publicAccessPreventionEnforced = "enforced" 385) 386 387func (p PublicAccessPrevention) String() string { 388 switch p { 389 case PublicAccessPreventionUnspecified: 390 return publicAccessPreventionUnspecified 391 case PublicAccessPreventionEnforced: 392 return publicAccessPreventionEnforced 393 default: 394 return publicAccessPreventionUnknown 395 } 396} 397 398// Lifecycle is the lifecycle configuration for objects in the bucket. 399type Lifecycle struct { 400 Rules []LifecycleRule 401} 402 403// RetentionPolicy enforces a minimum retention time for all objects 404// contained in the bucket. 405// 406// Any attempt to overwrite or delete objects younger than the retention 407// period will result in an error. An unlocked retention policy can be 408// modified or removed from the bucket via the Update method. A 409// locked retention policy cannot be removed or shortened in duration 410// for the lifetime of the bucket. 411// 412// This feature is in private alpha release. It is not currently available to 413// most customers. It might be changed in backwards-incompatible ways and is not 414// subject to any SLA or deprecation policy. 415type RetentionPolicy struct { 416 // RetentionPeriod specifies the duration that objects need to be 417 // retained. Retention duration must be greater than zero and less than 418 // 100 years. Note that enforcement of retention periods less than a day 419 // is not guaranteed. Such periods should only be used for testing 420 // purposes. 421 RetentionPeriod time.Duration 422 423 // EffectiveTime is the time from which the policy was enforced and 424 // effective. This field is read-only. 425 EffectiveTime time.Time 426 427 // IsLocked describes whether the bucket is locked. Once locked, an 428 // object retention policy cannot be modified. 429 // This field is read-only. 430 IsLocked bool 431} 432 433const ( 434 // RFC3339 timestamp with only the date segment, used for CreatedBefore, 435 // CustomTimeBefore, and NoncurrentTimeBefore in LifecycleRule. 436 rfc3339Date = "2006-01-02" 437 438 // DeleteAction is a lifecycle action that deletes a live and/or archived 439 // objects. Takes precedence over SetStorageClass actions. 440 DeleteAction = "Delete" 441 442 // SetStorageClassAction changes the storage class of live and/or archived 443 // objects. 444 SetStorageClassAction = "SetStorageClass" 445) 446 447// LifecycleRule is a lifecycle configuration rule. 448// 449// When all the configured conditions are met by an object in the bucket, the 450// configured action will automatically be taken on that object. 451type LifecycleRule struct { 452 // Action is the action to take when all of the associated conditions are 453 // met. 454 Action LifecycleAction 455 456 // Condition is the set of conditions that must be met for the associated 457 // action to be taken. 458 Condition LifecycleCondition 459} 460 461// LifecycleAction is a lifecycle configuration action. 462type LifecycleAction struct { 463 // Type is the type of action to take on matching objects. 464 // 465 // Acceptable values are "Delete" to delete matching objects and 466 // "SetStorageClass" to set the storage class defined in StorageClass on 467 // matching objects. 468 Type string 469 470 // StorageClass is the storage class to set on matching objects if the Action 471 // is "SetStorageClass". 472 StorageClass string 473} 474 475// Liveness specifies whether the object is live or not. 476type Liveness int 477 478const ( 479 // LiveAndArchived includes both live and archived objects. 480 LiveAndArchived Liveness = iota 481 // Live specifies that the object is still live. 482 Live 483 // Archived specifies that the object is archived. 484 Archived 485) 486 487// LifecycleCondition is a set of conditions used to match objects and take an 488// action automatically. 489// 490// All configured conditions must be met for the associated action to be taken. 491type LifecycleCondition struct { 492 // AgeInDays is the age of the object in days. 493 AgeInDays int64 494 495 // CreatedBefore is the time the object was created. 496 // 497 // This condition is satisfied when an object is created before midnight of 498 // the specified date in UTC. 499 CreatedBefore time.Time 500 501 // CustomTimeBefore is the CustomTime metadata field of the object. This 502 // condition is satisfied when an object's CustomTime timestamp is before 503 // midnight of the specified date in UTC. 504 // 505 // This condition can only be satisfied if CustomTime has been set. 506 CustomTimeBefore time.Time 507 508 // DaysSinceCustomTime is the days elapsed since the CustomTime date of the 509 // object. This condition can only be satisfied if CustomTime has been set. 510 DaysSinceCustomTime int64 511 512 // DaysSinceNoncurrentTime is the days elapsed since the noncurrent timestamp 513 // of the object. This condition is relevant only for versioned objects. 514 DaysSinceNoncurrentTime int64 515 516 // Liveness specifies the object's liveness. Relevant only for versioned objects 517 Liveness Liveness 518 519 // MatchesStorageClasses is the condition matching the object's storage 520 // class. 521 // 522 // Values include "STANDARD", "NEARLINE", "COLDLINE" and "ARCHIVE". 523 MatchesStorageClasses []string 524 525 // NoncurrentTimeBefore is the noncurrent timestamp of the object. This 526 // condition is satisfied when an object's noncurrent timestamp is before 527 // midnight of the specified date in UTC. 528 // 529 // This condition is relevant only for versioned objects. 530 NoncurrentTimeBefore time.Time 531 532 // NumNewerVersions is the condition matching objects with a number of newer versions. 533 // 534 // If the value is N, this condition is satisfied when there are at least N 535 // versions (including the live version) newer than this version of the 536 // object. 537 NumNewerVersions int64 538} 539 540// BucketLogging holds the bucket's logging configuration, which defines the 541// destination bucket and optional name prefix for the current bucket's 542// logs. 543type BucketLogging struct { 544 // The destination bucket where the current bucket's logs 545 // should be placed. 546 LogBucket string 547 548 // A prefix for log object names. 549 LogObjectPrefix string 550} 551 552// BucketWebsite holds the bucket's website configuration, controlling how the 553// service behaves when accessing bucket contents as a web site. See 554// https://cloud.google.com/storage/docs/static-website for more information. 555type BucketWebsite struct { 556 // If the requested object path is missing, the service will ensure the path has 557 // a trailing '/', append this suffix, and attempt to retrieve the resulting 558 // object. This allows the creation of index.html objects to represent directory 559 // pages. 560 MainPageSuffix string 561 562 // If the requested object path is missing, and any mainPageSuffix object is 563 // missing, if applicable, the service will return the named object from this 564 // bucket as the content for a 404 Not Found result. 565 NotFoundPage string 566} 567 568func newBucket(b *raw.Bucket) (*BucketAttrs, error) { 569 if b == nil { 570 return nil, nil 571 } 572 rp, err := toRetentionPolicy(b.RetentionPolicy) 573 if err != nil { 574 return nil, err 575 } 576 return &BucketAttrs{ 577 Name: b.Name, 578 Location: b.Location, 579 MetaGeneration: b.Metageneration, 580 DefaultEventBasedHold: b.DefaultEventBasedHold, 581 StorageClass: b.StorageClass, 582 Created: convertTime(b.TimeCreated), 583 VersioningEnabled: b.Versioning != nil && b.Versioning.Enabled, 584 ACL: toBucketACLRules(b.Acl), 585 DefaultObjectACL: toObjectACLRules(b.DefaultObjectAcl), 586 Labels: b.Labels, 587 RequesterPays: b.Billing != nil && b.Billing.RequesterPays, 588 Lifecycle: toLifecycle(b.Lifecycle), 589 RetentionPolicy: rp, 590 CORS: toCORS(b.Cors), 591 Encryption: toBucketEncryption(b.Encryption), 592 Logging: toBucketLogging(b.Logging), 593 Website: toBucketWebsite(b.Website), 594 BucketPolicyOnly: toBucketPolicyOnly(b.IamConfiguration), 595 UniformBucketLevelAccess: toUniformBucketLevelAccess(b.IamConfiguration), 596 PublicAccessPrevention: toPublicAccessPrevention(b.IamConfiguration), 597 Etag: b.Etag, 598 LocationType: b.LocationType, 599 }, nil 600} 601 602// toRawBucket copies the editable attribute from b to the raw library's Bucket type. 603func (b *BucketAttrs) toRawBucket() *raw.Bucket { 604 // Copy label map. 605 var labels map[string]string 606 if len(b.Labels) > 0 { 607 labels = make(map[string]string, len(b.Labels)) 608 for k, v := range b.Labels { 609 labels[k] = v 610 } 611 } 612 // Ignore VersioningEnabled if it is false. This is OK because 613 // we only call this method when creating a bucket, and by default 614 // new buckets have versioning off. 615 var v *raw.BucketVersioning 616 if b.VersioningEnabled { 617 v = &raw.BucketVersioning{Enabled: true} 618 } 619 var bb *raw.BucketBilling 620 if b.RequesterPays { 621 bb = &raw.BucketBilling{RequesterPays: true} 622 } 623 var bktIAM *raw.BucketIamConfiguration 624 if b.UniformBucketLevelAccess.Enabled || b.BucketPolicyOnly.Enabled || b.PublicAccessPrevention != PublicAccessPreventionUnknown { 625 bktIAM = &raw.BucketIamConfiguration{} 626 if b.UniformBucketLevelAccess.Enabled || b.BucketPolicyOnly.Enabled { 627 bktIAM.UniformBucketLevelAccess = &raw.BucketIamConfigurationUniformBucketLevelAccess{ 628 Enabled: true, 629 } 630 } 631 if b.PublicAccessPrevention != PublicAccessPreventionUnknown { 632 bktIAM.PublicAccessPrevention = b.PublicAccessPrevention.String() 633 } 634 } 635 return &raw.Bucket{ 636 Name: b.Name, 637 Location: b.Location, 638 StorageClass: b.StorageClass, 639 Acl: toRawBucketACL(b.ACL), 640 DefaultObjectAcl: toRawObjectACL(b.DefaultObjectACL), 641 Versioning: v, 642 Labels: labels, 643 Billing: bb, 644 Lifecycle: toRawLifecycle(b.Lifecycle), 645 RetentionPolicy: b.RetentionPolicy.toRawRetentionPolicy(), 646 Cors: toRawCORS(b.CORS), 647 Encryption: b.Encryption.toRawBucketEncryption(), 648 Logging: b.Logging.toRawBucketLogging(), 649 Website: b.Website.toRawBucketWebsite(), 650 IamConfiguration: bktIAM, 651 } 652} 653 654// CORS is the bucket's Cross-Origin Resource Sharing (CORS) configuration. 655type CORS struct { 656 // MaxAge is the value to return in the Access-Control-Max-Age 657 // header used in preflight responses. 658 MaxAge time.Duration 659 660 // Methods is the list of HTTP methods on which to include CORS response 661 // headers, (GET, OPTIONS, POST, etc) Note: "*" is permitted in the list 662 // of methods, and means "any method". 663 Methods []string 664 665 // Origins is the list of Origins eligible to receive CORS response 666 // headers. Note: "*" is permitted in the list of origins, and means 667 // "any Origin". 668 Origins []string 669 670 // ResponseHeaders is the list of HTTP headers other than the simple 671 // response headers to give permission for the user-agent to share 672 // across domains. 673 ResponseHeaders []string 674} 675 676// BucketEncryption is a bucket's encryption configuration. 677type BucketEncryption struct { 678 // A Cloud KMS key name, in the form 679 // projects/P/locations/L/keyRings/R/cryptoKeys/K, that will be used to encrypt 680 // objects inserted into this bucket, if no encryption method is specified. 681 // The key's location must be the same as the bucket's. 682 DefaultKMSKeyName string 683} 684 685// BucketAttrsToUpdate define the attributes to update during an Update call. 686type BucketAttrsToUpdate struct { 687 // If set, updates whether the bucket uses versioning. 688 VersioningEnabled optional.Bool 689 690 // If set, updates whether the bucket is a Requester Pays bucket. 691 RequesterPays optional.Bool 692 693 // DefaultEventBasedHold is the default value for event-based hold on 694 // newly created objects in this bucket. 695 DefaultEventBasedHold optional.Bool 696 697 // BucketPolicyOnly is an alias for UniformBucketLevelAccess. Use of 698 // UniformBucketLevelAccess is recommended above the use of this field. 699 // Setting BucketPolicyOnly.Enabled OR UniformBucketLevelAccess.Enabled to 700 // true, will enable UniformBucketLevelAccess. If both BucketPolicyOnly and 701 // UniformBucketLevelAccess are set, the value of UniformBucketLevelAccess 702 // will take precedence. 703 BucketPolicyOnly *BucketPolicyOnly 704 705 // UniformBucketLevelAccess configures access checks to use only bucket-level IAM 706 // policies and ignore any ACL rules for the bucket. 707 // See https://cloud.google.com/storage/docs/uniform-bucket-level-access 708 // for more information. 709 UniformBucketLevelAccess *UniformBucketLevelAccess 710 711 // PublicAccessPrevention is the setting for the bucket's 712 // PublicAccessPrevention policy, which can be used to prevent public access 713 // of data in the bucket. See 714 // https://cloud.google.com/storage/docs/public-access-prevention for more 715 // information. 716 PublicAccessPrevention PublicAccessPrevention 717 718 // StorageClass is the default storage class of the bucket. This defines 719 // how objects in the bucket are stored and determines the SLA 720 // and the cost of storage. Typical values are "STANDARD", "NEARLINE", 721 // "COLDLINE" and "ARCHIVE". Defaults to "STANDARD". 722 // See https://cloud.google.com/storage/docs/storage-classes for all 723 // valid values. 724 StorageClass string 725 726 // If set, updates the retention policy of the bucket. Using 727 // RetentionPolicy.RetentionPeriod = 0 will delete the existing policy. 728 // 729 // This feature is in private alpha release. It is not currently available to 730 // most customers. It might be changed in backwards-incompatible ways and is not 731 // subject to any SLA or deprecation policy. 732 RetentionPolicy *RetentionPolicy 733 734 // If set, replaces the CORS configuration with a new configuration. 735 // An empty (rather than nil) slice causes all CORS policies to be removed. 736 CORS []CORS 737 738 // If set, replaces the encryption configuration of the bucket. Using 739 // BucketEncryption.DefaultKMSKeyName = "" will delete the existing 740 // configuration. 741 Encryption *BucketEncryption 742 743 // If set, replaces the lifecycle configuration of the bucket. 744 Lifecycle *Lifecycle 745 746 // If set, replaces the logging configuration of the bucket. 747 Logging *BucketLogging 748 749 // If set, replaces the website configuration of the bucket. 750 Website *BucketWebsite 751 752 // If not empty, applies a predefined set of access controls. 753 // See https://cloud.google.com/storage/docs/json_api/v1/buckets/patch. 754 PredefinedACL string 755 756 // If not empty, applies a predefined set of default object access controls. 757 // See https://cloud.google.com/storage/docs/json_api/v1/buckets/patch. 758 PredefinedDefaultObjectACL string 759 760 setLabels map[string]string 761 deleteLabels map[string]bool 762} 763 764// SetLabel causes a label to be added or modified when ua is used 765// in a call to Bucket.Update. 766func (ua *BucketAttrsToUpdate) SetLabel(name, value string) { 767 if ua.setLabels == nil { 768 ua.setLabels = map[string]string{} 769 } 770 ua.setLabels[name] = value 771} 772 773// DeleteLabel causes a label to be deleted when ua is used in a 774// call to Bucket.Update. 775func (ua *BucketAttrsToUpdate) DeleteLabel(name string) { 776 if ua.deleteLabels == nil { 777 ua.deleteLabels = map[string]bool{} 778 } 779 ua.deleteLabels[name] = true 780} 781 782func (ua *BucketAttrsToUpdate) toRawBucket() *raw.Bucket { 783 rb := &raw.Bucket{} 784 if ua.CORS != nil { 785 rb.Cors = toRawCORS(ua.CORS) 786 rb.ForceSendFields = append(rb.ForceSendFields, "Cors") 787 } 788 if ua.DefaultEventBasedHold != nil { 789 rb.DefaultEventBasedHold = optional.ToBool(ua.DefaultEventBasedHold) 790 rb.ForceSendFields = append(rb.ForceSendFields, "DefaultEventBasedHold") 791 } 792 if ua.RetentionPolicy != nil { 793 if ua.RetentionPolicy.RetentionPeriod == 0 { 794 rb.NullFields = append(rb.NullFields, "RetentionPolicy") 795 rb.RetentionPolicy = nil 796 } else { 797 rb.RetentionPolicy = ua.RetentionPolicy.toRawRetentionPolicy() 798 } 799 } 800 if ua.VersioningEnabled != nil { 801 rb.Versioning = &raw.BucketVersioning{ 802 Enabled: optional.ToBool(ua.VersioningEnabled), 803 ForceSendFields: []string{"Enabled"}, 804 } 805 } 806 if ua.RequesterPays != nil { 807 rb.Billing = &raw.BucketBilling{ 808 RequesterPays: optional.ToBool(ua.RequesterPays), 809 ForceSendFields: []string{"RequesterPays"}, 810 } 811 } 812 if ua.BucketPolicyOnly != nil { 813 rb.IamConfiguration = &raw.BucketIamConfiguration{ 814 UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{ 815 Enabled: ua.BucketPolicyOnly.Enabled, 816 ForceSendFields: []string{"Enabled"}, 817 }, 818 } 819 } 820 if ua.UniformBucketLevelAccess != nil { 821 rb.IamConfiguration = &raw.BucketIamConfiguration{ 822 UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{ 823 Enabled: ua.UniformBucketLevelAccess.Enabled, 824 ForceSendFields: []string{"Enabled"}, 825 }, 826 } 827 } 828 if ua.PublicAccessPrevention != PublicAccessPreventionUnknown { 829 if rb.IamConfiguration == nil { 830 rb.IamConfiguration = &raw.BucketIamConfiguration{} 831 } 832 rb.IamConfiguration.PublicAccessPrevention = ua.PublicAccessPrevention.String() 833 } 834 if ua.Encryption != nil { 835 if ua.Encryption.DefaultKMSKeyName == "" { 836 rb.NullFields = append(rb.NullFields, "Encryption") 837 rb.Encryption = nil 838 } else { 839 rb.Encryption = ua.Encryption.toRawBucketEncryption() 840 } 841 } 842 if ua.Lifecycle != nil { 843 rb.Lifecycle = toRawLifecycle(*ua.Lifecycle) 844 rb.ForceSendFields = append(rb.ForceSendFields, "Lifecycle") 845 } 846 if ua.Logging != nil { 847 if *ua.Logging == (BucketLogging{}) { 848 rb.NullFields = append(rb.NullFields, "Logging") 849 rb.Logging = nil 850 } else { 851 rb.Logging = ua.Logging.toRawBucketLogging() 852 } 853 } 854 if ua.Website != nil { 855 if *ua.Website == (BucketWebsite{}) { 856 rb.NullFields = append(rb.NullFields, "Website") 857 rb.Website = nil 858 } else { 859 rb.Website = ua.Website.toRawBucketWebsite() 860 } 861 } 862 if ua.PredefinedACL != "" { 863 // Clear ACL or the call will fail. 864 rb.Acl = nil 865 rb.ForceSendFields = append(rb.ForceSendFields, "Acl") 866 } 867 if ua.PredefinedDefaultObjectACL != "" { 868 // Clear ACLs or the call will fail. 869 rb.DefaultObjectAcl = nil 870 rb.ForceSendFields = append(rb.ForceSendFields, "DefaultObjectAcl") 871 } 872 rb.StorageClass = ua.StorageClass 873 if ua.setLabels != nil || ua.deleteLabels != nil { 874 rb.Labels = map[string]string{} 875 for k, v := range ua.setLabels { 876 rb.Labels[k] = v 877 } 878 if len(rb.Labels) == 0 && len(ua.deleteLabels) > 0 { 879 rb.ForceSendFields = append(rb.ForceSendFields, "Labels") 880 } 881 for l := range ua.deleteLabels { 882 rb.NullFields = append(rb.NullFields, "Labels."+l) 883 } 884 } 885 return rb 886} 887 888// If returns a new BucketHandle that applies a set of preconditions. 889// Preconditions already set on the BucketHandle are ignored. 890// Operations on the new handle will return an error if the preconditions are not 891// satisfied. The only valid preconditions for buckets are MetagenerationMatch 892// and MetagenerationNotMatch. 893func (b *BucketHandle) If(conds BucketConditions) *BucketHandle { 894 b2 := *b 895 b2.conds = &conds 896 return &b2 897} 898 899// BucketConditions constrain bucket methods to act on specific metagenerations. 900// 901// The zero value is an empty set of constraints. 902type BucketConditions struct { 903 // MetagenerationMatch specifies that the bucket must have the given 904 // metageneration for the operation to occur. 905 // If MetagenerationMatch is zero, it has no effect. 906 MetagenerationMatch int64 907 908 // MetagenerationNotMatch specifies that the bucket must not have the given 909 // metageneration for the operation to occur. 910 // If MetagenerationNotMatch is zero, it has no effect. 911 MetagenerationNotMatch int64 912} 913 914func (c *BucketConditions) validate(method string) error { 915 if *c == (BucketConditions{}) { 916 return fmt.Errorf("storage: %s: empty conditions", method) 917 } 918 if c.MetagenerationMatch != 0 && c.MetagenerationNotMatch != 0 { 919 return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method) 920 } 921 return nil 922} 923 924// UserProject returns a new BucketHandle that passes the project ID as the user 925// project for all subsequent calls. Calls with a user project will be billed to that 926// project rather than to the bucket's owning project. 927// 928// A user project is required for all operations on Requester Pays buckets. 929func (b *BucketHandle) UserProject(projectID string) *BucketHandle { 930 b2 := *b 931 b2.userProject = projectID 932 b2.acl.userProject = projectID 933 b2.defaultObjectACL.userProject = projectID 934 return &b2 935} 936 937// LockRetentionPolicy locks a bucket's retention policy until a previously-configured 938// RetentionPeriod past the EffectiveTime. Note that if RetentionPeriod is set to less 939// than a day, the retention policy is treated as a development configuration and locking 940// will have no effect. The BucketHandle must have a metageneration condition that 941// matches the bucket's metageneration. See BucketHandle.If. 942// 943// This feature is in private alpha release. It is not currently available to 944// most customers. It might be changed in backwards-incompatible ways and is not 945// subject to any SLA or deprecation policy. 946func (b *BucketHandle) LockRetentionPolicy(ctx context.Context) error { 947 var metageneration int64 948 if b.conds != nil { 949 metageneration = b.conds.MetagenerationMatch 950 } 951 req := b.c.raw.Buckets.LockRetentionPolicy(b.name, metageneration) 952 return runWithRetry(ctx, func() error { 953 _, err := req.Context(ctx).Do() 954 return err 955 }) 956} 957 958// applyBucketConds modifies the provided call using the conditions in conds. 959// call is something that quacks like a *raw.WhateverCall. 960func applyBucketConds(method string, conds *BucketConditions, call interface{}) error { 961 if conds == nil { 962 return nil 963 } 964 if err := conds.validate(method); err != nil { 965 return err 966 } 967 cval := reflect.ValueOf(call) 968 switch { 969 case conds.MetagenerationMatch != 0: 970 if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) { 971 return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method) 972 } 973 case conds.MetagenerationNotMatch != 0: 974 if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) { 975 return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method) 976 } 977 } 978 return nil 979} 980 981func (rp *RetentionPolicy) toRawRetentionPolicy() *raw.BucketRetentionPolicy { 982 if rp == nil { 983 return nil 984 } 985 return &raw.BucketRetentionPolicy{ 986 RetentionPeriod: int64(rp.RetentionPeriod / time.Second), 987 } 988} 989 990func toRetentionPolicy(rp *raw.BucketRetentionPolicy) (*RetentionPolicy, error) { 991 if rp == nil { 992 return nil, nil 993 } 994 t, err := time.Parse(time.RFC3339, rp.EffectiveTime) 995 if err != nil { 996 return nil, err 997 } 998 return &RetentionPolicy{ 999 RetentionPeriod: time.Duration(rp.RetentionPeriod) * time.Second, 1000 EffectiveTime: t, 1001 IsLocked: rp.IsLocked, 1002 }, nil 1003} 1004 1005func toRawCORS(c []CORS) []*raw.BucketCors { 1006 var out []*raw.BucketCors 1007 for _, v := range c { 1008 out = append(out, &raw.BucketCors{ 1009 MaxAgeSeconds: int64(v.MaxAge / time.Second), 1010 Method: v.Methods, 1011 Origin: v.Origins, 1012 ResponseHeader: v.ResponseHeaders, 1013 }) 1014 } 1015 return out 1016} 1017 1018func toCORS(rc []*raw.BucketCors) []CORS { 1019 var out []CORS 1020 for _, v := range rc { 1021 out = append(out, CORS{ 1022 MaxAge: time.Duration(v.MaxAgeSeconds) * time.Second, 1023 Methods: v.Method, 1024 Origins: v.Origin, 1025 ResponseHeaders: v.ResponseHeader, 1026 }) 1027 } 1028 return out 1029} 1030 1031func toRawLifecycle(l Lifecycle) *raw.BucketLifecycle { 1032 var rl raw.BucketLifecycle 1033 if len(l.Rules) == 0 { 1034 rl.ForceSendFields = []string{"Rule"} 1035 } 1036 for _, r := range l.Rules { 1037 rr := &raw.BucketLifecycleRule{ 1038 Action: &raw.BucketLifecycleRuleAction{ 1039 Type: r.Action.Type, 1040 StorageClass: r.Action.StorageClass, 1041 }, 1042 Condition: &raw.BucketLifecycleRuleCondition{ 1043 Age: r.Condition.AgeInDays, 1044 DaysSinceCustomTime: r.Condition.DaysSinceCustomTime, 1045 DaysSinceNoncurrentTime: r.Condition.DaysSinceNoncurrentTime, 1046 MatchesStorageClass: r.Condition.MatchesStorageClasses, 1047 NumNewerVersions: r.Condition.NumNewerVersions, 1048 }, 1049 } 1050 1051 switch r.Condition.Liveness { 1052 case LiveAndArchived: 1053 rr.Condition.IsLive = nil 1054 case Live: 1055 rr.Condition.IsLive = googleapi.Bool(true) 1056 case Archived: 1057 rr.Condition.IsLive = googleapi.Bool(false) 1058 } 1059 1060 if !r.Condition.CreatedBefore.IsZero() { 1061 rr.Condition.CreatedBefore = r.Condition.CreatedBefore.Format(rfc3339Date) 1062 } 1063 if !r.Condition.CustomTimeBefore.IsZero() { 1064 rr.Condition.CustomTimeBefore = r.Condition.CustomTimeBefore.Format(rfc3339Date) 1065 } 1066 if !r.Condition.NoncurrentTimeBefore.IsZero() { 1067 rr.Condition.NoncurrentTimeBefore = r.Condition.NoncurrentTimeBefore.Format(rfc3339Date) 1068 } 1069 rl.Rule = append(rl.Rule, rr) 1070 } 1071 return &rl 1072} 1073 1074func toLifecycle(rl *raw.BucketLifecycle) Lifecycle { 1075 var l Lifecycle 1076 if rl == nil { 1077 return l 1078 } 1079 for _, rr := range rl.Rule { 1080 r := LifecycleRule{ 1081 Action: LifecycleAction{ 1082 Type: rr.Action.Type, 1083 StorageClass: rr.Action.StorageClass, 1084 }, 1085 Condition: LifecycleCondition{ 1086 AgeInDays: rr.Condition.Age, 1087 DaysSinceCustomTime: rr.Condition.DaysSinceCustomTime, 1088 DaysSinceNoncurrentTime: rr.Condition.DaysSinceNoncurrentTime, 1089 MatchesStorageClasses: rr.Condition.MatchesStorageClass, 1090 NumNewerVersions: rr.Condition.NumNewerVersions, 1091 }, 1092 } 1093 1094 if rr.Condition.IsLive == nil { 1095 r.Condition.Liveness = LiveAndArchived 1096 } else if *rr.Condition.IsLive { 1097 r.Condition.Liveness = Live 1098 } else { 1099 r.Condition.Liveness = Archived 1100 } 1101 1102 if rr.Condition.CreatedBefore != "" { 1103 r.Condition.CreatedBefore, _ = time.Parse(rfc3339Date, rr.Condition.CreatedBefore) 1104 } 1105 if rr.Condition.CustomTimeBefore != "" { 1106 r.Condition.CustomTimeBefore, _ = time.Parse(rfc3339Date, rr.Condition.CustomTimeBefore) 1107 } 1108 if rr.Condition.NoncurrentTimeBefore != "" { 1109 r.Condition.NoncurrentTimeBefore, _ = time.Parse(rfc3339Date, rr.Condition.NoncurrentTimeBefore) 1110 } 1111 l.Rules = append(l.Rules, r) 1112 } 1113 return l 1114} 1115 1116func (e *BucketEncryption) toRawBucketEncryption() *raw.BucketEncryption { 1117 if e == nil { 1118 return nil 1119 } 1120 return &raw.BucketEncryption{ 1121 DefaultKmsKeyName: e.DefaultKMSKeyName, 1122 } 1123} 1124 1125func toBucketEncryption(e *raw.BucketEncryption) *BucketEncryption { 1126 if e == nil { 1127 return nil 1128 } 1129 return &BucketEncryption{DefaultKMSKeyName: e.DefaultKmsKeyName} 1130} 1131 1132func (b *BucketLogging) toRawBucketLogging() *raw.BucketLogging { 1133 if b == nil { 1134 return nil 1135 } 1136 return &raw.BucketLogging{ 1137 LogBucket: b.LogBucket, 1138 LogObjectPrefix: b.LogObjectPrefix, 1139 } 1140} 1141 1142func toBucketLogging(b *raw.BucketLogging) *BucketLogging { 1143 if b == nil { 1144 return nil 1145 } 1146 return &BucketLogging{ 1147 LogBucket: b.LogBucket, 1148 LogObjectPrefix: b.LogObjectPrefix, 1149 } 1150} 1151 1152func (w *BucketWebsite) toRawBucketWebsite() *raw.BucketWebsite { 1153 if w == nil { 1154 return nil 1155 } 1156 return &raw.BucketWebsite{ 1157 MainPageSuffix: w.MainPageSuffix, 1158 NotFoundPage: w.NotFoundPage, 1159 } 1160} 1161 1162func toBucketWebsite(w *raw.BucketWebsite) *BucketWebsite { 1163 if w == nil { 1164 return nil 1165 } 1166 return &BucketWebsite{ 1167 MainPageSuffix: w.MainPageSuffix, 1168 NotFoundPage: w.NotFoundPage, 1169 } 1170} 1171 1172func toBucketPolicyOnly(b *raw.BucketIamConfiguration) BucketPolicyOnly { 1173 if b == nil || b.BucketPolicyOnly == nil || !b.BucketPolicyOnly.Enabled { 1174 return BucketPolicyOnly{} 1175 } 1176 lt, err := time.Parse(time.RFC3339, b.BucketPolicyOnly.LockedTime) 1177 if err != nil { 1178 return BucketPolicyOnly{ 1179 Enabled: true, 1180 } 1181 } 1182 return BucketPolicyOnly{ 1183 Enabled: true, 1184 LockedTime: lt, 1185 } 1186} 1187 1188func toUniformBucketLevelAccess(b *raw.BucketIamConfiguration) UniformBucketLevelAccess { 1189 if b == nil || b.UniformBucketLevelAccess == nil || !b.UniformBucketLevelAccess.Enabled { 1190 return UniformBucketLevelAccess{} 1191 } 1192 lt, err := time.Parse(time.RFC3339, b.UniformBucketLevelAccess.LockedTime) 1193 if err != nil { 1194 return UniformBucketLevelAccess{ 1195 Enabled: true, 1196 } 1197 } 1198 return UniformBucketLevelAccess{ 1199 Enabled: true, 1200 LockedTime: lt, 1201 } 1202} 1203 1204func toPublicAccessPrevention(b *raw.BucketIamConfiguration) PublicAccessPrevention { 1205 if b == nil { 1206 return PublicAccessPreventionUnknown 1207 } 1208 switch b.PublicAccessPrevention { 1209 case publicAccessPreventionUnspecified: 1210 return PublicAccessPreventionUnspecified 1211 case publicAccessPreventionEnforced: 1212 return PublicAccessPreventionEnforced 1213 default: 1214 return PublicAccessPreventionUnknown 1215 } 1216} 1217 1218// Objects returns an iterator over the objects in the bucket that match the 1219// Query q. If q is nil, no filtering is done. Objects will be iterated over 1220// lexicographically by name. 1221// 1222// Note: The returned iterator is not safe for concurrent operations without explicit synchronization. 1223func (b *BucketHandle) Objects(ctx context.Context, q *Query) *ObjectIterator { 1224 it := &ObjectIterator{ 1225 ctx: ctx, 1226 bucket: b, 1227 } 1228 it.pageInfo, it.nextFunc = iterator.NewPageInfo( 1229 it.fetch, 1230 func() int { return len(it.items) }, 1231 func() interface{} { b := it.items; it.items = nil; return b }) 1232 if q != nil { 1233 it.query = *q 1234 } 1235 return it 1236} 1237 1238// An ObjectIterator is an iterator over ObjectAttrs. 1239// 1240// Note: This iterator is not safe for concurrent operations without explicit synchronization. 1241type ObjectIterator struct { 1242 ctx context.Context 1243 bucket *BucketHandle 1244 query Query 1245 pageInfo *iterator.PageInfo 1246 nextFunc func() error 1247 items []*ObjectAttrs 1248} 1249 1250// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. 1251// 1252// Note: This method is not safe for concurrent operations without explicit synchronization. 1253func (it *ObjectIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } 1254 1255// Next returns the next result. Its second return value is iterator.Done if 1256// there are no more results. Once Next returns iterator.Done, all subsequent 1257// calls will return iterator.Done. 1258// 1259// In addition, if Next returns an error other than iterator.Done, all 1260// subsequent calls will return the same error. To continue iteration, a new 1261// `ObjectIterator` must be created. Since objects are ordered lexicographically 1262// by name, `Query.StartOffset` can be used to create a new iterator which will 1263// start at the desired place. See 1264// https://pkg.go.dev/cloud.google.com/go/storage?tab=doc#hdr-Listing_objects. 1265// 1266// If Query.Delimiter is non-empty, some of the ObjectAttrs returned by Next will 1267// have a non-empty Prefix field, and a zero value for all other fields. These 1268// represent prefixes. 1269// 1270// Note: This method is not safe for concurrent operations without explicit synchronization. 1271func (it *ObjectIterator) Next() (*ObjectAttrs, error) { 1272 if err := it.nextFunc(); err != nil { 1273 return nil, err 1274 } 1275 item := it.items[0] 1276 it.items = it.items[1:] 1277 return item, nil 1278} 1279 1280func (it *ObjectIterator) fetch(pageSize int, pageToken string) (string, error) { 1281 req := it.bucket.c.raw.Objects.List(it.bucket.name) 1282 setClientHeader(req.Header()) 1283 projection := it.query.Projection 1284 if projection == ProjectionDefault { 1285 projection = ProjectionFull 1286 } 1287 req.Projection(projection.String()) 1288 req.Delimiter(it.query.Delimiter) 1289 req.Prefix(it.query.Prefix) 1290 req.StartOffset(it.query.StartOffset) 1291 req.EndOffset(it.query.EndOffset) 1292 req.Versions(it.query.Versions) 1293 if len(it.query.fieldSelection) > 0 { 1294 req.Fields("nextPageToken", googleapi.Field(it.query.fieldSelection)) 1295 } 1296 req.PageToken(pageToken) 1297 if it.bucket.userProject != "" { 1298 req.UserProject(it.bucket.userProject) 1299 } 1300 if pageSize > 0 { 1301 req.MaxResults(int64(pageSize)) 1302 } 1303 var resp *raw.Objects 1304 var err error 1305 err = runWithRetry(it.ctx, func() error { 1306 resp, err = req.Context(it.ctx).Do() 1307 return err 1308 }) 1309 if err != nil { 1310 if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound { 1311 err = ErrBucketNotExist 1312 } 1313 return "", err 1314 } 1315 for _, item := range resp.Items { 1316 it.items = append(it.items, newObject(item)) 1317 } 1318 for _, prefix := range resp.Prefixes { 1319 it.items = append(it.items, &ObjectAttrs{Prefix: prefix}) 1320 } 1321 return resp.NextPageToken, nil 1322} 1323 1324// Buckets returns an iterator over the buckets in the project. You may 1325// optionally set the iterator's Prefix field to restrict the list to buckets 1326// whose names begin with the prefix. By default, all buckets in the project 1327// are returned. 1328// 1329// Note: The returned iterator is not safe for concurrent operations without explicit synchronization. 1330func (c *Client) Buckets(ctx context.Context, projectID string) *BucketIterator { 1331 it := &BucketIterator{ 1332 ctx: ctx, 1333 client: c, 1334 projectID: projectID, 1335 } 1336 it.pageInfo, it.nextFunc = iterator.NewPageInfo( 1337 it.fetch, 1338 func() int { return len(it.buckets) }, 1339 func() interface{} { b := it.buckets; it.buckets = nil; return b }) 1340 1341 return it 1342} 1343 1344// A BucketIterator is an iterator over BucketAttrs. 1345// 1346// Note: This iterator is not safe for concurrent operations without explicit synchronization. 1347type BucketIterator struct { 1348 // Prefix restricts the iterator to buckets whose names begin with it. 1349 Prefix string 1350 1351 ctx context.Context 1352 client *Client 1353 projectID string 1354 buckets []*BucketAttrs 1355 pageInfo *iterator.PageInfo 1356 nextFunc func() error 1357} 1358 1359// Next returns the next result. Its second return value is iterator.Done if 1360// there are no more results. Once Next returns iterator.Done, all subsequent 1361// calls will return iterator.Done. 1362// 1363// Note: This method is not safe for concurrent operations without explicit synchronization. 1364func (it *BucketIterator) Next() (*BucketAttrs, error) { 1365 if err := it.nextFunc(); err != nil { 1366 return nil, err 1367 } 1368 b := it.buckets[0] 1369 it.buckets = it.buckets[1:] 1370 return b, nil 1371} 1372 1373// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. 1374// 1375// Note: This method is not safe for concurrent operations without explicit synchronization. 1376func (it *BucketIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } 1377 1378func (it *BucketIterator) fetch(pageSize int, pageToken string) (token string, err error) { 1379 req := it.client.raw.Buckets.List(it.projectID) 1380 setClientHeader(req.Header()) 1381 req.Projection("full") 1382 req.Prefix(it.Prefix) 1383 req.PageToken(pageToken) 1384 if pageSize > 0 { 1385 req.MaxResults(int64(pageSize)) 1386 } 1387 var resp *raw.Buckets 1388 err = runWithRetry(it.ctx, func() error { 1389 resp, err = req.Context(it.ctx).Do() 1390 return err 1391 }) 1392 if err != nil { 1393 return "", err 1394 } 1395 for _, item := range resp.Items { 1396 b, err := newBucket(item) 1397 if err != nil { 1398 return "", err 1399 } 1400 it.buckets = append(it.buckets, b) 1401 } 1402 return resp.NextPageToken, nil 1403} 1404