From 01d0b026e967aed3704687fb342498485a31662c Mon Sep 17 00:00:00 2001 From: Andy Goldstein Date: Fri, 29 Jun 2018 13:54:44 -0400 Subject: [PATCH] Fail backup if it already exists in object storage Signed-off-by: Andy Goldstein --- pkg/cloudprovider/aws/object_store.go | 54 ++++++ pkg/cloudprovider/aws/object_store_test.go | 96 +++++++++- pkg/cloudprovider/azure/object_store.go | 19 ++ pkg/cloudprovider/azure/object_store_test.go | 153 +++++++++++++++ pkg/cloudprovider/gcp/object_store.go | 18 ++ pkg/cloudprovider/gcp/object_store_test.go | 56 +++++- pkg/cloudprovider/mocks/object_store.go | 21 +++ pkg/controller/backup_controller.go | 7 +- pkg/controller/backup_controller_test.go | 2 + .../clientmgmt/restartable_object_store.go | 9 + pkg/plugin/framework/object_store_client.go | 15 ++ pkg/plugin/framework/object_store_server.go | 21 +++ pkg/plugin/generated/BackupItemAction.pb.go | 2 + pkg/plugin/generated/ObjectStore.pb.go | 176 +++++++++++++----- pkg/plugin/generated/RestoreItemAction.pb.go | 43 ++--- pkg/plugin/proto/ObjectStore.proto | 11 ++ pkg/plugin/velero/object_store.go | 3 + pkg/util/test/comparisons.go | 6 +- 18 files changed, 640 insertions(+), 72 deletions(-) create mode 100644 pkg/cloudprovider/azure/object_store_test.go diff --git a/pkg/cloudprovider/aws/object_store.go b/pkg/cloudprovider/aws/object_store.go index 257c65124..eadc0a875 100644 --- a/pkg/cloudprovider/aws/object_store.go +++ b/pkg/cloudprovider/aws/object_store.go @@ -23,7 +23,9 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/endpoints" + "github.com/aws/aws-sdk-go/aws/request" v4 "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" @@ -42,6 +44,14 @@ const ( signatureVersionKey = "signatureVersion" ) +type s3Interface interface { + HeadObject(input *s3.HeadObjectInput) (*s3.HeadObjectOutput, error) + GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) + ListObjectsV2Pages(input *s3.ListObjectsV2Input, fn func(*s3.ListObjectsV2Output, bool) bool) error + DeleteObject(input *s3.DeleteObjectInput) (*s3.DeleteObjectOutput, error) + GetObjectRequest(input *s3.GetObjectInput) (req *request.Request, output *s3.GetObjectOutput) +} + type ObjectStore struct { log logrus.FieldLogger s3 *s3.S3 @@ -191,6 +201,50 @@ func (o *ObjectStore) PutObject(bucket, key string, body io.Reader) error { return errors.Wrapf(err, "error putting object %s", key) } +const notFoundCode = "NotFound" + +func (o *ObjectStore) ObjectExists(bucket, key string) (bool, error) { + log := o.log.WithFields( + logrus.Fields{ + "bucket": bucket, + "key": key, + }, + ) + + req := &s3.HeadObjectInput{ + Bucket: aws.String(bucket), + Key: aws.String(key), + } + + log.Debug("Checking if object exists") + _, err := o.s3.HeadObject(req) + + if err == nil { + log.Debug("Object exists") + return true, nil + } + + log.Debug("Checking for AWS specific error information") + if aerr, ok := err.(awserr.Error); ok { + log.WithFields( + logrus.Fields{ + "code": aerr.Code(), + "message": aerr.Message(), + }, + ).Debugf("awserr.Error contents (origErr=%v)", aerr.OrigErr()) + + // The code will be NotFound if the key doesn't exist. + // See https://github.com/aws/aws-sdk-go/issues/1208 and https://github.com/aws/aws-sdk-go/pull/1213. + log.Debugf("Checking for code=%s", notFoundCode) + if aerr.Code() == notFoundCode { + log.Debug("Object doesn't exist - got not found") + return false, nil + } + } + + return false, errors.WithStack(err) +} + func (o *ObjectStore) GetObject(bucket, key string) (io.ReadCloser, error) { req := &s3.GetObjectInput{ Bucket: &bucket, diff --git a/pkg/cloudprovider/aws/object_store_test.go b/pkg/cloudprovider/aws/object_store_test.go index 7be963ac6..57c97968a 100644 --- a/pkg/cloudprovider/aws/object_store_test.go +++ b/pkg/cloudprovider/aws/object_store_test.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +Copyright 2018 the Heptio Ark contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,7 +19,15 @@ package aws import ( "testing" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/heptio/ark/pkg/util/test" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" ) func TestIsValidSignatureVersion(t *testing.T) { @@ -27,3 +35,89 @@ func TestIsValidSignatureVersion(t *testing.T) { assert.True(t, isValidSignatureVersion("4")) assert.False(t, isValidSignatureVersion("3")) } + +type mockS3 struct { + mock.Mock +} + +func (m *mockS3) HeadObject(input *s3.HeadObjectInput) (*s3.HeadObjectOutput, error) { + args := m.Called(input) + return args.Get(0).(*s3.HeadObjectOutput), args.Error(1) +} + +func (m *mockS3) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) { + args := m.Called(input) + return args.Get(0).(*s3.GetObjectOutput), args.Error(1) +} + +func (m *mockS3) ListObjectsV2Pages(input *s3.ListObjectsV2Input, fn func(*s3.ListObjectsV2Output, bool) bool) error { + args := m.Called(input, fn) + return args.Error(0) +} + +func (m *mockS3) DeleteObject(input *s3.DeleteObjectInput) (*s3.DeleteObjectOutput, error) { + args := m.Called(input) + return args.Get(0).(*s3.DeleteObjectOutput), args.Error(1) +} + +func (m *mockS3) GetObjectRequest(input *s3.GetObjectInput) (req *request.Request, output *s3.GetObjectOutput) { + args := m.Called(input) + return args.Get(0).(*request.Request), args.Get(1).(*s3.GetObjectOutput) +} + +func TestObjectExists(t *testing.T) { + tests := []struct { + name string + errorResponse error + expectedExists bool + expectedError string + }{ + { + name: "exists", + errorResponse: nil, + expectedExists: true, + }, + { + name: "doesn't exist", + errorResponse: awserr.New(s3.ErrCodeNoSuchKey, "no such key", nil), + expectedExists: false, + }, + { + name: "error checking for existence", + errorResponse: errors.Errorf("bad"), + expectedExists: false, + expectedError: "bad", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + s := new(mockS3) + defer s.AssertExpectations(t) + + o := &objectStore{ + logger: test.NewLogger(), + s3: s, + } + + bucket := "b" + key := "k" + req := &s3.HeadObjectInput{ + Bucket: aws.String(bucket), + Key: aws.String(key), + } + + s.On("HeadObject", req).Return(&s3.HeadObjectOutput{}, tc.errorResponse) + + exists, err := o.ObjectExists(bucket, key) + + if tc.expectedError != "" { + assert.EqualError(t, err, tc.expectedError) + return + } + require.NoError(t, err) + + assert.Equal(t, tc.expectedExists, exists) + }) + } +} diff --git a/pkg/cloudprovider/azure/object_store.go b/pkg/cloudprovider/azure/object_store.go index c7858d80c..aada2c25f 100644 --- a/pkg/cloudprovider/azure/object_store.go +++ b/pkg/cloudprovider/azure/object_store.go @@ -141,6 +141,25 @@ func (o *ObjectStore) PutObject(bucket, key string, body io.Reader) error { return errors.WithStack(blob.CreateBlockBlobFromReader(body, nil)) } +func (o *ObjectStore) ObjectExists(bucket, key string) (bool, error) { + container, err := getContainerReference(o.blobClient, bucket) + if err != nil { + return false, err + } + + blob, err := getBlobReference(container, key) + if err != nil { + return false, err + } + + exists, err := blob.Exists() + if err != nil { + return false, errors.WithStack(err) + } + + return exists, nil +} + func (o *ObjectStore) GetObject(bucket, key string) (io.ReadCloser, error) { container, err := getContainerReference(o.blobClient, bucket) if err != nil { diff --git a/pkg/cloudprovider/azure/object_store_test.go b/pkg/cloudprovider/azure/object_store_test.go new file mode 100644 index 000000000..30c650898 --- /dev/null +++ b/pkg/cloudprovider/azure/object_store_test.go @@ -0,0 +1,153 @@ +/* +Copyright 2018 the Heptio Ark contributors. + +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 azure + +import ( + "io" + "testing" + "time" + + "github.com/Azure/azure-sdk-for-go/storage" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +type mockBlobGetter struct { + mock.Mock +} + +func (m *mockBlobGetter) getBlob(bucket string, key string) (blob, error) { + args := m.Called(bucket, key) + return args.Get(0).(blob), args.Error(1) +} + +type mockBlob struct { + mock.Mock +} + +func (m *mockBlob) CreateBlockBlobFromReader(blob io.Reader, options *storage.PutBlobOptions) error { + args := m.Called(blob, options) + return args.Error(0) +} + +func (m *mockBlob) Exists() (bool, error) { + args := m.Called() + return args.Bool(0), args.Error(1) +} + +func (m *mockBlob) Get(options *storage.GetBlobOptions) (io.ReadCloser, error) { + args := m.Called(options) + return args.Get(0).(io.ReadCloser), args.Error(1) +} + +func (m *mockBlob) Delete(options *storage.DeleteBlobOptions) error { + args := m.Called(options) + return args.Error(0) +} + +func (m *mockBlob) GetSASURI(expiry time.Time, permissions string) (string, error) { + args := m.Called(expiry, permissions) + return args.String(0), args.Error(1) +} + +type mockContainerGetter struct { + mock.Mock +} + +func (m *mockContainerGetter) getContainer(bucket string) (container, error) { + args := m.Called(bucket) + return args.Get(0).(container), args.Error(1) +} + +type mockContainer struct { + mock.Mock +} + +func (m *mockContainer) ListBlobs(params storage.ListBlobsParameters) (storage.BlobListResponse, error) { + args := m.Called(params) + return args.Get(0).(storage.BlobListResponse), args.Error(1) +} + +func TestObjectExists(t *testing.T) { + tests := []struct { + name string + getBlobError error + exists bool + errorResponse error + expectedExists bool + expectedError string + }{ + { + name: "getBlob error", + exists: false, + errorResponse: errors.New("getBlob"), + expectedExists: false, + expectedError: "getBlob", + }, + { + name: "exists", + exists: true, + errorResponse: nil, + expectedExists: true, + }, + { + name: "doesn't exist", + exists: false, + errorResponse: nil, + expectedExists: false, + }, + { + name: "error checking for existence", + exists: false, + errorResponse: errors.New("bad"), + expectedExists: false, + expectedError: "bad", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + blobGetter := new(mockBlobGetter) + defer blobGetter.AssertExpectations(t) + + o := &objectStore{ + blobGetter: blobGetter, + } + + bucket := "b" + key := "k" + + blob := new(mockBlob) + defer blob.AssertExpectations(t) + blobGetter.On("getBlob", bucket, key).Return(blob, tc.getBlobError) + + blob.On("Exists").Return(tc.exists, tc.errorResponse) + + exists, err := o.ObjectExists(bucket, key) + + if tc.expectedError != "" { + assert.EqualError(t, err, tc.expectedError) + return + } + require.NoError(t, err) + + assert.Equal(t, tc.expectedExists, exists) + }) + } +} diff --git a/pkg/cloudprovider/gcp/object_store.go b/pkg/cloudprovider/gcp/object_store.go index b244a9eb7..c9cad80aa 100644 --- a/pkg/cloudprovider/gcp/object_store.go +++ b/pkg/cloudprovider/gcp/object_store.go @@ -39,6 +39,7 @@ const credentialsEnvVar = "GOOGLE_APPLICATION_CREDENTIALS" type bucketWriter interface { // getWriteCloser returns an io.WriteCloser that can be used to upload data to the specified bucket for the specified key. getWriteCloser(bucket, key string) io.WriteCloser + getAttrs(bucket, key string) (*storage.ObjectAttrs, error) } type writer struct { @@ -49,6 +50,10 @@ func (w *writer) getWriteCloser(bucket, key string) io.WriteCloser { return w.client.Bucket(bucket).Object(key).NewWriter(context.Background()) } +func (w *writer) getAttrs(bucket, key string) (*storage.ObjectAttrs, error) { + return w.client.Bucket(bucket).Object(key).Attrs(context.Background()) +} + type ObjectStore struct { log logrus.FieldLogger client *storage.Client @@ -117,6 +122,19 @@ func (o *ObjectStore) PutObject(bucket, key string, body io.Reader) error { return closeErr } +func (o *ObjectStore) ObjectExists(bucket, key string) (bool, error) { + _, err := o.bucketWriter.getAttrs(bucket, key) + + switch err { + case nil: + return true, nil + case storage.ErrObjectNotExist: + return false, nil + } + + return false, errors.WithStack(err) +} + func (o *ObjectStore) GetObject(bucket, key string) (io.ReadCloser, error) { r, err := o.client.Bucket(bucket).Object(key).NewReader(context.Background()) if err != nil { diff --git a/pkg/cloudprovider/gcp/object_store_test.go b/pkg/cloudprovider/gcp/object_store_test.go index 4b4ec9224..2212090d1 100644 --- a/pkg/cloudprovider/gcp/object_store_test.go +++ b/pkg/cloudprovider/gcp/object_store_test.go @@ -21,7 +21,9 @@ import ( "strings" "testing" + "cloud.google.com/go/storage" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" velerotest "github.com/heptio/velero/pkg/util/test" ) @@ -45,6 +47,8 @@ func newMockWriteCloser(writeErr, closeErr error) *mockWriteCloser { type fakeWriter struct { wc *mockWriteCloser + + attrsErr error } func newFakeWriter(wc *mockWriteCloser) *fakeWriter { @@ -55,6 +59,10 @@ func (fw *fakeWriter) getWriteCloser(bucket, name string) io.WriteCloser { return fw.wc } +func (fw *fakeWriter) getAttrs(bucket, key string) (*storage.ObjectAttrs, error) { + return new(storage.ObjectAttrs), fw.attrsErr +} + func TestPutObject(t *testing.T) { tests := []struct { name string @@ -93,8 +101,54 @@ func TestPutObject(t *testing.T) { o.bucketWriter = newFakeWriter(wc) err := o.PutObject("bucket", "key", strings.NewReader("contents")) - assert.Equal(t, test.expectedErr, err) }) } } + +func TestObjectExists(t *testing.T) { + tests := []struct { + name string + errorResponse error + expectedExists bool + expectedError string + }{ + { + name: "exists", + errorResponse: nil, + expectedExists: true, + }, + { + name: "doesn't exist", + errorResponse: storage.ErrObjectNotExist, + expectedExists: false, + }, + { + name: "error checking for existence", + errorResponse: errors.New("bad"), + expectedExists: false, + expectedError: "bad", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + o := NewObjectStore().(*objectStore) + w := newFakeWriter(nil) + o.bucketWriter = w + w.attrsErr = tc.errorResponse + + bucket := "b" + key := "k" + exists, err := o.ObjectExists(bucket, key) + + if tc.expectedError != "" { + assert.EqualError(t, err, tc.expectedError) + return + } + require.NoError(t, err) + + assert.Equal(t, tc.expectedExists, exists) + }) + } +} diff --git a/pkg/cloudprovider/mocks/object_store.go b/pkg/cloudprovider/mocks/object_store.go index d17b024ed..3a6cd0598 100644 --- a/pkg/cloudprovider/mocks/object_store.go +++ b/pkg/cloudprovider/mocks/object_store.go @@ -144,6 +144,27 @@ func (_m *ObjectStore) ListObjects(bucket string, prefix string) ([]string, erro return r0, r1 } +// ObjectExists provides a mock function with given fields: bucket, key +func (_m *ObjectStore) ObjectExists(bucket string, key string) (bool, error) { + ret := _m.Called(bucket, key) + + var r0 bool + if rf, ok := ret.Get(0).(func(string, string) bool); ok { + r0 = rf(bucket, key) + } else { + r0 = ret.Get(0).(bool) + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(bucket, key) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // PutObject provides a mock function with given fields: bucket, key, body func (_m *ObjectStore) PutObject(bucket string, key string, body io.Reader) error { ret := _m.Called(bucket, key, body) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 589f41fc8..010a344ca 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -158,11 +158,16 @@ func (c *backupController) processBackup(key string) error { log.Debug("Running processBackup") ns, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { - return errors.Wrap(err, "error splitting queue key") + logContext.WithError(err).Errorf("error splitting key") + return nil } log.Debug("Getting backup") original, err := c.lister.Backups(ns).Get(name) + if apierrors.IsNotFound(err) { + log.Debug("backup not found") + return nil + } if err != nil { return errors.Wrap(err, "error getting backup") } diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index 5cf56694e..00f086c2c 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -101,6 +101,8 @@ func TestProcessBackupNonProcessedItems(t *testing.T) { sharedInformers = informers.NewSharedInformerFactory(fake.NewSimpleClientset(), 0) logger = logging.DefaultLogger(logrus.DebugLevel) ) + defer backupper.AssertExpectations(t) + defer cloudBackups.AssertExpectations(t) c := &backupController{ genericController: newGenericController("backup-test", logger), diff --git a/pkg/plugin/clientmgmt/restartable_object_store.go b/pkg/plugin/clientmgmt/restartable_object_store.go index 120677ea9..378202fde 100644 --- a/pkg/plugin/clientmgmt/restartable_object_store.go +++ b/pkg/plugin/clientmgmt/restartable_object_store.go @@ -120,6 +120,15 @@ func (r *restartableObjectStore) PutObject(bucket string, key string, body io.Re return delegate.PutObject(bucket, key, body) } +// ObjectExists restarts the plugin's process if needed, then delegates the call. +func (r *restartableObjectStore) ObjectExists(bucket, key string) (bool, error) { + delegate, err := r.getDelegate() + if err != nil { + return false, err + } + return delegate.ObjectExists(bucket, key) +} + // GetObject restarts the plugin's process if needed, then delegates the call. func (r *restartableObjectStore) GetObject(bucket string, key string) (io.ReadCloser, error) { delegate, err := r.getDelegate() diff --git a/pkg/plugin/framework/object_store_client.go b/pkg/plugin/framework/object_store_client.go index b2dc64ba5..74e8232d0 100644 --- a/pkg/plugin/framework/object_store_client.go +++ b/pkg/plugin/framework/object_store_client.go @@ -96,6 +96,21 @@ func (c *ObjectStoreGRPCClient) PutObject(bucket, key string, body io.Reader) er } } +// ObjectExists checks if there is an object with the given key in the object storage bucket. +func (c *ObjectStoreGRPCClient) ObjectExists(bucket, key string) (bool, error) { + req := &proto.ObjectExistsRequest{ + Bucket: bucket, + Key: key, + } + + res, err := c.grpcClient.ObjectExists(context.Background(), req) + if err != nil { + return false, err + } + + return res.Exists, nil +} + // GetObject retrieves the object with the given key from the specified // bucket in object storage. func (c *ObjectStoreGRPCClient) GetObject(bucket, key string) (io.ReadCloser, error) { diff --git a/pkg/plugin/framework/object_store_server.go b/pkg/plugin/framework/object_store_server.go index 9a345ac32..b17fa31f2 100644 --- a/pkg/plugin/framework/object_store_server.go +++ b/pkg/plugin/framework/object_store_server.go @@ -128,6 +128,27 @@ func (s *ObjectStoreGRPCServer) PutObject(stream proto.ObjectStore_PutObjectServ return nil } +// ObjectExists checks if there is an object with the given key in the object storage bucket. +func (s *ObjectStoreGRPCServer) ObjectExists(ctx context.Context, req *proto.ObjectExistsRequest) (response *proto.ObjectExistsResponse, err error) { + defer func() { + if recoveredErr := handlePanic(recover()); recoveredErr != nil { + err = recoveredErr + } + }() + + impl, err := s.getImpl(req.Plugin) + if err != nil { + return nil, newGRPCError(err) + } + + exists, err := impl.ObjectExists(req.Bucket, req.Key) + if err != nil { + return nil, err + } + + return &proto.ObjectExistsResponse{Exists: exists}, nil +} + // GetObject retrieves the object with the given key from the specified // bucket in object storage. func (s *ObjectStoreGRPCServer) GetObject(req *proto.GetObjectRequest, stream proto.ObjectStore_GetObjectServer) (err error) { diff --git a/pkg/plugin/generated/BackupItemAction.pb.go b/pkg/plugin/generated/BackupItemAction.pb.go index c9670e3c3..e8f15519c 100644 --- a/pkg/plugin/generated/BackupItemAction.pb.go +++ b/pkg/plugin/generated/BackupItemAction.pb.go @@ -18,6 +18,8 @@ It has these top-level messages: BackupItemActionAppliesToRequest BackupItemActionAppliesToResponse PutObjectRequest + ObjectExistsRequest + ObjectExistsResponse GetObjectRequest Bytes ListCommonPrefixesRequest diff --git a/pkg/plugin/generated/ObjectStore.pb.go b/pkg/plugin/generated/ObjectStore.pb.go index dd485e144..d93e615fd 100644 --- a/pkg/plugin/generated/ObjectStore.pb.go +++ b/pkg/plugin/generated/ObjectStore.pb.go @@ -57,6 +57,54 @@ func (m *PutObjectRequest) GetBody() []byte { return nil } +type ObjectExistsRequest struct { + Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` + Bucket string `protobuf:"bytes,2,opt,name=bucket" json:"bucket,omitempty"` + Key string `protobuf:"bytes,3,opt,name=key" json:"key,omitempty"` +} + +func (m *ObjectExistsRequest) Reset() { *m = ObjectExistsRequest{} } +func (m *ObjectExistsRequest) String() string { return proto.CompactTextString(m) } +func (*ObjectExistsRequest) ProtoMessage() {} +func (*ObjectExistsRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} } + +func (m *ObjectExistsRequest) GetPlugin() string { + if m != nil { + return m.Plugin + } + return "" +} + +func (m *ObjectExistsRequest) GetBucket() string { + if m != nil { + return m.Bucket + } + return "" +} + +func (m *ObjectExistsRequest) GetKey() string { + if m != nil { + return m.Key + } + return "" +} + +type ObjectExistsResponse struct { + Exists bool `protobuf:"varint,1,opt,name=exists" json:"exists,omitempty"` +} + +func (m *ObjectExistsResponse) Reset() { *m = ObjectExistsResponse{} } +func (m *ObjectExistsResponse) String() string { return proto.CompactTextString(m) } +func (*ObjectExistsResponse) ProtoMessage() {} +func (*ObjectExistsResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} } + +func (m *ObjectExistsResponse) GetExists() bool { + if m != nil { + return m.Exists + } + return false +} + type GetObjectRequest struct { Plugin string `protobuf:"bytes,1,opt,name=plugin" json:"plugin,omitempty"` Bucket string `protobuf:"bytes,2,opt,name=bucket" json:"bucket,omitempty"` @@ -66,7 +114,7 @@ type GetObjectRequest struct { func (m *GetObjectRequest) Reset() { *m = GetObjectRequest{} } func (m *GetObjectRequest) String() string { return proto.CompactTextString(m) } func (*GetObjectRequest) ProtoMessage() {} -func (*GetObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} } +func (*GetObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} } func (m *GetObjectRequest) GetPlugin() string { if m != nil { @@ -96,7 +144,7 @@ type Bytes struct { func (m *Bytes) Reset() { *m = Bytes{} } func (m *Bytes) String() string { return proto.CompactTextString(m) } func (*Bytes) ProtoMessage() {} -func (*Bytes) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} } +func (*Bytes) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} } func (m *Bytes) GetData() []byte { if m != nil { @@ -115,7 +163,7 @@ type ListCommonPrefixesRequest struct { func (m *ListCommonPrefixesRequest) Reset() { *m = ListCommonPrefixesRequest{} } func (m *ListCommonPrefixesRequest) String() string { return proto.CompactTextString(m) } func (*ListCommonPrefixesRequest) ProtoMessage() {} -func (*ListCommonPrefixesRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} } +func (*ListCommonPrefixesRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} } func (m *ListCommonPrefixesRequest) GetPlugin() string { if m != nil { @@ -152,7 +200,7 @@ type ListCommonPrefixesResponse struct { func (m *ListCommonPrefixesResponse) Reset() { *m = ListCommonPrefixesResponse{} } func (m *ListCommonPrefixesResponse) String() string { return proto.CompactTextString(m) } func (*ListCommonPrefixesResponse) ProtoMessage() {} -func (*ListCommonPrefixesResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} } +func (*ListCommonPrefixesResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{6} } func (m *ListCommonPrefixesResponse) GetPrefixes() []string { if m != nil { @@ -170,7 +218,7 @@ type ListObjectsRequest struct { func (m *ListObjectsRequest) Reset() { *m = ListObjectsRequest{} } func (m *ListObjectsRequest) String() string { return proto.CompactTextString(m) } func (*ListObjectsRequest) ProtoMessage() {} -func (*ListObjectsRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} } +func (*ListObjectsRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} } func (m *ListObjectsRequest) GetPlugin() string { if m != nil { @@ -200,7 +248,7 @@ type ListObjectsResponse struct { func (m *ListObjectsResponse) Reset() { *m = ListObjectsResponse{} } func (m *ListObjectsResponse) String() string { return proto.CompactTextString(m) } func (*ListObjectsResponse) ProtoMessage() {} -func (*ListObjectsResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{6} } +func (*ListObjectsResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{8} } func (m *ListObjectsResponse) GetKeys() []string { if m != nil { @@ -218,7 +266,7 @@ type DeleteObjectRequest struct { func (m *DeleteObjectRequest) Reset() { *m = DeleteObjectRequest{} } func (m *DeleteObjectRequest) String() string { return proto.CompactTextString(m) } func (*DeleteObjectRequest) ProtoMessage() {} -func (*DeleteObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} } +func (*DeleteObjectRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{9} } func (m *DeleteObjectRequest) GetPlugin() string { if m != nil { @@ -251,7 +299,7 @@ type CreateSignedURLRequest struct { func (m *CreateSignedURLRequest) Reset() { *m = CreateSignedURLRequest{} } func (m *CreateSignedURLRequest) String() string { return proto.CompactTextString(m) } func (*CreateSignedURLRequest) ProtoMessage() {} -func (*CreateSignedURLRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{8} } +func (*CreateSignedURLRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{10} } func (m *CreateSignedURLRequest) GetPlugin() string { if m != nil { @@ -288,7 +336,7 @@ type CreateSignedURLResponse struct { func (m *CreateSignedURLResponse) Reset() { *m = CreateSignedURLResponse{} } func (m *CreateSignedURLResponse) String() string { return proto.CompactTextString(m) } func (*CreateSignedURLResponse) ProtoMessage() {} -func (*CreateSignedURLResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{9} } +func (*CreateSignedURLResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{11} } func (m *CreateSignedURLResponse) GetUrl() string { if m != nil { @@ -305,7 +353,7 @@ type ObjectStoreInitRequest struct { func (m *ObjectStoreInitRequest) Reset() { *m = ObjectStoreInitRequest{} } func (m *ObjectStoreInitRequest) String() string { return proto.CompactTextString(m) } func (*ObjectStoreInitRequest) ProtoMessage() {} -func (*ObjectStoreInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{10} } +func (*ObjectStoreInitRequest) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{12} } func (m *ObjectStoreInitRequest) GetPlugin() string { if m != nil { @@ -323,6 +371,8 @@ func (m *ObjectStoreInitRequest) GetConfig() map[string]string { func init() { proto.RegisterType((*PutObjectRequest)(nil), "generated.PutObjectRequest") + proto.RegisterType((*ObjectExistsRequest)(nil), "generated.ObjectExistsRequest") + proto.RegisterType((*ObjectExistsResponse)(nil), "generated.ObjectExistsResponse") proto.RegisterType((*GetObjectRequest)(nil), "generated.GetObjectRequest") proto.RegisterType((*Bytes)(nil), "generated.Bytes") proto.RegisterType((*ListCommonPrefixesRequest)(nil), "generated.ListCommonPrefixesRequest") @@ -348,6 +398,7 @@ const _ = grpc.SupportPackageIsVersion4 type ObjectStoreClient interface { Init(ctx context.Context, in *ObjectStoreInitRequest, opts ...grpc.CallOption) (*Empty, error) PutObject(ctx context.Context, opts ...grpc.CallOption) (ObjectStore_PutObjectClient, error) + ObjectExists(ctx context.Context, in *ObjectExistsRequest, opts ...grpc.CallOption) (*ObjectExistsResponse, error) GetObject(ctx context.Context, in *GetObjectRequest, opts ...grpc.CallOption) (ObjectStore_GetObjectClient, error) ListCommonPrefixes(ctx context.Context, in *ListCommonPrefixesRequest, opts ...grpc.CallOption) (*ListCommonPrefixesResponse, error) ListObjects(ctx context.Context, in *ListObjectsRequest, opts ...grpc.CallOption) (*ListObjectsResponse, error) @@ -406,6 +457,15 @@ func (x *objectStorePutObjectClient) CloseAndRecv() (*Empty, error) { return m, nil } +func (c *objectStoreClient) ObjectExists(ctx context.Context, in *ObjectExistsRequest, opts ...grpc.CallOption) (*ObjectExistsResponse, error) { + out := new(ObjectExistsResponse) + err := grpc.Invoke(ctx, "/generated.ObjectStore/ObjectExists", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *objectStoreClient) GetObject(ctx context.Context, in *GetObjectRequest, opts ...grpc.CallOption) (ObjectStore_GetObjectClient, error) { stream, err := grpc.NewClientStream(ctx, &_ObjectStore_serviceDesc.Streams[1], c.cc, "/generated.ObjectStore/GetObject", opts...) if err != nil { @@ -479,6 +539,7 @@ func (c *objectStoreClient) CreateSignedURL(ctx context.Context, in *CreateSigne type ObjectStoreServer interface { Init(context.Context, *ObjectStoreInitRequest) (*Empty, error) PutObject(ObjectStore_PutObjectServer) error + ObjectExists(context.Context, *ObjectExistsRequest) (*ObjectExistsResponse, error) GetObject(*GetObjectRequest, ObjectStore_GetObjectServer) error ListCommonPrefixes(context.Context, *ListCommonPrefixesRequest) (*ListCommonPrefixesResponse, error) ListObjects(context.Context, *ListObjectsRequest) (*ListObjectsResponse, error) @@ -534,6 +595,24 @@ func (x *objectStorePutObjectServer) Recv() (*PutObjectRequest, error) { return m, nil } +func _ObjectStore_ObjectExists_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ObjectExistsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ObjectStoreServer).ObjectExists(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/generated.ObjectStore/ObjectExists", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ObjectStoreServer).ObjectExists(ctx, req.(*ObjectExistsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _ObjectStore_GetObject_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(GetObjectRequest) if err := stream.RecvMsg(m); err != nil { @@ -635,6 +714,10 @@ var _ObjectStore_serviceDesc = grpc.ServiceDesc{ MethodName: "Init", Handler: _ObjectStore_Init_Handler, }, + { + MethodName: "ObjectExists", + Handler: _ObjectStore_ObjectExists_Handler, + }, { MethodName: "ListCommonPrefixes", Handler: _ObjectStore_ListCommonPrefixes_Handler, @@ -670,39 +753,42 @@ var _ObjectStore_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("ObjectStore.proto", fileDescriptor1) } var fileDescriptor1 = []byte{ - // 531 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x5f, 0x8b, 0xd3, 0x40, - 0x10, 0x67, 0x9b, 0x5c, 0x31, 0xd3, 0x82, 0x71, 0x4f, 0x6a, 0xcc, 0xa9, 0xd4, 0x45, 0x21, 0x22, - 0x16, 0x39, 0x5f, 0xce, 0x3f, 0x0f, 0x62, 0x2d, 0x22, 0x14, 0x3c, 0x52, 0x45, 0x1f, 0x7c, 0x49, - 0x9b, 0xb9, 0x5e, 0x6c, 0x9a, 0xc4, 0x64, 0x23, 0xe6, 0xd1, 0xcf, 0xe0, 0x37, 0xf1, 0x13, 0x4a, - 0x36, 0x7b, 0xed, 0xa6, 0x97, 0xbb, 0x42, 0xe9, 0xdb, 0xcc, 0xec, 0xcc, 0xfc, 0x7e, 0xb3, 0xbb, - 0xbf, 0x81, 0x5b, 0x9f, 0xa6, 0x3f, 0x70, 0xc6, 0x27, 0x3c, 0x4e, 0x71, 0x90, 0xa4, 0x31, 0x8f, - 0xa9, 0x31, 0xc7, 0x08, 0x53, 0x8f, 0xa3, 0x6f, 0x77, 0x27, 0xe7, 0x5e, 0x8a, 0x7e, 0x75, 0xc0, - 0xce, 0xc1, 0x3c, 0xcd, 0x79, 0x55, 0xe0, 0xe2, 0xcf, 0x1c, 0x33, 0x4e, 0x7b, 0xd0, 0x4e, 0xc2, - 0x7c, 0x1e, 0x44, 0x16, 0xe9, 0x13, 0xc7, 0x70, 0xa5, 0x57, 0xc6, 0xa7, 0xf9, 0x6c, 0x81, 0xdc, - 0x6a, 0x55, 0xf1, 0xca, 0xa3, 0x26, 0x68, 0x0b, 0x2c, 0x2c, 0x4d, 0x04, 0x4b, 0x93, 0x52, 0xd0, - 0xa7, 0xb1, 0x5f, 0x58, 0x7a, 0x9f, 0x38, 0x5d, 0x57, 0xd8, 0xec, 0x33, 0x98, 0x1f, 0x70, 0xdf, - 0x48, 0xec, 0x08, 0x0e, 0xde, 0x15, 0x1c, 0xb3, 0x12, 0xd2, 0xf7, 0xb8, 0x27, 0x1a, 0x75, 0x5d, - 0x61, 0xb3, 0x3f, 0x04, 0xee, 0x8e, 0x83, 0x8c, 0x0f, 0xe3, 0xe5, 0x32, 0x8e, 0x4e, 0x53, 0x3c, - 0x0b, 0x7e, 0x63, 0xb6, 0x2b, 0xf8, 0x3d, 0x30, 0x7c, 0x0c, 0x83, 0x65, 0xc0, 0x31, 0x95, 0x14, - 0xd6, 0x01, 0xd1, 0x4d, 0x00, 0x88, 0xa1, 0xcb, 0x6e, 0xc2, 0x63, 0x27, 0x60, 0x37, 0x51, 0xc8, - 0x92, 0x38, 0xca, 0x90, 0xda, 0x70, 0x23, 0x91, 0x31, 0x8b, 0xf4, 0x35, 0xc7, 0x70, 0x57, 0x3e, - 0xfb, 0x0e, 0xb4, 0xac, 0xac, 0x6e, 0x6c, 0x67, 0xd6, 0x6b, 0x5e, 0x5a, 0x8d, 0xd7, 0x13, 0x38, - 0xac, 0x75, 0x97, 0x84, 0x28, 0xe8, 0x0b, 0x2c, 0x2e, 0xc8, 0x08, 0x9b, 0x7d, 0x85, 0xc3, 0xf7, - 0x18, 0x22, 0xc7, 0x7d, 0x3f, 0x5e, 0x08, 0xbd, 0x61, 0x8a, 0x1e, 0xc7, 0x49, 0x30, 0x8f, 0xd0, - 0xff, 0xe2, 0x8e, 0xf7, 0xf7, 0x05, 0x4d, 0xd0, 0x38, 0x0f, 0xc5, 0x63, 0x68, 0x6e, 0x69, 0xb2, - 0xa7, 0x70, 0xe7, 0x12, 0x9a, 0x9c, 0xda, 0x04, 0x2d, 0x4f, 0x43, 0x89, 0x55, 0x9a, 0xec, 0x1f, - 0x81, 0x9e, 0x22, 0xa3, 0x8f, 0x51, 0xb0, 0x75, 0xee, 0x11, 0xb4, 0x67, 0x71, 0x74, 0x16, 0xcc, - 0xad, 0x56, 0x5f, 0x73, 0x3a, 0xc7, 0xcf, 0x06, 0x2b, 0xd1, 0x0d, 0x9a, 0x5b, 0x0d, 0x86, 0x22, - 0x7f, 0x14, 0xf1, 0xb4, 0x70, 0x65, 0xb1, 0xfd, 0x12, 0x3a, 0x4a, 0xf8, 0x62, 0x32, 0xb2, 0x9e, - 0xec, 0x36, 0x1c, 0xfc, 0xf2, 0xc2, 0x1c, 0xe5, 0x15, 0x54, 0xce, 0xab, 0xd6, 0x09, 0x39, 0xfe, - 0xab, 0x43, 0x47, 0x41, 0xa2, 0xaf, 0x41, 0x2f, 0xd1, 0xe8, 0xc3, 0xad, 0x4c, 0x6c, 0x53, 0x49, - 0x19, 0x2d, 0x13, 0x5e, 0xd0, 0x37, 0x60, 0xac, 0x36, 0x03, 0x3d, 0x52, 0x8e, 0x37, 0xf7, 0xc5, - 0xe5, 0x5a, 0x87, 0x94, 0xd5, 0x2b, 0xb5, 0xd7, 0xaa, 0x37, 0x77, 0x40, 0xad, 0x5a, 0x48, 0xf9, - 0x39, 0xa1, 0x5e, 0xf5, 0xf5, 0xeb, 0xa2, 0xa1, 0x8f, 0x94, 0xcc, 0x2b, 0x65, 0x6d, 0x3f, 0xde, - 0x92, 0x25, 0x9f, 0x7c, 0x0c, 0x1d, 0xe5, 0xff, 0xd3, 0xfb, 0x1b, 0x55, 0x75, 0xd5, 0xd9, 0x0f, - 0xae, 0x3a, 0x96, 0xdd, 0xde, 0x42, 0x57, 0x95, 0x08, 0x55, 0xf3, 0x1b, 0xb4, 0xd3, 0x70, 0xdd, - 0xdf, 0xe0, 0xe6, 0xc6, 0xef, 0xac, 0x3d, 0x5b, 0xb3, 0x4e, 0x6c, 0x76, 0x5d, 0x4a, 0xc5, 0x6d, - 0xda, 0x16, 0x9b, 0xfe, 0xc5, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe6, 0x73, 0xe9, 0xf8, 0x17, - 0x06, 0x00, 0x00, + // 577 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0xd6, 0xc6, 0x69, 0x54, 0x4f, 0x22, 0x61, 0xb6, 0x55, 0x30, 0x2e, 0x94, 0xb0, 0x02, 0x29, + 0x08, 0x11, 0xa1, 0x72, 0x29, 0xd0, 0x03, 0x22, 0x44, 0x08, 0x29, 0x52, 0x2b, 0x07, 0x04, 0x07, + 0x2e, 0x4e, 0x3c, 0x4d, 0x4d, 0x1c, 0x3b, 0xd8, 0x6b, 0x54, 0x1f, 0x79, 0x25, 0x9e, 0x89, 0x07, + 0x41, 0xbb, 0xde, 0x26, 0xde, 0xfc, 0x10, 0xa9, 0xca, 0x6d, 0x66, 0x76, 0xe7, 0x9b, 0x6f, 0x66, + 0xfd, 0x8d, 0xe1, 0xee, 0xf9, 0xf0, 0x07, 0x8e, 0xf8, 0x80, 0xc7, 0x09, 0x76, 0x66, 0x49, 0xcc, + 0x63, 0x6a, 0x8e, 0x31, 0xc2, 0xc4, 0xe3, 0xe8, 0x3b, 0x8d, 0xc1, 0x95, 0x97, 0xa0, 0x5f, 0x1c, + 0xb0, 0x2b, 0xb0, 0x2e, 0x32, 0x5e, 0x24, 0xb8, 0xf8, 0x33, 0xc3, 0x94, 0xd3, 0x26, 0xd4, 0x66, + 0x61, 0x36, 0x0e, 0x22, 0x9b, 0xb4, 0x48, 0xdb, 0x74, 0x95, 0x27, 0xe2, 0xc3, 0x6c, 0x34, 0x41, + 0x6e, 0x57, 0x8a, 0x78, 0xe1, 0x51, 0x0b, 0x8c, 0x09, 0xe6, 0xb6, 0x21, 0x83, 0xc2, 0xa4, 0x14, + 0xaa, 0xc3, 0xd8, 0xcf, 0xed, 0x6a, 0x8b, 0xb4, 0x1b, 0xae, 0xb4, 0xd9, 0x57, 0x38, 0x28, 0xca, + 0xf4, 0xae, 0x83, 0x94, 0xa7, 0x3b, 0x2b, 0xc6, 0x3a, 0x70, 0xa8, 0x03, 0xa7, 0xb3, 0x38, 0x4a, + 0x51, 0x20, 0xa0, 0x8c, 0x48, 0xe4, 0x7d, 0x57, 0x79, 0xec, 0x33, 0x58, 0x1f, 0x71, 0xd7, 0x2d, + 0xb3, 0x23, 0xd8, 0x7b, 0x9f, 0x73, 0x4c, 0x45, 0xef, 0xbe, 0xc7, 0x3d, 0x09, 0xd4, 0x70, 0xa5, + 0xcd, 0x7e, 0x13, 0xb8, 0xdf, 0x0f, 0x52, 0xde, 0x8d, 0xa7, 0xd3, 0x38, 0xba, 0x48, 0xf0, 0x32, + 0xb8, 0xc6, 0x5b, 0x8f, 0xe0, 0x01, 0x98, 0x3e, 0x86, 0xc1, 0x34, 0xe0, 0x98, 0x28, 0x0a, 0x8b, + 0x80, 0x44, 0x93, 0x05, 0xe4, 0xf4, 0x05, 0x9a, 0xf4, 0xd8, 0x29, 0x38, 0xeb, 0x28, 0xa8, 0x61, + 0x39, 0xb0, 0x3f, 0x53, 0x31, 0x9b, 0xb4, 0x8c, 0xb6, 0xe9, 0xce, 0x7d, 0xf6, 0x1d, 0xa8, 0xc8, + 0x2c, 0x26, 0x76, 0x6b, 0xd6, 0x0b, 0x5e, 0x86, 0xc6, 0xeb, 0x19, 0x1c, 0x68, 0xe8, 0x8a, 0x10, + 0x85, 0xea, 0x04, 0xf3, 0x1b, 0x32, 0xd2, 0x16, 0x9f, 0xd0, 0x07, 0x0c, 0x91, 0xe3, 0xae, 0x1f, + 0x2f, 0x84, 0x66, 0x37, 0x41, 0x8f, 0xe3, 0x20, 0x18, 0x47, 0xe8, 0x7f, 0x71, 0xfb, 0xbb, 0xd3, + 0x82, 0x05, 0x06, 0xe7, 0xa1, 0x7c, 0x0c, 0xc3, 0x15, 0x26, 0x7b, 0x0e, 0xf7, 0x56, 0xaa, 0xa9, + 0xae, 0x2d, 0x30, 0xb2, 0x24, 0x54, 0xb5, 0x84, 0xc9, 0xfe, 0x10, 0x68, 0x96, 0xf4, 0xfc, 0x29, + 0x0a, 0xb6, 0xf6, 0xdd, 0x83, 0xda, 0x28, 0x8e, 0x2e, 0x83, 0xb1, 0x5d, 0x69, 0x19, 0xed, 0xfa, + 0xc9, 0x8b, 0xce, 0x5c, 0xfd, 0x9d, 0xf5, 0x50, 0x9d, 0xae, 0xbc, 0xdf, 0x8b, 0x78, 0x92, 0xbb, + 0x2a, 0xd9, 0x79, 0x0d, 0xf5, 0x52, 0xf8, 0xa6, 0x33, 0xb2, 0xe8, 0xec, 0x10, 0xf6, 0x7e, 0x79, + 0x61, 0x86, 0x6a, 0x04, 0x85, 0xf3, 0xa6, 0x72, 0x4a, 0x4e, 0xfe, 0x56, 0xa1, 0x5e, 0xaa, 0x44, + 0xdf, 0x42, 0x55, 0x54, 0xa3, 0x8f, 0xb7, 0x32, 0x71, 0xac, 0xd2, 0x95, 0xde, 0x74, 0xc6, 0x73, + 0x7a, 0x06, 0xe6, 0x7c, 0x45, 0xd1, 0xa3, 0xd2, 0xf1, 0xf2, 0xe2, 0x5a, 0xcd, 0x6d, 0x13, 0x7a, + 0x0e, 0x8d, 0xf2, 0x76, 0xa0, 0xc7, 0x2b, 0x14, 0xb4, 0x7d, 0xe4, 0x3c, 0xda, 0x78, 0xae, 0x9e, + 0xe8, 0x0c, 0xcc, 0xf9, 0xfa, 0xd0, 0xe8, 0x2c, 0x2f, 0x15, 0x8d, 0x8e, 0xdc, 0x0d, 0x2f, 0x09, + 0xf5, 0x0a, 0x2d, 0xe9, 0x2a, 0xa4, 0x4f, 0x4a, 0x37, 0x37, 0xee, 0x09, 0xe7, 0xe9, 0x96, 0x5b, + 0x8a, 0x60, 0x1f, 0xea, 0x25, 0x41, 0xd1, 0x87, 0x4b, 0x59, 0xba, 0x8c, 0x9d, 0xe3, 0x4d, 0xc7, + 0x0a, 0xed, 0x1d, 0x34, 0xca, 0x9a, 0xd3, 0xe6, 0xb7, 0x46, 0x8c, 0x6b, 0xde, 0xef, 0x1b, 0xdc, + 0x59, 0xfa, 0xdc, 0xb5, 0xef, 0x60, 0xbd, 0xf0, 0x1c, 0xf6, 0xbf, 0x2b, 0x05, 0xb7, 0x61, 0x4d, + 0xfe, 0xc3, 0x5e, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x0b, 0x4e, 0x76, 0xa3, 0xf1, 0x06, 0x00, + 0x00, } diff --git a/pkg/plugin/generated/RestoreItemAction.pb.go b/pkg/plugin/generated/RestoreItemAction.pb.go index 30a7d2c75..b4aa4c14d 100644 --- a/pkg/plugin/generated/RestoreItemAction.pb.go +++ b/pkg/plugin/generated/RestoreItemAction.pb.go @@ -242,25 +242,26 @@ var _RestoreItemAction_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("RestoreItemAction.proto", fileDescriptor3) } var fileDescriptor3 = []byte{ - // 319 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xcd, 0x4e, 0xf3, 0x30, - 0x10, 0x94, 0xdb, 0xaa, 0x55, 0xb7, 0xd5, 0xf7, 0x81, 0x0f, 0x60, 0x15, 0x21, 0x42, 0x0e, 0xa8, - 0xe2, 0xa7, 0x87, 0x72, 0xe4, 0x54, 0x24, 0xa8, 0x7a, 0x75, 0x79, 0x81, 0x34, 0x59, 0x5a, 0x8b, - 0x24, 0x36, 0xb6, 0x23, 0x71, 0xe3, 0x11, 0x78, 0x46, 0xde, 0x04, 0xe5, 0x57, 0xb4, 0x81, 0xd0, - 0x9b, 0x77, 0x3d, 0xb3, 0x33, 0x63, 0x2f, 0x1c, 0x73, 0x34, 0x56, 0x6a, 0x5c, 0x58, 0x8c, 0x66, - 0xbe, 0x15, 0x32, 0x9e, 0x28, 0x2d, 0xad, 0xa4, 0xfd, 0x35, 0xc6, 0xa8, 0x3d, 0x8b, 0xc1, 0x68, - 0xb8, 0xdc, 0x78, 0x1a, 0x83, 0xfc, 0xc2, 0xfd, 0x20, 0x70, 0x56, 0x23, 0x3d, 0xbc, 0xa1, 0x9f, - 0x58, 0xe4, 0xf8, 0x9a, 0xa0, 0xb1, 0xf4, 0x08, 0xba, 0x2a, 0x4c, 0xd6, 0x22, 0x66, 0xc4, 0x21, - 0xe3, 0x3e, 0x2f, 0x2a, 0x4a, 0xa1, 0x23, 0x2c, 0x46, 0xac, 0xe5, 0x90, 0xf1, 0x90, 0x67, 0x67, - 0xca, 0xa0, 0xa7, 0xf3, 0x71, 0xac, 0x9d, 0xb5, 0xcb, 0x92, 0x5e, 0xc0, 0xbf, 0x14, 0xf1, 0xa8, - 0x65, 0x74, 0xef, 0xf9, 0x2f, 0x89, 0x62, 0x9d, 0x0c, 0xb0, 0xd3, 0x75, 0xdf, 0xc1, 0xf9, 0xdd, - 0x90, 0x51, 0x32, 0x36, 0x58, 0x29, 0x93, 0x6f, 0xca, 0x73, 0xf8, 0xef, 0x05, 0x81, 0x48, 0xe1, - 0x5e, 0x98, 0x52, 0x0d, 0x6b, 0x39, 0xed, 0xf1, 0x60, 0x7a, 0x3a, 0xa9, 0xc2, 0x4f, 0x38, 0x1a, - 0x99, 0x68, 0x1f, 0x17, 0x01, 0xc6, 0x56, 0x3c, 0x0b, 0xd4, 0x7c, 0x97, 0xe5, 0xde, 0xc1, 0x79, - 0xcd, 0xc0, 0x4c, 0xa9, 0x50, 0xa0, 0x79, 0x92, 0x7f, 0xbc, 0x89, 0x1b, 0x81, 0xdb, 0x44, 0x2e, - 0xfc, 0xcf, 0xe1, 0xa0, 0x74, 0xb2, 0xc4, 0x10, 0x7d, 0x2b, 0x75, 0x36, 0x67, 0x30, 0x3d, 0xf9, - 0xc1, 0x6c, 0x09, 0xe1, 0x35, 0xd2, 0xf4, 0x93, 0xc0, 0x61, 0x4d, 0x8f, 0x6e, 0xa0, 0x5f, 0x69, - 0xd2, 0xeb, 0xed, 0x89, 0xcd, 0xb9, 0x46, 0x37, 0x7b, 0xa2, 0x8b, 0x20, 0x2b, 0xe8, 0x15, 0x7f, - 0x43, 0x2f, 0x9b, 0x98, 0xdb, 0x1b, 0x35, 0xba, 0xda, 0x0b, 0x9b, 0x6b, 0xac, 0xba, 0xd9, 0xa6, - 0xde, 0x7e, 0x05, 0x00, 0x00, 0xff, 0xff, 0x96, 0x3a, 0x66, 0xfa, 0xdd, 0x02, 0x00, 0x00, + // 332 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xdd, 0x4e, 0xc2, 0x30, + 0x14, 0x4e, 0x81, 0x80, 0x1c, 0x88, 0x3f, 0xbd, 0xd0, 0x06, 0x63, 0x9c, 0xbb, 0x30, 0xc4, 0x1f, + 0x2e, 0xf0, 0xd2, 0x2b, 0x4c, 0x94, 0x70, 0x5b, 0x7c, 0x81, 0xb1, 0x1d, 0xa1, 0x61, 0x5b, 0x6b, + 0xdb, 0x25, 0xbe, 0x85, 0xcf, 0xe0, 0xa3, 0xf9, 0x26, 0x86, 0x31, 0x96, 0xc1, 0x74, 0x72, 0xd7, + 0x73, 0xfa, 0x7d, 0xe7, 0xfb, 0xbe, 0xf6, 0xc0, 0x19, 0x47, 0x63, 0xa5, 0xc6, 0x89, 0xc5, 0x68, + 0xe4, 0x5b, 0x21, 0xe3, 0x81, 0xd2, 0xd2, 0x4a, 0xda, 0x9e, 0x63, 0x8c, 0xda, 0xb3, 0x18, 0xf4, + 0xba, 0xd3, 0x85, 0xa7, 0x31, 0x58, 0x5f, 0xb8, 0x9f, 0x04, 0x2e, 0x4b, 0xa4, 0xe7, 0x0f, 0xf4, + 0x13, 0x8b, 0x1c, 0xdf, 0x13, 0x34, 0x96, 0x9e, 0x42, 0x53, 0x85, 0xc9, 0x5c, 0xc4, 0x8c, 0x38, + 0xa4, 0xdf, 0xe6, 0x59, 0x45, 0x29, 0x34, 0x84, 0xc5, 0x88, 0xd5, 0x1c, 0xd2, 0xef, 0xf2, 0xf4, + 0x4c, 0x19, 0xb4, 0xf4, 0x7a, 0x1c, 0xab, 0xa7, 0xed, 0x4d, 0x49, 0xaf, 0xe1, 0x70, 0x85, 0x78, + 0xd1, 0x32, 0x7a, 0xf2, 0xfc, 0x65, 0xa2, 0x58, 0x23, 0x05, 0xec, 0x74, 0xdd, 0x2f, 0x02, 0xce, + 0xdf, 0x8e, 0x8c, 0x92, 0xb1, 0xc1, 0x5c, 0x9a, 0x14, 0xa4, 0xc7, 0x70, 0xe4, 0x05, 0x81, 0x58, + 0xc1, 0xbd, 0x70, 0x45, 0x35, 0xac, 0xe6, 0xd4, 0xfb, 0x9d, 0xe1, 0xc5, 0x20, 0x4f, 0x3f, 0xe0, + 0x68, 0x64, 0xa2, 0x7d, 0x9c, 0x04, 0x18, 0x5b, 0xf1, 0x26, 0x50, 0xf3, 0x5d, 0x16, 0x75, 0xa0, + 0x63, 0x96, 0x42, 0xf1, 0x42, 0x8e, 0x03, 0x5e, 0x6c, 0xb9, 0x8f, 0x70, 0x55, 0xb2, 0x38, 0x52, + 0x2a, 0x14, 0x68, 0x5e, 0xe5, 0x3f, 0xcf, 0xe6, 0x46, 0xe0, 0x56, 0x91, 0xb3, 0x84, 0x63, 0x38, + 0xde, 0x78, 0x9d, 0x62, 0x88, 0xbe, 0x95, 0x3a, 0x9d, 0xd3, 0x19, 0x9e, 0xff, 0x12, 0x67, 0x03, + 0xe1, 0x25, 0xd2, 0xf0, 0x9b, 0xc0, 0x49, 0x49, 0x8f, 0x2e, 0xa0, 0x9d, 0x6b, 0xd2, 0xbb, 0xed, + 0x89, 0xd5, 0xb9, 0x7a, 0xf7, 0x7b, 0xa2, 0xb3, 0x20, 0x33, 0x68, 0x65, 0xbf, 0x47, 0x6f, 0xaa, + 0x98, 0xdb, 0x4b, 0xd7, 0xbb, 0xdd, 0x0b, 0xbb, 0xd6, 0x98, 0x35, 0xd3, 0x65, 0x7e, 0xf8, 0x09, + 0x00, 0x00, 0xff, 0xff, 0x1b, 0x4c, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x00, } diff --git a/pkg/plugin/proto/ObjectStore.proto b/pkg/plugin/proto/ObjectStore.proto index a7876c1c3..f418568fe 100644 --- a/pkg/plugin/proto/ObjectStore.proto +++ b/pkg/plugin/proto/ObjectStore.proto @@ -10,6 +10,16 @@ message PutObjectRequest { bytes body = 4; } +message ObjectExistsRequest { + string plugin = 1; + string bucket = 2; + string key = 3; +} + +message ObjectExistsResponse { + bool exists = 1; +} + message GetObjectRequest { string plugin = 1; string bucket = 2; @@ -67,6 +77,7 @@ message ObjectStoreInitRequest { service ObjectStore { rpc Init(ObjectStoreInitRequest) returns (Empty); rpc PutObject(stream PutObjectRequest) returns (Empty); + rpc ObjectExists(ObjectExistsRequest) returns (ObjectExistsResponse); rpc GetObject(GetObjectRequest) returns (stream Bytes); rpc ListCommonPrefixes(ListCommonPrefixesRequest) returns (ListCommonPrefixesResponse); rpc ListObjects(ListObjectsRequest) returns (ListObjectsResponse); diff --git a/pkg/plugin/velero/object_store.go b/pkg/plugin/velero/object_store.go index 32d64bb95..29082e704 100644 --- a/pkg/plugin/velero/object_store.go +++ b/pkg/plugin/velero/object_store.go @@ -33,6 +33,9 @@ type ObjectStore interface { // object storage bucket with the given key. PutObject(bucket, key string, body io.Reader) error + // ObjectExists checks if there is an object with the given key in the object storage bucket. + ObjectExists(bucket, key string) (bool, error) + // GetObject retrieves the object with the given key from the specified // bucket in object storage. GetObject(bucket, key string) (io.ReadCloser, error) diff --git a/pkg/util/test/comparisons.go b/pkg/util/test/comparisons.go index b7dc4041a..edc76063e 100644 --- a/pkg/util/test/comparisons.go +++ b/pkg/util/test/comparisons.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/util/diff" core "k8s.io/client-go/testing" ) @@ -101,9 +102,8 @@ func AssertDeepEqual(t *testing.T, expected, actual interface{}) bool { } if !equality.Semantic.DeepEqual(expected, actual) { - fmt.Printf("expected = %+v\n", expected) - fmt.Printf("actual = %+v\n", actual) - return assert.Fail(t, fmt.Sprintf("Objects not equal")) + s := diff.ObjectDiff(expected, actual) + return assert.Fail(t, fmt.Sprintf("Objects not equal:\n\n%s", s)) } return true