From 36882b59f48431fbb8f5a7e2400ec554795fd6f2 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 14:42:33 -0500 Subject: [PATCH 01/40] Adding the kubestr browse pvc command. Handling kubestr browse support for backward compatibility. --- cmd/rootCmd.go | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 8855912..4086252 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -83,9 +83,19 @@ var ( }, } - pvcBrowseLocalPort int - pvcBrowseCmd = &cobra.Command{ - Use: "browse [PVC name]", + browseLocalPort int + browseCmd = &cobra.Command{ + Use: "browse", + Short: "Browse the contents of PVC or VolumeSnapshot", + Long: "Browse the contents of a CSI provisioned PVC or a CSI provisioned VolumeSnapshot.", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return browsePvcCmd.RunE(cmd, args) + }, + } + + browsePvcCmd = &cobra.Command{ + Use: "pvc [PVC name]", Short: "Browse the contents of a CSI PVC via file browser", Long: "Browse the contents of a CSI provisioned PVC by cloning the volume and mounting it with a file browser.", Args: cobra.ExactArgs(1), @@ -94,7 +104,7 @@ var ( namespace, csiCheckVolumeSnapshotClass, csiCheckRunAsUser, - pvcBrowseLocalPort, + browseLocalPort, ) }, } @@ -164,12 +174,19 @@ func init() { csiCheckCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the CSI check pod with the specified user ID (int)") csiCheckCmd.Flags().BoolVarP(&csiCheckSkipCFSCheck, "skipCFScheck", "k", false, "Use this flag to skip validating the ability to clone a snapshot.") - rootCmd.AddCommand(pvcBrowseCmd) - pvcBrowseCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)") - _ = pvcBrowseCmd.MarkFlagRequired("volumesnapshotclass") - pvcBrowseCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the PersistentVolumeClaim.") - pvcBrowseCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") - pvcBrowseCmd.Flags().IntVarP(&pvcBrowseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") + rootCmd.AddCommand(browseCmd) + browseCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)") + _ = browseCmd.MarkFlagRequired("volumesnapshotclass") + browseCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the PersistentVolumeClaim.") + browseCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") + browseCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") + + browseCmd.AddCommand(browsePvcCmd) + browsePvcCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)") + _ = browsePvcCmd.MarkFlagRequired("volumesnapshotclass") + browsePvcCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the PersistentVolumeClaim.") + browsePvcCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") + browsePvcCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") rootCmd.AddCommand(blockMountCmd) blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a Storageclass. (Required)") From 0927b25bad31eade18c5a231887f3df446da4088 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 11:41:56 -0500 Subject: [PATCH 02/40] Adding browse snapshot command. Updating browse command to browse pvc command. --- cmd/rootCmd.go | 58 +++++- pkg/csi/csi_ops.go | 49 +++++ pkg/csi/mocks/mock_argument_validator.go | 36 +++- pkg/csi/mocks/mock_snapshot_fetcher.go | 68 ++++++ pkg/csi/snapshot_inspector.go | 252 +++++++++++++++++++++++ pkg/csi/types/csi_types.go | 27 +++ 6 files changed, 479 insertions(+), 11 deletions(-) create mode 100644 pkg/csi/mocks/mock_snapshot_fetcher.go create mode 100644 pkg/csi/snapshot_inspector.go diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 4086252..32e08a4 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -109,6 +109,21 @@ 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, + storageClass, + csiCheckRunAsUser, + browseLocalPort, + ) + }, + } + blockMountRunAsUser int64 blockMountCleanup bool blockMountCleanupOnly bool @@ -188,8 +203,15 @@ func init() { browsePvcCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") browsePvcCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") + browseCmd.AddCommand(browseSnapshotCmd) + browseSnapshotCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)") + _ = browseSnapshotCmd.MarkFlagRequired("storageclass") + browseSnapshotCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the VolumeSnapshot.") + browseSnapshotCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") + browseSnapshotCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") + 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.") @@ -375,6 +397,40 @@ func CsiPvcBrowse(ctx context.Context, return err } +func CsiSnapshotBrowse(ctx context.Context, + snapshotName string, + namespace string, + storageClass 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, + StorageClassName: storageClass, + 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 { diff --git a/pkg/csi/csi_ops.go b/pkg/csi/csi_ops.go index 7316731..280a8d2 100644 --- a/pkg/csi/csi_ops.go +++ b/pkg/csi/csi_ops.go @@ -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) diff --git a/pkg/csi/mocks/mock_argument_validator.go b/pkg/csi/mocks/mock_argument_validator.go index 22553a0..86af720 100644 --- a/pkg/csi/mocks/mock_argument_validator.go +++ b/pkg/csi/mocks/mock_argument_validator.go @@ -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) diff --git a/pkg/csi/mocks/mock_snapshot_fetcher.go b/pkg/csi/mocks/mock_snapshot_fetcher.go new file mode 100644 index 0000000..8bd55e8 --- /dev/null +++ b/pkg/csi/mocks/mock_snapshot_fetcher.go @@ -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)) +} diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go new file mode 100644 index 0000000..64ae7fb --- /dev/null +++ b/pkg/csi/snapshot_inspector.go @@ -0,0 +1,252 @@ +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, r.snapshot) + }() + + if r.KubeCli == nil || r.DynCli == nil { + return fmt.Errorf("cli uninitialized") + } + + sc, err := r.browserSteps.ValidateArgs(ctx, args) + if err != nil { + return errors.Wrap(err, "Failed to validate arguments.") + } + + fmt.Println("Fetching the snapshot.") + r.snapshot, err = r.browserSteps.FetchVS(ctx, args) + if err != nil { + return errors.Wrap(err, "Failed to fetch VolumeSnapshot.") + } + + 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) (*sv1.StorageClass, error) + FetchVS(ctx context.Context, args *types.SnapshotBrowseArgs) (*snapv1.VolumeSnapshot, 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, snapshot *snapv1.VolumeSnapshot) +} + +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) (*sv1.StorageClass, error) { + if err := args.Validate(); err != nil { + return nil, errors.Wrap(err, "Failed to validate input arguments") + } + if err := s.validateOps.ValidateNamespace(ctx, args.Namespace); err != nil { + return nil, errors.Wrap(err, "Failed to validate Namespace") + } + sc, err := s.validateOps.ValidateStorageClass(ctx, args.StorageClassName) + if err != nil { + return nil, errors.Wrap(err, "Failed to validate SC") + } + groupVersion, err := s.versionFetchOps.GetCSISnapshotGroupVersion() + if err != nil { + return 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, errors.Wrap(err, "Failed to validate VolumeSnapshot") + } + uVSC, err := s.validateOps.ValidateVolumeSnapshotClass(ctx, *snapshot.Spec.VolumeSnapshotClassName, groupVersion) + if err != nil { + return nil, errors.Wrap(err, "Failed to validate VolumeSnapshotClass") + } + vscDriver := getDriverNameFromUVSC(*uVSC, groupVersion.GroupVersion) + if sc.Provisioner != vscDriver { + return nil, fmt.Errorf("StorageClass provisioner (%s) and VolumeSnapshotClass driver (%s) are different.", sc.Provisioner, vscDriver) + } + return sc, nil +} + +func (s *snapshotBrowserSteps) FetchVS(ctx context.Context, args *types.SnapshotBrowseArgs) (*snapv1.VolumeSnapshot, error) { + snapshotter, err := s.snapshotFetchOps.NewSnapshotter() + if err != nil { + return nil, errors.Wrap(err, "Failed to load snapshotter") + } + snapArgs := &types.FetchSnapshotArgs{ + Namespace: args.Namespace, + SnapshotName: args.SnapshotName, + } + return s.snapshotFetchOps.GetVolumeSnapshot(ctx, snapshotter, snapArgs) +} + +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, snapshot *snapv1.VolumeSnapshot) { + 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) + } + } +} diff --git a/pkg/csi/types/csi_types.go b/pkg/csi/types/csi_types.go index d09a745..e418365 100644 --- a/pkg/csi/types/csi_types.go +++ b/pkg/csi/types/csi_types.go @@ -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,21 @@ func (p *PVCBrowseArgs) Validate() error { return nil } +type SnapshotBrowseArgs struct { + SnapshotName string + Namespace string + StorageClassName string + RunAsUser int64 + LocalPort int +} + +func (p *SnapshotBrowseArgs) Validate() error { + if p.SnapshotName == "" || p.Namespace == "" || p.StorageClassName == "" { + return fmt.Errorf("Invalid SnapshotBrowseArgs (%v)", p) + } + return nil +} + type PortForwardAPodRequest struct { // RestConfig is the kubernetes config RestConfig *rest.Config From c2d35703bdd510c7cbd683387a576ef6b26900b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 10:30:17 -0700 Subject: [PATCH 03/40] 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](https://github.com/github/codeql-action/compare/4fa2a7953630fd2f3fb380f21be14ede0169dd4f...2d790406f505036ef40ecba973cc774a50395aac) --- 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 34cc6b1..2f71fea 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -45,7 +45,7 @@ jobs: - # Upload the results to GitHub's code scanning dashboard. name: "Upload to results to dashboard" - uses: github/codeql-action/upload-sarif@4fa2a7953630fd2f3fb380f21be14ede0169dd4f # v3.25.12 + uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 with: sarif_file: results.sarif - From b6bf82c9d5d9c9a98b991042889388804196bc72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:49:04 -0700 Subject: [PATCH 04/40] 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](https://github.com/docker/build-push-action/compare/1a162644f9a7e87d8f4b053101d1d9a712edc18c...1ca370b3a9802c92e886402e0dd88098a2533b12) --- 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index a3b927a..1c625e6 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -59,7 +59,7 @@ jobs: # Build and push Docker image with Buildx (don't push on PR) # https://github.com/docker/build-push-action - name: Build and push Docker image - uses: docker/build-push-action@1a162644f9a7e87d8f4b053101d1d9a712edc18c # v6.3.0 + uses: docker/build-push-action@1ca370b3a9802c92e886402e0dd88098a2533b12 # v6.4.1 with: context: . push: ${{ github.event_name != 'pull_request' }} From a6ac17763d54f82165846f650060e5e2f2044348 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 18:48:12 -0500 Subject: [PATCH 05/40] Adding --show-tree flag for browse snapshot & browse pvc commands --- cmd/rootCmd.go | 6 ++++++ pkg/csi/pvc_inspector.go | 4 ++++ pkg/csi/snapshot_inspector.go | 4 ++++ pkg/csi/types/csi_types.go | 2 ++ 4 files changed, 16 insertions(+) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 32e08a4..4547cea 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -94,6 +94,8 @@ var ( }, } + showTree bool + browsePvcCmd = &cobra.Command{ Use: "pvc [PVC name]", Short: "Browse the contents of a CSI PVC via file browser", @@ -202,6 +204,7 @@ func init() { browsePvcCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the PersistentVolumeClaim.") browsePvcCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") browsePvcCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") + browsePvcCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of PVC") browseCmd.AddCommand(browseSnapshotCmd) browseSnapshotCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)") @@ -209,6 +212,7 @@ func init() { browseSnapshotCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the VolumeSnapshot.") browseSnapshotCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") browseSnapshotCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") + browseSnapshotCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of VolumeSnapshot") rootCmd.AddCommand(blockMountCmd) blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)") @@ -390,6 +394,7 @@ func CsiPvcBrowse(ctx context.Context, VolumeSnapshotClass: volumeSnapshotClass, RunAsUser: runAsUser, LocalPort: localPort, + ShowTree: showTree, }) if err != nil { fmt.Printf("Failed to run PVC browser (%s)\n", err.Error()) @@ -424,6 +429,7 @@ func CsiSnapshotBrowse(ctx context.Context, StorageClassName: storageClass, RunAsUser: runAsUser, LocalPort: localPort, + ShowTree: showTree, }) if err != nil { fmt.Printf("Failed to run Snapshot browser (%s)\n", err.Error()) diff --git a/pkg/csi/pvc_inspector.go b/pkg/csi/pvc_inspector.go index 82fa838..0f9f8a1 100644 --- a/pkg/csi/pvc_inspector.go +++ b/pkg/csi/pvc_inspector.go @@ -54,6 +54,10 @@ func (r *PVCBrowseRunner) RunPVCBrowse(ctx context.Context, args *types.PVCBrows dynCli: r.DynCli, }, } + if args.ShowTree { + fmt.Println("Show Tree works for PVC!") + return nil + } return r.RunPVCBrowseHelper(ctx, args) } diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 64ae7fb..856689b 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -49,6 +49,10 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type dynCli: r.DynCli, }, } + if args.ShowTree { + fmt.Println("Show Tree works for VS!") + return nil + } return r.RunSnapshotBrowseHelper(ctx, args) } diff --git a/pkg/csi/types/csi_types.go b/pkg/csi/types/csi_types.go index e418365..2af3433 100644 --- a/pkg/csi/types/csi_types.go +++ b/pkg/csi/types/csi_types.go @@ -125,6 +125,7 @@ type PVCBrowseArgs struct { VolumeSnapshotClass string RunAsUser int64 LocalPort int + ShowTree bool } func (p *PVCBrowseArgs) Validate() error { @@ -140,6 +141,7 @@ type SnapshotBrowseArgs struct { StorageClassName string RunAsUser int64 LocalPort int + ShowTree bool } func (p *SnapshotBrowseArgs) Validate() error { From e2aa023f2209d6106ce31f1860fafa3f3b8aca94 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Thu, 25 Jul 2024 11:28:27 -0500 Subject: [PATCH 06/40] Removing unused snapshot function parameter in cleanup --- pkg/csi/snapshot_inspector.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 856689b..da504b0 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -59,7 +59,7 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type 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, r.snapshot) + r.browserSteps.Cleanup(ctx, r.pvc, r.pod) }() if r.KubeCli == nil || r.DynCli == nil { @@ -98,7 +98,7 @@ type SnapshotBrowserStepper interface { FetchVS(ctx context.Context, args *types.SnapshotBrowseArgs) (*snapv1.VolumeSnapshot, 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, snapshot *snapv1.VolumeSnapshot) + Cleanup(ctx context.Context, pvc *v1.PersistentVolumeClaim, pod *v1.Pod) } type snapshotBrowserSteps struct { @@ -240,7 +240,7 @@ func (s *snapshotBrowserSteps) PortForwardAPod(ctx context.Context, pod *v1.Pod, return nil } -func (s *snapshotBrowserSteps) Cleanup(ctx context.Context, pvc *v1.PersistentVolumeClaim, pod *v1.Pod, snapshot *snapv1.VolumeSnapshot) { +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 { From 1f10988e7d8ced090f9aa1f62aa057e7a9f5bed3 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 26 Jul 2024 16:32:05 -0500 Subject: [PATCH 07/40] Adding KubeExecutor Exec helper function to execute tree command --- pkg/csi/csi_ops.go | 17 ++++++++++ pkg/csi/mocks/mock_kube_executor.go | 50 +++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 pkg/csi/mocks/mock_kube_executor.go diff --git a/pkg/csi/csi_ops.go b/pkg/csi/csi_ops.go index 280a8d2..cbca361 100644 --- a/pkg/csi/csi_ops.go +++ b/pkg/csi/csi_ops.go @@ -568,3 +568,20 @@ func (p *portforward) PortForwardAPod(req *types.PortForwardAPodRequest) error { func (p *portforward) FetchRestConfig() (*rest.Config, error) { return kube.LoadConfig() } + +//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_kube_executor.go -package=mocks . KubeExecutor +type KubeExecutor interface { + Exec(ctx context.Context, namespace string, podName string, ContainerName string, command []string) (string, error) +} + +type kubeExec struct { + kubeCli kubernetes.Interface +} + +func (k *kubeExec) Exec(ctx context.Context, namespace string, podName string, ContainerName string, command []string) (string, error) { + if k.kubeCli == nil { + return "", fmt.Errorf("kubeCli not initialized") + } + stdout, _, err := kankube.Exec(ctx, k.kubeCli, namespace, podName, ContainerName, command, nil) + return stdout, err +} diff --git a/pkg/csi/mocks/mock_kube_executor.go b/pkg/csi/mocks/mock_kube_executor.go new file mode 100644 index 0000000..aabcbb2 --- /dev/null +++ b/pkg/csi/mocks/mock_kube_executor.go @@ -0,0 +1,50 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: KubeExecutor) + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockKubeExecutor is a mock of KubeExecutor interface. +type MockKubeExecutor struct { + ctrl *gomock.Controller + recorder *MockKubeExecutorMockRecorder +} + +// MockKubeExecutorMockRecorder is the mock recorder for MockKubeExecutor. +type MockKubeExecutorMockRecorder struct { + mock *MockKubeExecutor +} + +// NewMockKubeExecutor creates a new mock instance. +func NewMockKubeExecutor(ctrl *gomock.Controller) *MockKubeExecutor { + mock := &MockKubeExecutor{ctrl: ctrl} + mock.recorder = &MockKubeExecutorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockKubeExecutor) EXPECT() *MockKubeExecutorMockRecorder { + return m.recorder +} + +// Exec mocks base method. +func (m *MockKubeExecutor) Exec(arg0 context.Context, arg1, arg2, arg3 string, arg4 []string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Exec", arg0, arg1, arg2, arg3, arg4) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Exec indicates an expected call of Exec. +func (mr *MockKubeExecutorMockRecorder) Exec(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockKubeExecutor)(nil).Exec), arg0, arg1, arg2, arg3, arg4) +} From 34744f6b561c2f1b543bbb55c847f4b1efc08fab Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 26 Jul 2024 16:36:44 -0500 Subject: [PATCH 08/40] Adding --show-tree logic in pvc_inspector.go --- pkg/csi/pvc_inspector.go | 48 +++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/pkg/csi/pvc_inspector.go b/pkg/csi/pvc_inspector.go index 0f9f8a1..d3a9563 100644 --- a/pkg/csi/pvc_inspector.go +++ b/pkg/csi/pvc_inspector.go @@ -49,15 +49,14 @@ func (r *PVCBrowseRunner) RunPVCBrowse(ctx context.Context, args *types.PVCBrows dynCli: r.DynCli, }, portForwardOps: &portforward{}, + kubeExecutor: &kubeExec{ + kubeCli: r.KubeCli, + }, cleanerOps: &cleanse{ kubeCli: r.KubeCli, dynCli: r.DynCli, }, } - if args.ShowTree { - fmt.Println("Show Tree works for PVC!") - return nil - } return r.RunPVCBrowseHelper(ctx, args) } @@ -74,19 +73,29 @@ func (r *PVCBrowseRunner) RunPVCBrowseHelper(ctx context.Context, args *types.PV return errors.Wrap(err, "Failed to validate arguments.") } - fmt.Println("Taking a snapshot") + fmt.Println("Taking a snapshot.") snapName := snapshotPrefix + time.Now().Format("20060102150405") r.snapshot, err = r.browserSteps.SnapshotPVC(ctx, args, snapName) if err != nil { return errors.Wrap(err, "Failed to snapshot PVC.") } - fmt.Println("Creating the file browser application.") + fmt.Println("Creating the browser pod.") 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.") } + if args.ShowTree { + fmt.Println("Printing the tree structure from root directory.") + stdout, err := r.browserSteps.ExecuteTreeCommand(ctx, args, r.pod) + if err != nil { + return errors.Wrap(err, "Failed to execute tree command in pod.") + } + fmt.Println(stdout) + return nil + } + fmt.Println("Forwarding the port.") err = r.browserSteps.PortForwardAPod(ctx, r.pod, args.LocalPort) if err != nil { @@ -101,6 +110,7 @@ type PVCBrowserStepper interface { ValidateArgs(ctx context.Context, args *types.PVCBrowseArgs) (*sv1.StorageClass, error) SnapshotPVC(ctx context.Context, args *types.PVCBrowseArgs, snapshotName string) (*snapv1.VolumeSnapshot, error) CreateInspectorApplication(ctx context.Context, args *types.PVCBrowseArgs, snapshot *snapv1.VolumeSnapshot, storageClass *sv1.StorageClass) (*v1.Pod, *v1.PersistentVolumeClaim, error) + ExecuteTreeCommand(ctx context.Context, args *types.PVCBrowseArgs, pod *v1.Pod) (string, error) PortForwardAPod(ctx context.Context, pod *v1.Pod, localPort int) error Cleanup(ctx context.Context, pvc *v1.PersistentVolumeClaim, pod *v1.Pod, snapshot *snapv1.VolumeSnapshot) } @@ -112,6 +122,7 @@ type pvcBrowserSteps struct { snapshotCreateOps SnapshotCreator portForwardOps PortForwarder cleanerOps Cleaner + kubeExecutor KubeExecutor SnapshotGroupVersion *metav1.GroupVersionForDiscovery } @@ -199,11 +210,23 @@ func (p *pvcBrowserSteps) CreateInspectorApplication(ctx context.Context, args * RunAsUser: args.RunAsUser, ContainerImage: "filebrowser/filebrowser:v2", ContainerArgs: []string{"--noauth", "-r", "/data"}, - MountPath: "/data", + MountPath: "/pvc-data", + } + if args.ShowTree { + podArgs = &types.CreatePodArgs{ + GenerateName: clonedPodGenerateName, + PVCName: pvc.Name, + Namespace: args.Namespace, + RunAsUser: args.RunAsUser, + ContainerImage: "alpine:3.19", + Command: []string{"/bin/sh"}, + ContainerArgs: []string{"-c", "while true; do sleep 3600; done"}, + MountPath: "/pvc-data", + } } pod, err := p.createAppOps.CreatePod(ctx, podArgs) if err != nil { - return nil, pvc, errors.Wrap(err, "Failed to create restored Pod") + return nil, pvc, errors.Wrap(err, "Failed to create browse Pod") } if err = p.createAppOps.WaitForPodReady(ctx, args.Namespace, pod.Name); err != nil { return pod, pvc, errors.Wrap(err, "Pod failed to become ready") @@ -211,6 +234,15 @@ func (p *pvcBrowserSteps) CreateInspectorApplication(ctx context.Context, args * return pod, pvc, nil } +func (p *pvcBrowserSteps) ExecuteTreeCommand(ctx context.Context, args *types.PVCBrowseArgs, pod *v1.Pod) (string, error) { + command := []string{"tree", "/pvc-data"} + stdout, err := p.kubeExecutor.Exec(ctx, args.Namespace, pod.Name, pod.Spec.Containers[0].Name, command) + if err != nil { + return "", errors.Wrapf(err, "Error running command:(%v)", command) + } + return stdout, nil +} + func (p *pvcBrowserSteps) PortForwardAPod(ctx context.Context, pod *v1.Pod, localPort int) error { var wg sync.WaitGroup wg.Add(1) From cbed58b16bfbaf5b0e18546ad33a3627b61d698f Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 26 Jul 2024 16:37:01 -0500 Subject: [PATCH 09/40] Adding --show-tree logic in snapshot_inspector.go --- pkg/csi/snapshot_inspector.go | 46 +++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index da504b0..520af9e 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -44,15 +44,14 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type dynCli: r.DynCli, }, portForwardOps: &portforward{}, + kubeExecutor: &kubeExec{ + kubeCli: r.KubeCli, + }, cleanerOps: &cleanse{ kubeCli: r.KubeCli, dynCli: r.DynCli, }, } - if args.ShowTree { - fmt.Println("Show Tree works for VS!") - return nil - } return r.RunSnapshotBrowseHelper(ctx, args) } @@ -77,12 +76,22 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowseHelper(ctx context.Context, args return errors.Wrap(err, "Failed to fetch VolumeSnapshot.") } - fmt.Println("Creating the file browser application.") + fmt.Println("Creating the browser pod.") 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.") } + if args.ShowTree { + fmt.Println("Printing the tree structure from root directory.") + stdout, err := r.browserSteps.ExecuteTreeCommand(ctx, args, r.pod) + if err != nil { + return errors.Wrap(err, "Failed to execute tree command in pod.") + } + fmt.Println(stdout) + return nil + } + fmt.Println("Forwarding the port.") err = r.browserSteps.PortForwardAPod(ctx, r.pod, args.LocalPort) if err != nil { @@ -97,6 +106,7 @@ type SnapshotBrowserStepper interface { ValidateArgs(ctx context.Context, args *types.SnapshotBrowseArgs) (*sv1.StorageClass, error) FetchVS(ctx context.Context, args *types.SnapshotBrowseArgs) (*snapv1.VolumeSnapshot, error) CreateInspectorApplication(ctx context.Context, args *types.SnapshotBrowseArgs, snapshot *snapv1.VolumeSnapshot, storageClass *sv1.StorageClass) (*v1.Pod, *v1.PersistentVolumeClaim, error) + ExecuteTreeCommand(ctx context.Context, args *types.SnapshotBrowseArgs, pod *v1.Pod) (string, error) PortForwardAPod(ctx context.Context, pod *v1.Pod, localPort int) error Cleanup(ctx context.Context, pvc *v1.PersistentVolumeClaim, pod *v1.Pod) } @@ -108,6 +118,7 @@ type snapshotBrowserSteps struct { createAppOps ApplicationCreator portForwardOps PortForwarder cleanerOps Cleaner + kubeExecutor KubeExecutor SnapshotGroupVersion *metav1.GroupVersionForDiscovery } @@ -180,11 +191,23 @@ func (s *snapshotBrowserSteps) CreateInspectorApplication(ctx context.Context, a RunAsUser: args.RunAsUser, ContainerImage: "filebrowser/filebrowser:v2", ContainerArgs: []string{"--noauth", "-r", "/data"}, - MountPath: "/data", + MountPath: "/snapshot-data", + } + if args.ShowTree { + podArgs = &types.CreatePodArgs{ + GenerateName: clonedPodGenerateName, + PVCName: pvc.Name, + Namespace: args.Namespace, + RunAsUser: args.RunAsUser, + ContainerImage: "alpine:3.19", + Command: []string{"/bin/sh"}, + ContainerArgs: []string{"-c", "while true; do sleep 3600; done"}, + MountPath: "/snapshot-data", + } } pod, err := s.createAppOps.CreatePod(ctx, podArgs) if err != nil { - return nil, pvc, errors.Wrap(err, "Failed to create restored Pod") + return nil, pvc, errors.Wrap(err, "Failed to create browse Pod") } if err = s.createAppOps.WaitForPodReady(ctx, args.Namespace, pod.Name); err != nil { return pod, pvc, errors.Wrap(err, "Pod failed to become ready") @@ -192,6 +215,15 @@ func (s *snapshotBrowserSteps) CreateInspectorApplication(ctx context.Context, a return pod, pvc, nil } +func (s *snapshotBrowserSteps) ExecuteTreeCommand(ctx context.Context, args *types.SnapshotBrowseArgs, pod *v1.Pod) (string, error) { + command := []string{"tree", "/snapshot-data"} + stdout, err := s.kubeExecutor.Exec(ctx, args.Namespace, pod.Name, pod.Spec.Containers[0].Name, command) + if err != nil { + return "", errors.Wrapf(err, "Error running command:(%v)", command) + } + return stdout, nil +} + func (s *snapshotBrowserSteps) PortForwardAPod(ctx context.Context, pod *v1.Pod, localPort int) error { var wg sync.WaitGroup wg.Add(1) From 2f8e25576e323bd60bbc5dc2856734511201595d Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 26 Jul 2024 19:04:44 -0500 Subject: [PATCH 10/40] Printing out the tree structure for --show-tree --- pkg/csi/pvc_inspector.go | 2 +- pkg/csi/snapshot_inspector.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/csi/pvc_inspector.go b/pkg/csi/pvc_inspector.go index d3a9563..b64afe3 100644 --- a/pkg/csi/pvc_inspector.go +++ b/pkg/csi/pvc_inspector.go @@ -92,7 +92,7 @@ func (r *PVCBrowseRunner) RunPVCBrowseHelper(ctx context.Context, args *types.PV if err != nil { return errors.Wrap(err, "Failed to execute tree command in pod.") } - fmt.Println(stdout) + fmt.Printf("\n%s\n\n", stdout) return nil } diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 520af9e..89b8489 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -88,7 +88,7 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowseHelper(ctx context.Context, args if err != nil { return errors.Wrap(err, "Failed to execute tree command in pod.") } - fmt.Println(stdout) + fmt.Printf("\n%s\n\n", stdout) return nil } From 9098f9471ffd09af456fafdf95d94f87ba0e54b3 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 26 Jul 2024 19:29:22 -0500 Subject: [PATCH 11/40] Updating mock tests for new code changes --- pkg/csi/mocks/mock_pvc_browser_stepper.go | 15 +++ .../mocks/mock_snapshot_browser_stepper.go | 126 ++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 pkg/csi/mocks/mock_snapshot_browser_stepper.go diff --git a/pkg/csi/mocks/mock_pvc_browser_stepper.go b/pkg/csi/mocks/mock_pvc_browser_stepper.go index a26efc5..3dade31 100644 --- a/pkg/csi/mocks/mock_pvc_browser_stepper.go +++ b/pkg/csi/mocks/mock_pvc_browser_stepper.go @@ -66,6 +66,21 @@ func (mr *MockPVCBrowserStepperMockRecorder) CreateInspectorApplication(arg0, ar return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateInspectorApplication", reflect.TypeOf((*MockPVCBrowserStepper)(nil).CreateInspectorApplication), arg0, arg1, arg2, arg3) } +// ExecuteTreeCommand mocks base method. +func (m *MockPVCBrowserStepper) ExecuteTreeCommand(arg0 context.Context, arg1 *types.PVCBrowseArgs, arg2 *v10.Pod) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteTreeCommand", arg0, arg1, arg2) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteTreeCommand indicates an expected call of ExecuteTreeCommand. +func (mr *MockPVCBrowserStepperMockRecorder) ExecuteTreeCommand(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteTreeCommand", reflect.TypeOf((*MockPVCBrowserStepper)(nil).ExecuteTreeCommand), arg0, arg1, arg2) +} + // PortForwardAPod mocks base method. func (m *MockPVCBrowserStepper) PortForwardAPod(arg0 context.Context, arg1 *v10.Pod, arg2 int) error { m.ctrl.T.Helper() diff --git a/pkg/csi/mocks/mock_snapshot_browser_stepper.go b/pkg/csi/mocks/mock_snapshot_browser_stepper.go new file mode 100644 index 0000000..78d9d2c --- /dev/null +++ b/pkg/csi/mocks/mock_snapshot_browser_stepper.go @@ -0,0 +1,126 @@ +// 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) +} + +// ExecuteTreeCommand mocks base method. +func (m *MockSnapshotBrowserStepper) ExecuteTreeCommand(arg0 context.Context, arg1 *types.SnapshotBrowseArgs, arg2 *v10.Pod) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteTreeCommand", arg0, arg1, arg2) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteTreeCommand indicates an expected call of ExecuteTreeCommand. +func (mr *MockSnapshotBrowserStepperMockRecorder) ExecuteTreeCommand(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteTreeCommand", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).ExecuteTreeCommand), arg0, arg1, arg2) +} + +// FetchVS mocks base method. +func (m *MockSnapshotBrowserStepper) FetchVS(arg0 context.Context, arg1 *types.SnapshotBrowseArgs) (*v1.VolumeSnapshot, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FetchVS", arg0, arg1) + ret0, _ := ret[0].(*v1.VolumeSnapshot) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FetchVS indicates an expected call of FetchVS. +func (mr *MockSnapshotBrowserStepperMockRecorder) FetchVS(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchVS", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).FetchVS), arg0, arg1) +} + +// 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) (*v11.StorageClass, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateArgs", arg0, arg1) + ret0, _ := ret[0].(*v11.StorageClass) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// 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) +} From a0c97b91ef0ae2d89c9c363fbe046bdce39cf662 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Mon, 29 Jul 2024 12:13:16 -0500 Subject: [PATCH 12/40] Updating mount path in container args for creating a browse pod --- pkg/csi/pvc_inspector.go | 2 +- pkg/csi/snapshot_inspector.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/csi/pvc_inspector.go b/pkg/csi/pvc_inspector.go index b64afe3..89cad2a 100644 --- a/pkg/csi/pvc_inspector.go +++ b/pkg/csi/pvc_inspector.go @@ -209,7 +209,7 @@ func (p *pvcBrowserSteps) CreateInspectorApplication(ctx context.Context, args * Namespace: args.Namespace, RunAsUser: args.RunAsUser, ContainerImage: "filebrowser/filebrowser:v2", - ContainerArgs: []string{"--noauth", "-r", "/data"}, + ContainerArgs: []string{"--noauth", "-r", "/pvc-data"}, MountPath: "/pvc-data", } if args.ShowTree { diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 89b8489..3d3bef9 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -190,7 +190,7 @@ func (s *snapshotBrowserSteps) CreateInspectorApplication(ctx context.Context, a Namespace: args.Namespace, RunAsUser: args.RunAsUser, ContainerImage: "filebrowser/filebrowser:v2", - ContainerArgs: []string{"--noauth", "-r", "/data"}, + ContainerArgs: []string{"--noauth", "-r", "/snapshot-data"}, MountPath: "/snapshot-data", } if args.ShowTree { From 16e29c4c76de36e07dac35860b00d397d143301c Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Mon, 29 Jul 2024 12:24:09 -0500 Subject: [PATCH 13/40] Updating the CSITestSuite.TestCreateInspectorApplication for changes in the mount path --- pkg/csi/pvc_inspector_steps_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/csi/pvc_inspector_steps_test.go b/pkg/csi/pvc_inspector_steps_test.go index 8b53069..3859c4b 100644 --- a/pkg/csi/pvc_inspector_steps_test.go +++ b/pkg/csi/pvc_inspector_steps_test.go @@ -541,8 +541,8 @@ func (s *CSITestSuite) TestCreateInspectorApplication(c *C) { GenerateName: clonedPodGenerateName, PVCName: "pvc1", Namespace: "ns", - ContainerArgs: []string{"--noauth", "-r", "/data"}, - MountPath: "/data", + ContainerArgs: []string{"--noauth", "-r", "/pvc-data"}, + MountPath: "/pvc-data", RunAsUser: 100, ContainerImage: "filebrowser/filebrowser:v2", }).Return(&v1.Pod{ @@ -596,8 +596,8 @@ func (s *CSITestSuite) TestCreateInspectorApplication(c *C) { GenerateName: clonedPodGenerateName, PVCName: "pvc1", Namespace: "ns", - ContainerArgs: []string{"--noauth", "-r", "/data"}, - MountPath: "/data", + ContainerArgs: []string{"--noauth", "-r", "/pvc-data"}, + MountPath: "/pvc-data", RunAsUser: 100, ContainerImage: "filebrowser/filebrowser:v2", }).Return(&v1.Pod{ From 8b44243ce4652d0503b6b4647e151142646a5700 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Mon, 29 Jul 2024 15:08:30 -0500 Subject: [PATCH 14/40] Adding Deprecated msg to the 'browse' command --- cmd/rootCmd.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 4547cea..d8efef0 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -85,10 +85,11 @@ var ( browseLocalPort int browseCmd = &cobra.Command{ - Use: "browse", - Short: "Browse the contents of PVC or VolumeSnapshot", - Long: "Browse the contents of a CSI provisioned PVC or a CSI provisioned VolumeSnapshot.", - Args: cobra.ExactArgs(1), + Use: "browse", + Short: "Browse the contents of PVC or VolumeSnapshot", + Long: "Browse the contents of a CSI provisioned PVC or a CSI provisioned VolumeSnapshot.", + Deprecated: "we recommend you to use command 'browse pvc' instead. Command 'browse pvc' will support newer updates for browsing through the contents of a PVC.", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return browsePvcCmd.RunE(cmd, args) }, From 0bb5fd50f2606fe35925ef3c6d14497e252d838b Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 26 Jul 2024 19:33:12 -0500 Subject: [PATCH 15/40] Adding mock tests for SnapshotBrowserStepper --- pkg/csi/mocks/mock_snapshot_browser_stepper.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/pkg/csi/mocks/mock_snapshot_browser_stepper.go b/pkg/csi/mocks/mock_snapshot_browser_stepper.go index 78d9d2c..7f28913 100644 --- a/pkg/csi/mocks/mock_snapshot_browser_stepper.go +++ b/pkg/csi/mocks/mock_snapshot_browser_stepper.go @@ -66,21 +66,6 @@ func (mr *MockSnapshotBrowserStepperMockRecorder) CreateInspectorApplication(arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateInspectorApplication", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).CreateInspectorApplication), arg0, arg1, arg2, arg3) } -// ExecuteTreeCommand mocks base method. -func (m *MockSnapshotBrowserStepper) ExecuteTreeCommand(arg0 context.Context, arg1 *types.SnapshotBrowseArgs, arg2 *v10.Pod) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExecuteTreeCommand", arg0, arg1, arg2) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ExecuteTreeCommand indicates an expected call of ExecuteTreeCommand. -func (mr *MockSnapshotBrowserStepperMockRecorder) ExecuteTreeCommand(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteTreeCommand", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).ExecuteTreeCommand), arg0, arg1, arg2) -} - // FetchVS mocks base method. func (m *MockSnapshotBrowserStepper) FetchVS(arg0 context.Context, arg1 *types.SnapshotBrowseArgs) (*v1.VolumeSnapshot, error) { m.ctrl.T.Helper() From 778b71cf920d33ba1179b193d2eb51d466b475ad Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Mon, 29 Jul 2024 15:48:22 -0500 Subject: [PATCH 16/40] Adding fake tests for snapshot_inspector.go --- pkg/csi/snapshot_inspector_test.go | 209 +++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 pkg/csi/snapshot_inspector_test.go diff --git a/pkg/csi/snapshot_inspector_test.go b/pkg/csi/snapshot_inspector_test.go new file mode 100644 index 0000000..f0db79d --- /dev/null +++ b/pkg/csi/snapshot_inspector_test.go @@ -0,0 +1,209 @@ +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( + &sv1.StorageClass{}, nil, + ), + f.stepperOps.EXPECT().FetchVS(gomock.Any(), gomock.Any()).Return( + &snapv1.VolumeSnapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "snap1", + Namespace: "ns", + }, + }, + nil, + ), + f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), gomock.Any(), + &snapv1.VolumeSnapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "snap1", + Namespace: "ns", + }, + }, &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), + f.stepperOps.EXPECT().FetchVS(gomock.Any(), gomock.Any()).Return(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), + f.stepperOps.EXPECT().FetchVS(gomock.Any(), gomock.Any()).Return(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), + f.stepperOps.EXPECT().FetchVS(gomock.Any(), gomock.Any()).Return(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, fmt.Errorf("snapshot 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) +} From b17344a0090863cea996597a256c450ccc4d46ef Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Mon, 29 Jul 2024 17:15:33 -0500 Subject: [PATCH 17/40] Renamed testcase CSITestSuite.TestCreateInspectorApplication to TestCreateInspectorApplicationForPVC --- pkg/csi/pvc_inspector_steps_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/csi/pvc_inspector_steps_test.go b/pkg/csi/pvc_inspector_steps_test.go index 3859c4b..25d6a62 100644 --- a/pkg/csi/pvc_inspector_steps_test.go +++ b/pkg/csi/pvc_inspector_steps_test.go @@ -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" From f8480453dbd95da68308f4a502a54d70c24a72b9 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Mon, 29 Jul 2024 17:16:23 -0500 Subject: [PATCH 18/40] Adding snapshot_inspector_steps_test.go --- pkg/csi/snapshot_inspector_steps_test.go | 610 +++++++++++++++++++++++ 1 file changed, 610 insertions(+) create mode 100644 pkg/csi/snapshot_inspector_steps_test.go diff --git a/pkg/csi/snapshot_inspector_steps_test.go b/pkg/csi/snapshot_inspector_steps_test.go new file mode 100644 index 0000000..f492767 --- /dev/null +++ b/pkg/csi/snapshot_inspector_steps_test.go @@ -0,0 +1,610 @@ +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() + 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", + StorageClassName: "sc", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), "sc").Return( + &sv1.StorageClass{ + Provisioner: "p1", + }, 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().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", + StorageClassName: "sc", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return( + &sv1.StorageClass{ + Provisioner: "p1", + }, 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().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", + StorageClassName: "sc", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, 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().ValidateVolumeSnapshotClass(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("vsc error")), + ) + }, + errChecker: NotNil, + }, + { // get driver versionn error + args: &types.SnapshotBrowseArgs{ + SnapshotName: "vs", + StorageClassName: "sc", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, nil), + f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, fmt.Errorf("driver version error")), + ) + }, + errChecker: NotNil, + }, + { // sc error + args: &types.SnapshotBrowseArgs{ + SnapshotName: "vs", + StorageClassName: "sc", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(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", + StorageClassName: "sc", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, 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", + StorageClassName: "sc", + 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: "", + StorageClassName: "sc", + Namespace: "ns", + }, + errChecker: NotNil, + }, + { // validate vsc error + args: &types.SnapshotBrowseArgs{ + SnapshotName: "dfd", + StorageClassName: "", + Namespace: "ns", + }, + errChecker: NotNil, + }, + { // validate ns error + args: &types.SnapshotBrowseArgs{ + SnapshotName: "dfd", + StorageClassName: "ddd", + 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) TestSnapshotBrowseFetchVS(c *C) { + ctx := context.Background() + snapshotter := &fakeSnapshotter{name: "snapshotter"} + groupversion := &metav1.GroupVersionForDiscovery{ + GroupVersion: "gv", + Version: "v", + } + type fields struct { + snapshotOps *mocks.MockSnapshotFetcher + } + for _, tc := range []struct { + args *types.SnapshotBrowseArgs + prepare func(f *fields) + errChecker Checker + snapChecker Checker + }{ + { + args: &types.SnapshotBrowseArgs{ + Namespace: "ns", + StorageClassName: "sc", + SnapshotName: "vs", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.snapshotOps.EXPECT().NewSnapshotter().Return(snapshotter, nil), + f.snapshotOps.EXPECT().GetVolumeSnapshot(gomock.Any(), snapshotter, &types.FetchSnapshotArgs{ + Namespace: "ns", + SnapshotName: "vs", + }).Return(&snapv1.VolumeSnapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vs", + }, + }, nil), + ) + }, + errChecker: IsNil, + snapChecker: NotNil, + }, + { + args: &types.SnapshotBrowseArgs{ + Namespace: "ns", + StorageClassName: "sc", + SnapshotName: "vs", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.snapshotOps.EXPECT().NewSnapshotter().Return(snapshotter, nil), + f.snapshotOps.EXPECT().GetVolumeSnapshot(gomock.Any(), snapshotter, &types.FetchSnapshotArgs{ + Namespace: "ns", + SnapshotName: "vs", + }).Return(nil, fmt.Errorf("error")), + ) + }, + errChecker: NotNil, + snapChecker: IsNil, + }, + { + args: &types.SnapshotBrowseArgs{ + Namespace: "ns", + StorageClassName: "sc", + SnapshotName: "vs", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.snapshotOps.EXPECT().NewSnapshotter().Return(nil, fmt.Errorf("error")), + ) + }, + errChecker: NotNil, + snapChecker: IsNil, + }, + } { + ctrl := gomock.NewController(c) + defer ctrl.Finish() + f := fields{ + snapshotOps: mocks.NewMockSnapshotFetcher(ctrl), + } + if tc.prepare != nil { + tc.prepare(&f) + } + stepper := &snapshotBrowserSteps{ + snapshotFetchOps: f.snapshotOps, + SnapshotGroupVersion: groupversion, + } + snapshot, err := stepper.FetchVS(ctx, tc.args) + c.Check(err, tc.errChecker) + c.Check(snapshot, tc.snapChecker) + } +} + +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) + } +} From 78d498160d53a7ec0566284f742635a4c7965808 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 26 Jul 2024 19:29:22 -0500 Subject: [PATCH 19/40] Updating mock tests for new code changes --- pkg/csi/mocks/mock_snapshot_browser_stepper.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/csi/mocks/mock_snapshot_browser_stepper.go b/pkg/csi/mocks/mock_snapshot_browser_stepper.go index 7f28913..78d9d2c 100644 --- a/pkg/csi/mocks/mock_snapshot_browser_stepper.go +++ b/pkg/csi/mocks/mock_snapshot_browser_stepper.go @@ -66,6 +66,21 @@ func (mr *MockSnapshotBrowserStepperMockRecorder) CreateInspectorApplication(arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateInspectorApplication", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).CreateInspectorApplication), arg0, arg1, arg2, arg3) } +// ExecuteTreeCommand mocks base method. +func (m *MockSnapshotBrowserStepper) ExecuteTreeCommand(arg0 context.Context, arg1 *types.SnapshotBrowseArgs, arg2 *v10.Pod) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteTreeCommand", arg0, arg1, arg2) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteTreeCommand indicates an expected call of ExecuteTreeCommand. +func (mr *MockSnapshotBrowserStepperMockRecorder) ExecuteTreeCommand(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteTreeCommand", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).ExecuteTreeCommand), arg0, arg1, arg2) +} + // FetchVS mocks base method. func (m *MockSnapshotBrowserStepper) FetchVS(arg0 context.Context, arg1 *types.SnapshotBrowseArgs) (*v1.VolumeSnapshot, error) { m.ctrl.T.Helper() From 74bd34d09af3c4d6b8f7f011b873801e17c534ad Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Mon, 29 Jul 2024 17:28:53 -0500 Subject: [PATCH 20/40] Updating the mount paths in CSITestSuite.TestCreateInspectorApplicationForSnapshot --- pkg/csi/snapshot_inspector_steps_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/csi/snapshot_inspector_steps_test.go b/pkg/csi/snapshot_inspector_steps_test.go index f492767..61773bd 100644 --- a/pkg/csi/snapshot_inspector_steps_test.go +++ b/pkg/csi/snapshot_inspector_steps_test.go @@ -389,8 +389,8 @@ func (s *CSITestSuite) TestCreateInspectorApplicationForSnapshot(c *C) { GenerateName: clonedPodGenerateName, PVCName: "pvc", Namespace: "ns", - ContainerArgs: []string{"--noauth", "-r", "/data"}, - MountPath: "/data", + ContainerArgs: []string{"--noauth", "-r", "/snapshot-data"}, + MountPath: "/snapshot-data", RunAsUser: 100, ContainerImage: "filebrowser/filebrowser:v2", }).Return(&v1.Pod{ @@ -444,8 +444,8 @@ func (s *CSITestSuite) TestCreateInspectorApplicationForSnapshot(c *C) { GenerateName: clonedPodGenerateName, PVCName: "pvc", Namespace: "ns", - ContainerArgs: []string{"--noauth", "-r", "/data"}, - MountPath: "/data", + ContainerArgs: []string{"--noauth", "-r", "/snapshot-data"}, + MountPath: "/snapshot-data", RunAsUser: 100, ContainerImage: "filebrowser/filebrowser:v2", }).Return(&v1.Pod{ From 4e160b18958952445678be75dc9f21ebb405d451 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Tue, 30 Jul 2024 15:40:29 -0500 Subject: [PATCH 21/40] Updating Deprecated msg for 'browse' command --- cmd/rootCmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index d8efef0..2c5c112 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -88,7 +88,7 @@ var ( Use: "browse", Short: "Browse the contents of PVC or VolumeSnapshot", Long: "Browse the contents of a CSI provisioned PVC or a CSI provisioned VolumeSnapshot.", - Deprecated: "we recommend you to use command 'browse pvc' instead. Command 'browse pvc' will support newer updates for browsing through the contents of a PVC.", + Deprecated: "use 'browse pvc' instead", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return browsePvcCmd.RunE(cmd, args) From 62af1788d83787808472cfa7fdd6ebfe0ab792d8 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 10:57:57 -0500 Subject: [PATCH 22/40] Making namespace, runAsUser & localport flags persistent --- cmd/rootCmd.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 2c5c112..c36e4bd 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -195,24 +195,18 @@ func init() { rootCmd.AddCommand(browseCmd) browseCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)") _ = browseCmd.MarkFlagRequired("volumesnapshotclass") - browseCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the PersistentVolumeClaim.") - browseCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") - browseCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") + browseCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the PersistentVolumeClaim.") + 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") browseCmd.AddCommand(browsePvcCmd) browsePvcCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)") _ = browsePvcCmd.MarkFlagRequired("volumesnapshotclass") - browsePvcCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the PersistentVolumeClaim.") - browsePvcCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") - browsePvcCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") browsePvcCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of PVC") browseCmd.AddCommand(browseSnapshotCmd) browseSnapshotCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)") _ = browseSnapshotCmd.MarkFlagRequired("storageclass") - browseSnapshotCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the VolumeSnapshot.") - browseSnapshotCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") - browseSnapshotCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") browseSnapshotCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of VolumeSnapshot") rootCmd.AddCommand(blockMountCmd) From dc4a65e5e12fcfdaba5416f4c9d84c9964679462 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 12:00:57 -0500 Subject: [PATCH 23/40] Removing namespace, runAsUser & localport flags for browse snapshot because we made those persistent --- cmd/rootCmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index c36e4bd..058fe01 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -195,7 +195,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 above resource.") 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") From 2d419ec7e26c038a6d0c91113f6a3d979bef4094 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 18:48:12 -0500 Subject: [PATCH 24/40] Adding --show-tree flag for browse snapshot & browse pvc commands --- pkg/csi/pvc_inspector.go | 4 ++++ pkg/csi/snapshot_inspector.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/pkg/csi/pvc_inspector.go b/pkg/csi/pvc_inspector.go index 89cad2a..7ef5be7 100644 --- a/pkg/csi/pvc_inspector.go +++ b/pkg/csi/pvc_inspector.go @@ -57,6 +57,10 @@ func (r *PVCBrowseRunner) RunPVCBrowse(ctx context.Context, args *types.PVCBrows dynCli: r.DynCli, }, } + if args.ShowTree { + fmt.Println("Show Tree works for PVC!") + return nil + } return r.RunPVCBrowseHelper(ctx, args) } diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 3d3bef9..99efd8e 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -52,6 +52,10 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type dynCli: r.DynCli, }, } + if args.ShowTree { + fmt.Println("Show Tree works for VS!") + return nil + } return r.RunSnapshotBrowseHelper(ctx, args) } From 0314b8e6518a338a016dadb255e18cdb8f5abac3 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 12:17:45 -0500 Subject: [PATCH 25/40] Updating namespace flag usage for better understanding --- cmd/rootCmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 058fe01..01b3d7e 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -195,7 +195,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 above resource.") + browseCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "Namespace of the resources provided in command.") 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") From f63aebf24684e21057adc2e2909ef796878b5969 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 18:54:10 -0500 Subject: [PATCH 26/40] Removing storage class flag --- cmd/rootCmd.go | 15 +- .../mocks/mock_snapshot_browser_stepper.go | 9 +- pkg/csi/snapshot_inspector.go | 53 ++-- pkg/csi/snapshot_inspector_steps_test.go | 228 +++++++----------- pkg/csi/snapshot_inspector_test.go | 29 +-- pkg/csi/types/csi_types.go | 13 +- 6 files changed, 134 insertions(+), 213 deletions(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 01b3d7e..539f392 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -120,7 +120,6 @@ var ( RunE: func(cmd *cobra.Command, args []string) error { return CsiSnapshotBrowse(context.Background(), args[0], namespace, - storageClass, csiCheckRunAsUser, browseLocalPort, ) @@ -205,8 +204,6 @@ func init() { browsePvcCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of PVC") browseCmd.AddCommand(browseSnapshotCmd) - browseSnapshotCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)") - _ = browseSnapshotCmd.MarkFlagRequired("storageclass") browseSnapshotCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of VolumeSnapshot") rootCmd.AddCommand(blockMountCmd) @@ -400,7 +397,6 @@ func CsiPvcBrowse(ctx context.Context, func CsiSnapshotBrowse(ctx context.Context, snapshotName string, namespace string, - storageClass string, runAsUser int64, localPort int, ) error { @@ -419,12 +415,11 @@ func CsiSnapshotBrowse(ctx context.Context, DynCli: dyncli, } err = browseRunner.RunSnapshotBrowse(ctx, &csitypes.SnapshotBrowseArgs{ - SnapshotName: snapshotName, - Namespace: namespace, - StorageClassName: storageClass, - RunAsUser: runAsUser, - LocalPort: localPort, - ShowTree: showTree, + SnapshotName: snapshotName, + Namespace: namespace, + RunAsUser: runAsUser, + LocalPort: localPort, + ShowTree: showTree, }) if err != nil { fmt.Printf("Failed to run Snapshot browser (%s)\n", err.Error()) diff --git a/pkg/csi/mocks/mock_snapshot_browser_stepper.go b/pkg/csi/mocks/mock_snapshot_browser_stepper.go index 78d9d2c..c83a792 100644 --- a/pkg/csi/mocks/mock_snapshot_browser_stepper.go +++ b/pkg/csi/mocks/mock_snapshot_browser_stepper.go @@ -111,12 +111,13 @@ func (mr *MockSnapshotBrowserStepperMockRecorder) PortForwardAPod(arg0, arg1, ar } // ValidateArgs mocks base method. -func (m *MockSnapshotBrowserStepper) ValidateArgs(arg0 context.Context, arg1 *types.SnapshotBrowseArgs) (*v11.StorageClass, error) { +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].(*v11.StorageClass) - ret1, _ := ret[1].(error) - return ret0, ret1 + 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. diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 99efd8e..442b579 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -69,16 +69,12 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowseHelper(ctx context.Context, args return fmt.Errorf("cli uninitialized") } - sc, err := r.browserSteps.ValidateArgs(ctx, args) - if err != nil { - return errors.Wrap(err, "Failed to validate arguments.") - } - fmt.Println("Fetching the snapshot.") - r.snapshot, err = r.browserSteps.FetchVS(ctx, args) + vs, sc, err := r.browserSteps.ValidateArgs(ctx, args) if err != nil { - return errors.Wrap(err, "Failed to fetch VolumeSnapshot.") + return errors.Wrap(err, "Failed to validate arguments.") } + r.snapshot = vs fmt.Println("Creating the browser pod.") r.pod, r.pvc, err = r.browserSteps.CreateInspectorApplication(ctx, args, r.snapshot, sc) @@ -107,8 +103,7 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowseHelper(ctx context.Context, args //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) (*sv1.StorageClass, error) - FetchVS(ctx context.Context, args *types.SnapshotBrowseArgs) (*snapv1.VolumeSnapshot, error) + 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) ExecuteTreeCommand(ctx context.Context, args *types.SnapshotBrowseArgs, pod *v1.Pod) (string, error) PortForwardAPod(ctx context.Context, pod *v1.Pod, localPort int) error @@ -126,47 +121,39 @@ type snapshotBrowserSteps struct { SnapshotGroupVersion *metav1.GroupVersionForDiscovery } -func (s *snapshotBrowserSteps) ValidateArgs(ctx context.Context, args *types.SnapshotBrowseArgs) (*sv1.StorageClass, error) { +func (s *snapshotBrowserSteps) ValidateArgs(ctx context.Context, args *types.SnapshotBrowseArgs) (*snapv1.VolumeSnapshot, *sv1.StorageClass, error) { if err := args.Validate(); err != nil { - return nil, errors.Wrap(err, "Failed to validate input arguments") + return nil, nil, errors.Wrap(err, "Failed to validate input arguments") } if err := s.validateOps.ValidateNamespace(ctx, args.Namespace); err != nil { - return nil, errors.Wrap(err, "Failed to validate Namespace") - } - sc, err := s.validateOps.ValidateStorageClass(ctx, args.StorageClassName) - if err != nil { - return nil, errors.Wrap(err, "Failed to validate SC") + return nil, nil, errors.Wrap(err, "Failed to validate Namespace") } groupVersion, err := s.versionFetchOps.GetCSISnapshotGroupVersion() if err != nil { - return nil, errors.Wrap(err, "Failed to fetch groupVersion") + 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, errors.Wrap(err, "Failed to validate VolumeSnapshot") + return nil, nil, errors.Wrap(err, "Failed to validate VolumeSnapshot") } - uVSC, err := s.validateOps.ValidateVolumeSnapshotClass(ctx, *snapshot.Spec.VolumeSnapshotClassName, groupVersion) + pvc, err := s.validateOps.ValidatePVC(ctx, *snapshot.Spec.Source.PersistentVolumeClaimName, args.Namespace) if err != nil { - return nil, errors.Wrap(err, "Failed to validate VolumeSnapshotClass") + return nil, nil, errors.Wrap(err, "Failed to validate source PVC") } - vscDriver := getDriverNameFromUVSC(*uVSC, groupVersion.GroupVersion) - if sc.Provisioner != vscDriver { - return nil, fmt.Errorf("StorageClass provisioner (%s) and VolumeSnapshotClass driver (%s) are different.", sc.Provisioner, vscDriver) + sc, err := s.validateOps.ValidateStorageClass(ctx, *pvc.Spec.StorageClassName) + if err != nil { + return nil, nil, errors.Wrap(err, "Failed to validate SC") } - return sc, nil -} - -func (s *snapshotBrowserSteps) FetchVS(ctx context.Context, args *types.SnapshotBrowseArgs) (*snapv1.VolumeSnapshot, error) { - snapshotter, err := s.snapshotFetchOps.NewSnapshotter() + uVSC, err := s.validateOps.ValidateVolumeSnapshotClass(ctx, *snapshot.Spec.VolumeSnapshotClassName, groupVersion) if err != nil { - return nil, errors.Wrap(err, "Failed to load snapshotter") + return nil, nil, errors.Wrap(err, "Failed to validate VolumeSnapshotClass") } - snapArgs := &types.FetchSnapshotArgs{ - Namespace: args.Namespace, - SnapshotName: args.SnapshotName, + 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 s.snapshotFetchOps.GetVolumeSnapshot(ctx, snapshotter, snapArgs) + 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) { diff --git a/pkg/csi/snapshot_inspector_steps_test.go b/pkg/csi/snapshot_inspector_steps_test.go index 61773bd..423c8ac 100644 --- a/pkg/csi/snapshot_inspector_steps_test.go +++ b/pkg/csi/snapshot_inspector_steps_test.go @@ -19,6 +19,7 @@ import ( func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { ctx := context.Background() + scName := "sc" vscName := "vsc" pvcName := "pvc" type fields struct { @@ -32,17 +33,12 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }{ { // valid args args: &types.SnapshotBrowseArgs{ - SnapshotName: "vs", - StorageClassName: "sc", - Namespace: "ns", + SnapshotName: "vs", + Namespace: "ns", }, prepare: func(f *fields) { gomock.InOrder( f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), - f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), "sc").Return( - &sv1.StorageClass{ - Provisioner: "p1", - }, nil), f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return( &metav1.GroupVersionForDiscovery{ GroupVersion: common.SnapshotAlphaVersion, @@ -61,6 +57,22 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }, }, 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{ @@ -74,17 +86,12 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }, { // driver mismatch args: &types.SnapshotBrowseArgs{ - SnapshotName: "vs", - StorageClassName: "sc", - Namespace: "ns", + SnapshotName: "vs", + Namespace: "ns", }, prepare: func(f *fields) { gomock.InOrder( f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), - f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return( - &sv1.StorageClass{ - Provisioner: "p1", - }, nil), f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return( &metav1.GroupVersionForDiscovery{ GroupVersion: common.SnapshotAlphaVersion, @@ -103,6 +110,22 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }, }, 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{ @@ -116,14 +139,12 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }, { // vsc error args: &types.SnapshotBrowseArgs{ - SnapshotName: "vs", - StorageClassName: "sc", - Namespace: "ns", + SnapshotName: "vs", + Namespace: "ns", }, prepare: func(f *fields) { gomock.InOrder( f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), - f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, nil), f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, nil), f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), "vs", "ns", gomock.Any()).Return( &snapv1.VolumeSnapshot{ @@ -139,6 +160,19 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }, }, 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")), ) }, @@ -146,14 +180,12 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }, { // get driver versionn error args: &types.SnapshotBrowseArgs{ - SnapshotName: "vs", - StorageClassName: "sc", - Namespace: "ns", + SnapshotName: "vs", + Namespace: "ns", }, prepare: func(f *fields) { gomock.InOrder( f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), - f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, nil), f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, fmt.Errorf("driver version error")), ) }, @@ -161,13 +193,39 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }, { // sc error args: &types.SnapshotBrowseArgs{ - SnapshotName: "vs", - StorageClassName: "sc", - Namespace: "ns", + 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")), ) }, @@ -175,14 +233,12 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }, { // validate vs error args: &types.SnapshotBrowseArgs{ - SnapshotName: "vs", - StorageClassName: "sc", - Namespace: "ns", + SnapshotName: "vs", + Namespace: "ns", }, prepare: func(f *fields) { gomock.InOrder( f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), - f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, 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")), ) @@ -191,9 +247,8 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }, { // validate ns error args: &types.SnapshotBrowseArgs{ - SnapshotName: "vs", - StorageClassName: "sc", - Namespace: "ns", + SnapshotName: "vs", + Namespace: "ns", }, prepare: func(f *fields) { gomock.InOrder( @@ -204,25 +259,15 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { }, { // validate vs error args: &types.SnapshotBrowseArgs{ - SnapshotName: "", - StorageClassName: "sc", - Namespace: "ns", - }, - errChecker: NotNil, - }, - { // validate vsc error - args: &types.SnapshotBrowseArgs{ - SnapshotName: "dfd", - StorageClassName: "", - Namespace: "ns", + SnapshotName: "", + Namespace: "ns", }, errChecker: NotNil, }, { // validate ns error args: &types.SnapshotBrowseArgs{ - SnapshotName: "dfd", - StorageClassName: "ddd", - Namespace: "", + SnapshotName: "dfd", + Namespace: "", }, errChecker: NotNil, }, @@ -240,97 +285,8 @@ func (s *CSITestSuite) TestSnapshotBrowseValidateArgs(c *C) { validateOps: f.validateOps, versionFetchOps: f.versionOps, } - _, err := stepper.ValidateArgs(ctx, tc.args) - c.Check(err, tc.errChecker) - } -} - -func (s *CSITestSuite) TestSnapshotBrowseFetchVS(c *C) { - ctx := context.Background() - snapshotter := &fakeSnapshotter{name: "snapshotter"} - groupversion := &metav1.GroupVersionForDiscovery{ - GroupVersion: "gv", - Version: "v", - } - type fields struct { - snapshotOps *mocks.MockSnapshotFetcher - } - for _, tc := range []struct { - args *types.SnapshotBrowseArgs - prepare func(f *fields) - errChecker Checker - snapChecker Checker - }{ - { - args: &types.SnapshotBrowseArgs{ - Namespace: "ns", - StorageClassName: "sc", - SnapshotName: "vs", - }, - prepare: func(f *fields) { - gomock.InOrder( - f.snapshotOps.EXPECT().NewSnapshotter().Return(snapshotter, nil), - f.snapshotOps.EXPECT().GetVolumeSnapshot(gomock.Any(), snapshotter, &types.FetchSnapshotArgs{ - Namespace: "ns", - SnapshotName: "vs", - }).Return(&snapv1.VolumeSnapshot{ - ObjectMeta: metav1.ObjectMeta{ - Name: "vs", - }, - }, nil), - ) - }, - errChecker: IsNil, - snapChecker: NotNil, - }, - { - args: &types.SnapshotBrowseArgs{ - Namespace: "ns", - StorageClassName: "sc", - SnapshotName: "vs", - }, - prepare: func(f *fields) { - gomock.InOrder( - f.snapshotOps.EXPECT().NewSnapshotter().Return(snapshotter, nil), - f.snapshotOps.EXPECT().GetVolumeSnapshot(gomock.Any(), snapshotter, &types.FetchSnapshotArgs{ - Namespace: "ns", - SnapshotName: "vs", - }).Return(nil, fmt.Errorf("error")), - ) - }, - errChecker: NotNil, - snapChecker: IsNil, - }, - { - args: &types.SnapshotBrowseArgs{ - Namespace: "ns", - StorageClassName: "sc", - SnapshotName: "vs", - }, - prepare: func(f *fields) { - gomock.InOrder( - f.snapshotOps.EXPECT().NewSnapshotter().Return(nil, fmt.Errorf("error")), - ) - }, - errChecker: NotNil, - snapChecker: IsNil, - }, - } { - ctrl := gomock.NewController(c) - defer ctrl.Finish() - f := fields{ - snapshotOps: mocks.NewMockSnapshotFetcher(ctrl), - } - if tc.prepare != nil { - tc.prepare(&f) - } - stepper := &snapshotBrowserSteps{ - snapshotFetchOps: f.snapshotOps, - SnapshotGroupVersion: groupversion, - } - snapshot, err := stepper.FetchVS(ctx, tc.args) + _, _, err := stepper.ValidateArgs(ctx, tc.args) c.Check(err, tc.errChecker) - c.Check(snapshot, tc.snapChecker) } } diff --git a/pkg/csi/snapshot_inspector_test.go b/pkg/csi/snapshot_inspector_test.go index f0db79d..22a0232 100644 --- a/pkg/csi/snapshot_inspector_test.go +++ b/pkg/csi/snapshot_inspector_test.go @@ -39,24 +39,10 @@ func (s *CSITestSuite) TestRunSnapshotBrowseHelper(c *C) { prepare: func(f *fields) { gomock.InOrder( f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return( - &sv1.StorageClass{}, nil, - ), - f.stepperOps.EXPECT().FetchVS(gomock.Any(), gomock.Any()).Return( - &snapv1.VolumeSnapshot{ - ObjectMeta: metav1.ObjectMeta{ - Name: "snap1", - Namespace: "ns", - }, - }, - nil, + &snapv1.VolumeSnapshot{}, &sv1.StorageClass{}, nil, ), f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), gomock.Any(), - &snapv1.VolumeSnapshot{ - ObjectMeta: metav1.ObjectMeta{ - Name: "snap1", - Namespace: "ns", - }, - }, &sv1.StorageClass{}, + &snapv1.VolumeSnapshot{}, &sv1.StorageClass{}, ).Return( &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -105,8 +91,7 @@ func (s *CSITestSuite) TestRunSnapshotBrowseHelper(c *C) { args: &types.SnapshotBrowseArgs{}, prepare: func(f *fields) { gomock.InOrder( - f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil), - f.stepperOps.EXPECT().FetchVS(gomock.Any(), gomock.Any()).Return(nil, nil), + 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()), @@ -121,8 +106,7 @@ func (s *CSITestSuite) TestRunSnapshotBrowseHelper(c *C) { args: &types.SnapshotBrowseArgs{}, prepare: func(f *fields) { gomock.InOrder( - f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil), - f.stepperOps.EXPECT().FetchVS(gomock.Any(), gomock.Any()).Return(nil, nil), + 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()), ) @@ -136,8 +120,7 @@ func (s *CSITestSuite) TestRunSnapshotBrowseHelper(c *C) { args: &types.SnapshotBrowseArgs{}, prepare: func(f *fields) { gomock.InOrder( - f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil), - f.stepperOps.EXPECT().FetchVS(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("snapshot error")), + 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()), ) }, @@ -150,7 +133,7 @@ func (s *CSITestSuite) TestRunSnapshotBrowseHelper(c *C) { args: &types.SnapshotBrowseArgs{}, prepare: func(f *fields) { gomock.InOrder( - f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("snapshot error")), + 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()), ) }, diff --git a/pkg/csi/types/csi_types.go b/pkg/csi/types/csi_types.go index 2af3433..2755582 100644 --- a/pkg/csi/types/csi_types.go +++ b/pkg/csi/types/csi_types.go @@ -136,16 +136,15 @@ func (p *PVCBrowseArgs) Validate() error { } type SnapshotBrowseArgs struct { - SnapshotName string - Namespace string - StorageClassName string - RunAsUser int64 - LocalPort int - ShowTree bool + SnapshotName string + Namespace string + RunAsUser int64 + LocalPort int + ShowTree bool } func (p *SnapshotBrowseArgs) Validate() error { - if p.SnapshotName == "" || p.Namespace == "" || p.StorageClassName == "" { + if p.SnapshotName == "" || p.Namespace == "" { return fmt.Errorf("Invalid SnapshotBrowseArgs (%v)", p) } return nil From e0f5dcfbd5bda620b29f5f58dbe0fbc2d3c57c63 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 26 Jul 2024 16:37:01 -0500 Subject: [PATCH 27/40] Adding --show-tree logic in snapshot_inspector.go --- pkg/csi/snapshot_inspector.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 442b579..d7fdb24 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -52,10 +52,6 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type dynCli: r.DynCli, }, } - if args.ShowTree { - fmt.Println("Show Tree works for VS!") - return nil - } return r.RunSnapshotBrowseHelper(ctx, args) } From d7b492c3f4e15c70b560438a905d2ffdbc5ae647 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 19:08:25 -0500 Subject: [PATCH 28/40] Updating mock objects for SnapshotBrowserStepper --- pkg/csi/mocks/mock_snapshot_browser_stepper.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/pkg/csi/mocks/mock_snapshot_browser_stepper.go b/pkg/csi/mocks/mock_snapshot_browser_stepper.go index c83a792..26b2349 100644 --- a/pkg/csi/mocks/mock_snapshot_browser_stepper.go +++ b/pkg/csi/mocks/mock_snapshot_browser_stepper.go @@ -81,21 +81,6 @@ func (mr *MockSnapshotBrowserStepperMockRecorder) ExecuteTreeCommand(arg0, arg1, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteTreeCommand", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).ExecuteTreeCommand), arg0, arg1, arg2) } -// FetchVS mocks base method. -func (m *MockSnapshotBrowserStepper) FetchVS(arg0 context.Context, arg1 *types.SnapshotBrowseArgs) (*v1.VolumeSnapshot, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "FetchVS", arg0, arg1) - ret0, _ := ret[0].(*v1.VolumeSnapshot) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// FetchVS indicates an expected call of FetchVS. -func (mr *MockSnapshotBrowserStepperMockRecorder) FetchVS(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchVS", reflect.TypeOf((*MockSnapshotBrowserStepper)(nil).FetchVS), arg0, arg1) -} - // PortForwardAPod mocks base method. func (m *MockSnapshotBrowserStepper) PortForwardAPod(arg0 context.Context, arg1 *v10.Pod, arg2 int) error { m.ctrl.T.Helper() From 938e6257e3276ddca616e133d965428a67b27aab Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 18:48:12 -0500 Subject: [PATCH 29/40] Adding --show-tree flag for browse snapshot & browse pvc commands --- pkg/csi/snapshot_inspector.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index d7fdb24..442b579 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -52,6 +52,10 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type dynCli: r.DynCli, }, } + if args.ShowTree { + fmt.Println("Show Tree works for VS!") + return nil + } return r.RunSnapshotBrowseHelper(ctx, args) } From 127e473a2c4ae378cb06af09597cfa5c4bde481a Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 18:54:10 -0500 Subject: [PATCH 30/40] Removing storage class flag --- pkg/csi/snapshot_inspector.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 442b579..d7fdb24 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -52,10 +52,6 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type dynCli: r.DynCli, }, } - if args.ShowTree { - fmt.Println("Show Tree works for VS!") - return nil - } return r.RunSnapshotBrowseHelper(ctx, args) } From e781e2eb2f10710caa710f038e71d15282c92351 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 18:48:12 -0500 Subject: [PATCH 31/40] Adding --show-tree flag for browse snapshot & browse pvc commands --- pkg/csi/snapshot_inspector.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index d7fdb24..442b579 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -52,6 +52,10 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type dynCli: r.DynCli, }, } + if args.ShowTree { + fmt.Println("Show Tree works for VS!") + return nil + } return r.RunSnapshotBrowseHelper(ctx, args) } From ffd10ad8e0d1b9a5c54b284bd1dc93168ea3eb24 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 26 Jul 2024 16:37:01 -0500 Subject: [PATCH 32/40] Adding --show-tree logic in snapshot_inspector.go --- pkg/csi/snapshot_inspector.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 442b579..d7fdb24 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -52,10 +52,6 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type dynCli: r.DynCli, }, } - if args.ShowTree { - fmt.Println("Show Tree works for VS!") - return nil - } return r.RunSnapshotBrowseHelper(ctx, args) } From 3ae24b3ae460805b962135a6ca16d56360db09a9 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 20:17:19 -0500 Subject: [PATCH 33/40] Passing showTree var as function argument --- cmd/rootCmd.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 539f392..0d4308d 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -108,6 +108,7 @@ var ( csiCheckVolumeSnapshotClass, csiCheckRunAsUser, browseLocalPort, + showTree, ) }, } @@ -122,6 +123,7 @@ var ( namespace, csiCheckRunAsUser, browseLocalPort, + showTree, ) }, } @@ -365,6 +367,7 @@ func CsiPvcBrowse(ctx context.Context, volumeSnapshotClass string, runAsUser int64, localPort int, + showTree bool, ) error { kubecli, err := kubestr.LoadKubeCli() if err != nil { @@ -399,6 +402,7 @@ func CsiSnapshotBrowse(ctx context.Context, namespace string, runAsUser int64, localPort int, + showTree bool, ) error { kubecli, err := kubestr.LoadKubeCli() if err != nil { From e79535af916fe94c1eb7669ef2ce86830c8757ec Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 20:22:39 -0500 Subject: [PATCH 34/40] Making --show-tree a persistent flag --- cmd/rootCmd.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 0d4308d..bc9e1b2 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -199,14 +199,13 @@ func init() { browseCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "Namespace of the resources provided in command.") 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") + browseCmd.PersistentFlags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of given PVC or VolumeSnapshot") browseCmd.AddCommand(browsePvcCmd) browsePvcCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)") _ = browsePvcCmd.MarkFlagRequired("volumesnapshotclass") - browsePvcCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of PVC") browseCmd.AddCommand(browseSnapshotCmd) - browseSnapshotCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of VolumeSnapshot") rootCmd.AddCommand(blockMountCmd) blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)") From df910e29a72ca5df8e1391c26eb57630463195ea Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 23:35:52 -0500 Subject: [PATCH 35/40] Removing ShowTree dummy condition --- pkg/csi/pvc_inspector.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/csi/pvc_inspector.go b/pkg/csi/pvc_inspector.go index 7ef5be7..89cad2a 100644 --- a/pkg/csi/pvc_inspector.go +++ b/pkg/csi/pvc_inspector.go @@ -57,10 +57,6 @@ func (r *PVCBrowseRunner) RunPVCBrowse(ctx context.Context, args *types.PVCBrows dynCli: r.DynCli, }, } - if args.ShowTree { - fmt.Println("Show Tree works for PVC!") - return nil - } return r.RunPVCBrowseHelper(ctx, args) } From 1f20ed3adc37497dc0dbf95039a1722d897d427a Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Thu, 1 Aug 2024 22:52:34 -0500 Subject: [PATCH 36/40] Removing duplicate browseSnapshotCmd --- cmd/rootCmd.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index ec61414..023b900 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -128,20 +128,6 @@ 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 From 0d60092c604e1125af79ea515e261f186164bc5e Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 18:48:12 -0500 Subject: [PATCH 37/40] Adding --show-tree flag for browse snapshot & browse pvc commands --- cmd/rootCmd.go | 2 ++ pkg/csi/pvc_inspector.go | 4 ++++ pkg/csi/snapshot_inspector.go | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 023b900..02c4f63 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -204,8 +204,10 @@ func init() { browseCmd.AddCommand(browsePvcCmd) browsePvcCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)") _ = browsePvcCmd.MarkFlagRequired("volumesnapshotclass") + browsePvcCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of PVC") browseCmd.AddCommand(browseSnapshotCmd) + browseSnapshotCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of VolumeSnapshot") rootCmd.AddCommand(blockMountCmd) blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)") diff --git a/pkg/csi/pvc_inspector.go b/pkg/csi/pvc_inspector.go index 89cad2a..7ef5be7 100644 --- a/pkg/csi/pvc_inspector.go +++ b/pkg/csi/pvc_inspector.go @@ -57,6 +57,10 @@ func (r *PVCBrowseRunner) RunPVCBrowse(ctx context.Context, args *types.PVCBrows dynCli: r.DynCli, }, } + if args.ShowTree { + fmt.Println("Show Tree works for PVC!") + return nil + } return r.RunPVCBrowseHelper(ctx, args) } diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index d7fdb24..442b579 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -52,6 +52,10 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type dynCli: r.DynCli, }, } + if args.ShowTree { + fmt.Println("Show Tree works for VS!") + return nil + } return r.RunSnapshotBrowseHelper(ctx, args) } From dddd9bf03ea921d2fc4c26cefaef6b3335924d87 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 20:22:39 -0500 Subject: [PATCH 38/40] Making --show-tree a persistent flag --- cmd/rootCmd.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 02c4f63..023b900 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -204,10 +204,8 @@ func init() { browseCmd.AddCommand(browsePvcCmd) browsePvcCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)") _ = browsePvcCmd.MarkFlagRequired("volumesnapshotclass") - browsePvcCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of PVC") browseCmd.AddCommand(browseSnapshotCmd) - browseSnapshotCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of VolumeSnapshot") rootCmd.AddCommand(blockMountCmd) blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)") From 5a440080ab221a37b8edf07ee604911146cae6c5 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 18:48:12 -0500 Subject: [PATCH 39/40] Adding --show-tree flag for browse snapshot & browse pvc commands --- cmd/rootCmd.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 023b900..02c4f63 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -204,8 +204,10 @@ func init() { browseCmd.AddCommand(browsePvcCmd) browsePvcCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)") _ = browsePvcCmd.MarkFlagRequired("volumesnapshotclass") + browsePvcCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of PVC") browseCmd.AddCommand(browseSnapshotCmd) + browseSnapshotCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of VolumeSnapshot") rootCmd.AddCommand(blockMountCmd) blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)") From 02ec051fb81a155d1cc6fce0321cf3e89228d41e Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 20:22:39 -0500 Subject: [PATCH 40/40] Making --show-tree a persistent flag --- cmd/rootCmd.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index 02c4f63..023b900 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -204,10 +204,8 @@ func init() { browseCmd.AddCommand(browsePvcCmd) browsePvcCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)") _ = browsePvcCmd.MarkFlagRequired("volumesnapshotclass") - browsePvcCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of PVC") browseCmd.AddCommand(browseSnapshotCmd) - browseSnapshotCmd.Flags().BoolVarP(&showTree, "show-tree", "t", false, "Prints the contents of VolumeSnapshot") rootCmd.AddCommand(blockMountCmd) blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)")