123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984 |
- // Copyright 2014 Google Inc. LiveAndArchived Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package storage
- import (
- "fmt"
- "net/http"
- "reflect"
- "time"
- "cloud.google.com/go/internal/optional"
- "cloud.google.com/go/internal/trace"
- "golang.org/x/net/context"
- "google.golang.org/api/googleapi"
- "google.golang.org/api/iterator"
- raw "google.golang.org/api/storage/v1"
- )
- // BucketHandle provides operations on a Google Cloud Storage bucket.
- // Use Client.Bucket to get a handle.
- type BucketHandle struct {
- c *Client
- name string
- acl ACLHandle
- defaultObjectACL ACLHandle
- conds *BucketConditions
- userProject string // project for Requester Pays buckets
- }
- // Bucket returns a BucketHandle, which provides operations on the named bucket.
- // This call does not perform any network operations.
- //
- // The supplied name must contain only lowercase letters, numbers, dashes,
- // underscores, and dots. The full specification for valid bucket names can be
- // found at:
- // https://cloud.google.com/storage/docs/bucket-naming
- func (c *Client) Bucket(name string) *BucketHandle {
- return &BucketHandle{
- c: c,
- name: name,
- acl: ACLHandle{
- c: c,
- bucket: name,
- },
- defaultObjectACL: ACLHandle{
- c: c,
- bucket: name,
- isDefault: true,
- },
- }
- }
- // Create creates the Bucket in the project.
- // If attrs is nil the API defaults will be used.
- func (b *BucketHandle) Create(ctx context.Context, projectID string, attrs *BucketAttrs) (err error) {
- ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Create")
- defer func() { trace.EndSpan(ctx, err) }()
- var bkt *raw.Bucket
- if attrs != nil {
- bkt = attrs.toRawBucket()
- } else {
- bkt = &raw.Bucket{}
- }
- bkt.Name = b.name
- // If there is lifecycle information but no location, explicitly set
- // the location. This is a GCS quirk/bug.
- if bkt.Location == "" && bkt.Lifecycle != nil {
- bkt.Location = "US"
- }
- req := b.c.raw.Buckets.Insert(projectID, bkt)
- setClientHeader(req.Header())
- return runWithRetry(ctx, func() error { _, err := req.Context(ctx).Do(); return err })
- }
- // Delete deletes the Bucket.
- func (b *BucketHandle) Delete(ctx context.Context) (err error) {
- ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Delete")
- defer func() { trace.EndSpan(ctx, err) }()
- req, err := b.newDeleteCall()
- if err != nil {
- return err
- }
- return runWithRetry(ctx, func() error { return req.Context(ctx).Do() })
- }
- func (b *BucketHandle) newDeleteCall() (*raw.BucketsDeleteCall, error) {
- req := b.c.raw.Buckets.Delete(b.name)
- setClientHeader(req.Header())
- if err := applyBucketConds("BucketHandle.Delete", b.conds, req); err != nil {
- return nil, err
- }
- if b.userProject != "" {
- req.UserProject(b.userProject)
- }
- return req, nil
- }
- // ACL returns an ACLHandle, which provides access to the bucket's access control list.
- // This controls who can list, create or overwrite the objects in a bucket.
- // This call does not perform any network operations.
- func (b *BucketHandle) ACL() *ACLHandle {
- return &b.acl
- }
- // DefaultObjectACL returns an ACLHandle, which provides access to the bucket's default object ACLs.
- // These ACLs are applied to newly created objects in this bucket that do not have a defined ACL.
- // This call does not perform any network operations.
- func (b *BucketHandle) DefaultObjectACL() *ACLHandle {
- return &b.defaultObjectACL
- }
- // Object returns an ObjectHandle, which provides operations on the named object.
- // This call does not perform any network operations.
- //
- // name must consist entirely of valid UTF-8-encoded runes. The full specification
- // for valid object names can be found at:
- // https://cloud.google.com/storage/docs/bucket-naming
- func (b *BucketHandle) Object(name string) *ObjectHandle {
- return &ObjectHandle{
- c: b.c,
- bucket: b.name,
- object: name,
- acl: ACLHandle{
- c: b.c,
- bucket: b.name,
- object: name,
- userProject: b.userProject,
- },
- gen: -1,
- userProject: b.userProject,
- }
- }
- // Attrs returns the metadata for the bucket.
- func (b *BucketHandle) Attrs(ctx context.Context) (attrs *BucketAttrs, err error) {
- ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Attrs")
- defer func() { trace.EndSpan(ctx, err) }()
- req, err := b.newGetCall()
- if err != nil {
- return nil, err
- }
- var resp *raw.Bucket
- err = runWithRetry(ctx, func() error {
- resp, err = req.Context(ctx).Do()
- return err
- })
- if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
- return nil, ErrBucketNotExist
- }
- if err != nil {
- return nil, err
- }
- return newBucket(resp)
- }
- func (b *BucketHandle) newGetCall() (*raw.BucketsGetCall, error) {
- req := b.c.raw.Buckets.Get(b.name).Projection("full")
- setClientHeader(req.Header())
- if err := applyBucketConds("BucketHandle.Attrs", b.conds, req); err != nil {
- return nil, err
- }
- if b.userProject != "" {
- req.UserProject(b.userProject)
- }
- return req, nil
- }
- func (b *BucketHandle) Update(ctx context.Context, uattrs BucketAttrsToUpdate) (attrs *BucketAttrs, err error) {
- ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Create")
- defer func() { trace.EndSpan(ctx, err) }()
- req, err := b.newPatchCall(&uattrs)
- if err != nil {
- return nil, err
- }
- // TODO(jba): retry iff metagen is set?
- rb, err := req.Context(ctx).Do()
- if err != nil {
- return nil, err
- }
- return newBucket(rb)
- }
- func (b *BucketHandle) newPatchCall(uattrs *BucketAttrsToUpdate) (*raw.BucketsPatchCall, error) {
- rb := uattrs.toRawBucket()
- req := b.c.raw.Buckets.Patch(b.name, rb).Projection("full")
- setClientHeader(req.Header())
- if err := applyBucketConds("BucketHandle.Update", b.conds, req); err != nil {
- return nil, err
- }
- if b.userProject != "" {
- req.UserProject(b.userProject)
- }
- return req, nil
- }
- // BucketAttrs represents the metadata for a Google Cloud Storage bucket.
- // Read-only fields are ignored by BucketHandle.Create.
- type BucketAttrs struct {
- // Name is the name of the bucket.
- // This field is read-only.
- Name string
- // ACL is the list of access control rules on the bucket.
- ACL []ACLRule
- // DefaultObjectACL is the list of access controls to
- // apply to new objects when no object ACL is provided.
- DefaultObjectACL []ACLRule
- // Location is the location of the bucket. It defaults to "US".
- Location string
- // MetaGeneration is the metadata generation of the bucket.
- // This field is read-only.
- MetaGeneration int64
- // StorageClass is the default storage class of the bucket. This defines
- // how objects in the bucket are stored and determines the SLA
- // and the cost of storage. Typical values are "MULTI_REGIONAL",
- // "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD" and
- // "DURABLE_REDUCED_AVAILABILITY". Defaults to "STANDARD", which
- // is equivalent to "MULTI_REGIONAL" or "REGIONAL" depending on
- // the bucket's location settings.
- StorageClass string
- // Created is the creation time of the bucket.
- // This field is read-only.
- Created time.Time
- // VersioningEnabled reports whether this bucket has versioning enabled.
- VersioningEnabled bool
- // Labels are the bucket's labels.
- Labels map[string]string
- // RequesterPays reports whether the bucket is a Requester Pays bucket.
- // Clients performing operations on Requester Pays buckets must provide
- // a user project (see BucketHandle.UserProject), which will be billed
- // for the operations.
- RequesterPays bool
- // Lifecycle is the lifecycle configuration for objects in the bucket.
- Lifecycle Lifecycle
- // Retention policy enforces a minimum retention time for all objects
- // contained in the bucket. A RetentionPolicy of nil implies the bucket
- // has no minimum data retention.
- //
- // This feature is in private alpha release. It is not currently available to
- // most customers. It might be changed in backwards-incompatible ways and is not
- // subject to any SLA or deprecation policy.
- RetentionPolicy *RetentionPolicy
- // The bucket's Cross-Origin Resource Sharing (CORS) configuration.
- CORS []CORS
- // The encryption configuration used by default for newly inserted objects.
- Encryption *BucketEncryption
- }
- // Lifecycle is the lifecycle configuration for objects in the bucket.
- type Lifecycle struct {
- Rules []LifecycleRule
- }
- // Retention policy enforces a minimum retention time for all objects
- // contained in the bucket.
- //
- // Any attempt to overwrite or delete objects younger than the retention
- // period will result in an error. An unlocked retention policy can be
- // modified or removed from the bucket via the Update method. A
- // locked retention policy cannot be removed or shortened in duration
- // for the lifetime of the bucket.
- //
- // This feature is in private alpha release. It is not currently available to
- // most customers. It might be changed in backwards-incompatible ways and is not
- // subject to any SLA or deprecation policy.
- type RetentionPolicy struct {
- // RetentionPeriod specifies the duration that objects need to be
- // retained. Retention duration must be greater than zero and less than
- // 100 years. Note that enforcement of retention periods less than a day
- // is not guaranteed. Such periods should only be used for testing
- // purposes.
- RetentionPeriod time.Duration
- // EffectiveTime is the time from which the policy was enforced and
- // effective. This field is read-only.
- EffectiveTime time.Time
- }
- const (
- // RFC3339 date with only the date segment, used for CreatedBefore in LifecycleRule.
- rfc3339Date = "2006-01-02"
- // DeleteAction is a lifecycle action that deletes a live and/or archived
- // objects. Takes precendence over SetStorageClass actions.
- DeleteAction = "Delete"
- // SetStorageClassAction changes the storage class of live and/or archived
- // objects.
- SetStorageClassAction = "SetStorageClass"
- )
- // LifecycleRule is a lifecycle configuration rule.
- //
- // When all the configured conditions are met by an object in the bucket, the
- // configured action will automatically be taken on that object.
- type LifecycleRule struct {
- // Action is the action to take when all of the associated conditions are
- // met.
- Action LifecycleAction
- // Condition is the set of conditions that must be met for the associated
- // action to be taken.
- Condition LifecycleCondition
- }
- // LifecycleAction is a lifecycle configuration action.
- type LifecycleAction struct {
- // Type is the type of action to take on matching objects.
- //
- // Acceptable values are "Delete" to delete matching objects and
- // "SetStorageClass" to set the storage class defined in StorageClass on
- // matching objects.
- Type string
- // StorageClass is the storage class to set on matching objects if the Action
- // is "SetStorageClass".
- StorageClass string
- }
- // Liveness specifies whether the object is live or not.
- type Liveness int
- const (
- // LiveAndArchived includes both live and archived objects.
- LiveAndArchived Liveness = iota
- // Live specifies that the object is still live.
- Live
- // Archived specifies that the object is archived.
- Archived
- )
- // LifecycleCondition is a set of conditions used to match objects and take an
- // action automatically.
- //
- // All configured conditions must be met for the associated action to be taken.
- type LifecycleCondition struct {
- // AgeInDays is the age of the object in days.
- AgeInDays int64
- // CreatedBefore is the time the object was created.
- //
- // This condition is satisfied when an object is created before midnight of
- // the specified date in UTC.
- CreatedBefore time.Time
- // Liveness specifies the object's liveness. Relevant only for versioned objects
- Liveness Liveness
- // MatchesStorageClasses is the condition matching the object's storage
- // class.
- //
- // Values include "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE",
- // "STANDARD", and "DURABLE_REDUCED_AVAILABILITY".
- MatchesStorageClasses []string
- // NumNewerVersions is the condition matching objects with a number of newer versions.
- //
- // If the value is N, this condition is satisfied when there are at least N
- // versions (including the live version) newer than this version of the
- // object.
- NumNewerVersions int64
- }
- func newBucket(b *raw.Bucket) (*BucketAttrs, error) {
- if b == nil {
- return nil, nil
- }
- rp, err := toRetentionPolicy(b.RetentionPolicy)
- if err != nil {
- return nil, err
- }
- bucket := &BucketAttrs{
- Name: b.Name,
- Location: b.Location,
- MetaGeneration: b.Metageneration,
- StorageClass: b.StorageClass,
- Created: convertTime(b.TimeCreated),
- VersioningEnabled: b.Versioning != nil && b.Versioning.Enabled,
- Labels: b.Labels,
- RequesterPays: b.Billing != nil && b.Billing.RequesterPays,
- Lifecycle: toLifecycle(b.Lifecycle),
- RetentionPolicy: rp,
- CORS: toCORS(b.Cors),
- Encryption: toBucketEncryption(b.Encryption),
- }
- acl := make([]ACLRule, len(b.Acl))
- for i, rule := range b.Acl {
- acl[i] = ACLRule{
- Entity: ACLEntity(rule.Entity),
- Role: ACLRole(rule.Role),
- }
- }
- bucket.ACL = acl
- objACL := make([]ACLRule, len(b.DefaultObjectAcl))
- for i, rule := range b.DefaultObjectAcl {
- objACL[i] = ACLRule{
- Entity: ACLEntity(rule.Entity),
- Role: ACLRole(rule.Role),
- }
- }
- bucket.DefaultObjectACL = objACL
- return bucket, nil
- }
- // toRawBucket copies the editable attribute from b to the raw library's Bucket type.
- func (b *BucketAttrs) toRawBucket() *raw.Bucket {
- var acl []*raw.BucketAccessControl
- if len(b.ACL) > 0 {
- acl = make([]*raw.BucketAccessControl, len(b.ACL))
- for i, rule := range b.ACL {
- acl[i] = &raw.BucketAccessControl{
- Entity: string(rule.Entity),
- Role: string(rule.Role),
- }
- }
- }
- dACL := toRawObjectACL(b.DefaultObjectACL)
- // Copy label map.
- var labels map[string]string
- if len(b.Labels) > 0 {
- labels = make(map[string]string, len(b.Labels))
- for k, v := range b.Labels {
- labels[k] = v
- }
- }
- // Ignore VersioningEnabled if it is false. This is OK because
- // we only call this method when creating a bucket, and by default
- // new buckets have versioning off.
- var v *raw.BucketVersioning
- if b.VersioningEnabled {
- v = &raw.BucketVersioning{Enabled: true}
- }
- var bb *raw.BucketBilling
- if b.RequesterPays {
- bb = &raw.BucketBilling{RequesterPays: true}
- }
- return &raw.Bucket{
- Name: b.Name,
- DefaultObjectAcl: dACL,
- Location: b.Location,
- StorageClass: b.StorageClass,
- Acl: acl,
- Versioning: v,
- Labels: labels,
- Billing: bb,
- Lifecycle: toRawLifecycle(b.Lifecycle),
- RetentionPolicy: b.RetentionPolicy.toRawRetentionPolicy(),
- Cors: toRawCORS(b.CORS),
- Encryption: b.Encryption.toRawBucketEncryption(),
- }
- }
- // CORS is the bucket's Cross-Origin Resource Sharing (CORS) configuration.
- type CORS struct {
- // MaxAge is the value to return in the Access-Control-Max-Age
- // header used in preflight responses.
- MaxAge time.Duration
- // Methods is the list of HTTP methods on which to include CORS response
- // headers, (GET, OPTIONS, POST, etc) Note: "*" is permitted in the list
- // of methods, and means "any method".
- Methods []string
- // Origins is the list of Origins eligible to receive CORS response
- // headers. Note: "*" is permitted in the list of origins, and means
- // "any Origin".
- Origins []string
- // ResponseHeaders is the list of HTTP headers other than the simple
- // response headers to give permission for the user-agent to share
- // across domains.
- ResponseHeaders []string
- }
- // BucketEncryption is a bucket's encryption configuration.
- type BucketEncryption struct {
- // A Cloud KMS key name, in the form
- // projects/P/locations/L/keyRings/R/cryptoKeys/K, that will be used to encrypt
- // objects inserted into this bucket, if no encryption method is specified.
- // The key's location must be the same as the bucket's.
- DefaultKMSKeyName string
- }
- type BucketAttrsToUpdate struct {
- // If set, updates whether the bucket uses versioning.
- VersioningEnabled optional.Bool
- // If set, updates whether the bucket is a Requester Pays bucket.
- RequesterPays optional.Bool
- // If set, updates the retention policy of the bucket. Using
- // RetentionPolicy.RetentionPeriod = 0 will delete the existing policy.
- //
- // This feature is in private alpha release. It is not currently available to
- // most customers. It might be changed in backwards-incompatible ways and is not
- // subject to any SLA or deprecation policy.
- RetentionPolicy *RetentionPolicy
- // If set, replaces the CORS configuration with a new configuration.
- // An empty (rather than nil) slice causes all CORS policies to be removed.
- CORS []CORS
- // If set, replaces the encryption configuration of the bucket. Using
- // BucketEncryption.DefaultKMSKeyName = "" will delete the existing
- // configuration.
- Encryption *BucketEncryption
- setLabels map[string]string
- deleteLabels map[string]bool
- }
- // SetLabel causes a label to be added or modified when ua is used
- // in a call to Bucket.Update.
- func (ua *BucketAttrsToUpdate) SetLabel(name, value string) {
- if ua.setLabels == nil {
- ua.setLabels = map[string]string{}
- }
- ua.setLabels[name] = value
- }
- // DeleteLabel causes a label to be deleted when ua is used in a
- // call to Bucket.Update.
- func (ua *BucketAttrsToUpdate) DeleteLabel(name string) {
- if ua.deleteLabels == nil {
- ua.deleteLabels = map[string]bool{}
- }
- ua.deleteLabels[name] = true
- }
- func (ua *BucketAttrsToUpdate) toRawBucket() *raw.Bucket {
- rb := &raw.Bucket{}
- if ua.CORS != nil {
- rb.Cors = toRawCORS(ua.CORS)
- rb.ForceSendFields = append(rb.ForceSendFields, "Cors")
- }
- if ua.RetentionPolicy != nil {
- if ua.RetentionPolicy.RetentionPeriod == 0 {
- rb.NullFields = append(rb.NullFields, "RetentionPolicy")
- rb.RetentionPolicy = nil
- } else {
- rb.RetentionPolicy = ua.RetentionPolicy.toRawRetentionPolicy()
- }
- }
- if ua.VersioningEnabled != nil {
- rb.Versioning = &raw.BucketVersioning{
- Enabled: optional.ToBool(ua.VersioningEnabled),
- ForceSendFields: []string{"Enabled"},
- }
- }
- if ua.RequesterPays != nil {
- rb.Billing = &raw.BucketBilling{
- RequesterPays: optional.ToBool(ua.RequesterPays),
- ForceSendFields: []string{"RequesterPays"},
- }
- }
- if ua.Encryption != nil {
- if ua.Encryption.DefaultKMSKeyName == "" {
- rb.NullFields = append(rb.NullFields, "Encryption")
- rb.Encryption = nil
- } else {
- rb.Encryption = ua.Encryption.toRawBucketEncryption()
- }
- }
- if ua.setLabels != nil || ua.deleteLabels != nil {
- rb.Labels = map[string]string{}
- for k, v := range ua.setLabels {
- rb.Labels[k] = v
- }
- if len(rb.Labels) == 0 && len(ua.deleteLabels) > 0 {
- rb.ForceSendFields = append(rb.ForceSendFields, "Labels")
- }
- for l := range ua.deleteLabels {
- rb.NullFields = append(rb.NullFields, "Labels."+l)
- }
- }
- return rb
- }
- // If returns a new BucketHandle that applies a set of preconditions.
- // Preconditions already set on the BucketHandle are ignored.
- // Operations on the new handle will only occur if the preconditions are
- // satisfied. The only valid preconditions for buckets are MetagenerationMatch
- // and MetagenerationNotMatch.
- func (b *BucketHandle) If(conds BucketConditions) *BucketHandle {
- b2 := *b
- b2.conds = &conds
- return &b2
- }
- // BucketConditions constrain bucket methods to act on specific metagenerations.
- //
- // The zero value is an empty set of constraints.
- type BucketConditions struct {
- // MetagenerationMatch specifies that the bucket must have the given
- // metageneration for the operation to occur.
- // If MetagenerationMatch is zero, it has no effect.
- MetagenerationMatch int64
- // MetagenerationNotMatch specifies that the bucket must not have the given
- // metageneration for the operation to occur.
- // If MetagenerationNotMatch is zero, it has no effect.
- MetagenerationNotMatch int64
- }
- func (c *BucketConditions) validate(method string) error {
- if *c == (BucketConditions{}) {
- return fmt.Errorf("storage: %s: empty conditions", method)
- }
- if c.MetagenerationMatch != 0 && c.MetagenerationNotMatch != 0 {
- return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method)
- }
- return nil
- }
- // UserProject returns a new BucketHandle that passes the project ID as the user
- // project for all subsequent calls. Calls with a user project will be billed to that
- // project rather than to the bucket's owning project.
- //
- // A user project is required for all operations on Requester Pays buckets.
- func (b *BucketHandle) UserProject(projectID string) *BucketHandle {
- b2 := *b
- b2.userProject = projectID
- b2.acl.userProject = projectID
- b2.defaultObjectACL.userProject = projectID
- return &b2
- }
- // LockRetentionPolicy locks a bucket's retention policy until a previously-configured
- // RetentionPeriod past the EffectiveTime. Note that if RetentionPeriod is set to less
- // than a day, the retention policy is treated as a development configuration and locking
- // will have no effect. The BucketHandle must have a metageneration condition that
- // matches the bucket's metageneration. See BucketHandle.If.
- //
- // This feature is in private alpha release. It is not currently available to
- // most customers. It might be changed in backwards-incompatible ways and is not
- // subject to any SLA or deprecation policy.
- func (b *BucketHandle) LockRetentionPolicy(ctx context.Context) error {
- var metageneration int64
- if b.conds != nil {
- metageneration = b.conds.MetagenerationMatch
- }
- req := b.c.raw.Buckets.LockRetentionPolicy(b.name, metageneration)
- _, err := req.Context(ctx).Do()
- return err
- }
- // applyBucketConds modifies the provided call using the conditions in conds.
- // call is something that quacks like a *raw.WhateverCall.
- func applyBucketConds(method string, conds *BucketConditions, call interface{}) error {
- if conds == nil {
- return nil
- }
- if err := conds.validate(method); err != nil {
- return err
- }
- cval := reflect.ValueOf(call)
- switch {
- case conds.MetagenerationMatch != 0:
- if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) {
- return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method)
- }
- case conds.MetagenerationNotMatch != 0:
- if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) {
- return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method)
- }
- }
- return nil
- }
- func (rp *RetentionPolicy) toRawRetentionPolicy() *raw.BucketRetentionPolicy {
- if rp == nil {
- return nil
- }
- return &raw.BucketRetentionPolicy{
- RetentionPeriod: int64(rp.RetentionPeriod / time.Second),
- }
- }
- func toRetentionPolicy(rp *raw.BucketRetentionPolicy) (*RetentionPolicy, error) {
- if rp == nil {
- return nil, nil
- }
- t, err := time.Parse(time.RFC3339, rp.EffectiveTime)
- if err != nil {
- return nil, err
- }
- return &RetentionPolicy{
- RetentionPeriod: time.Duration(rp.RetentionPeriod) * time.Second,
- EffectiveTime: t,
- }, nil
- }
- func toRawCORS(c []CORS) []*raw.BucketCors {
- var out []*raw.BucketCors
- for _, v := range c {
- out = append(out, &raw.BucketCors{
- MaxAgeSeconds: int64(v.MaxAge / time.Second),
- Method: v.Methods,
- Origin: v.Origins,
- ResponseHeader: v.ResponseHeaders,
- })
- }
- return out
- }
- func toCORS(rc []*raw.BucketCors) []CORS {
- var out []CORS
- for _, v := range rc {
- out = append(out, CORS{
- MaxAge: time.Duration(v.MaxAgeSeconds) * time.Second,
- Methods: v.Method,
- Origins: v.Origin,
- ResponseHeaders: v.ResponseHeader,
- })
- }
- return out
- }
- func toRawLifecycle(l Lifecycle) *raw.BucketLifecycle {
- var rl raw.BucketLifecycle
- if len(l.Rules) == 0 {
- return nil
- }
- for _, r := range l.Rules {
- rr := &raw.BucketLifecycleRule{
- Action: &raw.BucketLifecycleRuleAction{
- Type: r.Action.Type,
- StorageClass: r.Action.StorageClass,
- },
- Condition: &raw.BucketLifecycleRuleCondition{
- Age: r.Condition.AgeInDays,
- MatchesStorageClass: r.Condition.MatchesStorageClasses,
- NumNewerVersions: r.Condition.NumNewerVersions,
- },
- }
- switch r.Condition.Liveness {
- case LiveAndArchived:
- rr.Condition.IsLive = nil
- case Live:
- rr.Condition.IsLive = googleapi.Bool(true)
- case Archived:
- rr.Condition.IsLive = googleapi.Bool(false)
- }
- if !r.Condition.CreatedBefore.IsZero() {
- rr.Condition.CreatedBefore = r.Condition.CreatedBefore.Format(rfc3339Date)
- }
- rl.Rule = append(rl.Rule, rr)
- }
- return &rl
- }
- func toLifecycle(rl *raw.BucketLifecycle) Lifecycle {
- var l Lifecycle
- if rl == nil {
- return l
- }
- for _, rr := range rl.Rule {
- r := LifecycleRule{
- Action: LifecycleAction{
- Type: rr.Action.Type,
- StorageClass: rr.Action.StorageClass,
- },
- Condition: LifecycleCondition{
- AgeInDays: rr.Condition.Age,
- MatchesStorageClasses: rr.Condition.MatchesStorageClass,
- NumNewerVersions: rr.Condition.NumNewerVersions,
- },
- }
- switch {
- case rr.Condition.IsLive == nil:
- r.Condition.Liveness = LiveAndArchived
- case *rr.Condition.IsLive == true:
- r.Condition.Liveness = Live
- case *rr.Condition.IsLive == false:
- r.Condition.Liveness = Archived
- }
- if rr.Condition.CreatedBefore != "" {
- r.Condition.CreatedBefore, _ = time.Parse(rfc3339Date, rr.Condition.CreatedBefore)
- }
- l.Rules = append(l.Rules, r)
- }
- return l
- }
- func (e *BucketEncryption) toRawBucketEncryption() *raw.BucketEncryption {
- if e == nil {
- return nil
- }
- return &raw.BucketEncryption{
- DefaultKmsKeyName: e.DefaultKMSKeyName,
- }
- }
- func toBucketEncryption(e *raw.BucketEncryption) *BucketEncryption {
- if e == nil {
- return nil
- }
- return &BucketEncryption{DefaultKMSKeyName: e.DefaultKmsKeyName}
- }
- // Objects returns an iterator over the objects in the bucket that match the Query q.
- // If q is nil, no filtering is done.
- func (b *BucketHandle) Objects(ctx context.Context, q *Query) *ObjectIterator {
- it := &ObjectIterator{
- ctx: ctx,
- bucket: b,
- }
- it.pageInfo, it.nextFunc = iterator.NewPageInfo(
- it.fetch,
- func() int { return len(it.items) },
- func() interface{} { b := it.items; it.items = nil; return b })
- if q != nil {
- it.query = *q
- }
- return it
- }
- // An ObjectIterator is an iterator over ObjectAttrs.
- type ObjectIterator struct {
- ctx context.Context
- bucket *BucketHandle
- query Query
- pageInfo *iterator.PageInfo
- nextFunc func() error
- items []*ObjectAttrs
- }
- // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
- func (it *ObjectIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
- // Next returns the next result. Its second return value is iterator.Done if
- // there are no more results. Once Next returns iterator.Done, all subsequent
- // calls will return iterator.Done.
- //
- // If Query.Delimiter is non-empty, some of the ObjectAttrs returned by Next will
- // have a non-empty Prefix field, and a zero value for all other fields. These
- // represent prefixes.
- func (it *ObjectIterator) Next() (*ObjectAttrs, error) {
- if err := it.nextFunc(); err != nil {
- return nil, err
- }
- item := it.items[0]
- it.items = it.items[1:]
- return item, nil
- }
- func (it *ObjectIterator) fetch(pageSize int, pageToken string) (string, error) {
- req := it.bucket.c.raw.Objects.List(it.bucket.name)
- setClientHeader(req.Header())
- req.Projection("full")
- req.Delimiter(it.query.Delimiter)
- req.Prefix(it.query.Prefix)
- req.Versions(it.query.Versions)
- req.PageToken(pageToken)
- if it.bucket.userProject != "" {
- req.UserProject(it.bucket.userProject)
- }
- if pageSize > 0 {
- req.MaxResults(int64(pageSize))
- }
- var resp *raw.Objects
- var err error
- err = runWithRetry(it.ctx, func() error {
- resp, err = req.Context(it.ctx).Do()
- return err
- })
- if err != nil {
- if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
- err = ErrBucketNotExist
- }
- return "", err
- }
- for _, item := range resp.Items {
- it.items = append(it.items, newObject(item))
- }
- for _, prefix := range resp.Prefixes {
- it.items = append(it.items, &ObjectAttrs{Prefix: prefix})
- }
- return resp.NextPageToken, nil
- }
- // Buckets returns an iterator over the buckets in the project. You may
- // optionally set the iterator's Prefix field to restrict the list to buckets
- // whose names begin with the prefix. By default, all buckets in the project
- // are returned.
- func (c *Client) Buckets(ctx context.Context, projectID string) *BucketIterator {
- it := &BucketIterator{
- ctx: ctx,
- client: c,
- projectID: projectID,
- }
- it.pageInfo, it.nextFunc = iterator.NewPageInfo(
- it.fetch,
- func() int { return len(it.buckets) },
- func() interface{} { b := it.buckets; it.buckets = nil; return b })
- return it
- }
- // A BucketIterator is an iterator over BucketAttrs.
- type BucketIterator struct {
- // Prefix restricts the iterator to buckets whose names begin with it.
- Prefix string
- ctx context.Context
- client *Client
- projectID string
- buckets []*BucketAttrs
- pageInfo *iterator.PageInfo
- nextFunc func() error
- }
- // Next returns the next result. Its second return value is iterator.Done if
- // there are no more results. Once Next returns iterator.Done, all subsequent
- // calls will return iterator.Done.
- func (it *BucketIterator) Next() (*BucketAttrs, error) {
- if err := it.nextFunc(); err != nil {
- return nil, err
- }
- b := it.buckets[0]
- it.buckets = it.buckets[1:]
- return b, nil
- }
- // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
- func (it *BucketIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
- func (it *BucketIterator) fetch(pageSize int, pageToken string) (token string, err error) {
- req := it.client.raw.Buckets.List(it.projectID)
- setClientHeader(req.Header())
- req.Projection("full")
- req.Prefix(it.Prefix)
- req.PageToken(pageToken)
- if pageSize > 0 {
- req.MaxResults(int64(pageSize))
- }
- var resp *raw.Buckets
- err = runWithRetry(it.ctx, func() error {
- resp, err = req.Context(it.ctx).Do()
- return err
- })
- if err != nil {
- return "", err
- }
- for _, item := range resp.Items {
- b, err := newBucket(item)
- if err != nil {
- return "", err
- }
- it.buckets = append(it.buckets, b)
- }
- return resp.NextPageToken, nil
- }
|