| // 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" |
| "os" |
| |
| gapic "cloud.google.com/go/storage/internal/apiv2" |
| "google.golang.org/api/option" |
| iampb "google.golang.org/genproto/googleapis/iam/v1" |
| storagepb "google.golang.org/genproto/googleapis/storage/v2" |
| "google.golang.org/grpc" |
| "google.golang.org/grpc/codes" |
| "google.golang.org/grpc/status" |
| ) |
| |
| const ( |
| // defaultConnPoolSize is the default number of connections |
| // to initialize in the GAPIC gRPC connection pool. A larger |
| // connection pool may be necessary for jobs that require |
| // high throughput and/or leverage many concurrent streams. |
| // |
| // This is an experimental API and not intended for public use. |
| defaultConnPoolSize = 4 |
| |
| // globalProjectAlias is the project ID alias used for global buckets. |
| // |
| // This is only used for the gRPC API. |
| globalProjectAlias = "_" |
| ) |
| |
| // defaultGRPCOptions returns a set of the default client options |
| // for gRPC client initialization. |
| // |
| // This is an experimental API and not intended for public use. |
| func defaultGRPCOptions() []option.ClientOption { |
| defaults := []option.ClientOption{ |
| option.WithGRPCConnectionPool(defaultConnPoolSize), |
| } |
| |
| // Set emulator options for gRPC if an emulator was specified. Note that in a |
| // hybrid client, STORAGE_EMULATOR_HOST will set the host to use for HTTP and |
| // STORAGE_EMULATOR_HOST_GRPC will set the host to use for gRPC (when using a |
| // local emulator, HTTP and gRPC must use different ports, so this is |
| // necessary). |
| // |
| // TODO: When the newHybridClient is not longer used, remove |
| // STORAGE_EMULATOR_HOST_GRPC and use STORAGE_EMULATOR_HOST for both the |
| // HTTP and gRPC based clients. |
| if host := os.Getenv("STORAGE_EMULATOR_HOST_GRPC"); host != "" { |
| // Strip the scheme from the emulator host. WithEndpoint does not take a |
| // scheme for gRPC. |
| host = stripScheme(host) |
| |
| defaults = append(defaults, |
| option.WithEndpoint(host), |
| option.WithGRPCDialOption(grpc.WithInsecure()), |
| option.WithoutAuthentication(), |
| ) |
| } |
| |
| return defaults |
| } |
| |
| // grpcStorageClient is the gRPC API implementation of the transport-agnostic |
| // storageClient interface. |
| // |
| // This is an experimental API and not intended for public use. |
| type grpcStorageClient struct { |
| raw *gapic.Client |
| settings *settings |
| } |
| |
| // newGRPCStorageClient initializes a new storageClient that uses the gRPC |
| // Storage API. |
| // |
| // This is an experimental API and not intended for public use. |
| func newGRPCStorageClient(ctx context.Context, opts ...storageOption) (storageClient, error) { |
| s := initSettings(opts...) |
| s.clientOption = append(defaultGRPCOptions(), s.clientOption...) |
| |
| g, err := gapic.NewClient(ctx, s.clientOption...) |
| if err != nil { |
| return nil, err |
| } |
| |
| return &grpcStorageClient{ |
| raw: g, |
| settings: s, |
| }, nil |
| } |
| |
| func (c *grpcStorageClient) Close() error { |
| return c.raw.Close() |
| } |
| |
| // Top-level methods. |
| |
| func (c *grpcStorageClient) GetServiceAccount(ctx context.Context, project string, opts ...storageOption) (string, error) { |
| s := callSettings(c.settings, opts...) |
| req := &storagepb.GetServiceAccountRequest{ |
| Project: toProjectResource(project), |
| } |
| var resp *storagepb.ServiceAccount |
| err := run(ctx, func() error { |
| var err error |
| resp, err = c.raw.GetServiceAccount(ctx, req, s.gax...) |
| return err |
| }, s.retry, s.idempotent) |
| if err != nil { |
| return "", err |
| } |
| return resp.EmailAddress, err |
| } |
| |
| func (c *grpcStorageClient) CreateBucket(ctx context.Context, project string, attrs *BucketAttrs, opts ...storageOption) (*BucketAttrs, error) { |
| s := callSettings(c.settings, opts...) |
| b := attrs.toProtoBucket() |
| |
| // If there is lifecycle information but no location, explicitly set |
| // the location. This is a GCS quirk/bug. |
| if b.GetLocation() == "" && b.GetLifecycle() != nil { |
| b.Location = "US" |
| } |
| |
| req := &storagepb.CreateBucketRequest{ |
| Parent: toProjectResource(project), |
| Bucket: b, |
| BucketId: b.GetName(), |
| PredefinedAcl: attrs.PredefinedACL, |
| PredefinedDefaultObjectAcl: attrs.PredefinedDefaultObjectACL, |
| } |
| |
| var battrs *BucketAttrs |
| err := run(ctx, func() error { |
| res, err := c.raw.CreateBucket(ctx, req, s.gax...) |
| |
| battrs = newBucketFromProto(res) |
| |
| return err |
| }, s.retry, s.idempotent) |
| |
| return battrs, err |
| } |
| |
| func (c *grpcStorageClient) ListBuckets(ctx context.Context, project string, opts ...storageOption) (*BucketIterator, error) { |
| return nil, errMethodNotSupported |
| } |
| |
| // Bucket methods. |
| |
| func (c *grpcStorageClient) DeleteBucket(ctx context.Context, bucket string, conds *BucketConditions, opts ...storageOption) error { |
| s := callSettings(c.settings, opts...) |
| req := &storagepb.DeleteBucketRequest{ |
| Name: bucketResourceName(globalProjectAlias, bucket), |
| } |
| if err := applyBucketCondsProto("grpcStorageClient.DeleteBucket", conds, req); err != nil { |
| return err |
| } |
| if s.userProject != "" { |
| req.CommonRequestParams = &storagepb.CommonRequestParams{ |
| UserProject: toProjectResource(s.userProject), |
| } |
| } |
| |
| return run(ctx, func() error { |
| return c.raw.DeleteBucket(ctx, req, s.gax...) |
| }, s.retry, s.idempotent) |
| } |
| |
| func (c *grpcStorageClient) GetBucket(ctx context.Context, bucket string, conds *BucketConditions, opts ...storageOption) (*BucketAttrs, error) { |
| s := callSettings(c.settings, opts...) |
| req := &storagepb.GetBucketRequest{ |
| Name: bucketResourceName(globalProjectAlias, bucket), |
| } |
| if err := applyBucketCondsProto("grpcStorageClient.GetBucket", conds, req); err != nil { |
| return nil, err |
| } |
| if s.userProject != "" { |
| req.CommonRequestParams = &storagepb.CommonRequestParams{ |
| UserProject: toProjectResource(s.userProject), |
| } |
| } |
| |
| var battrs *BucketAttrs |
| err := run(ctx, func() error { |
| res, err := c.raw.GetBucket(ctx, req, s.gax...) |
| |
| battrs = newBucketFromProto(res) |
| |
| return err |
| }, s.retry, s.idempotent) |
| |
| if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound { |
| return nil, ErrBucketNotExist |
| } |
| |
| return battrs, err |
| } |
| func (c *grpcStorageClient) UpdateBucket(ctx context.Context, uattrs *BucketAttrsToUpdate, conds *BucketConditions, opts ...storageOption) (*BucketAttrs, error) { |
| return nil, errMethodNotSupported |
| } |
| func (c *grpcStorageClient) LockBucketRetentionPolicy(ctx context.Context, bucket string, conds *BucketConditions, opts ...storageOption) error { |
| return errMethodNotSupported |
| } |
| func (c *grpcStorageClient) ListObjects(ctx context.Context, bucket string, q *Query, opts ...storageOption) (*ObjectIterator, error) { |
| return nil, errMethodNotSupported |
| } |
| |
| // Object metadata methods. |
| |
| func (c *grpcStorageClient) DeleteObject(ctx context.Context, bucket, object string, conds *Conditions, opts ...storageOption) error { |
| return errMethodNotSupported |
| } |
| func (c *grpcStorageClient) GetObject(ctx context.Context, bucket, object string, conds *Conditions, opts ...storageOption) (*ObjectAttrs, error) { |
| return nil, errMethodNotSupported |
| } |
| func (c *grpcStorageClient) UpdateObject(ctx context.Context, bucket, object string, uattrs *ObjectAttrsToUpdate, conds *Conditions, opts ...storageOption) (*ObjectAttrs, error) { |
| return nil, errMethodNotSupported |
| } |
| |
| // Default Object ACL methods. |
| |
| func (c *grpcStorageClient) DeleteDefaultObjectACL(ctx context.Context, bucket string, entity ACLEntity, opts ...storageOption) error { |
| return errMethodNotSupported |
| } |
| func (c *grpcStorageClient) ListDefaultObjectACLs(ctx context.Context, bucket string, opts ...storageOption) ([]ACLRule, error) { |
| return nil, errMethodNotSupported |
| } |
| func (c *grpcStorageClient) UpdateDefaultObjectACL(ctx context.Context, opts ...storageOption) (*ACLRule, error) { |
| return nil, errMethodNotSupported |
| } |
| |
| // Bucket ACL methods. |
| |
| func (c *grpcStorageClient) DeleteBucketACL(ctx context.Context, bucket string, entity ACLEntity, opts ...storageOption) error { |
| return errMethodNotSupported |
| } |
| func (c *grpcStorageClient) ListBucketACLs(ctx context.Context, bucket string, opts ...storageOption) ([]ACLRule, error) { |
| return nil, errMethodNotSupported |
| } |
| func (c *grpcStorageClient) UpdateBucketACL(ctx context.Context, bucket string, entity ACLEntity, role ACLRole, opts ...storageOption) (*ACLRule, error) { |
| return nil, errMethodNotSupported |
| } |
| |
| // Object ACL methods. |
| |
| func (c *grpcStorageClient) DeleteObjectACL(ctx context.Context, bucket, object string, entity ACLEntity, opts ...storageOption) error { |
| return errMethodNotSupported |
| } |
| func (c *grpcStorageClient) ListObjectACLs(ctx context.Context, bucket, object string, opts ...storageOption) ([]ACLRule, error) { |
| return nil, errMethodNotSupported |
| } |
| func (c *grpcStorageClient) UpdateObjectACL(ctx context.Context, bucket, object string, entity ACLEntity, role ACLRole, opts ...storageOption) (*ACLRule, error) { |
| return nil, errMethodNotSupported |
| } |
| |
| // Media operations. |
| |
| func (c *grpcStorageClient) ComposeObject(ctx context.Context, req *composeObjectRequest, opts ...storageOption) (*ObjectAttrs, error) { |
| return nil, errMethodNotSupported |
| } |
| func (c *grpcStorageClient) RewriteObject(ctx context.Context, req *rewriteObjectRequest, opts ...storageOption) (*rewriteObjectResponse, error) { |
| return nil, errMethodNotSupported |
| } |
| |
| func (c *grpcStorageClient) OpenReader(ctx context.Context, r *Reader, opts ...storageOption) error { |
| return errMethodNotSupported |
| } |
| func (c *grpcStorageClient) OpenWriter(ctx context.Context, w *Writer, opts ...storageOption) error { |
| return errMethodNotSupported |
| } |
| |
| // IAM methods. |
| |
| func (c *grpcStorageClient) GetIamPolicy(ctx context.Context, resource string, version int32, opts ...storageOption) (*iampb.Policy, error) { |
| // TODO: Need a way to set UserProject, potentially in X-Goog-User-Project system parameter. |
| s := callSettings(c.settings, opts...) |
| req := &iampb.GetIamPolicyRequest{ |
| Resource: bucketResourceName(globalProjectAlias, resource), |
| Options: &iampb.GetPolicyOptions{ |
| RequestedPolicyVersion: version, |
| }, |
| } |
| var rp *iampb.Policy |
| err := run(ctx, func() error { |
| var err error |
| rp, err = c.raw.GetIamPolicy(ctx, req, s.gax...) |
| return err |
| }, s.retry, s.idempotent) |
| |
| return rp, err |
| } |
| |
| func (c *grpcStorageClient) SetIamPolicy(ctx context.Context, resource string, policy *iampb.Policy, opts ...storageOption) error { |
| // TODO: Need a way to set UserProject, potentially in X-Goog-User-Project system parameter. |
| s := callSettings(c.settings, opts...) |
| |
| req := &iampb.SetIamPolicyRequest{ |
| Resource: bucketResourceName(globalProjectAlias, resource), |
| Policy: policy, |
| } |
| |
| return run(ctx, func() error { |
| _, err := c.raw.SetIamPolicy(ctx, req, s.gax...) |
| return err |
| }, s.retry, s.idempotent) |
| } |
| |
| func (c *grpcStorageClient) TestIamPermissions(ctx context.Context, resource string, permissions []string, opts ...storageOption) ([]string, error) { |
| // TODO: Need a way to set UserProject, potentially in X-Goog-User-Project system parameter. |
| s := callSettings(c.settings, opts...) |
| req := &iampb.TestIamPermissionsRequest{ |
| Resource: bucketResourceName(globalProjectAlias, resource), |
| Permissions: permissions, |
| } |
| var res *iampb.TestIamPermissionsResponse |
| err := run(ctx, func() error { |
| var err error |
| res, err = c.raw.TestIamPermissions(ctx, req, s.gax...) |
| return err |
| }, s.retry, s.idempotent) |
| if err != nil { |
| return nil, err |
| } |
| return res.Permissions, nil |
| } |
| |
| // HMAC Key methods. |
| |
| func (c *grpcStorageClient) GetHMACKey(ctx context.Context, desc *hmacKeyDesc, opts ...storageOption) (*HMACKey, error) { |
| return nil, errMethodNotSupported |
| } |
| func (c *grpcStorageClient) ListHMACKey(ctx context.Context, desc *hmacKeyDesc, opts ...storageOption) *HMACKeysIterator { |
| return &HMACKeysIterator{} |
| } |
| func (c *grpcStorageClient) UpdateHMACKey(ctx context.Context, desc *hmacKeyDesc, attrs *HMACKeyAttrsToUpdate, opts ...storageOption) (*HMACKey, error) { |
| return nil, errMethodNotSupported |
| } |
| func (c *grpcStorageClient) CreateHMACKey(ctx context.Context, desc *hmacKeyDesc, opts ...storageOption) (*HMACKey, error) { |
| return nil, errMethodNotSupported |
| } |
| func (c *grpcStorageClient) DeleteHMACKey(ctx context.Context, desc *hmacKeyDesc, opts ...storageOption) error { |
| return errMethodNotSupported |
| } |