From 36882b59f48431fbb8f5a7e2400ec554795fd6f2 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 14:42:33 -0500 Subject: [PATCH 01/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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/59] 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 06814294ea8747198f612cc2f7b72242939df6fc Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 20:17:19 -0500 Subject: [PATCH 33/59] 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 c3b76da87f1fb8b87f5ca34100e0f0e522346eed Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 20:22:39 -0500 Subject: [PATCH 34/59] 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 f11801d8c289ab19ca9074bbdc1ae12d6b4818c3 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 23:35:52 -0500 Subject: [PATCH 35/59] 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 13f4b25477f54c8149188b298037eda7e8767f57 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 18:48:12 -0500 Subject: [PATCH 36/59] 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 bc9e1b2..9599072 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 023ec6cde7093ec6f165c4b08e1d557fb8aebe4e Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 20:22:39 -0500 Subject: [PATCH 37/59] 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 9599072..bc9e1b2 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 16bbac4c349cf463751a1f94630878f5541be772 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 24 Jul 2024 18:48:12 -0500 Subject: [PATCH 38/59] 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 bc9e1b2..9599072 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 c7faf68159190c4a755442f89f04f945fa38aa41 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 31 Jul 2024 20:22:39 -0500 Subject: [PATCH 39/59] 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 9599072..bc9e1b2 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 b13bc797209f6e5236907a9058582e8b89e368ff Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Thu, 1 Aug 2024 21:06:34 -0500 Subject: [PATCH 40/59] Adding "./kubestr browse snapshot" command (#277) * Adding the kubestr browse pvc command. Handling kubestr browse support for backward compatibility. * Adding browse snapshot command. Updating browse command to browse pvc command. * chore(deps): bump github/codeql-action in the github-actions group (#272) Bumps the github-actions group with 1 update: [github/codeql-action](https://github.com/github/codeql-action). Updates `github/codeql-action` from 3.25.12 to 3.25.13 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](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> * 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> * Removing unused snapshot function parameter in cleanup * Adding mock tests for SnapshotBrowserStepper * Adding Deprecated msg to the 'browse' command * Adding fake tests for snapshot_inspector.go * Renamed testcase CSITestSuite.TestCreateInspectorApplication to TestCreateInspectorApplicationForPVC * Adding snapshot_inspector_steps_test.go * Updating Deprecated msg for 'browse' command * Making namespace, runAsUser & localport flags persistent * Removing namespace, runAsUser & localport flags for browse snapshot because we made those persistent * Removing storage class flag * Update cmd/rootCmd.go Co-authored-by: Sirish Bathina --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sirish Bathina --- 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 1a1a2fd35a769e7b306fcc5312ab668e92df2d2c Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 2 Aug 2024 15:27:02 -0500 Subject: [PATCH 41/59] Adding --show-tree flag to both "./kubestr browse pvc" & "./kubestr browse snapshot" commands (#278) * Adding the kubestr browse pvc command. Handling kubestr browse support for backward compatibility. * Adding browse snapshot command. Updating browse command to browse pvc command. * chore(deps): bump github/codeql-action in the github-actions group (#272) Bumps the github-actions group with 1 update: [github/codeql-action](https://github.com/github/codeql-action). Updates `github/codeql-action` from 3.25.12 to 3.25.13 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](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> * 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> * Adding --show-tree flag for browse snapshot & browse pvc commands * Removing unused snapshot function parameter in cleanup * Adding KubeExecutor Exec helper function to execute tree command * Adding --show-tree logic in pvc_inspector.go * Adding --show-tree logic in snapshot_inspector.go * Printing out the tree structure for --show-tree * Updating mock tests for new code changes * Updating mount path in container args for creating a browse pod * Updating the CSITestSuite.TestCreateInspectorApplication for changes in the mount path * Adding Deprecated msg to the 'browse' command * Adding mock tests for SnapshotBrowserStepper * Adding fake tests for snapshot_inspector.go * Renamed testcase CSITestSuite.TestCreateInspectorApplication to TestCreateInspectorApplicationForPVC * Adding snapshot_inspector_steps_test.go * Updating mock tests for new code changes * Updating the mount paths in CSITestSuite.TestCreateInspectorApplicationForSnapshot * Updating Deprecated msg for 'browse' command * Making namespace, runAsUser & localport flags persistent * Removing namespace, runAsUser & localport flags for browse snapshot because we made those persistent * Adding --show-tree flag for browse snapshot & browse pvc commands * Updating namespace flag usage for better understanding * Removing storage class flag * Adding --show-tree logic in snapshot_inspector.go * Updating mock objects for SnapshotBrowserStepper * Adding --show-tree flag for browse snapshot & browse pvc commands * Removing storage class flag * Adding --show-tree flag for browse snapshot & browse pvc commands * Adding --show-tree logic in snapshot_inspector.go * Passing showTree var as function argument * Making --show-tree a persistent flag * Removing ShowTree dummy condition * Removing duplicate browseSnapshotCmd * Adding --show-tree flag for browse snapshot & browse pvc commands * Making --show-tree a persistent flag * Adding --show-tree flag for browse snapshot & browse pvc commands * Making --show-tree a persistent flag --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 f1c257803d53f702d38cf9992dbd5496a46163b2 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 2 Aug 2024 17:20:05 -0500 Subject: [PATCH 42/59] Removing dummy ShowTree arg test --- pkg/csi/pvc_inspector.go | 4 ---- pkg/csi/snapshot_inspector.go | 4 ---- 2 files changed, 8 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) } 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 d22fcc556bb81d8bb3a58f24895b93fe72d13a41 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 2 Aug 2024 15:27:02 -0500 Subject: [PATCH 43/59] Adding --show-tree flag to both "./kubestr browse pvc" & "./kubestr browse snapshot" commands (#278) * Adding the kubestr browse pvc command. Handling kubestr browse support for backward compatibility. * Adding browse snapshot command. Updating browse command to browse pvc command. * chore(deps): bump github/codeql-action in the github-actions group (#272) Bumps the github-actions group with 1 update: [github/codeql-action](https://github.com/github/codeql-action). Updates `github/codeql-action` from 3.25.12 to 3.25.13 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](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> * 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> * Adding --show-tree flag for browse snapshot & browse pvc commands * Removing unused snapshot function parameter in cleanup * Adding KubeExecutor Exec helper function to execute tree command * Adding --show-tree logic in pvc_inspector.go * Adding --show-tree logic in snapshot_inspector.go * Printing out the tree structure for --show-tree * Updating mock tests for new code changes * Updating mount path in container args for creating a browse pod * Updating the CSITestSuite.TestCreateInspectorApplication for changes in the mount path * Adding Deprecated msg to the 'browse' command * Adding mock tests for SnapshotBrowserStepper * Adding fake tests for snapshot_inspector.go * Renamed testcase CSITestSuite.TestCreateInspectorApplication to TestCreateInspectorApplicationForPVC * Adding snapshot_inspector_steps_test.go * Updating mock tests for new code changes * Updating the mount paths in CSITestSuite.TestCreateInspectorApplicationForSnapshot * Updating Deprecated msg for 'browse' command * Making namespace, runAsUser & localport flags persistent * Removing namespace, runAsUser & localport flags for browse snapshot because we made those persistent * Adding --show-tree flag for browse snapshot & browse pvc commands * Updating namespace flag usage for better understanding * Removing storage class flag * Adding --show-tree logic in snapshot_inspector.go * Updating mock objects for SnapshotBrowserStepper * Adding --show-tree flag for browse snapshot & browse pvc commands * Removing storage class flag * Adding --show-tree flag for browse snapshot & browse pvc commands * Adding --show-tree logic in snapshot_inspector.go * Passing showTree var as function argument * Making --show-tree a persistent flag * Removing ShowTree dummy condition * Removing duplicate browseSnapshotCmd * Adding --show-tree flag for browse snapshot & browse pvc commands * Making --show-tree a persistent flag * Adding --show-tree flag for browse snapshot & browse pvc commands * Making --show-tree a persistent flag --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 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 dcccafe8c5617b9c0944c0c5208e3b5c4a3d2740 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:23:35 -0700 Subject: [PATCH 44/59] chore(deps): bump the github-actions group across 1 directory with 2 updates (#282) Bumps the github-actions group with 2 updates in the / directory: [github/codeql-action](https://github.com/github/codeql-action) and [actions/upload-artifact](https://github.com/actions/upload-artifact). Updates `github/codeql-action` from 3.25.13 to 3.25.15 - [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/2d790406f505036ef40ecba973cc774a50395aac...afb54ba388a7dca6ecae48f608c4ff05ff4cc77a) Updates `actions/upload-artifact` from 4.3.4 to 4.3.5 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/0b2256b8c012f0828dc542b3febcab082c67f72b...89ef406dd8d7e03cfd12d9e0a4a378f454709029) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: actions/upload-artifact 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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 2f71fea..811131a 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -45,12 +45,12 @@ jobs: - # Upload the results to GitHub's code scanning dashboard. name: "Upload to results to dashboard" - uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13 + uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 with: sarif_file: results.sarif - name: "Upload analysis results as 'Job Artifact'" - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: SARIF file path: results.sarif From ba09ef9554d08bff0a660898df0dff7d19c88330 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:24:06 -0700 Subject: [PATCH 45/59] chore(deps): bump the docker group across 1 directory with 4 updates (#283) Bumps the docker group with 4 updates in the / directory: [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action), [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action), [docker/login-action](https://github.com/docker/login-action) and [docker/build-push-action](https://github.com/docker/build-push-action). Updates `docker/setup-qemu-action` from 3.1.0 to 3.2.0 - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/5927c834f5b4fdf503fca6f4c7eccda82949e1ee...49b3bc8e6bdd4a60e6116a5414239cba5943d3cf) Updates `docker/setup-buildx-action` from 3.4.0 to 3.6.1 - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/4fd812986e6c8c2a69e18311145f9371337f27d4...988b5a0280414f521da01fcc63a27aeeb4b104db) Updates `docker/login-action` from 3.2.0 to 3.3.0 - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/0d4c9c5ea7693da7b068278f7b52bda2a190a446...9780b0c442fbb1117ed29e0efdff1e18412f7567) Updates `docker/build-push-action` from 6.4.1 to 6.5.0 - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/1ca370b3a9802c92e886402e0dd88098a2533b12...5176d81f87c23d6fc96624dfdbcd9f3830bbe445) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: docker - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: docker - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: docker - 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 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 1c625e6..c11bf27 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -40,17 +40,17 @@ jobs: # This action can be useful if you want to add emulation # support with QEMU to be able to build against more platforms. - name: Set up QEMU - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee # v3.1.0 + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 # This action will create and boot a builder using # by default the docker-container builder driver. # Recommended for build multi-platform images, export cache, etc. - name: Set up Docker Buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 # v3.4.0 + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - name: Log into ${{ env.REGISTRY }} if: github.event_name != 'pull_request' - uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -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@1ca370b3a9802c92e886402e0dd88098a2533b12 # v6.4.1 + uses: docker/build-push-action@5176d81f87c23d6fc96624dfdbcd9f3830bbe445 # v6.5.0 with: context: . push: ${{ github.event_name != 'pull_request' }} From 00b738b46b6707488d109e77a90b2597fc0be510 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 Aug 2024 23:24:33 -0700 Subject: [PATCH 46/59] chore(deps): bump ossf/scorecard-action from 2.3.3 to 2.4.0 (#281) Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.3.3 to 2.4.0. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/dc50aa9510b46c811795eb24b2f1ba02a914e534...62b2cac7ed8198b15735ed49ab1e5cf35480ba46) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-type: direct:production update-type: version-update:semver-minor ... 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 811131a..78ff23e 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -37,7 +37,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 with: results_file: results.sarif results_format: sarif From 94bbfea86177c8aa83c0df9648ce5172decd169a Mon Sep 17 00:00:00 2001 From: saima sultana Date: Mon, 5 Aug 2024 14:09:36 +0530 Subject: [PATCH 47/59] K10-23320: Fix function definition for Create and CreateFromSource (#274) * fix function definition for Create and CreateFromSource * update go mod * update go mod * update fakesnapshotter * update create and createFromSource method * change snapshot and content meta struct type * sync kanister dependency * Renamed struct * Sync kanister dependency * Sync kanister dependency * Sync kanister dependency * Sync kanister dependency to merge master commit --- go.mod | 35 ++++++------ go.sum | 122 ++++++++++++++++++++-------------------- pkg/csi/csi_ops.go | 12 +++- pkg/csi/csi_ops_test.go | 10 ++-- 4 files changed, 95 insertions(+), 84 deletions(-) diff --git a/go.mod b/go.mod index 9413cbc..ca58e75 100644 --- a/go.mod +++ b/go.mod @@ -10,18 +10,18 @@ require ( github.com/briandowns/spinner v1.23.1 github.com/frankban/quicktest v1.14.6 github.com/golang/mock v1.6.0 - github.com/kanisterio/kanister v0.0.0-20240604102935-89345dc73d6e + github.com/kanisterio/kanister v0.0.0-20240805080534-ea6cb88542d6 github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.8.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c - k8s.io/api v0.29.5 - k8s.io/apimachinery v0.29.5 - k8s.io/client-go v0.29.5 + k8s.io/api v0.29.7 + k8s.io/apimachinery v0.29.7 + k8s.io/client-go v0.29.7 ) require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect @@ -55,6 +55,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kanisterio/errkit v0.0.2 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect @@ -82,26 +83,26 @@ require ( go.mongodb.org/mongo-driver v1.14.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/oauth2 v0.20.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.19.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.29.5 // indirect - k8s.io/cli-runtime v0.29.5 // indirect - k8s.io/code-generator v0.29.5 // indirect - k8s.io/component-base v0.29.5 // indirect + k8s.io/apiextensions-apiserver v0.29.7 // indirect + k8s.io/cli-runtime v0.29.7 // indirect + k8s.io/code-generator v0.29.7 // indirect + k8s.io/component-base v0.29.7 // indirect k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect - k8s.io/kubectl v0.29.5 // indirect + k8s.io/kubectl v0.29.7 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect diff --git a/go.sum b/go.sum index 9a91c81..475b932 100644 --- a/go.sum +++ b/go.sum @@ -6,26 +6,26 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= -cloud.google.com/go v0.114.0 h1:OIPFAdfrFDFO2ve2U7r/H5SwSbBzEdrBdE7xkgwc+kY= -cloud.google.com/go/auth v0.4.2 h1:sb0eyLkhRtpq5jA+a8KWw0W70YcdVca7KJ8TM0AFYDg= -cloud.google.com/go/auth v0.4.2/go.mod h1:Kqvlz1cf1sNA0D+sYJnkPQOP+JMHkuHeIgVmCRtZOLc= -cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= -cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= +cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= +cloud.google.com/go/auth v0.7.3 h1:98Vr+5jMaCZ5NZk6e/uBgf60phTk/XN84r8QEWB9yjY= +cloud.google.com/go/auth v0.7.3/go.mod h1:HJtWUx1P5eqjy/f6Iq5KeytNpbAcGolPhOgyop2LlzA= +cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI= +cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 h1:FDif4R1+UUR+00q6wquyX90K7A8dN+R5E8GEadoP7sU= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 h1:GJHeeA2N7xrG3q30L2UXDyuWRzDM900/65j70wcM4Ww= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 h1:wxQx2Bt4xzPIKvW59WQf1tJNx/ZZKPfN+EhPX3Z6CYY= @@ -76,8 +76,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.53.15 h1:FtZmkg7xM8RfP2oY6p7xdKBYrRgkITk9yve2QV7N938= -github.com/aws/aws-sdk-go v1.53.15/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/briandowns/spinner v1.23.1 h1:t5fDPmScwUjozhDj4FA46p5acZWIPXYE30qW2Ptu650= github.com/briandowns/spinner v1.23.1/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -209,8 +209,8 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f h1:f00RU+zOX+B3rLAmMMkzHUF2h1z4DeYR9tTCvEq2REY= github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -220,8 +220,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfF github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg= -github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI= +github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= +github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -251,8 +251,10 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kanisterio/kanister v0.0.0-20240604102935-89345dc73d6e h1:p7C9Pu026cpcHtrLbUnCPql6BHsFIyi4dZIteVyN9pg= -github.com/kanisterio/kanister v0.0.0-20240604102935-89345dc73d6e/go.mod h1:WEci2PQj6hnniCJUNrpuL6vFWiFDgpgLoW5bchmESlE= +github.com/kanisterio/errkit v0.0.2 h1:3v3HGz9lHIbZR6Jr9qIJpRjaqUX0rsJSLMEQGsMHiUk= +github.com/kanisterio/errkit v0.0.2/go.mod h1:ViQ6kPJ2gTJDEvRytmwde7pzG9/sndObF9BPZoEZixc= +github.com/kanisterio/kanister v0.0.0-20240805080534-ea6cb88542d6 h1:64U8pfRw79OP59IH7Bi6s95ehcQYcktYbZrxMRoEMkU= +github.com/kanisterio/kanister v0.0.0-20240805080534-ea6cb88542d6/go.mod h1:huHR+Z+XUuDqqvSeyIKBUm2vJ417Yw4o7DVWdq8ROxY= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -393,8 +395,8 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -438,15 +440,15 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= -golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -481,18 +483,18 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -525,8 +527,8 @@ golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -536,8 +538,8 @@ google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.182.0 h1:if5fPvudRQ78GeRx3RayIoiuV7modtErPIZC/T2bIvE= -google.golang.org/api v0.182.0/go.mod h1:cGhjy4caqA5yXRzEhkHI8Y9mfyC2VLTlER2l08xaqtM= +google.golang.org/api v0.190.0 h1:ASM+IhLY1zljNdLu19W1jTmU6A+gMk6M46Wlur61s+Q= +google.golang.org/api v0.190.0/go.mod h1:QIr6I9iedBLnfqoD6L6Vze1UvS5Hzj5r2aUBOaZnLHo= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -553,17 +555,17 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e h1:Elxv5MwEkCI9f5SkoL6afed6NTdxaGoAo39eANBwHL8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf h1:OqdXDEakZCVtDiZTjcxfwbHPCT11ycCEsTKesBVKvyY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -574,8 +576,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -601,23 +603,23 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= -k8s.io/api v0.29.5 h1:levS+umUigHCfI3riD36pMY1vQEbrzh4r1ivVWAhHaI= -k8s.io/api v0.29.5/go.mod h1:7b18TtPcJzdjk7w5zWyIHgoAtpGeRvGGASxlS7UZXdQ= -k8s.io/apiextensions-apiserver v0.29.5 h1:njDywexhE6n+1NEl3A4axT0TMQHREnndrk3/ztdWcNE= -k8s.io/apiextensions-apiserver v0.29.5/go.mod h1:pfIvij+MH9a8NQKtW7MD4EFnzvUjJ1ZQsDL8wuP8fnc= +k8s.io/api v0.29.7 h1:Q2/thp7YYESgy0MGzxT9RvA/6doLJHBXSFH8GGLxSbc= +k8s.io/api v0.29.7/go.mod h1:mPimdbyuIjwoLtBEVIGVUYb4BKOE+44XHt/n4IqKsLA= +k8s.io/apiextensions-apiserver v0.29.7 h1:X62u7vUGfwW5rYJB5jkZDr0uV2XSyEHJRdxnfD5PaLs= +k8s.io/apiextensions-apiserver v0.29.7/go.mod h1:JzBXxlZKKdtEYGr4yiN+s0eXheCTYgKDay8JXPfSGoQ= k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/apimachinery v0.29.5 h1:Hofa2BmPfpoT+IyDTlcPdCHSnHtEQMoJYGVoQpRTfv4= -k8s.io/apimachinery v0.29.5/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y= -k8s.io/cli-runtime v0.29.5 h1:gnDrFEA4Xr8lAdc5U4tVfU7RGAmM/yvVvW9f0ao7g2s= -k8s.io/cli-runtime v0.29.5/go.mod h1:6KuWJyLIpbh2II874fA1SnmGHvZCitsHRR1XDowsMC0= +k8s.io/apimachinery v0.29.7 h1:ICXzya58Q7hyEEfnTrbmdfX1n1schSepX2KUfC2/ykc= +k8s.io/apimachinery v0.29.7/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y= +k8s.io/cli-runtime v0.29.7 h1:6IxyxaIm3N31+PKXb1K7Tpf+100mm9hd9HMMYWMH2QE= +k8s.io/cli-runtime v0.29.7/go.mod h1:0pcclC4k3rkzYNAvw3zeiPNtg8Buv0orK+5MuhEKFBU= k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU= -k8s.io/client-go v0.29.5 h1:nlASXmPQy190qTteaVP31g3c/wi2kycznkTP7Sv1zPc= -k8s.io/client-go v0.29.5/go.mod h1:aY5CnqUUvXYccJhm47XHoPcRyX6vouHdIBHaKZGTbK4= +k8s.io/client-go v0.29.7 h1:vTtiFrGBKlcBhxaeZC4eDrqui1e108nsTyue/KU63IY= +k8s.io/client-go v0.29.7/go.mod h1:69BvVqdRozgR/9TP45u/oO0tfrdbP+I8RqrcCJQshzg= k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= -k8s.io/code-generator v0.29.5 h1:WqSdBPVV1B3jsPnKtPS39U02zj6Q7+FsjhAj1EPBJec= -k8s.io/code-generator v0.29.5/go.mod h1:7TYnI0dYItL2cKuhhgPSuF3WED9uMdELgbVXFfn/joE= -k8s.io/component-base v0.29.5 h1:Ptj8AzG+p8c2a839XriHwxakDpZH9uvIgYz+o1agjg8= -k8s.io/component-base v0.29.5/go.mod h1:9nBUoPxW/yimISIgAG7sJDrUGJlu7t8HnDafIrOdU8Q= +k8s.io/code-generator v0.29.7 h1:NEwmKOJVNObCh3upBLEojL1QuJMzGplOTYZnee4h0TY= +k8s.io/code-generator v0.29.7/go.mod h1:7TYnI0dYItL2cKuhhgPSuF3WED9uMdELgbVXFfn/joE= +k8s.io/component-base v0.29.7 h1:zXLJvZjvvDWdYmZCwZYk95E1Fd2oRXUz71mQukkRk5I= +k8s.io/component-base v0.29.7/go.mod h1:ddLTpIrjazaRI1EG83M41GNcYEAdskuQmx4JOOSXCOg= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= @@ -629,8 +631,8 @@ k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/kubectl v0.29.5 h1:Y41B5AhRKMfDVKgFkS5vHKX43aHVch6+QIszCborERc= -k8s.io/kubectl v0.29.5/go.mod h1:/bxZpurdrKZFz/sa7jlLy1S8IxSiWhJCoA+DhLp7/T4= +k8s.io/kubectl v0.29.7 h1:D+Jheug9M++zlt67cROZgxaIjrDdLqp9jkW/EYrXAoM= +k8s.io/kubectl v0.29.7/go.mod h1:VOEJkcfKTO/X8xSSB6d2JXP/Qni6xtjuI3CUP52T9bM= k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= diff --git a/pkg/csi/csi_ops.go b/pkg/csi/csi_ops.go index cbca361..d3ac4ee 100644 --- a/pkg/csi/csi_ops.go +++ b/pkg/csi/csi_ops.go @@ -395,7 +395,11 @@ func (c *snapshotCreate) CreateSnapshot(ctx context.Context, snapshotter kansnap if err := args.Validate(); err != nil { return nil, err } - err := snapshotter.Create(ctx, args.SnapshotName, args.Namespace, args.PVCName, &args.VolumeSnapshotClass, true, nil) + snapshotMeta := kansnapshot.ObjectMeta{ + Name: args.SnapshotName, + Namespace: args.Namespace, + } + err := snapshotter.Create(ctx, args.PVCName, &args.VolumeSnapshotClass, true, snapshotMeta) if err != nil { return nil, errors.Wrapf(err, "CSI Driver failed to create snapshot for PVC (%s) in Namespace (%s)", args.PVCName, args.Namespace) } @@ -446,7 +450,11 @@ func (c *snapshotCreate) CreateFromSourceCheck(ctx context.Context, snapshotter Driver: snapSrc.Driver, VolumeSnapshotClassName: targetSnapClassName, } - err = snapshotter.CreateFromSource(ctx, src, snapshotCFSCloneName, args.Namespace, true, nil) + snapshotMeta := kansnapshot.ObjectMeta{ + Name: snapshotCFSCloneName, + Namespace: args.Namespace, + } + err = snapshotter.CreateFromSource(ctx, src, true, snapshotMeta, kansnapshot.ObjectMeta{}) if err != nil { return errors.Wrapf(err, "Failed to clone snapshot from source (%s)", snapshotCFSCloneName) } diff --git a/pkg/csi/csi_ops_test.go b/pkg/csi/csi_ops_test.go index 390e707..72f27a5 100644 --- a/pkg/csi/csi_ops_test.go +++ b/pkg/csi/csi_ops_test.go @@ -20,13 +20,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" discoveryfake "k8s.io/client-go/discovery/fake" "k8s.io/client-go/dynamic" fakedynamic "k8s.io/client-go/dynamic/fake" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" k8stesting "k8s.io/client-go/testing" - "k8s.io/apimachinery/pkg/runtime/schema" ) func (s *CSITestSuite) TestGetDriverNameFromUVSC(c *C) { @@ -996,7 +996,7 @@ func (f *fakeSnapshotter) GetVolumeSnapshotClass(ctx context.Context, annotation func (f *fakeSnapshotter) CloneVolumeSnapshotClass(ctx context.Context, sourceClassName, targetClassName, newDeletionPolicy string, excludeAnnotations []string) error { return f.cvsErr } -func (f *fakeSnapshotter) Create(ctx context.Context, name, namespace, pvcName string, snapshotClass *string, waitForReady bool, labels map[string]string) error { +func (f *fakeSnapshotter) Create(ctx context.Context, pvcName string, snapshotClass *string, waitForReady bool, snapshotMeta kansnapshot.ObjectMeta) error { return f.createErr } func (f *fakeSnapshotter) Get(ctx context.Context, name, namespace string) (*snapv1.VolumeSnapshot, error) { @@ -1006,16 +1006,16 @@ func (f *fakeSnapshotter) Delete(ctx context.Context, name, namespace string) (* return nil, nil } func (f *fakeSnapshotter) DeleteContent(ctx context.Context, name string) error { return nil } -func (f *fakeSnapshotter) Clone(ctx context.Context, name, namespace, cloneName, cloneNamespace string, waitForReady bool, labels map[string]string) error { +func (f *fakeSnapshotter) Clone(ctx context.Context, name, namespace string, waitForReady bool, snapshotMeta, contentMeta kansnapshot.ObjectMeta) error { return nil } func (f *fakeSnapshotter) GetSource(ctx context.Context, snapshotName, namespace string) (*kansnapshot.Source, error) { return f.gsSrc, f.gsErr } -func (f *fakeSnapshotter) CreateFromSource(ctx context.Context, source *kansnapshot.Source, snapshotName, namespace string, waitForReady bool, labels map[string]string) error { +func (f *fakeSnapshotter) CreateFromSource(ctx context.Context, source *kansnapshot.Source, waitForReady bool, snapshotMeta, contentMeta kansnapshot.ObjectMeta) error { return f.cfsErr } -func (f *fakeSnapshotter) CreateContentFromSource(ctx context.Context, source *kansnapshot.Source, contentName, snapshotName, namespace, deletionPolicy string) error { +func (f *fakeSnapshotter) CreateContentFromSource(ctx context.Context, source *kansnapshot.Source, snapshotName, snapshotNs, deletionPolicy string, contentMeta kansnapshot.ObjectMeta) error { return nil } func (f *fakeSnapshotter) WaitOnReadyToUse(ctx context.Context, snapshotName, namespace string) error { From acb052e28102cb8249a05f39222ec1bc4298d9a8 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 7 Aug 2024 15:06:53 -0500 Subject: [PATCH 48/59] Updating CreatePodArgs to consume PVC args in []string{} format instead of string (#285) * Updating the PVCName, MountPath, DevicePath signature in CreatePodArgs * Refactoring PVCName, MountPath, DevicePath variables into a single PVCMap with path definitions * Removing unused PVCName variable from CreatePodArgs * Updating DevicePath and MountPath error messages --- pkg/block/block_mount.go | 7 +- pkg/block/block_mount_test.go | 7 +- pkg/csi/csi_ops.go | 39 ++++---- pkg/csi/csi_ops_test.go | 113 ++++++++++++++--------- pkg/csi/pvc_inspector.go | 14 ++- pkg/csi/pvc_inspector_steps_test.go | 14 ++- pkg/csi/snapshot_inspector.go | 14 ++- pkg/csi/snapshot_inspector_steps_test.go | 14 ++- pkg/csi/snapshot_restore.go | 16 +++- pkg/csi/snapshot_restore_steps_test.go | 35 +++++-- pkg/csi/types/csi_types.go | 24 +++-- 11 files changed, 194 insertions(+), 103 deletions(-) diff --git a/pkg/block/block_mount.go b/pkg/block/block_mount.go index b126d9b..e4c2070 100644 --- a/pkg/block/block_mount.go +++ b/pkg/block/block_mount.go @@ -133,13 +133,16 @@ func (b *blockMountChecker) Mount(ctx context.Context) (*BlockMountCheckerResult tB = time.Now() _, err = b.appCreator.CreatePod(ctx, &types.CreatePodArgs{ Name: b.podName, - PVCName: b.pvcName, Namespace: b.args.Namespace, RunAsUser: b.args.RunAsUser, ContainerImage: b.args.ContainerImage, Command: []string{"/bin/sh"}, ContainerArgs: []string{"-c", "tail -f /dev/null"}, - DevicePath: "/mnt/block", + PVCMap: map[string]types.VolumePath{ + b.pvcName: { + DevicePath: "/mnt/block", + }, + }, }) if err != nil { fmt.Printf(" -> Failed to create Pod (%v)\n", err) diff --git a/pkg/block/block_mount_test.go b/pkg/block/block_mount_test.go index d69847b..d010c7e 100644 --- a/pkg/block/block_mount_test.go +++ b/pkg/block/block_mount_test.go @@ -299,13 +299,16 @@ func TestBlockMountCheckerMount(t *testing.T) { createPodArgs := func(b *blockMountChecker) *types.CreatePodArgs { return &types.CreatePodArgs{ Name: b.podName, - PVCName: b.pvcName, Namespace: b.args.Namespace, RunAsUser: b.args.RunAsUser, ContainerImage: b.args.ContainerImage, Command: []string{"/bin/sh"}, ContainerArgs: []string{"-c", "tail -f /dev/null"}, - DevicePath: "/mnt/block", + PVCMap: map[string]types.VolumePath{ + b.pvcName: { + DevicePath: "/mnt/block", + }, + }, } } createPod := func(b *blockMountChecker) *v1.Pod { diff --git a/pkg/csi/csi_ops.go b/pkg/csi/csi_ops.go index d3ac4ee..37f23b7 100644 --- a/pkg/csi/csi_ops.go +++ b/pkg/csi/csi_ops.go @@ -208,27 +208,28 @@ func (c *applicationCreate) CreatePod(ctx context.Context, args *types.CreatePod Command: args.Command, Args: args.ContainerArgs, }}, - Volumes: []v1.Volume{{ - Name: volumeNameInPod, - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ - ClaimName: args.PVCName, - }, - }}, - }, }, } - - if args.MountPath != "" { - pod.Spec.Containers[0].VolumeMounts = []v1.VolumeMount{{ - Name: volumeNameInPod, - MountPath: args.MountPath, - }} - } else { // args.DevicePath - pod.Spec.Containers[0].VolumeDevices = []v1.VolumeDevice{{ - Name: volumeNameInPod, - DevicePath: args.DevicePath, - }} + for pvcName, path := range args.PVCMap { + pod.Spec.Volumes = append(pod.Spec.Volumes, v1.Volume{ + Name: fmt.Sprintf("%s-%s", volumeNameInPod, pvcName), + VolumeSource: v1.VolumeSource{ + PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + ClaimName: pvcName, + }, + }, + }) + if len(path.MountPath) != 0 { + pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, v1.VolumeMount{ + Name: fmt.Sprintf("%s-%s", volumeNameInPod, pvcName), + MountPath: path.MountPath, + }) + } else { + pod.Spec.Containers[0].VolumeDevices = append(pod.Spec.Containers[0].VolumeDevices, v1.VolumeDevice{ + Name: fmt.Sprintf("%s-%s", volumeNameInPod, pvcName), + DevicePath: path.DevicePath, + }) + } } if args.RunAsUser > 0 { diff --git a/pkg/csi/csi_ops_test.go b/pkg/csi/csi_ops_test.go index 72f27a5..34a0f98 100644 --- a/pkg/csi/csi_ops_test.go +++ b/pkg/csi/csi_ops_test.go @@ -517,12 +517,15 @@ func (s *CSITestSuite) TestCreatePod(c *C) { cli: fake.NewSimpleClientset(), args: &types.CreatePodArgs{ GenerateName: "name", - PVCName: "pvcname", Namespace: "ns", Command: []string{"somecommand"}, RunAsUser: 1000, ContainerImage: "containerimage", - MountPath: "/mnt/fs", + PVCMap: map[string]types.VolumePath{ + "pvcname": { + MountPath: "/mnt/fs", + }, + }, }, errChecker: IsNil, podChecker: NotNil, @@ -532,10 +535,13 @@ func (s *CSITestSuite) TestCreatePod(c *C) { cli: fake.NewSimpleClientset(), args: &types.CreatePodArgs{ GenerateName: "name", - PVCName: "pvcname", Namespace: "ns", Command: []string{"somecommand"}, - MountPath: "/mnt/fs", + PVCMap: map[string]types.VolumePath{ + "pvcname": { + MountPath: "/mnt/fs", + }, + }, }, failCreates: true, errChecker: NotNil, @@ -546,10 +552,13 @@ func (s *CSITestSuite) TestCreatePod(c *C) { cli: fake.NewSimpleClientset(), args: &types.CreatePodArgs{ GenerateName: "", - PVCName: "pvcname", Namespace: "ns", Command: []string{"somecommand"}, - MountPath: "/mnt/fs", + PVCMap: map[string]types.VolumePath{ + "pvcname": { + MountPath: "/mnt/fs", + }, + }, }, errChecker: NotNil, podChecker: IsNil, @@ -560,10 +569,13 @@ func (s *CSITestSuite) TestCreatePod(c *C) { args: &types.CreatePodArgs{ GenerateName: "name", Name: "name", - PVCName: "pvcname", Namespace: "ns", Command: []string{"somecommand"}, - MountPath: "/mnt/fs", + PVCMap: map[string]types.VolumePath{ + "pvcname": { + MountPath: "/mnt/fs", + }, + }, }, errChecker: NotNil, podChecker: IsNil, @@ -573,9 +585,9 @@ func (s *CSITestSuite) TestCreatePod(c *C) { cli: fake.NewSimpleClientset(), args: &types.CreatePodArgs{ GenerateName: "name", - PVCName: "", Namespace: "ns", Command: []string{"somecommand"}, + PVCMap: map[string]types.VolumePath{"pvcname": {}}, }, errChecker: NotNil, podChecker: IsNil, @@ -585,11 +597,14 @@ func (s *CSITestSuite) TestCreatePod(c *C) { cli: fake.NewSimpleClientset(), args: &types.CreatePodArgs{ GenerateName: "name", - PVCName: "", Namespace: "ns", Command: []string{"somecommand"}, - MountPath: "/mnt/fs", - DevicePath: "/mnt/dev", + PVCMap: map[string]types.VolumePath{ + "pvcname": { + MountPath: "/mnt/fs", + DevicePath: "/mnt/dev", + }, + }, }, errChecker: NotNil, podChecker: IsNil, @@ -599,10 +614,9 @@ func (s *CSITestSuite) TestCreatePod(c *C) { cli: fake.NewSimpleClientset(), args: &types.CreatePodArgs{ GenerateName: "name", - PVCName: "", Namespace: "ns", Command: []string{"somecommand"}, - MountPath: "/mnt/fs", + PVCMap: map[string]types.VolumePath{"": {MountPath: "/mnt/fs"}}, }, errChecker: NotNil, podChecker: IsNil, @@ -612,10 +626,13 @@ func (s *CSITestSuite) TestCreatePod(c *C) { cli: fake.NewSimpleClientset(), args: &types.CreatePodArgs{ GenerateName: "name", - PVCName: "pvcname", Namespace: "", Command: []string{"somecommand"}, - MountPath: "/mnt/fs", + PVCMap: map[string]types.VolumePath{ + "pvcname": { + MountPath: "/mnt/fs", + }, + }, }, errChecker: NotNil, podChecker: IsNil, @@ -625,10 +642,13 @@ func (s *CSITestSuite) TestCreatePod(c *C) { cli: fake.NewSimpleClientset(), args: &types.CreatePodArgs{ GenerateName: "name", - PVCName: "pvcname", Namespace: "ns", Command: []string{"somecommand"}, - MountPath: "/mnt/fs", + PVCMap: map[string]types.VolumePath{ + "pvcname": { + MountPath: "/mnt/fs", + }, + }, }, errChecker: IsNil, podChecker: NotNil, @@ -637,11 +657,14 @@ func (s *CSITestSuite) TestCreatePod(c *C) { description: "ns namespace pod is created (Name/DevicePath)", cli: fake.NewSimpleClientset(), args: &types.CreatePodArgs{ - Name: "name", - PVCName: "pvcname", - Namespace: "ns", - Command: []string{"somecommand"}, - DevicePath: "/mnt/dev", + Name: "name", + Namespace: "ns", + Command: []string{"somecommand"}, + PVCMap: map[string]types.VolumePath{ + "pvcname": { + DevicePath: "/mnt/dev", + }, + }, }, errChecker: IsNil, podChecker: NotNil, @@ -678,27 +701,31 @@ func (s *CSITestSuite) TestCreatePod(c *C) { c.Assert(len(pod.Spec.Containers), Equals, 1) c.Assert(pod.Spec.Containers[0].Command, DeepEquals, tc.args.Command) c.Assert(pod.Spec.Containers[0].Args, DeepEquals, tc.args.ContainerArgs) - if tc.args.MountPath != "" { - c.Assert(pod.Spec.Containers[0].VolumeMounts, DeepEquals, []v1.VolumeMount{{ - Name: "persistent-storage", - MountPath: tc.args.MountPath, - }}) - c.Assert(pod.Spec.Containers[0].VolumeDevices, IsNil) - } else { - c.Assert(pod.Spec.Containers[0].VolumeDevices, DeepEquals, []v1.VolumeDevice{{ - Name: "persistent-storage", - DevicePath: tc.args.DevicePath, - }}) - c.Assert(pod.Spec.Containers[0].VolumeMounts, IsNil) - } - c.Assert(pod.Spec.Volumes, DeepEquals, []v1.Volume{{ - Name: "persistent-storage", - VolumeSource: v1.VolumeSource{ - PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ - ClaimName: tc.args.PVCName, + index := 0 + for pvcName, path := range tc.args.PVCMap { + if len(path.MountPath) != 0 { + c.Assert(pod.Spec.Containers[0].VolumeMounts[index], DeepEquals, v1.VolumeMount{ + Name: fmt.Sprintf("persistent-storage-%s", pvcName), + MountPath: path.MountPath, + }) + c.Assert(pod.Spec.Containers[0].VolumeDevices, IsNil) + } else { + c.Assert(pod.Spec.Containers[0].VolumeDevices[index], DeepEquals, v1.VolumeDevice{ + Name: fmt.Sprintf("persistent-storage-%s", pvcName), + DevicePath: path.DevicePath, + }) + c.Assert(pod.Spec.Containers[0].VolumeMounts, IsNil) + } + c.Assert(pod.Spec.Volumes[index], DeepEquals, v1.Volume{ + Name: fmt.Sprintf("persistent-storage-%s", pvcName), + VolumeSource: v1.VolumeSource{ + PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ + ClaimName: pvcName, + }, }, - }}, - }) + }) + index++ + } if tc.args.ContainerImage == "" { c.Assert(pod.Spec.Containers[0].Image, Equals, common.DefaultPodImage) } else { diff --git a/pkg/csi/pvc_inspector.go b/pkg/csi/pvc_inspector.go index 7ef5be7..c2409b7 100644 --- a/pkg/csi/pvc_inspector.go +++ b/pkg/csi/pvc_inspector.go @@ -209,23 +209,29 @@ func (p *pvcBrowserSteps) CreateInspectorApplication(ctx context.Context, args * } podArgs := &types.CreatePodArgs{ GenerateName: clonedPodGenerateName, - PVCName: pvc.Name, Namespace: args.Namespace, RunAsUser: args.RunAsUser, ContainerImage: "filebrowser/filebrowser:v2", ContainerArgs: []string{"--noauth", "-r", "/pvc-data"}, - MountPath: "/pvc-data", + PVCMap: map[string]types.VolumePath{ + pvc.Name: { + 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", + PVCMap: map[string]types.VolumePath{ + pvc.Name: { + MountPath: "/pvc-data", + }, + }, } } pod, err := p.createAppOps.CreatePod(ctx, podArgs) diff --git a/pkg/csi/pvc_inspector_steps_test.go b/pkg/csi/pvc_inspector_steps_test.go index 25d6a62..9c9a471 100644 --- a/pkg/csi/pvc_inspector_steps_test.go +++ b/pkg/csi/pvc_inspector_steps_test.go @@ -539,12 +539,15 @@ func (s *CSITestSuite) TestCreateInspectorApplicationForPVC(c *C) { }, nil), f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ GenerateName: clonedPodGenerateName, - PVCName: "pvc1", Namespace: "ns", ContainerArgs: []string{"--noauth", "-r", "/pvc-data"}, - MountPath: "/pvc-data", RunAsUser: 100, ContainerImage: "filebrowser/filebrowser:v2", + PVCMap: map[string]types.VolumePath{ + "pvc1": { + MountPath: "/pvc-data", + }, + }, }).Return(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod1", @@ -594,12 +597,15 @@ func (s *CSITestSuite) TestCreateInspectorApplicationForPVC(c *C) { }, nil), f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ GenerateName: clonedPodGenerateName, - PVCName: "pvc1", Namespace: "ns", ContainerArgs: []string{"--noauth", "-r", "/pvc-data"}, - MountPath: "/pvc-data", RunAsUser: 100, ContainerImage: "filebrowser/filebrowser:v2", + PVCMap: map[string]types.VolumePath{ + "pvc1": { + MountPath: "/pvc-data", + }, + }, }).Return(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod1", diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 442b579..f8fbd02 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -177,23 +177,29 @@ func (s *snapshotBrowserSteps) CreateInspectorApplication(ctx context.Context, a } podArgs := &types.CreatePodArgs{ GenerateName: clonedPodGenerateName, - PVCName: pvc.Name, Namespace: args.Namespace, RunAsUser: args.RunAsUser, ContainerImage: "filebrowser/filebrowser:v2", ContainerArgs: []string{"--noauth", "-r", "/snapshot-data"}, - MountPath: "/snapshot-data", + PVCMap: map[string]types.VolumePath{ + pvc.Name: { + 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", + PVCMap: map[string]types.VolumePath{ + pvc.Name: { + MountPath: "/snapshot-data", + }, + }, } } pod, err := s.createAppOps.CreatePod(ctx, podArgs) diff --git a/pkg/csi/snapshot_inspector_steps_test.go b/pkg/csi/snapshot_inspector_steps_test.go index 423c8ac..8f3cc66 100644 --- a/pkg/csi/snapshot_inspector_steps_test.go +++ b/pkg/csi/snapshot_inspector_steps_test.go @@ -343,12 +343,15 @@ func (s *CSITestSuite) TestCreateInspectorApplicationForSnapshot(c *C) { }, nil), f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ GenerateName: clonedPodGenerateName, - PVCName: "pvc", Namespace: "ns", ContainerArgs: []string{"--noauth", "-r", "/snapshot-data"}, - MountPath: "/snapshot-data", RunAsUser: 100, ContainerImage: "filebrowser/filebrowser:v2", + PVCMap: map[string]types.VolumePath{ + "pvc": { + MountPath: "/snapshot-data", + }, + }, }).Return(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod", @@ -398,12 +401,15 @@ func (s *CSITestSuite) TestCreateInspectorApplicationForSnapshot(c *C) { }, nil), f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ GenerateName: clonedPodGenerateName, - PVCName: "pvc", Namespace: "ns", ContainerArgs: []string{"--noauth", "-r", "/snapshot-data"}, - MountPath: "/snapshot-data", RunAsUser: 100, ContainerImage: "filebrowser/filebrowser:v2", + PVCMap: map[string]types.VolumePath{ + "pvc": { + MountPath: "/snapshot-data", + }, + }, }).Return(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod", diff --git a/pkg/csi/snapshot_restore.go b/pkg/csi/snapshot_restore.go index e2adcb9..1751546 100644 --- a/pkg/csi/snapshot_restore.go +++ b/pkg/csi/snapshot_restore.go @@ -48,7 +48,7 @@ func (r *SnapshotRestoreRunner) RunSnapshotRestore(ctx context.Context, args *ty kubeCli: r.KubeCli, }, createAppOps: &applicationCreate{ - kubeCli: r.KubeCli, + kubeCli: r.KubeCli, k8sObjectReadyTimeout: args.K8sObjectReadyTimeout, }, dataValidatorOps: &validateData{ @@ -179,13 +179,16 @@ func (s *snapshotRestoreSteps) CreateApplication(ctx context.Context, args *type } podArgs := &types.CreatePodArgs{ GenerateName: originalPodGenerateName, - PVCName: pvc.Name, Namespace: args.Namespace, RunAsUser: args.RunAsUser, ContainerImage: args.ContainerImage, Command: []string{"/bin/sh"}, ContainerArgs: []string{"-c", fmt.Sprintf("echo '%s' >> /data/out.txt; sync; tail -f /dev/null", genString)}, - MountPath: "/data", + PVCMap: map[string]types.VolumePath{ + pvc.Name: { + MountPath: "/data", + }, + }, } pod, err := s.createAppOps.CreatePod(ctx, podArgs) if err != nil { @@ -262,13 +265,16 @@ func (s *snapshotRestoreSteps) RestoreApplication(ctx context.Context, args *typ } podArgs := &types.CreatePodArgs{ GenerateName: clonedPodGenerateName, - PVCName: pvc.Name, Namespace: args.Namespace, RunAsUser: args.RunAsUser, ContainerImage: args.ContainerImage, Command: []string{"/bin/sh"}, ContainerArgs: []string{"-c", "tail -f /dev/null"}, - MountPath: "/data", + PVCMap: map[string]types.VolumePath{ + pvc.Name: { + MountPath: "/data", + }, + }, } pod, err := s.createAppOps.CreatePod(ctx, podArgs) if err != nil { diff --git a/pkg/csi/snapshot_restore_steps_test.go b/pkg/csi/snapshot_restore_steps_test.go index 3c56a58..4c37ff8 100644 --- a/pkg/csi/snapshot_restore_steps_test.go +++ b/pkg/csi/snapshot_restore_steps_test.go @@ -230,13 +230,16 @@ func (s *CSITestSuite) TestCreateApplication(c *C) { }, nil), f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ GenerateName: originalPodGenerateName, - PVCName: "pvc1", Namespace: "ns", Command: []string{"/bin/sh"}, ContainerArgs: []string{"-c", "echo 'some string' >> /data/out.txt; sync; tail -f /dev/null"}, RunAsUser: 100, ContainerImage: "image", - MountPath: "/data", + PVCMap: map[string]types.VolumePath{ + "pvc1": { + MountPath: "/data", + }, + }, }).Return(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod1", @@ -271,13 +274,16 @@ func (s *CSITestSuite) TestCreateApplication(c *C) { }, nil), f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ GenerateName: originalPodGenerateName, - PVCName: "pvc1", Namespace: "ns", Command: []string{"/bin/sh"}, ContainerArgs: []string{"-c", "echo 'some string' >> /data/out.txt; sync; tail -f /dev/null"}, RunAsUser: 100, ContainerImage: "image", - MountPath: "/data", + PVCMap: map[string]types.VolumePath{ + "pvc1": { + MountPath: "/data", + }, + }, }).Return(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod1", @@ -351,13 +357,16 @@ func (s *CSITestSuite) TestCreateApplication(c *C) { }, nil), f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ GenerateName: originalPodGenerateName, - PVCName: "pvc1", Namespace: "ns", Command: []string{"/bin/sh"}, ContainerArgs: []string{"-c", "echo 'some string' >> /data/out.txt; sync; tail -f /dev/null"}, RunAsUser: 100, ContainerImage: "image", - MountPath: "/data", + PVCMap: map[string]types.VolumePath{ + "pvc1": { + MountPath: "/data", + }, + }, }).Return(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod1", @@ -618,13 +627,16 @@ func (s *CSITestSuite) TestRestoreApplication(c *C) { }, nil), f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ GenerateName: clonedPodGenerateName, - PVCName: "pvc1", Namespace: "ns", Command: []string{"/bin/sh"}, ContainerArgs: []string{"-c", "tail -f /dev/null"}, - MountPath: "/data", RunAsUser: 100, ContainerImage: "image", + PVCMap: map[string]types.VolumePath{ + "pvc1": { + MountPath: "/data", + }, + }, }).Return(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod1", @@ -672,13 +684,16 @@ func (s *CSITestSuite) TestRestoreApplication(c *C) { }, nil), f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ GenerateName: clonedPodGenerateName, - PVCName: "pvc1", Namespace: "ns", Command: []string{"/bin/sh"}, ContainerArgs: []string{"-c", "tail -f /dev/null"}, - MountPath: "/data", RunAsUser: 100, ContainerImage: "image", + PVCMap: map[string]types.VolumePath{ + "pvc1": { + MountPath: "/data", + }, + }, }).Return(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pod1", diff --git a/pkg/csi/types/csi_types.go b/pkg/csi/types/csi_types.go index 2755582..dd92b36 100644 --- a/pkg/csi/types/csi_types.go +++ b/pkg/csi/types/csi_types.go @@ -56,27 +56,39 @@ func (c *CreatePVCArgs) Validate() error { return nil } +type VolumePath struct { + MountPath string // Only one of MountPath or + DevicePath string // DevicePath should be specified. +} + type CreatePodArgs struct { Name string // Only one of Name or GenerateName string // GenerateName should be specified. - PVCName string + PVCMap map[string]VolumePath Namespace string RunAsUser int64 ContainerImage string Command []string ContainerArgs []string - MountPath string // Only one of MountPath or - DevicePath string // DevicePath should be specified. } func (c *CreatePodArgs) Validate() error { if (c.GenerateName == "" && c.Name == "") || (c.GenerateName != "" && c.Name != "") || - (c.MountPath == "" && c.DevicePath == "") || - (c.MountPath != "" && c.DevicePath != "") || - c.PVCName == "" || c.Namespace == "" { + (c.Namespace == "") || (c.PVCMap == nil) { return fmt.Errorf("Invalid CreatePodArgs (%#v)", c) } + for pvcName, path := range c.PVCMap { + if pvcName == "" { + return fmt.Errorf("PVC Name not set") + } + if path.DevicePath == "" && path.MountPath == "" { + return fmt.Errorf("Neither DevicePath nor MountPath are set. One is required.") + } + if path.DevicePath != "" && path.MountPath != "" { + return fmt.Errorf("Both MountPath and DevicePath are set. Only one must be set.") + } + } return nil } From dbbb0f92b902f84d10727e7ef0269e20efb3356d Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Thu, 8 Aug 2024 11:21:04 -0500 Subject: [PATCH 49/59] Removing placeholder test for browse snapshot and browse pvc --- pkg/csi/pvc_inspector.go | 4 ---- pkg/csi/snapshot_inspector.go | 4 ---- 2 files changed, 8 deletions(-) diff --git a/pkg/csi/pvc_inspector.go b/pkg/csi/pvc_inspector.go index c2409b7..2de4393 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) } diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index f8fbd02..8301c8c 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 d6d359bc58803cf8a2e0b24a157c96f25557d13c Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Thu, 8 Aug 2024 21:33:06 -0500 Subject: [PATCH 50/59] Removing unused snapshotFetchOps from snapshotBrowserSteps --- pkg/csi/snapshot_inspector.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pkg/csi/snapshot_inspector.go b/pkg/csi/snapshot_inspector.go index 8301c8c..7a7824b 100644 --- a/pkg/csi/snapshot_inspector.go +++ b/pkg/csi/snapshot_inspector.go @@ -39,10 +39,6 @@ func (r *SnapshotBrowseRunner) RunSnapshotBrowse(ctx context.Context, args *type createAppOps: &applicationCreate{ kubeCli: r.KubeCli, }, - snapshotFetchOps: &snapshotFetch{ - kubeCli: r.KubeCli, - dynCli: r.DynCli, - }, portForwardOps: &portforward{}, kubeExecutor: &kubeExec{ kubeCli: r.KubeCli, @@ -109,7 +105,6 @@ type SnapshotBrowserStepper interface { type snapshotBrowserSteps struct { validateOps ArgumentValidator versionFetchOps ApiVersionFetcher - snapshotFetchOps SnapshotFetcher createAppOps ApplicationCreator portForwardOps PortForwarder cleanerOps Cleaner From f94e6e0d055021c8914bf2c76c759506a97ace6d Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 9 Aug 2024 16:26:55 -0500 Subject: [PATCH 51/59] Adding File restore command --- cmd/rootCmd.go | 59 +++++++ pkg/csi/file_restore_runner.go | 283 +++++++++++++++++++++++++++++++++ pkg/csi/types/csi_types.go | 15 ++ 3 files changed, 357 insertions(+) create mode 100644 pkg/csi/file_restore_runner.go diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index bc9e1b2..a765ada 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -128,6 +128,23 @@ var ( }, } + fromSnapshot string + path string + restoreFileCmd = &cobra.Command{ + Use: "file-restore", + Short: "Restore file(s) from a VolumeSnapshot to it's source PVC", + Long: "Restore file(s) from a given CSI provisioned VolumeSnapshot to the contents of it's source PVC.", + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return FileRestore(context.Background(), + fromSnapshot, + namespace, + csiCheckRunAsUser, + browseLocalPort, + path) + }, + } + blockMountRunAsUser int64 blockMountCleanup bool blockMountCleanupOnly bool @@ -207,6 +224,14 @@ func init() { browseCmd.AddCommand(browseSnapshotCmd) + rootCmd.AddCommand(restoreFileCmd) + restoreFileCmd.Flags().StringVarP(&fromSnapshot, "fromSnapshot", "f", "", "The name of a VolumeSnapshot. (Required)") + _ = restoreFileCmd.MarkFlagRequired("fromSnapshot") + restoreFileCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of both the given PVC & VS.") + restoreFileCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") + restoreFileCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") + restoreFileCmd.Flags().StringVarP(&path, "path", "p", "", "Path of a file or directory that needs to be restored") + rootCmd.AddCommand(blockMountCmd) blockMountCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a StorageClass. (Required)") _ = blockMountCmd.MarkFlagRequired("storageclass") @@ -430,6 +455,40 @@ func CsiSnapshotBrowse(ctx context.Context, return err } +func FileRestore(ctx context.Context, + snapshotName string, + namespace string, + runAsUser int64, + localPort int, + path string, +) 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 + } + fileRestoreRunner := &csi.FileRestoreRunner{ + KubeCli: kubecli, + DynCli: dyncli, + } + err = fileRestoreRunner.RunFileRestore(ctx, &csitypes.FileRestoreArgs{ + SnapshotName: snapshotName, + Namespace: namespace, + RunAsUser: runAsUser, + LocalPort: localPort, + Path: path, + }) + if err != nil { + fmt.Printf("Failed to run file-restore (%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/file_restore_runner.go b/pkg/csi/file_restore_runner.go new file mode 100644 index 0000000..ca2ba7f --- /dev/null +++ b/pkg/csi/file_restore_runner.go @@ -0,0 +1,283 @@ +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 FileRestoreRunner struct { + KubeCli kubernetes.Interface + DynCli dynamic.Interface + restoreSteps FileRestoreStepper + restorePVC *v1.PersistentVolumeClaim + pod *v1.Pod + snapshot *snapv1.VolumeSnapshot +} + +func (f *FileRestoreRunner) RunFileRestore(ctx context.Context, args *types.FileRestoreArgs) error { + f.restoreSteps = &fileRestoreSteps{ + validateOps: &validateOperations{ + kubeCli: f.KubeCli, + dynCli: f.DynCli, + }, + versionFetchOps: &apiVersionFetch{ + kubeCli: f.KubeCli, + }, + createAppOps: &applicationCreate{ + kubeCli: f.KubeCli, + }, + portForwardOps: &portforward{}, + kubeExecutor: &kubeExec{ + kubeCli: f.KubeCli, + }, + cleanerOps: &cleanse{ + kubeCli: f.KubeCli, + dynCli: f.DynCli, + }, + } + return f.RunFileRestoreHelper(ctx, args) +} + +func (f *FileRestoreRunner) RunFileRestoreHelper(ctx context.Context, args *types.FileRestoreArgs) error { + defer func() { + fmt.Println("Cleaning up browser pod & restored PVC.") + f.restoreSteps.Cleanup(ctx, f.restorePVC, f.pod) + }() + + if f.KubeCli == nil || f.DynCli == nil { + return fmt.Errorf("cli uninitialized") + } + + fmt.Println("Fetching the snapshot.") + vs, sourcePVC, sc, err := f.restoreSteps.ValidateArgs(ctx, args) + if err != nil { + return errors.Wrap(err, "Failed to validate arguments.") + } + f.snapshot = vs + + fmt.Println("Creating the restored PVC & browser Pod.") + f.pod, f.restorePVC, err = f.restoreSteps.CreateInspectorApplication(ctx, args, f.snapshot, sourcePVC, sc) + if err != nil { + return errors.Wrap(err, "Failed to create inspector application.") + } + + if args.Path != "" { + fmt.Printf("Restoring the file %s\n", args.Path) + _, err := f.restoreSteps.ExecuteCopyCommand(ctx, args, f.pod) + if err != nil { + return errors.Wrap(err, "Failed to execute cp command in pod.") + } + fmt.Printf("File restored from VolumeSnapshot %s to Source PVC %s.\n", f.snapshot.Name, sourcePVC.Name) + return nil + } + + fmt.Println("Forwarding the port.") + err = f.restoreSteps.PortForwardAPod(f.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_file_restore_stepper.go -package=mocks . FileRestoreStepper +type FileRestoreStepper interface { + ValidateArgs(ctx context.Context, args *types.FileRestoreArgs) (*snapv1.VolumeSnapshot, *v1.PersistentVolumeClaim, *sv1.StorageClass, error) + CreateInspectorApplication(ctx context.Context, args *types.FileRestoreArgs, snapshot *snapv1.VolumeSnapshot, sourcePVC *v1.PersistentVolumeClaim, storageClass *sv1.StorageClass) (*v1.Pod, *v1.PersistentVolumeClaim, error) + ExecuteCopyCommand(ctx context.Context, args *types.FileRestoreArgs, pod *v1.Pod) (string, error) + PortForwardAPod(pod *v1.Pod, localPort int) error + Cleanup(ctx context.Context, restorePVC *v1.PersistentVolumeClaim, pod *v1.Pod) +} + +type fileRestoreSteps struct { + validateOps ArgumentValidator + versionFetchOps ApiVersionFetcher + createAppOps ApplicationCreator + portForwardOps PortForwarder + cleanerOps Cleaner + kubeExecutor KubeExecutor + SnapshotGroupVersion *metav1.GroupVersionForDiscovery +} + +func (f *fileRestoreSteps) ValidateArgs(ctx context.Context, args *types.FileRestoreArgs) (*snapv1.VolumeSnapshot, *v1.PersistentVolumeClaim, *sv1.StorageClass, error) { + if err := args.Validate(); err != nil { + return nil, nil, nil, errors.Wrap(err, "Failed to validate input arguments") + } + if err := f.validateOps.ValidateNamespace(ctx, args.Namespace); err != nil { + return nil, nil, nil, errors.Wrap(err, "Failed to validate Namespace") + } + groupVersion, err := f.versionFetchOps.GetCSISnapshotGroupVersion() + if err != nil { + return nil, nil, nil, errors.Wrap(err, "Failed to fetch groupVersion") + } + f.SnapshotGroupVersion = groupVersion + snapshot, err := f.validateOps.ValidateVolumeSnapshot(ctx, args.SnapshotName, args.Namespace, groupVersion) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "Failed to validate VolumeSnapshot") + } + sourcePVC, err := f.validateOps.ValidatePVC(ctx, *snapshot.Spec.Source.PersistentVolumeClaimName, args.Namespace) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "Failed to validate source PVC") + } + // Validate source PVC acceptable access modes + sc, err := f.validateOps.ValidateStorageClass(ctx, *sourcePVC.Spec.StorageClassName) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "Failed to validate StorageClass") + } + uVSC, err := f.validateOps.ValidateVolumeSnapshotClass(ctx, *snapshot.Spec.VolumeSnapshotClassName, groupVersion) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "Failed to validate VolumeSnapshotClass") + } + vscDriver := getDriverNameFromUVSC(*uVSC, groupVersion.GroupVersion) + if sc.Provisioner != vscDriver { + return nil, nil, nil, fmt.Errorf("StorageClass provisioner (%s) and VolumeSnapshotClass driver (%s) are different.", sc.Provisioner, vscDriver) + } + return snapshot, sourcePVC, sc, nil +} + +func (f *fileRestoreSteps) CreateInspectorApplication(ctx context.Context, args *types.FileRestoreArgs, snapshot *snapv1.VolumeSnapshot, sourcePVC *v1.PersistentVolumeClaim, 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, + } + restorePVC, err := f.createAppOps.CreatePVC(ctx, pvcArgs) + if err != nil { + return nil, nil, errors.Wrap(err, "Failed to restore PVC") + } + podArgs := &types.CreatePodArgs{ + GenerateName: clonedPodGenerateName, + Namespace: args.Namespace, + RunAsUser: args.RunAsUser, + ContainerImage: "filebrowser/filebrowser:v2", + ContainerArgs: []string{"--noauth"}, + PVCMap: map[string]types.VolumePath{ + restorePVC.Name: { + MountPath: "/srv/snapshot-data", + }, + sourcePVC.Name: { + MountPath: "/srv/source-data", + }, + }, + } + if args.Path != "" { + podArgs = &types.CreatePodArgs{ + GenerateName: clonedPodGenerateName, + Namespace: args.Namespace, + RunAsUser: args.RunAsUser, + ContainerImage: "alpine:3.19", + Command: []string{"/bin/sh"}, + ContainerArgs: []string{"-c", "while true; do sleep 3600; done"}, + PVCMap: map[string]types.VolumePath{ + restorePVC.Name: { + MountPath: "/snapshot-data", + }, + sourcePVC.Name: { + MountPath: "/source-data", + }, + }, + } + } + pod, err := f.createAppOps.CreatePod(ctx, podArgs) + if err != nil { + return nil, restorePVC, errors.Wrap(err, "Failed to create browse Pod") + } + if err = f.createAppOps.WaitForPodReady(ctx, args.Namespace, pod.Name); err != nil { + return pod, restorePVC, errors.Wrap(err, "Pod failed to become ready") + } + return pod, restorePVC, nil +} + +func (f *fileRestoreSteps) ExecuteCopyCommand(ctx context.Context, args *types.FileRestoreArgs, pod *v1.Pod) (string, error) { + command := []string{"cp", "-rf", fmt.Sprintf("/snapshot-data%s", args.Path), fmt.Sprintf("/source-data%s", args.Path)} + stdout, err := f.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 (f *fileRestoreSteps) PortForwardAPod(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 := f.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("\nStopping 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 = f.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 (f *fileRestoreSteps) Cleanup(ctx context.Context, restorePVC *v1.PersistentVolumeClaim, pod *v1.Pod) { + if restorePVC != nil { + err := f.cleanerOps.DeletePVC(ctx, restorePVC.Name, restorePVC.Namespace) + if err != nil { + fmt.Println("Failed to delete restore PVC", restorePVC) + } + } + if pod != nil { + err := f.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 dd92b36..505d944 100644 --- a/pkg/csi/types/csi_types.go +++ b/pkg/csi/types/csi_types.go @@ -162,6 +162,21 @@ func (p *SnapshotBrowseArgs) Validate() error { return nil } +type FileRestoreArgs struct { + SnapshotName string + Namespace string + RunAsUser int64 + LocalPort int + Path string +} + +func (f *FileRestoreArgs) Validate() error { + if f.SnapshotName == "" || f.Namespace == "" { + return fmt.Errorf("Invalid FileRestoreArgs (%v)", f) + } + return nil +} + type PortForwardAPodRequest struct { // RestConfig is the kubernetes config RestConfig *rest.Config From a0eb8e8573d061479b94529b30332d621ed7534f Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 9 Aug 2024 17:24:51 -0500 Subject: [PATCH 52/59] Adding mock objects and fake tests for file restore command --- pkg/csi/file_restore_inspector_steps_test.go | 594 +++++++++++++++++++ pkg/csi/file_restore_inspector_test.go | 192 ++++++ pkg/csi/mocks/mock_file_restore_stepper.go | 113 ++++ 3 files changed, 899 insertions(+) create mode 100644 pkg/csi/file_restore_inspector_steps_test.go create mode 100644 pkg/csi/file_restore_inspector_test.go create mode 100644 pkg/csi/mocks/mock_file_restore_stepper.go diff --git a/pkg/csi/file_restore_inspector_steps_test.go b/pkg/csi/file_restore_inspector_steps_test.go new file mode 100644 index 0000000..24974ea --- /dev/null +++ b/pkg/csi/file_restore_inspector_steps_test.go @@ -0,0 +1,594 @@ +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) TestFileRestoreValidateArgs(c *C) { + ctx := context.Background() + scName := "sc" + vscName := "vsc" + pvcName := "pvc" + type fields struct { + validateOps *mocks.MockArgumentValidator + versionOps *mocks.MockApiVersionFetcher + } + for _, tc := range []struct { + args *types.FileRestoreArgs + prepare func(f *fields) + errChecker Checker + }{ + { // valid args + args: &types.FileRestoreArgs{ + SnapshotName: "vs", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return( + &metav1.GroupVersionForDiscovery{ + GroupVersion: common.SnapshotAlphaVersion, + }, nil), + f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), "vs", "ns", gomock.Any()).Return( + &snapv1.VolumeSnapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vs", + Namespace: "ns", + }, + Spec: snapv1.VolumeSnapshotSpec{ + Source: snapv1.VolumeSnapshotSource{ + PersistentVolumeClaimName: &pvcName, + }, + VolumeSnapshotClassName: &vscName, + }, + }, nil, + ), + f.validateOps.EXPECT().ValidatePVC(gomock.Any(), "pvc", "ns").Return( + &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pvc", + Namespace: "ns", + }, + Spec: v1.PersistentVolumeClaimSpec{ + VolumeName: "vol", + StorageClassName: &scName, + }, + }, nil, + ), + f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), scName).Return( + &sv1.StorageClass{ + Provisioner: "p1", + }, nil), + f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), "vsc", &metav1.GroupVersionForDiscovery{ + GroupVersion: common.SnapshotAlphaVersion, + }).Return(&unstructured.Unstructured{ + Object: map[string]interface{}{ + common.VolSnapClassAlphaDriverKey: "p1", + }, + }, nil), + ) + }, + errChecker: IsNil, + }, + { // driver mismatch + args: &types.FileRestoreArgs{ + SnapshotName: "vs", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return( + &metav1.GroupVersionForDiscovery{ + GroupVersion: common.SnapshotAlphaVersion, + }, nil), + f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), "vs", "ns", gomock.Any()).Return( + &snapv1.VolumeSnapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vs", + Namespace: "ns", + }, + Spec: snapv1.VolumeSnapshotSpec{ + Source: snapv1.VolumeSnapshotSource{ + PersistentVolumeClaimName: &pvcName, + }, + VolumeSnapshotClassName: &vscName, + }, + }, nil, + ), + f.validateOps.EXPECT().ValidatePVC(gomock.Any(), "pvc", "ns").Return( + &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pvc", + Namespace: "ns", + }, + Spec: v1.PersistentVolumeClaimSpec{ + VolumeName: "vol", + StorageClassName: &scName, + }, + }, nil, + ), + f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return( + &sv1.StorageClass{ + Provisioner: "p1", + }, nil), + f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), "vsc", &metav1.GroupVersionForDiscovery{ + GroupVersion: common.SnapshotAlphaVersion, + }).Return(&unstructured.Unstructured{ + Object: map[string]interface{}{ + common.VolSnapClassAlphaDriverKey: "p2", + }, + }, nil), + ) + }, + errChecker: NotNil, + }, + { // vsc error + args: &types.FileRestoreArgs{ + SnapshotName: "vs", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, nil), + f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), "vs", "ns", gomock.Any()).Return( + &snapv1.VolumeSnapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vs", + Namespace: "ns", + }, + Spec: snapv1.VolumeSnapshotSpec{ + Source: snapv1.VolumeSnapshotSource{ + PersistentVolumeClaimName: &pvcName, + }, + VolumeSnapshotClassName: &vscName, + }, + }, nil, + ), + f.validateOps.EXPECT().ValidatePVC(gomock.Any(), "pvc", "ns").Return( + &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pvc", + Namespace: "ns", + }, + Spec: v1.PersistentVolumeClaimSpec{ + VolumeName: "vol", + StorageClassName: &scName, + }, + }, nil, + ), + f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, nil), + f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("vsc error")), + ) + }, + errChecker: NotNil, + }, + { // get driver versionn error + args: &types.FileRestoreArgs{ + SnapshotName: "vs", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, fmt.Errorf("driver version error")), + ) + }, + errChecker: NotNil, + }, + { // sc error + args: &types.FileRestoreArgs{ + SnapshotName: "vs", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, nil), + f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), "vs", "ns", gomock.Any()).Return( + &snapv1.VolumeSnapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vs", + Namespace: "ns", + }, + Spec: snapv1.VolumeSnapshotSpec{ + Source: snapv1.VolumeSnapshotSource{ + PersistentVolumeClaimName: &pvcName, + }, + VolumeSnapshotClassName: &vscName, + }, + }, nil, + ), + f.validateOps.EXPECT().ValidatePVC(gomock.Any(), "pvc", "ns").Return( + &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pvc", + Namespace: "ns", + }, + Spec: v1.PersistentVolumeClaimSpec{ + VolumeName: "vol", + StorageClassName: &scName, + }, + }, nil, + ), + f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("sc error")), + ) + }, + errChecker: NotNil, + }, + { // validate vs error + args: &types.FileRestoreArgs{ + SnapshotName: "vs", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil), + f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, nil), + f.validateOps.EXPECT().ValidateVolumeSnapshot(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("validate vs error")), + ) + }, + errChecker: NotNil, + }, + { // validate ns error + args: &types.FileRestoreArgs{ + SnapshotName: "vs", + Namespace: "ns", + }, + prepare: func(f *fields) { + gomock.InOrder( + f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(fmt.Errorf("validate ns error")), + ) + }, + errChecker: NotNil, + }, + { // validate vs error + args: &types.FileRestoreArgs{ + SnapshotName: "", + Namespace: "ns", + }, + errChecker: NotNil, + }, + { // validate ns error + args: &types.FileRestoreArgs{ + SnapshotName: "dfd", + Namespace: "", + }, + errChecker: NotNil, + }, + } { + ctrl := gomock.NewController(c) + defer ctrl.Finish() + f := fields{ + validateOps: mocks.NewMockArgumentValidator(ctrl), + versionOps: mocks.NewMockApiVersionFetcher(ctrl), + } + if tc.prepare != nil { + tc.prepare(&f) + } + stepper := &fileRestoreSteps{ + validateOps: f.validateOps, + versionFetchOps: f.versionOps, + } + _, _, _, err := stepper.ValidateArgs(ctx, tc.args) + c.Check(err, tc.errChecker) + } +} + +func (s *CSITestSuite) TestCreateInspectorApplicationForFileRestore(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.FileRestoreArgs + snapshot *snapv1.VolumeSnapshot + sc *sv1.StorageClass + prepare func(f *fields) + errChecker Checker + podChecker Checker + pvcChecker Checker + }{ + { + args: &types.FileRestoreArgs{ + 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: "restorePVC", + }, + }, nil), + f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ + GenerateName: clonedPodGenerateName, + Namespace: "ns", + ContainerArgs: []string{"--noauth"}, + RunAsUser: 100, + ContainerImage: "filebrowser/filebrowser:v2", + PVCMap: map[string]types.VolumePath{ + "restorePVC": { + MountPath: "/srv/snapshot-data", + }, + "sourcePVC": { + MountPath: "/srv/source-data", + }, + }, + }).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.FileRestoreArgs{ + 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: "restorePVC", + }, + }, nil), + f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{ + GenerateName: clonedPodGenerateName, + Namespace: "ns", + ContainerArgs: []string{"--noauth"}, + RunAsUser: 100, + ContainerImage: "filebrowser/filebrowser:v2", + PVCMap: map[string]types.VolumePath{ + "restorePVC": { + MountPath: "/srv/snapshot-data", + }, + "sourcePVC": { + MountPath: "/srv/source-data", + }, + }, + }).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.FileRestoreArgs{ + 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: "restorePVC", + }, + }, nil), + f.createAppOps.EXPECT().CreatePod(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("pod error")), + ) + }, + errChecker: NotNil, + podChecker: IsNil, + pvcChecker: NotNil, + }, + { + args: &types.FileRestoreArgs{ + 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 := &fileRestoreSteps{ + createAppOps: f.createAppOps, + } + sourcePVC := v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sourcePVC", + Namespace: tc.args.Namespace, + }, + Spec: v1.PersistentVolumeClaimSpec{ + AccessModes: []v1.PersistentVolumeAccessMode{ + v1.ReadWriteOnce, + }, + Resources: v1.VolumeResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceStorage: resource.MustParse("1Gi"), + }, + }, + }, + } + pod, pvc, err := stepper.CreateInspectorApplication(ctx, tc.args, tc.snapshot, &sourcePVC, tc.sc) + c.Check(err, tc.errChecker) + c.Check(pod, tc.podChecker) + c.Check(pvc, tc.pvcChecker) + } +} + +func (s *CSITestSuite) TestFileRestoreCleanup(c *C) { + ctx := context.Background() + groupversion := &metav1.GroupVersionForDiscovery{ + GroupVersion: "gv", + Version: "v", + } + type fields struct { + cleanerOps *mocks.MockCleaner + } + for _, tc := range []struct { + restorePVC *v1.PersistentVolumeClaim + pod *v1.Pod + prepare func(f *fields) + }{ + { + restorePVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "restorePVC", + Namespace: "ns", + }, + }, + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod", + Namespace: "ns", + }, + }, + prepare: func(f *fields) { + gomock.InOrder( + f.cleanerOps.EXPECT().DeletePVC(ctx, "restorePVC", "ns").Return(nil), + f.cleanerOps.EXPECT().DeletePod(ctx, "pod", "ns").Return(nil), + ) + }, + }, + { + restorePVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "restorePVC", + Namespace: "ns", + }, + }, + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod", + Namespace: "ns", + }, + }, + prepare: func(f *fields) { + gomock.InOrder( + f.cleanerOps.EXPECT().DeletePVC(ctx, "restorePVC", "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 := &fileRestoreSteps{ + cleanerOps: f.cleanerOps, + SnapshotGroupVersion: groupversion, + } + stepper.Cleanup(ctx, tc.restorePVC, tc.pod) + } +} diff --git a/pkg/csi/file_restore_inspector_test.go b/pkg/csi/file_restore_inspector_test.go new file mode 100644 index 0000000..713586b --- /dev/null +++ b/pkg/csi/file_restore_inspector_test.go @@ -0,0 +1,192 @@ +package csi + +import ( + "context" + "fmt" + + "github.com/golang/mock/gomock" + "github.com/kastenhq/kubestr/pkg/csi/mocks" + "github.com/kastenhq/kubestr/pkg/csi/types" + snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + . "gopkg.in/check.v1" + v1 "k8s.io/api/core/v1" + sv1 "k8s.io/api/storage/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/dynamic" + fakedynamic "k8s.io/client-go/dynamic/fake" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/fake" +) + +func (s *CSITestSuite) TestRunFileRestoreHelper(c *C) { + ctx := context.Background() + type fields struct { + stepperOps *mocks.MockFileRestoreStepper + } + for _, tc := range []struct { + kubeCli kubernetes.Interface + dynCli dynamic.Interface + args *types.FileRestoreArgs + prepare func(f *fields) + errChecker Checker + }{ + { + // success + kubeCli: fake.NewSimpleClientset(), + dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()), + args: &types.FileRestoreArgs{}, + prepare: func(f *fields) { + gomock.InOrder( + f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return( + &snapv1.VolumeSnapshot{}, &v1.PersistentVolumeClaim{}, &sv1.StorageClass{}, nil, + ), + f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), gomock.Any(), + &snapv1.VolumeSnapshot{}, &v1.PersistentVolumeClaim{}, &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( + &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.FileRestoreArgs{}, + prepare: func(f *fields) { + gomock.InOrder( + f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil, nil, nil), + f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil), + f.stepperOps.EXPECT().PortForwardAPod(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.FileRestoreArgs{}, + prepare: func(f *fields) { + gomock.InOrder( + f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil, nil, nil), + f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), 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.FileRestoreArgs{}, + prepare: func(f *fields) { + gomock.InOrder( + f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil, nil, fmt.Errorf("snapshot error")), + f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any()), + ) + }, + errChecker: NotNil, + }, + { + // validate failure + kubeCli: fake.NewSimpleClientset(), + dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()), + args: &types.FileRestoreArgs{}, + prepare: func(f *fields) { + gomock.InOrder( + f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil, nil, fmt.Errorf("validate error")), + f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any()), + ) + }, + errChecker: NotNil, + }, + { + // emptycli failure + kubeCli: nil, + dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()), + args: &types.FileRestoreArgs{}, + 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.FileRestoreArgs{}, + 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.NewMockFileRestoreStepper(ctrl), + } + if tc.prepare != nil { + tc.prepare(&f) + } + runner := &FileRestoreRunner{ + KubeCli: tc.kubeCli, + DynCli: tc.dynCli, + restoreSteps: f.stepperOps, + } + err := runner.RunFileRestoreHelper(ctx, tc.args) + c.Check(err, tc.errChecker) + } +} + +func (s *CSITestSuite) TestFileRestoreRunner(c *C) { + ctx := context.Background() + r := &FileRestoreRunner{ + restoreSteps: &fileRestoreSteps{}, + } + err := r.RunFileRestoreHelper(ctx, nil) + c.Check(err, NotNil) +} diff --git a/pkg/csi/mocks/mock_file_restore_stepper.go b/pkg/csi/mocks/mock_file_restore_stepper.go new file mode 100644 index 0000000..1e2c90c --- /dev/null +++ b/pkg/csi/mocks/mock_file_restore_stepper.go @@ -0,0 +1,113 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: FileRestoreStepper) + +// 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" +) + +// MockFileRestoreStepper is a mock of FileRestoreStepper interface. +type MockFileRestoreStepper struct { + ctrl *gomock.Controller + recorder *MockFileRestoreStepperMockRecorder +} + +// MockFileRestoreStepperMockRecorder is the mock recorder for MockFileRestoreStepper. +type MockFileRestoreStepperMockRecorder struct { + mock *MockFileRestoreStepper +} + +// NewMockFileRestoreStepper creates a new mock instance. +func NewMockFileRestoreStepper(ctrl *gomock.Controller) *MockFileRestoreStepper { + mock := &MockFileRestoreStepper{ctrl: ctrl} + mock.recorder = &MockFileRestoreStepperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFileRestoreStepper) EXPECT() *MockFileRestoreStepperMockRecorder { + return m.recorder +} + +// Cleanup mocks base method. +func (m *MockFileRestoreStepper) 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 *MockFileRestoreStepperMockRecorder) Cleanup(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cleanup", reflect.TypeOf((*MockFileRestoreStepper)(nil).Cleanup), arg0, arg1, arg2) +} + +// CreateInspectorApplication mocks base method. +func (m *MockFileRestoreStepper) CreateInspectorApplication(arg0 context.Context, arg1 *types.FileRestoreArgs, arg2 *v1.VolumeSnapshot, arg3 *v10.PersistentVolumeClaim, arg4 *v11.StorageClass) (*v10.Pod, *v10.PersistentVolumeClaim, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateInspectorApplication", arg0, arg1, arg2, arg3, arg4) + 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 *MockFileRestoreStepperMockRecorder) CreateInspectorApplication(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateInspectorApplication", reflect.TypeOf((*MockFileRestoreStepper)(nil).CreateInspectorApplication), arg0, arg1, arg2, arg3, arg4) +} + +// ExecuteCopyCommand mocks base method. +func (m *MockFileRestoreStepper) ExecuteCopyCommand(arg0 context.Context, arg1 *types.FileRestoreArgs, arg2 *v10.Pod) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExecuteCopyCommand", arg0, arg1, arg2) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExecuteCopyCommand indicates an expected call of ExecuteCopyCommand. +func (mr *MockFileRestoreStepperMockRecorder) ExecuteCopyCommand(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecuteCopyCommand", reflect.TypeOf((*MockFileRestoreStepper)(nil).ExecuteCopyCommand), arg0, arg1, arg2) +} + +// PortForwardAPod mocks base method. +func (m *MockFileRestoreStepper) PortForwardAPod(arg0 *v10.Pod, arg1 int) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PortForwardAPod", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// PortForwardAPod indicates an expected call of PortForwardAPod. +func (mr *MockFileRestoreStepperMockRecorder) PortForwardAPod(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PortForwardAPod", reflect.TypeOf((*MockFileRestoreStepper)(nil).PortForwardAPod), arg0, arg1) +} + +// ValidateArgs mocks base method. +func (m *MockFileRestoreStepper) ValidateArgs(arg0 context.Context, arg1 *types.FileRestoreArgs) (*v1.VolumeSnapshot, *v10.PersistentVolumeClaim, *v11.StorageClass, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateArgs", arg0, arg1) + ret0, _ := ret[0].(*v1.VolumeSnapshot) + ret1, _ := ret[1].(*v10.PersistentVolumeClaim) + ret2, _ := ret[2].(*v11.StorageClass) + ret3, _ := ret[3].(error) + return ret0, ret1, ret2, ret3 +} + +// ValidateArgs indicates an expected call of ValidateArgs. +func (mr *MockFileRestoreStepperMockRecorder) ValidateArgs(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateArgs", reflect.TypeOf((*MockFileRestoreStepper)(nil).ValidateArgs), arg0, arg1) +} From d0dc63ec10d4bc0f3aa8ce787b88505040b9e908 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 9 Aug 2024 18:07:07 -0500 Subject: [PATCH 53/59] Renaming file_restore_inspector.go --- pkg/csi/{file_restore_runner.go => file_restore_inspector.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pkg/csi/{file_restore_runner.go => file_restore_inspector.go} (100%) diff --git a/pkg/csi/file_restore_runner.go b/pkg/csi/file_restore_inspector.go similarity index 100% rename from pkg/csi/file_restore_runner.go rename to pkg/csi/file_restore_inspector.go From 21aada53e6792c9859fec845b8449a54ea7d9059 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Fri, 9 Aug 2024 18:30:40 -0500 Subject: [PATCH 54/59] Removing unused SnapshotFetcher interface --- pkg/csi/csi_ops.go | 35 ------------- pkg/csi/mocks/mock_snapshot_fetcher.go | 68 -------------------------- 2 files changed, 103 deletions(-) delete mode 100644 pkg/csi/mocks/mock_snapshot_fetcher.go diff --git a/pkg/csi/csi_ops.go b/pkg/csi/csi_ops.go index 37f23b7..202db70 100644 --- a/pkg/csi/csi_ops.go +++ b/pkg/csi/csi_ops.go @@ -332,41 +332,6 @@ 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_snapshot_fetcher.go b/pkg/csi/mocks/mock_snapshot_fetcher.go deleted file mode 100644 index 8bd55e8..0000000 --- a/pkg/csi/mocks/mock_snapshot_fetcher.go +++ /dev/null @@ -1,68 +0,0 @@ -// 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)) -} From d4983892d7041d77da751c013b18a5b22379faef Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Tue, 13 Aug 2024 13:32:32 -0500 Subject: [PATCH 55/59] Adding check for source PVC in Snapshot and supported accessModes in source PVC --- pkg/csi/file_restore_inspector.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/csi/file_restore_inspector.go b/pkg/csi/file_restore_inspector.go index ca2ba7f..d53987f 100644 --- a/pkg/csi/file_restore_inspector.go +++ b/pkg/csi/file_restore_inspector.go @@ -128,11 +128,18 @@ func (f *fileRestoreSteps) ValidateArgs(ctx context.Context, args *types.FileRes if err != nil { return nil, nil, nil, errors.Wrap(err, "Failed to validate VolumeSnapshot") } + if *snapshot.Spec.Source.PersistentVolumeClaimName == "" { + return nil, nil, nil, errors.Wrap(err, "Failed to fetch source PVC. VolumeSnapshot does not have a PVC as it's source") + } sourcePVC, err := f.validateOps.ValidatePVC(ctx, *snapshot.Spec.Source.PersistentVolumeClaimName, args.Namespace) if err != nil { return nil, nil, nil, errors.Wrap(err, "Failed to validate source PVC") } - // Validate source PVC acceptable access modes + for _, sourceAccessMode := range sourcePVC.Spec.AccessModes { + if sourceAccessMode == v1.ReadWriteOnce || sourceAccessMode == v1.ReadWriteOncePod { + return nil, nil, nil, errors.Wrap(err, "Unsupported AccessModes found. Supported AccessModes are ReadOnlyMany & ReadWriteMany") + } + } sc, err := f.validateOps.ValidateStorageClass(ctx, *sourcePVC.Spec.StorageClassName) if err != nil { return nil, nil, nil, errors.Wrap(err, "Failed to validate StorageClass") From 6ba5cd7aa4a52df7988275d194d06c7765b655eb Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Tue, 13 Aug 2024 15:15:02 -0500 Subject: [PATCH 56/59] Adding --toPVC flag --- cmd/rootCmd.go | 5 +++++ pkg/csi/file_restore_inspector.go | 22 ++++++++++++++++------ pkg/csi/types/csi_types.go | 1 + 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index c7d5949..bf0fe53 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -129,6 +129,7 @@ var ( } fromSnapshot string + toPVC string path string restoreFileCmd = &cobra.Command{ Use: "file-restore", @@ -138,6 +139,7 @@ var ( RunE: func(cmd *cobra.Command, args []string) error { return FileRestore(context.Background(), fromSnapshot, + toPVC, namespace, csiCheckRunAsUser, browseLocalPort, @@ -227,6 +229,7 @@ func init() { rootCmd.AddCommand(restoreFileCmd) restoreFileCmd.Flags().StringVarP(&fromSnapshot, "fromSnapshot", "f", "", "The name of a VolumeSnapshot. (Required)") _ = restoreFileCmd.MarkFlagRequired("fromSnapshot") + restoreFileCmd.Flags().StringVarP(&toPVC, "toPVC", "t", "", "The name of a PersistentVolumeClaim.") restoreFileCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of both the given PVC & VS.") restoreFileCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)") restoreFileCmd.Flags().IntVarP(&browseLocalPort, "localport", "l", 8080, "The local port to expose the inspector") @@ -457,6 +460,7 @@ func CsiSnapshotBrowse(ctx context.Context, func FileRestore(ctx context.Context, snapshotName string, + pvcName string, namespace string, runAsUser int64, localPort int, @@ -478,6 +482,7 @@ func FileRestore(ctx context.Context, } err = fileRestoreRunner.RunFileRestore(ctx, &csitypes.FileRestoreArgs{ SnapshotName: snapshotName, + PVCName: pvcName, Namespace: namespace, RunAsUser: runAsUser, LocalPort: localPort, diff --git a/pkg/csi/file_restore_inspector.go b/pkg/csi/file_restore_inspector.go index d53987f..602194e 100644 --- a/pkg/csi/file_restore_inspector.go +++ b/pkg/csi/file_restore_inspector.go @@ -128,12 +128,22 @@ func (f *fileRestoreSteps) ValidateArgs(ctx context.Context, args *types.FileRes if err != nil { return nil, nil, nil, errors.Wrap(err, "Failed to validate VolumeSnapshot") } - if *snapshot.Spec.Source.PersistentVolumeClaimName == "" { - return nil, nil, nil, errors.Wrap(err, "Failed to fetch source PVC. VolumeSnapshot does not have a PVC as it's source") - } - sourcePVC, err := f.validateOps.ValidatePVC(ctx, *snapshot.Spec.Source.PersistentVolumeClaimName, args.Namespace) - if err != nil { - return nil, nil, nil, errors.Wrap(err, "Failed to validate source PVC") + var sourcePVC *v1.PersistentVolumeClaim + if args.PVCName == "" { + fmt.Println("Fetching the source PVC from snapshot.") + if *snapshot.Spec.Source.PersistentVolumeClaimName == "" { + return nil, nil, nil, errors.Wrap(err, "Failed to fetch source PVC. VolumeSnapshot does not have a PVC as it's source") + } + sourcePVC, err = f.validateOps.ValidatePVC(ctx, *snapshot.Spec.Source.PersistentVolumeClaimName, args.Namespace) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "Failed to validate source PVC") + } + } else { + fmt.Println("Fetching the source PVC.") + sourcePVC, err = f.validateOps.ValidatePVC(ctx, args.PVCName, args.Namespace) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "Failed to validate source PVC") + } } for _, sourceAccessMode := range sourcePVC.Spec.AccessModes { if sourceAccessMode == v1.ReadWriteOnce || sourceAccessMode == v1.ReadWriteOncePod { diff --git a/pkg/csi/types/csi_types.go b/pkg/csi/types/csi_types.go index 505d944..3cdc2da 100644 --- a/pkg/csi/types/csi_types.go +++ b/pkg/csi/types/csi_types.go @@ -164,6 +164,7 @@ func (p *SnapshotBrowseArgs) Validate() error { type FileRestoreArgs struct { SnapshotName string + PVCName string Namespace string RunAsUser int64 LocalPort int From a02598669cb4f5f9317e36be29041dbf56d2308e Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Tue, 13 Aug 2024 15:16:46 -0500 Subject: [PATCH 57/59] Fixing seg fault occurred because of the invalid error thrown in accessmodes check --- pkg/csi/file_restore_inspector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/csi/file_restore_inspector.go b/pkg/csi/file_restore_inspector.go index 602194e..af4358d 100644 --- a/pkg/csi/file_restore_inspector.go +++ b/pkg/csi/file_restore_inspector.go @@ -147,7 +147,7 @@ func (f *fileRestoreSteps) ValidateArgs(ctx context.Context, args *types.FileRes } for _, sourceAccessMode := range sourcePVC.Spec.AccessModes { if sourceAccessMode == v1.ReadWriteOnce || sourceAccessMode == v1.ReadWriteOncePod { - return nil, nil, nil, errors.Wrap(err, "Unsupported AccessModes found. Supported AccessModes are ReadOnlyMany & ReadWriteMany") + return nil, nil, nil, fmt.Errorf("Unsupported %s AccessMode found in source PVC. Supported AccessModes are ReadOnlyMany & ReadWriteMany", sourceAccessMode) } } sc, err := f.validateOps.ValidateStorageClass(ctx, *sourcePVC.Spec.StorageClassName) From 487ad81be84d776e97e40724b6de2bbd9b9da343 Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Wed, 14 Aug 2024 14:43:20 -0500 Subject: [PATCH 58/59] Removing check for ReadWriteOnce accessmode --- pkg/csi/file_restore_inspector.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/csi/file_restore_inspector.go b/pkg/csi/file_restore_inspector.go index af4358d..8b343bb 100644 --- a/pkg/csi/file_restore_inspector.go +++ b/pkg/csi/file_restore_inspector.go @@ -146,7 +146,7 @@ func (f *fileRestoreSteps) ValidateArgs(ctx context.Context, args *types.FileRes } } for _, sourceAccessMode := range sourcePVC.Spec.AccessModes { - if sourceAccessMode == v1.ReadWriteOnce || sourceAccessMode == v1.ReadWriteOncePod { + if sourceAccessMode == v1.ReadWriteOncePod { return nil, nil, nil, fmt.Errorf("Unsupported %s AccessMode found in source PVC. Supported AccessModes are ReadOnlyMany & ReadWriteMany", sourceAccessMode) } } From dab671824542456b32fdcda25bf813615c6c7b3c Mon Sep 17 00:00:00 2001 From: Shlok Chaudhari Date: Tue, 20 Aug 2024 15:41:42 -0500 Subject: [PATCH 59/59] Update cmd/rootCmd.go Co-authored-by: Sirish Bathina --- cmd/rootCmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/rootCmd.go b/cmd/rootCmd.go index bf0fe53..b9d6596 100644 --- a/cmd/rootCmd.go +++ b/cmd/rootCmd.go @@ -134,7 +134,7 @@ var ( restoreFileCmd = &cobra.Command{ Use: "file-restore", Short: "Restore file(s) from a VolumeSnapshot to it's source PVC", - Long: "Restore file(s) from a given CSI provisioned VolumeSnapshot to the contents of it's source PVC.", + Long: "Restore file(s) from a given CSI provisioned VolumeSnapshot to a PVC.", Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { return FileRestore(context.Background(),