| // Copyright 2014 Google LLC |
| // |
| // 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 ( |
| "context" |
| "encoding/base64" |
| "encoding/json" |
| "errors" |
| "fmt" |
| "reflect" |
| "strings" |
| "time" |
| |
| "cloud.google.com/go/compute/metadata" |
| "cloud.google.com/go/internal/optional" |
| "cloud.google.com/go/internal/trace" |
| "cloud.google.com/go/storage/internal/apiv2/storagepb" |
| "google.golang.org/api/googleapi" |
| "google.golang.org/api/iamcredentials/v1" |
| "google.golang.org/api/iterator" |
| "google.golang.org/api/option" |
| raw "google.golang.org/api/storage/v1" |
| dpb "google.golang.org/genproto/googleapis/type/date" |
| "google.golang.org/protobuf/proto" |
| "google.golang.org/protobuf/types/known/durationpb" |
| ) |
| |
| // 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 |
| retry *retryConfig |
| enableObjectRetention *bool |
| } |
| |
| // 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 { |
| retry := c.retry.clone() |
| return &BucketHandle{ |
| c: c, |
| name: name, |
| acl: ACLHandle{ |
| c: c, |
| bucket: name, |
| retry: retry, |
| }, |
| defaultObjectACL: ACLHandle{ |
| c: c, |
| bucket: name, |
| isDefault: true, |
| retry: retry, |
| }, |
| retry: retry, |
| } |
| } |
| |
| // 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) }() |
| |
| o := makeStorageOpts(true, b.retry, b.userProject) |
| |
| if _, err := b.c.tc.CreateBucket(ctx, projectID, b.name, attrs, b.enableObjectRetention, o...); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| // 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) }() |
| |
| o := makeStorageOpts(true, b.retry, b.userProject) |
| return b.c.tc.DeleteBucket(ctx, b.name, b.conds, o...) |
| } |
| |
| // 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 |
| } |
| |
| // BucketName returns the name of the bucket. |
| func (b *BucketHandle) BucketName() string { |
| return b.name |
| } |
| |
| // Object returns an ObjectHandle, which provides operations on the named object. |
| // This call does not perform any network operations such as fetching the object or verifying its existence. |
| // Use methods on ObjectHandle to perform 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/naming-objects |
| func (b *BucketHandle) Object(name string) *ObjectHandle { |
| retry := b.retry.clone() |
| return &ObjectHandle{ |
| c: b.c, |
| bucket: b.name, |
| object: name, |
| acl: ACLHandle{ |
| c: b.c, |
| bucket: b.name, |
| object: name, |
| userProject: b.userProject, |
| retry: retry, |
| }, |
| gen: -1, |
| userProject: b.userProject, |
| retry: retry, |
| } |
| } |
| |
| // 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) }() |
| |
| o := makeStorageOpts(true, b.retry, b.userProject) |
| return b.c.tc.GetBucket(ctx, b.name, b.conds, o...) |
| } |
| |
| // Update updates a bucket's attributes. |
| func (b *BucketHandle) Update(ctx context.Context, uattrs BucketAttrsToUpdate) (attrs *BucketAttrs, err error) { |
| ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Bucket.Update") |
| defer func() { trace.EndSpan(ctx, err) }() |
| |
| isIdempotent := b.conds != nil && b.conds.MetagenerationMatch != 0 |
| o := makeStorageOpts(isIdempotent, b.retry, b.userProject) |
| return b.c.tc.UpdateBucket(ctx, b.name, &uattrs, b.conds, o...) |
| } |
| |
| // SignedURL returns a URL for the specified object. Signed URLs allow anyone |
| // access to a restricted resource for a limited time without needing a Google |
| // account or signing in. |
| // For more information about signed URLs, see "[Overview of access control]." |
| // |
| // This method requires the Method and Expires fields in the specified |
| // SignedURLOptions to be non-nil. You may need to set the GoogleAccessID and |
| // PrivateKey fields in some cases. Read more on the [automatic detection of credentials] |
| // for this method. |
| // |
| // [Overview of access control]: https://cloud.google.com/storage/docs/accesscontrol#signed_urls_query_string_authentication |
| // [automatic detection of credentials]: https://pkg.go.dev/cloud.google.com/go/storage#hdr-Credential_requirements_for_signing |
| func (b *BucketHandle) SignedURL(object string, opts *SignedURLOptions) (string, error) { |
| // Make a copy of opts so we don't modify the pointer parameter. |
| newopts := opts.clone() |
| |
| if newopts.Hostname == "" { |
| // Extract the correct host from the readhost set on the client |
| newopts.Hostname = b.c.xmlHost |
| } |
| |
| if opts.GoogleAccessID != "" && (opts.SignBytes != nil || len(opts.PrivateKey) > 0) { |
| return SignedURL(b.name, object, newopts) |
| } |
| |
| if newopts.GoogleAccessID == "" { |
| id, err := b.detectDefaultGoogleAccessID() |
| if err != nil { |
| return "", err |
| } |
| newopts.GoogleAccessID = id |
| } |
| if newopts.SignBytes == nil && len(newopts.PrivateKey) == 0 { |
| if b.c.creds != nil && len(b.c.creds.JSON) > 0 { |
| var sa struct { |
| PrivateKey string `json:"private_key"` |
| } |
| err := json.Unmarshal(b.c.creds.JSON, &sa) |
| if err == nil && sa.PrivateKey != "" { |
| newopts.PrivateKey = []byte(sa.PrivateKey) |
| } |
| } |
| |
| // Don't error out if we can't unmarshal the private key from the client, |
| // fallback to the default sign function for the service account. |
| if len(newopts.PrivateKey) == 0 { |
| newopts.SignBytes = b.defaultSignBytesFunc(newopts.GoogleAccessID) |
| } |
| } |
| return SignedURL(b.name, object, newopts) |
| } |
| |
| // GenerateSignedPostPolicyV4 generates a PostPolicyV4 value from bucket, object and opts. |
| // The generated URL and fields will then allow an unauthenticated client to perform multipart uploads. |
| // |
| // This method requires the Expires field in the specified PostPolicyV4Options |
| // to be non-nil. You may need to set the GoogleAccessID and PrivateKey fields |
| // in some cases. Read more on the [automatic detection of credentials] for this method. |
| // |
| // [automatic detection of credentials]: https://pkg.go.dev/cloud.google.com/go/storage#hdr-Credential_requirements_for_signing |
| func (b *BucketHandle) GenerateSignedPostPolicyV4(object string, opts *PostPolicyV4Options) (*PostPolicyV4, error) { |
| // Make a copy of opts so we don't modify the pointer parameter. |
| newopts := opts.clone() |
| |
| if newopts.Hostname == "" { |
| // Extract the correct host from the readhost set on the client |
| newopts.Hostname = b.c.xmlHost |
| } |
| |
| if opts.GoogleAccessID != "" && (opts.SignRawBytes != nil || opts.SignBytes != nil || len(opts.PrivateKey) > 0) { |
| return GenerateSignedPostPolicyV4(b.name, object, newopts) |
| } |
| |
| if newopts.GoogleAccessID == "" { |
| id, err := b.detectDefaultGoogleAccessID() |
| if err != nil { |
| return nil, err |
| } |
| newopts.GoogleAccessID = id |
| } |
| if newopts.SignBytes == nil && newopts.SignRawBytes == nil && len(newopts.PrivateKey) == 0 { |
| if b.c.creds != nil && len(b.c.creds.JSON) > 0 { |
| var sa struct { |
| PrivateKey string `json:"private_key"` |
| } |
| err := json.Unmarshal(b.c.creds.JSON, &sa) |
| if err == nil && sa.PrivateKey != "" { |
| newopts.PrivateKey = []byte(sa.PrivateKey) |
| } |
| } |
| |
| // Don't error out if we can't unmarshal the private key from the client, |
| // fallback to the default sign function for the service account. |
| if len(newopts.PrivateKey) == 0 { |
| newopts.SignRawBytes = b.defaultSignBytesFunc(newopts.GoogleAccessID) |
| } |
| } |
| return GenerateSignedPostPolicyV4(b.name, object, newopts) |
| } |
| |
| func (b *BucketHandle) detectDefaultGoogleAccessID() (string, error) { |
| returnErr := errors.New("no credentials found on client and not on GCE (Google Compute Engine)") |
| |
| if b.c.creds != nil && len(b.c.creds.JSON) > 0 { |
| var sa struct { |
| ClientEmail string `json:"client_email"` |
| SAImpersonationURL string `json:"service_account_impersonation_url"` |
| CredType string `json:"type"` |
| } |
| |
| err := json.Unmarshal(b.c.creds.JSON, &sa) |
| if err != nil { |
| returnErr = err |
| } else { |
| switch sa.CredType { |
| case "impersonated_service_account", "external_account": |
| start, end := strings.LastIndex(sa.SAImpersonationURL, "/"), strings.LastIndex(sa.SAImpersonationURL, ":") |
| |
| if end <= start { |
| returnErr = errors.New("error parsing external or impersonated service account credentials") |
| } else { |
| return sa.SAImpersonationURL[start+1 : end], nil |
| } |
| case "service_account": |
| if sa.ClientEmail != "" { |
| return sa.ClientEmail, nil |
| } |
| returnErr = errors.New("empty service account client email") |
| default: |
| returnErr = errors.New("unable to parse credentials; only service_account, external_account and impersonated_service_account credentials are supported") |
| } |
| } |
| } |
| |
| // Don't error out if we can't unmarshal, fallback to GCE check. |
| if metadata.OnGCE() { |
| email, err := metadata.Email("default") |
| if err == nil && email != "" { |
| return email, nil |
| } else if err != nil { |
| returnErr = err |
| } else { |
| returnErr = errors.New("empty email from GCE metadata service") |
| } |
| |
| } |
| return "", fmt.Errorf("storage: unable to detect default GoogleAccessID: %w. Please provide the GoogleAccessID or use a supported means for autodetecting it (see https://pkg.go.dev/cloud.google.com/go/storage#hdr-Credential_requirements_for_signing)", returnErr) |
| } |
| |
| func (b *BucketHandle) defaultSignBytesFunc(email string) func([]byte) ([]byte, error) { |
| return func(in []byte) ([]byte, error) { |
| ctx := context.Background() |
| |
| // It's ok to recreate this service per call since we pass in the http client, |
| // circumventing the cost of recreating the auth/transport layer |
| svc, err := iamcredentials.NewService(ctx, option.WithHTTPClient(b.c.hc)) |
| if err != nil { |
| return nil, fmt.Errorf("unable to create iamcredentials client: %w", err) |
| } |
| |
| resp, err := svc.Projects.ServiceAccounts.SignBlob(fmt.Sprintf("projects/-/serviceAccounts/%s", email), &iamcredentials.SignBlobRequest{ |
| Payload: base64.StdEncoding.EncodeToString(in), |
| }).Do() |
| if err != nil { |
| return nil, fmt.Errorf("unable to sign bytes: %w", err) |
| } |
| out, err := base64.StdEncoding.DecodeString(resp.SignedBlob) |
| if err != nil { |
| return nil, fmt.Errorf("unable to base64 decode response: %w", err) |
| } |
| return out, 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 |
| |
| // BucketPolicyOnly is an alias for UniformBucketLevelAccess. Use of |
| // UniformBucketLevelAccess is recommended above the use of this field. |
| // Setting BucketPolicyOnly.Enabled OR UniformBucketLevelAccess.Enabled to |
| // true, will enable UniformBucketLevelAccess. |
| BucketPolicyOnly BucketPolicyOnly |
| |
| // UniformBucketLevelAccess configures access checks to use only bucket-level IAM |
| // policies and ignore any ACL rules for the bucket. |
| // See https://cloud.google.com/storage/docs/uniform-bucket-level-access |
| // for more information. |
| UniformBucketLevelAccess UniformBucketLevelAccess |
| |
| // PublicAccessPrevention is the setting for the bucket's |
| // PublicAccessPrevention policy, which can be used to prevent public access |
| // of data in the bucket. See |
| // https://cloud.google.com/storage/docs/public-access-prevention for more |
| // information. |
| PublicAccessPrevention PublicAccessPrevention |
| |
| // DefaultObjectACL is the list of access controls to |
| // apply to new objects when no object ACL is provided. |
| DefaultObjectACL []ACLRule |
| |
| // DefaultEventBasedHold is the default value for event-based hold on |
| // newly created objects in this bucket. It defaults to false. |
| DefaultEventBasedHold bool |
| |
| // If not empty, applies a predefined set of access controls. It should be set |
| // only when creating a bucket. |
| // It is always empty for BucketAttrs returned from the service. |
| // See https://cloud.google.com/storage/docs/json_api/v1/buckets/insert |
| // for valid values. |
| PredefinedACL string |
| |
| // If not empty, applies a predefined set of default object access controls. |
| // It should be set only when creating a bucket. |
| // It is always empty for BucketAttrs returned from the service. |
| // See https://cloud.google.com/storage/docs/json_api/v1/buckets/insert |
| // for valid values. |
| PredefinedDefaultObjectACL string |
| |
| // Location is the location of the bucket. It defaults to "US". |
| // If specifying a dual-region, CustomPlacementConfig should be set in conjunction. |
| Location string |
| |
| // The bucket's custom placement configuration that holds a list of |
| // regional locations for custom dual regions. |
| CustomPlacementConfig *CustomPlacementConfig |
| |
| // 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 "STANDARD", "NEARLINE", |
| // "COLDLINE" and "ARCHIVE". Defaults to "STANDARD". |
| // See https://cloud.google.com/storage/docs/storage-classes for all |
| // valid values. |
| 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 |
| |
| // The logging configuration. |
| Logging *BucketLogging |
| |
| // The website configuration. |
| Website *BucketWebsite |
| |
| // Etag is the HTTP/1.1 Entity tag for the bucket. |
| // This field is read-only. |
| Etag string |
| |
| // LocationType describes how data is stored and replicated. |
| // Typical values are "multi-region", "region" and "dual-region". |
| // This field is read-only. |
| LocationType string |
| |
| // The project number of the project the bucket belongs to. |
| // This field is read-only. |
| ProjectNumber uint64 |
| |
| // RPO configures the Recovery Point Objective (RPO) policy of the bucket. |
| // Set to RPOAsyncTurbo to turn on Turbo Replication for a bucket. |
| // See https://cloud.google.com/storage/docs/managing-turbo-replication for |
| // more information. |
| RPO RPO |
| |
| // Autoclass holds the bucket's autoclass configuration. If enabled, |
| // allows for the automatic selection of the best storage class |
| // based on object access patterns. |
| Autoclass *Autoclass |
| |
| // ObjectRetentionMode reports whether individual objects in the bucket can |
| // be configured with a retention policy. An empty value means that object |
| // retention is disabled. |
| // This field is read-only. Object retention can be enabled only by creating |
| // a bucket with SetObjectRetention set to true on the BucketHandle. It |
| // cannot be modified once the bucket is created. |
| // ObjectRetention cannot be configured or reported through the gRPC API. |
| ObjectRetentionMode string |
| |
| // SoftDeletePolicy contains the bucket's soft delete policy, which defines |
| // the period of time that soft-deleted objects will be retained, and cannot |
| // be permanently deleted. By default, new buckets will be created with a |
| // 7 day retention duration. In order to fully disable soft delete, you need |
| // to set a policy with a RetentionDuration of 0. |
| SoftDeletePolicy *SoftDeletePolicy |
| |
| // HierarchicalNamespace contains the bucket's hierarchical namespace |
| // configuration. Hierarchical namespace enabled buckets can contain |
| // [cloud.google.com/go/storage/control/apiv2/controlpb.Folder] resources. |
| // It cannot be modified after bucket creation time. |
| // UniformBucketLevelAccess must also also be enabled on the bucket. |
| HierarchicalNamespace *HierarchicalNamespace |
| } |
| |
| // BucketPolicyOnly is an alias for UniformBucketLevelAccess. |
| // Use of UniformBucketLevelAccess is preferred above BucketPolicyOnly. |
| type BucketPolicyOnly struct { |
| // Enabled specifies whether access checks use only bucket-level IAM |
| // policies. Enabled may be disabled until the locked time. |
| Enabled bool |
| // LockedTime specifies the deadline for changing Enabled from true to |
| // false. |
| LockedTime time.Time |
| } |
| |
| // UniformBucketLevelAccess configures access checks to use only bucket-level IAM |
| // policies. |
| type UniformBucketLevelAccess struct { |
| // Enabled specifies whether access checks use only bucket-level IAM |
| // policies. Enabled may be disabled until the locked time. |
| Enabled bool |
| // LockedTime specifies the deadline for changing Enabled from true to |
| // false. |
| LockedTime time.Time |
| } |
| |
| // PublicAccessPrevention configures the Public Access Prevention feature, which |
| // can be used to disallow public access to any data in a bucket. See |
| // https://cloud.google.com/storage/docs/public-access-prevention for more |
| // information. |
| type PublicAccessPrevention int |
| |
| const ( |
| // PublicAccessPreventionUnknown is a zero value, used only if this field is |
| // not set in a call to GCS. |
| PublicAccessPreventionUnknown PublicAccessPrevention = iota |
| |
| // PublicAccessPreventionUnspecified corresponds to a value of "unspecified". |
| // Deprecated: use PublicAccessPreventionInherited |
| PublicAccessPreventionUnspecified |
| |
| // PublicAccessPreventionEnforced corresponds to a value of "enforced". This |
| // enforces Public Access Prevention on the bucket. |
| PublicAccessPreventionEnforced |
| |
| // PublicAccessPreventionInherited corresponds to a value of "inherited" |
| // and is the default for buckets. |
| PublicAccessPreventionInherited |
| |
| publicAccessPreventionUnknown string = "" |
| // TODO: remove unspecified when change is fully completed |
| publicAccessPreventionUnspecified = "unspecified" |
| publicAccessPreventionEnforced = "enforced" |
| publicAccessPreventionInherited = "inherited" |
| ) |
| |
| func (p PublicAccessPrevention) String() string { |
| switch p { |
| case PublicAccessPreventionInherited, PublicAccessPreventionUnspecified: |
| return publicAccessPreventionInherited |
| case PublicAccessPreventionEnforced: |
| return publicAccessPreventionEnforced |
| default: |
| return publicAccessPreventionUnknown |
| } |
| } |
| |
| // Lifecycle is the lifecycle configuration for objects in the bucket. |
| type Lifecycle struct { |
| Rules []LifecycleRule |
| } |
| |
| // RetentionPolicy 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 |
| |
| // IsLocked describes whether the bucket is locked. Once locked, an |
| // object retention policy cannot be modified. |
| // This field is read-only. |
| IsLocked bool |
| } |
| |
| const ( |
| // RFC3339 timestamp with only the date segment, used for CreatedBefore, |
| // CustomTimeBefore, and NoncurrentTimeBefore in LifecycleRule. |
| rfc3339Date = "2006-01-02" |
| |
| // DeleteAction is a lifecycle action that deletes a live and/or archived |
| // objects. Takes precedence over SetStorageClass actions. |
| DeleteAction = "Delete" |
| |
| // SetStorageClassAction changes the storage class of live and/or archived |
| // objects. |
| SetStorageClassAction = "SetStorageClass" |
| |
| // AbortIncompleteMPUAction is a lifecycle action that aborts an incomplete |
| // multipart upload when the multipart upload meets the conditions specified |
| // in the lifecycle rule. The AgeInDays condition is the only allowed |
| // condition for this action. AgeInDays is measured from the time the |
| // multipart upload was created. |
| AbortIncompleteMPUAction = "AbortIncompleteMultipartUpload" |
| ) |
| |
| // 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 storage.DeleteAction, storage.SetStorageClassAction, |
| // and storage.AbortIncompleteMPUAction. |
| 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 { |
| // AllObjects is used to select all objects in a bucket by |
| // setting AgeInDays to 0. |
| AllObjects bool |
| |
| // AgeInDays is the age of the object in days. |
| // If you want to set AgeInDays to `0` use AllObjects set to `true`. |
| 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 |
| |
| // CustomTimeBefore is the CustomTime metadata field of the object. This |
| // condition is satisfied when an object's CustomTime timestamp is before |
| // midnight of the specified date in UTC. |
| // |
| // This condition can only be satisfied if CustomTime has been set. |
| CustomTimeBefore time.Time |
| |
| // DaysSinceCustomTime is the days elapsed since the CustomTime date of the |
| // object. This condition can only be satisfied if CustomTime has been set. |
| // Note: Using `0` as the value will be ignored by the library and not sent to the API. |
| DaysSinceCustomTime int64 |
| |
| // DaysSinceNoncurrentTime is the days elapsed since the noncurrent timestamp |
| // of the object. This condition is relevant only for versioned objects. |
| // Note: Using `0` as the value will be ignored by the library and not sent to the API. |
| DaysSinceNoncurrentTime int64 |
| |
| // Liveness specifies the object's liveness. Relevant only for versioned objects |
| Liveness Liveness |
| |
| // MatchesPrefix is the condition matching an object if any of the |
| // matches_prefix strings are an exact prefix of the object's name. |
| MatchesPrefix []string |
| |
| // MatchesStorageClasses is the condition matching the object's storage |
| // class. |
| // |
| // Values include "STANDARD", "NEARLINE", "COLDLINE" and "ARCHIVE". |
| MatchesStorageClasses []string |
| |
| // MatchesSuffix is the condition matching an object if any of the |
| // matches_suffix strings are an exact suffix of the object's name. |
| MatchesSuffix []string |
| |
| // NoncurrentTimeBefore is the noncurrent timestamp of the object. This |
| // condition is satisfied when an object's noncurrent timestamp is before |
| // midnight of the specified date in UTC. |
| // |
| // This condition is relevant only for versioned objects. |
| NoncurrentTimeBefore time.Time |
| |
| // 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. |
| // Note: Using `0` as the value will be ignored by the library and not sent to the API. |
| NumNewerVersions int64 |
| } |
| |
| // BucketLogging holds the bucket's logging configuration, which defines the |
| // destination bucket and optional name prefix for the current bucket's |
| // logs. |
| type BucketLogging struct { |
| // The destination bucket where the current bucket's logs |
| // should be placed. |
| LogBucket string |
| |
| // A prefix for log object names. |
| LogObjectPrefix string |
| } |
| |
| // BucketWebsite holds the bucket's website configuration, controlling how the |
| // service behaves when accessing bucket contents as a web site. See |
| // https://cloud.google.com/storage/docs/static-website for more information. |
| type BucketWebsite struct { |
| // If the requested object path is missing, the service will ensure the path has |
| // a trailing '/', append this suffix, and attempt to retrieve the resulting |
| // object. This allows the creation of index.html objects to represent directory |
| // pages. |
| MainPageSuffix string |
| |
| // If the requested object path is missing, and any mainPageSuffix object is |
| // missing, if applicable, the service will return the named object from this |
| // bucket as the content for a 404 Not Found result. |
| NotFoundPage string |
| } |
| |
| // CustomPlacementConfig holds the bucket's custom placement |
| // configuration for Custom Dual Regions. See |
| // https://cloud.google.com/storage/docs/locations#location-dr for more information. |
| type CustomPlacementConfig struct { |
| // The list of regional locations in which data is placed. |
| // Custom Dual Regions require exactly 2 regional locations. |
| DataLocations []string |
| } |
| |
| // Autoclass holds the bucket's autoclass configuration. If enabled, |
| // allows for the automatic selection of the best storage class |
| // based on object access patterns. See |
| // https://cloud.google.com/storage/docs/using-autoclass for more information. |
| type Autoclass struct { |
| // Enabled specifies whether the autoclass feature is enabled |
| // on the bucket. |
| Enabled bool |
| // ToggleTime is the time from which Autoclass was last toggled. |
| // If Autoclass is enabled when the bucket is created, the ToggleTime |
| // is set to the bucket creation time. This field is read-only. |
| ToggleTime time.Time |
| // TerminalStorageClass: The storage class that objects in the bucket |
| // eventually transition to if they are not read for a certain length of |
| // time. Valid values are NEARLINE and ARCHIVE. |
| // To modify TerminalStorageClass, Enabled must be set to true. |
| TerminalStorageClass string |
| // TerminalStorageClassUpdateTime represents the time of the most recent |
| // update to "TerminalStorageClass". |
| TerminalStorageClassUpdateTime time.Time |
| } |
| |
| // SoftDeletePolicy contains the bucket's soft delete policy, which defines the |
| // period of time that soft-deleted objects will be retained, and cannot be |
| // permanently deleted. |
| type SoftDeletePolicy struct { |
| // EffectiveTime indicates the time from which the policy, or one with a |
| // greater retention, was effective. This field is read-only. |
| EffectiveTime time.Time |
| |
| // RetentionDuration is the amount of time that soft-deleted objects in the |
| // bucket will be retained and cannot be permanently deleted. |
| RetentionDuration time.Duration |
| } |
| |
| // HierarchicalNamespace contains the bucket's hierarchical namespace |
| // configuration. Hierarchical namespace enabled buckets can contain |
| // [cloud.google.com/go/storage/control/apiv2/controlpb.Folder] resources. |
| type HierarchicalNamespace struct { |
| // Enabled indicates whether hierarchical namespace features are enabled on |
| // the bucket. This can only be set at bucket creation time currently. |
| Enabled bool |
| } |
| |
| func newBucket(b *raw.Bucket) (*BucketAttrs, error) { |
| if b == nil { |
| return nil, nil |
| } |
| rp, err := toRetentionPolicy(b.RetentionPolicy) |
| if err != nil { |
| return nil, err |
| } |
| |
| return &BucketAttrs{ |
| Name: b.Name, |
| Location: b.Location, |
| MetaGeneration: b.Metageneration, |
| DefaultEventBasedHold: b.DefaultEventBasedHold, |
| StorageClass: b.StorageClass, |
| Created: convertTime(b.TimeCreated), |
| VersioningEnabled: b.Versioning != nil && b.Versioning.Enabled, |
| ACL: toBucketACLRules(b.Acl), |
| DefaultObjectACL: toObjectACLRules(b.DefaultObjectAcl), |
| Labels: b.Labels, |
| RequesterPays: b.Billing != nil && b.Billing.RequesterPays, |
| Lifecycle: toLifecycle(b.Lifecycle), |
| RetentionPolicy: rp, |
| ObjectRetentionMode: toBucketObjectRetention(b.ObjectRetention), |
| CORS: toCORS(b.Cors), |
| Encryption: toBucketEncryption(b.Encryption), |
| Logging: toBucketLogging(b.Logging), |
| Website: toBucketWebsite(b.Website), |
| BucketPolicyOnly: toBucketPolicyOnly(b.IamConfiguration), |
| UniformBucketLevelAccess: toUniformBucketLevelAccess(b.IamConfiguration), |
| PublicAccessPrevention: toPublicAccessPrevention(b.IamConfiguration), |
| Etag: b.Etag, |
| LocationType: b.LocationType, |
| ProjectNumber: b.ProjectNumber, |
| RPO: toRPO(b), |
| CustomPlacementConfig: customPlacementFromRaw(b.CustomPlacementConfig), |
| Autoclass: toAutoclassFromRaw(b.Autoclass), |
| SoftDeletePolicy: toSoftDeletePolicyFromRaw(b.SoftDeletePolicy), |
| HierarchicalNamespace: toHierarchicalNamespaceFromRaw(b.HierarchicalNamespace), |
| }, nil |
| } |
| |
| func newBucketFromProto(b *storagepb.Bucket) *BucketAttrs { |
| if b == nil { |
| return nil |
| } |
| return &BucketAttrs{ |
| Name: parseBucketName(b.GetName()), |
| Location: b.GetLocation(), |
| MetaGeneration: b.GetMetageneration(), |
| DefaultEventBasedHold: b.GetDefaultEventBasedHold(), |
| StorageClass: b.GetStorageClass(), |
| Created: b.GetCreateTime().AsTime(), |
| VersioningEnabled: b.GetVersioning().GetEnabled(), |
| ACL: toBucketACLRulesFromProto(b.GetAcl()), |
| DefaultObjectACL: toObjectACLRulesFromProto(b.GetDefaultObjectAcl()), |
| Labels: b.GetLabels(), |
| RequesterPays: b.GetBilling().GetRequesterPays(), |
| Lifecycle: toLifecycleFromProto(b.GetLifecycle()), |
| RetentionPolicy: toRetentionPolicyFromProto(b.GetRetentionPolicy()), |
| CORS: toCORSFromProto(b.GetCors()), |
| Encryption: toBucketEncryptionFromProto(b.GetEncryption()), |
| Logging: toBucketLoggingFromProto(b.GetLogging()), |
| Website: toBucketWebsiteFromProto(b.GetWebsite()), |
| BucketPolicyOnly: toBucketPolicyOnlyFromProto(b.GetIamConfig()), |
| UniformBucketLevelAccess: toUniformBucketLevelAccessFromProto(b.GetIamConfig()), |
| PublicAccessPrevention: toPublicAccessPreventionFromProto(b.GetIamConfig()), |
| LocationType: b.GetLocationType(), |
| RPO: toRPOFromProto(b), |
| CustomPlacementConfig: customPlacementFromProto(b.GetCustomPlacementConfig()), |
| ProjectNumber: parseProjectNumber(b.GetProject()), // this can return 0 the project resource name is ID based |
| Autoclass: toAutoclassFromProto(b.GetAutoclass()), |
| SoftDeletePolicy: toSoftDeletePolicyFromProto(b.SoftDeletePolicy), |
| HierarchicalNamespace: toHierarchicalNamespaceFromProto(b.HierarchicalNamespace), |
| } |
| } |
| |
| // toRawBucket copies the editable attribute from b to the raw library's Bucket type. |
| func (b *BucketAttrs) toRawBucket() *raw.Bucket { |
| // 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} |
| } |
| var bktIAM *raw.BucketIamConfiguration |
| if b.UniformBucketLevelAccess.Enabled || b.BucketPolicyOnly.Enabled || b.PublicAccessPrevention != PublicAccessPreventionUnknown { |
| bktIAM = &raw.BucketIamConfiguration{} |
| if b.UniformBucketLevelAccess.Enabled || b.BucketPolicyOnly.Enabled { |
| bktIAM.UniformBucketLevelAccess = &raw.BucketIamConfigurationUniformBucketLevelAccess{ |
| Enabled: true, |
| } |
| } |
| if b.PublicAccessPrevention != PublicAccessPreventionUnknown { |
| bktIAM.PublicAccessPrevention = b.PublicAccessPrevention.String() |
| } |
| } |
| return &raw.Bucket{ |
| Name: b.Name, |
| Location: b.Location, |
| StorageClass: b.StorageClass, |
| Acl: toRawBucketACL(b.ACL), |
| DefaultObjectAcl: toRawObjectACL(b.DefaultObjectACL), |
| Versioning: v, |
| Labels: labels, |
| Billing: bb, |
| Lifecycle: toRawLifecycle(b.Lifecycle), |
| RetentionPolicy: b.RetentionPolicy.toRawRetentionPolicy(), |
| Cors: toRawCORS(b.CORS), |
| Encryption: b.Encryption.toRawBucketEncryption(), |
| Logging: b.Logging.toRawBucketLogging(), |
| Website: b.Website.toRawBucketWebsite(), |
| IamConfiguration: bktIAM, |
| Rpo: b.RPO.String(), |
| CustomPlacementConfig: b.CustomPlacementConfig.toRawCustomPlacement(), |
| Autoclass: b.Autoclass.toRawAutoclass(), |
| SoftDeletePolicy: b.SoftDeletePolicy.toRawSoftDeletePolicy(), |
| HierarchicalNamespace: b.HierarchicalNamespace.toRawHierarchicalNamespace(), |
| } |
| } |
| |
| func (b *BucketAttrs) toProtoBucket() *storagepb.Bucket { |
| if b == nil { |
| return &storagepb.Bucket{} |
| } |
| |
| // 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 *storagepb.Bucket_Versioning |
| if b.VersioningEnabled { |
| v = &storagepb.Bucket_Versioning{Enabled: true} |
| } |
| var bb *storagepb.Bucket_Billing |
| if b.RequesterPays { |
| bb = &storagepb.Bucket_Billing{RequesterPays: true} |
| } |
| var bktIAM *storagepb.Bucket_IamConfig |
| if b.UniformBucketLevelAccess.Enabled || b.BucketPolicyOnly.Enabled || b.PublicAccessPrevention != PublicAccessPreventionUnknown { |
| bktIAM = &storagepb.Bucket_IamConfig{} |
| if b.UniformBucketLevelAccess.Enabled || b.BucketPolicyOnly.Enabled { |
| bktIAM.UniformBucketLevelAccess = &storagepb.Bucket_IamConfig_UniformBucketLevelAccess{ |
| Enabled: true, |
| } |
| } |
| if b.PublicAccessPrevention != PublicAccessPreventionUnknown { |
| bktIAM.PublicAccessPrevention = b.PublicAccessPrevention.String() |
| } |
| } |
| |
| return &storagepb.Bucket{ |
| Name: b.Name, |
| Location: b.Location, |
| StorageClass: b.StorageClass, |
| Acl: toProtoBucketACL(b.ACL), |
| DefaultObjectAcl: toProtoObjectACL(b.DefaultObjectACL), |
| Versioning: v, |
| Labels: labels, |
| Billing: bb, |
| Lifecycle: toProtoLifecycle(b.Lifecycle), |
| RetentionPolicy: b.RetentionPolicy.toProtoRetentionPolicy(), |
| Cors: toProtoCORS(b.CORS), |
| Encryption: b.Encryption.toProtoBucketEncryption(), |
| Logging: b.Logging.toProtoBucketLogging(), |
| Website: b.Website.toProtoBucketWebsite(), |
| IamConfig: bktIAM, |
| Rpo: b.RPO.String(), |
| CustomPlacementConfig: b.CustomPlacementConfig.toProtoCustomPlacement(), |
| Autoclass: b.Autoclass.toProtoAutoclass(), |
| SoftDeletePolicy: b.SoftDeletePolicy.toProtoSoftDeletePolicy(), |
| HierarchicalNamespace: b.HierarchicalNamespace.toProtoHierarchicalNamespace(), |
| } |
| } |
| |
| func (ua *BucketAttrsToUpdate) toProtoBucket() *storagepb.Bucket { |
| if ua == nil { |
| return &storagepb.Bucket{} |
| } |
| |
| var v *storagepb.Bucket_Versioning |
| if ua.VersioningEnabled != nil { |
| v = &storagepb.Bucket_Versioning{Enabled: optional.ToBool(ua.VersioningEnabled)} |
| } |
| var bb *storagepb.Bucket_Billing |
| if ua.RequesterPays != nil { |
| bb = &storagepb.Bucket_Billing{RequesterPays: optional.ToBool(ua.RequesterPays)} |
| } |
| |
| var bktIAM *storagepb.Bucket_IamConfig |
| if ua.UniformBucketLevelAccess != nil || ua.BucketPolicyOnly != nil || ua.PublicAccessPrevention != PublicAccessPreventionUnknown { |
| bktIAM = &storagepb.Bucket_IamConfig{} |
| |
| if ua.BucketPolicyOnly != nil { |
| bktIAM.UniformBucketLevelAccess = &storagepb.Bucket_IamConfig_UniformBucketLevelAccess{ |
| Enabled: optional.ToBool(ua.BucketPolicyOnly.Enabled), |
| } |
| } |
| |
| if ua.UniformBucketLevelAccess != nil { |
| // UniformBucketLevelAccess takes precedence over BucketPolicyOnly, |
| // so Enabled will be overriden here if both are set |
| bktIAM.UniformBucketLevelAccess = &storagepb.Bucket_IamConfig_UniformBucketLevelAccess{ |
| Enabled: optional.ToBool(ua.UniformBucketLevelAccess.Enabled), |
| } |
| } |
| |
| if ua.PublicAccessPrevention != PublicAccessPreventionUnknown { |
| bktIAM.PublicAccessPrevention = ua.PublicAccessPrevention.String() |
| } |
| } |
| |
| var defaultHold bool |
| if ua.DefaultEventBasedHold != nil { |
| defaultHold = optional.ToBool(ua.DefaultEventBasedHold) |
| } |
| var lifecycle Lifecycle |
| if ua.Lifecycle != nil { |
| lifecycle = *ua.Lifecycle |
| } |
| var bktACL []*storagepb.BucketAccessControl |
| if ua.acl != nil { |
| bktACL = toProtoBucketACL(ua.acl) |
| } |
| if ua.PredefinedACL != "" { |
| // Clear ACL or the call will fail. |
| bktACL = nil |
| } |
| var bktDefaultObjectACL []*storagepb.ObjectAccessControl |
| if ua.defaultObjectACL != nil { |
| bktDefaultObjectACL = toProtoObjectACL(ua.defaultObjectACL) |
| } |
| if ua.PredefinedDefaultObjectACL != "" { |
| // Clear ACLs or the call will fail. |
| bktDefaultObjectACL = nil |
| } |
| |
| return &storagepb.Bucket{ |
| StorageClass: ua.StorageClass, |
| Acl: bktACL, |
| DefaultObjectAcl: bktDefaultObjectACL, |
| DefaultEventBasedHold: defaultHold, |
| Versioning: v, |
| Billing: bb, |
| Lifecycle: toProtoLifecycle(lifecycle), |
| RetentionPolicy: ua.RetentionPolicy.toProtoRetentionPolicy(), |
| Cors: toProtoCORS(ua.CORS), |
| Encryption: ua.Encryption.toProtoBucketEncryption(), |
| Logging: ua.Logging.toProtoBucketLogging(), |
| Website: ua.Website.toProtoBucketWebsite(), |
| IamConfig: bktIAM, |
| Rpo: ua.RPO.String(), |
| Autoclass: ua.Autoclass.toProtoAutoclass(), |
| SoftDeletePolicy: ua.SoftDeletePolicy.toProtoSoftDeletePolicy(), |
| Labels: ua.setLabels, |
| } |
| } |
| |
| // 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 |
| } |
| |
| // BucketAttrsToUpdate define the attributes to update during an Update call. |
| 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 |
| |
| // DefaultEventBasedHold is the default value for event-based hold on |
| // newly created objects in this bucket. |
| DefaultEventBasedHold optional.Bool |
| |
| // BucketPolicyOnly is an alias for UniformBucketLevelAccess. Use of |
| // UniformBucketLevelAccess is recommended above the use of this field. |
| // Setting BucketPolicyOnly.Enabled OR UniformBucketLevelAccess.Enabled to |
| // true, will enable UniformBucketLevelAccess. If both BucketPolicyOnly and |
| // UniformBucketLevelAccess are set, the value of UniformBucketLevelAccess |
| // will take precedence. |
| BucketPolicyOnly *BucketPolicyOnly |
| |
| // UniformBucketLevelAccess configures access checks to use only bucket-level IAM |
| // policies and ignore any ACL rules for the bucket. |
| // See https://cloud.google.com/storage/docs/uniform-bucket-level-access |
| // for more information. |
| UniformBucketLevelAccess *UniformBucketLevelAccess |
| |
| // PublicAccessPrevention is the setting for the bucket's |
| // PublicAccessPrevention policy, which can be used to prevent public access |
| // of data in the bucket. See |
| // https://cloud.google.com/storage/docs/public-access-prevention for more |
| // information. |
| PublicAccessPrevention PublicAccessPrevention |
| |
| // 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 "STANDARD", "NEARLINE", |
| // "COLDLINE" and "ARCHIVE". Defaults to "STANDARD". |
| // See https://cloud.google.com/storage/docs/storage-classes for all |
| // valid values. |
| StorageClass string |
| |
| // 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 |
| |
| // If set, replaces the lifecycle configuration of the bucket. |
| Lifecycle *Lifecycle |
| |
| // If set, replaces the logging configuration of the bucket. |
| Logging *BucketLogging |
| |
| // If set, replaces the website configuration of the bucket. |
| Website *BucketWebsite |
| |
| // If not empty, applies a predefined set of access controls. |
| // See https://cloud.google.com/storage/docs/json_api/v1/buckets/patch. |
| PredefinedACL string |
| |
| // If not empty, applies a predefined set of default object access controls. |
| // See https://cloud.google.com/storage/docs/json_api/v1/buckets/patch. |
| PredefinedDefaultObjectACL string |
| |
| // RPO configures the Recovery Point Objective (RPO) policy of the bucket. |
| // Set to RPOAsyncTurbo to turn on Turbo Replication for a bucket. |
| // See https://cloud.google.com/storage/docs/managing-turbo-replication for |
| // more information. |
| RPO RPO |
| |
| // If set, updates the autoclass configuration of the bucket. |
| // To disable autoclass on the bucket, set to an empty &Autoclass{}. |
| // To update the configuration for Autoclass.TerminalStorageClass, |
| // Autoclass.Enabled must also be set to true. |
| // See https://cloud.google.com/storage/docs/using-autoclass for more information. |
| Autoclass *Autoclass |
| |
| // If set, updates the soft delete policy of the bucket. |
| SoftDeletePolicy *SoftDeletePolicy |
| |
| // acl is the list of access control rules on the bucket. |
| // It is unexported and only used internally by the gRPC client. |
| // Library users should use ACLHandle methods directly. |
| acl []ACLRule |
| |
| // defaultObjectACL is the list of access controls to |
| // apply to new objects when no object ACL is provided. |
| // It is unexported and only used internally by the gRPC client. |
| // Library users should use ACLHandle methods directly. |
| defaultObjectACL []ACLRule |
| |
| 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.DefaultEventBasedHold != nil { |
| rb.DefaultEventBasedHold = optional.ToBool(ua.DefaultEventBasedHold) |
| rb.ForceSendFields = append(rb.ForceSendFields, "DefaultEventBasedHold") |
| } |
| 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.BucketPolicyOnly != nil { |
| rb.IamConfiguration = &raw.BucketIamConfiguration{ |
| UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{ |
| Enabled: ua.BucketPolicyOnly.Enabled, |
| ForceSendFields: []string{"Enabled"}, |
| }, |
| } |
| } |
| if ua.UniformBucketLevelAccess != nil { |
| rb.IamConfiguration = &raw.BucketIamConfiguration{ |
| UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{ |
| Enabled: ua.UniformBucketLevelAccess.Enabled, |
| ForceSendFields: []string{"Enabled"}, |
| }, |
| } |
| } |
| if ua.PublicAccessPrevention != PublicAccessPreventionUnknown { |
| if rb.IamConfiguration == nil { |
| rb.IamConfiguration = &raw.BucketIamConfiguration{} |
| } |
| rb.IamConfiguration.PublicAccessPrevention = ua.PublicAccessPrevention.String() |
| } |
| 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.Lifecycle != nil { |
| rb.Lifecycle = toRawLifecycle(*ua.Lifecycle) |
| rb.ForceSendFields = append(rb.ForceSendFields, "Lifecycle") |
| } |
| if ua.Logging != nil { |
| if *ua.Logging == (BucketLogging{}) { |
| rb.NullFields = append(rb.NullFields, "Logging") |
| rb.Logging = nil |
| } else { |
| rb.Logging = ua.Logging.toRawBucketLogging() |
| } |
| } |
| if ua.Website != nil { |
| if *ua.Website == (BucketWebsite{}) { |
| rb.NullFields = append(rb.NullFields, "Website") |
| rb.Website = nil |
| } else { |
| rb.Website = ua.Website.toRawBucketWebsite() |
| } |
| } |
| if ua.Autoclass != nil { |
| rb.Autoclass = &raw.BucketAutoclass{ |
| Enabled: ua.Autoclass.Enabled, |
| TerminalStorageClass: ua.Autoclass.TerminalStorageClass, |
| ForceSendFields: []string{"Enabled"}, |
| } |
| rb.ForceSendFields = append(rb.ForceSendFields, "Autoclass") |
| } |
| if ua.SoftDeletePolicy != nil { |
| if ua.SoftDeletePolicy.RetentionDuration == 0 { |
| rb.NullFields = append(rb.NullFields, "SoftDeletePolicy") |
| rb.SoftDeletePolicy = nil |
| } else { |
| rb.SoftDeletePolicy = ua.SoftDeletePolicy.toRawSoftDeletePolicy() |
| } |
| } |
| if ua.PredefinedACL != "" { |
| // Clear ACL or the call will fail. |
| rb.Acl = nil |
| rb.ForceSendFields = append(rb.ForceSendFields, "Acl") |
| } |
| if ua.PredefinedDefaultObjectACL != "" { |
| // Clear ACLs or the call will fail. |
| rb.DefaultObjectAcl = nil |
| rb.ForceSendFields = append(rb.ForceSendFields, "DefaultObjectAcl") |
| } |
| |
| rb.StorageClass = ua.StorageClass |
| rb.Rpo = ua.RPO.String() |
| |
| 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. The supplied |
| // BucketConditions must have exactly one field set to a non-zero value; |
| // otherwise an error will be returned from any operation on the BucketHandle. |
| // Operations on the new handle will return an error if the preconditions are not |
| // 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 { |
| o := makeStorageOpts(true, b.retry, b.userProject) |
| return b.c.tc.LockBucketRetentionPolicy(ctx, b.name, b.conds, o...) |
| } |
| |
| // SetObjectRetention returns a new BucketHandle that will enable object retention |
| // on bucket creation. To enable object retention, you must use the returned |
| // handle to create the bucket. This has no effect on an already existing bucket. |
| // ObjectRetention is not enabled by default. |
| // ObjectRetention cannot be configured through the gRPC API. |
| func (b *BucketHandle) SetObjectRetention(enable bool) *BucketHandle { |
| b2 := *b |
| b2.enableObjectRetention = &enable |
| return &b2 |
| } |
| |
| // 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 !setIfMetagenerationMatch(cval, conds.MetagenerationMatch) { |
| return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method) |
| } |
| case conds.MetagenerationNotMatch != 0: |
| if !setIfMetagenerationNotMatch(cval, conds.MetagenerationNotMatch) { |
| return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method) |
| } |
| } |
| return nil |
| } |
| |
| // applyBucketConds modifies the provided request message using the conditions |
| // in conds. msg is a protobuf Message that has fields if_metageneration_match |
| // and if_metageneration_not_match. |
| func applyBucketCondsProto(method string, conds *BucketConditions, msg proto.Message) error { |
| rmsg := msg.ProtoReflect() |
| |
| if conds == nil { |
| return nil |
| } |
| if err := conds.validate(method); err != nil { |
| return err |
| } |
| |
| switch { |
| case conds.MetagenerationMatch != 0: |
| if !setConditionProtoField(rmsg, "if_metageneration_match", conds.MetagenerationMatch) { |
| return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method) |
| } |
| case conds.MetagenerationNotMatch != 0: |
| if !setConditionProtoField(rmsg, "if_metageneration_not_match", 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 (rp *RetentionPolicy) toProtoRetentionPolicy() *storagepb.Bucket_RetentionPolicy { |
| if rp == nil { |
| return nil |
| } |
| // RetentionPeriod must be greater than 0, so if it is 0, the user left it |
| // unset, and so we should not send it in the request i.e. nil is sent. |
| var dur *durationpb.Duration |
| if rp.RetentionPeriod != 0 { |
| dur = durationpb.New(rp.RetentionPeriod) |
| } |
| return &storagepb.Bucket_RetentionPolicy{ |
| RetentionDuration: dur, |
| } |
| } |
| |
| func toRetentionPolicy(rp *raw.BucketRetentionPolicy) (*RetentionPolicy, error) { |
| if rp == nil || rp.EffectiveTime == "" { |
| 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, |
| IsLocked: rp.IsLocked, |
| }, nil |
| } |
| |
| func toRetentionPolicyFromProto(rp *storagepb.Bucket_RetentionPolicy) *RetentionPolicy { |
| if rp == nil || rp.GetEffectiveTime().AsTime().Unix() == 0 { |
| return nil |
| } |
| return &RetentionPolicy{ |
| RetentionPeriod: rp.GetRetentionDuration().AsDuration(), |
| EffectiveTime: rp.GetEffectiveTime().AsTime(), |
| IsLocked: rp.GetIsLocked(), |
| } |
| } |
| |
| func toBucketObjectRetention(or *raw.BucketObjectRetention) string { |
| if or == nil { |
| return "" |
| } |
| return or.Mode |
| } |
| |
| 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 toProtoCORS(c []CORS) []*storagepb.Bucket_Cors { |
| var out []*storagepb.Bucket_Cors |
| for _, v := range c { |
| out = append(out, &storagepb.Bucket_Cors{ |
| MaxAgeSeconds: int32(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 toCORSFromProto(rc []*storagepb.Bucket_Cors) []CORS { |
| var out []CORS |
| for _, v := range rc { |
| out = append(out, CORS{ |
| MaxAge: time.Duration(v.GetMaxAgeSeconds()) * time.Second, |
| Methods: v.GetMethod(), |
| Origins: v.GetOrigin(), |
| ResponseHeaders: v.GetResponseHeader(), |
| }) |
| } |
| return out |
| } |
| |
| func toRawLifecycle(l Lifecycle) *raw.BucketLifecycle { |
| var rl raw.BucketLifecycle |
| if len(l.Rules) == 0 { |
| rl.ForceSendFields = []string{"Rule"} |
| } |
| for _, r := range l.Rules { |
| rr := &raw.BucketLifecycleRule{ |
| Action: &raw.BucketLifecycleRuleAction{ |
| Type: r.Action.Type, |
| StorageClass: r.Action.StorageClass, |
| }, |
| Condition: &raw.BucketLifecycleRuleCondition{ |
| DaysSinceCustomTime: r.Condition.DaysSinceCustomTime, |
| DaysSinceNoncurrentTime: r.Condition.DaysSinceNoncurrentTime, |
| MatchesPrefix: r.Condition.MatchesPrefix, |
| MatchesStorageClass: r.Condition.MatchesStorageClasses, |
| MatchesSuffix: r.Condition.MatchesSuffix, |
| NumNewerVersions: r.Condition.NumNewerVersions, |
| }, |
| } |
| |
| // AllObjects takes precedent when both AllObjects and AgeInDays are set |
| // Rationale: If you've opted into using AllObjects, it makes sense that you |
| // understand the implications of how this option works with AgeInDays. |
| if r.Condition.AllObjects { |
| rr.Condition.Age = googleapi.Int64(0) |
| rr.Condition.ForceSendFields = []string{"Age"} |
| } else if r.Condition.AgeInDays > 0 { |
| rr.Condition.Age = googleapi.Int64(r.Condition.AgeInDays) |
| } |
| |
| 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) |
| } |
| if !r.Condition.CustomTimeBefore.IsZero() { |
| rr.Condition.CustomTimeBefore = r.Condition.CustomTimeBefore.Format(rfc3339Date) |
| } |
| if !r.Condition.NoncurrentTimeBefore.IsZero() { |
| rr.Condition.NoncurrentTimeBefore = r.Condition.NoncurrentTimeBefore.Format(rfc3339Date) |
| } |
| rl.Rule = append(rl.Rule, rr) |
| } |
| return &rl |
| } |
| |
| func toProtoLifecycle(l Lifecycle) *storagepb.Bucket_Lifecycle { |
| var rl storagepb.Bucket_Lifecycle |
| |
| for _, r := range l.Rules { |
| rr := &storagepb.Bucket_Lifecycle_Rule{ |
| Action: &storagepb.Bucket_Lifecycle_Rule_Action{ |
| Type: r.Action.Type, |
| StorageClass: r.Action.StorageClass, |
| }, |
| Condition: &storagepb.Bucket_Lifecycle_Rule_Condition{ |
| // Note: The Apiary types use int64 (even though the Discovery |
| // doc states "format: int32"), so the client types used int64, |
| // but the proto uses int32 so we have a potentially lossy |
| // conversion. |
| DaysSinceCustomTime: proto.Int32(int32(r.Condition.DaysSinceCustomTime)), |
| DaysSinceNoncurrentTime: proto.Int32(int32(r.Condition.DaysSinceNoncurrentTime)), |
| MatchesPrefix: r.Condition.MatchesPrefix, |
| MatchesStorageClass: r.Condition.MatchesStorageClasses, |
| MatchesSuffix: r.Condition.MatchesSuffix, |
| NumNewerVersions: proto.Int32(int32(r.Condition.NumNewerVersions)), |
| }, |
| } |
| |
| // Only set AgeDays in the proto if it is non-zero, or if the user has set |
| // Condition.AllObjects. |
| if r.Condition.AgeInDays != 0 { |
| rr.Condition.AgeDays = proto.Int32(int32(r.Condition.AgeInDays)) |
| } |
| if r.Condition.AllObjects { |
| rr.Condition.AgeDays = proto.Int32(0) |
| } |
| |
| switch r.Condition.Liveness { |
| case LiveAndArchived: |
| rr.Condition.IsLive = nil |
| case Live: |
| rr.Condition.IsLive = proto.Bool(true) |
| case Archived: |
| rr.Condition.IsLive = proto.Bool(false) |
| } |
| |
| if !r.Condition.CreatedBefore.IsZero() { |
| rr.Condition.CreatedBefore = timeToProtoDate(r.Condition.CreatedBefore) |
| } |
| if !r.Condition.CustomTimeBefore.IsZero() { |
| rr.Condition.CustomTimeBefore = timeToProtoDate(r.Condition.CustomTimeBefore) |
| } |
| if !r.Condition.NoncurrentTimeBefore.IsZero() { |
| rr.Condition.NoncurrentTimeBefore = timeToProtoDate(r.Condition.NoncurrentTimeBefore) |
| } |
| 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{ |
| DaysSinceCustomTime: rr.Condition.DaysSinceCustomTime, |
| DaysSinceNoncurrentTime: rr.Condition.DaysSinceNoncurrentTime, |
| MatchesPrefix: rr.Condition.MatchesPrefix, |
| MatchesStorageClasses: rr.Condition.MatchesStorageClass, |
| MatchesSuffix: rr.Condition.MatchesSuffix, |
| NumNewerVersions: rr.Condition.NumNewerVersions, |
| }, |
| } |
| if rr.Condition.Age != nil { |
| r.Condition.AgeInDays = *rr.Condition.Age |
| if *rr.Condition.Age == 0 { |
| r.Condition.AllObjects = true |
| } |
| } |
| |
| if rr.Condition.IsLive == nil { |
| r.Condition.Liveness = LiveAndArchived |
| } else if *rr.Condition.IsLive { |
| r.Condition.Liveness = Live |
| } else { |
| r.Condition.Liveness = Archived |
| } |
| |
| if rr.Condition.CreatedBefore != "" { |
| r.Condition.CreatedBefore, _ = time.Parse(rfc3339Date, rr.Condition.CreatedBefore) |
| } |
| if rr.Condition.CustomTimeBefore != "" { |
| r.Condition.CustomTimeBefore, _ = time.Parse(rfc3339Date, rr.Condition.CustomTimeBefore) |
| } |
| if rr.Condition.NoncurrentTimeBefore != "" { |
| r.Condition.NoncurrentTimeBefore, _ = time.Parse(rfc3339Date, rr.Condition.NoncurrentTimeBefore) |
| } |
| l.Rules = append(l.Rules, r) |
| } |
| return l |
| } |
| |
| func toLifecycleFromProto(rl *storagepb.Bucket_Lifecycle) Lifecycle { |
| var l Lifecycle |
| if rl == nil { |
| return l |
| } |
| for _, rr := range rl.GetRule() { |
| r := LifecycleRule{ |
| Action: LifecycleAction{ |
| Type: rr.GetAction().GetType(), |
| StorageClass: rr.GetAction().GetStorageClass(), |
| }, |
| Condition: LifecycleCondition{ |
| AgeInDays: int64(rr.GetCondition().GetAgeDays()), |
| DaysSinceCustomTime: int64(rr.GetCondition().GetDaysSinceCustomTime()), |
| DaysSinceNoncurrentTime: int64(rr.GetCondition().GetDaysSinceNoncurrentTime()), |
| MatchesPrefix: rr.GetCondition().GetMatchesPrefix(), |
| MatchesStorageClasses: rr.GetCondition().GetMatchesStorageClass(), |
| MatchesSuffix: rr.GetCondition().GetMatchesSuffix(), |
| NumNewerVersions: int64(rr.GetCondition().GetNumNewerVersions()), |
| }, |
| } |
| |
| // Only set Condition.AllObjects if AgeDays is zero, not if it is nil. |
| if rr.GetCondition().AgeDays != nil && rr.GetCondition().GetAgeDays() == 0 { |
| r.Condition.AllObjects = true |
| } |
| |
| if rr.GetCondition().IsLive == nil { |
| r.Condition.Liveness = LiveAndArchived |
| } else if rr.GetCondition().GetIsLive() { |
| r.Condition.Liveness = Live |
| } else { |
| r.Condition.Liveness = Archived |
| } |
| |
| if rr.GetCondition().GetCreatedBefore() != nil { |
| r.Condition.CreatedBefore = protoDateToUTCTime(rr.GetCondition().GetCreatedBefore()) |
| } |
| if rr.GetCondition().GetCustomTimeBefore() != nil { |
| r.Condition.CustomTimeBefore = protoDateToUTCTime(rr.GetCondition().GetCustomTimeBefore()) |
| } |
| if rr.GetCondition().GetNoncurrentTimeBefore() != nil { |
| r.Condition.NoncurrentTimeBefore = protoDateToUTCTime(rr.GetCondition().GetNoncurrentTimeBefore()) |
| } |
| 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 (e *BucketEncryption) toProtoBucketEncryption() *storagepb.Bucket_Encryption { |
| if e == nil { |
| return nil |
| } |
| return &storagepb.Bucket_Encryption{ |
| DefaultKmsKey: e.DefaultKMSKeyName, |
| } |
| } |
| |
| func toBucketEncryption(e *raw.BucketEncryption) *BucketEncryption { |
| if e == nil { |
| return nil |
| } |
| return &BucketEncryption{DefaultKMSKeyName: e.DefaultKmsKeyName} |
| } |
| |
| func toBucketEncryptionFromProto(e *storagepb.Bucket_Encryption) *BucketEncryption { |
| if e == nil { |
| return nil |
| } |
| return &BucketEncryption{DefaultKMSKeyName: e.GetDefaultKmsKey()} |
| } |
| |
| func (b *BucketLogging) toRawBucketLogging() *raw.BucketLogging { |
| if b == nil { |
| return nil |
| } |
| return &raw.BucketLogging{ |
| LogBucket: b.LogBucket, |
| LogObjectPrefix: b.LogObjectPrefix, |
| } |
| } |
| |
| func (b *BucketLogging) toProtoBucketLogging() *storagepb.Bucket_Logging { |
| if b == nil { |
| return nil |
| } |
| return &storagepb.Bucket_Logging{ |
| LogBucket: bucketResourceName(globalProjectAlias, b.LogBucket), |
| LogObjectPrefix: b.LogObjectPrefix, |
| } |
| } |
| |
| func toBucketLogging(b *raw.BucketLogging) *BucketLogging { |
| if b == nil { |
| return nil |
| } |
| return &BucketLogging{ |
| LogBucket: b.LogBucket, |
| LogObjectPrefix: b.LogObjectPrefix, |
| } |
| } |
| |
| func toBucketLoggingFromProto(b *storagepb.Bucket_Logging) *BucketLogging { |
| if b == nil { |
| return nil |
| } |
| lb := parseBucketName(b.GetLogBucket()) |
| return &BucketLogging{ |
| LogBucket: lb, |
| LogObjectPrefix: b.GetLogObjectPrefix(), |
| } |
| } |
| |
| func (w *BucketWebsite) toRawBucketWebsite() *raw.BucketWebsite { |
| if w == nil { |
| return nil |
| } |
| return &raw.BucketWebsite{ |
| MainPageSuffix: w.MainPageSuffix, |
| NotFoundPage: w.NotFoundPage, |
| } |
| } |
| |
| func (w *BucketWebsite) toProtoBucketWebsite() *storagepb.Bucket_Website { |
| if w == nil { |
| return nil |
| } |
| return &storagepb.Bucket_Website{ |
| MainPageSuffix: w.MainPageSuffix, |
| NotFoundPage: w.NotFoundPage, |
| } |
| } |
| |
| func toBucketWebsite(w *raw.BucketWebsite) *BucketWebsite { |
| if w == nil { |
| return nil |
| } |
| return &BucketWebsite{ |
| MainPageSuffix: w.MainPageSuffix, |
| NotFoundPage: w.NotFoundPage, |
| } |
| } |
| |
| func toBucketWebsiteFromProto(w *storagepb.Bucket_Website) *BucketWebsite { |
| if w == nil { |
| return nil |
| } |
| return &BucketWebsite{ |
| MainPageSuffix: w.GetMainPageSuffix(), |
| NotFoundPage: w.GetNotFoundPage(), |
| } |
| } |
| |
| func toBucketPolicyOnly(b *raw.BucketIamConfiguration) BucketPolicyOnly { |
| if b == nil || b.BucketPolicyOnly == nil || !b.BucketPolicyOnly.Enabled { |
| return BucketPolicyOnly{} |
| } |
| lt, err := time.Parse(time.RFC3339, b.BucketPolicyOnly.LockedTime) |
| if err != nil { |
| return BucketPolicyOnly{ |
| Enabled: true, |
| } |
| } |
| return BucketPolicyOnly{ |
| Enabled: true, |
| LockedTime: lt, |
| } |
| } |
| |
| func toBucketPolicyOnlyFromProto(b *storagepb.Bucket_IamConfig) BucketPolicyOnly { |
| if b == nil || !b.GetUniformBucketLevelAccess().GetEnabled() { |
| return BucketPolicyOnly{} |
| } |
| return BucketPolicyOnly{ |
| Enabled: true, |
| LockedTime: b.GetUniformBucketLevelAccess().GetLockTime().AsTime(), |
| } |
| } |
| |
| func toUniformBucketLevelAccess(b *raw.BucketIamConfiguration) UniformBucketLevelAccess { |
| if b == nil || b.UniformBucketLevelAccess == nil || !b.UniformBucketLevelAccess.Enabled { |
| return UniformBucketLevelAccess{} |
| } |
| lt, err := time.Parse(time.RFC3339, b.UniformBucketLevelAccess.LockedTime) |
| if err != nil { |
| return UniformBucketLevelAccess{ |
| Enabled: true, |
| } |
| } |
| return UniformBucketLevelAccess{ |
| Enabled: true, |
| LockedTime: lt, |
| } |
| } |
| |
| func toUniformBucketLevelAccessFromProto(b *storagepb.Bucket_IamConfig) UniformBucketLevelAccess { |
| if b == nil || !b.GetUniformBucketLevelAccess().GetEnabled() { |
| return UniformBucketLevelAccess{} |
| } |
| return UniformBucketLevelAccess{ |
| Enabled: true, |
| LockedTime: b.GetUniformBucketLevelAccess().GetLockTime().AsTime(), |
| } |
| } |
| |
| func toPublicAccessPrevention(b *raw.BucketIamConfiguration) PublicAccessPrevention { |
| if b == nil { |
| return PublicAccessPreventionUnknown |
| } |
| switch b.PublicAccessPrevention { |
| case publicAccessPreventionInherited, publicAccessPreventionUnspecified: |
| return PublicAccessPreventionInherited |
| case publicAccessPreventionEnforced: |
| return PublicAccessPreventionEnforced |
| default: |
| return PublicAccessPreventionUnknown |
| } |
| } |
| |
| func toPublicAccessPreventionFromProto(b *storagepb.Bucket_IamConfig) PublicAccessPrevention { |
| if b == nil { |
| return PublicAccessPreventionUnknown |
| } |
| switch b.GetPublicAccessPrevention() { |
| case publicAccessPreventionInherited, publicAccessPreventionUnspecified: |
| return PublicAccessPreventionInherited |
| case publicAccessPreventionEnforced: |
| return PublicAccessPreventionEnforced |
| default: |
| return PublicAccessPreventionUnknown |
| } |
| } |
| |
| func toRPO(b *raw.Bucket) RPO { |
| if b == nil { |
| return RPOUnknown |
| } |
| switch b.Rpo { |
| case rpoDefault: |
| return RPODefault |
| case rpoAsyncTurbo: |
| return RPOAsyncTurbo |
| default: |
| return RPOUnknown |
| } |
| } |
| |
| func toRPOFromProto(b *storagepb.Bucket) RPO { |
| if b == nil { |
| return RPOUnknown |
| } |
| switch b.GetRpo() { |
| case rpoDefault: |
| return RPODefault |
| case rpoAsyncTurbo: |
| return RPOAsyncTurbo |
| default: |
| return RPOUnknown |
| } |
| } |
| |
| func customPlacementFromRaw(c *raw.BucketCustomPlacementConfig) *CustomPlacementConfig { |
| if c == nil { |
| return nil |
| } |
| return &CustomPlacementConfig{DataLocations: c.DataLocations} |
| } |
| |
| func (c *CustomPlacementConfig) toRawCustomPlacement() *raw.BucketCustomPlacementConfig { |
| if c == nil { |
| return nil |
| } |
| return &raw.BucketCustomPlacementConfig{ |
| DataLocations: c.DataLocations, |
| } |
| } |
| |
| func (c *CustomPlacementConfig) toProtoCustomPlacement() *storagepb.Bucket_CustomPlacementConfig { |
| if c == nil { |
| return nil |
| } |
| return &storagepb.Bucket_CustomPlacementConfig{ |
| DataLocations: c.DataLocations, |
| } |
| } |
| |
| func customPlacementFromProto(c *storagepb.Bucket_CustomPlacementConfig) *CustomPlacementConfig { |
| if c == nil { |
| return nil |
| } |
| return &CustomPlacementConfig{DataLocations: c.GetDataLocations()} |
| } |
| |
| func (a *Autoclass) toRawAutoclass() *raw.BucketAutoclass { |
| if a == nil { |
| return nil |
| } |
| // Excluding read only fields ToggleTime and TerminalStorageClassUpdateTime. |
| return &raw.BucketAutoclass{ |
| Enabled: a.Enabled, |
| TerminalStorageClass: a.TerminalStorageClass, |
| } |
| } |
| |
| func (a *Autoclass) toProtoAutoclass() *storagepb.Bucket_Autoclass { |
| if a == nil { |
| return nil |
| } |
| // Excluding read only fields ToggleTime and TerminalStorageClassUpdateTime. |
| ba := &storagepb.Bucket_Autoclass{ |
| Enabled: a.Enabled, |
| } |
| if a.TerminalStorageClass != "" { |
| ba.TerminalStorageClass = &a.TerminalStorageClass |
| } |
| return ba |
| } |
| |
| func toAutoclassFromRaw(a *raw.BucketAutoclass) *Autoclass { |
| if a == nil || a.ToggleTime == "" { |
| return nil |
| } |
| ac := &Autoclass{ |
| Enabled: a.Enabled, |
| TerminalStorageClass: a.TerminalStorageClass, |
| } |
| // Return ToggleTime and TSCUpdateTime only if parsed with valid values. |
| t, err := time.Parse(time.RFC3339, a.ToggleTime) |
| if err == nil { |
| ac.ToggleTime = t |
| } |
| ut, err := time.Parse(time.RFC3339, a.TerminalStorageClassUpdateTime) |
| if err == nil { |
| ac.TerminalStorageClassUpdateTime = ut |
| } |
| return ac |
| } |
| |
| func toAutoclassFromProto(a *storagepb.Bucket_Autoclass) *Autoclass { |
| if a == nil || a.GetToggleTime().AsTime().Unix() == 0 { |
| return nil |
| } |
| return &Autoclass{ |
| Enabled: a.GetEnabled(), |
| ToggleTime: a.GetToggleTime().AsTime(), |
| TerminalStorageClass: a.GetTerminalStorageClass(), |
| TerminalStorageClassUpdateTime: a.GetTerminalStorageClassUpdateTime().AsTime(), |
| } |
| } |
| |
| func (p *SoftDeletePolicy) toRawSoftDeletePolicy() *raw.BucketSoftDeletePolicy { |
| if p == nil { |
| return nil |
| } |
| // Excluding read only field EffectiveTime. |
| return &raw.BucketSoftDeletePolicy{ |
| RetentionDurationSeconds: int64(p.RetentionDuration.Seconds()), |
| } |
| } |
| |
| func (p *SoftDeletePolicy) toProtoSoftDeletePolicy() *storagepb.Bucket_SoftDeletePolicy { |
| if p == nil { |
| return nil |
| } |
| // Excluding read only field EffectiveTime. |
| return &storagepb.Bucket_SoftDeletePolicy{ |
| RetentionDuration: durationpb.New(p.RetentionDuration), |
| } |
| } |
| |
| func toSoftDeletePolicyFromRaw(p *raw.BucketSoftDeletePolicy) *SoftDeletePolicy { |
| if p == nil { |
| return nil |
| } |
| |
| policy := &SoftDeletePolicy{ |
| RetentionDuration: time.Duration(p.RetentionDurationSeconds) * time.Second, |
| } |
| |
| // Return EffectiveTime only if parsed to a valid value. |
| if t, err := time.Parse(time.RFC3339, p.EffectiveTime); err == nil { |
| policy.EffectiveTime = t |
| } |
| |
| return policy |
| } |
| |
| func toSoftDeletePolicyFromProto(p *storagepb.Bucket_SoftDeletePolicy) *SoftDeletePolicy { |
| if p == nil { |
| return nil |
| } |
| return &SoftDeletePolicy{ |
| EffectiveTime: p.GetEffectiveTime().AsTime(), |
| RetentionDuration: p.GetRetentionDuration().AsDuration(), |
| } |
| } |
| |
| func (hns *HierarchicalNamespace) toProtoHierarchicalNamespace() *storagepb.Bucket_HierarchicalNamespace { |
| if hns == nil { |
| return nil |
| } |
| return &storagepb.Bucket_HierarchicalNamespace{ |
| Enabled: hns.Enabled, |
| } |
| } |
| |
| func (hns *HierarchicalNamespace) toRawHierarchicalNamespace() *raw.BucketHierarchicalNamespace { |
| if hns == nil { |
| return nil |
| } |
| return &raw.BucketHierarchicalNamespace{ |
| Enabled: hns.Enabled, |
| } |
| } |
| |
| func toHierarchicalNamespaceFromProto(p *storagepb.Bucket_HierarchicalNamespace) *HierarchicalNamespace { |
| if p == nil { |
| return nil |
| } |
| return &HierarchicalNamespace{ |
| Enabled: p.Enabled, |
| } |
| } |
| |
| func toHierarchicalNamespaceFromRaw(r *raw.BucketHierarchicalNamespace) *HierarchicalNamespace { |
| if r == nil { |
| return nil |
| } |
| return &HierarchicalNamespace{ |
| Enabled: r.Enabled, |
| } |
| } |
| |
| // Objects returns an iterator over the objects in the bucket that match the |
| // Query q. If q is nil, no filtering is done. Objects will be iterated over |
| // lexicographically by name. |
| // |
| // Note: The returned iterator is not safe for concurrent operations without explicit synchronization. |
| func (b *BucketHandle) Objects(ctx context.Context, q *Query) *ObjectIterator { |
| o := makeStorageOpts(true, b.retry, b.userProject) |
| return b.c.tc.ListObjects(ctx, b.name, q, o...) |
| } |
| |
| // Retryer returns a bucket handle that is configured with custom retry |
| // behavior as specified by the options that are passed to it. All operations |
| // on the new handle will use the customized retry configuration. |
| // Retry options set on a object handle will take precedence over options set on |
| // the bucket handle. |
| // These retry options will merge with the client's retry configuration (if set) |
| // for the returned handle. Options passed into this method will take precedence |
| // over retry options on the client. Note that you must explicitly pass in each |
| // option you want to override. |
| func (b *BucketHandle) Retryer(opts ...RetryOption) *BucketHandle { |
| b2 := *b |
| var retry *retryConfig |
| if b.retry != nil { |
| // merge the options with the existing retry |
| retry = b.retry |
| } else { |
| retry = &retryConfig{} |
| } |
| for _, opt := range opts { |
| opt.apply(retry) |
| } |
| b2.retry = retry |
| b2.acl.retry = retry |
| b2.defaultObjectACL.retry = retry |
| return &b2 |
| } |
| |
| // An ObjectIterator is an iterator over ObjectAttrs. |
| // |
| // Note: This iterator is not safe for concurrent operations without explicit synchronization. |
| type ObjectIterator struct { |
| ctx context.Context |
| query Query |
| pageInfo *iterator.PageInfo |
| nextFunc func() error |
| items []*ObjectAttrs |
| } |
| |
| // PageInfo supports pagination. See the google.golang.org/api/iterator package for details. |
| // |
| // Note: This method is not safe for concurrent operations without explicit synchronization. |
| 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. |
| // |
| // In addition, if Next returns an error other than iterator.Done, all |
| // subsequent calls will return the same error. To continue iteration, a new |
| // `ObjectIterator` must be created. Since objects are ordered lexicographically |
| // by name, `Query.StartOffset` can be used to create a new iterator which will |
| // start at the desired place. See |
| // https://pkg.go.dev/cloud.google.com/go/storage?tab=doc#hdr-Listing_objects. |
| // |
| // 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. |
| // |
| // Note: This method is not safe for concurrent operations without explicit synchronization. |
| 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 |
| } |
| |
| // 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. |
| // |
| // Note: The returned iterator is not safe for concurrent operations without explicit synchronization. |
| func (c *Client) Buckets(ctx context.Context, projectID string) *BucketIterator { |
| o := makeStorageOpts(true, c.retry, "") |
| return c.tc.ListBuckets(ctx, projectID, o...) |
| } |
| |
| // A BucketIterator is an iterator over BucketAttrs. |
| // |
| // Note: This iterator is not safe for concurrent operations without explicit synchronization. |
| type BucketIterator struct { |
| // Prefix restricts the iterator to buckets whose names begin with it. |
| Prefix string |
| |
| ctx context.Context |
| 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. |
| // |
| // Note: This method is not safe for concurrent operations without explicit synchronization. |
| 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. |
| // |
| // Note: This method is not safe for concurrent operations without explicit synchronization. |
| func (it *BucketIterator) PageInfo() *iterator.PageInfo { return it.pageInfo } |
| |
| // RPO (Recovery Point Objective) configures the turbo replication feature. See |
| // https://cloud.google.com/storage/docs/managing-turbo-replication for more information. |
| type RPO int |
| |
| const ( |
| // RPOUnknown is a zero value. It may be returned from bucket.Attrs() if RPO |
| // is not present in the bucket metadata, that is, the bucket is not dual-region. |
| // This value is also used if the RPO field is not set in a call to GCS. |
| RPOUnknown RPO = iota |
| |
| // RPODefault represents default replication. It is used to reset RPO on an |
| // existing bucket that has this field set to RPOAsyncTurbo. Otherwise it |
| // is equivalent to RPOUnknown, and is always ignored. This value is valid |
| // for dual- or multi-region buckets. |
| RPODefault |
| |
| // RPOAsyncTurbo represents turbo replication and is used to enable Turbo |
| // Replication on a bucket. This value is only valid for dual-region buckets. |
| RPOAsyncTurbo |
| |
| rpoUnknown string = "" |
| rpoDefault = "DEFAULT" |
| rpoAsyncTurbo = "ASYNC_TURBO" |
| ) |
| |
| func (rpo RPO) String() string { |
| switch rpo { |
| case RPODefault: |
| return rpoDefault |
| case RPOAsyncTurbo: |
| return rpoAsyncTurbo |
| default: |
| return rpoUnknown |
| } |
| } |
| |
| // protoDateToUTCTime returns a new Time based on the google.type.Date, in UTC. |
| // |
| // Hours, minutes, seconds, and nanoseconds are set to 0. |
| func protoDateToUTCTime(d *dpb.Date) time.Time { |
| return protoDateToTime(d, time.UTC) |
| } |
| |
| // protoDateToTime returns a new Time based on the google.type.Date and provided |
| // *time.Location. |
| // |
| // Hours, minutes, seconds, and nanoseconds are set to 0. |
| func protoDateToTime(d *dpb.Date, l *time.Location) time.Time { |
| return time.Date(int(d.GetYear()), time.Month(d.GetMonth()), int(d.GetDay()), 0, 0, 0, 0, l) |
| } |
| |
| // timeToProtoDate returns a new google.type.Date based on the provided time.Time. |
| // The location is ignored, as is anything more precise than the day. |
| func timeToProtoDate(t time.Time) *dpb.Date { |
| return &dpb.Date{ |
| Year: int32(t.Year()), |
| Month: int32(t.Month()), |
| Day: int32(t.Day()), |
| } |
| } |