From 4f5b27d7ca0630ef7e79acb21fcd6b950ba84142 Mon Sep 17 00:00:00 2001 From: Shivanjan Chakravorty Date: Mon, 10 Jul 2023 02:57:14 -0600 Subject: [PATCH] feat: create cloud backup CSI create snapshot will now support triggering snapshot that will initiate a cloud backup. Signed-off-by: Shivanjan Chakravorty --- Makefile | 3 + api/mock/mock_cloud_backup.go | 585 ++++++++++++++++++++++++++++++ api/server/sdk/volume_ops.go | 8 +- csi/controller.go | 107 ++++++ csi/controller_test.go | 205 ++++++++++- csi/csi.go | 24 +- pkg/loadbalancer/mock/balancer.go | 52 +++ 7 files changed, 966 insertions(+), 18 deletions(-) create mode 100644 api/mock/mock_cloud_backup.go create mode 100644 pkg/loadbalancer/mock/balancer.go diff --git a/Makefile b/Makefile index f735d918d..801562749 100644 --- a/Makefile +++ b/Makefile @@ -430,12 +430,15 @@ mockgen: mockgen -destination=api/mock/mock_diags.go -package=mock github.com/libopenstorage/openstorage/api OpenStorageDiagsServer,OpenStorageDiagsClient mockgen -destination=api/mock/mock_volume.go -package=mock github.com/libopenstorage/openstorage/api OpenStorageVolumeServer,OpenStorageVolumeClient mockgen -destination=api/mock/mock_bucket.go -package=mock github.com/libopenstorage/openstorage/api OpenStorageBucketServer,OpenStorageBucketClient + mockgen -destination=api/mock/mock_cloud_backup.go -package=mock github.com/libopenstorage/openstorage/api OpenStorageCloudBackupServer,OpenStorageCloudBackupClient mockgen -destination=cluster/mock/cluster.mock.go -package=mock github.com/libopenstorage/openstorage/cluster Cluster mockgen -destination=api/mock/mock_fstrim.go -package=mock github.com/libopenstorage/openstorage/api OpenStorageFilesystemTrimServer,OpenStorageFilesystemTrimClient mockgen -destination=api/mock/mock_fscheck.go -package=mock github.com/libopenstorage/openstorage/api OpenStorageFilesystemCheckServer,OpenStorageFilesystemCheckClient mockgen -destination=api/server/mock/mock_schedops_k8s.go -package=mock github.com/portworx/sched-ops/k8s/core Ops mockgen -destination=volume/drivers/mock/driver.mock.go -package=mock github.com/libopenstorage/openstorage/volume VolumeDriver mockgen -destination=bucket/drivers/mock/bucket_driver.mock.go -package=mock github.com/libopenstorage/openstorage/bucket BucketDriver + mockgen -destination=pkg/loadbalancer/mock/balancer.go -package=mock github.com/libopenstorage/openstorage/pkg/loadbalancer Balancer + osd-tests: install ./hack/csi-sanity-test.sh diff --git a/api/mock/mock_cloud_backup.go b/api/mock/mock_cloud_backup.go new file mode 100644 index 000000000..d1e28114b --- /dev/null +++ b/api/mock/mock_cloud_backup.go @@ -0,0 +1,585 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/libopenstorage/openstorage/api (interfaces: OpenStorageCloudBackupServer,OpenStorageCloudBackupClient) + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + api "github.com/libopenstorage/openstorage/api" + grpc "google.golang.org/grpc" +) + +// MockOpenStorageCloudBackupServer is a mock of OpenStorageCloudBackupServer interface. +type MockOpenStorageCloudBackupServer struct { + ctrl *gomock.Controller + recorder *MockOpenStorageCloudBackupServerMockRecorder +} + +// MockOpenStorageCloudBackupServerMockRecorder is the mock recorder for MockOpenStorageCloudBackupServer. +type MockOpenStorageCloudBackupServerMockRecorder struct { + mock *MockOpenStorageCloudBackupServer +} + +// NewMockOpenStorageCloudBackupServer creates a new mock instance. +func NewMockOpenStorageCloudBackupServer(ctrl *gomock.Controller) *MockOpenStorageCloudBackupServer { + mock := &MockOpenStorageCloudBackupServer{ctrl: ctrl} + mock.recorder = &MockOpenStorageCloudBackupServerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockOpenStorageCloudBackupServer) EXPECT() *MockOpenStorageCloudBackupServerMockRecorder { + return m.recorder +} + +// Catalog mocks base method. +func (m *MockOpenStorageCloudBackupServer) Catalog(arg0 context.Context, arg1 *api.SdkCloudBackupCatalogRequest) (*api.SdkCloudBackupCatalogResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Catalog", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupCatalogResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Catalog indicates an expected call of Catalog. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) Catalog(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Catalog", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).Catalog), arg0, arg1) +} + +// Create mocks base method. +func (m *MockOpenStorageCloudBackupServer) Create(arg0 context.Context, arg1 *api.SdkCloudBackupCreateRequest) (*api.SdkCloudBackupCreateResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupCreateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).Create), arg0, arg1) +} + +// Delete mocks base method. +func (m *MockOpenStorageCloudBackupServer) Delete(arg0 context.Context, arg1 *api.SdkCloudBackupDeleteRequest) (*api.SdkCloudBackupDeleteResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupDeleteResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).Delete), arg0, arg1) +} + +// DeleteAll mocks base method. +func (m *MockOpenStorageCloudBackupServer) DeleteAll(arg0 context.Context, arg1 *api.SdkCloudBackupDeleteAllRequest) (*api.SdkCloudBackupDeleteAllResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteAll", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupDeleteAllResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteAll indicates an expected call of DeleteAll. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) DeleteAll(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAll", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).DeleteAll), arg0, arg1) +} + +// EnumerateWithFilters mocks base method. +func (m *MockOpenStorageCloudBackupServer) EnumerateWithFilters(arg0 context.Context, arg1 *api.SdkCloudBackupEnumerateWithFiltersRequest) (*api.SdkCloudBackupEnumerateWithFiltersResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnumerateWithFilters", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupEnumerateWithFiltersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EnumerateWithFilters indicates an expected call of EnumerateWithFilters. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) EnumerateWithFilters(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnumerateWithFilters", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).EnumerateWithFilters), arg0, arg1) +} + +// GroupCreate mocks base method. +func (m *MockOpenStorageCloudBackupServer) GroupCreate(arg0 context.Context, arg1 *api.SdkCloudBackupGroupCreateRequest) (*api.SdkCloudBackupGroupCreateResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GroupCreate", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupGroupCreateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GroupCreate indicates an expected call of GroupCreate. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) GroupCreate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupCreate", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).GroupCreate), arg0, arg1) +} + +// History mocks base method. +func (m *MockOpenStorageCloudBackupServer) History(arg0 context.Context, arg1 *api.SdkCloudBackupHistoryRequest) (*api.SdkCloudBackupHistoryResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "History", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupHistoryResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// History indicates an expected call of History. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) History(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "History", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).History), arg0, arg1) +} + +// Restore mocks base method. +func (m *MockOpenStorageCloudBackupServer) Restore(arg0 context.Context, arg1 *api.SdkCloudBackupRestoreRequest) (*api.SdkCloudBackupRestoreResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Restore", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupRestoreResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Restore indicates an expected call of Restore. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) Restore(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Restore", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).Restore), arg0, arg1) +} + +// SchedCreate mocks base method. +func (m *MockOpenStorageCloudBackupServer) SchedCreate(arg0 context.Context, arg1 *api.SdkCloudBackupSchedCreateRequest) (*api.SdkCloudBackupSchedCreateResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SchedCreate", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupSchedCreateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SchedCreate indicates an expected call of SchedCreate. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) SchedCreate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedCreate", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).SchedCreate), arg0, arg1) +} + +// SchedDelete mocks base method. +func (m *MockOpenStorageCloudBackupServer) SchedDelete(arg0 context.Context, arg1 *api.SdkCloudBackupSchedDeleteRequest) (*api.SdkCloudBackupSchedDeleteResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SchedDelete", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupSchedDeleteResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SchedDelete indicates an expected call of SchedDelete. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) SchedDelete(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedDelete", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).SchedDelete), arg0, arg1) +} + +// SchedEnumerate mocks base method. +func (m *MockOpenStorageCloudBackupServer) SchedEnumerate(arg0 context.Context, arg1 *api.SdkCloudBackupSchedEnumerateRequest) (*api.SdkCloudBackupSchedEnumerateResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SchedEnumerate", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupSchedEnumerateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SchedEnumerate indicates an expected call of SchedEnumerate. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) SchedEnumerate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedEnumerate", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).SchedEnumerate), arg0, arg1) +} + +// SchedUpdate mocks base method. +func (m *MockOpenStorageCloudBackupServer) SchedUpdate(arg0 context.Context, arg1 *api.SdkCloudBackupSchedUpdateRequest) (*api.SdkCloudBackupSchedUpdateResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SchedUpdate", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupSchedUpdateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SchedUpdate indicates an expected call of SchedUpdate. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) SchedUpdate(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedUpdate", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).SchedUpdate), arg0, arg1) +} + +// Size mocks base method. +func (m *MockOpenStorageCloudBackupServer) Size(arg0 context.Context, arg1 *api.SdkCloudBackupSizeRequest) (*api.SdkCloudBackupSizeResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Size", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupSizeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Size indicates an expected call of Size. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) Size(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Size", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).Size), arg0, arg1) +} + +// StateChange mocks base method. +func (m *MockOpenStorageCloudBackupServer) StateChange(arg0 context.Context, arg1 *api.SdkCloudBackupStateChangeRequest) (*api.SdkCloudBackupStateChangeResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateChange", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupStateChangeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateChange indicates an expected call of StateChange. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) StateChange(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateChange", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).StateChange), arg0, arg1) +} + +// Status mocks base method. +func (m *MockOpenStorageCloudBackupServer) Status(arg0 context.Context, arg1 *api.SdkCloudBackupStatusRequest) (*api.SdkCloudBackupStatusResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Status", arg0, arg1) + ret0, _ := ret[0].(*api.SdkCloudBackupStatusResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Status indicates an expected call of Status. +func (mr *MockOpenStorageCloudBackupServerMockRecorder) Status(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockOpenStorageCloudBackupServer)(nil).Status), arg0, arg1) +} + +// MockOpenStorageCloudBackupClient is a mock of OpenStorageCloudBackupClient interface. +type MockOpenStorageCloudBackupClient struct { + ctrl *gomock.Controller + recorder *MockOpenStorageCloudBackupClientMockRecorder +} + +// MockOpenStorageCloudBackupClientMockRecorder is the mock recorder for MockOpenStorageCloudBackupClient. +type MockOpenStorageCloudBackupClientMockRecorder struct { + mock *MockOpenStorageCloudBackupClient +} + +// NewMockOpenStorageCloudBackupClient creates a new mock instance. +func NewMockOpenStorageCloudBackupClient(ctrl *gomock.Controller) *MockOpenStorageCloudBackupClient { + mock := &MockOpenStorageCloudBackupClient{ctrl: ctrl} + mock.recorder = &MockOpenStorageCloudBackupClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockOpenStorageCloudBackupClient) EXPECT() *MockOpenStorageCloudBackupClientMockRecorder { + return m.recorder +} + +// Catalog mocks base method. +func (m *MockOpenStorageCloudBackupClient) Catalog(arg0 context.Context, arg1 *api.SdkCloudBackupCatalogRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupCatalogResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Catalog", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupCatalogResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Catalog indicates an expected call of Catalog. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) Catalog(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Catalog", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).Catalog), varargs...) +} + +// Create mocks base method. +func (m *MockOpenStorageCloudBackupClient) Create(arg0 context.Context, arg1 *api.SdkCloudBackupCreateRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupCreateResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Create", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupCreateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Create indicates an expected call of Create. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) Create(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).Create), varargs...) +} + +// Delete mocks base method. +func (m *MockOpenStorageCloudBackupClient) Delete(arg0 context.Context, arg1 *api.SdkCloudBackupDeleteRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupDeleteResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Delete", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupDeleteResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) Delete(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).Delete), varargs...) +} + +// DeleteAll mocks base method. +func (m *MockOpenStorageCloudBackupClient) DeleteAll(arg0 context.Context, arg1 *api.SdkCloudBackupDeleteAllRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupDeleteAllResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteAll", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupDeleteAllResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteAll indicates an expected call of DeleteAll. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) DeleteAll(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAll", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).DeleteAll), varargs...) +} + +// EnumerateWithFilters mocks base method. +func (m *MockOpenStorageCloudBackupClient) EnumerateWithFilters(arg0 context.Context, arg1 *api.SdkCloudBackupEnumerateWithFiltersRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupEnumerateWithFiltersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "EnumerateWithFilters", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupEnumerateWithFiltersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EnumerateWithFilters indicates an expected call of EnumerateWithFilters. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) EnumerateWithFilters(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnumerateWithFilters", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).EnumerateWithFilters), varargs...) +} + +// GroupCreate mocks base method. +func (m *MockOpenStorageCloudBackupClient) GroupCreate(arg0 context.Context, arg1 *api.SdkCloudBackupGroupCreateRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupGroupCreateResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GroupCreate", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupGroupCreateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GroupCreate indicates an expected call of GroupCreate. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) GroupCreate(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GroupCreate", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).GroupCreate), varargs...) +} + +// History mocks base method. +func (m *MockOpenStorageCloudBackupClient) History(arg0 context.Context, arg1 *api.SdkCloudBackupHistoryRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupHistoryResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "History", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupHistoryResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// History indicates an expected call of History. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) History(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "History", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).History), varargs...) +} + +// Restore mocks base method. +func (m *MockOpenStorageCloudBackupClient) Restore(arg0 context.Context, arg1 *api.SdkCloudBackupRestoreRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupRestoreResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Restore", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupRestoreResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Restore indicates an expected call of Restore. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) Restore(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Restore", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).Restore), varargs...) +} + +// SchedCreate mocks base method. +func (m *MockOpenStorageCloudBackupClient) SchedCreate(arg0 context.Context, arg1 *api.SdkCloudBackupSchedCreateRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupSchedCreateResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SchedCreate", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupSchedCreateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SchedCreate indicates an expected call of SchedCreate. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) SchedCreate(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedCreate", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).SchedCreate), varargs...) +} + +// SchedDelete mocks base method. +func (m *MockOpenStorageCloudBackupClient) SchedDelete(arg0 context.Context, arg1 *api.SdkCloudBackupSchedDeleteRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupSchedDeleteResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SchedDelete", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupSchedDeleteResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SchedDelete indicates an expected call of SchedDelete. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) SchedDelete(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedDelete", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).SchedDelete), varargs...) +} + +// SchedEnumerate mocks base method. +func (m *MockOpenStorageCloudBackupClient) SchedEnumerate(arg0 context.Context, arg1 *api.SdkCloudBackupSchedEnumerateRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupSchedEnumerateResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SchedEnumerate", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupSchedEnumerateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SchedEnumerate indicates an expected call of SchedEnumerate. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) SchedEnumerate(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedEnumerate", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).SchedEnumerate), varargs...) +} + +// SchedUpdate mocks base method. +func (m *MockOpenStorageCloudBackupClient) SchedUpdate(arg0 context.Context, arg1 *api.SdkCloudBackupSchedUpdateRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupSchedUpdateResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SchedUpdate", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupSchedUpdateResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SchedUpdate indicates an expected call of SchedUpdate. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) SchedUpdate(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchedUpdate", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).SchedUpdate), varargs...) +} + +// Size mocks base method. +func (m *MockOpenStorageCloudBackupClient) Size(arg0 context.Context, arg1 *api.SdkCloudBackupSizeRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupSizeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Size", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupSizeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Size indicates an expected call of Size. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) Size(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Size", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).Size), varargs...) +} + +// StateChange mocks base method. +func (m *MockOpenStorageCloudBackupClient) StateChange(arg0 context.Context, arg1 *api.SdkCloudBackupStateChangeRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupStateChangeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "StateChange", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupStateChangeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateChange indicates an expected call of StateChange. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) StateChange(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateChange", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).StateChange), varargs...) +} + +// Status mocks base method. +func (m *MockOpenStorageCloudBackupClient) Status(arg0 context.Context, arg1 *api.SdkCloudBackupStatusRequest, arg2 ...grpc.CallOption) (*api.SdkCloudBackupStatusResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Status", varargs...) + ret0, _ := ret[0].(*api.SdkCloudBackupStatusResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Status indicates an expected call of Status. +func (mr *MockOpenStorageCloudBackupClientMockRecorder) Status(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockOpenStorageCloudBackupClient)(nil).Status), varargs...) +} diff --git a/api/server/sdk/volume_ops.go b/api/server/sdk/volume_ops.go index b78e7b4df..afcc76947 100644 --- a/api/server/sdk/volume_ops.go +++ b/api/server/sdk/volume_ops.go @@ -768,10 +768,10 @@ func (s *VolumeServer) VolumeBytesUsedByNode( req *api.SdkVolumeBytesUsedRequest, ) (*api.SdkVolumeBytesUsedResponse, error) { return nil, status.Errorf( - codes.Unimplemented, - "Failed to obtain volume utilization on node %s: %v", - req.GetNodeId(), - volume.ErrNotSupported.Error()) + codes.Unimplemented, + "Failed to obtain volume utilization on node %s: %v", + req.GetNodeId(), + volume.ErrNotSupported.Error()) } func (s *VolumeServer) CapacityUsage( diff --git a/csi/controller.go b/csi/controller.go index 9d26bcee8..0a3774144 100644 --- a/csi/controller.go +++ b/csi/controller.go @@ -46,6 +46,10 @@ const ( osdPvcAnnotationsKey = osdParameterPrefix + "pvc-annotations" osdPvcLabelsKey = osdParameterPrefix + "pvc-labels" + // These keys are for accessing Snapshot Metadata added from the external-provisioner + osdSnapshotLabelsTypeKey = osdParameterPrefix + "snapshot-type" + osdSnapshotCredentialIDKey = osdParameterPrefix + "credential-id" + // in-tree keys for name and namespace intreePvcNameKey = "pvc" intreePvcNamespaceKey = "namespace" @@ -60,6 +64,10 @@ const ( volumeCapabilityMessageReadOnlyVolume = "Volume is read only" volumeCapabilityMessageNotReadOnlyVolume = "Volume is not read only" defaultCSIVolumeSize = uint64(units.GiB * 1) + + // driver type + DriverTypeLocal = "local" + DriverTypeCloud = "cloud" ) // ControllerGetCapabilities is a CSI API functions which returns to the caller @@ -887,6 +895,35 @@ func (s *OsdCsiServer) CreateSnapshot( return nil, status.Error(codes.InvalidArgument, "Name must be provided") } + // Get secret if any was passed + ctx = s.setupContext(ctx, req.GetSecrets()) + ctx, cancel := grpcutil.WithDefaultTimeout(ctx) + defer cancel() + + // Get any labels passed in by the CO + _, locator, _, err := s.specHandler.SpecFromOpts(req.GetParameters()) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Unable to get parameters: %v", err) + } + // Check ID is valid with the specified volume capabilities + snapshotType, ok := locator.VolumeLabels[osdSnapshotLabelsTypeKey] + if !ok { + snapshotType = DriverTypeLocal + } + switch snapshotType { + case DriverTypeCloud: + return s.createCloudBackup(ctx, req) + case DriverTypeLocal: + fallthrough + default: + return s.createLocalSnapshot(ctx, req) + } +} + +func (s *OsdCsiServer) createLocalSnapshot( + ctx context.Context, + req *csi.CreateSnapshotRequest, +) (*csi.CreateSnapshotResponse, error) { // Get grpc connection conn, err := s.getConn() if err != nil { @@ -980,6 +1017,76 @@ func (s *OsdCsiServer) CreateSnapshot( }, }, nil } +func (s *OsdCsiServer) getCloudBackupClient(ctx context.Context) (api.OpenStorageCloudBackupClient, error) { + // Get grpc connection + conn, err := s.getRemoteConn(ctx) + if err != nil { + return nil, status.Errorf( + codes.Unavailable, + "Unable to connect to SDK server: %v", err) + } + return s.cloudBackupClient(conn), nil +} + +func (s *OsdCsiServer) createCloudBackup( + ctx context.Context, + req *csi.CreateSnapshotRequest, +) (*csi.CreateSnapshotResponse, error) { + cloudBackupClient, err := s.getCloudBackupClient(ctx) + if err != nil { + return nil, err + } + + // Get any labels passed in by the CO + _, locator, _, err := s.specHandler.SpecFromOpts(req.GetParameters()) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Unable to get parameters: %v", err) + } + + credentialID := locator.VolumeLabels[osdSnapshotCredentialIDKey] + backupID := req.GetName() + // Create snapshot + _, err = cloudBackupClient.Create(ctx, &api.SdkCloudBackupCreateRequest{ + VolumeId: req.GetSourceVolumeId(), + TaskId: backupID, + CredentialId: credentialID, + Labels: locator.GetVolumeLabels(), + }) + + if err != nil { + return nil, status.Errorf(codes.Aborted, "Failed to create cloud snapshot: %v", err) + } + + var isBackupReady bool + var backupStatus *api.SdkCloudBackupStatusResponse + + // Check if snapshot has been created but is in error state + backupStatus, errFindFailed := cloudBackupClient.Status(ctx, &api.SdkCloudBackupStatusRequest{ + VolumeId: req.GetSourceVolumeId(), + TaskId: backupID, + }) + if errFindFailed != nil { + return nil, status.Errorf(codes.Aborted, "Failed to create cloud snapshot: %v", err) + } + isBackupReady = backupStatus.Statuses[backupID].Status == api.SdkCloudBackupStatusType_SdkCloudBackupStatusTypeDone + + snapSize, errSizeFailed := cloudBackupClient.Size(ctx, &api.SdkCloudBackupSizeRequest{ + BackupId: backupID, + }) + if errSizeFailed != nil { + return nil, status.Errorf(codes.Aborted, "Failed to get cloud snapshot size: %v", err) + } + + return &csi.CreateSnapshotResponse{ + Snapshot: &csi.Snapshot{ + SizeBytes: int64(snapSize.GetTotalDownloadBytes()), + SnapshotId: backupID, + SourceVolumeId: req.GetSourceVolumeId(), + CreationTime: backupStatus.Statuses[backupID].StartTime, + ReadyToUse: isBackupReady, + }, + }, nil +} // DeleteSnapshot is a CSI implementation to delete a snapshot func (s *OsdCsiServer) DeleteSnapshot( diff --git a/csi/controller_test.go b/csi/controller_test.go index 17b0dcdc3..e2557caaf 100644 --- a/csi/controller_test.go +++ b/csi/controller_test.go @@ -18,23 +18,29 @@ package csi import ( "encoding/json" + "errors" "fmt" "math" + "reflect" + "sync" "testing" + csi "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/golang/mock/gomock" "github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes/timestamp" - "github.com/libopenstorage/openstorage/api" + "github.com/libopenstorage/openstorage/api/mock" + "github.com/libopenstorage/openstorage/api/spec" authsecrets "github.com/libopenstorage/openstorage/pkg/auth/secrets" + mockLoadBalancer "github.com/libopenstorage/openstorage/pkg/loadbalancer/mock" "github.com/libopenstorage/openstorage/pkg/units" - - csi "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "golang.org/x/net/context" + "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/timestamppb" ) func containsCap(c csi.ControllerServiceCapability_RPC_Type, resp *csi.ControllerGetCapabilitiesResponse) bool { @@ -3372,3 +3378,194 @@ func TestGetCapacity(t *testing.T) { assert.NotNil(t, res) assert.Equal(t, int64(0), res.AvailableCapacity) } + +type fakeOsdCsiServer struct { + *OsdCsiServer + mockCloudBackupClient api.OpenStorageCloudBackupClient +} + +func (f *fakeOsdCsiServer) getCloudBackupClient(ctx context.Context) (api.OpenStorageCloudBackupClient, error) { + return f.mockCloudBackupClient, nil +} +func TestOsdCsiServer_CreateSnapshot(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockCloudBackupClient := mock.NewMockOpenStorageCloudBackupClient(ctrl) + + ctx := context.Background() + + mockErr := errors.New("MOCK ERROR") + creationTime := timestamppb.Now() + + mockCloudBackupClient.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, req *api.SdkCloudBackupCreateRequest, opts ...grpc.CallOption) (*api.SdkCloudBackupCreateResponse, error) { + if req.TaskId == "create-error" { + return nil, mockErr + } + + if req.TaskId == "create-notfound" { + return nil, status.Errorf(codes.NotFound, "Volume id not found") + } + + return &api.SdkCloudBackupCreateResponse{ + TaskId: req.TaskId, + }, nil + + }).AnyTimes() + + mockCloudBackupClient.EXPECT().Status(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, req *api.SdkCloudBackupStatusRequest, opts ...grpc.CallOption) (*api.SdkCloudBackupStatusResponse, error) { + if req.TaskId == "status-error" { + return nil, mockErr + } + + // if req.TaskId == "status-failed" || + if req.TaskId == "delete-error" { + return &api.SdkCloudBackupStatusResponse{ + Statuses: map[string]*api.SdkCloudBackupStatus{ + req.TaskId: { + Status: api.SdkCloudBackupStatusType_SdkCloudBackupStatusTypeFailed, + StartTime: creationTime, + }, + }, + }, nil + } + + return &api.SdkCloudBackupStatusResponse{ + Statuses: map[string]*api.SdkCloudBackupStatus{ + req.TaskId: { + Status: api.SdkCloudBackupStatusType_SdkCloudBackupStatusTypeDone, + StartTime: creationTime, + }, + }, + }, nil + + }).AnyTimes() + + mockCloudBackupClient.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, req *api.SdkCloudBackupDeleteRequest, opts ...grpc.CallOption) (*api.SdkCloudBackupDeleteResponse, error) { + if req.BackupId == "delete-error" { + return nil, mockErr + } + + return &api.SdkCloudBackupDeleteResponse{}, nil + + }).AnyTimes() + + mockCloudBackupClient.EXPECT().Size(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, req *api.SdkCloudBackupSizeRequest, opts ...grpc.CallOption) (*api.SdkCloudBackupSizeResponse, error) { + if req.BackupId == "size-error" { + return nil, mockErr + } + + return &api.SdkCloudBackupSizeResponse{ + TotalDownloadBytes: defaultCSIVolumeSize, + }, nil + + }).AnyTimes() + + mockSourceVolumeID := "mock-volume-id" + + tests := []struct { + name string + SnapshotName string + want *csi.CreateSnapshotResponse + wantErr bool + }{ + { + "remote client connection failed", + "remote-client-error", + nil, + true, + }, + { + "fail snapshot create", + "create-error", + nil, + true, + }, + { + "volume id not found while creating", + "create-notfound", + nil, + true, + }, + { + "fail to get snapshot status", + "status-error", + nil, + true, + }, + { + "fail to cleanup failed snapshot", + "delete-error", + nil, + true, + }, + { + "fail to get snapshot size", + "size-error", + nil, + true, + }, + { + "creation completes without any error", + "ok", + &csi.CreateSnapshotResponse{ + Snapshot: &csi.Snapshot{ + SizeBytes: int64(defaultCSIVolumeSize), + SnapshotId: "ok", + SourceVolumeId: mockSourceVolumeID, + CreationTime: creationTime, + ReadyToUse: true, + }, + }, + false, + }, + } + mockRoundRobinBalancer := mockLoadBalancer.NewMockBalancer(ctrl) + // nil, false, nil + mockRoundRobinBalancer.EXPECT().GetRemoteNodeConnection(gomock.Any()).DoAndReturn( + func(ctx context.Context) (*grpc.ClientConn, bool, error) { + var err error + if ctx.Value("remote-client-error").(bool) { + err = mockErr + } + return nil, false, err + }).AnyTimes() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := &csi.CreateSnapshotRequest{ + Name: tt.SnapshotName, + SourceVolumeId: mockSourceVolumeID, + Parameters: map[string]string{ + api.SpecLabels: osdSnapshotLabelsTypeKey + "=cloud", + }, + } + + s := &OsdCsiServer{ + specHandler: spec.NewSpecHandler(), + mu: sync.Mutex{}, + cloudBackupClient: func(cc grpc.ClientConnInterface) api.OpenStorageCloudBackupClient { + return mockCloudBackupClient + }, + roundRobinBalancer: mockRoundRobinBalancer, + } + + doClientErr := tt.SnapshotName == "remote-client-error" + + ctx = context.WithValue(ctx, "remote-client-error", doClientErr) + + got, err := s.CreateSnapshot(ctx, req) + if (err != nil) != tt.wantErr { + t.Errorf("OsdCsiServer.CreateSnapshot() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("OsdCsiServer.CreateSnapshot() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/csi/csi.go b/csi/csi.go index 656d93e33..a19931cf4 100644 --- a/csi/csi.go +++ b/csi/csi.go @@ -80,16 +80,19 @@ type OsdCsiServer struct { csi.IdentityServer *grpcserver.GrpcServer - specHandler spec.SpecHandler - driver volume.VolumeDriver - cluster cluster.Cluster - sdkUds string - sdkPort string - conn *grpc.ClientConn - mu sync.Mutex - csiDriverName string - allowInlineVolumes bool - roundRobinBalancer loadbalancer.Balancer + cloudBackupClient func(cc grpc.ClientConnInterface) api.OpenStorageCloudBackupClient + specHandler spec.SpecHandler + driver volume.VolumeDriver + cluster cluster.Cluster + sdkUds string + sdkPort string + conn *grpc.ClientConn + roundRobinBalancer loadbalancer.Balancer + nextCreateNodeNumber int + mu sync.Mutex + csiDriverName string + allowInlineVolumes bool + stopCleanupCh chan bool } // NewOsdCsiServer creates a gRPC CSI complient server on the @@ -156,6 +159,7 @@ func NewOsdCsiServer(config *OsdCsiServerConfig) (grpcserver.Server, error) { csiDriverName: config.CsiDriverName, allowInlineVolumes: config.EnableInlineVolumes, roundRobinBalancer: config.RoundRobinBalancer, + cloudBackupClient: api.NewOpenStorageCloudBackupClient, }, nil } diff --git a/pkg/loadbalancer/mock/balancer.go b/pkg/loadbalancer/mock/balancer.go new file mode 100644 index 000000000..31b795f22 --- /dev/null +++ b/pkg/loadbalancer/mock/balancer.go @@ -0,0 +1,52 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/libopenstorage/openstorage/pkg/loadbalancer (interfaces: Balancer) + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + grpc "google.golang.org/grpc" +) + +// MockBalancer is a mock of Balancer interface. +type MockBalancer struct { + ctrl *gomock.Controller + recorder *MockBalancerMockRecorder +} + +// MockBalancerMockRecorder is the mock recorder for MockBalancer. +type MockBalancerMockRecorder struct { + mock *MockBalancer +} + +// NewMockBalancer creates a new mock instance. +func NewMockBalancer(ctrl *gomock.Controller) *MockBalancer { + mock := &MockBalancer{ctrl: ctrl} + mock.recorder = &MockBalancerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBalancer) EXPECT() *MockBalancerMockRecorder { + return m.recorder +} + +// GetRemoteNodeConnection mocks base method. +func (m *MockBalancer) GetRemoteNodeConnection(arg0 context.Context) (*grpc.ClientConn, bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRemoteNodeConnection", arg0) + ret0, _ := ret[0].(*grpc.ClientConn) + ret1, _ := ret[1].(bool) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetRemoteNodeConnection indicates an expected call of GetRemoteNodeConnection. +func (mr *MockBalancerMockRecorder) GetRemoteNodeConnection(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRemoteNodeConnection", reflect.TypeOf((*MockBalancer)(nil).GetRemoteNodeConnection), arg0) +}