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