Skip to content

Commit

Permalink
feat(storage): add object retention feature (#9072)
Browse files Browse the repository at this point in the history
  • Loading branch information
BrennaEpp committed Dec 13, 2023
1 parent 77c1cfa commit 16ecfd1
Show file tree
Hide file tree
Showing 8 changed files with 417 additions and 71 deletions.
47 changes: 39 additions & 8 deletions storage/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ import (
// 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
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.
Expand Down Expand Up @@ -85,7 +86,8 @@ func (b *BucketHandle) Create(ctx context.Context, projectID string, attrs *Buck
defer func() { trace.EndSpan(ctx, err) }()

o := makeStorageOpts(true, b.retry, b.userProject)
if _, err := b.c.tc.CreateBucket(ctx, projectID, b.name, attrs, o...); err != nil {

if _, err := b.c.tc.CreateBucket(ctx, projectID, b.name, attrs, b.enableObjectRetention, o...); err != nil {
return err
}
return nil
Expand Down Expand Up @@ -462,6 +464,15 @@ type BucketAttrs struct {
// 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
}

// BucketPolicyOnly is an alias for UniformBucketLevelAccess.
Expand Down Expand Up @@ -757,6 +768,7 @@ func newBucket(b *raw.Bucket) (*BucketAttrs, error) {
if err != nil {
return nil, err
}

return &BucketAttrs{
Name: b.Name,
Location: b.Location,
Expand All @@ -771,6 +783,7 @@ func newBucket(b *raw.Bucket) (*BucketAttrs, error) {
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),
Expand Down Expand Up @@ -1348,6 +1361,17 @@ func (b *BucketHandle) LockRetentionPolicy(ctx context.Context) error {
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 {
Expand Down Expand Up @@ -1447,6 +1471,13 @@ func toRetentionPolicyFromProto(rp *storagepb.Bucket_RetentionPolicy) *Retention
}
}

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 {
Expand Down
8 changes: 6 additions & 2 deletions storage/bucket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,9 @@ func TestNewBucket(t *testing.T) {
RetentionPeriod: 3,
EffectiveTime: aTime.Format(time.RFC3339),
},
ObjectRetention: &raw.BucketObjectRetention{
Mode: "Enabled",
},
IamConfiguration: &raw.BucketIamConfiguration{
BucketPolicyOnly: &raw.BucketIamConfigurationBucketPolicyOnly{
Enabled: true,
Expand Down Expand Up @@ -686,6 +689,7 @@ func TestNewBucket(t *testing.T) {
EffectiveTime: aTime,
RetentionPeriod: 3 * time.Second,
},
ObjectRetentionMode: "Enabled",
BucketPolicyOnly: BucketPolicyOnly{Enabled: true, LockedTime: aTime},
UniformBucketLevelAccess: UniformBucketLevelAccess{Enabled: true, LockedTime: aTime},
CORS: []CORS{
Expand Down Expand Up @@ -714,7 +718,7 @@ func TestNewBucket(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if diff := testutil.Diff(got, want); diff != "" {
if diff := cmp.Diff(got, want); diff != "" {
t.Errorf("got=-, want=+:\n%s", diff)
}
}
Expand Down Expand Up @@ -817,7 +821,7 @@ func TestNewBucketFromProto(t *testing.T) {
},
}
got := newBucketFromProto(pb)
if diff := testutil.Diff(got, want); diff != "" {
if diff := cmp.Diff(got, want); diff != "" {
t.Errorf("got=-, want=+:\n%s", diff)
}
}
Expand Down
13 changes: 11 additions & 2 deletions storage/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type storageClient interface {
// Top-level methods.

GetServiceAccount(ctx context.Context, project string, opts ...storageOption) (string, error)
CreateBucket(ctx context.Context, project, bucket string, attrs *BucketAttrs, opts ...storageOption) (*BucketAttrs, error)
CreateBucket(ctx context.Context, project, bucket string, attrs *BucketAttrs, enableObjectRetention *bool, opts ...storageOption) (*BucketAttrs, error)
ListBuckets(ctx context.Context, project string, opts ...storageOption) *BucketIterator
Close() error

Expand All @@ -60,7 +60,7 @@ type storageClient interface {

DeleteObject(ctx context.Context, bucket, object string, gen int64, conds *Conditions, opts ...storageOption) error
GetObject(ctx context.Context, bucket, object string, gen int64, encryptionKey []byte, conds *Conditions, opts ...storageOption) (*ObjectAttrs, error)
UpdateObject(ctx context.Context, bucket, object string, uattrs *ObjectAttrsToUpdate, gen int64, encryptionKey []byte, conds *Conditions, opts ...storageOption) (*ObjectAttrs, error)
UpdateObject(ctx context.Context, params *updateObjectParams, opts ...storageOption) (*ObjectAttrs, error)

// Default Object ACL methods.

Expand Down Expand Up @@ -291,6 +291,15 @@ type newRangeReaderParams struct {
readCompressed bool // Use accept-encoding: gzip. Only works for HTTP currently.
}

type updateObjectParams struct {
bucket, object string
uattrs *ObjectAttrsToUpdate
gen int64
encryptionKey []byte
conds *Conditions
overrideRetention *bool
}

type composeObjectRequest struct {
dstBucket string
dstObject destinationObject
Expand Down
Loading

0 comments on commit 16ecfd1

Please sign in to comment.