Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(storage): internal client interface design #5426

Merged
merged 11 commits into from Feb 22, 2022
183 changes: 183 additions & 0 deletions storage/client.go
@@ -0,0 +1,183 @@
// Copyright 2022 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"

gax "github.com/googleapis/gax-go/v2"
iampb "google.golang.org/genproto/googleapis/iam/v1"
)

// TODO(noahdietz): Move existing factory methods to this file.

// storageClient is an internal-only interface designed to separate the
noahdietz marked this conversation as resolved.
Show resolved Hide resolved
// transport-specific logic of making Storage API calls from the logic of the
// client library.
//
// Implementation requirements beyond implementing the interface include:
// * factory method(s) must accept a `userProject string` param
// * `settings` must be retained per instance
// * `storageOption`s must be resolved in the order they are received
// * all API errors must be wrapped in the gax-go APIError type
// * any unimplemented interface methods must return a StorageUnimplementedErr
//
// TODO(noahdietz): This interface is currently not used in the production code
// paths
type storageClient interface {

// Top-level methods.

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

// Bucket methods.

DeleteBucket(ctx context.Context, bucket string, conds *BucketConditions, opts ...storageOption) error
GetBucket(ctx context.Context, bucket string, conds *BucketConditions, opts ...storageOption) (*BucketAttrs, error)
UpdateBucket(ctx context.Context, uattrs *BucketAttrsToUpdate, conds *BucketConditions, opts ...storageOption) (*BucketAttrs, error)
LockBucketRetentionPolicy(ctx context.Context, bucket string, conds *BucketConditions, opts ...storageOption) error
ListObjects(ctx context.Context, bucket string, q *Query, opts ...storageOption) (*ObjectIterator, error)

// Object metadata methods.

DeleteObject(ctx context.Context, bucket, object string, conds *Conditions, opts ...storageOption) error
GetObject(ctx context.Context, bucket, object string, conds *Conditions, opts ...storageOption) (*ObjectAttrs, error)
UpdateObject(ctx context.Context, bucket, object string, uattrs *ObjectAttrsToUpdate, conds *Conditions, opts ...storageOption) (*ObjectAttrs, error)

// Default Object ACL methods.

DeleteDefaultObjectACL(ctx context.Context, bucket string, entity ACLEntity, opts ...storageOption) error
ListDefaultObjectACLs(ctx context.Context, bucket string, opts ...storageOption) ([]ACLRule, error)
UpdateDefaultObjectACL(ctx context.Context, opts ...storageOption) (ACLRule, error)

// Bucket ACL methods.

DeleteBucketACL(ctx context.Context, bucket string, entity ACLEntity, opts ...storageOption) error
ListBucketACLs(ctx context.Context, bucket string, opts ...storageOption) ([]ACLRule, error)
UpdateBucketACL(ctx context.Context, bucket string, entity ACLEntity, role ACLRole, opts ...storageOption) (ACLRule, error)

// Object ACL methods.

DeleteObjectACL(ctx context.Context, bucket, object string, entity ACLEntity, opts ...storageOption) error
ListObjectACLs(ctx context.Context, bucket, object string, opts ...storageOption) ([]ACLRule, error)
UpdateObjectACL(ctx context.Context, bucket, object string, entity ACLEntity, role ACLRole, opts ...storageOption) (ACLRule, error)

// Media operations.

ComposeObject(ctx context.Context, req composeObjectRequest, opts ...storageOption) (*ObjectAttrs, error)
noahdietz marked this conversation as resolved.
Show resolved Hide resolved
RewriteObject(ctx context.Context, req rewriteObjectRequest, opts ...storageOption) (rewriteObjectResponse, error)

OpenReader(ctx context.Context, r *Reader, opts ...storageOption) error
OpenWriter(ctx context.Context, w *Writer, opts ...storageOption) error

// IAM methods.

GetIamPolicy(ctx context.Context, resource string, version int32, opts ...storageOption) (*iampb.Policy, error)
SetIamPolicy(ctx context.Context, resource string, policy *iampb.Policy, opts ...storageOption) error
TestIamPermissions(ctx context.Context, resource string, permissions []string, opts ...storageOption) ([]string, error)

// HMAC Key methods.
// TODO(noahdietz): Determine how to work with HMACKeyOptions.

GetHMACKey(ctx context.Context, project, accessID string, opts ...storageOption) (*HMACKey, error)
ListHMACKey(ctx context.Context, project string, opts ...storageOption) *HMACKeysIterator
UpdateHMACKey(ctx context.Context, project, accessID string, attrs *HMACKeyAttrsToUpdate, opts ...storageOption) (*HMACKey, error)
CreateHMACKey(ctx context.Context, project, serviceAccountEmail string, opts ...storageOption) (*HMACKey, error)
DeleteHMACKey(ctx context.Context, project, accessID string, opts ...storageOption) error
}
noahdietz marked this conversation as resolved.
Show resolved Hide resolved

// settings contains transport-agnostic configuration for API calls made via
// the storageClient inteface. All implementations must utilize settings
// and respect those that are applicable.
type settings struct {
// retry is the complete retry configuration to use when evaluating if an
// API call should be retried.
retry *retryConfig

// gax is a set of gax.CallOption to be conveyed to gax.Invoke.
// Note: Not all storageClient interfaces will must use gax.Invoke.
gax []gax.CallOption

// idempotent indicates if the call is idempotent or not when considering
// if the call should be retired or not.
idempotent bool
}

// storageOption is the transport-agnostic call option for the storageClient
// interface.
type storageOption interface {
Apply(s *settings)
}

func withGAXOptions(opts ...gax.CallOption) storageOption {
return &gaxOption{opts}
}

type gaxOption struct {
opts []gax.CallOption
}

func (o *gaxOption) Apply(s *settings) { s.gax = o.opts }

func withRetryConfig(rc *retryConfig) storageOption {
return &retryOption{rc}
}

type retryOption struct {
rc *retryConfig
}

func (o *retryOption) Apply(s *settings) { s.retry = o.rc }

func idempotent(i bool) storageOption {
return &idempotentOption{i}
}

type idempotentOption struct {
idempotency bool
}

func (o *idempotentOption) Apply(s *settings) { s.idempotent = o.idempotency }

type composeObjectRequest struct {
dstBucket string
dstObject string
srcs []string
gen int64
conds *Conditions
predefinedACL string
}

type rewriteObjectRequest struct {
srcBucket string
srcObject string
dstBucket string
dstObject string
dstKeyName string
attrs *ObjectAttrs
gen int64
conds *Conditions
predefinedACL string
token string
}

type rewriteObjectResponse struct {
resource *ObjectAttrs
done bool
written int64
token string
}