mirror of
https://github.com/kastenhq/kubestr.git
synced 2024-12-14 11:57:56 +00:00
CSI snapshot restore (#37)
* starting with the validation * Create application and use mocks * Added snapshotter * cleanup and full execution test * last bit of wiring * Nil checks for comments * removing debug print * unused variable
This commit is contained in:
parent
8077b22799
commit
8d5674a59e
21 changed files with 4241 additions and 46 deletions
106
cmd/rootCmd.go
106
cmd/rootCmd.go
|
@ -20,6 +20,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/kastenhq/kubestr/pkg/csi"
|
||||||
|
csitypes "github.com/kastenhq/kubestr/pkg/csi/types"
|
||||||
"github.com/kastenhq/kubestr/pkg/fio"
|
"github.com/kastenhq/kubestr/pkg/fio"
|
||||||
"github.com/kastenhq/kubestr/pkg/kubestr"
|
"github.com/kastenhq/kubestr/pkg/kubestr"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -40,19 +42,36 @@ var (
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fioCheckerStorageClass string
|
storageClass string
|
||||||
fioCheckerSize string
|
namespace string
|
||||||
fioCheckerNamespace string
|
|
||||||
fioCheckerFilePath string
|
fioCheckerSize string
|
||||||
fioCheckerTestName string
|
fioCheckerFilePath string
|
||||||
fioCmd = &cobra.Command{
|
fioCheckerTestName string
|
||||||
|
fioCmd = &cobra.Command{
|
||||||
Use: "fio",
|
Use: "fio",
|
||||||
Short: "Runs an fio test",
|
Short: "Runs an fio test",
|
||||||
Long: `Run an fio test`,
|
Long: `Run an fio test`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
Fio(ctx, output, fioCheckerStorageClass, fioCheckerSize, fioCheckerNamespace, fioCheckerTestName, fioCheckerFilePath)
|
Fio(ctx, output, storageClass, fioCheckerSize, namespace, fioCheckerTestName, fioCheckerFilePath)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
csiCheckVolumeSnapshotClass string
|
||||||
|
csiCheckRunAsUser int64
|
||||||
|
csiCheckContainerImage string
|
||||||
|
csiCheckCleanup bool
|
||||||
|
csiCheckSkipCFSCheck bool
|
||||||
|
csiCheckCmd = &cobra.Command{
|
||||||
|
Use: "csicheck",
|
||||||
|
Short: "Runs the CSI snapshot restore check",
|
||||||
|
Long: "Validates a CSI provisioners ability to take a snapshot of an application and restore it",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
CSICheck(ctx, output, namespace, storageClass, csiCheckVolumeSnapshotClass, csiCheckRunAsUser, csiCheckContainerImage, csiCheckCleanup, csiCheckSkipCFSCheck)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -61,13 +80,23 @@ func init() {
|
||||||
rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "", "Options(json)")
|
rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "", "Options(json)")
|
||||||
|
|
||||||
rootCmd.AddCommand(fioCmd)
|
rootCmd.AddCommand(fioCmd)
|
||||||
fioCmd.Flags().StringVarP(&fioCheckerStorageClass, "storageclass", "c", "", "The name of a storageclass. (Required)")
|
fioCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a Storageclass. (Required)")
|
||||||
_ = fioCmd.MarkFlagRequired("storageclass")
|
_ = fioCmd.MarkFlagRequired("storageclass")
|
||||||
fioCmd.Flags().StringVarP(&fioCheckerSize, "size", "s", fio.DefaultPVCSize, "The size of the volume used to run FIO.")
|
fioCmd.Flags().StringVarP(&fioCheckerSize, "size", "z", fio.DefaultPVCSize, "The size of the volume used to run FIO.")
|
||||||
fioCmd.Flags().StringVarP(&fioCheckerNamespace, "namespace", "n", fio.DefaultNS, "The namespace used to run FIO.")
|
fioCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace used to run FIO.")
|
||||||
fioCmd.Flags().StringVarP(&fioCheckerFilePath, "fiofile", "f", "", "The path to a an fio config file.")
|
fioCmd.Flags().StringVarP(&fioCheckerFilePath, "fiofile", "f", "", "The path to a an fio config file.")
|
||||||
fioCmd.Flags().StringVarP(&fioCheckerTestName, "testname", "t", "", "The Name of a predefined kubestr fio test. Options(default-fio)")
|
fioCmd.Flags().StringVarP(&fioCheckerTestName, "testname", "t", "", "The Name of a predefined kubestr fio test. Options(default-fio)")
|
||||||
// //rootCmd.AddCommand(provCmd)
|
|
||||||
|
rootCmd.AddCommand(csiCheckCmd)
|
||||||
|
csiCheckCmd.Flags().StringVarP(&storageClass, "storageclass", "s", "", "The name of a Storageclass. (Required)")
|
||||||
|
_ = csiCheckCmd.MarkFlagRequired("storageclass")
|
||||||
|
csiCheckCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)")
|
||||||
|
_ = csiCheckCmd.MarkFlagRequired("volumesnapshotclass")
|
||||||
|
csiCheckCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace used to run the check.")
|
||||||
|
csiCheckCmd.Flags().StringVarP(&csiCheckContainerImage, "image", "i", "", "The container image used to create a pod.")
|
||||||
|
csiCheckCmd.Flags().BoolVarP(&csiCheckCleanup, "cleanup", "c", true, "Clean up the objects created by tool")
|
||||||
|
csiCheckCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the CSI check using pods as a user (int)")
|
||||||
|
csiCheckCmd.Flags().BoolVarP(&csiCheckSkipCFSCheck, "skipCFScheck", "k", false, "Use this flag to skip validating the ability to clone a snapshot.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute executes the main command
|
// Execute executes the main command
|
||||||
|
@ -117,14 +146,17 @@ func Baseline(ctx context.Context, output string) {
|
||||||
|
|
||||||
// Fio executes the FIO test.
|
// Fio executes the FIO test.
|
||||||
func Fio(ctx context.Context, output, storageclass, size, namespace, jobName, fioFilePath string) {
|
func Fio(ctx context.Context, output, storageclass, size, namespace, jobName, fioFilePath string) {
|
||||||
p, err := kubestr.NewKubestr()
|
cli, err := kubestr.LoadKubeCli()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
fioRunner := &fio.FIOrunner{
|
||||||
|
Cli: cli,
|
||||||
|
}
|
||||||
testName := "FIO test results"
|
testName := "FIO test results"
|
||||||
var result *kubestr.TestOutput
|
var result *kubestr.TestOutput
|
||||||
if fioResult, err := p.Fio.RunFio(ctx, &fio.RunFIOArgs{
|
if fioResult, err := fioRunner.RunFio(ctx, &fio.RunFIOArgs{
|
||||||
StorageClass: storageclass,
|
StorageClass: storageclass,
|
||||||
Size: size,
|
Size: size,
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
|
@ -143,3 +175,51 @@ func Fio(ctx context.Context, output, storageclass, size, namespace, jobName, fi
|
||||||
}
|
}
|
||||||
result.Print()
|
result.Print()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CSICheck(ctx context.Context, output,
|
||||||
|
namespace string,
|
||||||
|
storageclass string,
|
||||||
|
volumesnapshotclass string,
|
||||||
|
runAsUser int64,
|
||||||
|
containerImage string,
|
||||||
|
cleanup bool,
|
||||||
|
skipCFScheck bool,
|
||||||
|
) {
|
||||||
|
testName := "CSI checker test"
|
||||||
|
kubecli, err := kubestr.LoadKubeCli()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to load kubeCLi (%s)", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dyncli, err := kubestr.LoadDynCli()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to load kubeCLi (%s)", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
csiCheckRunner := &csi.SnapshotRestoreRunner{
|
||||||
|
KubeCli: kubecli,
|
||||||
|
DynCli: dyncli,
|
||||||
|
}
|
||||||
|
var result *kubestr.TestOutput
|
||||||
|
csiCheckResult, err := csiCheckRunner.RunSnapshotRestore(ctx, &csitypes.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: storageclass,
|
||||||
|
VolumeSnapshotClass: volumesnapshotclass,
|
||||||
|
Namespace: namespace,
|
||||||
|
RunAsUser: runAsUser,
|
||||||
|
ContainerImage: containerImage,
|
||||||
|
Cleanup: cleanup,
|
||||||
|
SkipCFSCheck: skipCFScheck,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
result = kubestr.MakeTestOutput(testName, kubestr.StatusError, err.Error(), csiCheckResult)
|
||||||
|
} else {
|
||||||
|
result = kubestr.MakeTestOutput(testName, kubestr.StatusOK, "CSI application successfully snapshotted and restored.", csiCheckResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
if output == "json" {
|
||||||
|
jsonRes, _ := json.MarshalIndent(result, "", " ")
|
||||||
|
fmt.Println(string(jsonRes))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result.Print()
|
||||||
|
}
|
||||||
|
|
|
@ -28,12 +28,11 @@ Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persiste
|
||||||
[Datatom-InfinityCSI](https://github.com/datatom-infinity/infinity-csi) | `csi-infiblock-plugin` | v0.3, v1.0.0, v1.1.0 | A Container Storage Interface (CSI) Driver for DATATOM Infinity storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology
|
[Datatom-InfinityCSI](https://github.com/datatom-infinity/infinity-csi) | `csi-infiblock-plugin` | v0.3, v1.0.0, v1.1.0 | A Container Storage Interface (CSI) Driver for DATATOM Infinity storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology
|
||||||
[Datatom-InfinityCSI (filesystem)](https://github.com/datatom-infinity/infinity-csi) | `csi-infifs-plugin` | v0.3, v1.0.0, v1.1.0 | A Container Storage Interface (CSI) Driver for DATATOM Infinity filesystem storage | Persistent | Read/Write Multiple Pods | Yes | Expansion
|
[Datatom-InfinityCSI (filesystem)](https://github.com/datatom-infinity/infinity-csi) | `csi-infifs-plugin` | v0.3, v1.0.0, v1.1.0 | A Container Storage Interface (CSI) Driver for DATATOM Infinity filesystem storage | Persistent | Read/Write Multiple Pods | Yes | Expansion
|
||||||
[Datera](https://github.com/Datera/datera-csi) | `dsp.csi.daterainc.io` | v1.0 | A Container Storage Interface (CSI) Driver for Datera Data Services Platform (DSP) | Persistent | Read/Write Single Pod | Yes |Snapshot
|
[Datera](https://github.com/Datera/datera-csi) | `dsp.csi.daterainc.io` | v1.0 | A Container Storage Interface (CSI) Driver for Datera Data Services Platform (DSP) | Persistent | Read/Write Single Pod | Yes |Snapshot
|
||||||
[Dell EMC Isilon](https://github.com/dell/csi-isilon) | `csi-isilon.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC Isilon](https://www.delltechnologies.com/en-us/storage/isilon/index.htm) | Persistent | Read/Write Multiple Pods | Yes | Snapshot
|
[Dell EMC PowerScale](https://github.com/dell/csi-powerscale) | `csi-isilon.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC PowerScale](https://www.delltechnologies.com/en-us/storage/powerscale.htm) | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning
|
||||||
[Dell EMC PowerMax](https://github.com/dell/csi-powermax) | `csi-powermax.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC PowerMax](https://www.delltechnologies.com/en-us/storage/powermax.htm) | Persistent | Read/Write Single Pod | Yes | Snapshot
|
[Dell EMC PowerMax](https://github.com/dell/csi-powermax) | `csi-powermax.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC PowerMax](https://www.delltechnologies.com/en-us/storage/powermax.htm) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
|
||||||
[Dell EMC PowerStore](https://github.com/dell/csi-powerstore) | `csi-powerstore.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC PowerStore](https://www.delltechnologies.com/en-us/storage/powerstore-storage-appliance.htm) | Persistent | Read/Write Single Pod | Yes |
|
[Dell EMC PowerStore](https://github.com/dell/csi-powerstore) | `csi-powerstore.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC PowerStore](https://www.delltechnologies.com/en-us/storage/powerstore-storage-appliance.htm) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
|
||||||
[Dell EMC Unity](https://github.com/dell/csi-unity) | `csi-unity.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC Unity](https://www.delltechnologies.com/en-us/storage/unity.htm) | Persistent | Read/Write Single Pod | Yes | Snapshot
|
[Dell EMC Unity](https://github.com/dell/csi-unity) | `csi-unity.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC Unity](https://www.delltechnologies.com/en-us/storage/unity.htm) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
|
||||||
[Dell EMC VxFlexOS](https://github.com/dell/csi-vxflexos) | `csi-vxflexos.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC VxFlexOS](https://www.delltechnologies.com/en-us/hyperconverged-infrastructure/vxflex.htm) | Persistent | Read/Write Single Pod | Yes | Snapshot
|
[Dell EMC VxFlexOS](https://github.com/dell/csi-vxflexos) | `csi-vxflexos.dellemc.com` | v1.1 | A Container Storage Interface (CSI) Driver for [Dell EMC VxFlexOS](https://www.delltechnologies.com/en-us/hyperconverged-infrastructure/vxflex.htm) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology
|
||||||
[Dell EMC XtremIO](https://github.com/dell/csi-xtremio-deploy) | `csi-xtremio.dellemc.com` | v1.0 | A Container Storage Interface (CSI) Driver for [Dell EMC XtremIO](https://www.delltechnologies.com/en-us/storage/xtremio-all-flash.htm) | Persistent | Read/Write Single Pod | Yes | Snapshot
|
|
||||||
[democratic-csi](https://github.com/democratic-csi/democratic-csi) | `org.democratic-csi.[X]` | v1.0,v1.1,v1.2 | Generic CSI plugin supporting zfs based solutions ([FreeNAS](https://www.freenas.org/) / [TrueNAS](https://www.truenas.com/) and [ZoL](https://zfsonlinux.org/) solutions such as [Ubuntu](https://ubuntu.com/)) | Persistent and Ephemeral | Read/Write Single Pod (Block Volume) <br/><br/> Read/Write Multiple Pods (File Volume) | Yes | Raw Block, Snapshot, Expansion, Cloning
|
[democratic-csi](https://github.com/democratic-csi/democratic-csi) | `org.democratic-csi.[X]` | v1.0,v1.1,v1.2 | Generic CSI plugin supporting zfs based solutions ([FreeNAS](https://www.freenas.org/) / [TrueNAS](https://www.truenas.com/) and [ZoL](https://zfsonlinux.org/) solutions such as [Ubuntu](https://ubuntu.com/)) | Persistent and Ephemeral | Read/Write Single Pod (Block Volume) <br/><br/> Read/Write Multiple Pods (File Volume) | Yes | Raw Block, Snapshot, Expansion, Cloning
|
||||||
[Diamanti-CSI](https://diamanti.com/use-cases/io-acceleration/#csi) | `dcx.csi.diamanti.com` | v1.0 | A Container Storage Interface (CSI) Driver for Diamanti DCX Platform | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
|
[Diamanti-CSI](https://diamanti.com/use-cases/io-acceleration/#csi) | `dcx.csi.diamanti.com` | v1.0 | A Container Storage Interface (CSI) Driver for Diamanti DCX Platform | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
|
||||||
[DigitalOcean Block Storage](https://github.com/digitalocean/csi-digitalocean) | `dobs.csi.digitalocean.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for DigitalOcean Block Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
|
[DigitalOcean Block Storage](https://github.com/digitalocean/csi-digitalocean) | `dobs.csi.digitalocean.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for DigitalOcean Block Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
|
||||||
|
@ -46,7 +45,7 @@ Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persiste
|
||||||
[GlusterFS](https://github.com/gluster/gluster-csi-driver) | `org.gluster.glusterfs` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for GlusterFS | Persistent | Read/Write Multiple Pods | Yes | Snapshot
|
[GlusterFS](https://github.com/gluster/gluster-csi-driver) | `org.gluster.glusterfs` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for GlusterFS | Persistent | Read/Write Multiple Pods | Yes | Snapshot
|
||||||
[Gluster VirtBlock](https://github.com/gluster/gluster-csi-driver) | `org.gluster.glustervirtblock` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Gluster Virtual Block volumes | Persistent | Read/Write Single Pod | Yes |
|
[Gluster VirtBlock](https://github.com/gluster/gluster-csi-driver) | `org.gluster.glustervirtblock` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Gluster Virtual Block volumes | Persistent | Read/Write Single Pod | Yes |
|
||||||
[Hammerspace CSI](https://github.com/hammer-space/csi-plugin) | `com.hammerspace.csi` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Hammerspace Storage | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot
|
[Hammerspace CSI](https://github.com/hammer-space/csi-plugin) | `com.hammerspace.csi` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Hammerspace Storage | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot
|
||||||
[Hedvig](https://documentation.commvault.com/commvault/hedvig/others/pdf/Hedvig_CSI_User_Guide.pdf) | `io.hedvig.csi` | v1.0 | A Container Storage Interface (CSI) Driver for Hedvig | Persistent | Read/Write Multiple Pods | Yes | Snapshot
|
[Hedvig](https://documentation.commvault.com/commvault/hedvig/others/pdf/Hedvig_CSI_User_Guide.pdf) | `io.hedvig.csi` | v1.0 | A Container Storage Interface (CSI) Driver for Hedvig | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion
|
||||||
[Hetzner Cloud Volumes CSI](https://github.com/hetznercloud/csi-driver) | `csi.hetzner.cloud` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Hetzner Cloud Volumes | Persistent | Read/Write Single Pod | Yes | Raw Block, Expansion
|
[Hetzner Cloud Volumes CSI](https://github.com/hetznercloud/csi-driver) | `csi.hetzner.cloud` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Hetzner Cloud Volumes | Persistent | Read/Write Single Pod | Yes | Raw Block, Expansion
|
||||||
[Hitachi Vantara](https://knowledge.hitachivantara.com/Documents/Adapters_and_Drivers/Storage_Adapters_and_Drivers/Containers) | `hspc.csi.hitachi.com` | v1.2 | A Container Storage Interface (CSI) Driver for VSP series Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
|
[Hitachi Vantara](https://knowledge.hitachivantara.com/Documents/Adapters_and_Drivers/Storage_Adapters_and_Drivers/Containers) | `hspc.csi.hitachi.com` | v1.2 | A Container Storage Interface (CSI) Driver for VSP series Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
|
||||||
[HPE](https://github.com/hpe-storage/csi-driver) | `csi.hpe.com` | v1.0, v1.1, v1.2 | A [multi-platform](https://scod.hpedev.io/csi_driver) Container Storage Interface (CSI) driver. Supports [HPE Nimble Storage](https://hpe.com/storage/nimble), [HPE Primera](https://hpe.com/storage/primera) and [HPE 3PAR](https://hpe.com/storage/3par) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
|
[HPE](https://github.com/hpe-storage/csi-driver) | `csi.hpe.com` | v1.0, v1.1, v1.2 | A [multi-platform](https://scod.hpedev.io/csi_driver) Container Storage Interface (CSI) driver. Supports [HPE Nimble Storage](https://hpe.com/storage/nimble), [HPE Primera](https://hpe.com/storage/primera) and [HPE 3PAR](https://hpe.com/storage/3par) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
|
||||||
|
@ -60,6 +59,7 @@ Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persiste
|
||||||
[Intel PMEM-CSI](https://github.com/intel/pmem-csi) | `pmem-csi.intel.com` | v1.0 | A Container Storage Interface (CSI) driver for [PMEM](https://pmem.io/) from Intel | Persistent and Ephemeral | Read/Write Single Pod | Yes | Raw Block
|
[Intel PMEM-CSI](https://github.com/intel/pmem-csi) | `pmem-csi.intel.com` | v1.0 | A Container Storage Interface (CSI) driver for [PMEM](https://pmem.io/) from Intel | Persistent and Ephemeral | Read/Write Single Pod | Yes | Raw Block
|
||||||
[JuiceFS](https://github.com/juicedata/juicefs-csi-driver) | `csi.juicefs.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for JuiceFS File System | Persistent | Read/Write Multiple Pod | Yes |
|
[JuiceFS](https://github.com/juicedata/juicefs-csi-driver) | `csi.juicefs.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for JuiceFS File System | Persistent | Read/Write Multiple Pod | Yes |
|
||||||
[kaDalu](https://github.com/kadalu/kadalu) | `org.kadalu.gluster` | v0.3 | A CSI Driver (and operator) for GlusterFS | Persistent | Read/Write Multiple Pods | Yes |
|
[kaDalu](https://github.com/kadalu/kadalu) | `org.kadalu.gluster` | v0.3 | A CSI Driver (and operator) for GlusterFS | Persistent | Read/Write Multiple Pods | Yes |
|
||||||
|
[KumoScale Block Storage](https://github.com/KioxiaAmerica/kumoscale-csi) | `kumoscale.kioxia.com` | v1.0 | A Container Storage Interface (CSI) Driver for KumoScale Block Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology
|
||||||
[Linode Block Storage](https://github.com/linode/linode-blockstorage-csi-driver) | `linodebs.csi.linode.com` | v1.0 | A Container Storage Interface (CSI) Driver for Linode Block Storage | Persistent | Read/Write Single Pod | Yes |
|
[Linode Block Storage](https://github.com/linode/linode-blockstorage-csi-driver) | `linodebs.csi.linode.com` | v1.0 | A Container Storage Interface (CSI) Driver for Linode Block Storage | Persistent | Read/Write Single Pod | Yes |
|
||||||
[LINSTOR](https://github.com/LINBIT/linstor-csi) | `io.drbd.linstor-csi` | v1.1 | A Container Storage Interface (CSI) Driver for [LINSTOR](https://www.linbit.com/en/linstor/) volumes | Persistent | Read/Write Single Pod | Yes | Snapshot
|
[LINSTOR](https://github.com/LINBIT/linstor-csi) | `io.drbd.linstor-csi` | v1.1 | A Container Storage Interface (CSI) Driver for [LINSTOR](https://www.linbit.com/en/linstor/) volumes | Persistent | Read/Write Single Pod | Yes | Snapshot
|
||||||
[Longhorn](https://github.com/longhorn/longhorn) | `driver.longhorn.io` | v1.1 | A Container Storage Interface (CSI) Driver for [Longhorn](https://longhorn.io/) volumes | Persistent | Read/Write Single Node | Yes | Raw Block
|
[Longhorn](https://github.com/longhorn/longhorn) | `driver.longhorn.io` | v1.1 | A Container Storage Interface (CSI) Driver for [Longhorn](https://longhorn.io/) volumes | Persistent | Read/Write Single Node | Yes | Raw Block
|
||||||
|
@ -75,13 +75,14 @@ Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persiste
|
||||||
[OpenSDS](https://github.com/opensds/nbp/tree/master/csi) | `csi-opensdsplugin` | v1.0 | A Container Storage Interface (CSI) Driver for [OpenSDS]((https://www.opensds.io/)) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot
|
[OpenSDS](https://github.com/opensds/nbp/tree/master/csi) | `csi-opensdsplugin` | v1.0 | A Container Storage Interface (CSI) Driver for [OpenSDS]((https://www.opensds.io/)) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot
|
||||||
[Open-E](https://github.com/open-e/JovianDSS-KubernetesCSI) | `com.open-e.joviandss.csi` | v1.0 | A Container Storage Interface (CSI) Driver for Open-E JovianDSS Storage | Persistent | Read/Write Single Pod | Yes | Snapshot, Cloning
|
[Open-E](https://github.com/open-e/JovianDSS-KubernetesCSI) | `com.open-e.joviandss.csi` | v1.0 | A Container Storage Interface (CSI) Driver for Open-E JovianDSS Storage | Persistent | Read/Write Single Pod | Yes | Snapshot, Cloning
|
||||||
[Portworx](https://github.com/libopenstorage/openstorage/tree/master/csi) | `pxd.openstorage.org` | v0.3, v1.1 | A Container Storage Interface (CSI) Driver for [Portworx](https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/csi/) | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion
|
[Portworx](https://github.com/libopenstorage/openstorage/tree/master/csi) | `pxd.openstorage.org` | v0.3, v1.1 | A Container Storage Interface (CSI) Driver for [Portworx](https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/csi/) | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion
|
||||||
[Pure Storage CSI](https://github.com/purestorage/pso-csi)| `pure-csi` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for Pure Storage's [Pure Service Orchestrator](https://purestorage.com/containers) | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Cloning, Raw Block, Topology, Expansion
|
[Pure Storage CSI](https://github.com/purestorage/pso-csi)| `pure-csi` | v1.0, v1.1, v1.2, v1.3 | A Container Storage Interface (CSI) Driver for Pure Storage's [Pure Service Orchestrator](https://purestorage.com/containers) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Snapshot, Cloning, Raw Block, Topology, Expansion
|
||||||
[QingCloud CSI](https://github.com/yunify/qingcloud-csi)| `disk.csi.qingcloud.com` | v1.1 | A Container Storage Interface (CSI) Driver for QingCloud Block Storage | Persistent | Read/Write Single Pod | Yes | Snapshot, Expansion, Cloning
|
[QingCloud CSI](https://github.com/yunify/qingcloud-csi)| `disk.csi.qingcloud.com` | v1.1 | A Container Storage Interface (CSI) Driver for QingCloud Block Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
|
||||||
[QingStor CSI](https://github.com/yunify/qingstor-csi) | `csi-neonsan` | v0.3 | A Container Storage Interface (CSI) Driver for NeonSAN storage system | Persistent | Read/Write Single Pod | Yes | Snapshot
|
[QingStor CSI](https://github.com/yunify/qingstor-csi) | `neonsan.csi.qingstor.com` | v0.3, v1.1 | A Container Storage Interface (CSI) Driver for NeonSAN storage system | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
|
||||||
[Quobyte](https://github.com/quobyte/quobyte-csi) | `quobyte-csi` | v0.2 | A Container Storage Interface (CSI) Driver for Quobyte | Persistent | Read/Write Multiple Pods | Yes |
|
[Quobyte](https://github.com/quobyte/quobyte-csi) | `quobyte-csi` | v0.2 | A Container Storage Interface (CSI) Driver for Quobyte | Persistent | Read/Write Multiple Pods | Yes |
|
||||||
[ROBIN](https://get.robin.io/) | `robin` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for [ROBIN](https://docs.robin.io) | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Cloning
|
[ROBIN](https://get.robin.io/) | `robin` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for [ROBIN](https://docs.robin.io) | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Cloning
|
||||||
[SandStone](https://github.com/sandstone-storage/sandstone-csi-driver) | `csi-sandstone-plugin` | v1.0 | A Container Storage Interface (CSI) Driver for SandStone USP | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
|
[SandStone](https://github.com/sandstone-storage/sandstone-csi-driver) | `csi-sandstone-plugin` | v1.0 | A Container Storage Interface (CSI) Driver for SandStone USP | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
|
||||||
[Sangfor-EDS](https://github.com/evan37717/sangfor-eds-csi) | `eds.csi.sangfor.com` | v1.0 | A Container Storage Interface (CSI) Driver for Sangfor Distributed File Storage(EDS) | Persistent | Read/Write Multiple Pods | Yes |
|
[Sangfor-EDS](https://github.com/evan37717/sangfor-eds-csi) | `eds.csi.sangfor.com` | v1.0 | A Container Storage Interface (CSI) Driver for Sangfor Distributed File Storage(EDS) | Persistent | Read/Write Multiple Pods | Yes |
|
||||||
|
[Sangfor-EDS-Block-Storage](https://github.com/eds-wzc/sangfor-eds-csi) | `eds.csi.block.sangfor.com` | v1.0 | A Container Storage Interface (CSI) Driver for Sangfor Block Storage(EDS) | Persistent | Read/Write Single Pod | Yes |
|
||||||
[SeaweedFS](https://github.com/seaweedfs/seaweedfs-csi-driver) | `seaweedfs-csi-driver` | v1.0 | A Container Storage Interface (CSI Driver for [SeaweedFS](https://github.com/chrislusf/seaweedfs)) | Persistent | Read/Write Multiple Pods | Yes |
|
[SeaweedFS](https://github.com/seaweedfs/seaweedfs-csi-driver) | `seaweedfs-csi-driver` | v1.0 | A Container Storage Interface (CSI Driver for [SeaweedFS](https://github.com/chrislusf/seaweedfs)) | Persistent | Read/Write Multiple Pods | Yes |
|
||||||
[Secrets Store CSI Driver](https://github.com/kubernetes-sigs/secrets-store-csi-driver) | `secrets-store.csi.k8s.io` | v0.0.10 | A Container Storage Interface (CSI) Driver for mounting secrets, keys, and certs stored in enterprise-grade external secrets stores as volumes. | Ephemeral | N/A | N/A |
|
[Secrets Store CSI Driver](https://github.com/kubernetes-sigs/secrets-store-csi-driver) | `secrets-store.csi.k8s.io` | v0.0.10 | A Container Storage Interface (CSI) Driver for mounting secrets, keys, and certs stored in enterprise-grade external secrets stores as volumes. | Ephemeral | N/A | N/A |
|
||||||
[SmartX](http://www.smartx.com/?locale=en) | `csi-smtx-plugin` | v1.0 | A Container Storage Interface (CSI) Driver for SmartX ZBS Storage | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion
|
[SmartX](http://www.smartx.com/?locale=en) | `csi-smtx-plugin` | v1.0 | A Container Storage Interface (CSI) Driver for SmartX ZBS Storage | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion
|
||||||
|
@ -97,7 +98,9 @@ Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persiste
|
||||||
[XSKY-EBS](https://xsky-storage.github.io/xsky-csi-driver/csi-block.html) | `csi.block.xsky.com` | v1.0 | A Container Storage Interface (CSI) Driver for XSKY Distributed Block Storage (X-EBS) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
|
[XSKY-EBS](https://xsky-storage.github.io/xsky-csi-driver/csi-block.html) | `csi.block.xsky.com` | v1.0 | A Container Storage Interface (CSI) Driver for XSKY Distributed Block Storage (X-EBS) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
|
||||||
[XSKY-EUS](https://xsky-storage.github.io/xsky-csi-driver/csi-fs.html) | `csi.fs.xsky.com` | v1.0 | A Container Storage Interface (CSI) Driver for XSKY Distributed File Storage (X-EUS) | Persistent | Read/Write Multiple Pods | Yes |
|
[XSKY-EUS](https://xsky-storage.github.io/xsky-csi-driver/csi-fs.html) | `csi.fs.xsky.com` | v1.0 | A Container Storage Interface (CSI) Driver for XSKY Distributed File Storage (X-EUS) | Persistent | Read/Write Multiple Pods | Yes |
|
||||||
[Vault](https://github.com/kubevault/csi-driver) | `secrets.csi.kubevault.com` | v1.0 | A Container Storage Interface (CSI) Driver for mounting HashiCorp Vault secrets as volumes. | Ephemeral | N/A | N/A |
|
[Vault](https://github.com/kubevault/csi-driver) | `secrets.csi.kubevault.com` | v1.0 | A Container Storage Interface (CSI) Driver for mounting HashiCorp Vault secrets as volumes. | Ephemeral | N/A | N/A |
|
||||||
[vSphere](https://github.com/kubernetes-sigs/vsphere-csi-driver) | `csi.vsphere.vmware.com` | v2.0.0 | A Container Storage Interface (CSI) Driver for VMware vSphere | Persistent | Read/Write Single Pod (Block Volume) <br/><br/> Read/Write Multiple Pods (File Volume) | Yes | Raw Block,<br/><br/>Expansion (Block Volume),<br/><br/>Topology Aware (Block Volume)
|
[Veritas InfoScale Volumes](https://www.veritas.com/solution/virtualization/containers.html) | `org.veritas.infoscale` | v1.2 | A Container Storage Interface (CSI) Driver for Veritas InfoScale volumes | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning
|
||||||
|
[vSphere](https://github.com/kubernetes-sigs/vsphere-csi-driver) | `csi.vsphere.vmware.com` | v1.0 | A Container Storage Interface (CSI) Driver for VMware vSphere | Persistent | Read/Write Single Pod (Block Volume) <br/><br/> Read/Write Multiple Pods (File Volume) | Yes | Raw Block,<br/><br/>Expansion (Block Volume),<br/><br/>Topology Aware (Block Volume)
|
||||||
|
[Vultr Block Storage](https://github.com/vultr/vultr-csi) | `block.csi.vultr.com` | v1.2 | A Container Storage Interface (CSI) Driver for Vultr Block Storage | Persistent | Read/Write Single Pod | Yes |
|
||||||
[WekaIO](https://github.com/weka/csi-wekafs) | `csi.weka.io` | v1.0 | A Container Storage Interface (CSI) Driver for mounting WekaIO WekaFS filesystem as volumes | Persistent | Read/Write Multiple Pods | Yes |
|
[WekaIO](https://github.com/weka/csi-wekafs) | `csi.weka.io` | v1.0 | A Container Storage Interface (CSI) Driver for mounting WekaIO WekaFS filesystem as volumes | Persistent | Read/Write Multiple Pods | Yes |
|
||||||
[Yandex.Cloud](https://github.com/flant/yandex-csi-driver) | `yandex.csi.flant.com` | v1.2 | A Container Storage Interface (CSI) plugin for Yandex.Cloud Compute Disks | Persistent | Read/Write Single Pod | Yes |
|
[Yandex.Cloud](https://github.com/flant/yandex-csi-driver) | `yandex.csi.flant.com` | v1.2 | A Container Storage Interface (CSI) plugin for Yandex.Cloud Compute Disks | Persistent | Read/Write Single Pod | Yes |
|
||||||
[YanRongYun](http://www.yanrongyun.com/) | ? | v1.0 | A Container Storage Interface (CSI) Driver for YanRong YRCloudFile Storage | Persistent | Read/Write Multiple Pods | Yes |
|
[YanRongYun](http://www.yanrongyun.com/) | ? | v1.0 | A Container Storage Interface (CSI) Driver for YanRong YRCloudFile Storage | Persistent | Read/Write Multiple Pods | Yes |
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -8,10 +8,13 @@ replace (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/golang/mock v1.4.4
|
||||||
github.com/kanisterio/kanister v0.0.0-00010101000000-000000000000
|
github.com/kanisterio/kanister v0.0.0-00010101000000-000000000000
|
||||||
|
github.com/kubernetes-csi/external-snapshotter v1.2.2
|
||||||
|
github.com/moby/buildkit v0.8.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/spf13/cobra v1.0.0
|
github.com/spf13/cobra v1.0.0
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f
|
||||||
k8s.io/api v0.19.0
|
k8s.io/api v0.19.0
|
||||||
k8s.io/apimachinery v0.19.0
|
k8s.io/apimachinery v0.19.0
|
||||||
k8s.io/client-go v0.19.0
|
k8s.io/client-go v0.19.0
|
||||||
|
|
11
pkg/csi/csi.go
Normal file
11
pkg/csi/csi.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/kastenhq/kubestr/pkg/csi/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CSI interface {
|
||||||
|
RunSnapshotRestore(ctx context.Context, args *types.CSISnapshotRestoreArgs) (*types.CSISnapshotRestoreResults, error)
|
||||||
|
}
|
49
pkg/csi/mocks/mock_api_version_fetcher.go
Normal file
49
pkg/csi/mocks/mock_api_version_fetcher.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: ApiVersionFetcher)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockApiVersionFetcher is a mock of ApiVersionFetcher interface
|
||||||
|
type MockApiVersionFetcher struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockApiVersionFetcherMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockApiVersionFetcherMockRecorder is the mock recorder for MockApiVersionFetcher
|
||||||
|
type MockApiVersionFetcherMockRecorder struct {
|
||||||
|
mock *MockApiVersionFetcher
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockApiVersionFetcher creates a new mock instance
|
||||||
|
func NewMockApiVersionFetcher(ctrl *gomock.Controller) *MockApiVersionFetcher {
|
||||||
|
mock := &MockApiVersionFetcher{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockApiVersionFetcherMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockApiVersionFetcher) EXPECT() *MockApiVersionFetcherMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCSISnapshotGroupVersion mocks base method
|
||||||
|
func (m *MockApiVersionFetcher) GetCSISnapshotGroupVersion() (*v1.GroupVersionForDiscovery, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetCSISnapshotGroupVersion")
|
||||||
|
ret0, _ := ret[0].(*v1.GroupVersionForDiscovery)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCSISnapshotGroupVersion indicates an expected call of GetCSISnapshotGroupVersion
|
||||||
|
func (mr *MockApiVersionFetcherMockRecorder) GetCSISnapshotGroupVersion() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCSISnapshotGroupVersion", reflect.TypeOf((*MockApiVersionFetcher)(nil).GetCSISnapshotGroupVersion))
|
||||||
|
}
|
80
pkg/csi/mocks/mock_application_creator.go
Normal file
80
pkg/csi/mocks/mock_application_creator.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: ApplicationCreator)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
types "github.com/kastenhq/kubestr/pkg/csi/types"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockApplicationCreator is a mock of ApplicationCreator interface
|
||||||
|
type MockApplicationCreator struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockApplicationCreatorMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockApplicationCreatorMockRecorder is the mock recorder for MockApplicationCreator
|
||||||
|
type MockApplicationCreatorMockRecorder struct {
|
||||||
|
mock *MockApplicationCreator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockApplicationCreator creates a new mock instance
|
||||||
|
func NewMockApplicationCreator(ctrl *gomock.Controller) *MockApplicationCreator {
|
||||||
|
mock := &MockApplicationCreator{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockApplicationCreatorMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockApplicationCreator) EXPECT() *MockApplicationCreatorMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePVC mocks base method
|
||||||
|
func (m *MockApplicationCreator) CreatePVC(arg0 context.Context, arg1 *types.CreatePVCArgs) (*v1.PersistentVolumeClaim, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CreatePVC", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*v1.PersistentVolumeClaim)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePVC indicates an expected call of CreatePVC
|
||||||
|
func (mr *MockApplicationCreatorMockRecorder) CreatePVC(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePVC", reflect.TypeOf((*MockApplicationCreator)(nil).CreatePVC), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePod mocks base method
|
||||||
|
func (m *MockApplicationCreator) CreatePod(arg0 context.Context, arg1 *types.CreatePodArgs) (*v1.Pod, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CreatePod", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*v1.Pod)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePod indicates an expected call of CreatePod
|
||||||
|
func (mr *MockApplicationCreatorMockRecorder) CreatePod(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePod", reflect.TypeOf((*MockApplicationCreator)(nil).CreatePod), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForPodReady mocks base method
|
||||||
|
func (m *MockApplicationCreator) WaitForPodReady(arg0 context.Context, arg1, arg2 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "WaitForPodReady", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForPodReady indicates an expected call of WaitForPodReady
|
||||||
|
func (mr *MockApplicationCreatorMockRecorder) WaitForPodReady(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForPodReady", reflect.TypeOf((*MockApplicationCreator)(nil).WaitForPodReady), arg0, arg1, arg2)
|
||||||
|
}
|
81
pkg/csi/mocks/mock_argument_validator.go
Normal file
81
pkg/csi/mocks/mock_argument_validator.go
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: ArgumentValidator)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
v1 "k8s.io/api/storage/v1"
|
||||||
|
v10 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockArgumentValidator is a mock of ArgumentValidator interface
|
||||||
|
type MockArgumentValidator struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockArgumentValidatorMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockArgumentValidatorMockRecorder is the mock recorder for MockArgumentValidator
|
||||||
|
type MockArgumentValidatorMockRecorder struct {
|
||||||
|
mock *MockArgumentValidator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockArgumentValidator creates a new mock instance
|
||||||
|
func NewMockArgumentValidator(ctrl *gomock.Controller) *MockArgumentValidator {
|
||||||
|
mock := &MockArgumentValidator{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockArgumentValidatorMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockArgumentValidator) EXPECT() *MockArgumentValidatorMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateNamespace mocks base method
|
||||||
|
func (m *MockArgumentValidator) ValidateNamespace(arg0 context.Context, arg1 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ValidateNamespace", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateNamespace indicates an expected call of ValidateNamespace
|
||||||
|
func (mr *MockArgumentValidatorMockRecorder) ValidateNamespace(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateNamespace", reflect.TypeOf((*MockArgumentValidator)(nil).ValidateNamespace), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateStorageClass mocks base method
|
||||||
|
func (m *MockArgumentValidator) ValidateStorageClass(arg0 context.Context, arg1 string) (*v1.StorageClass, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ValidateStorageClass", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(*v1.StorageClass)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateStorageClass indicates an expected call of ValidateStorageClass
|
||||||
|
func (mr *MockArgumentValidatorMockRecorder) ValidateStorageClass(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateStorageClass", reflect.TypeOf((*MockArgumentValidator)(nil).ValidateStorageClass), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateVolumeSnapshotClass mocks base method
|
||||||
|
func (m *MockArgumentValidator) ValidateVolumeSnapshotClass(arg0 context.Context, arg1 string, arg2 *v10.GroupVersionForDiscovery) (*unstructured.Unstructured, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ValidateVolumeSnapshotClass", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(*unstructured.Unstructured)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateVolumeSnapshotClass indicates an expected call of ValidateVolumeSnapshotClass
|
||||||
|
func (mr *MockArgumentValidatorMockRecorder) ValidateVolumeSnapshotClass(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateVolumeSnapshotClass", reflect.TypeOf((*MockArgumentValidator)(nil).ValidateVolumeSnapshotClass), arg0, arg1, arg2)
|
||||||
|
}
|
76
pkg/csi/mocks/mock_cleaner.go
Normal file
76
pkg/csi/mocks/mock_cleaner.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: Cleaner)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockCleaner is a mock of Cleaner interface
|
||||||
|
type MockCleaner struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockCleanerMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockCleanerMockRecorder is the mock recorder for MockCleaner
|
||||||
|
type MockCleanerMockRecorder struct {
|
||||||
|
mock *MockCleaner
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockCleaner creates a new mock instance
|
||||||
|
func NewMockCleaner(ctrl *gomock.Controller) *MockCleaner {
|
||||||
|
mock := &MockCleaner{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockCleanerMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockCleaner) EXPECT() *MockCleanerMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePVC mocks base method
|
||||||
|
func (m *MockCleaner) DeletePVC(arg0 context.Context, arg1, arg2 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "DeletePVC", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePVC indicates an expected call of DeletePVC
|
||||||
|
func (mr *MockCleanerMockRecorder) DeletePVC(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePVC", reflect.TypeOf((*MockCleaner)(nil).DeletePVC), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePod mocks base method
|
||||||
|
func (m *MockCleaner) DeletePod(arg0 context.Context, arg1, arg2 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "DeletePod", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePod indicates an expected call of DeletePod
|
||||||
|
func (mr *MockCleanerMockRecorder) DeletePod(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePod", reflect.TypeOf((*MockCleaner)(nil).DeletePod), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSnapshot mocks base method
|
||||||
|
func (m *MockCleaner) DeleteSnapshot(arg0 context.Context, arg1, arg2 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "DeleteSnapshot", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSnapshot indicates an expected call of DeleteSnapshot
|
||||||
|
func (mr *MockCleanerMockRecorder) DeleteSnapshot(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSnapshot", reflect.TypeOf((*MockCleaner)(nil).DeleteSnapshot), arg0, arg1, arg2)
|
||||||
|
}
|
48
pkg/csi/mocks/mock_data_validator.go
Normal file
48
pkg/csi/mocks/mock_data_validator.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: DataValidator)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockDataValidator is a mock of DataValidator interface
|
||||||
|
type MockDataValidator struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockDataValidatorMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockDataValidatorMockRecorder is the mock recorder for MockDataValidator
|
||||||
|
type MockDataValidatorMockRecorder struct {
|
||||||
|
mock *MockDataValidator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockDataValidator creates a new mock instance
|
||||||
|
func NewMockDataValidator(ctrl *gomock.Controller) *MockDataValidator {
|
||||||
|
mock := &MockDataValidator{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockDataValidatorMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockDataValidator) EXPECT() *MockDataValidatorMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchPodData mocks base method
|
||||||
|
func (m *MockDataValidator) FetchPodData(arg0, arg1 string) (string, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "FetchPodData", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchPodData indicates an expected call of FetchPodData
|
||||||
|
func (mr *MockDataValidatorMockRecorder) FetchPodData(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchPodData", reflect.TypeOf((*MockDataValidator)(nil).FetchPodData), arg0, arg1)
|
||||||
|
}
|
81
pkg/csi/mocks/mock_snapshot_creator.go
Normal file
81
pkg/csi/mocks/mock_snapshot_creator.go
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: SnapshotCreator)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
snapshot "github.com/kanisterio/kanister/pkg/kube/snapshot"
|
||||||
|
v1alpha1 "github.com/kanisterio/kanister/pkg/kube/snapshot/apis/v1alpha1"
|
||||||
|
types "github.com/kastenhq/kubestr/pkg/csi/types"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockSnapshotCreator is a mock of SnapshotCreator interface
|
||||||
|
type MockSnapshotCreator struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockSnapshotCreatorMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockSnapshotCreatorMockRecorder is the mock recorder for MockSnapshotCreator
|
||||||
|
type MockSnapshotCreatorMockRecorder struct {
|
||||||
|
mock *MockSnapshotCreator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockSnapshotCreator creates a new mock instance
|
||||||
|
func NewMockSnapshotCreator(ctrl *gomock.Controller) *MockSnapshotCreator {
|
||||||
|
mock := &MockSnapshotCreator{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockSnapshotCreatorMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockSnapshotCreator) EXPECT() *MockSnapshotCreatorMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFromSourceCheck mocks base method
|
||||||
|
func (m *MockSnapshotCreator) CreateFromSourceCheck(arg0 context.Context, arg1 snapshot.Snapshotter, arg2 *types.CreateFromSourceCheckArgs) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CreateFromSourceCheck", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateFromSourceCheck indicates an expected call of CreateFromSourceCheck
|
||||||
|
func (mr *MockSnapshotCreatorMockRecorder) CreateFromSourceCheck(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFromSourceCheck", reflect.TypeOf((*MockSnapshotCreator)(nil).CreateFromSourceCheck), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSnapshot mocks base method
|
||||||
|
func (m *MockSnapshotCreator) CreateSnapshot(arg0 context.Context, arg1 snapshot.Snapshotter, arg2 *types.CreateSnapshotArgs) (*v1alpha1.VolumeSnapshot, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CreateSnapshot", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(*v1alpha1.VolumeSnapshot)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSnapshot indicates an expected call of CreateSnapshot
|
||||||
|
func (mr *MockSnapshotCreatorMockRecorder) CreateSnapshot(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSnapshot", reflect.TypeOf((*MockSnapshotCreator)(nil).CreateSnapshot), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSnapshotter mocks base method
|
||||||
|
func (m *MockSnapshotCreator) 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 *MockSnapshotCreatorMockRecorder) NewSnapshotter() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewSnapshotter", reflect.TypeOf((*MockSnapshotCreator)(nil).NewSnapshotter))
|
||||||
|
}
|
124
pkg/csi/mocks/mock_snapshot_restore_stepper.go
Normal file
124
pkg/csi/mocks/mock_snapshot_restore_stepper.go
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: SnapshotRestoreStepper)
|
||||||
|
|
||||||
|
// Package mocks is a generated GoMock package.
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
v1alpha1 "github.com/kanisterio/kanister/pkg/kube/snapshot/apis/v1alpha1"
|
||||||
|
types "github.com/kastenhq/kubestr/pkg/csi/types"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockSnapshotRestoreStepper is a mock of SnapshotRestoreStepper interface
|
||||||
|
type MockSnapshotRestoreStepper struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockSnapshotRestoreStepperMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockSnapshotRestoreStepperMockRecorder is the mock recorder for MockSnapshotRestoreStepper
|
||||||
|
type MockSnapshotRestoreStepperMockRecorder struct {
|
||||||
|
mock *MockSnapshotRestoreStepper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockSnapshotRestoreStepper creates a new mock instance
|
||||||
|
func NewMockSnapshotRestoreStepper(ctrl *gomock.Controller) *MockSnapshotRestoreStepper {
|
||||||
|
mock := &MockSnapshotRestoreStepper{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockSnapshotRestoreStepperMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockSnapshotRestoreStepper) EXPECT() *MockSnapshotRestoreStepperMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup mocks base method
|
||||||
|
func (m *MockSnapshotRestoreStepper) Cleanup(arg0 context.Context, arg1 *types.CSISnapshotRestoreResults) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Cleanup", arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup indicates an expected call of Cleanup
|
||||||
|
func (mr *MockSnapshotRestoreStepperMockRecorder) Cleanup(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cleanup", reflect.TypeOf((*MockSnapshotRestoreStepper)(nil).Cleanup), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateApplication mocks base method
|
||||||
|
func (m *MockSnapshotRestoreStepper) CreateApplication(arg0 context.Context, arg1 *types.CSISnapshotRestoreArgs, arg2 string) (*v1.Pod, *v1.PersistentVolumeClaim, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CreateApplication", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(*v1.Pod)
|
||||||
|
ret1, _ := ret[1].(*v1.PersistentVolumeClaim)
|
||||||
|
ret2, _ := ret[2].(error)
|
||||||
|
return ret0, ret1, ret2
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateApplication indicates an expected call of CreateApplication
|
||||||
|
func (mr *MockSnapshotRestoreStepperMockRecorder) CreateApplication(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateApplication", reflect.TypeOf((*MockSnapshotRestoreStepper)(nil).CreateApplication), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreApplication mocks base method
|
||||||
|
func (m *MockSnapshotRestoreStepper) RestoreApplication(arg0 context.Context, arg1 *types.CSISnapshotRestoreArgs, arg2 *v1alpha1.VolumeSnapshot) (*v1.Pod, *v1.PersistentVolumeClaim, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "RestoreApplication", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(*v1.Pod)
|
||||||
|
ret1, _ := ret[1].(*v1.PersistentVolumeClaim)
|
||||||
|
ret2, _ := ret[2].(error)
|
||||||
|
return ret0, ret1, ret2
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreApplication indicates an expected call of RestoreApplication
|
||||||
|
func (mr *MockSnapshotRestoreStepperMockRecorder) RestoreApplication(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestoreApplication", reflect.TypeOf((*MockSnapshotRestoreStepper)(nil).RestoreApplication), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SnapshotApplication mocks base method
|
||||||
|
func (m *MockSnapshotRestoreStepper) SnapshotApplication(arg0 context.Context, arg1 *types.CSISnapshotRestoreArgs, arg2 *v1.PersistentVolumeClaim, arg3 string) (*v1alpha1.VolumeSnapshot, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SnapshotApplication", arg0, arg1, arg2, arg3)
|
||||||
|
ret0, _ := ret[0].(*v1alpha1.VolumeSnapshot)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// SnapshotApplication indicates an expected call of SnapshotApplication
|
||||||
|
func (mr *MockSnapshotRestoreStepperMockRecorder) SnapshotApplication(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SnapshotApplication", reflect.TypeOf((*MockSnapshotRestoreStepper)(nil).SnapshotApplication), arg0, arg1, arg2, arg3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateArgs mocks base method
|
||||||
|
func (m *MockSnapshotRestoreStepper) ValidateArgs(arg0 context.Context, arg1 *types.CSISnapshotRestoreArgs) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ValidateArgs", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateArgs indicates an expected call of ValidateArgs
|
||||||
|
func (mr *MockSnapshotRestoreStepperMockRecorder) ValidateArgs(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateArgs", reflect.TypeOf((*MockSnapshotRestoreStepper)(nil).ValidateArgs), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateData mocks base method
|
||||||
|
func (m *MockSnapshotRestoreStepper) ValidateData(arg0 context.Context, arg1 *v1.Pod, arg2 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ValidateData", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateData indicates an expected call of ValidateData
|
||||||
|
func (mr *MockSnapshotRestoreStepperMockRecorder) ValidateData(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateData", reflect.TypeOf((*MockSnapshotRestoreStepper)(nil).ValidateData), arg0, arg1, arg2)
|
||||||
|
}
|
627
pkg/csi/snapshot_restore.go
Normal file
627
pkg/csi/snapshot_restore.go
Normal file
|
@ -0,0 +1,627 @@
|
||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
kankube "github.com/kanisterio/kanister/pkg/kube"
|
||||||
|
kansnapshot "github.com/kanisterio/kanister/pkg/kube/snapshot"
|
||||||
|
"github.com/kanisterio/kanister/pkg/kube/snapshot/apis/v1alpha1"
|
||||||
|
"github.com/kastenhq/kubestr/pkg/csi/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
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"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/client-go/dynamic"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SnapGroupName describes the snapshot group name
|
||||||
|
SnapGroupName = "snapshot.storage.k8s.io"
|
||||||
|
// VolumeSnapshotClassResourcePlural describes volume snapshot classses
|
||||||
|
VolumeSnapshotClassResourcePlural = "volumesnapshotclasses"
|
||||||
|
// VolSnapClassAlphaDriverKey describes alpha driver key
|
||||||
|
VolSnapClassAlphaDriverKey = "snapshotter"
|
||||||
|
// VolSnapClassBetaDriverKey describes beta driver key
|
||||||
|
VolSnapClassBetaDriverKey = "driver"
|
||||||
|
alphaVersion = "snapshot.storage.k8s.io/v1alpha1"
|
||||||
|
betaVersion = "snapshot.storage.k8s.io/v1beta1"
|
||||||
|
originalPVCGenerateName = "kubestr-csi-original-pvc"
|
||||||
|
originalPodGenerateName = "kubestr-csi-original-pod"
|
||||||
|
clonedPVCGenerateName = "kubestr-csi-cloned-pvc"
|
||||||
|
clonedPodGenerateName = "kubestr-csi-cloned-pod"
|
||||||
|
createdByLabel = "created-by-kubestr-csi"
|
||||||
|
DefaultPodImage = "ghcr.io/kastenhq/kubestr:latest"
|
||||||
|
clonePrefix = "kubestr-clone-"
|
||||||
|
snapshotPrefix = "kubestr-snapshot-"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SnapshotRestoreRunner struct {
|
||||||
|
KubeCli kubernetes.Interface
|
||||||
|
DynCli dynamic.Interface
|
||||||
|
srSteps SnapshotRestoreStepper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SnapshotRestoreRunner) RunSnapshotRestore(ctx context.Context, args *types.CSISnapshotRestoreArgs) (*types.CSISnapshotRestoreResults, error) {
|
||||||
|
r.srSteps = &snapshotRestoreSteps{
|
||||||
|
validateOps: &validateOperations{
|
||||||
|
kubeCli: r.KubeCli,
|
||||||
|
dynCli: r.DynCli,
|
||||||
|
},
|
||||||
|
versionFetchOps: &apiVersionFetch{
|
||||||
|
kubeCli: r.KubeCli,
|
||||||
|
},
|
||||||
|
createAppOps: &applicationCreate{
|
||||||
|
kubeCli: r.KubeCli,
|
||||||
|
},
|
||||||
|
dataValidatorOps: &validateData{
|
||||||
|
kubeCli: r.KubeCli,
|
||||||
|
},
|
||||||
|
snapshotCreateOps: &snapshotCreate{
|
||||||
|
kubeCli: r.KubeCli,
|
||||||
|
dynCli: r.DynCli,
|
||||||
|
},
|
||||||
|
cleanerOps: &cleanse{
|
||||||
|
kubeCli: r.KubeCli,
|
||||||
|
dynCli: r.DynCli,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return r.RunSnapshotRestoreHelper(ctx, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SnapshotRestoreRunner) RunSnapshotRestoreHelper(ctx context.Context, args *types.CSISnapshotRestoreArgs) (*types.CSISnapshotRestoreResults, error) {
|
||||||
|
results := &types.CSISnapshotRestoreResults{}
|
||||||
|
var err error
|
||||||
|
if r.KubeCli == nil || r.DynCli == nil {
|
||||||
|
return results, fmt.Errorf("cli uninitialized")
|
||||||
|
}
|
||||||
|
if err := r.srSteps.ValidateArgs(ctx, args); err != nil {
|
||||||
|
return results, errors.Wrap(err, "Failed to validate arguments.")
|
||||||
|
}
|
||||||
|
data := time.Now().Format("20060102150405")
|
||||||
|
|
||||||
|
fmt.Println("Creating application")
|
||||||
|
results.OriginalPod, results.OriginalPVC, err = r.srSteps.CreateApplication(ctx, args, data)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if results.OriginalPod != nil && results.OriginalPVC != nil {
|
||||||
|
fmt.Printf(" -> Created pod (%s) and pvc (%s)\n", results.OriginalPod.Name, results.OriginalPVC.Name)
|
||||||
|
}
|
||||||
|
err = r.srSteps.ValidateData(ctx, results.OriginalPod, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
snapName := snapshotPrefix + data
|
||||||
|
if err == nil {
|
||||||
|
fmt.Println("Taking a snapshot")
|
||||||
|
results.Snapshot, err = r.srSteps.SnapshotApplication(ctx, args, results.OriginalPVC, snapName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if results.Snapshot != nil {
|
||||||
|
fmt.Printf(" -> Created snapshot (%s)\n", results.Snapshot.Name)
|
||||||
|
}
|
||||||
|
fmt.Println("Restoring application")
|
||||||
|
results.ClonedPod, results.ClonedPVC, err = r.srSteps.RestoreApplication(ctx, args, results.Snapshot)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if results.ClonedPod != nil && results.ClonedPVC != nil {
|
||||||
|
fmt.Printf(" -> Restored pod (%s) and pvc (%s)\n", results.ClonedPod.Name, results.ClonedPVC.Name)
|
||||||
|
}
|
||||||
|
err = r.srSteps.ValidateData(ctx, results.ClonedPod, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.Cleanup {
|
||||||
|
fmt.Println("Cleaning up resources")
|
||||||
|
r.srSteps.Cleanup(ctx, results)
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=mocks/mock_snapshot_restore_stepper.go -package=mocks . SnapshotRestoreStepper
|
||||||
|
type SnapshotRestoreStepper interface {
|
||||||
|
ValidateArgs(ctx context.Context, args *types.CSISnapshotRestoreArgs) error
|
||||||
|
CreateApplication(ctx context.Context, args *types.CSISnapshotRestoreArgs, data string) (*v1.Pod, *v1.PersistentVolumeClaim, error)
|
||||||
|
ValidateData(ctx context.Context, pod *v1.Pod, data string) error
|
||||||
|
SnapshotApplication(ctx context.Context, args *types.CSISnapshotRestoreArgs, pvc *v1.PersistentVolumeClaim, snapshotName string) (*v1alpha1.VolumeSnapshot, error)
|
||||||
|
RestoreApplication(ctx context.Context, args *types.CSISnapshotRestoreArgs, snapshot *v1alpha1.VolumeSnapshot) (*v1.Pod, *v1.PersistentVolumeClaim, error)
|
||||||
|
Cleanup(ctx context.Context, results *types.CSISnapshotRestoreResults)
|
||||||
|
}
|
||||||
|
|
||||||
|
type snapshotRestoreSteps struct {
|
||||||
|
validateOps ArgumentValidator
|
||||||
|
versionFetchOps ApiVersionFetcher
|
||||||
|
createAppOps ApplicationCreator
|
||||||
|
dataValidatorOps DataValidator
|
||||||
|
snapshotCreateOps SnapshotCreator
|
||||||
|
cleanerOps Cleaner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotRestoreSteps) ValidateArgs(ctx context.Context, args *types.CSISnapshotRestoreArgs) error {
|
||||||
|
if err := args.Validate(); err != nil {
|
||||||
|
return errors.Wrap(err, "Failed to validate input arguments")
|
||||||
|
}
|
||||||
|
if err := s.validateOps.ValidateNamespace(ctx, args.Namespace); err != nil {
|
||||||
|
return errors.Wrap(err, "Failed to validate Namespace")
|
||||||
|
}
|
||||||
|
sc, err := s.validateOps.ValidateStorageClass(ctx, args.StorageClass)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Failed to validate Storageclass")
|
||||||
|
}
|
||||||
|
|
||||||
|
groupVersion, err := s.versionFetchOps.GetCSISnapshotGroupVersion()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Failed to fetch groupVersion")
|
||||||
|
}
|
||||||
|
|
||||||
|
uVSC, err := s.validateOps.ValidateVolumeSnapshotClass(ctx, args.VolumeSnapshotClass, groupVersion)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Failed to validate VolumeSnapshotClass")
|
||||||
|
}
|
||||||
|
|
||||||
|
vscDriver := getDriverNameFromUVSC(*uVSC, groupVersion.GroupVersion)
|
||||||
|
if sc.Provisioner != vscDriver {
|
||||||
|
return fmt.Errorf("StorageClass provisioner (%s) and VolumeSnapshotClass driver (%s) are different.", sc.Provisioner, vscDriver)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotRestoreSteps) CreateApplication(ctx context.Context, args *types.CSISnapshotRestoreArgs, genString string) (*v1.Pod, *v1.PersistentVolumeClaim, error) {
|
||||||
|
pvcArgs := &types.CreatePVCArgs{
|
||||||
|
GenerateName: originalPVCGenerateName,
|
||||||
|
StorageClass: args.StorageClass,
|
||||||
|
Namespace: args.Namespace,
|
||||||
|
}
|
||||||
|
pvc, err := s.createAppOps.CreatePVC(ctx, pvcArgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrap(err, "Failed to create PVC")
|
||||||
|
}
|
||||||
|
podArgs := &types.CreatePodArgs{
|
||||||
|
GenerateName: originalPodGenerateName,
|
||||||
|
PVCName: pvc.Name,
|
||||||
|
Namespace: args.Namespace,
|
||||||
|
Cmd: fmt.Sprintf("echo '%s' >> /data/out.txt; sync; tail -f /dev/null", genString),
|
||||||
|
RunAsUser: args.RunAsUser,
|
||||||
|
ContainerImage: args.ContainerImage,
|
||||||
|
}
|
||||||
|
pod, err := s.createAppOps.CreatePod(ctx, podArgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, pvc, errors.Wrap(err, "Failed to create 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 *snapshotRestoreSteps) ValidateData(ctx context.Context, pod *v1.Pod, data string) error {
|
||||||
|
podData, err := s.dataValidatorOps.FetchPodData(pod.Name, pod.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Failed to fetch data from pod")
|
||||||
|
}
|
||||||
|
if podData != data {
|
||||||
|
return fmt.Errorf("string didn't match (%s , %s)", podData, data)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotRestoreSteps) SnapshotApplication(ctx context.Context, args *types.CSISnapshotRestoreArgs, pvc *v1.PersistentVolumeClaim, snapshotName string) (*v1alpha1.VolumeSnapshot, error) {
|
||||||
|
snapshotter, err := s.snapshotCreateOps.NewSnapshotter()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Failed to load snapshotter")
|
||||||
|
}
|
||||||
|
createSnapshotArgs := &types.CreateSnapshotArgs{
|
||||||
|
Namespace: args.Namespace,
|
||||||
|
PVCName: pvc.Name,
|
||||||
|
VolumeSnapshotClass: args.VolumeSnapshotClass,
|
||||||
|
SnapshotName: snapshotName,
|
||||||
|
}
|
||||||
|
snapshot, err := s.snapshotCreateOps.CreateSnapshot(ctx, snapshotter, createSnapshotArgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Failed to create Snapshot")
|
||||||
|
}
|
||||||
|
if !args.SkipCFSCheck {
|
||||||
|
cfsArgs := &types.CreateFromSourceCheckArgs{
|
||||||
|
VolumeSnapshotClass: args.VolumeSnapshotClass,
|
||||||
|
SnapshotName: snapshot.Name,
|
||||||
|
Namespace: args.Namespace,
|
||||||
|
}
|
||||||
|
if err = s.snapshotCreateOps.CreateFromSourceCheck(ctx, snapshotter, cfsArgs); err != nil {
|
||||||
|
return snapshot, errors.Wrap(err, "Failed to create duplicate snapshot from source. To skip check use '--skipcfs=true' option.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return snapshot, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *snapshotRestoreSteps) RestoreApplication(ctx context.Context, args *types.CSISnapshotRestoreArgs, snapshot *v1alpha1.VolumeSnapshot) (*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: args.StorageClass,
|
||||||
|
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,
|
||||||
|
Cmd: "tail -f /dev/null",
|
||||||
|
RunAsUser: args.RunAsUser,
|
||||||
|
ContainerImage: args.ContainerImage,
|
||||||
|
}
|
||||||
|
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 *snapshotRestoreSteps) Cleanup(ctx context.Context, results *types.CSISnapshotRestoreResults) {
|
||||||
|
if results == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if results.OriginalPVC != nil {
|
||||||
|
_ = s.cleanerOps.DeletePVC(ctx, results.OriginalPVC.Name, results.OriginalPVC.Namespace)
|
||||||
|
}
|
||||||
|
if results.OriginalPod != nil {
|
||||||
|
_ = s.cleanerOps.DeletePod(ctx, results.OriginalPod.Name, results.OriginalPod.Namespace)
|
||||||
|
}
|
||||||
|
if results.ClonedPVC != nil {
|
||||||
|
_ = s.cleanerOps.DeletePVC(ctx, results.ClonedPVC.Name, results.ClonedPVC.Namespace)
|
||||||
|
}
|
||||||
|
if results.ClonedPod != nil {
|
||||||
|
_ = s.cleanerOps.DeletePod(ctx, results.ClonedPod.Name, results.ClonedPod.Namespace)
|
||||||
|
}
|
||||||
|
if results.Snapshot != nil {
|
||||||
|
_ = s.cleanerOps.DeleteSnapshot(ctx, results.Snapshot.Name, results.Snapshot.Namespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=mocks/mock_argument_validator.go -package=mocks . ArgumentValidator
|
||||||
|
type ArgumentValidator interface {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
type validateOperations struct {
|
||||||
|
kubeCli kubernetes.Interface
|
||||||
|
dynCli dynamic.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *validateOperations) ValidateNamespace(ctx context.Context, namespace string) error {
|
||||||
|
if o.kubeCli == nil {
|
||||||
|
return fmt.Errorf("kubeCli not initialized")
|
||||||
|
}
|
||||||
|
_, err := o.kubeCli.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *validateOperations) ValidateStorageClass(ctx context.Context, storageClass string) (*sv1.StorageClass, error) {
|
||||||
|
if o.kubeCli == nil {
|
||||||
|
return nil, fmt.Errorf("kubeCli not initialized")
|
||||||
|
}
|
||||||
|
return o.kubeCli.StorageV1().StorageClasses().Get(ctx, storageClass, metav1.GetOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *validateOperations) ValidateVolumeSnapshotClass(ctx context.Context, volumeSnapshotClass string, groupVersion *metav1.GroupVersionForDiscovery) (*unstructured.Unstructured, error) {
|
||||||
|
if o.dynCli == nil {
|
||||||
|
return nil, fmt.Errorf("dynCli not initialized")
|
||||||
|
}
|
||||||
|
VolSnapClassGVR := schema.GroupVersionResource{Group: SnapGroupName, Version: groupVersion.Version, Resource: VolumeSnapshotClassResourcePlural}
|
||||||
|
return o.dynCli.Resource(VolSnapClassGVR).Get(ctx, volumeSnapshotClass, metav1.GetOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=mocks/mock_application_creator.go -package=mocks . ApplicationCreator
|
||||||
|
type ApplicationCreator interface {
|
||||||
|
CreatePVC(ctx context.Context, args *types.CreatePVCArgs) (*v1.PersistentVolumeClaim, error)
|
||||||
|
CreatePod(ctx context.Context, args *types.CreatePodArgs) (*v1.Pod, error)
|
||||||
|
WaitForPodReady(ctx context.Context, namespace string, podName string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type applicationCreate struct {
|
||||||
|
kubeCli kubernetes.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *applicationCreate) CreatePVC(ctx context.Context, args *types.CreatePVCArgs) (*v1.PersistentVolumeClaim, error) {
|
||||||
|
if c.kubeCli == nil {
|
||||||
|
return nil, fmt.Errorf("kubeCli not initialized")
|
||||||
|
}
|
||||||
|
if err := args.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pvc := &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
GenerateName: args.GenerateName,
|
||||||
|
Namespace: args.Namespace,
|
||||||
|
Labels: map[string]string{
|
||||||
|
createdByLabel: "yes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: v1.PersistentVolumeClaimSpec{
|
||||||
|
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
|
||||||
|
StorageClassName: &args.StorageClass,
|
||||||
|
Resources: v1.ResourceRequirements{
|
||||||
|
Requests: v1.ResourceList{
|
||||||
|
v1.ResourceStorage: resource.MustParse("1Gi"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.DataSource != nil {
|
||||||
|
pvc.Spec.DataSource = args.DataSource
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.RestoreSize != nil && !args.RestoreSize.IsZero() {
|
||||||
|
pvc.Spec.Resources.Requests[v1.ResourceStorage] = *args.RestoreSize
|
||||||
|
}
|
||||||
|
|
||||||
|
pvcRes, err := c.kubeCli.CoreV1().PersistentVolumeClaims(args.Namespace).Create(ctx, pvc, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return pvc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pvcRes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *applicationCreate) CreatePod(ctx context.Context, args *types.CreatePodArgs) (*v1.Pod, error) {
|
||||||
|
if c.kubeCli == nil {
|
||||||
|
return nil, fmt.Errorf("kubeCli not initialized")
|
||||||
|
}
|
||||||
|
if err := args.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if args.ContainerImage == "" {
|
||||||
|
args.ContainerImage = DefaultPodImage
|
||||||
|
}
|
||||||
|
|
||||||
|
pod := &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
GenerateName: args.GenerateName,
|
||||||
|
Namespace: args.Namespace,
|
||||||
|
Labels: map[string]string{
|
||||||
|
createdByLabel: "yes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{{
|
||||||
|
Name: args.GenerateName,
|
||||||
|
Image: args.ContainerImage,
|
||||||
|
Command: []string{"/bin/sh"},
|
||||||
|
Args: []string{"-c", args.Cmd},
|
||||||
|
VolumeMounts: []v1.VolumeMount{{
|
||||||
|
Name: "persistent-storage",
|
||||||
|
MountPath: "/data",
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
Volumes: []v1.Volume{{
|
||||||
|
Name: "persistent-storage",
|
||||||
|
VolumeSource: v1.VolumeSource{
|
||||||
|
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
||||||
|
ClaimName: args.PVCName,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.RunAsUser > 0 {
|
||||||
|
pod.Spec.SecurityContext = &v1.PodSecurityContext{
|
||||||
|
RunAsUser: &args.RunAsUser,
|
||||||
|
FSGroup: &args.RunAsUser,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
podRes, err := c.kubeCli.CoreV1().Pods(args.Namespace).Create(ctx, pod, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return pod, err
|
||||||
|
}
|
||||||
|
return podRes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *applicationCreate) WaitForPodReady(ctx context.Context, namespace string, podName string) error {
|
||||||
|
if c.kubeCli == nil {
|
||||||
|
return fmt.Errorf("kubeCli not initialized")
|
||||||
|
}
|
||||||
|
err := kankube.WaitForPodReady(ctx, c.kubeCli, namespace, podName)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=mocks/mock_snapshot_creator.go -package=mocks . SnapshotCreator
|
||||||
|
type SnapshotCreator interface {
|
||||||
|
NewSnapshotter() (kansnapshot.Snapshotter, error)
|
||||||
|
CreateSnapshot(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.CreateSnapshotArgs) (*v1alpha1.VolumeSnapshot, error)
|
||||||
|
CreateFromSourceCheck(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.CreateFromSourceCheckArgs) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type snapshotCreate struct {
|
||||||
|
kubeCli kubernetes.Interface
|
||||||
|
dynCli dynamic.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *snapshotCreate) NewSnapshotter() (kansnapshot.Snapshotter, error) {
|
||||||
|
if c.kubeCli == nil {
|
||||||
|
return nil, fmt.Errorf("kubeCli not initialized")
|
||||||
|
}
|
||||||
|
if c.dynCli == nil {
|
||||||
|
return nil, fmt.Errorf("dynCli not initialized")
|
||||||
|
}
|
||||||
|
return kansnapshot.NewSnapshotter(c.kubeCli, c.dynCli)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *snapshotCreate) CreateSnapshot(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.CreateSnapshotArgs) (*v1alpha1.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
|
||||||
|
}
|
||||||
|
err := snapshotter.Create(ctx, args.SnapshotName, args.Namespace, args.PVCName, &args.VolumeSnapshotClass, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "CSI Driver failed to create snapshot for PVC (%s) in Namspace (%s)", args.PVCName, args.Namespace)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *snapshotCreate) CreateFromSourceCheck(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.CreateFromSourceCheckArgs) error {
|
||||||
|
if c.dynCli == nil {
|
||||||
|
return fmt.Errorf("dynCli not initialized")
|
||||||
|
}
|
||||||
|
if snapshotter == nil || args == nil {
|
||||||
|
return fmt.Errorf("snapshotter or args are nil")
|
||||||
|
}
|
||||||
|
if err := args.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
targetSnapClassName := clonePrefix + args.VolumeSnapshotClass
|
||||||
|
err := snapshotter.CloneVolumeSnapshotClass(args.VolumeSnapshotClass, targetSnapClassName, kansnapshot.DeletionPolicyRetain, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed to create a VolumeSnapshotClass to use to restore the snapshot")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = c.dynCli.Resource(v1alpha1.VolSnapClassGVR).Delete(ctx, targetSnapClassName, metav1.DeleteOptions{})
|
||||||
|
}()
|
||||||
|
|
||||||
|
snapSrc, err := snapshotter.GetSource(ctx, args.SnapshotName, args.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed to get source snapshot source (%s)", args.SnapshotName)
|
||||||
|
}
|
||||||
|
snapshotCFSCloneName := clonePrefix + args.SnapshotName
|
||||||
|
// test the CreateFromSource API
|
||||||
|
defer func() {
|
||||||
|
_, _ = snapshotter.Delete(context.Background(), snapshotCFSCloneName, args.Namespace)
|
||||||
|
}()
|
||||||
|
src := &kansnapshot.Source{
|
||||||
|
Handle: snapSrc.Handle,
|
||||||
|
Driver: snapSrc.Driver,
|
||||||
|
VolumeSnapshotClassName: targetSnapClassName,
|
||||||
|
}
|
||||||
|
err = snapshotter.CreateFromSource(ctx, src, snapshotCFSCloneName, args.Namespace, true)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed to clone snapshot from source (%s)", snapshotCFSCloneName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=mocks/mock_cleaner.go -package=mocks . Cleaner
|
||||||
|
type Cleaner interface {
|
||||||
|
DeletePVC(ctx context.Context, pvcName string, namespace string) error
|
||||||
|
DeletePod(ctx context.Context, podName string, namespace string) error
|
||||||
|
DeleteSnapshot(ctx context.Context, snapshotName string, namespace string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type cleanse struct {
|
||||||
|
kubeCli kubernetes.Interface
|
||||||
|
dynCli dynamic.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cleanse) DeletePVC(ctx context.Context, pvcName string, namespace string) error {
|
||||||
|
if c.kubeCli == nil {
|
||||||
|
return fmt.Errorf("kubeCli not initialized")
|
||||||
|
}
|
||||||
|
return c.kubeCli.CoreV1().PersistentVolumeClaims(namespace).Delete(ctx, pvcName, metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cleanse) DeletePod(ctx context.Context, podName string, namespace string) error {
|
||||||
|
if c.kubeCli == nil {
|
||||||
|
return fmt.Errorf("kubeCli not initialized")
|
||||||
|
}
|
||||||
|
return c.kubeCli.CoreV1().Pods(namespace).Delete(ctx, podName, metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cleanse) DeleteSnapshot(ctx context.Context, snapshotName string, namespace string) error {
|
||||||
|
if c.dynCli == nil {
|
||||||
|
return fmt.Errorf("dynCli not initialized")
|
||||||
|
}
|
||||||
|
return c.dynCli.Resource(v1alpha1.VolSnapGVR).Namespace(namespace).Delete(ctx, snapshotName, metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=mocks/mock_api_version_fetcher.go -package=mocks . ApiVersionFetcher
|
||||||
|
type ApiVersionFetcher interface {
|
||||||
|
GetCSISnapshotGroupVersion() (*metav1.GroupVersionForDiscovery, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiVersionFetch struct {
|
||||||
|
kubeCli kubernetes.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *apiVersionFetch) GetCSISnapshotGroupVersion() (*metav1.GroupVersionForDiscovery, error) {
|
||||||
|
if p.kubeCli == nil {
|
||||||
|
return nil, fmt.Errorf("kubeCli not initialized")
|
||||||
|
}
|
||||||
|
groups, _, err := p.kubeCli.Discovery().ServerGroupsAndResources()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, group := range groups {
|
||||||
|
if group.Name == SnapGroupName {
|
||||||
|
return &group.PreferredVersion, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Snapshot API group not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate mockgen -destination=mocks/mock_data_validator.go -package=mocks . DataValidator
|
||||||
|
type DataValidator interface {
|
||||||
|
FetchPodData(podName string, podNamespace string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type validateData struct {
|
||||||
|
kubeCli kubernetes.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *validateData) FetchPodData(podName string, podNamespace string) (string, error) {
|
||||||
|
if p.kubeCli == nil {
|
||||||
|
return "", fmt.Errorf("kubeCli not initialized")
|
||||||
|
}
|
||||||
|
stdout, _, err := kankube.Exec(p.kubeCli, podNamespace, podName, "", []string{"sh", "-c", "cat /data/out.txt"}, nil)
|
||||||
|
return stdout, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDriverNameFromUVSC(vsc unstructured.Unstructured, version string) string {
|
||||||
|
var driverName interface{}
|
||||||
|
var ok bool
|
||||||
|
switch version {
|
||||||
|
case alphaVersion:
|
||||||
|
driverName, ok = vsc.Object[VolSnapClassAlphaDriverKey]
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
case betaVersion:
|
||||||
|
driverName, ok = vsc.Object[VolSnapClassBetaDriverKey]
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
driver, ok := driverName.(string)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return driver
|
||||||
|
}
|
943
pkg/csi/snapshot_restore_ops_test.go
Normal file
943
pkg/csi/snapshot_restore_ops_test.go
Normal file
|
@ -0,0 +1,943 @@
|
||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
kansnapshot "github.com/kanisterio/kanister/pkg/kube/snapshot"
|
||||||
|
"github.com/kanisterio/kanister/pkg/kube/snapshot/apis/v1alpha1"
|
||||||
|
"github.com/kastenhq/kubestr/pkg/csi/types"
|
||||||
|
. "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"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestGetDriverNameFromUVSC(c *C) {
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
vsc unstructured.Unstructured
|
||||||
|
version string
|
||||||
|
expOut string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
vsc: unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
VolSnapClassAlphaDriverKey: "p2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
version: alphaVersion,
|
||||||
|
expOut: "p2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
vsc: unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{},
|
||||||
|
},
|
||||||
|
version: alphaVersion,
|
||||||
|
expOut: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
vsc: unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
VolSnapClassBetaDriverKey: "p2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
version: betaVersion,
|
||||||
|
expOut: "p2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
vsc: unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{},
|
||||||
|
},
|
||||||
|
version: betaVersion,
|
||||||
|
expOut: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
vsc: unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
VolSnapClassBetaDriverKey: map[string]string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
version: betaVersion,
|
||||||
|
expOut: "",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
driverName := getDriverNameFromUVSC(tc.vsc, tc.version)
|
||||||
|
c.Assert(driverName, Equals, tc.expOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestGetCSISnapshotGroupVersion(c *C) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
cli kubernetes.Interface
|
||||||
|
resources []*metav1.APIResourceList
|
||||||
|
errChecker Checker
|
||||||
|
gvChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
resources: []*metav1.APIResourceList{
|
||||||
|
{
|
||||||
|
GroupVersion: "/////",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
gvChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
resources: []*metav1.APIResourceList{
|
||||||
|
{
|
||||||
|
GroupVersion: "snapshot.storage.k8s.io/v1alpha1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
gvChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
resources: []*metav1.APIResourceList{
|
||||||
|
{
|
||||||
|
GroupVersion: "notrbac.authorization.k8s.io/v1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
gvChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: nil,
|
||||||
|
resources: nil,
|
||||||
|
errChecker: NotNil,
|
||||||
|
gvChecker: IsNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
cli := tc.cli
|
||||||
|
if cli != nil {
|
||||||
|
cli.Discovery().(*discoveryfake.FakeDiscovery).Resources = tc.resources
|
||||||
|
}
|
||||||
|
p := &apiVersionFetch{kubeCli: cli}
|
||||||
|
gv, err := p.GetCSISnapshotGroupVersion()
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
c.Check(gv, tc.gvChecker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestValidateNamespace(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
ops := &validateOperations{
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
}
|
||||||
|
err := ops.ValidateNamespace(ctx, "ns")
|
||||||
|
c.Check(err, NotNil)
|
||||||
|
|
||||||
|
ops = &validateOperations{
|
||||||
|
kubeCli: fake.NewSimpleClientset(&v1.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "ns",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
err = ops.ValidateNamespace(ctx, "ns")
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
|
||||||
|
ops = &validateOperations{
|
||||||
|
kubeCli: nil,
|
||||||
|
}
|
||||||
|
err = ops.ValidateNamespace(ctx, "ns")
|
||||||
|
c.Check(err, NotNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestValidateStorageClass(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
ops := &validateOperations{
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
}
|
||||||
|
sc, err := ops.ValidateStorageClass(ctx, "sc")
|
||||||
|
c.Check(err, NotNil)
|
||||||
|
c.Check(sc, IsNil)
|
||||||
|
|
||||||
|
ops = &validateOperations{
|
||||||
|
kubeCli: fake.NewSimpleClientset(&sv1.StorageClass{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "sc",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
sc, err = ops.ValidateStorageClass(ctx, "sc")
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
c.Check(sc, NotNil)
|
||||||
|
|
||||||
|
ops = &validateOperations{
|
||||||
|
kubeCli: nil,
|
||||||
|
}
|
||||||
|
sc, err = ops.ValidateStorageClass(ctx, "sc")
|
||||||
|
c.Check(err, NotNil)
|
||||||
|
c.Check(sc, IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestValidateVolumeSnapshotClass(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
ops := &validateOperations{
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
}
|
||||||
|
uVSC, err := ops.ValidateVolumeSnapshotClass(ctx, "vsc", &metav1.GroupVersionForDiscovery{GroupVersion: alphaVersion})
|
||||||
|
c.Check(err, NotNil)
|
||||||
|
c.Check(uVSC, IsNil)
|
||||||
|
|
||||||
|
ops = &validateOperations{
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(
|
||||||
|
runtime.NewScheme(),
|
||||||
|
&unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": fmt.Sprintf("%s/%s", v1alpha1.GroupName, v1alpha1.Version),
|
||||||
|
"kind": "VolumeSnapshotClass",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "vsc",
|
||||||
|
},
|
||||||
|
"snapshotter": "somesnapshotter",
|
||||||
|
"deletionPolicy": "Delete",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
uVSC, err = ops.ValidateVolumeSnapshotClass(ctx, "vsc", &metav1.GroupVersionForDiscovery{Version: v1alpha1.Version})
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
c.Check(uVSC, NotNil)
|
||||||
|
|
||||||
|
ops = &validateOperations{
|
||||||
|
dynCli: nil,
|
||||||
|
}
|
||||||
|
uVSC, err = ops.ValidateVolumeSnapshotClass(ctx, "vsc", &metav1.GroupVersionForDiscovery{Version: v1alpha1.Version})
|
||||||
|
c.Check(err, NotNil)
|
||||||
|
c.Check(uVSC, IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestCreatePVC(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
resourceQuantity := resource.MustParse("1Gi")
|
||||||
|
for _, tc := range []struct {
|
||||||
|
cli kubernetes.Interface
|
||||||
|
args *types.CreatePVCArgs
|
||||||
|
failCreates bool
|
||||||
|
errChecker Checker
|
||||||
|
pvcChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePVCArgs{
|
||||||
|
GenerateName: "genName",
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
DataSource: &v1.TypedLocalObjectReference{
|
||||||
|
Name: "ds",
|
||||||
|
},
|
||||||
|
RestoreSize: &resourceQuantity,
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
pvcChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePVCArgs{
|
||||||
|
GenerateName: "genName",
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
DataSource: &v1.TypedLocalObjectReference{
|
||||||
|
Name: "ds",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
pvcChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePVCArgs{
|
||||||
|
GenerateName: "genName",
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
pvcChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePVCArgs{
|
||||||
|
GenerateName: "genName",
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
failCreates: true,
|
||||||
|
errChecker: NotNil,
|
||||||
|
pvcChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePVCArgs{
|
||||||
|
GenerateName: "",
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
pvcChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePVCArgs{
|
||||||
|
GenerateName: "something",
|
||||||
|
StorageClass: "",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
pvcChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePVCArgs{
|
||||||
|
GenerateName: "Something",
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
pvcChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: nil,
|
||||||
|
args: &types.CreatePVCArgs{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
pvcChecker: IsNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
creator := &applicationCreate{kubeCli: tc.cli}
|
||||||
|
if tc.failCreates {
|
||||||
|
creator.kubeCli.(*fake.Clientset).Fake.PrependReactor("create", "persistentvolumeclaims", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||||
|
return true, nil, errors.New("Error creating object")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pvc, err := creator.CreatePVC(ctx, tc.args)
|
||||||
|
c.Check(pvc, tc.pvcChecker)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
if pvc != nil && err == nil {
|
||||||
|
_, ok := pvc.Labels[createdByLabel]
|
||||||
|
c.Assert(ok, Equals, true)
|
||||||
|
c.Assert(pvc.GenerateName, Equals, tc.args.GenerateName)
|
||||||
|
c.Assert(pvc.Namespace, Equals, tc.args.Namespace)
|
||||||
|
c.Assert(pvc.Spec.AccessModes, DeepEquals, []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce})
|
||||||
|
c.Assert(*pvc.Spec.StorageClassName, Equals, tc.args.StorageClass)
|
||||||
|
c.Assert(pvc.Spec.DataSource, DeepEquals, tc.args.DataSource)
|
||||||
|
if tc.args.RestoreSize != nil {
|
||||||
|
c.Assert(pvc.Spec.Resources, DeepEquals, v1.ResourceRequirements{
|
||||||
|
Requests: v1.ResourceList{
|
||||||
|
v1.ResourceStorage: *tc.args.RestoreSize,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
c.Assert(pvc.Spec.Resources, DeepEquals, v1.ResourceRequirements{
|
||||||
|
Requests: v1.ResourceList{
|
||||||
|
v1.ResourceStorage: resource.MustParse("1Gi"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestCreatePod(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
for _, tc := range []struct {
|
||||||
|
cli kubernetes.Interface
|
||||||
|
args *types.CreatePodArgs
|
||||||
|
failCreates bool
|
||||||
|
errChecker Checker
|
||||||
|
podChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePodArgs{
|
||||||
|
GenerateName: "name",
|
||||||
|
PVCName: "pvcname",
|
||||||
|
Namespace: "ns",
|
||||||
|
Cmd: "somecommand",
|
||||||
|
RunAsUser: 1000,
|
||||||
|
ContainerImage: "containerimage",
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
podChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePodArgs{
|
||||||
|
GenerateName: "name",
|
||||||
|
PVCName: "pvcname",
|
||||||
|
Namespace: "ns",
|
||||||
|
Cmd: "somecommand",
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
podChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePodArgs{
|
||||||
|
GenerateName: "name",
|
||||||
|
PVCName: "pvcname",
|
||||||
|
Namespace: "ns",
|
||||||
|
Cmd: "somecommand",
|
||||||
|
},
|
||||||
|
failCreates: true,
|
||||||
|
errChecker: NotNil,
|
||||||
|
podChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePodArgs{
|
||||||
|
GenerateName: "",
|
||||||
|
PVCName: "pvcname",
|
||||||
|
Namespace: "ns",
|
||||||
|
Cmd: "somecommand",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
podChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePodArgs{
|
||||||
|
GenerateName: "name",
|
||||||
|
PVCName: "",
|
||||||
|
Namespace: "ns",
|
||||||
|
Cmd: "somecommand",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
podChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePodArgs{
|
||||||
|
GenerateName: "name",
|
||||||
|
PVCName: "pvcname",
|
||||||
|
Namespace: "",
|
||||||
|
Cmd: "somecommand",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
podChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
args: &types.CreatePodArgs{
|
||||||
|
GenerateName: "name",
|
||||||
|
PVCName: "pvcname",
|
||||||
|
Namespace: "ns",
|
||||||
|
Cmd: "",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
podChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: nil,
|
||||||
|
args: &types.CreatePodArgs{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
podChecker: IsNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
creator := &applicationCreate{kubeCli: tc.cli}
|
||||||
|
if tc.failCreates {
|
||||||
|
creator.kubeCli.(*fake.Clientset).Fake.PrependReactor("create", "pods", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||||
|
return true, nil, errors.New("Error creating object")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pod, err := creator.CreatePod(ctx, tc.args)
|
||||||
|
c.Check(pod, tc.podChecker)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
if pod != nil && err == nil {
|
||||||
|
_, ok := pod.Labels[createdByLabel]
|
||||||
|
c.Assert(ok, Equals, true)
|
||||||
|
c.Assert(pod.GenerateName, Equals, tc.args.GenerateName)
|
||||||
|
c.Assert(pod.Namespace, Equals, tc.args.Namespace)
|
||||||
|
c.Assert(len(pod.Spec.Containers), Equals, 1)
|
||||||
|
c.Assert(pod.Spec.Containers[0].Name, Equals, tc.args.GenerateName)
|
||||||
|
c.Assert(pod.Spec.Containers[0].Command, DeepEquals, []string{"/bin/sh"})
|
||||||
|
c.Assert(pod.Spec.Containers[0].Args, DeepEquals, []string{"-c", tc.args.Cmd})
|
||||||
|
c.Assert(pod.Spec.Containers[0].VolumeMounts, DeepEquals, []v1.VolumeMount{{
|
||||||
|
Name: "persistent-storage",
|
||||||
|
MountPath: "/data",
|
||||||
|
}})
|
||||||
|
c.Assert(pod.Spec.Volumes, DeepEquals, []v1.Volume{{
|
||||||
|
Name: "persistent-storage",
|
||||||
|
VolumeSource: v1.VolumeSource{
|
||||||
|
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
|
||||||
|
ClaimName: tc.args.PVCName,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
if tc.args.ContainerImage == "" {
|
||||||
|
c.Assert(pod.Spec.Containers[0].Image, Equals, DefaultPodImage)
|
||||||
|
} else {
|
||||||
|
c.Assert(pod.Spec.Containers[0].Image, Equals, tc.args.ContainerImage)
|
||||||
|
}
|
||||||
|
if tc.args.RunAsUser > 0 {
|
||||||
|
c.Assert(pod.Spec.SecurityContext, DeepEquals, &v1.PodSecurityContext{
|
||||||
|
RunAsUser: &tc.args.RunAsUser,
|
||||||
|
FSGroup: &tc.args.RunAsUser,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
c.Check(pod.Spec.SecurityContext, IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestCreateSnapshot(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
for _, tc := range []struct {
|
||||||
|
snapshotter kansnapshot.Snapshotter
|
||||||
|
args *types.CreateSnapshotArgs
|
||||||
|
snapChecker Checker
|
||||||
|
errChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
getSnap: &v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "createdName",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
PVCName: "pvc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snap1",
|
||||||
|
},
|
||||||
|
snapChecker: NotNil,
|
||||||
|
errChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
getErr: fmt.Errorf("get Error"),
|
||||||
|
},
|
||||||
|
args: &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
PVCName: "pvc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snap1",
|
||||||
|
},
|
||||||
|
snapChecker: IsNil,
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
createErr: fmt.Errorf("create Error"),
|
||||||
|
},
|
||||||
|
args: &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
PVCName: "pvc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snap1",
|
||||||
|
},
|
||||||
|
snapChecker: IsNil,
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
createErr: fmt.Errorf("create Error"),
|
||||||
|
},
|
||||||
|
args: &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "",
|
||||||
|
PVCName: "pvc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snap1",
|
||||||
|
},
|
||||||
|
snapChecker: IsNil,
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
createErr: fmt.Errorf("create Error"),
|
||||||
|
},
|
||||||
|
args: &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
PVCName: "",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snap1",
|
||||||
|
},
|
||||||
|
snapChecker: IsNil,
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
createErr: fmt.Errorf("create Error"),
|
||||||
|
},
|
||||||
|
args: &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
PVCName: "pvc",
|
||||||
|
VolumeSnapshotClass: "",
|
||||||
|
SnapshotName: "snap1",
|
||||||
|
},
|
||||||
|
snapChecker: IsNil,
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
createErr: fmt.Errorf("create Error"),
|
||||||
|
},
|
||||||
|
args: &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
PVCName: "pvc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "",
|
||||||
|
},
|
||||||
|
snapChecker: IsNil,
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
snapshotter: &fakeSnapshotter{},
|
||||||
|
snapChecker: IsNil,
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
snapChecker: IsNil,
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
snapCreator := &snapshotCreate{}
|
||||||
|
snapshot, err := snapCreator.CreateSnapshot(ctx, tc.snapshotter, tc.args)
|
||||||
|
c.Check(snapshot, tc.snapChecker)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestCreateFromSourceCheck(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
for _, tc := range []struct {
|
||||||
|
dyncli dynamic.Interface
|
||||||
|
snapshotter kansnapshot.Snapshotter
|
||||||
|
args *types.CreateFromSourceCheckArgs
|
||||||
|
errChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
dyncli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
gsSrc: &kansnapshot.Source{
|
||||||
|
Handle: "handle",
|
||||||
|
Driver: "driver",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: &types.CreateFromSourceCheckArgs{
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snapshot",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dyncli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
gsSrc: &kansnapshot.Source{
|
||||||
|
Handle: "handle",
|
||||||
|
Driver: "driver",
|
||||||
|
},
|
||||||
|
cfsErr: fmt.Errorf("cfs error"),
|
||||||
|
},
|
||||||
|
args: &types.CreateFromSourceCheckArgs{
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snapshot",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dyncli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
gsErr: fmt.Errorf("gs error"),
|
||||||
|
},
|
||||||
|
args: &types.CreateFromSourceCheckArgs{
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snapshot",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dyncli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
snapshotter: &fakeSnapshotter{
|
||||||
|
cvsErr: fmt.Errorf("cvs error"),
|
||||||
|
},
|
||||||
|
args: &types.CreateFromSourceCheckArgs{
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snapshot",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dyncli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
snapshotter: &fakeSnapshotter{},
|
||||||
|
args: &types.CreateFromSourceCheckArgs{
|
||||||
|
VolumeSnapshotClass: "",
|
||||||
|
SnapshotName: "snapshot",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dyncli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
snapshotter: &fakeSnapshotter{},
|
||||||
|
args: &types.CreateFromSourceCheckArgs{
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dyncli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
snapshotter: &fakeSnapshotter{},
|
||||||
|
args: &types.CreateFromSourceCheckArgs{
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snapshot",
|
||||||
|
Namespace: "",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dyncli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
snapshotter: &fakeSnapshotter{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dyncli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dyncli: nil,
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
snapCreator := &snapshotCreate{
|
||||||
|
dynCli: tc.dyncli,
|
||||||
|
}
|
||||||
|
err := snapCreator.CreateFromSourceCheck(ctx, tc.snapshotter, tc.args)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type fakeSnapshotter struct {
|
||||||
|
name string
|
||||||
|
|
||||||
|
createErr error
|
||||||
|
|
||||||
|
getSnap *v1alpha1.VolumeSnapshot
|
||||||
|
getErr error
|
||||||
|
|
||||||
|
cvsErr error
|
||||||
|
|
||||||
|
gsSrc *kansnapshot.Source
|
||||||
|
gsErr error
|
||||||
|
|
||||||
|
cfsErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeSnapshotter) GetVolumeSnapshotClass(annotationKey, annotationValue, storageClassName string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
func (f *fakeSnapshotter) CloneVolumeSnapshotClass(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) error {
|
||||||
|
return f.createErr
|
||||||
|
}
|
||||||
|
func (f *fakeSnapshotter) Get(ctx context.Context, name, namespace string) (*v1alpha1.VolumeSnapshot, error) {
|
||||||
|
return f.getSnap, f.getErr
|
||||||
|
}
|
||||||
|
func (f *fakeSnapshotter) Delete(ctx context.Context, name, namespace string) (*v1alpha1.VolumeSnapshot, error) {
|
||||||
|
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) 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) error {
|
||||||
|
return f.cfsErr
|
||||||
|
}
|
||||||
|
func (f *fakeSnapshotter) CreateContentFromSource(ctx context.Context, source *kansnapshot.Source, contentName, snapshotName, namespace, deletionPolicy string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (f *fakeSnapshotter) WaitOnReadyToUse(ctx context.Context, snapshotName, namespace string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestDeletePVC(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
for _, tc := range []struct {
|
||||||
|
cli kubernetes.Interface
|
||||||
|
pvcName string
|
||||||
|
namespace string
|
||||||
|
errChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
pvcName: "pvc",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc",
|
||||||
|
Namespace: "notns",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
pvcName: "pvc",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
pvcName: "pvc",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: nil,
|
||||||
|
pvcName: "pvc",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
cleaner := &cleanse{
|
||||||
|
kubeCli: tc.cli,
|
||||||
|
}
|
||||||
|
err := cleaner.DeletePVC(ctx, tc.pvcName, tc.namespace)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestDeletePod(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
for _, tc := range []struct {
|
||||||
|
cli kubernetes.Interface
|
||||||
|
podName string
|
||||||
|
namespace string
|
||||||
|
errChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(),
|
||||||
|
podName: "pod",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(&v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod",
|
||||||
|
Namespace: "notns",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
podName: "pod",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fake.NewSimpleClientset(&v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
podName: "pod",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: nil,
|
||||||
|
podName: "pod",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
cleaner := &cleanse{
|
||||||
|
kubeCli: tc.cli,
|
||||||
|
}
|
||||||
|
err := cleaner.DeletePod(ctx, tc.podName, tc.namespace)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestDeleteSnapshot(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
for _, tc := range []struct {
|
||||||
|
cli dynamic.Interface
|
||||||
|
snapshotName string
|
||||||
|
namespace string
|
||||||
|
errChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
snapshotName: "snap1",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme(),
|
||||||
|
&unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": fmt.Sprintf("%s/%s", v1alpha1.GroupName, v1alpha1.Version),
|
||||||
|
"kind": "VolumeSnapshot",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "snap1",
|
||||||
|
"namespace": "notns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
snapshotName: "pod",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme(),
|
||||||
|
&unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": fmt.Sprintf("%s/%s", v1alpha1.GroupName, v1alpha1.Version),
|
||||||
|
"kind": "VolumeSnapshot",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "snap1",
|
||||||
|
"namespace": "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
snapshotName: "snap1",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cli: nil,
|
||||||
|
snapshotName: "pod",
|
||||||
|
namespace: "ns",
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
cleaner := &cleanse{
|
||||||
|
dynCli: tc.cli,
|
||||||
|
}
|
||||||
|
err := cleaner.DeleteSnapshot(ctx, tc.snapshotName, tc.namespace)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
}
|
||||||
|
}
|
850
pkg/csi/snapshot_restore_steps_test.go
Normal file
850
pkg/csi/snapshot_restore_steps_test.go
Normal file
|
@ -0,0 +1,850 @@
|
||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/kanisterio/kanister/pkg/kube/snapshot/apis/v1alpha1"
|
||||||
|
"github.com/kastenhq/kubestr/pkg/csi/mocks"
|
||||||
|
"github.com/kastenhq/kubestr/pkg/csi/types"
|
||||||
|
. "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) TestValidateArgs(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
type fields struct {
|
||||||
|
validateOps *mocks.MockArgumentValidator
|
||||||
|
versionOps *mocks.MockApiVersionFetcher
|
||||||
|
}
|
||||||
|
for _, tc := range []struct {
|
||||||
|
args *types.CSISnapshotRestoreArgs
|
||||||
|
prepare func(f *fields)
|
||||||
|
errChecker Checker
|
||||||
|
}{
|
||||||
|
{ // valid args
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
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: alphaVersion,
|
||||||
|
}, nil),
|
||||||
|
f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), "vsc", &metav1.GroupVersionForDiscovery{
|
||||||
|
GroupVersion: alphaVersion,
|
||||||
|
}).Return(&unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
VolSnapClassAlphaDriverKey: "p1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
},
|
||||||
|
{ // driver mismatch
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
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: alphaVersion,
|
||||||
|
}, nil),
|
||||||
|
f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), "vsc", &metav1.GroupVersionForDiscovery{
|
||||||
|
GroupVersion: alphaVersion,
|
||||||
|
}).Return(&unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
VolSnapClassAlphaDriverKey: "p2",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // vsc error
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
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: alphaVersion,
|
||||||
|
}, nil),
|
||||||
|
f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), "vsc", &metav1.GroupVersionForDiscovery{
|
||||||
|
GroupVersion: alphaVersion,
|
||||||
|
}).Return(nil, fmt.Errorf("vsc error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // groupversion error
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
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(
|
||||||
|
nil, fmt.Errorf("groupversion error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
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(
|
||||||
|
nil, fmt.Errorf("sc error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(fmt.Errorf("ns error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
}, {
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
VolumeSnapshotClass: "",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
}, {
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
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 := &snapshotRestoreSteps{
|
||||||
|
validateOps: f.validateOps,
|
||||||
|
versionFetchOps: f.versionOps,
|
||||||
|
}
|
||||||
|
err := stepper.ValidateArgs(ctx, tc.args)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestCreateApplication(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
type fields struct {
|
||||||
|
createAppOps *mocks.MockApplicationCreator
|
||||||
|
}
|
||||||
|
for _, tc := range []struct {
|
||||||
|
args *types.CSISnapshotRestoreArgs
|
||||||
|
genString string
|
||||||
|
prepare func(f *fields)
|
||||||
|
errChecker Checker
|
||||||
|
podChecker Checker
|
||||||
|
pvcChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
},
|
||||||
|
genString: "some string",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), &types.CreatePVCArgs{
|
||||||
|
GenerateName: originalPVCGenerateName,
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
}).Return(&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{
|
||||||
|
GenerateName: originalPodGenerateName,
|
||||||
|
PVCName: "pvc1",
|
||||||
|
Namespace: "ns",
|
||||||
|
Cmd: "echo 'some string' >> /data/out.txt; sync; tail -f /dev/null",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
}).Return(&v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.createAppOps.EXPECT().WaitForPodReady(gomock.Any(), "ns", "pod1").Return(nil),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
podChecker: NotNil,
|
||||||
|
pvcChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
},
|
||||||
|
genString: "some string",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), &types.CreatePVCArgs{
|
||||||
|
GenerateName: originalPVCGenerateName,
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
}).Return(&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{
|
||||||
|
GenerateName: originalPodGenerateName,
|
||||||
|
PVCName: "pvc1",
|
||||||
|
Namespace: "ns",
|
||||||
|
Cmd: "echo 'some string' >> /data/out.txt; sync; tail -f /dev/null",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
}).Return(&v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.createAppOps.EXPECT().WaitForPodReady(gomock.Any(), "ns", "pod1").Return(fmt.Errorf("pod ready error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
podChecker: NotNil,
|
||||||
|
pvcChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
},
|
||||||
|
genString: "some string",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), gomock.Any()).Return(&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.createAppOps.EXPECT().CreatePod(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("create pod error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
podChecker: IsNil,
|
||||||
|
pvcChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
},
|
||||||
|
genString: "some string",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("create pvc 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 := &snapshotRestoreSteps{
|
||||||
|
createAppOps: f.createAppOps,
|
||||||
|
}
|
||||||
|
pod, pvc, err := stepper.CreateApplication(ctx, tc.args, tc.genString)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
c.Check(pod, tc.podChecker)
|
||||||
|
c.Check(pvc, tc.pvcChecker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestSnapshotApplication(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
snapshotter := &fakeSnapshotter{name: "snapshotter"}
|
||||||
|
type fields struct {
|
||||||
|
snapshotOps *mocks.MockSnapshotCreator
|
||||||
|
}
|
||||||
|
for _, tc := range []struct {
|
||||||
|
args *types.CSISnapshotRestoreArgs
|
||||||
|
pvc *v1.PersistentVolumeClaim
|
||||||
|
snapshotName string
|
||||||
|
prepare func(f *fields)
|
||||||
|
errChecker Checker
|
||||||
|
snapChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
},
|
||||||
|
pvc: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
snapshotName: "snap1",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.snapshotOps.EXPECT().NewSnapshotter().Return(snapshotter, nil),
|
||||||
|
f.snapshotOps.EXPECT().CreateSnapshot(gomock.Any(), snapshotter, &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
PVCName: "pvc1",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snap1",
|
||||||
|
}).Return(&v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "createdName",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.snapshotOps.EXPECT().CreateFromSourceCheck(gomock.Any(), snapshotter, &types.CreateFromSourceCheckArgs{
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "createdName",
|
||||||
|
Namespace: "ns",
|
||||||
|
}).Return(nil),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
snapChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SkipCFSCheck: true,
|
||||||
|
},
|
||||||
|
pvc: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
snapshotName: "snap1",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.snapshotOps.EXPECT().NewSnapshotter().Return(snapshotter, nil),
|
||||||
|
f.snapshotOps.EXPECT().CreateSnapshot(gomock.Any(), snapshotter, &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
PVCName: "pvc1",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snap1",
|
||||||
|
}).Return(&v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "createdName",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
snapChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
},
|
||||||
|
pvc: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
snapshotName: "snap1",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.snapshotOps.EXPECT().NewSnapshotter().Return(snapshotter, nil),
|
||||||
|
f.snapshotOps.EXPECT().CreateSnapshot(gomock.Any(), snapshotter, &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
PVCName: "pvc1",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snap1",
|
||||||
|
}).Return(&v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "createdName",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.snapshotOps.EXPECT().CreateFromSourceCheck(gomock.Any(), snapshotter, &types.CreateFromSourceCheckArgs{
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "createdName",
|
||||||
|
Namespace: "ns",
|
||||||
|
}).Return(fmt.Errorf("cfs error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
snapChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
},
|
||||||
|
pvc: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
snapshotName: "snap1",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.snapshotOps.EXPECT().NewSnapshotter().Return(snapshotter, nil),
|
||||||
|
f.snapshotOps.EXPECT().CreateSnapshot(gomock.Any(), snapshotter, &types.CreateSnapshotArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
PVCName: "pvc1",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
SnapshotName: "snap1",
|
||||||
|
}).Return(nil, fmt.Errorf("create snapshot error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
snapChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Namespace: "ns",
|
||||||
|
VolumeSnapshotClass: "vsc",
|
||||||
|
},
|
||||||
|
pvc: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
snapshotName: "snap1",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.snapshotOps.EXPECT().NewSnapshotter().Return(nil, fmt.Errorf("snapshotter error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
snapChecker: IsNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
ctrl := gomock.NewController(c)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
f := fields{
|
||||||
|
snapshotOps: mocks.NewMockSnapshotCreator(ctrl),
|
||||||
|
}
|
||||||
|
if tc.prepare != nil {
|
||||||
|
tc.prepare(&f)
|
||||||
|
}
|
||||||
|
stepper := &snapshotRestoreSteps{
|
||||||
|
snapshotCreateOps: f.snapshotOps,
|
||||||
|
}
|
||||||
|
snapshot, err := stepper.SnapshotApplication(ctx, tc.args, tc.pvc, tc.snapshotName)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
c.Check(snapshot, tc.snapChecker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestRestoreApplication(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.CSISnapshotRestoreArgs
|
||||||
|
snapshot *v1alpha1.VolumeSnapshot
|
||||||
|
prepare func(f *fields)
|
||||||
|
errChecker Checker
|
||||||
|
podChecker Checker
|
||||||
|
pvcChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
},
|
||||||
|
snapshot: &v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "snap1",
|
||||||
|
},
|
||||||
|
Status: v1alpha1.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: "snap1",
|
||||||
|
},
|
||||||
|
RestoreSize: &resourceQuantity,
|
||||||
|
}).Return(&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{
|
||||||
|
GenerateName: clonedPodGenerateName,
|
||||||
|
PVCName: "pvc1",
|
||||||
|
Namespace: "ns",
|
||||||
|
Cmd: "tail -f /dev/null",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
}).Return(&v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.createAppOps.EXPECT().WaitForPodReady(gomock.Any(), "ns", "pod1").Return(nil),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
podChecker: NotNil,
|
||||||
|
pvcChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
},
|
||||||
|
snapshot: &v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "snap1",
|
||||||
|
},
|
||||||
|
Status: v1alpha1.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: "snap1",
|
||||||
|
},
|
||||||
|
RestoreSize: &resourceQuantity,
|
||||||
|
}).Return(&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.createAppOps.EXPECT().CreatePod(gomock.Any(), &types.CreatePodArgs{
|
||||||
|
GenerateName: clonedPodGenerateName,
|
||||||
|
PVCName: "pvc1",
|
||||||
|
Namespace: "ns",
|
||||||
|
Cmd: "tail -f /dev/null",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
}).Return(&v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.createAppOps.EXPECT().WaitForPodReady(gomock.Any(), "ns", "pod1").Return(fmt.Errorf("pod ready error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
podChecker: NotNil,
|
||||||
|
pvcChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
},
|
||||||
|
snapshot: &v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "snap1",
|
||||||
|
},
|
||||||
|
Status: v1alpha1.VolumeSnapshotStatus{
|
||||||
|
RestoreSize: &resourceQuantity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), gomock.Any()).Return(&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
},
|
||||||
|
}, nil),
|
||||||
|
f.createAppOps.EXPECT().CreatePod(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("create pod error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
podChecker: IsNil,
|
||||||
|
pvcChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
StorageClass: "sc",
|
||||||
|
Namespace: "ns",
|
||||||
|
RunAsUser: 100,
|
||||||
|
ContainerImage: "image",
|
||||||
|
},
|
||||||
|
snapshot: &v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "snap1",
|
||||||
|
},
|
||||||
|
Status: v1alpha1.VolumeSnapshotStatus{
|
||||||
|
RestoreSize: &resourceQuantity,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("create pvc 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 := &snapshotRestoreSteps{
|
||||||
|
createAppOps: f.createAppOps,
|
||||||
|
}
|
||||||
|
pod, pvc, err := stepper.RestoreApplication(ctx, tc.args, tc.snapshot)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
c.Check(pod, tc.podChecker)
|
||||||
|
c.Check(pvc, tc.pvcChecker)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestCleanup(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
type fields struct {
|
||||||
|
cleanerOps *mocks.MockCleaner
|
||||||
|
}
|
||||||
|
for _, tc := range []struct {
|
||||||
|
results *types.CSISnapshotRestoreResults
|
||||||
|
prepare func(f *fields)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
results: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
results: &types.CSISnapshotRestoreResults{
|
||||||
|
OriginalPVC: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OriginalPod: &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClonedPVC: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClonedPod: &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Snapshot: &v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "snapshot",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.cleanerOps.EXPECT().DeletePVC(ctx, "pvc1", "ns").Return(nil),
|
||||||
|
f.cleanerOps.EXPECT().DeletePod(ctx, "pod1", "ns").Return(nil),
|
||||||
|
f.cleanerOps.EXPECT().DeletePVC(ctx, "pvc2", "ns").Return(nil),
|
||||||
|
f.cleanerOps.EXPECT().DeletePod(ctx, "pod2", "ns").Return(nil),
|
||||||
|
f.cleanerOps.EXPECT().DeleteSnapshot(ctx, "snapshot", "ns").Return(nil),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
ctrl := gomock.NewController(c)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
f := fields{
|
||||||
|
cleanerOps: mocks.NewMockCleaner(ctrl),
|
||||||
|
}
|
||||||
|
if tc.prepare != nil {
|
||||||
|
tc.prepare(&f)
|
||||||
|
}
|
||||||
|
stepper := &snapshotRestoreSteps{
|
||||||
|
cleanerOps: f.cleanerOps,
|
||||||
|
}
|
||||||
|
stepper.Cleanup(ctx, tc.results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestValidateData(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
type fields struct {
|
||||||
|
validatorOps *mocks.MockDataValidator
|
||||||
|
}
|
||||||
|
for _, tc := range []struct {
|
||||||
|
prepare func(f *fields)
|
||||||
|
pod *v1.Pod
|
||||||
|
data string
|
||||||
|
errChecker Checker
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pod: &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: "somedata",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.validatorOps.EXPECT().FetchPodData("pod", "ns").Return("somedata", nil),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pod: &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: "somedata",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.validatorOps.EXPECT().FetchPodData("pod", "ns").Return("someotherdata", nil),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pod: &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: "somedata",
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.validatorOps.EXPECT().FetchPodData("pod", "ns").Return("", fmt.Errorf("error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
ctrl := gomock.NewController(c)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
f := fields{
|
||||||
|
validatorOps: mocks.NewMockDataValidator(ctrl),
|
||||||
|
}
|
||||||
|
if tc.prepare != nil {
|
||||||
|
tc.prepare(&f)
|
||||||
|
}
|
||||||
|
stepper := &snapshotRestoreSteps{
|
||||||
|
dataValidatorOps: f.validatorOps,
|
||||||
|
}
|
||||||
|
err := stepper.ValidateData(ctx, tc.pod, tc.data)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
}
|
||||||
|
}
|
421
pkg/csi/snapshot_restore_test.go
Normal file
421
pkg/csi/snapshot_restore_test.go
Normal file
|
@ -0,0 +1,421 @@
|
||||||
|
package csi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/kanisterio/kanister/pkg/kube/snapshot/apis/v1alpha1"
|
||||||
|
"github.com/kastenhq/kubestr/pkg/csi/mocks"
|
||||||
|
"github.com/kastenhq/kubestr/pkg/csi/types"
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
|
v1 "k8s.io/api/core/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 Test(t *testing.T) { TestingT(t) }
|
||||||
|
|
||||||
|
type CSITestSuite struct{}
|
||||||
|
|
||||||
|
var _ = Suite(&CSITestSuite{})
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestRunSnapshotRestoreHelper(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
type fields struct {
|
||||||
|
stepperOps *mocks.MockSnapshotRestoreStepper
|
||||||
|
}
|
||||||
|
for _, tc := range []struct {
|
||||||
|
kubeCli kubernetes.Interface
|
||||||
|
dynCli dynamic.Interface
|
||||||
|
args *types.CSISnapshotRestoreArgs
|
||||||
|
prepare func(f *fields)
|
||||||
|
result *types.CSISnapshotRestoreResults
|
||||||
|
errChecker Checker
|
||||||
|
}{
|
||||||
|
{ // success
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: true,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().CreateApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(
|
||||||
|
&v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
}, gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().SnapshotApplication(gomock.Any(), gomock.Any(),
|
||||||
|
&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
}, gomock.Any(),
|
||||||
|
).Return(
|
||||||
|
&v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "snapshot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
f.stepperOps.EXPECT().RestoreApplication(gomock.Any(), gomock.Any(),
|
||||||
|
&v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "snapshot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
).Return(
|
||||||
|
&v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
}, gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any()).Return(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{
|
||||||
|
OriginalPVC: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OriginalPod: &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClonedPVC: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClonedPod: &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Snapshot: &v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "snapshot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errChecker: IsNil,
|
||||||
|
},
|
||||||
|
{ // no cleanup
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: false,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().CreateApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().SnapshotApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil),
|
||||||
|
f.stepperOps.EXPECT().RestoreApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{},
|
||||||
|
errChecker: IsNil,
|
||||||
|
},
|
||||||
|
{ // restored data validation fails
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: false,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().CreateApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().SnapshotApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil),
|
||||||
|
f.stepperOps.EXPECT().RestoreApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("validation error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // restore error, objects still returned
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: false,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().CreateApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().SnapshotApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil),
|
||||||
|
f.stepperOps.EXPECT().RestoreApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(
|
||||||
|
&v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fmt.Errorf("restore error"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{
|
||||||
|
ClonedPVC: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClonedPod: &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod2",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // restore error, no objects returned
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: false,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().CreateApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().SnapshotApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil),
|
||||||
|
f.stepperOps.EXPECT().RestoreApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, fmt.Errorf("restore error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // snapshot error, object still returned
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: false,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().CreateApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().SnapshotApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(
|
||||||
|
&v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "snapshot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fmt.Errorf("snapshot error"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{
|
||||||
|
Snapshot: &v1alpha1.VolumeSnapshot{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "snapshot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // snapshot error, object not returned
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: false,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().CreateApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().SnapshotApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("snapshot error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // created data validation error
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: false,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().CreateApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
|
||||||
|
f.stepperOps.EXPECT().ValidateData(gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("validation error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // create error, objects still returned
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: false,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().CreateApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(
|
||||||
|
&v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fmt.Errorf("create error"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{
|
||||||
|
OriginalPVC: &v1.PersistentVolumeClaim{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pvc1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OriginalPod: &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod1",
|
||||||
|
Namespace: "ns",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // create error, objects not returned
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: false,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil),
|
||||||
|
f.stepperOps.EXPECT().CreateApplication(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, fmt.Errorf("create error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // args validate error
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
args: &types.CSISnapshotRestoreArgs{
|
||||||
|
Cleanup: false,
|
||||||
|
},
|
||||||
|
prepare: func(f *fields) {
|
||||||
|
gomock.InOrder(
|
||||||
|
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(fmt.Errorf("create error")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
result: &types.CSISnapshotRestoreResults{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // empty cli
|
||||||
|
kubeCli: nil,
|
||||||
|
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
|
||||||
|
result: &types.CSISnapshotRestoreResults{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
{ // empty dyncli
|
||||||
|
kubeCli: fake.NewSimpleClientset(),
|
||||||
|
dynCli: nil,
|
||||||
|
result: &types.CSISnapshotRestoreResults{},
|
||||||
|
errChecker: NotNil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
ctrl := gomock.NewController(c)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
f := fields{
|
||||||
|
stepperOps: mocks.NewMockSnapshotRestoreStepper(ctrl),
|
||||||
|
}
|
||||||
|
if tc.prepare != nil {
|
||||||
|
tc.prepare(&f)
|
||||||
|
}
|
||||||
|
runner := &SnapshotRestoreRunner{
|
||||||
|
KubeCli: tc.kubeCli,
|
||||||
|
DynCli: tc.dynCli,
|
||||||
|
srSteps: f.stepperOps,
|
||||||
|
}
|
||||||
|
result, err := runner.RunSnapshotRestoreHelper(ctx, tc.args)
|
||||||
|
c.Check(err, tc.errChecker)
|
||||||
|
c.Assert(result, DeepEquals, tc.result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CSITestSuite) TestRunSnapshotRestoreRunner(c *C) {
|
||||||
|
ctx := context.Background()
|
||||||
|
r := &SnapshotRestoreRunner{}
|
||||||
|
_, err := r.RunSnapshotRestore(ctx, nil)
|
||||||
|
c.Check(err, NotNil)
|
||||||
|
}
|
92
pkg/csi/types/csi_types.go
Normal file
92
pkg/csi/types/csi_types.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/kanisterio/kanister/pkg/kube/snapshot/apis/v1alpha1"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CSISnapshotRestoreArgs struct {
|
||||||
|
StorageClass string
|
||||||
|
VolumeSnapshotClass string
|
||||||
|
Namespace string
|
||||||
|
RunAsUser int64
|
||||||
|
ContainerImage string
|
||||||
|
Cleanup bool
|
||||||
|
SkipCFSCheck bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *CSISnapshotRestoreArgs) Validate() error {
|
||||||
|
if a.StorageClass == "" || a.VolumeSnapshotClass == "" || a.Namespace == "" {
|
||||||
|
return fmt.Errorf("Require fields are missing. (StorageClass, VolumeSnapshotClass, Namespace)")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CSISnapshotRestoreResults struct {
|
||||||
|
OriginalPVC *v1.PersistentVolumeClaim
|
||||||
|
OriginalPod *v1.Pod
|
||||||
|
Snapshot *v1alpha1.VolumeSnapshot
|
||||||
|
ClonedPVC *v1.PersistentVolumeClaim
|
||||||
|
ClonedPod *v1.Pod
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreatePVCArgs struct {
|
||||||
|
GenerateName string
|
||||||
|
StorageClass string
|
||||||
|
Namespace string
|
||||||
|
DataSource *v1.TypedLocalObjectReference
|
||||||
|
RestoreSize *resource.Quantity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CreatePVCArgs) Validate() error {
|
||||||
|
if c.GenerateName == "" || c.StorageClass == "" || c.Namespace == "" {
|
||||||
|
return fmt.Errorf("Invalid CreatePVCArgs (%v)", c)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreatePodArgs struct {
|
||||||
|
GenerateName string
|
||||||
|
PVCName string
|
||||||
|
Namespace string
|
||||||
|
Cmd string
|
||||||
|
RunAsUser int64
|
||||||
|
ContainerImage string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CreatePodArgs) Validate() error {
|
||||||
|
if c.GenerateName == "" || c.PVCName == "" || c.Namespace == "" || c.Cmd == "" {
|
||||||
|
return fmt.Errorf("Invalid CreatePodArgs (%v)", c)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateSnapshotArgs struct {
|
||||||
|
Namespace string
|
||||||
|
PVCName string
|
||||||
|
VolumeSnapshotClass string
|
||||||
|
SnapshotName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CreateSnapshotArgs) Validate() error {
|
||||||
|
if c.Namespace == "" || c.PVCName == "" || c.VolumeSnapshotClass == "" || c.SnapshotName == "" {
|
||||||
|
return fmt.Errorf("Invalid CreateSnapshotArgs (%v)", c)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateFromSourceCheckArgs struct {
|
||||||
|
VolumeSnapshotClass string
|
||||||
|
SnapshotName string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CreateFromSourceCheckArgs) Validate() error {
|
||||||
|
if c.VolumeSnapshotClass == "" || c.SnapshotName == "" || c.Namespace == "" {
|
||||||
|
return fmt.Errorf("Invalid CreateFromSourceCheckArgs (%v)", c)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -524,7 +524,7 @@ func (s *FIOTestSuite) TestDeletePVC(c *C) {
|
||||||
|
|
||||||
func (s *FIOTestSuite) TestCreatPod(c *C) {
|
func (s *FIOTestSuite) TestCreatPod(c *C) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
for i, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
pvcName string
|
pvcName string
|
||||||
configMapName string
|
configMapName string
|
||||||
testFileName string
|
testFileName string
|
||||||
|
@ -610,7 +610,6 @@ func (s *FIOTestSuite) TestCreatPod(c *C) {
|
||||||
errChecker: NotNil,
|
errChecker: NotNil,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
fmt.Println(i)
|
|
||||||
stepper := &fioStepper{
|
stepper := &fioStepper{
|
||||||
cli: fake.NewSimpleClientset(),
|
cli: fake.NewSimpleClientset(),
|
||||||
podReady: &fakePodReadyChecker{prcErr: tc.podReadyErr},
|
podReady: &fakePodReadyChecker{prcErr: tc.podReadyErr},
|
||||||
|
|
|
@ -24,12 +24,11 @@ var CSIDriverList = []*CSIDriver{
|
||||||
{NameUrl: "[Datatom-InfinityCSI](https://github.com/datatom-infinity/infinity-csi)", DriverName: "csi-infiblock-plugin", Versions: "v0.3, v1.0.0, v1.1.0", Description: "A Container Storage Interface (CSI) Driver for DATATOM Infinity storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Topology"},
|
{NameUrl: "[Datatom-InfinityCSI](https://github.com/datatom-infinity/infinity-csi)", DriverName: "csi-infiblock-plugin", Versions: "v0.3, v1.0.0, v1.1.0", Description: "A Container Storage Interface (CSI) Driver for DATATOM Infinity storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Topology"},
|
||||||
{NameUrl: "[Datatom-InfinityCSI (filesystem)](https://github.com/datatom-infinity/infinity-csi)", DriverName: "csi-infifs-plugin", Versions: "v0.3, v1.0.0, v1.1.0", Description: "A Container Storage Interface (CSI) Driver for DATATOM Infinity filesystem storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Expansion"},
|
{NameUrl: "[Datatom-InfinityCSI (filesystem)](https://github.com/datatom-infinity/infinity-csi)", DriverName: "csi-infifs-plugin", Versions: "v0.3, v1.0.0, v1.1.0", Description: "A Container Storage Interface (CSI) Driver for DATATOM Infinity filesystem storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Expansion"},
|
||||||
{NameUrl: "[Datera](https://github.com/Datera/datera-csi)", DriverName: "dsp.csi.daterainc.io", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Datera Data Services Platform (DSP)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
{NameUrl: "[Datera](https://github.com/Datera/datera-csi)", DriverName: "dsp.csi.daterainc.io", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Datera Data Services Platform (DSP)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
||||||
{NameUrl: "[Dell EMC Isilon](https://github.com/dell/csi-isilon)", DriverName: "csi-isilon.dellemc.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC Isilon](https://www.delltechnologies.com/en-us/storage/isilon/index.htm)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
{NameUrl: "[Dell EMC PowerScale](https://github.com/dell/csi-powerscale)", DriverName: "csi-isilon.dellemc.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC PowerScale](https://www.delltechnologies.com/en-us/storage/powerscale.htm)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Cloning"},
|
||||||
{NameUrl: "[Dell EMC PowerMax](https://github.com/dell/csi-powermax)", DriverName: "csi-powermax.dellemc.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC PowerMax](https://www.delltechnologies.com/en-us/storage/powermax.htm)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
{NameUrl: "[Dell EMC PowerMax](https://github.com/dell/csi-powermax)", DriverName: "csi-powermax.dellemc.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC PowerMax](https://www.delltechnologies.com/en-us/storage/powermax.htm)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
||||||
{NameUrl: "[Dell EMC PowerStore](https://github.com/dell/csi-powerstore)", DriverName: "csi-powerstore.dellemc.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC PowerStore](https://www.delltechnologies.com/en-us/storage/powerstore-storage-appliance.htm)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[Dell EMC PowerStore](https://github.com/dell/csi-powerstore)", DriverName: "csi-powerstore.dellemc.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC PowerStore](https://www.delltechnologies.com/en-us/storage/powerstore-storage-appliance.htm)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
||||||
{NameUrl: "[Dell EMC Unity](https://github.com/dell/csi-unity)", DriverName: "csi-unity.dellemc.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC Unity](https://www.delltechnologies.com/en-us/storage/unity.htm)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
{NameUrl: "[Dell EMC Unity](https://github.com/dell/csi-unity)", DriverName: "csi-unity.dellemc.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC Unity](https://www.delltechnologies.com/en-us/storage/unity.htm)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
||||||
{NameUrl: "[Dell EMC VxFlexOS](https://github.com/dell/csi-vxflexos)", DriverName: "csi-vxflexos.dellemc.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC VxFlexOS](https://www.delltechnologies.com/en-us/hyperconverged-infrastructure/vxflex.htm)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
{NameUrl: "[Dell EMC VxFlexOS](https://github.com/dell/csi-vxflexos)", DriverName: "csi-vxflexos.dellemc.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC VxFlexOS](https://www.delltechnologies.com/en-us/hyperconverged-infrastructure/vxflex.htm)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Topology"},
|
||||||
{NameUrl: "[Dell EMC XtremIO](https://github.com/dell/csi-xtremio-deploy)", DriverName: "csi-xtremio.dellemc.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for [Dell EMC XtremIO](https://www.delltechnologies.com/en-us/storage/xtremio-all-flash.htm)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
|
||||||
{NameUrl: "[democratic-csi](https://github.com/democratic-csi/democratic-csi)", DriverName: "org.democratic-csi.[X]", Versions: "v1.0,v1.1,v1.2", Description: "Generic CSI plugin supporting zfs based solutions ([FreeNAS](https://www.freenas.org/) / [TrueNAS](https://www.truenas.com/) and [ZoL](https://zfsonlinux.org/) solutions such as [Ubuntu](https://ubuntu.com/))", Persistence: "Persistent and Ephemeral", AccessModes: "Read/Write Single Pod (Block Volume) <br/><br/> Read/Write Multiple Pods (File Volume)", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
{NameUrl: "[democratic-csi](https://github.com/democratic-csi/democratic-csi)", DriverName: "org.democratic-csi.[X]", Versions: "v1.0,v1.1,v1.2", Description: "Generic CSI plugin supporting zfs based solutions ([FreeNAS](https://www.freenas.org/) / [TrueNAS](https://www.truenas.com/) and [ZoL](https://zfsonlinux.org/) solutions such as [Ubuntu](https://ubuntu.com/))", Persistence: "Persistent and Ephemeral", AccessModes: "Read/Write Single Pod (Block Volume) <br/><br/> Read/Write Multiple Pods (File Volume)", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
||||||
{NameUrl: "[Diamanti-CSI](https://diamanti.com/use-cases/io-acceleration/#csi)", DriverName: "dcx.csi.diamanti.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Diamanti DCX Platform", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion"},
|
{NameUrl: "[Diamanti-CSI](https://diamanti.com/use-cases/io-acceleration/#csi)", DriverName: "dcx.csi.diamanti.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Diamanti DCX Platform", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion"},
|
||||||
{NameUrl: "[DigitalOcean Block Storage](https://github.com/digitalocean/csi-digitalocean)", DriverName: "dobs.csi.digitalocean.com", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for DigitalOcean Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion"},
|
{NameUrl: "[DigitalOcean Block Storage](https://github.com/digitalocean/csi-digitalocean)", DriverName: "dobs.csi.digitalocean.com", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for DigitalOcean Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion"},
|
||||||
|
@ -42,7 +41,7 @@ var CSIDriverList = []*CSIDriver{
|
||||||
{NameUrl: "[GlusterFS](https://github.com/gluster/gluster-csi-driver)", DriverName: "org.gluster.glusterfs", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for GlusterFS", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
{NameUrl: "[GlusterFS](https://github.com/gluster/gluster-csi-driver)", DriverName: "org.gluster.glusterfs", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for GlusterFS", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
||||||
{NameUrl: "[Gluster VirtBlock](https://github.com/gluster/gluster-csi-driver)", DriverName: "org.gluster.glustervirtblock", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for Gluster Virtual Block volumes", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[Gluster VirtBlock](https://github.com/gluster/gluster-csi-driver)", DriverName: "org.gluster.glustervirtblock", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for Gluster Virtual Block volumes", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
|
||||||
{NameUrl: "[Hammerspace CSI](https://github.com/hammer-space/csi-plugin)", DriverName: "com.hammerspace.csi", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for Hammerspace Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot"},
|
{NameUrl: "[Hammerspace CSI](https://github.com/hammer-space/csi-plugin)", DriverName: "com.hammerspace.csi", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for Hammerspace Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot"},
|
||||||
{NameUrl: "[Hedvig](https://documentation.commvault.com/commvault/hedvig/others/pdf/Hedvig_CSI_User_Guide.pdf)", DriverName: "io.hedvig.csi", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Hedvig", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
{NameUrl: "[Hedvig](https://documentation.commvault.com/commvault/hedvig/others/pdf/Hedvig_CSI_User_Guide.pdf)", DriverName: "io.hedvig.csi", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Hedvig", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion"},
|
||||||
{NameUrl: "[Hetzner Cloud Volumes CSI](https://github.com/hetznercloud/csi-driver)", DriverName: "csi.hetzner.cloud", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for Hetzner Cloud Volumes", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Expansion"},
|
{NameUrl: "[Hetzner Cloud Volumes CSI](https://github.com/hetznercloud/csi-driver)", DriverName: "csi.hetzner.cloud", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for Hetzner Cloud Volumes", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Expansion"},
|
||||||
{NameUrl: "[Hitachi Vantara](https://knowledge.hitachivantara.com/Documents/Adapters_and_Drivers/Storage_Adapters_and_Drivers/Containers)", DriverName: "hspc.csi.hitachi.com", Versions: "v1.2", Description: "A Container Storage Interface (CSI) Driver for VSP series Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
{NameUrl: "[Hitachi Vantara](https://knowledge.hitachivantara.com/Documents/Adapters_and_Drivers/Storage_Adapters_and_Drivers/Containers)", DriverName: "hspc.csi.hitachi.com", Versions: "v1.2", Description: "A Container Storage Interface (CSI) Driver for VSP series Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
||||||
{NameUrl: "[HPE](https://github.com/hpe-storage/csi-driver)", DriverName: "csi.hpe.com", Versions: "v1.0, v1.1, v1.2", Description: "A [multi-platform](https://scod.hpedev.io/csi_driver) Container Storage Interface (CSI) driver. Supports [HPE Nimble Storage](https://hpe.com/storage/nimble), [HPE Primera](https://hpe.com/storage/primera) and [HPE 3PAR](https://hpe.com/storage/3par)", Persistence: "Persistent and Ephemeral", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
{NameUrl: "[HPE](https://github.com/hpe-storage/csi-driver)", DriverName: "csi.hpe.com", Versions: "v1.0, v1.1, v1.2", Description: "A [multi-platform](https://scod.hpedev.io/csi_driver) Container Storage Interface (CSI) driver. Supports [HPE Nimble Storage](https://hpe.com/storage/nimble), [HPE Primera](https://hpe.com/storage/primera) and [HPE 3PAR](https://hpe.com/storage/3par)", Persistence: "Persistent and Ephemeral", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
||||||
|
@ -56,6 +55,7 @@ var CSIDriverList = []*CSIDriver{
|
||||||
{NameUrl: "[Intel PMEM-CSI](https://github.com/intel/pmem-csi)", DriverName: "pmem-csi.intel.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) driver for [PMEM](https://pmem.io/) from Intel", Persistence: "Persistent and Ephemeral", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block"},
|
{NameUrl: "[Intel PMEM-CSI](https://github.com/intel/pmem-csi)", DriverName: "pmem-csi.intel.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) driver for [PMEM](https://pmem.io/) from Intel", Persistence: "Persistent and Ephemeral", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block"},
|
||||||
{NameUrl: "[JuiceFS](https://github.com/juicedata/juicefs-csi-driver)", DriverName: "csi.juicefs.com", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for JuiceFS File System", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pod", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[JuiceFS](https://github.com/juicedata/juicefs-csi-driver)", DriverName: "csi.juicefs.com", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for JuiceFS File System", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pod", DynamicProvisioning: "Yes", Features: ""},
|
||||||
{NameUrl: "[kaDalu](https://github.com/kadalu/kadalu)", DriverName: "org.kadalu.gluster", Versions: "v0.3", Description: "A CSI Driver (and operator) for GlusterFS", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[kaDalu](https://github.com/kadalu/kadalu)", DriverName: "org.kadalu.gluster", Versions: "v0.3", Description: "A CSI Driver (and operator) for GlusterFS", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
||||||
|
{NameUrl: "[KumoScale Block Storage](https://github.com/KioxiaAmerica/kumoscale-csi)", DriverName: "kumoscale.kioxia.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for KumoScale Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Topology"},
|
||||||
{NameUrl: "[Linode Block Storage](https://github.com/linode/linode-blockstorage-csi-driver)", DriverName: "linodebs.csi.linode.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Linode Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[Linode Block Storage](https://github.com/linode/linode-blockstorage-csi-driver)", DriverName: "linodebs.csi.linode.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Linode Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
|
||||||
{NameUrl: "[LINSTOR](https://github.com/LINBIT/linstor-csi)", DriverName: "io.drbd.linstor-csi", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [LINSTOR](https://www.linbit.com/en/linstor/) volumes", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
{NameUrl: "[LINSTOR](https://github.com/LINBIT/linstor-csi)", DriverName: "io.drbd.linstor-csi", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [LINSTOR](https://www.linbit.com/en/linstor/) volumes", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
||||||
{NameUrl: "[Longhorn](https://github.com/longhorn/longhorn)", DriverName: "driver.longhorn.io", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Longhorn](https://longhorn.io/) volumes", Persistence: "Persistent", AccessModes: "Read/Write Single Node", DynamicProvisioning: "Yes", Features: "Raw Block"},
|
{NameUrl: "[Longhorn](https://github.com/longhorn/longhorn)", DriverName: "driver.longhorn.io", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for [Longhorn](https://longhorn.io/) volumes", Persistence: "Persistent", AccessModes: "Read/Write Single Node", DynamicProvisioning: "Yes", Features: "Raw Block"},
|
||||||
|
@ -71,13 +71,14 @@ var CSIDriverList = []*CSIDriver{
|
||||||
{NameUrl: "[OpenSDS](https://github.com/opensds/nbp/tree/master/csi)", DriverName: "csi-opensdsplugin", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for [OpenSDS]((https://www.opensds.io/))", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot"},
|
{NameUrl: "[OpenSDS](https://github.com/opensds/nbp/tree/master/csi)", DriverName: "csi-opensdsplugin", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for [OpenSDS]((https://www.opensds.io/))", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot"},
|
||||||
{NameUrl: "[Open-E](https://github.com/open-e/JovianDSS-KubernetesCSI)", DriverName: "com.open-e.joviandss.csi", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Open-E JovianDSS Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot, Cloning"},
|
{NameUrl: "[Open-E](https://github.com/open-e/JovianDSS-KubernetesCSI)", DriverName: "com.open-e.joviandss.csi", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Open-E JovianDSS Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot, Cloning"},
|
||||||
{NameUrl: "[Portworx](https://github.com/libopenstorage/openstorage/tree/master/csi)", DriverName: "pxd.openstorage.org", Versions: "v0.3, v1.1", Description: "A Container Storage Interface (CSI) Driver for [Portworx](https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/csi/)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion"},
|
{NameUrl: "[Portworx](https://github.com/libopenstorage/openstorage/tree/master/csi)", DriverName: "pxd.openstorage.org", Versions: "v0.3, v1.1", Description: "A Container Storage Interface (CSI) Driver for [Portworx](https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/csi/)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion"},
|
||||||
{NameUrl: "[Pure Storage CSI](https://github.com/purestorage/pso-csi)", DriverName: "pure-csi", Versions: "v1.0, v1.1, v1.2", Description: "A Container Storage Interface (CSI) Driver for Pure Storage's [Pure Service Orchestrator](https://purestorage.com/containers)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Cloning, Raw Block, Topology, Expansion"},
|
{NameUrl: "[Pure Storage CSI](https://github.com/purestorage/pso-csi)", DriverName: "pure-csi", Versions: "v1.0, v1.1, v1.2, v1.3", Description: "A Container Storage Interface (CSI) Driver for Pure Storage's [Pure Service Orchestrator](https://purestorage.com/containers)", Persistence: "Persistent and Ephemeral", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Cloning, Raw Block, Topology, Expansion"},
|
||||||
{NameUrl: "[QingCloud CSI](https://github.com/yunify/qingcloud-csi)", DriverName: "disk.csi.qingcloud.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for QingCloud Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Cloning"},
|
{NameUrl: "[QingCloud CSI](https://github.com/yunify/qingcloud-csi)", DriverName: "disk.csi.qingcloud.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for QingCloud Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
||||||
{NameUrl: "[QingStor CSI](https://github.com/yunify/qingstor-csi)", DriverName: "csi-neonsan", Versions: "v0.3", Description: "A Container Storage Interface (CSI) Driver for NeonSAN storage system", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
|
{NameUrl: "[QingStor CSI](https://github.com/yunify/qingstor-csi)", DriverName: "neonsan.csi.qingstor.com", Versions: "v0.3, v1.1", Description: "A Container Storage Interface (CSI) Driver for NeonSAN storage system", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
||||||
{NameUrl: "[Quobyte](https://github.com/quobyte/quobyte-csi)", DriverName: "quobyte-csi", Versions: "v0.2", Description: "A Container Storage Interface (CSI) Driver for Quobyte", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[Quobyte](https://github.com/quobyte/quobyte-csi)", DriverName: "quobyte-csi", Versions: "v0.2", Description: "A Container Storage Interface (CSI) Driver for Quobyte", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
||||||
{NameUrl: "[ROBIN](https://get.robin.io/)", DriverName: "robin", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for [ROBIN](https://docs.robin.io)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Cloning"},
|
{NameUrl: "[ROBIN](https://get.robin.io/)", DriverName: "robin", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for [ROBIN](https://docs.robin.io)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Cloning"},
|
||||||
{NameUrl: "[SandStone](https://github.com/sandstone-storage/sandstone-csi-driver)", DriverName: "csi-sandstone-plugin", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for SandStone USP", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
{NameUrl: "[SandStone](https://github.com/sandstone-storage/sandstone-csi-driver)", DriverName: "csi-sandstone-plugin", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for SandStone USP", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
||||||
{NameUrl: "[Sangfor-EDS](https://github.com/evan37717/sangfor-eds-csi)", DriverName: "eds.csi.sangfor.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Sangfor Distributed File Storage(EDS)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[Sangfor-EDS](https://github.com/evan37717/sangfor-eds-csi)", DriverName: "eds.csi.sangfor.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Sangfor Distributed File Storage(EDS)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
||||||
|
{NameUrl: "[Sangfor-EDS-Block-Storage](https://github.com/eds-wzc/sangfor-eds-csi)", DriverName: "eds.csi.block.sangfor.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Sangfor Block Storage(EDS)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
|
||||||
{NameUrl: "[SeaweedFS](https://github.com/seaweedfs/seaweedfs-csi-driver)", DriverName: "seaweedfs-csi-driver", Versions: "v1.0", Description: "A Container Storage Interface (CSI Driver for [SeaweedFS](https://github.com/chrislusf/seaweedfs))", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[SeaweedFS](https://github.com/seaweedfs/seaweedfs-csi-driver)", DriverName: "seaweedfs-csi-driver", Versions: "v1.0", Description: "A Container Storage Interface (CSI Driver for [SeaweedFS](https://github.com/chrislusf/seaweedfs))", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
||||||
{NameUrl: "[Secrets Store CSI Driver](https://github.com/kubernetes-sigs/secrets-store-csi-driver)", DriverName: "secrets-store.csi.k8s.io", Versions: "v0.0.10", Description: "A Container Storage Interface (CSI) Driver for mounting secrets, keys, and certs stored in enterprise-grade external secrets stores as volumes.", Persistence: "Ephemeral", AccessModes: "N/A", DynamicProvisioning: "N/A", Features: ""},
|
{NameUrl: "[Secrets Store CSI Driver](https://github.com/kubernetes-sigs/secrets-store-csi-driver)", DriverName: "secrets-store.csi.k8s.io", Versions: "v0.0.10", Description: "A Container Storage Interface (CSI) Driver for mounting secrets, keys, and certs stored in enterprise-grade external secrets stores as volumes.", Persistence: "Ephemeral", AccessModes: "N/A", DynamicProvisioning: "N/A", Features: ""},
|
||||||
{NameUrl: "[SmartX](http://www.smartx.com/?locale=en)", DriverName: "csi-smtx-plugin", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for SmartX ZBS Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion"},
|
{NameUrl: "[SmartX](http://www.smartx.com/?locale=en)", DriverName: "csi-smtx-plugin", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for SmartX ZBS Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion"},
|
||||||
|
@ -93,7 +94,9 @@ var CSIDriverList = []*CSIDriver{
|
||||||
{NameUrl: "[XSKY-EBS](https://xsky-storage.github.io/xsky-csi-driver/csi-block.html)", DriverName: "csi.block.xsky.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for XSKY Distributed Block Storage (X-EBS)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
{NameUrl: "[XSKY-EBS](https://xsky-storage.github.io/xsky-csi-driver/csi-block.html)", DriverName: "csi.block.xsky.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for XSKY Distributed Block Storage (X-EBS)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
|
||||||
{NameUrl: "[XSKY-EUS](https://xsky-storage.github.io/xsky-csi-driver/csi-fs.html)", DriverName: "csi.fs.xsky.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for XSKY Distributed File Storage (X-EUS)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[XSKY-EUS](https://xsky-storage.github.io/xsky-csi-driver/csi-fs.html)", DriverName: "csi.fs.xsky.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for XSKY Distributed File Storage (X-EUS)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
||||||
{NameUrl: "[Vault](https://github.com/kubevault/csi-driver)", DriverName: "secrets.csi.kubevault.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for mounting HashiCorp Vault secrets as volumes.", Persistence: "Ephemeral", AccessModes: "N/A", DynamicProvisioning: "N/A", Features: ""},
|
{NameUrl: "[Vault](https://github.com/kubevault/csi-driver)", DriverName: "secrets.csi.kubevault.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for mounting HashiCorp Vault secrets as volumes.", Persistence: "Ephemeral", AccessModes: "N/A", DynamicProvisioning: "N/A", Features: ""},
|
||||||
{NameUrl: "[vSphere](https://github.com/kubernetes-sigs/vsphere-csi-driver)", DriverName: "csi.vsphere.vmware.com", Versions: "v2.0.0", Description: "A Container Storage Interface (CSI) Driver for VMware vSphere", Persistence: "Persistent", AccessModes: "Read/Write Single Pod (Block Volume) <br/><br/> Read/Write Multiple Pods (File Volume)", DynamicProvisioning: "Yes", Features: "Raw Block,<br/><br/>Expansion (Block Volume),<br/><br/>Topology Aware (Block Volume)"},
|
{NameUrl: "[Veritas InfoScale Volumes](https://www.veritas.com/solution/virtualization/containers.html)", DriverName: "org.veritas.infoscale", Versions: "v1.2", Description: "A Container Storage Interface (CSI) Driver for Veritas InfoScale volumes", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Cloning"},
|
||||||
|
{NameUrl: "[vSphere](https://github.com/kubernetes-sigs/vsphere-csi-driver)", DriverName: "csi.vsphere.vmware.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for VMware vSphere", Persistence: "Persistent", AccessModes: "Read/Write Single Pod (Block Volume) <br/><br/> Read/Write Multiple Pods (File Volume)", DynamicProvisioning: "Yes", Features: "Raw Block,<br/><br/>Expansion (Block Volume),<br/><br/>Topology Aware (Block Volume)"},
|
||||||
|
{NameUrl: "[Vultr Block Storage](https://github.com/vultr/vultr-csi)", DriverName: "block.csi.vultr.com", Versions: "v1.2", Description: "A Container Storage Interface (CSI) Driver for Vultr Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
|
||||||
{NameUrl: "[WekaIO](https://github.com/weka/csi-wekafs)", DriverName: "csi.weka.io", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for mounting WekaIO WekaFS filesystem as volumes", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[WekaIO](https://github.com/weka/csi-wekafs)", DriverName: "csi.weka.io", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for mounting WekaIO WekaFS filesystem as volumes", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
||||||
{NameUrl: "[Yandex.Cloud](https://github.com/flant/yandex-csi-driver)", DriverName: "yandex.csi.flant.com", Versions: "v1.2", Description: "A Container Storage Interface (CSI) plugin for Yandex.Cloud Compute Disks", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[Yandex.Cloud](https://github.com/flant/yandex-csi-driver)", DriverName: "yandex.csi.flant.com", Versions: "v1.2", Description: "A Container Storage Interface (CSI) plugin for Yandex.Cloud Compute Disks", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
|
||||||
{NameUrl: "[YanRongYun](http://www.yanrongyun.com/)", DriverName: "?", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for YanRong YRCloudFile Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
{NameUrl: "[YanRongYun](http://www.yanrongyun.com/)", DriverName: "?", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for YanRong YRCloudFile Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
|
|
@ -38,11 +38,11 @@ var (
|
||||||
|
|
||||||
// NewKubestr initializes a new kubestr object to run preflight tests
|
// NewKubestr initializes a new kubestr object to run preflight tests
|
||||||
func NewKubestr() (*Kubestr, error) {
|
func NewKubestr() (*Kubestr, error) {
|
||||||
cli, err := getKubeCli()
|
cli, err := LoadKubeCli()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dynCli, err := getDynCli()
|
dynCli, err := LoadDynCli()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,8 @@ func NewKubestr() (*Kubestr, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDynCli loads the config and returns a dynamic CLI
|
// LoadDynCli loads the config and returns a dynamic CLI
|
||||||
func getDynCli() (dynamic.Interface, error) {
|
func LoadDynCli() (dynamic.Interface, error) {
|
||||||
cfg, err := kube.LoadConfig()
|
cfg, err := kube.LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Failed to load config for Dynamic client")
|
return nil, errors.Wrap(err, "Failed to load config for Dynamic client")
|
||||||
|
@ -72,9 +72,9 @@ func getDynCli() (dynamic.Interface, error) {
|
||||||
return clientset, nil
|
return clientset, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getKubeCli load the config and returns a kubernetes client
|
// LoadKubeCli load the config and returns a kubernetes client
|
||||||
// NewClient returns a k8 client configured by the kanister environment.
|
// NewClient returns a k8 client configured by the kanister environment.
|
||||||
func getKubeCli() (kubernetes.Interface, error) {
|
func LoadKubeCli() (kubernetes.Interface, error) {
|
||||||
config, err := kube.LoadConfig()
|
config, err := kube.LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -152,7 +152,7 @@ func (v *Provisioner) Print() {
|
||||||
if len(v.StorageClasses) > 0 {
|
if len(v.StorageClasses) > 0 {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println(" To perform a FIO test, run-")
|
fmt.Println(" To perform a FIO test, run-")
|
||||||
fmt.Println(" ./kubestr fio -c <storage class>")
|
fmt.Println(" ./kubestr fio -s <storage class>")
|
||||||
fmt.Println(" Validate the results against our benchmarks at <website>. For more options try '-h'.")
|
fmt.Println(" Validate the results against our benchmarks at <website>. For more options try '-h'.")
|
||||||
switch {
|
switch {
|
||||||
case len(v.VolumeSnapshotClasses) == 0 && v.CSIDriver != nil && v.CSIDriver.SupportsSnapshots():
|
case len(v.VolumeSnapshotClasses) == 0 && v.CSIDriver != nil && v.CSIDriver.SupportsSnapshots():
|
||||||
|
@ -160,7 +160,8 @@ func (v *Provisioner) Print() {
|
||||||
fmt.Println(" This provisioner supports snapshots, however no Volume Snaphsot Classes were found.")
|
fmt.Println(" This provisioner supports snapshots, however no Volume Snaphsot Classes were found.")
|
||||||
case len(v.VolumeSnapshotClasses) > 0:
|
case len(v.VolumeSnapshotClasses) > 0:
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println(" (Coming soon) Test snapshot/restore functionality.")
|
fmt.Println(" To test CSI snapshot/restore functionality, run-")
|
||||||
|
fmt.Println(" ./kubestr csicheck -s <storage class> -v <volume snapshot class>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue