mirror of
https://github.com/kastenhq/kubestr.git
synced 2024-12-14 11:57:56 +00:00
Adding "./kubestr browse snapshot" command (#277)
* Adding the kubestr browse pvc command. Handling kubestr browse support for backward compatibility. * Adding browse snapshot command. Updating browse command to browse pvc command. * chore(deps): bump github/codeql-action in the github-actions group (#272) Bumps the github-actions group with 1 update: [github/codeql-action](https://github.com/github/codeql-action). Updates `github/codeql-action` from 3.25.12 to 3.25.13 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](4fa2a79536...2d790406f5
) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump docker/build-push-action in the docker group (#273) Bumps the docker group with 1 update: [docker/build-push-action](https://github.com/docker/build-push-action). Updates `docker/build-push-action` from 6.3.0 to 6.4.1 - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](1a162644f9...1ca370b3a9
) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: docker ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Removing unused snapshot function parameter in cleanup * Adding mock tests for SnapshotBrowserStepper * Adding Deprecated msg to the 'browse' command * Adding fake tests for snapshot_inspector.go * Renamed testcase CSITestSuite.TestCreateInspectorApplication to TestCreateInspectorApplicationForPVC * Adding snapshot_inspector_steps_test.go * Updating Deprecated msg for 'browse' command * Making namespace, runAsUser & localport flags persistent * Removing namespace, runAsUser & localport flags for browse snapshot because we made those persistent * Removing storage class flag * Update cmd/rootCmd.go Co-authored-by: Sirish Bathina <sirish@kasten.io> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sirish Bathina <sirish@kasten.io>
This commit is contained in:
parent
68d195495e
commit
2cf1758a0c
10 changed files with 1314 additions and 13 deletions
|
@ -110,6 +110,20 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
browseSnapshotCmd = &cobra.Command{
|
||||
Use: "snapshot [Snapshot name]",
|
||||
Short: "Browse the contents of a CSI VolumeSnapshot via file browser",
|
||||
Long: "Browse the contents of a CSI provisioned VolumeSnapshot by cloning the volume and mounting it with a file browser.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return CsiSnapshotBrowse(context.Background(), args[0],
|
||||
namespace,
|
||||
csiCheckRunAsUser,
|
||||
browseLocalPort,
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
blockMountRunAsUser int64
|
||||
blockMountCleanup bool
|
||||
blockMountCleanupOnly bool
|
||||
|
@ -178,7 +192,7 @@ func init() {
|
|||
rootCmd.AddCommand(browseCmd)
|
||||
browseCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)")
|
||||
_ = browseCmd.MarkFlagRequired("volumesnapshotclass")
|
||||
browseCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the PersistentVolumeClaim.")
|
||||
browseCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the resource to browse.")
|
||||
browseCmd.PersistentFlags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)")
|
||||
browseCmd.PersistentFlags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector")
|
||||
|
||||
|
@ -186,8 +200,10 @@ func init() {
|
|||
browsePvcCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)")
|
||||
_ = browsePvcCmd.MarkFlagRequired("volumesnapshotclass")
|
||||
|
||||
browseCmd.AddCommand(browseSnapshotCmd)
|
||||
|
||||
rootCmd.AddCommand(blockMountCmd)
|
||||
blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a Storageclass. (Required)")
|
||||
blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)")
|
||||
_ = blockMountCmd.MarkFlagRequired("storageclass")
|
||||
blockMountCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace used to run the check.")
|
||||
blockMountCmd.Flags().StringVarP(&containerImage, "image", "i", "", "The container image used to create a pod.")
|
||||
|
@ -373,6 +389,38 @@ func CsiPvcBrowse(ctx context.Context,
|
|||
return err
|
||||
}
|
||||
|
||||
func CsiSnapshotBrowse(ctx context.Context,
|
||||
snapshotName string,
|
||||
namespace string,
|
||||
runAsUser int64,
|
||||
localPort int,
|
||||
) error {
|
||||
kubecli, err := kubestr.LoadKubeCli()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to load kubeCli (%s)", err.Error())
|
||||
return err
|
||||
}
|
||||
dyncli, err := kubestr.LoadDynCli()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to load dynCli (%s)", err.Error())
|
||||
return err
|
||||
}
|
||||
browseRunner := &csi.SnapshotBrowseRunner{
|
||||
KubeCli: kubecli,
|
||||
DynCli: dyncli,
|
||||
}
|
||||
err = browseRunner.RunSnapshotBrowse(ctx, &csitypes.SnapshotBrowseArgs{
|
||||
SnapshotName: snapshotName,
|
||||
Namespace: namespace,
|
||||
RunAsUser: runAsUser,
|
||||
LocalPort: localPort,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to run Snapshot browser (%s)\n", err.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func BlockMountCheck(ctx context.Context, output, outfile string, cleanupOnly bool, checkerArgs block.BlockMountCheckerArgs) error {
|
||||
kubecli, err := kubestr.LoadKubeCli()
|
||||
if err != nil {
|
||||
|
|
|
@ -5,6 +5,8 @@ package csi
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
@ -44,6 +46,7 @@ type ArgumentValidator interface {
|
|||
//Rename
|
||||
ValidatePVC(ctx context.Context, pvcName, namespace string) (*v1.PersistentVolumeClaim, error)
|
||||
FetchPV(ctx context.Context, pvName string) (*v1.PersistentVolume, error)
|
||||
ValidateVolumeSnapshot(ctx context.Context, snapshotName, namespace string, groupVersion *metav1.GroupVersionForDiscovery) (*snapv1.VolumeSnapshot, error)
|
||||
ValidateNamespace(ctx context.Context, namespace string) error
|
||||
ValidateStorageClass(ctx context.Context, storageClass string) (*sv1.StorageClass, error)
|
||||
ValidateVolumeSnapshotClass(ctx context.Context, volumeSnapshotClass string, groupVersion *metav1.GroupVersionForDiscovery) (*unstructured.Unstructured, error)
|
||||
|
@ -68,6 +71,17 @@ func (o *validateOperations) ValidatePVC(ctx context.Context, pvcName, namespace
|
|||
return o.kubeCli.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, pvcName, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func (o *validateOperations) ValidateVolumeSnapshot(ctx context.Context, snapshotName, namespace string, groupVersion *metav1.GroupVersionForDiscovery) (*snapv1.VolumeSnapshot, error) {
|
||||
VolSnapGVR := schema.GroupVersionResource{Group: snapv1.GroupName, Version: groupVersion.Version, Resource: common.VolumeSnapshotResourcePlural}
|
||||
uVS, err := o.dynCli.Resource(VolSnapGVR).Namespace(namespace).Get(ctx, snapshotName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get VolumeSnapshot: %v", err)
|
||||
}
|
||||
volumeSnapshot := &snapv1.VolumeSnapshot{}
|
||||
err = runtime.DefaultUnstructuredConverter.FromUnstructured(uVS.UnstructuredContent(), volumeSnapshot)
|
||||
return volumeSnapshot, err
|
||||
}
|
||||
|
||||
func (o *validateOperations) FetchPV(ctx context.Context, pvName string) (*v1.PersistentVolume, error) {
|
||||
if o.kubeCli == nil {
|
||||
return nil, fmt.Errorf("kubeCli not initialized")
|
||||
|
@ -317,6 +331,41 @@ func (c *applicationCreate) getErrorFromEvents(ctx context.Context, namespace, n
|
|||
return nil
|
||||
}
|
||||
|
||||
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_snapshot_fetcher.go -package=mocks . SnapshotFetcher
|
||||
type SnapshotFetcher interface {
|
||||
NewSnapshotter() (kansnapshot.Snapshotter, error)
|
||||
GetVolumeSnapshot(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.FetchSnapshotArgs) (*snapv1.VolumeSnapshot, error)
|
||||
}
|
||||
|
||||
type snapshotFetch struct {
|
||||
kubeCli kubernetes.Interface
|
||||
dynCli dynamic.Interface
|
||||
}
|
||||
|
||||
func (f *snapshotFetch) NewSnapshotter() (kansnapshot.Snapshotter, error) {
|
||||
if f.kubeCli == nil {
|
||||
return nil, fmt.Errorf("kubeCli not initialized")
|
||||
}
|
||||
if f.dynCli == nil {
|
||||
return nil, fmt.Errorf("dynCli not initialized")
|
||||
}
|
||||
return kansnapshot.NewSnapshotter(f.kubeCli, f.dynCli)
|
||||
}
|
||||
|
||||
func (f *snapshotFetch) GetVolumeSnapshot(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.FetchSnapshotArgs) (*snapv1.VolumeSnapshot, error) {
|
||||
if snapshotter == nil || args == nil {
|
||||
return nil, fmt.Errorf("snapshotter or args are empty")
|
||||
}
|
||||
if err := args.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snap, err := snapshotter.Get(ctx, args.SnapshotName, args.Namespace)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Failed to get CSI snapshot (%s) in Namespace (%s)", args.SnapshotName, args.Namespace)
|
||||
}
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_snapshot_creator.go -package=mocks . SnapshotCreator
|
||||
type SnapshotCreator interface {
|
||||
NewSnapshotter() (kansnapshot.Snapshotter, error)
|
||||
|
|
|
@ -9,9 +9,10 @@ import (
|
|||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
v10 "k8s.io/api/storage/v1"
|
||||
v11 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||
v10 "k8s.io/api/core/v1"
|
||||
v11 "k8s.io/api/storage/v1"
|
||||
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
|
@ -39,10 +40,10 @@ func (m *MockArgumentValidator) EXPECT() *MockArgumentValidatorMockRecorder {
|
|||
}
|
||||
|
||||
// FetchPV mocks base method.
|
||||
func (m *MockArgumentValidator) FetchPV(arg0 context.Context, arg1 string) (*v1.PersistentVolume, error) {
|
||||
func (m *MockArgumentValidator) FetchPV(arg0 context.Context, arg1 string) (*v10.PersistentVolume, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FetchPV", arg0, arg1)
|
||||
ret0, _ := ret[0].(*v1.PersistentVolume)
|
||||
ret0, _ := ret[0].(*v10.PersistentVolume)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
@ -68,10 +69,10 @@ func (mr *MockArgumentValidatorMockRecorder) ValidateNamespace(arg0, arg1 interf
|
|||
}
|
||||
|
||||
// ValidatePVC mocks base method.
|
||||
func (m *MockArgumentValidator) ValidatePVC(arg0 context.Context, arg1, arg2 string) (*v1.PersistentVolumeClaim, error) {
|
||||
func (m *MockArgumentValidator) ValidatePVC(arg0 context.Context, arg1, arg2 string) (*v10.PersistentVolumeClaim, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidatePVC", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*v1.PersistentVolumeClaim)
|
||||
ret0, _ := ret[0].(*v10.PersistentVolumeClaim)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
@ -83,10 +84,10 @@ func (mr *MockArgumentValidatorMockRecorder) ValidatePVC(arg0, arg1, arg2 interf
|
|||
}
|
||||
|
||||
// ValidateStorageClass mocks base method.
|
||||
func (m *MockArgumentValidator) ValidateStorageClass(arg0 context.Context, arg1 string) (*v10.StorageClass, error) {
|
||||
func (m *MockArgumentValidator) ValidateStorageClass(arg0 context.Context, arg1 string) (*v11.StorageClass, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateStorageClass", arg0, arg1)
|
||||
ret0, _ := ret[0].(*v10.StorageClass)
|
||||
ret0, _ := ret[0].(*v11.StorageClass)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
@ -97,8 +98,23 @@ func (mr *MockArgumentValidatorMockRecorder) ValidateStorageClass(arg0, arg1 int
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateStorageClass", reflect.TypeOf((*MockArgumentValidator)(nil).ValidateStorageClass), arg0, arg1)
|
||||
}
|
||||
|
||||
// ValidateVolumeSnapshot mocks base method.
|
||||
func (m *MockArgumentValidator) ValidateVolumeSnapshot(arg0 context.Context, arg1, arg2 string, arg3 *v12.GroupVersionForDiscovery) (*v1.VolumeSnapshot, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateVolumeSnapshot", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(*v1.VolumeSnapshot)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ValidateVolumeSnapshot indicates an expected call of ValidateVolumeSnapshot.
|
||||
func (mr *MockArgumentValidatorMockRecorder) ValidateVolumeSnapshot(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateVolumeSnapshot", reflect.TypeOf((*MockArgumentValidator)(nil).ValidateVolumeSnapshot), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// ValidateVolumeSnapshotClass mocks base method.
|
||||
func (m *MockArgumentValidator) ValidateVolumeSnapshotClass(arg0 context.Context, arg1 string, arg2 *v11.GroupVersionForDiscovery) (*unstructured.Unstructured, error) {
|
||||
func (m *MockArgumentValidator) ValidateVolumeSnapshotClass(arg0 context.Context, arg1 string, arg2 *v12.GroupVersionForDiscovery) (*unstructured.Unstructured, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateVolumeSnapshotClass", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*unstructured.Unstructured)
|
||||
|
|
97
pkg/csi/mocks/mock_snapshot_browser_stepper.go
Normal file
97
pkg/csi/mocks/mock_snapshot_browser_stepper.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: SnapshotBrowserStepper)
|
||||
|
||||
// Package mocks is a generated GoMock package.
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
types "github.com/kastenhq/kubestr/pkg/csi/types"
|
||||
v1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||
v10 "k8s.io/api/core/v1"
|
||||
v11 "k8s.io/api/storage/v1"
|
||||
)
|
||||
|
||||
// MockSnapshotBrowserStepper is a mock of SnapshotBrowserStepper interface.
|
||||
type MockSnapshotBrowserStepper struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockSnapshotBrowserStepperMockRecorder
|
||||
}
|
||||
|
||||
// MockSnapshotBrowserStepperMockRecorder is the mock recorder for MockSnapshotBrowserStepper.
|
||||
type MockSnapshotBrowserStepperMockRecorder struct {
|
||||
mock *MockSnapshotBrowserStepper
|
||||
}
|
||||
|
||||
// NewMockSnapshotBrowserStepper creates a new mock instance.
|
||||
func NewMockSnapshotBrowserStepper(ctrl *gomock.Controller) *MockSnapshotBrowserStepper {
|
||||
mock := &MockSnapshotBrowserStepper{ctrl: ctrl}
|
||||
mock.recorder = &MockSnapshotBrowserStepperMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockSnapshotBrowserStepper) EXPECT() *MockSnapshotBrowserStepperMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Cleanup mocks base method.
|
||||
func (m *MockSnapshotBrowserStepper) Cleanup(arg0 context.Context, arg1 *v10.PersistentVolumeClaim, arg2 *v10.Pod) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "Cleanup", arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// Cleanup indicates an expected call of Cleanup.
|
||||
func (mr *MockSnapshotBrowserStepperMockRecorder) Cleanup(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cleanup", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).Cleanup), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// CreateInspectorApplication mocks base method.
|
||||
func (m *MockSnapshotBrowserStepper) CreateInspectorApplication(arg0 context.Context, arg1 *types.SnapshotBrowseArgs, arg2 *v1.VolumeSnapshot, arg3 *v11.StorageClass) (*v10.Pod, *v10.PersistentVolumeClaim, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateInspectorApplication", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(*v10.Pod)
|
||||
ret1, _ := ret[1].(*v10.PersistentVolumeClaim)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// CreateInspectorApplication indicates an expected call of CreateInspectorApplication.
|
||||
func (mr *MockSnapshotBrowserStepperMockRecorder) CreateInspectorApplication(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateInspectorApplication", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).CreateInspectorApplication), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// PortForwardAPod mocks base method.
|
||||
func (m *MockSnapshotBrowserStepper) PortForwardAPod(arg0 context.Context, arg1 *v10.Pod, arg2 int) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "PortForwardAPod", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// PortForwardAPod indicates an expected call of PortForwardAPod.
|
||||
func (mr *MockSnapshotBrowserStepperMockRecorder) PortForwardAPod(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PortForwardAPod", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).PortForwardAPod), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// ValidateArgs mocks base method.
|
||||
func (m *MockSnapshotBrowserStepper) ValidateArgs(arg0 context.Context, arg1 *types.SnapshotBrowseArgs) (*v1.VolumeSnapshot, *v11.StorageClass, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateArgs", arg0, arg1)
|
||||
ret0, _ := ret[0].(*v1.VolumeSnapshot)
|
||||
ret1, _ := ret[1].(*v11.StorageClass)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// ValidateArgs indicates an expected call of ValidateArgs.
|
||||
func (mr *MockSnapshotBrowserStepperMockRecorder) ValidateArgs(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateArgs", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).ValidateArgs), arg0, arg1)
|
||||
}
|
68
pkg/csi/mocks/mock_snapshot_fetcher.go
Normal file
68
pkg/csi/mocks/mock_snapshot_fetcher.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: SnapshotFetcher)
|
||||
|
||||
// Package mocks is a generated GoMock package.
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
snapshot "github.com/kanisterio/kanister/pkg/kube/snapshot"
|
||||
types "github.com/kastenhq/kubestr/pkg/csi/types"
|
||||
v1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||
)
|
||||
|
||||
// MockSnapshotFetcher is a mock of SnapshotFetcher interface.
|
||||
type MockSnapshotFetcher struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockSnapshotFetcherMockRecorder
|
||||
}
|
||||
|
||||
// MockSnapshotFetcherMockRecorder is the mock recorder for MockSnapshotFetcher.
|
||||
type MockSnapshotFetcherMockRecorder struct {
|
||||
mock *MockSnapshotFetcher
|
||||
}
|
||||
|
||||
// NewMockSnapshotFetcher creates a new mock instance.
|
||||
func NewMockSnapshotFetcher(ctrl *gomock.Controller) *MockSnapshotFetcher {
|
||||
mock := &MockSnapshotFetcher{ctrl: ctrl}
|
||||
mock.recorder = &MockSnapshotFetcherMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockSnapshotFetcher) EXPECT() *MockSnapshotFetcherMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// GetVolumeSnapshot mocks base method.
|
||||
func (m *MockSnapshotFetcher) GetVolumeSnapshot(arg0 context.Context, arg1 snapshot.Snapshotter, arg2 *types.FetchSnapshotArgs) (*v1.VolumeSnapshot, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetVolumeSnapshot", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*v1.VolumeSnapshot)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetVolumeSnapshot indicates an expected call of GetVolumeSnapshot.
|
||||
func (mr *MockSnapshotFetcherMockRecorder) GetVolumeSnapshot(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeSnapshot", reflect.TypeOf((*MockSnapshotFetcher)(nil).GetVolumeSnapshot), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// NewSnapshotter mocks base method.
|
||||
func (m *MockSnapshotFetcher) NewSnapshotter() (snapshot.Snapshotter, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "NewSnapshotter")
|
||||
ret0, _ := ret[0].(snapshot.Snapshotter)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// NewSnapshotter indicates an expected call of NewSnapshotter.
|
||||
func (mr *MockSnapshotFetcherMockRecorder) NewSnapshotter() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewSnapshotter", reflect.TypeOf((*MockSnapshotFetcher)(nil).NewSnapshotter))
|
||||
}
|
|
@ -486,7 +486,7 @@ func (s *CSITestSuite) TestPvcBrowseSnapshotPVC(c *C) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *CSITestSuite) TestCreateInspectorApplication(c *C) {
|
||||
func (s *CSITestSuite) TestCreateInspectorApplicationForPVC(c *C) {
|
||||
ctx := context.Background()
|
||||
resourceQuantity := resource.MustParse("1Gi")
|
||||
snapshotAPIGroup := "snapshot.storage.k8s.io"
|
||||
|
|
239
pkg/csi/snapshot_inspector.go
Normal file
239
pkg/csi/snapshot_inspector.go
Normal file
|
@ -0,0 +1,239 @@
|
|||
package csi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/kastenhq/kubestr/pkg/csi/types"
|
||||
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||
"github.com/pkg/errors"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
sv1 "k8s.io/api/storage/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type SnapshotBrowseRunner struct {
|
||||
KubeCli kubernetes.Interface
|
||||
DynCli dynamic.Interface
|
||||
browserSteps SnapshotBrowserStepper
|
||||
pvc *v1.PersistentVolumeClaim
|
||||
pod *v1.Pod
|
||||
snapshot *snapv1.VolumeSnapshot
|
||||
}
|
||||
|
||||
func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *types.SnapshotBrowseArgs) error {
|
||||
r.browserSteps = &snapshotBrowserSteps{
|
||||
validateOps: &validateOperations{
|
||||
kubeCli: r.KubeCli,
|
||||
dynCli: r.DynCli,
|
||||
},
|
||||
versionFetchOps: &apiVersionFetch{
|
||||
kubeCli: r.KubeCli,
|
||||
},
|
||||
createAppOps: &applicationCreate{
|
||||
kubeCli: r.KubeCli,
|
||||
},
|
||||
snapshotFetchOps: &snapshotFetch{
|
||||
kubeCli: r.KubeCli,
|
||||
dynCli: r.DynCli,
|
||||
},
|
||||
portForwardOps: &portforward{},
|
||||
cleanerOps: &cleanse{
|
||||
kubeCli: r.KubeCli,
|
||||
dynCli: r.DynCli,
|
||||
},
|
||||
}
|
||||
return r.RunSnapshotBrowseHelper(ctx, args)
|
||||
}
|
||||
|
||||
func (r *SnapshotBrowseRunner) RunSnapshotBrowseHelper(ctx context.Context, args *types.SnapshotBrowseArgs) error {
|
||||
defer func() {
|
||||
fmt.Println("Cleaning up resources.")
|
||||
r.browserSteps.Cleanup(ctx, r.pvc, r.pod)
|
||||
}()
|
||||
|
||||
if r.KubeCli == nil || r.DynCli == nil {
|
||||
return fmt.Errorf("cli uninitialized")
|
||||
}
|
||||
|
||||
fmt.Println("Fetching the snapshot.")
|
||||
vs, sc, err := r.browserSteps.ValidateArgs(ctx, args)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to validate arguments.")
|
||||
}
|
||||
r.snapshot = vs
|
||||
|
||||
fmt.Println("Creating the file browser application.")
|
||||
r.pod, r.pvc, err = r.browserSteps.CreateInspectorApplication(ctx, args, r.snapshot, sc)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to create inspector application.")
|
||||
}
|
||||
|
||||
fmt.Println("Forwarding the port.")
|
||||
err = r.browserSteps.PortForwardAPod(ctx, r.pod, args.LocalPort)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to port forward Pod.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_snapshot_browser_stepper.go -package=mocks . SnapshotBrowserStepper
|
||||
type SnapshotBrowserStepper interface {
|
||||
ValidateArgs(ctx context.Context, args *types.SnapshotBrowseArgs) (*snapv1.VolumeSnapshot, *sv1.StorageClass, error)
|
||||
CreateInspectorApplication(ctx context.Context, args *types.SnapshotBrowseArgs, snapshot *snapv1.VolumeSnapshot, storageClass *sv1.StorageClass) (*v1.Pod, *v1.PersistentVolumeClaim, error)
|
||||
PortForwardAPod(ctx context.Context, pod *v1.Pod, localPort int) error
|
||||
Cleanup(ctx context.Context, pvc *v1.PersistentVolumeClaim, pod *v1.Pod)
|
||||
}
|
||||
|
||||
type snapshotBrowserSteps struct {
|
||||
validateOps ArgumentValidator
|
||||
versionFetchOps ApiVersionFetcher
|
||||
snapshotFetchOps SnapshotFetcher
|
||||
createAppOps ApplicationCreator
|
||||
portForwardOps PortForwarder
|
||||
cleanerOps Cleaner
|
||||
SnapshotGroupVersion *metav1.GroupVersionForDiscovery
|
||||
}
|
||||
|
||||
func (s *snapshotBrowserSteps) ValidateArgs(ctx context.Context, args *types.SnapshotBrowseArgs) (*snapv1.VolumeSnapshot, *sv1.StorageClass, error) {
|
||||
if err := args.Validate(); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to validate input arguments")
|
||||
}
|
||||
if err := s.validateOps.ValidateNamespace(ctx, args.Namespace); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to validate Namespace")
|
||||
}
|
||||
groupVersion, err := s.versionFetchOps.GetCSISnapshotGroupVersion()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to fetch groupVersion")
|
||||
}
|
||||
s.SnapshotGroupVersion = groupVersion
|
||||
snapshot, err := s.validateOps.ValidateVolumeSnapshot(ctx, args.SnapshotName, args.Namespace, groupVersion)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to validate VolumeSnapshot")
|
||||
}
|
||||
pvc, err := s.validateOps.ValidatePVC(ctx, *snapshot.Spec.Source.PersistentVolumeClaimName, args.Namespace)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to validate source PVC")
|
||||
}
|
||||
sc, err := s.validateOps.ValidateStorageClass(ctx, *pvc.Spec.StorageClassName)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to validate SC")
|
||||
}
|
||||
uVSC, err := s.validateOps.ValidateVolumeSnapshotClass(ctx, *snapshot.Spec.VolumeSnapshotClassName, groupVersion)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to validate VolumeSnapshotClass")
|
||||
}
|
||||
vscDriver := getDriverNameFromUVSC(*uVSC, groupVersion.GroupVersion)
|
||||
if sc.Provisioner != vscDriver {
|
||||
return nil, nil, fmt.Errorf("StorageClass provisioner (%s) and VolumeSnapshotClass driver (%s) are different.", sc.Provisioner, vscDriver)
|
||||
}
|
||||
return snapshot, sc, nil
|
||||
}
|
||||
|
||||
func (s *snapshotBrowserSteps) CreateInspectorApplication(ctx context.Context, args *types.SnapshotBrowseArgs, snapshot *snapv1.VolumeSnapshot, storageClass *sv1.StorageClass) (*v1.Pod, *v1.PersistentVolumeClaim, error) {
|
||||
snapshotAPIGroup := "snapshot.storage.k8s.io"
|
||||
snapshotKind := "VolumeSnapshot"
|
||||
dataSource := &v1.TypedLocalObjectReference{
|
||||
APIGroup: &snapshotAPIGroup,
|
||||
Kind: snapshotKind,
|
||||
Name: snapshot.Name,
|
||||
}
|
||||
pvcArgs := &types.CreatePVCArgs{
|
||||
GenerateName: clonedPVCGenerateName,
|
||||
StorageClass: storageClass.Name,
|
||||
Namespace: args.Namespace,
|
||||
DataSource: dataSource,
|
||||
RestoreSize: snapshot.Status.RestoreSize,
|
||||
}
|
||||
pvc, err := s.createAppOps.CreatePVC(ctx, pvcArgs)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Failed to restore PVC")
|
||||
}
|
||||
podArgs := &types.CreatePodArgs{
|
||||
GenerateName: clonedPodGenerateName,
|
||||
PVCName: pvc.Name,
|
||||
Namespace: args.Namespace,
|
||||
RunAsUser: args.RunAsUser,
|
||||
ContainerImage: "filebrowser/filebrowser:v2",
|
||||
ContainerArgs: []string{"--noauth", "-r", "/data"},
|
||||
MountPath: "/data",
|
||||
}
|
||||
pod, err := s.createAppOps.CreatePod(ctx, podArgs)
|
||||
if err != nil {
|
||||
return nil, pvc, errors.Wrap(err, "Failed to create restored Pod")
|
||||
}
|
||||
if err = s.createAppOps.WaitForPodReady(ctx, args.Namespace, pod.Name); err != nil {
|
||||
return pod, pvc, errors.Wrap(err, "Pod failed to become ready")
|
||||
}
|
||||
return pod, pvc, nil
|
||||
}
|
||||
|
||||
func (s *snapshotBrowserSteps) PortForwardAPod(ctx context.Context, pod *v1.Pod, localPort int) error {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
stopChan, readyChan, errChan := make(chan struct{}, 1), make(chan struct{}, 1), make(chan string)
|
||||
out, errOut := new(bytes.Buffer), new(bytes.Buffer)
|
||||
cfg, err := s.portForwardOps.FetchRestConfig()
|
||||
if err != nil {
|
||||
return errors.New("Failed to fetch rest config")
|
||||
}
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-sigs
|
||||
fmt.Println("Stopping port forward")
|
||||
close(stopChan)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
pfArgs := &types.PortForwardAPodRequest{
|
||||
RestConfig: cfg,
|
||||
Pod: pod,
|
||||
LocalPort: localPort,
|
||||
PodPort: 80,
|
||||
OutStream: bytes.Buffer(*out),
|
||||
ErrOutStream: bytes.Buffer(*errOut),
|
||||
StopCh: stopChan,
|
||||
ReadyCh: readyChan,
|
||||
}
|
||||
err = s.portForwardOps.PortForwardAPod(pfArgs)
|
||||
if err != nil {
|
||||
errChan <- fmt.Sprintf("Failed to port forward (%s)", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-readyChan:
|
||||
url := fmt.Sprintf("http://localhost:%d/", localPort)
|
||||
fmt.Printf("Port forwarding is ready to get traffic. visit %s\n", url)
|
||||
openbrowser(url)
|
||||
wg.Wait()
|
||||
case msg := <-errChan:
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *snapshotBrowserSteps) Cleanup(ctx context.Context, pvc *v1.PersistentVolumeClaim, pod *v1.Pod) {
|
||||
if pvc != nil {
|
||||
err := s.cleanerOps.DeletePVC(ctx, pvc.Name, pvc.Namespace)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to delete PVC", pvc)
|
||||
}
|
||||
}
|
||||
if pod != nil {
|
||||
err := s.cleanerOps.DeletePod(ctx, pod.Name, pod.Namespace)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to delete Pod", pod)
|
||||
}
|
||||
}
|
||||
}
|
566
pkg/csi/snapshot_inspector_steps_test.go
Normal file
566
pkg/csi/snapshot_inspector_steps_test.go
Normal file
|
@ -0,0 +1,566 @@
|
|||
package csi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/kastenhq/kubestr/pkg/common"
|
||||
"github.com/kastenhq/kubestr/pkg/csi/mocks"
|
||||
"github.com/kastenhq/kubestr/pkg/csi/types"
|
||||
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||
. "gopkg.in/check.v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
sv1 "k8s.io/api/storage/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) {
|
||||
ctx := context.Background()
|
||||
scName := "sc"
|
||||
vscName := "vsc"
|
||||
pvcName := "pvc"
|
||||
type fields struct {
|
||||
validateOps *mocks.MockArgumentValidator
|
||||
versionOps *mocks.MockApiVersionFetcher
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
args *types.SnapshotBrowseArgs
|
||||
prepare func(f *fields)
|
||||
errChecker Checker
|
||||
}{
|
||||
{ // valid args
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
SnapshotName: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
|
||||
f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(
|
||||
&metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: common.SnapshotAlphaVersion,
|
||||
}, nil),
|
||||
f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), "vs", "ns", gomock.Any()).Return(
|
||||
&snapv1.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: snapv1.VolumeSnapshotSpec{
|
||||
Source: snapv1.VolumeSnapshotSource{
|
||||
PersistentVolumeClaimName: &pvcName,
|
||||
},
|
||||
VolumeSnapshotClassName: &vscName,
|
||||
},
|
||||
}, nil,
|
||||
),
|
||||
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), "pvc", "ns").Return(
|
||||
&v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: v1.PersistentVolumeClaimSpec{
|
||||
VolumeName: "vol",
|
||||
StorageClassName: &scName,
|
||||
},
|
||||
}, nil,
|
||||
),
|
||||
f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), scName).Return(
|
||||
&sv1.StorageClass{
|
||||
Provisioner: "p1",
|
||||
}, nil),
|
||||
f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), "vsc", &metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: common.SnapshotAlphaVersion,
|
||||
}).Return(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
common.VolSnapClassAlphaDriverKey: "p1",
|
||||
},
|
||||
}, nil),
|
||||
)
|
||||
},
|
||||
errChecker: IsNil,
|
||||
},
|
||||
{ // driver mismatch
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
SnapshotName: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
|
||||
f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(
|
||||
&metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: common.SnapshotAlphaVersion,
|
||||
}, nil),
|
||||
f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), "vs", "ns", gomock.Any()).Return(
|
||||
&snapv1.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: snapv1.VolumeSnapshotSpec{
|
||||
Source: snapv1.VolumeSnapshotSource{
|
||||
PersistentVolumeClaimName: &pvcName,
|
||||
},
|
||||
VolumeSnapshotClassName: &vscName,
|
||||
},
|
||||
}, nil,
|
||||
),
|
||||
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), "pvc", "ns").Return(
|
||||
&v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: v1.PersistentVolumeClaimSpec{
|
||||
VolumeName: "vol",
|
||||
StorageClassName: &scName,
|
||||
},
|
||||
}, nil,
|
||||
),
|
||||
f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(
|
||||
&sv1.StorageClass{
|
||||
Provisioner: "p1",
|
||||
}, nil),
|
||||
f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), "vsc", &metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: common.SnapshotAlphaVersion,
|
||||
}).Return(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
common.VolSnapClassAlphaDriverKey: "p2",
|
||||
},
|
||||
}, nil),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{ // vsc error
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
SnapshotName: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
|
||||
f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, nil),
|
||||
f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), "vs", "ns", gomock.Any()).Return(
|
||||
&snapv1.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: snapv1.VolumeSnapshotSpec{
|
||||
Source: snapv1.VolumeSnapshotSource{
|
||||
PersistentVolumeClaimName: &pvcName,
|
||||
},
|
||||
VolumeSnapshotClassName: &vscName,
|
||||
},
|
||||
}, nil,
|
||||
),
|
||||
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), "pvc", "ns").Return(
|
||||
&v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: v1.PersistentVolumeClaimSpec{
|
||||
VolumeName: "vol",
|
||||
StorageClassName: &scName,
|
||||
},
|
||||
}, nil,
|
||||
),
|
||||
f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, nil),
|
||||
f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("vsc error")),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{ // get driver versionn error
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
SnapshotName: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
|
||||
f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, fmt.Errorf("driver version error")),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{ // sc error
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
SnapshotName: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
|
||||
f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, nil),
|
||||
f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), "vs", "ns", gomock.Any()).Return(
|
||||
&snapv1.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: snapv1.VolumeSnapshotSpec{
|
||||
Source: snapv1.VolumeSnapshotSource{
|
||||
PersistentVolumeClaimName: &pvcName,
|
||||
},
|
||||
VolumeSnapshotClassName: &vscName,
|
||||
},
|
||||
}, nil,
|
||||
),
|
||||
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), "pvc", "ns").Return(
|
||||
&v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: v1.PersistentVolumeClaimSpec{
|
||||
VolumeName: "vol",
|
||||
StorageClassName: &scName,
|
||||
},
|
||||
}, nil,
|
||||
),
|
||||
f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("sc error")),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{ // validate vs error
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
SnapshotName: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
|
||||
f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, nil),
|
||||
f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("validate vs error")),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{ // validate ns error
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
SnapshotName: "vs",
|
||||
Namespace: "ns",
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(fmt.Errorf("validate ns error")),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{ // validate vs error
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
SnapshotName: "",
|
||||
Namespace: "ns",
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{ // validate ns error
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
SnapshotName: "dfd",
|
||||
Namespace: "",
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
} {
|
||||
ctrl := gomock.NewController(c)
|
||||
defer ctrl.Finish()
|
||||
f := fields{
|
||||
validateOps: mocks.NewMockArgumentValidator(ctrl),
|
||||
versionOps: mocks.NewMockApiVersionFetcher(ctrl),
|
||||
}
|
||||
if tc.prepare != nil {
|
||||
tc.prepare(&f)
|
||||
}
|
||||
stepper := &snapshotBrowserSteps{
|
||||
validateOps: f.validateOps,
|
||||
versionFetchOps: f.versionOps,
|
||||
}
|
||||
_, _, err := stepper.ValidateArgs(ctx, tc.args)
|
||||
c.Check(err, tc.errChecker)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CSITestSuite) TestCreateInspectorApplicationForSnapshot(c *C) {
|
||||
ctx := context.Background()
|
||||
resourceQuantity := resource.MustParse("1Gi")
|
||||
snapshotAPIGroup := "snapshot.storage.k8s.io"
|
||||
type fields struct {
|
||||
createAppOps *mocks.MockApplicationCreator
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
args *types.SnapshotBrowseArgs
|
||||
snapshot *snapv1.VolumeSnapshot
|
||||
sc *sv1.StorageClass
|
||||
prepare func(f *fields)
|
||||
errChecker Checker
|
||||
podChecker Checker
|
||||
pvcChecker Checker
|
||||
}{
|
||||
{
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
Namespace: "ns",
|
||||
RunAsUser: 100,
|
||||
},
|
||||
sc: &sv1.StorageClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sc",
|
||||
},
|
||||
},
|
||||
snapshot: &snapv1.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs",
|
||||
},
|
||||
Status: &snapv1.VolumeSnapshotStatus{
|
||||
RestoreSize: &resourceQuantity,
|
||||
},
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), &types.CreatePVCArgs{
|
||||
GenerateName: clonedPVCGenerateName,
|
||||
StorageClass: "sc",
|
||||
Namespace: "ns",
|
||||
DataSource: &v1.TypedLocalObjectReference{
|
||||
APIGroup: &snapshotAPIGroup,
|
||||
Kind: "VolumeSnapshot",
|
||||
Name: "vs",
|
||||
},
|
||||
RestoreSize: &resourceQuantity,
|
||||
}).Return(&v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc",
|
||||
},
|
||||
}, nil),
|
||||
f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{
|
||||
GenerateName: clonedPodGenerateName,
|
||||
PVCName: "pvc",
|
||||
Namespace: "ns",
|
||||
ContainerArgs: []string{"--noauth", "-r", "/data"},
|
||||
MountPath: "/data",
|
||||
RunAsUser: 100,
|
||||
ContainerImage: "filebrowser/filebrowser:v2",
|
||||
}).Return(&v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod",
|
||||
},
|
||||
}, nil),
|
||||
f.createAppOps.EXPECT().WaitForPodReady(gomock.Any(), "ns", "pod").Return(nil),
|
||||
)
|
||||
},
|
||||
errChecker: IsNil,
|
||||
podChecker: NotNil,
|
||||
pvcChecker: NotNil,
|
||||
},
|
||||
{
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
Namespace: "ns",
|
||||
RunAsUser: 100,
|
||||
},
|
||||
sc: &sv1.StorageClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sc",
|
||||
},
|
||||
},
|
||||
snapshot: &snapv1.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs",
|
||||
},
|
||||
Status: &snapv1.VolumeSnapshotStatus{
|
||||
RestoreSize: &resourceQuantity,
|
||||
},
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), &types.CreatePVCArgs{
|
||||
GenerateName: clonedPVCGenerateName,
|
||||
StorageClass: "sc",
|
||||
Namespace: "ns",
|
||||
DataSource: &v1.TypedLocalObjectReference{
|
||||
APIGroup: &snapshotAPIGroup,
|
||||
Kind: "VolumeSnapshot",
|
||||
Name: "vs",
|
||||
},
|
||||
RestoreSize: &resourceQuantity,
|
||||
}).Return(&v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc",
|
||||
},
|
||||
}, nil),
|
||||
f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{
|
||||
GenerateName: clonedPodGenerateName,
|
||||
PVCName: "pvc",
|
||||
Namespace: "ns",
|
||||
ContainerArgs: []string{"--noauth", "-r", "/data"},
|
||||
MountPath: "/data",
|
||||
RunAsUser: 100,
|
||||
ContainerImage: "filebrowser/filebrowser:v2",
|
||||
}).Return(&v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod",
|
||||
},
|
||||
}, nil),
|
||||
f.createAppOps.EXPECT().WaitForPodReady(gomock.Any(), "ns", "pod").Return(fmt.Errorf("pod ready error")),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
podChecker: NotNil,
|
||||
pvcChecker: NotNil,
|
||||
},
|
||||
{
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
Namespace: "ns",
|
||||
RunAsUser: 100,
|
||||
},
|
||||
sc: &sv1.StorageClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sc",
|
||||
},
|
||||
},
|
||||
snapshot: &snapv1.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs",
|
||||
},
|
||||
Status: &snapv1.VolumeSnapshotStatus{
|
||||
RestoreSize: &resourceQuantity,
|
||||
},
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), gomock.Any()).Return(&v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc",
|
||||
},
|
||||
}, nil),
|
||||
f.createAppOps.EXPECT().CreatePod(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("pod error")),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
podChecker: IsNil,
|
||||
pvcChecker: NotNil,
|
||||
},
|
||||
{
|
||||
args: &types.SnapshotBrowseArgs{
|
||||
Namespace: "ns",
|
||||
RunAsUser: 100,
|
||||
},
|
||||
sc: &sv1.StorageClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sc",
|
||||
},
|
||||
},
|
||||
snapshot: &snapv1.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vs",
|
||||
},
|
||||
Status: &snapv1.VolumeSnapshotStatus{
|
||||
RestoreSize: &resourceQuantity,
|
||||
},
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
podChecker: IsNil,
|
||||
pvcChecker: IsNil,
|
||||
},
|
||||
} {
|
||||
ctrl := gomock.NewController(c)
|
||||
defer ctrl.Finish()
|
||||
f := fields{
|
||||
createAppOps: mocks.NewMockApplicationCreator(ctrl),
|
||||
}
|
||||
if tc.prepare != nil {
|
||||
tc.prepare(&f)
|
||||
}
|
||||
stepper := &snapshotBrowserSteps{
|
||||
createAppOps: f.createAppOps,
|
||||
}
|
||||
pod, pvc, err := stepper.CreateInspectorApplication(ctx, tc.args, tc.snapshot, tc.sc)
|
||||
c.Check(err, tc.errChecker)
|
||||
c.Check(pod, tc.podChecker)
|
||||
c.Check(pvc, tc.pvcChecker)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CSITestSuite) TestSnapshotBrowseCleanup(c *C) {
|
||||
ctx := context.Background()
|
||||
groupversion := &metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: "gv",
|
||||
Version: "v",
|
||||
}
|
||||
type fields struct {
|
||||
cleanerOps *mocks.MockCleaner
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
pvc *v1.PersistentVolumeClaim
|
||||
pod *v1.Pod
|
||||
prepare func(f *fields)
|
||||
}{
|
||||
{
|
||||
pvc: &v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc",
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod",
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.cleanerOps.EXPECT().DeletePVC(ctx, "pvc", "ns").Return(nil),
|
||||
f.cleanerOps.EXPECT().DeletePod(ctx, "pod", "ns").Return(nil),
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
pvc: &v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc",
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod",
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.cleanerOps.EXPECT().DeletePVC(ctx, "pvc", "ns").Return(fmt.Errorf("err")),
|
||||
f.cleanerOps.EXPECT().DeletePod(ctx, "pod", "ns").Return(fmt.Errorf("err")),
|
||||
)
|
||||
},
|
||||
},
|
||||
} {
|
||||
ctrl := gomock.NewController(c)
|
||||
defer ctrl.Finish()
|
||||
f := fields{
|
||||
cleanerOps: mocks.NewMockCleaner(ctrl),
|
||||
}
|
||||
if tc.prepare != nil {
|
||||
tc.prepare(&f)
|
||||
}
|
||||
stepper := &snapshotBrowserSteps{
|
||||
cleanerOps: f.cleanerOps,
|
||||
SnapshotGroupVersion: groupversion,
|
||||
}
|
||||
stepper.Cleanup(ctx, tc.pvc, tc.pod)
|
||||
}
|
||||
}
|
192
pkg/csi/snapshot_inspector_test.go
Normal file
192
pkg/csi/snapshot_inspector_test.go
Normal file
|
@ -0,0 +1,192 @@
|
|||
package csi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/kastenhq/kubestr/pkg/csi/mocks"
|
||||
"github.com/kastenhq/kubestr/pkg/csi/types"
|
||||
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||
. "gopkg.in/check.v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
sv1 "k8s.io/api/storage/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/dynamic"
|
||||
fakedynamic "k8s.io/client-go/dynamic/fake"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func (s *CSITestSuite) TestRunSnapshotBrowseHelper(c *C) {
|
||||
ctx := context.Background()
|
||||
type fields struct {
|
||||
stepperOps *mocks.MockSnapshotBrowserStepper
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
kubeCli kubernetes.Interface
|
||||
dynCli dynamic.Interface
|
||||
args *types.SnapshotBrowseArgs
|
||||
prepare func(f *fields)
|
||||
errChecker Checker
|
||||
}{
|
||||
{
|
||||
// success
|
||||
kubeCli: fake.NewSimpleClientset(),
|
||||
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||
args: &types.SnapshotBrowseArgs{},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(
|
||||
&snapv1.VolumeSnapshot{}, &sv1.StorageClass{}, nil,
|
||||
),
|
||||
f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), gomock.Any(),
|
||||
&snapv1.VolumeSnapshot{}, &sv1.StorageClass{},
|
||||
).Return(
|
||||
&v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
&v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc1",
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
),
|
||||
f.stepperOps.EXPECT().PortForwardAPod(gomock.Any(),
|
||||
&v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
Namespace: "ns",
|
||||
},
|
||||
}, gomock.Any(),
|
||||
).Return(nil),
|
||||
f.stepperOps.EXPECT().Cleanup(gomock.Any(),
|
||||
&v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pvc1",
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
&v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
},
|
||||
errChecker: IsNil,
|
||||
},
|
||||
{
|
||||
// portforward failure
|
||||
kubeCli: fake.NewSimpleClientset(),
|
||||
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||
args: &types.SnapshotBrowseArgs{},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||
f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||
f.stepperOps.EXPECT().PortForwardAPod(gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("portforward error")),
|
||||
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any()),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{
|
||||
// createapp failure
|
||||
kubeCli: fake.NewSimpleClientset(),
|
||||
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||
args: &types.SnapshotBrowseArgs{},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||
f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, fmt.Errorf("createapp error")),
|
||||
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any()),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{
|
||||
// fetch snapshot failure
|
||||
kubeCli: fake.NewSimpleClientset(),
|
||||
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||
args: &types.SnapshotBrowseArgs{},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil, fmt.Errorf("snapshot error")),
|
||||
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any()),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{
|
||||
// validate failure
|
||||
kubeCli: fake.NewSimpleClientset(),
|
||||
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||
args: &types.SnapshotBrowseArgs{},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil, fmt.Errorf("validate error")),
|
||||
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any()),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{
|
||||
// emptycli failure
|
||||
kubeCli: nil,
|
||||
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||
args: &types.SnapshotBrowseArgs{},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any()),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
{
|
||||
// emptydyncli failure
|
||||
kubeCli: fake.NewSimpleClientset(),
|
||||
dynCli: nil,
|
||||
args: &types.SnapshotBrowseArgs{},
|
||||
prepare: func(f *fields) {
|
||||
gomock.InOrder(
|
||||
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any()),
|
||||
)
|
||||
},
|
||||
errChecker: NotNil,
|
||||
},
|
||||
} {
|
||||
ctrl := gomock.NewController(c)
|
||||
defer ctrl.Finish()
|
||||
f := fields{
|
||||
stepperOps: mocks.NewMockSnapshotBrowserStepper(ctrl),
|
||||
}
|
||||
if tc.prepare != nil {
|
||||
tc.prepare(&f)
|
||||
}
|
||||
runner := &SnapshotBrowseRunner{
|
||||
KubeCli: tc.kubeCli,
|
||||
DynCli: tc.dynCli,
|
||||
browserSteps: f.stepperOps,
|
||||
}
|
||||
err := runner.RunSnapshotBrowseHelper(ctx, tc.args)
|
||||
c.Check(err, tc.errChecker)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CSITestSuite) TestSnapshotBrowseRunner(c *C) {
|
||||
ctx := context.Background()
|
||||
r := &SnapshotBrowseRunner{
|
||||
browserSteps: &snapshotBrowserSteps{},
|
||||
}
|
||||
err := r.RunSnapshotBrowseHelper(ctx, nil)
|
||||
c.Check(err, NotNil)
|
||||
}
|
|
@ -94,6 +94,18 @@ func (c *CreateSnapshotArgs) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type FetchSnapshotArgs struct {
|
||||
Namespace string
|
||||
SnapshotName string
|
||||
}
|
||||
|
||||
func (c *FetchSnapshotArgs) Validate() error {
|
||||
if c.Namespace == "" || c.SnapshotName == "" {
|
||||
return fmt.Errorf("Invalid FetchSnapshotArgs (%v)", c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CreateFromSourceCheckArgs struct {
|
||||
VolumeSnapshotClass string
|
||||
SnapshotName string
|
||||
|
@ -122,6 +134,20 @@ func (p *PVCBrowseArgs) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type SnapshotBrowseArgs struct {
|
||||
SnapshotName string
|
||||
Namespace string
|
||||
RunAsUser int64
|
||||
LocalPort int
|
||||
}
|
||||
|
||||
func (p *SnapshotBrowseArgs) Validate() error {
|
||||
if p.SnapshotName == "" || p.Namespace == "" {
|
||||
return fmt.Errorf("Invalid SnapshotBrowseArgs (%v)", p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PortForwardAPodRequest struct {
|
||||
// RestConfig is the kubernetes config
|
||||
RestConfig *rest.Config
|
||||
|
|
Loading…
Reference in a new issue