1 // Copyright 2014 Google LLC
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
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"
31 // BucketHandle provides operations on a Google Cloud Storage bucket.
32 // Use Client.Bucket to get a handle.
33 type BucketHandle struct {
37 defaultObjectACL ACLHandle
38 conds *BucketConditions
39 userProject string // project for Requester Pays buckets
42 // Bucket returns a BucketHandle, which provides operations on the named bucket.
43 // This call does not perform any network operations.
45 // The supplied name must contain only lowercase letters, numbers, dashes,
46 // underscores, and dots. The full specification for valid bucket names can be
48 // https://cloud.google.com/storage/docs/bucket-naming
49 func (c *Client) Bucket(name string) *BucketHandle {
57 defaultObjectACL: ACLHandle{
65 // Create creates the Bucket in the project.
66 // If attrs is nil the API defaults will be used.
67 func (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) }()
73 bkt = attrs.toRawBucket()
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 {
83 req := b.c.raw.Buckets.Insert(projectID, bkt)
84 setClientHeader(req.Header())
85 if attrs != nil && attrs.PredefinedACL != "" {
86 req.PredefinedAcl(attrs.PredefinedACL)
88 if attrs != nil && attrs.PredefinedDefaultObjectACL != "" {
89 req.PredefinedDefaultObjectAcl(attrs.PredefinedDefaultObjectACL)
91 return runWithRetry(ctx, func() error { _, err := req.Context(ctx).Do(); return err })
94 // Delete deletes the Bucket.
95 func (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) }()
99 req, err := b.newDeleteCall()
103 return runWithRetry(ctx, func() error { return req.Context(ctx).Do() })
106 func (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 {
112 if b.userProject != "" {
113 req.UserProject(b.userProject)
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.
121 func (b *BucketHandle) ACL() *ACLHandle {
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.
128 func (b *BucketHandle) DefaultObjectACL() *ACLHandle {
129 return &b.defaultObjectACL
132 // Object returns an ObjectHandle, which provides operations on the named object.
133 // This call does not perform any network operations.
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/bucket-naming
138 func (b *BucketHandle) Object(name string) *ObjectHandle {
139 return &ObjectHandle{
147 userProject: b.userProject,
150 userProject: b.userProject,
154 // Attrs returns the metadata for the bucket.
155 func (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) }()
159 req, err := b.newGetCall()
164 err = runWithRetry(ctx, func() error {
165 resp, err = req.Context(ctx).Do()
168 if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
169 return nil, ErrBucketNotExist
174 return newBucket(resp)
177 func (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 {
183 if b.userProject != "" {
184 req.UserProject(b.userProject)
189 // Update updates a bucket's attributes.
190 func (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) }()
194 req, err := b.newPatchCall(&uattrs)
198 if uattrs.PredefinedACL != "" {
199 req.PredefinedAcl(uattrs.PredefinedACL)
201 if uattrs.PredefinedDefaultObjectACL != "" {
202 req.PredefinedDefaultObjectAcl(uattrs.PredefinedDefaultObjectACL)
204 // TODO(jba): retry iff metagen is set?
205 rb, err := req.Context(ctx).Do()
212 func (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 {
219 if b.userProject != "" {
220 req.UserProject(b.userProject)
225 // BucketAttrs represents the metadata for a Google Cloud Storage bucket.
226 // Read-only fields are ignored by BucketHandle.Create.
227 type BucketAttrs struct {
228 // Name is the name of the bucket.
229 // This field is read-only.
232 // ACL is the list of access control rules on the bucket.
235 // BucketPolicyOnly configures access checks to use only bucket-level IAM
237 BucketPolicyOnly BucketPolicyOnly
239 // DefaultObjectACL is the list of access controls to
240 // apply to new objects when no object ACL is provided.
241 DefaultObjectACL []ACLRule
243 // DefaultEventBasedHold is the default value for event-based hold on
244 // newly created objects in this bucket. It defaults to false.
245 DefaultEventBasedHold bool
247 // If not empty, applies a predefined set of access controls. It should be set
248 // only when creating a bucket.
249 // It is always empty for BucketAttrs returned from the service.
250 // See https://cloud.google.com/storage/docs/json_api/v1/buckets/insert
254 // If not empty, applies a predefined set of default object access controls.
255 // It should be set only when creating a bucket.
256 // It is always empty for BucketAttrs returned from the service.
257 // See https://cloud.google.com/storage/docs/json_api/v1/buckets/insert
259 PredefinedDefaultObjectACL string
261 // Location is the location of the bucket. It defaults to "US".
264 // MetaGeneration is the metadata generation of the bucket.
265 // This field is read-only.
268 // StorageClass is the default storage class of the bucket. This defines
269 // how objects in the bucket are stored and determines the SLA
270 // and the cost of storage. Typical values are "MULTI_REGIONAL",
271 // "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD" and
272 // "DURABLE_REDUCED_AVAILABILITY". Defaults to "STANDARD", which
273 // is equivalent to "MULTI_REGIONAL" or "REGIONAL" depending on
274 // the bucket's location settings.
277 // Created is the creation time of the bucket.
278 // This field is read-only.
281 // VersioningEnabled reports whether this bucket has versioning enabled.
282 VersioningEnabled bool
284 // Labels are the bucket's labels.
285 Labels map[string]string
287 // RequesterPays reports whether the bucket is a Requester Pays bucket.
288 // Clients performing operations on Requester Pays buckets must provide
289 // a user project (see BucketHandle.UserProject), which will be billed
290 // for the operations.
293 // Lifecycle is the lifecycle configuration for objects in the bucket.
296 // Retention policy enforces a minimum retention time for all objects
297 // contained in the bucket. A RetentionPolicy of nil implies the bucket
298 // has no minimum data retention.
300 // This feature is in private alpha release. It is not currently available to
301 // most customers. It might be changed in backwards-incompatible ways and is not
302 // subject to any SLA or deprecation policy.
303 RetentionPolicy *RetentionPolicy
305 // The bucket's Cross-Origin Resource Sharing (CORS) configuration.
308 // The encryption configuration used by default for newly inserted objects.
309 Encryption *BucketEncryption
311 // The logging configuration.
312 Logging *BucketLogging
314 // The website configuration.
315 Website *BucketWebsite
318 // BucketPolicyOnly configures access checks to use only bucket-level IAM
320 type BucketPolicyOnly struct {
321 // Enabled specifies whether access checks use only bucket-level IAM
322 // policies. Enabled may be disabled until the locked time.
324 // LockedTime specifies the deadline for changing Enabled from true to
329 // Lifecycle is the lifecycle configuration for objects in the bucket.
330 type Lifecycle struct {
331 Rules []LifecycleRule
334 // RetentionPolicy enforces a minimum retention time for all objects
335 // contained in the bucket.
337 // Any attempt to overwrite or delete objects younger than the retention
338 // period will result in an error. An unlocked retention policy can be
339 // modified or removed from the bucket via the Update method. A
340 // locked retention policy cannot be removed or shortened in duration
341 // for the lifetime of the bucket.
343 // This feature is in private alpha release. It is not currently available to
344 // most customers. It might be changed in backwards-incompatible ways and is not
345 // subject to any SLA or deprecation policy.
346 type RetentionPolicy struct {
347 // RetentionPeriod specifies the duration that objects need to be
348 // retained. Retention duration must be greater than zero and less than
349 // 100 years. Note that enforcement of retention periods less than a day
350 // is not guaranteed. Such periods should only be used for testing
352 RetentionPeriod time.Duration
354 // EffectiveTime is the time from which the policy was enforced and
355 // effective. This field is read-only.
356 EffectiveTime time.Time
358 // IsLocked describes whether the bucket is locked. Once locked, an
359 // object retention policy cannot be modified.
360 // This field is read-only.
365 // RFC3339 date with only the date segment, used for CreatedBefore in LifecycleRule.
366 rfc3339Date = "2006-01-02"
368 // DeleteAction is a lifecycle action that deletes a live and/or archived
369 // objects. Takes precedence over SetStorageClass actions.
370 DeleteAction = "Delete"
372 // SetStorageClassAction changes the storage class of live and/or archived
374 SetStorageClassAction = "SetStorageClass"
377 // LifecycleRule is a lifecycle configuration rule.
379 // When all the configured conditions are met by an object in the bucket, the
380 // configured action will automatically be taken on that object.
381 type LifecycleRule struct {
382 // Action is the action to take when all of the associated conditions are
384 Action LifecycleAction
386 // Condition is the set of conditions that must be met for the associated
387 // action to be taken.
388 Condition LifecycleCondition
391 // LifecycleAction is a lifecycle configuration action.
392 type LifecycleAction struct {
393 // Type is the type of action to take on matching objects.
395 // Acceptable values are "Delete" to delete matching objects and
396 // "SetStorageClass" to set the storage class defined in StorageClass on
400 // StorageClass is the storage class to set on matching objects if the Action
401 // is "SetStorageClass".
405 // Liveness specifies whether the object is live or not.
409 // LiveAndArchived includes both live and archived objects.
410 LiveAndArchived Liveness = iota
411 // Live specifies that the object is still live.
413 // Archived specifies that the object is archived.
417 // LifecycleCondition is a set of conditions used to match objects and take an
418 // action automatically.
420 // All configured conditions must be met for the associated action to be taken.
421 type LifecycleCondition struct {
422 // AgeInDays is the age of the object in days.
425 // CreatedBefore is the time the object was created.
427 // This condition is satisfied when an object is created before midnight of
428 // the specified date in UTC.
429 CreatedBefore time.Time
431 // Liveness specifies the object's liveness. Relevant only for versioned objects
434 // MatchesStorageClasses is the condition matching the object's storage
437 // Values include "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE",
438 // "STANDARD", and "DURABLE_REDUCED_AVAILABILITY".
439 MatchesStorageClasses []string
441 // NumNewerVersions is the condition matching objects with a number of newer versions.
443 // If the value is N, this condition is satisfied when there are at least N
444 // versions (including the live version) newer than this version of the
446 NumNewerVersions int64
449 // BucketLogging holds the bucket's logging configuration, which defines the
450 // destination bucket and optional name prefix for the current bucket's
452 type BucketLogging struct {
453 // The destination bucket where the current bucket's logs
457 // A prefix for log object names.
458 LogObjectPrefix string
461 // BucketWebsite holds the bucket's website configuration, controlling how the
462 // service behaves when accessing bucket contents as a web site. See
463 // https://cloud.google.com/storage/docs/static-website for more information.
464 type BucketWebsite struct {
465 // If the requested object path is missing, the service will ensure the path has
466 // a trailing '/', append this suffix, and attempt to retrieve the resulting
467 // object. This allows the creation of index.html objects to represent directory
469 MainPageSuffix string
471 // If the requested object path is missing, and any mainPageSuffix object is
472 // missing, if applicable, the service will return the named object from this
473 // bucket as the content for a 404 Not Found result.
477 func newBucket(b *raw.Bucket) (*BucketAttrs, error) {
481 rp, err := toRetentionPolicy(b.RetentionPolicy)
487 Location: b.Location,
488 MetaGeneration: b.Metageneration,
489 DefaultEventBasedHold: b.DefaultEventBasedHold,
490 StorageClass: b.StorageClass,
491 Created: convertTime(b.TimeCreated),
492 VersioningEnabled: b.Versioning != nil && b.Versioning.Enabled,
493 ACL: toBucketACLRules(b.Acl),
494 DefaultObjectACL: toObjectACLRules(b.DefaultObjectAcl),
496 RequesterPays: b.Billing != nil && b.Billing.RequesterPays,
497 Lifecycle: toLifecycle(b.Lifecycle),
499 CORS: toCORS(b.Cors),
500 Encryption: toBucketEncryption(b.Encryption),
501 Logging: toBucketLogging(b.Logging),
502 Website: toBucketWebsite(b.Website),
503 BucketPolicyOnly: toBucketPolicyOnly(b.IamConfiguration),
507 // toRawBucket copies the editable attribute from b to the raw library's Bucket type.
508 func (b *BucketAttrs) toRawBucket() *raw.Bucket {
510 var labels map[string]string
511 if len(b.Labels) > 0 {
512 labels = make(map[string]string, len(b.Labels))
513 for k, v := range b.Labels {
517 // Ignore VersioningEnabled if it is false. This is OK because
518 // we only call this method when creating a bucket, and by default
519 // new buckets have versioning off.
520 var v *raw.BucketVersioning
521 if b.VersioningEnabled {
522 v = &raw.BucketVersioning{Enabled: true}
524 var bb *raw.BucketBilling
526 bb = &raw.BucketBilling{RequesterPays: true}
528 var bktIAM *raw.BucketIamConfiguration
529 if b.BucketPolicyOnly.Enabled {
530 bktIAM = &raw.BucketIamConfiguration{
531 BucketPolicyOnly: &raw.BucketIamConfigurationBucketPolicyOnly{
538 Location: b.Location,
539 StorageClass: b.StorageClass,
540 Acl: toRawBucketACL(b.ACL),
541 DefaultObjectAcl: toRawObjectACL(b.DefaultObjectACL),
545 Lifecycle: toRawLifecycle(b.Lifecycle),
546 RetentionPolicy: b.RetentionPolicy.toRawRetentionPolicy(),
547 Cors: toRawCORS(b.CORS),
548 Encryption: b.Encryption.toRawBucketEncryption(),
549 Logging: b.Logging.toRawBucketLogging(),
550 Website: b.Website.toRawBucketWebsite(),
551 IamConfiguration: bktIAM,
555 // CORS is the bucket's Cross-Origin Resource Sharing (CORS) configuration.
557 // MaxAge is the value to return in the Access-Control-Max-Age
558 // header used in preflight responses.
561 // Methods is the list of HTTP methods on which to include CORS response
562 // headers, (GET, OPTIONS, POST, etc) Note: "*" is permitted in the list
563 // of methods, and means "any method".
566 // Origins is the list of Origins eligible to receive CORS response
567 // headers. Note: "*" is permitted in the list of origins, and means
571 // ResponseHeaders is the list of HTTP headers other than the simple
572 // response headers to give permission for the user-agent to share
574 ResponseHeaders []string
577 // BucketEncryption is a bucket's encryption configuration.
578 type BucketEncryption struct {
579 // A Cloud KMS key name, in the form
580 // projects/P/locations/L/keyRings/R/cryptoKeys/K, that will be used to encrypt
581 // objects inserted into this bucket, if no encryption method is specified.
582 // The key's location must be the same as the bucket's.
583 DefaultKMSKeyName string
586 // BucketAttrsToUpdate define the attributes to update during an Update call.
587 type BucketAttrsToUpdate struct {
588 // If set, updates whether the bucket uses versioning.
589 VersioningEnabled optional.Bool
591 // If set, updates whether the bucket is a Requester Pays bucket.
592 RequesterPays optional.Bool
594 // DefaultEventBasedHold is the default value for event-based hold on
595 // newly created objects in this bucket.
596 DefaultEventBasedHold optional.Bool
598 // BucketPolicyOnly configures access checks to use only bucket-level IAM
600 BucketPolicyOnly *BucketPolicyOnly
602 // If set, updates the retention policy of the bucket. Using
603 // RetentionPolicy.RetentionPeriod = 0 will delete the existing policy.
605 // This feature is in private alpha release. It is not currently available to
606 // most customers. It might be changed in backwards-incompatible ways and is not
607 // subject to any SLA or deprecation policy.
608 RetentionPolicy *RetentionPolicy
610 // If set, replaces the CORS configuration with a new configuration.
611 // An empty (rather than nil) slice causes all CORS policies to be removed.
614 // If set, replaces the encryption configuration of the bucket. Using
615 // BucketEncryption.DefaultKMSKeyName = "" will delete the existing
617 Encryption *BucketEncryption
619 // If set, replaces the lifecycle configuration of the bucket.
622 // If set, replaces the logging configuration of the bucket.
623 Logging *BucketLogging
625 // If set, replaces the website configuration of the bucket.
626 Website *BucketWebsite
628 // If not empty, applies a predefined set of access controls.
629 // See https://cloud.google.com/storage/docs/json_api/v1/buckets/patch.
632 // If not empty, applies a predefined set of default object access controls.
633 // See https://cloud.google.com/storage/docs/json_api/v1/buckets/patch.
634 PredefinedDefaultObjectACL string
636 setLabels map[string]string
637 deleteLabels map[string]bool
640 // SetLabel causes a label to be added or modified when ua is used
641 // in a call to Bucket.Update.
642 func (ua *BucketAttrsToUpdate) SetLabel(name, value string) {
643 if ua.setLabels == nil {
644 ua.setLabels = map[string]string{}
646 ua.setLabels[name] = value
649 // DeleteLabel causes a label to be deleted when ua is used in a
650 // call to Bucket.Update.
651 func (ua *BucketAttrsToUpdate) DeleteLabel(name string) {
652 if ua.deleteLabels == nil {
653 ua.deleteLabels = map[string]bool{}
655 ua.deleteLabels[name] = true
658 func (ua *BucketAttrsToUpdate) toRawBucket() *raw.Bucket {
661 rb.Cors = toRawCORS(ua.CORS)
662 rb.ForceSendFields = append(rb.ForceSendFields, "Cors")
664 if ua.DefaultEventBasedHold != nil {
665 rb.DefaultEventBasedHold = optional.ToBool(ua.DefaultEventBasedHold)
666 rb.ForceSendFields = append(rb.ForceSendFields, "DefaultEventBasedHold")
668 if ua.RetentionPolicy != nil {
669 if ua.RetentionPolicy.RetentionPeriod == 0 {
670 rb.NullFields = append(rb.NullFields, "RetentionPolicy")
671 rb.RetentionPolicy = nil
673 rb.RetentionPolicy = ua.RetentionPolicy.toRawRetentionPolicy()
676 if ua.VersioningEnabled != nil {
677 rb.Versioning = &raw.BucketVersioning{
678 Enabled: optional.ToBool(ua.VersioningEnabled),
679 ForceSendFields: []string{"Enabled"},
682 if ua.RequesterPays != nil {
683 rb.Billing = &raw.BucketBilling{
684 RequesterPays: optional.ToBool(ua.RequesterPays),
685 ForceSendFields: []string{"RequesterPays"},
688 if ua.BucketPolicyOnly != nil {
689 rb.IamConfiguration = &raw.BucketIamConfiguration{
690 BucketPolicyOnly: &raw.BucketIamConfigurationBucketPolicyOnly{
691 Enabled: ua.BucketPolicyOnly.Enabled,
695 if ua.Encryption != nil {
696 if ua.Encryption.DefaultKMSKeyName == "" {
697 rb.NullFields = append(rb.NullFields, "Encryption")
700 rb.Encryption = ua.Encryption.toRawBucketEncryption()
703 if ua.Lifecycle != nil {
704 rb.Lifecycle = toRawLifecycle(*ua.Lifecycle)
706 if ua.Logging != nil {
707 if *ua.Logging == (BucketLogging{}) {
708 rb.NullFields = append(rb.NullFields, "Logging")
711 rb.Logging = ua.Logging.toRawBucketLogging()
714 if ua.Website != nil {
715 if *ua.Website == (BucketWebsite{}) {
716 rb.NullFields = append(rb.NullFields, "Website")
719 rb.Website = ua.Website.toRawBucketWebsite()
722 if ua.PredefinedACL != "" {
723 // Clear ACL or the call will fail.
725 rb.ForceSendFields = append(rb.ForceSendFields, "Acl")
727 if ua.PredefinedDefaultObjectACL != "" {
728 // Clear ACLs or the call will fail.
729 rb.DefaultObjectAcl = nil
730 rb.ForceSendFields = append(rb.ForceSendFields, "DefaultObjectAcl")
732 if ua.setLabels != nil || ua.deleteLabels != nil {
733 rb.Labels = map[string]string{}
734 for k, v := range ua.setLabels {
737 if len(rb.Labels) == 0 && len(ua.deleteLabels) > 0 {
738 rb.ForceSendFields = append(rb.ForceSendFields, "Labels")
740 for l := range ua.deleteLabels {
741 rb.NullFields = append(rb.NullFields, "Labels."+l)
747 // If returns a new BucketHandle that applies a set of preconditions.
748 // Preconditions already set on the BucketHandle are ignored.
749 // Operations on the new handle will return an error if the preconditions are not
750 // satisfied. The only valid preconditions for buckets are MetagenerationMatch
751 // and MetagenerationNotMatch.
752 func (b *BucketHandle) If(conds BucketConditions) *BucketHandle {
758 // BucketConditions constrain bucket methods to act on specific metagenerations.
760 // The zero value is an empty set of constraints.
761 type BucketConditions struct {
762 // MetagenerationMatch specifies that the bucket must have the given
763 // metageneration for the operation to occur.
764 // If MetagenerationMatch is zero, it has no effect.
765 MetagenerationMatch int64
767 // MetagenerationNotMatch specifies that the bucket must not have the given
768 // metageneration for the operation to occur.
769 // If MetagenerationNotMatch is zero, it has no effect.
770 MetagenerationNotMatch int64
773 func (c *BucketConditions) validate(method string) error {
774 if *c == (BucketConditions{}) {
775 return fmt.Errorf("storage: %s: empty conditions", method)
777 if c.MetagenerationMatch != 0 && c.MetagenerationNotMatch != 0 {
778 return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method)
783 // UserProject returns a new BucketHandle that passes the project ID as the user
784 // project for all subsequent calls. Calls with a user project will be billed to that
785 // project rather than to the bucket's owning project.
787 // A user project is required for all operations on Requester Pays buckets.
788 func (b *BucketHandle) UserProject(projectID string) *BucketHandle {
790 b2.userProject = projectID
791 b2.acl.userProject = projectID
792 b2.defaultObjectACL.userProject = projectID
796 // LockRetentionPolicy locks a bucket's retention policy until a previously-configured
797 // RetentionPeriod past the EffectiveTime. Note that if RetentionPeriod is set to less
798 // than a day, the retention policy is treated as a development configuration and locking
799 // will have no effect. The BucketHandle must have a metageneration condition that
800 // matches the bucket's metageneration. See BucketHandle.If.
802 // This feature is in private alpha release. It is not currently available to
803 // most customers. It might be changed in backwards-incompatible ways and is not
804 // subject to any SLA or deprecation policy.
805 func (b *BucketHandle) LockRetentionPolicy(ctx context.Context) error {
806 var metageneration int64
808 metageneration = b.conds.MetagenerationMatch
810 req := b.c.raw.Buckets.LockRetentionPolicy(b.name, metageneration)
811 _, err := req.Context(ctx).Do()
815 // applyBucketConds modifies the provided call using the conditions in conds.
816 // call is something that quacks like a *raw.WhateverCall.
817 func applyBucketConds(method string, conds *BucketConditions, call interface{}) error {
821 if err := conds.validate(method); err != nil {
824 cval := reflect.ValueOf(call)
826 case conds.MetagenerationMatch != 0:
827 if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) {
828 return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method)
830 case conds.MetagenerationNotMatch != 0:
831 if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) {
832 return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method)
838 func (rp *RetentionPolicy) toRawRetentionPolicy() *raw.BucketRetentionPolicy {
842 return &raw.BucketRetentionPolicy{
843 RetentionPeriod: int64(rp.RetentionPeriod / time.Second),
847 func toRetentionPolicy(rp *raw.BucketRetentionPolicy) (*RetentionPolicy, error) {
851 t, err := time.Parse(time.RFC3339, rp.EffectiveTime)
855 return &RetentionPolicy{
856 RetentionPeriod: time.Duration(rp.RetentionPeriod) * time.Second,
858 IsLocked: rp.IsLocked,
862 func toRawCORS(c []CORS) []*raw.BucketCors {
863 var out []*raw.BucketCors
864 for _, v := range c {
865 out = append(out, &raw.BucketCors{
866 MaxAgeSeconds: int64(v.MaxAge / time.Second),
869 ResponseHeader: v.ResponseHeaders,
875 func toCORS(rc []*raw.BucketCors) []CORS {
877 for _, v := range rc {
878 out = append(out, CORS{
879 MaxAge: time.Duration(v.MaxAgeSeconds) * time.Second,
882 ResponseHeaders: v.ResponseHeader,
888 func toRawLifecycle(l Lifecycle) *raw.BucketLifecycle {
889 var rl raw.BucketLifecycle
890 if len(l.Rules) == 0 {
893 for _, r := range l.Rules {
894 rr := &raw.BucketLifecycleRule{
895 Action: &raw.BucketLifecycleRuleAction{
897 StorageClass: r.Action.StorageClass,
899 Condition: &raw.BucketLifecycleRuleCondition{
900 Age: r.Condition.AgeInDays,
901 MatchesStorageClass: r.Condition.MatchesStorageClasses,
902 NumNewerVersions: r.Condition.NumNewerVersions,
906 switch r.Condition.Liveness {
907 case LiveAndArchived:
908 rr.Condition.IsLive = nil
910 rr.Condition.IsLive = googleapi.Bool(true)
912 rr.Condition.IsLive = googleapi.Bool(false)
915 if !r.Condition.CreatedBefore.IsZero() {
916 rr.Condition.CreatedBefore = r.Condition.CreatedBefore.Format(rfc3339Date)
918 rl.Rule = append(rl.Rule, rr)
923 func toLifecycle(rl *raw.BucketLifecycle) Lifecycle {
928 for _, rr := range rl.Rule {
930 Action: LifecycleAction{
931 Type: rr.Action.Type,
932 StorageClass: rr.Action.StorageClass,
934 Condition: LifecycleCondition{
935 AgeInDays: rr.Condition.Age,
936 MatchesStorageClasses: rr.Condition.MatchesStorageClass,
937 NumNewerVersions: rr.Condition.NumNewerVersions,
942 case rr.Condition.IsLive == nil:
943 r.Condition.Liveness = LiveAndArchived
944 case *rr.Condition.IsLive == true:
945 r.Condition.Liveness = Live
946 case *rr.Condition.IsLive == false:
947 r.Condition.Liveness = Archived
950 if rr.Condition.CreatedBefore != "" {
951 r.Condition.CreatedBefore, _ = time.Parse(rfc3339Date, rr.Condition.CreatedBefore)
953 l.Rules = append(l.Rules, r)
958 func (e *BucketEncryption) toRawBucketEncryption() *raw.BucketEncryption {
962 return &raw.BucketEncryption{
963 DefaultKmsKeyName: e.DefaultKMSKeyName,
967 func toBucketEncryption(e *raw.BucketEncryption) *BucketEncryption {
971 return &BucketEncryption{DefaultKMSKeyName: e.DefaultKmsKeyName}
974 func (b *BucketLogging) toRawBucketLogging() *raw.BucketLogging {
978 return &raw.BucketLogging{
979 LogBucket: b.LogBucket,
980 LogObjectPrefix: b.LogObjectPrefix,
984 func toBucketLogging(b *raw.BucketLogging) *BucketLogging {
988 return &BucketLogging{
989 LogBucket: b.LogBucket,
990 LogObjectPrefix: b.LogObjectPrefix,
994 func (w *BucketWebsite) toRawBucketWebsite() *raw.BucketWebsite {
998 return &raw.BucketWebsite{
999 MainPageSuffix: w.MainPageSuffix,
1000 NotFoundPage: w.NotFoundPage,
1004 func toBucketWebsite(w *raw.BucketWebsite) *BucketWebsite {
1008 return &BucketWebsite{
1009 MainPageSuffix: w.MainPageSuffix,
1010 NotFoundPage: w.NotFoundPage,
1014 func toBucketPolicyOnly(b *raw.BucketIamConfiguration) BucketPolicyOnly {
1015 if b == nil || b.BucketPolicyOnly == nil || !b.BucketPolicyOnly.Enabled {
1016 return BucketPolicyOnly{}
1018 lt, err := time.Parse(time.RFC3339, b.BucketPolicyOnly.LockedTime)
1020 return BucketPolicyOnly{
1024 return BucketPolicyOnly{
1030 // Objects returns an iterator over the objects in the bucket that match the Query q.
1031 // If q is nil, no filtering is done.
1032 func (b *BucketHandle) Objects(ctx context.Context, q *Query) *ObjectIterator {
1033 it := &ObjectIterator{
1037 it.pageInfo, it.nextFunc = iterator.NewPageInfo(
1039 func() int { return len(it.items) },
1040 func() interface{} { b := it.items; it.items = nil; return b })
1047 // An ObjectIterator is an iterator over ObjectAttrs.
1048 type ObjectIterator struct {
1050 bucket *BucketHandle
1052 pageInfo *iterator.PageInfo
1053 nextFunc func() error
1054 items []*ObjectAttrs
1057 // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
1058 func (it *ObjectIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
1060 // Next returns the next result. Its second return value is iterator.Done if
1061 // there are no more results. Once Next returns iterator.Done, all subsequent
1062 // calls will return iterator.Done.
1064 // If Query.Delimiter is non-empty, some of the ObjectAttrs returned by Next will
1065 // have a non-empty Prefix field, and a zero value for all other fields. These
1066 // represent prefixes.
1067 func (it *ObjectIterator) Next() (*ObjectAttrs, error) {
1068 if err := it.nextFunc(); err != nil {
1072 it.items = it.items[1:]
1076 func (it *ObjectIterator) fetch(pageSize int, pageToken string) (string, error) {
1077 req := it.bucket.c.raw.Objects.List(it.bucket.name)
1078 setClientHeader(req.Header())
1079 req.Projection("full")
1080 req.Delimiter(it.query.Delimiter)
1081 req.Prefix(it.query.Prefix)
1082 req.Versions(it.query.Versions)
1083 req.PageToken(pageToken)
1084 if it.bucket.userProject != "" {
1085 req.UserProject(it.bucket.userProject)
1088 req.MaxResults(int64(pageSize))
1090 var resp *raw.Objects
1092 err = runWithRetry(it.ctx, func() error {
1093 resp, err = req.Context(it.ctx).Do()
1097 if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
1098 err = ErrBucketNotExist
1102 for _, item := range resp.Items {
1103 it.items = append(it.items, newObject(item))
1105 for _, prefix := range resp.Prefixes {
1106 it.items = append(it.items, &ObjectAttrs{Prefix: prefix})
1108 return resp.NextPageToken, nil
1111 // Buckets returns an iterator over the buckets in the project. You may
1112 // optionally set the iterator's Prefix field to restrict the list to buckets
1113 // whose names begin with the prefix. By default, all buckets in the project
1115 func (c *Client) Buckets(ctx context.Context, projectID string) *BucketIterator {
1116 it := &BucketIterator{
1119 projectID: projectID,
1121 it.pageInfo, it.nextFunc = iterator.NewPageInfo(
1123 func() int { return len(it.buckets) },
1124 func() interface{} { b := it.buckets; it.buckets = nil; return b })
1128 // A BucketIterator is an iterator over BucketAttrs.
1129 type BucketIterator struct {
1130 // Prefix restricts the iterator to buckets whose names begin with it.
1136 buckets []*BucketAttrs
1137 pageInfo *iterator.PageInfo
1138 nextFunc func() error
1141 // Next returns the next result. Its second return value is iterator.Done if
1142 // there are no more results. Once Next returns iterator.Done, all subsequent
1143 // calls will return iterator.Done.
1144 func (it *BucketIterator) Next() (*BucketAttrs, error) {
1145 if err := it.nextFunc(); err != nil {
1149 it.buckets = it.buckets[1:]
1153 // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
1154 func (it *BucketIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
1156 func (it *BucketIterator) fetch(pageSize int, pageToken string) (token string, err error) {
1157 req := it.client.raw.Buckets.List(it.projectID)
1158 setClientHeader(req.Header())
1159 req.Projection("full")
1160 req.Prefix(it.Prefix)
1161 req.PageToken(pageToken)
1163 req.MaxResults(int64(pageSize))
1165 var resp *raw.Buckets
1166 err = runWithRetry(it.ctx, func() error {
1167 resp, err = req.Context(it.ctx).Do()
1173 for _, item := range resp.Items {
1174 b, err := newBucket(item)
1178 it.buckets = append(it.buckets, b)
1180 return resp.NextPageToken, nil