1
0
Fork 0
mirror of https://github.com/kastenhq/kubestr.git synced 2024-12-14 11:57:56 +00:00

Kubestr browse PVC functionality (#83)

* browse changes

* after onkars review
This commit is contained in:
Sirish Bathina 2021-10-08 09:53:57 -10:00 committed by GitHub
parent 4d3cb00b76
commit 032878da5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 2192 additions and 471 deletions

View file

@ -77,6 +77,22 @@ var (
return CSICheck(ctx, output, outfile, namespace, storageClass, csiCheckVolumeSnapshotClass, csiCheckRunAsUser, containerImage, csiCheckCleanup, csiCheckSkipCFSCheck)
},
}
pvcBrowseLocalPort int
pvcBrowseCmd = &cobra.Command{
Use: "browse [PVC name]",
Short: "Browse the contents of a CSI PVC via file browser",
Args: cobra.ExactArgs(1),
Long: "Browse the contents of a CSI provisioned PVC by cloning the volume and mounting it with a file browser.",
RunE: func(cmd *cobra.Command, args []string) error {
return CsiPvcBrowse(context.Background(), args[0],
namespace,
csiCheckVolumeSnapshotClass,
csiCheckRunAsUser,
pvcBrowseLocalPort,
)
},
}
)
func init() {
@ -102,6 +118,13 @@ func init() {
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.")
rootCmd.AddCommand(pvcBrowseCmd)
pvcBrowseCmd.Flags().StringVarP(&csiCheckVolumeSnapshotClass, "volumesnapshotclass", "v", "", "The name of a VolumeSnapshotClass. (Required)")
_ = pvcBrowseCmd.MarkFlagRequired("volumesnapshotclass")
pvcBrowseCmd.Flags().StringVarP(&namespace, "namespace", "n", fio.DefaultNS, "The namespace of the PersistentVolumeClaim.")
pvcBrowseCmd.Flags().Int64VarP(&csiCheckRunAsUser, "runAsUser", "u", 0, "Runs the inspector pod as a user (int)")
pvcBrowseCmd.Flags().IntVarP(&pvcBrowseLocalPort, "localport", "l", 8080, "The local port to expose the inspector")
}
// Execute executes the main command
@ -209,12 +232,12 @@ func CSICheck(ctx context.Context, output, outfile,
testName := "CSI checker test"
kubecli, err := kubestr.LoadKubeCli()
if err != nil {
fmt.Printf("Failed to load kubeCLi (%s)", err.Error())
fmt.Printf("Failed to load kubeCli (%s)", err.Error())
return err
}
dyncli, err := kubestr.LoadDynCli()
if err != nil {
fmt.Printf("Failed to load kubeCLi (%s)", err.Error())
fmt.Printf("Failed to load dynCli (%s)", err.Error())
return err
}
csiCheckRunner := &csi.SnapshotRestoreRunner{
@ -243,3 +266,37 @@ func CSICheck(ctx context.Context, output, outfile,
}
return err
}
func CsiPvcBrowse(ctx context.Context,
pvcName string,
namespace string,
volumeSnapshotClass string,
runAsUser int64,
localPort int,
) error {
kubecli, err := kubestr.LoadKubeCli()
if err != nil {
fmt.Printf("Failed to load kubeCli (%s)", err.Error())
return err
}
dyncli, err := kubestr.LoadDynCli()
if err != nil {
fmt.Printf("Failed to load dynCli (%s)", err.Error())
return err
}
browseRunner := &csi.PVCBrowseRunner{
KubeCli: kubecli,
DynCli: dyncli,
}
err = browseRunner.RunPVCBrowse(ctx, &csitypes.PVCBrowseArgs{
PVCName: pvcName,
Namespace: namespace,
VolumeSnapshotClass: volumeSnapshotClass,
RunAsUser: runAsUser,
LocalPort: localPort,
})
if err != nil {
fmt.Printf("Failed to run PVC browser (%s)\n", err.Error())
}
return err
}

View file

@ -22,10 +22,10 @@ Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persiste
[Bigtera VirtualStor (block)](https://github.com/bigtera-ce/ceph-csi) | `csi.block.bigtera.com` | v0.3, v1.0.0, v1.1.0 | A Container Storage Interface (CSI) Driver for Bigtera VirtualStor block storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
[Bigtera VirtualStor (filesystem)](https://github.com/bigtera-ce/ceph-csi) | `csi.fs.bigtera.com` | v0.3, v1.0.0, v1.1.0 | A Container Storage Interface (CSI) Driver for Bigtera VirtualStor filesystem | Persistent | Read/Write Multiple Pods | Yes | Expansion
[BizFlyCloud Block Storage](https://github.com/bizflycloud/csi-bizflycloud) | `volume.csi.bizflycloud.vn` | v1.2 | A Container Storage Interface (CSI) Driver for BizFly Cloud block storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion
[CephFS](https://github.com/ceph/ceph-csi) | `cephfs.csi.ceph.com` | v0.3, v1.0.0, v1.1.0, v1.2.0 | A Container Storage Interface (CSI) Driver for CephFS | Persistent | Read/Write Multiple Pods | Yes | Expansion, Snapshot, Clone
[Ceph RBD](https://github.com/ceph/ceph-csi) | `rbd.csi.ceph.com` | v0.3, v1.0.0, v1.1.0, v1.2.0 | A Container Storage Interface (CSI) Driver for Ceph RBD | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology, Cloning
[CephFS](https://github.com/ceph/ceph-csi) | `cephfs.csi.ceph.com` | v0.3, >=v1.0.0 | A Container Storage Interface (CSI) Driver for CephFS | Persistent | Read/Write Multiple Pods | Yes | Expansion, Snapshot, Cloning
[Ceph RBD](https://github.com/ceph/ceph-csi) | `rbd.csi.ceph.com` | v0.3, >=v1.0.0 | A Container Storage Interface (CSI) Driver for Ceph RBD | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology, Cloning
[ChubaoFS](https://github.com/chubaofs/chubaofs-csi) | `csi.chubaofs.com` | v1.0.0 | A Container Storage Interface (CSI) Driver for ChubaoFS Storage | Persistent | Read/Write Multiple Pods | Yes |
[Cinder](https://github.com/kubernetes/cloud-provider-openstack/tree/master/pkg/csi/cinder) | `cinder.csi.openstack.org` | v0.3, v1.0, v1.1 | A Container Storage Interface (CSI) Driver for OpenStack Cinder | Persistent and Ephemeral | Depends on the storage backend used | Yes, if storage backend supports it | Raw Block, Snapshot, Expansion
[Cinder](https://github.com/kubernetes/cloud-provider-openstack/tree/master/pkg/csi/cinder) | `cinder.csi.openstack.org` | v0.3, v1.0, v1.1.0, v1.2.0, v1.3.0 | A Container Storage Interface (CSI) Driver for OpenStack Cinder | Persistent and Ephemeral | Depends on the storage backend used | Yes, if storage backend supports it | Raw Block, Snapshot, Expansion, Cloning, Topology
[cloudscale.ch](https://github.com/cloudscale-ch/csi-cloudscale) | `csi.cloudscale.ch` | v1.0 | A Container Storage Interface (CSI) Driver for the [cloudscale.ch](https://www.cloudscale.ch/) IaaS platform | Persistent | Read/Write Single Pod | Yes |Snapshot
[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
@ -36,11 +36,10 @@ Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persiste
[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 and Ephemeral | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[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 and Ephemeral | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[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, Cloning, Topology
[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,v1.3,v1.4,v1.5 | 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/)), [Synology](https://www.synology.com/), and more | 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
[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
[Dothill-CSI](https://github.com/enix/dothill-csi) | `dothill.csi.enix.io` | v1.3 | Generic CSI plugin supporting [Seagate AssuredSan](https://www.seagate.com/fr/fr/support/dothill-san/assuredsan-pro-5000-series/) appliances such as [HPE MSA](https://www.hpe.com/us/en/storage/flash-hybrid.html), [Dell EMC PowerVault ME4](https://www.dell.com/fr-fr/work/shop/productdetailstxn/powervault-me4-series) and others ... | Persistent | Read/Write Single Node | Yes | Snapshot, Expansion
[DriveScale](https://github.com/DriveScale/k8s-plugins) | `csi.drivescale.com` | v1.0 |A Container Storage Interface (CSI) Driver for DriveScale software composable infrastructure solution | Persistent | Read/Write Single Pod | Yes |
[Ember CSI](https://ember-csi.io) | `[x].ember-csi.io` | v0.2, v0.3, v1.0 | Multi-vendor CSI plugin supporting over 80 Drivers to provide block and mount storage to Container Orchestration systems. | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot
[Excelero NVMesh](https://github.com/Excelero/nvmesh-csi-driver) | `nvmesh-csi.excelero.com` | v1.0, v1.1 | A Container Storage Interface (CSI) Driver for Excelero NVMesh | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Expansion
[GCE Persistent Disk](https://github.com/kubernetes-sigs/gcp-compute-persistent-disk-csi-driver) | `pd.csi.storage.gke.io` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for Google Compute Engine Persistent Disk (GCE PD) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology
@ -52,10 +51,11 @@ Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persiste
[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
[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.3 | A [multi-platform](https://scod.hpedev.io/csi_driver) Container Storage Interface (CSI) driver. Supports [HPE Alletra](https://hpe.com/storage/alletra), [Nimble Storage](https://hpe.com/storage/nimble), [Primera](https://hpe.com/storage/primera) and [3PAR](https://hpe.com/storage/3par) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
[HPE Ezmeral (MapR)](https://github.com/mapr/mapr-csi) | `com.mapr.csi-kdf` | v1.3 | A Container Storage Interface (CSI) Driver for HPE Ezmeral Data Fabric | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
[Huawei Storage CSI](https://github.com/Huawei/eSDK_K8S_Plugin) | `csi.huawei.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for FusionStorage, OceanStor 100D, OceanStor Pacific, OceanStor Dorado V3, OceanStor Dorado V6, OceanStor V3, OceanStor V5 | Persistent | Read/Write Multiple Pod | Yes | Snapshot, Expansion, Cloning
[HyperV CSI](https://github.com/Zetanova/hyperv-csi-driver) | `eu.zetanova.csi.hyperv` | v1.0, v1.1 | A Container Storage Interface (CSI) driver to manage hyperv hosts | Persistent | Read/Write Multiple Pods | Yes |
[IBM Block Storage](https://github.com/ibm/ibm-block-csi-driver) | `block.csi.ibm.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) [Driver](https://www.ibm.com/support/knowledgecenter/SSRQ8T) for IBM Spectrum Virtualize Family, IBM FlashSystem A9000 and A9000R, IBM DS8880 and DS8900. | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning
[IBM Block Storage](https://github.com/ibm/ibm-block-csi-driver) | `block.csi.ibm.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) [Driver](https://www.ibm.com/docs/en/stg-block-csi-driver) for IBM Spectrum Virtualize Family, IBM FlashSystem A9000 and A9000R, IBM DS8000 Family 8.x and higher. | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[IBM Spectrum Scale](https://github.com/IBM/ibm-spectrum-scale-csi) | `spectrumscale.csi.ibm.com` | v1.0, v1.1 | A Container Storage Interface (CSI) [Driver](https://www.ibm.com/docs/en/spectrum-scale-csi) for the IBM Spectrum Scale File System | Persistent | Read/Write Multiple Pod | Yes | Snapshot
[IBM Cloud Block Storage VPC CSI Driver](https://cloud.ibm.com/docs/containers?topic=containers-vpc-block) | `vpc.block.csi.ibm.io` | v1.0 | A Container Storage Interface (CSI) [Driver](https://cloud.ibm.com/docs/containers?topic=containers-vpc-block) for IBM Cloud Kubernetes Service and Red Hat OpenShift on IBM Cloud | Persistent | Read/Write Single Pod | Yes | Raw Block |
[Infinidat](https://github.com/Infinidat/infinibox-csi-driver) | `infinibox-csi-driver` | v1.0, v1.1 | A Container Storage Interface (CSI) Driver for Infinidat [InfiniBox](https://infinidat.com/en/products-technology/infinibox) | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning
@ -63,25 +63,26 @@ 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
[Intelliflash Block Storage](https://github.com/DDNStorage/intelliflash-csi-block-driver) | `intelliflash-csi-block-driver.intelliflash.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for Intelliflash Block Storage | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning, Topology
[Intelliflash File Storage](https://github.com/DDNStorage/intelliflash-csi-file-driver) | `intelliflash-csi-file-driver.intelliflash.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for Intelliflash File Storage | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning, Topology
[ionir ](https://github.com/ionir-cloud) | `ionir` | v1.2 | A Container Storage Interface (CSI) Driver for [ionir](https://www.ionir.com/) Kubernetes-Native Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Cloning
[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 |
[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 |
[LINSTOR](https://github.com/piraeusdatastore/linstor-csi) | `linstor.csi.linbit.com` | v1.2 | A Container Storage Interface (CSI) Driver for [LINSTOR](https://www.linbit.com/en/linstor/) volumes | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[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.2 | A Container Storage Interface (CSI) Driver for [Longhorn](https://longhorn.io/) volumes | Persistent | Read/Write Single Node | Yes | Raw Block
[MacroSAN](https://github.com/macrosan-csi/macrosan-csi-driver) | `csi-macrosan` | v1.0 | A Container Storage Interface (CSI) Driver for MacroSAN Block Storage | Persistent | Read/Write Single Pod | Yes |
[Manila](https://github.com/kubernetes/cloud-provider-openstack/tree/master/pkg/csi/manila) | `manila.csi.openstack.org` | v1.1, v1.2 | A Container Storage Interface (CSI) Driver for OpenStack Shared File System Service (Manila) | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Topology
[MapR](https://github.com/mapr/mapr-csi) | `com.mapr.csi-kdf` | v1.0 | A Container Storage Interface (CSI) Driver for MapR Data Platform | Persistent | Read/Write Multiple Pods | Yes | Snapshot
[MooseFS](https://github.com/moosefs/moosefs-csi) | `com.tuxera.csi.moosefs` | v1.0 | A Container Storage Interface (CSI) Driver for [MooseFS](https://moosefs.com/) clusters. | Persistent | Read/Write Multiple Pods | Yes |
[NetApp](https://github.com/NetApp/trident) | `csi.trident.netapp.io` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for NetApp's [Trident](https://netapp-trident.readthedocs.io/) container storage orchestrator | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[NetApp](https://github.com/NetApp/trident) | `csi.trident.netapp.io` | v1.0, v1.1, v1.2, v1.3 | A Container Storage Interface (CSI) Driver for NetApp's [Trident](https://netapp-trident.readthedocs.io/) container storage orchestrator | Persistent | Read/Write Multiple Pods | Yes | Raw Block, Snapshot, Expansion, Cloning, Topology
[NexentaStor File Storage](https://github.com/Nexenta/nexentastor-csi-driver) | `nexentastor-csi-driver.nexenta.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for NexentaStor File Storage | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning, Topology
[NexentaStor Block Storage](https://github.com/Nexenta/nexentastor-csi-driver-block) | `nexentastor-block-csi-driver.nexenta.com` | v1.0, v1.1, v1.2 | A Container Storage Interface (CSI) Driver for NexentaStor over iSCSI protocol | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning, Topology, Raw block
[Nutanix](https://github.com/nutanix/csi-plugin) | `com.nutanix.csi` | v0.3, v1.0, v1.2 | A Container Storage Interface (CSI) Driver for Nutanix | Persistent | "Read/Write Single Pod" with Nutanix Volumes and "Read/Write Multiple Pods" with Nutanix Files | Yes | Raw Block, Snapshot, Expansion, Cloning
[OpenEBS](https://github.com/openebs/csi)| `cstor.csi.openebs.io` | v1.0 | A Container Storage Interface (CSI) Driver for [OpenEBS](https://www.openebs.io/)| Persistent | Read/Write Single Pod | Yes | Expansion, 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
[Open-Local](https://github.com/alibaba/open-local) | `local.csi.alibaba.com` | v1.0 | A Container Storage Interface (CSI) Driver for Local Storage | Persistent | Read/Write Single Pod | Yes | Raw Block, Expansion, Snapshot
[Oracle Cloud Infrastructure(OCI) Block Storage](https://github.com/oracle/oci-cloud-controller-manager/blob/master/container-storage-interface.md) | `blockvolume.csi.oraclecloud.com` | v1.1 | A Container Storage Interface (CSI) Driver for Oracle Cloud Infrastructure (OCI) Block Storage | Persistent | Read/Write Single Pod | Yes | Topology
[oVirt](https://github.com/openshift/ovirt-csi-driver) | `csi.ovirt.org` | v1.0 | A Container Storage Interface (CSI) Driver for [oVirt](https://ovirt.org) | Persistent | Read/Write Single Pod | Yes | Block, File Storage
[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.portworx.com` | v1.4 | A Container Storage Interface (CSI) Driver for [Portworx](https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/csi/) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Raw Block, Cloning
[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 | Raw Block, Snapshot, Expansion, Cloning
[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
@ -91,6 +92,7 @@ Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persiste
[Sangfor-EDS-File-Storage](https://github.com/evan37717/sangfor-eds-csi) | `eds.csi.file.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 |
[Scaleway CSI](https://github.com/scaleway/scaleway-csi) | `csi.scaleway.com` | v1.2.0 | Container Storage Interface (CSI) Driver for [Scaleway Block Storage](https://www.scaleway.com/block-storage/) | Persistent | Read/Write Single Pod | Yes | Raw Block, Snapshot, Expansion, Topology
[Seagate Exos X](https://github.com/Seagate/seagate-exos-x-csi) | `csi-exos-x.seagate.com` | v1.3 | CSI driver for [Seagate Exos X](https://www.seagate.com/products/storage/data-storage-systems/raid/) and OEM systems | Persistent | Read/Write Single Pod | Yes | Snapshot, Expansion, Cloning
[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 |
[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
@ -99,6 +101,7 @@ Name | CSI Driver Name | Compatible with CSI Version(s) | Description | Persiste
[StorageOS](https://docs.storageos.com/docs/platforms/kubernetes/install/) | `storageos` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for [StorageOS](https://storageos.com/) | Persistent | Read/Write Multiple Pods | Yes |
[Storidge](https://docs.storidge.com/kubernetes_storage/overview.html) | `csi.cio.storidge.com` | v0.3, v1.0 | A Container Storage Interface (CSI) Driver for [Storidge CIO](https://storidge.com/) | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion
[StorPool](https://kb.storpool.com/storpool_integrations/github/kubernetes.html) | `csi-driver.storpool.com` | v1.0 | A Container Storage Interface (CSI) Driver for [StorPool](https://storpool.com/) | Persistent and Ephemeral | Read/Write Multiple Pods | Yes | Expansion
[Synology](https://github.com/SynologyOpenSource/synology-csi) | `csi.san.synology.com` | v1.0 | A Container Storage Interface (CSI) Driver for Synology NAS | Persistent | Read/Write Multiple Pods | Yes | Snapshot, Expansion, Cloning
[Tencent Cloud Block Storage](https://github.com/TencentCloud/kubernetes-csi-tencentcloud)| `com.tencent.cloud.csi.cbs` | v1.0 | A Container Storage Interface (CSI) Driver for Tencent Cloud Block Storage | Persistent | Read/Write Single Pod | Yes | Snapshot
[Tencent Cloud File Storage](https://github.com/TencentCloud/kubernetes-csi-tencentcloud)| `com.tencent.cloud.csi.cfs` | v1.0 | A Container Storage Interface (CSI) Driver for Tencent Cloud File Storage | Persistent | Read/Write Multiple Pods | Yes |
[Tencent Cloud Object Storage](https://github.com/TencentCloud/kubernetes-csi-tencentcloud)| `com.tencent.cloud.csi.cosfs` | v1.0 | A Container Storage Interface (CSI) Driver for Tencent Cloud Object Storage | Persistent | Read/Write Multiple Pods | No |
@ -107,6 +110,7 @@ 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-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 |
[VDA](https://virtual-disk-array.readthedocs.io/en/latest/Introduction.html) | `csi.vda.io` | v1.0 | An open source block storage system base on SPDK | Persistent | Read/Write Single Pod | N/A |
[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 |

8
go.mod
View file

@ -11,12 +11,10 @@ require (
github.com/jarcoal/httpmock v1.0.5 // indirect
github.com/kanisterio/kanister v0.0.0-20210805190523-86f566052e0e
github.com/kubernetes-csi/external-snapshotter/client/v4 v4.0.0
github.com/onsi/ginkgo v1.12.0 // indirect
github.com/onsi/gomega v1.9.0 // indirect
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.1.1
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
k8s.io/api v0.20.1
k8s.io/apimachinery v0.20.1
k8s.io/client-go v0.20.1
k8s.io/api v0.22.2
k8s.io/apimachinery v0.22.2
k8s.io/client-go v0.22.2
)

39
go.sum
View file

@ -287,6 +287,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
@ -397,6 +399,8 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -448,6 +452,7 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
github.com/gomodule/redigo v1.8.3/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/fswalker v0.2.1-0.20200214223026-f0e929ba4126/go.mod h1:ZSEBqY0IHKqWPeAbTyvccv9bb9vCnaQfHe31cm911Ng=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -512,8 +517,11 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
github.com/googleapis/gnostic v0.5.3 h1:2qsuRm+bzgwSIKikigPASa2GhW8H2Dn4Qq7UxD8K/48=
github.com/googleapis/gnostic v0.5.3/go.mod h1:TRWw1s4gxBGjSe301Dai3c7wXJAZy57+/6tawkOvqHQ=
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
@ -640,6 +648,7 @@ github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dv
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
@ -750,6 +759,8 @@ github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxd
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mmcloughlin/avo v0.0.0-20201105074841-5d2f697d268f/go.mod h1:6aKT4zZIrpGqB3RpFU14ByCSSyKY6LfJz4J/JJChHfI=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
@ -785,6 +796,7 @@ github.com/ncw/directio v1.0.5/go.mod h1:rX/pKEYkOXBGOggmcyJeJGloCkleSvphPx2eV3t
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nsqio/go-nsq v1.0.8/go.mod h1:vKq36oyeVXgsS5Q8YEO7WghqidAVXQlcFxzQbQTuDEY=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
@ -797,6 +809,8 @@ github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
@ -804,6 +818,7 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/openshift/api v0.0.0-20200521101457-60c476765272/go.mod h1:TkhafijfTiRi1Q3120/ZSE4oIWKQ4DGRh3byPywv4Mw=
github.com/openshift/api v0.0.0-20200526144822-34f54f12813a h1:riE/kCXnb051RWT/z+DytxKEZ3+JromVDl79rXAKyFY=
@ -1097,6 +1112,7 @@ golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@ -1181,6 +1197,7 @@ golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
@ -1202,6 +1219,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210420210106-798c2154c571/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -1265,6 +1283,7 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -1291,6 +1310,7 @@ golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8=
@ -1338,6 +1358,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -1364,6 +1385,8 @@ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -1425,6 +1448,7 @@ golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d h1:szSOL78iTCl0LF1AMjhSWJj8tIM0KixlUUnBtYXsmd8=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
@ -1435,6 +1459,7 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@ -1677,6 +1702,8 @@ k8s.io/api v0.19.0 h1:XyrFIJqTYZJ2DU7FBE/bSPz7b1HvbVBuBf07oeo6eTc=
k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw=
k8s.io/api v0.20.1 h1:ud1c3W3YNzGd6ABJlbFfKXBKXO+1KdGfcgGGNgFR03E=
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw=
k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8=
k8s.io/apiextensions-apiserver v0.20.1 h1:ZrXQeslal+6zKM/HjDXLzThlz/vPSxrfK3OqL8txgVQ=
k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk=
k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
@ -1684,12 +1711,16 @@ k8s.io/apimachinery v0.19.0 h1:gjKnAda/HZp5k4xQYjL0K/Yb66IvNqjthCb03QlKpaQ=
k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
k8s.io/apimachinery v0.20.1 h1:LAhz8pKbgR8tUwn7boK+b2HZdt7MiTu2mkYtFMUjTRQ=
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk=
k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
k8s.io/client-go v0.19.0 h1:1+0E0zfWFIWeyRhQYWzimJOyAk2UT7TiARaLNwJCf7k=
k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU=
k8s.io/client-go v0.20.1 h1:Qquik0xNFbK9aUG92pxHYsyfea5/RPO9o9bSywNor+M=
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc=
k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U=
k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
k8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
@ -1708,17 +1739,23 @@ k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ=
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K8Hf8whTseBgJcg=
k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
@ -1732,6 +1769,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSq
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

392
pkg/csi/csi_ops.go Normal file
View file

@ -0,0 +1,392 @@
package csi
import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/kanisterio/kanister/pkg/kube"
kankube "github.com/kanisterio/kanister/pkg/kube"
kansnapshot "github.com/kanisterio/kanister/pkg/kube/snapshot"
"github.com/kastenhq/kubestr/pkg/common"
"github.com/kastenhq/kubestr/pkg/csi/types"
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
sv1 "k8s.io/api/storage/v1"
"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"
"k8s.io/client-go/rest"
pf "k8s.io/client-go/tools/portforward"
"k8s.io/client-go/transport/spdy"
)
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_argument_validator.go -package=mocks . ArgumentValidator
type ArgumentValidator interface {
//Rename
ValidatePVC(ctx context.Context, pvcName, namespace string) (*v1.PersistentVolumeClaim, error)
FetchPV(ctx context.Context, pvName string) (*v1.PersistentVolume, error)
ValidateNamespace(ctx context.Context, namespace string) error
ValidateStorageClass(ctx context.Context, storageClass string) (*sv1.StorageClass, error)
ValidateVolumeSnapshotClass(ctx context.Context, volumeSnapshotClass string, groupVersion *metav1.GroupVersionForDiscovery) (*unstructured.Unstructured, error)
}
type validateOperations struct {
kubeCli kubernetes.Interface
dynCli dynamic.Interface
}
func (o *validateOperations) ValidatePVC(ctx context.Context, pvcName, namespace string) (*v1.PersistentVolumeClaim, error) {
if o.kubeCli == nil {
return nil, fmt.Errorf("kubeCli not initialized")
}
return o.kubeCli.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, pvcName, metav1.GetOptions{})
}
func (o *validateOperations) FetchPV(ctx context.Context, pvName string) (*v1.PersistentVolume, error) {
if o.kubeCli == nil {
return nil, fmt.Errorf("kubeCli not initialized")
}
return o.kubeCli.CoreV1().PersistentVolumes().Get(ctx, pvName, metav1.GetOptions{})
}
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: common.SnapGroupName, Version: groupVersion.Version, Resource: common.VolumeSnapshotClassResourcePlural}
return o.dynCli.Resource(VolSnapClassGVR).Get(ctx, volumeSnapshotClass, metav1.GetOptions{})
}
//go:generate go run github.com/golang/mock/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 = common.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: args.Command,
Args: args.ContainerArgs,
VolumeMounts: []v1.VolumeMount{{
Name: "persistent-storage",
MountPath: args.MountPath,
}},
}},
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 go run github.com/golang/mock/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) (*snapv1.VolumeSnapshot, error)
CreateFromSourceCheck(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.CreateFromSourceCheckArgs, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) 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) (*snapv1.VolumeSnapshot, error) {
if snapshotter == nil || args == nil {
return nil, fmt.Errorf("snapshotter or args are empty")
}
if err := args.Validate(); err != nil {
return nil, err
}
err := snapshotter.Create(ctx, args.SnapshotName, args.Namespace, args.PVCName, &args.VolumeSnapshotClass, true, nil)
if err != nil {
return nil, errors.Wrapf(err, "CSI Driver failed to create snapshot for PVC (%s) in Namespace (%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, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) error {
if c.dynCli == nil {
return fmt.Errorf("dynCli not initialized")
}
if SnapshotGroupVersion == nil || SnapshotGroupVersion.Version == "" {
return fmt.Errorf("snapshot group version not provided")
}
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(ctx, args.VolumeSnapshotClass, targetSnapClassName, kansnapshot.DeletionPolicyRetain, nil)
if err != nil {
return errors.Wrapf(err, "Failed to clone a VolumeSnapshotClass to use to restore the snapshot")
}
defer func() {
VolSnapClassGVR := schema.GroupVersionResource{Group: common.SnapGroupName, Version: SnapshotGroupVersion.Version, Resource: common.VolumeSnapshotClassResourcePlural}
err := c.dynCli.Resource(VolSnapClassGVR).Delete(ctx, targetSnapClassName, metav1.DeleteOptions{})
if err != nil {
fmt.Printf("Delete VSC Error (%s) - (%v)\n", targetSnapClassName, err)
}
}()
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 go run github.com/golang/mock/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, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) 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, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) error {
if c.dynCli == nil {
return fmt.Errorf("dynCli not initialized")
}
if SnapshotGroupVersion == nil || SnapshotGroupVersion.Version == "" {
return fmt.Errorf("snapshot group version not provided")
}
VolSnapGVR := schema.GroupVersionResource{Group: common.SnapGroupName, Version: SnapshotGroupVersion.Version, Resource: common.VolumeSnapshotResourcePlural}
return c.dynCli.Resource(VolSnapGVR).Namespace(namespace).Delete(ctx, snapshotName, metav1.DeleteOptions{})
}
//go:generate go run github.com/golang/mock/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 == common.SnapGroupName {
return &group.PreferredVersion, nil
}
}
return nil, fmt.Errorf("Snapshot API group not found")
}
//go:generate go run github.com/golang/mock/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
}
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_port_forwarder.go -package=mocks . PortForwarder
type PortForwarder interface {
FetchRestConfig() (*rest.Config, error)
PortForwardAPod(req *types.PortForwardAPodRequest) error
}
type portforward struct{}
func (p *portforward) PortForwardAPod(req *types.PortForwardAPodRequest) error {
path := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward",
req.Pod.Namespace, req.Pod.Name)
hostIP := strings.TrimLeft(req.RestConfig.Host, "https:/")
transport, upgrader, err := spdy.RoundTripperFor(req.RestConfig)
if err != nil {
return err
}
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, http.MethodPost, &url.URL{Scheme: "https", Path: path, Host: hostIP})
fw, err := pf.New(dialer, []string{fmt.Sprintf("%d:%d", req.LocalPort, req.PodPort)}, req.StopCh, req.ReadyCh, &req.OutStream, &req.ErrOutStream)
if err != nil {
return err
}
return fw.ForwardPorts()
}
func (p *portforward) FetchRestConfig() (*rest.Config, error) {
return kube.LoadConfig()
}

View file

@ -474,7 +474,7 @@ func (s *CSITestSuite) TestCreatePod(c *C) {
GenerateName: "name",
PVCName: "pvcname",
Namespace: "ns",
Cmd: "somecommand",
Command: []string{"somecommand"},
RunAsUser: 1000,
ContainerImage: "containerimage",
},
@ -487,7 +487,7 @@ func (s *CSITestSuite) TestCreatePod(c *C) {
GenerateName: "name",
PVCName: "pvcname",
Namespace: "ns",
Cmd: "somecommand",
Command: []string{"somecommand"},
},
errChecker: IsNil,
podChecker: NotNil,
@ -498,7 +498,7 @@ func (s *CSITestSuite) TestCreatePod(c *C) {
GenerateName: "name",
PVCName: "pvcname",
Namespace: "ns",
Cmd: "somecommand",
Command: []string{"somecommand"},
},
failCreates: true,
errChecker: NotNil,
@ -510,7 +510,7 @@ func (s *CSITestSuite) TestCreatePod(c *C) {
GenerateName: "",
PVCName: "pvcname",
Namespace: "ns",
Cmd: "somecommand",
Command: []string{"somecommand"},
},
errChecker: NotNil,
podChecker: IsNil,
@ -521,7 +521,7 @@ func (s *CSITestSuite) TestCreatePod(c *C) {
GenerateName: "name",
PVCName: "",
Namespace: "ns",
Cmd: "somecommand",
Command: []string{"somecommand"},
},
errChecker: NotNil,
podChecker: IsNil,
@ -532,7 +532,7 @@ func (s *CSITestSuite) TestCreatePod(c *C) {
GenerateName: "name",
PVCName: "pvcname",
Namespace: "",
Cmd: "somecommand",
Command: []string{"somecommand"},
},
errChecker: NotNil,
podChecker: IsNil,
@ -543,10 +543,10 @@ func (s *CSITestSuite) TestCreatePod(c *C) {
GenerateName: "name",
PVCName: "pvcname",
Namespace: "ns",
Cmd: "",
Command: []string{"somecommand"},
},
errChecker: NotNil,
podChecker: IsNil,
errChecker: IsNil,
podChecker: NotNil,
},
{
cli: nil,
@ -571,11 +571,11 @@ func (s *CSITestSuite) TestCreatePod(c *C) {
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].Command, DeepEquals, tc.args.Command)
c.Assert(pod.Spec.Containers[0].Args, DeepEquals, tc.args.ContainerArgs)
c.Assert(pod.Spec.Containers[0].VolumeMounts, DeepEquals, []v1.VolumeMount{{
Name: "persistent-storage",
MountPath: "/data",
MountPath: tc.args.MountPath,
}})
c.Assert(pod.Spec.Volumes, DeepEquals, []v1.Volume{{
Name: "persistent-storage",

View file

@ -5,35 +5,36 @@
package mocks
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
reflect "reflect"
)
// MockApiVersionFetcher is a mock of ApiVersionFetcher interface
// MockApiVersionFetcher is a mock of ApiVersionFetcher interface.
type MockApiVersionFetcher struct {
ctrl *gomock.Controller
recorder *MockApiVersionFetcherMockRecorder
}
// MockApiVersionFetcherMockRecorder is the mock recorder for MockApiVersionFetcher
// MockApiVersionFetcherMockRecorder is the mock recorder for MockApiVersionFetcher.
type MockApiVersionFetcherMockRecorder struct {
mock *MockApiVersionFetcher
}
// NewMockApiVersionFetcher creates a new mock instance
// 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
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockApiVersionFetcher) EXPECT() *MockApiVersionFetcherMockRecorder {
return m.recorder
}
// GetCSISnapshotGroupVersion mocks base method
// GetCSISnapshotGroupVersion mocks base method.
func (m *MockApiVersionFetcher) GetCSISnapshotGroupVersion() (*v1.GroupVersionForDiscovery, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCSISnapshotGroupVersion")
@ -42,7 +43,7 @@ func (m *MockApiVersionFetcher) GetCSISnapshotGroupVersion() (*v1.GroupVersionFo
return ret0, ret1
}
// GetCSISnapshotGroupVersion indicates an expected call of GetCSISnapshotGroupVersion
// 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))

View file

@ -6,36 +6,37 @@ package mocks
import (
context "context"
reflect "reflect"
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
// MockApplicationCreator is a mock of ApplicationCreator interface.
type MockApplicationCreator struct {
ctrl *gomock.Controller
recorder *MockApplicationCreatorMockRecorder
}
// MockApplicationCreatorMockRecorder is the mock recorder for MockApplicationCreator
// MockApplicationCreatorMockRecorder is the mock recorder for MockApplicationCreator.
type MockApplicationCreatorMockRecorder struct {
mock *MockApplicationCreator
}
// NewMockApplicationCreator creates a new mock instance
// 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
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockApplicationCreator) EXPECT() *MockApplicationCreatorMockRecorder {
return m.recorder
}
// CreatePVC mocks base method
// 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)
@ -44,13 +45,13 @@ func (m *MockApplicationCreator) CreatePVC(arg0 context.Context, arg1 *types.Cre
return ret0, ret1
}
// CreatePVC indicates an expected call of CreatePVC
// 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
// 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)
@ -59,13 +60,13 @@ func (m *MockApplicationCreator) CreatePod(arg0 context.Context, arg1 *types.Cre
return ret0, ret1
}
// CreatePod indicates an expected call of CreatePod
// 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
// 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)
@ -73,7 +74,7 @@ func (m *MockApplicationCreator) WaitForPodReady(arg0 context.Context, arg1, arg
return ret0
}
// WaitForPodReady indicates an expected call of WaitForPodReady
// 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)

View file

@ -6,37 +6,54 @@ 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"
gomock "github.com/golang/mock/gomock"
v1 "k8s.io/api/core/v1"
v10 "k8s.io/api/storage/v1"
v11 "k8s.io/apimachinery/pkg/apis/meta/v1"
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// MockArgumentValidator is a mock of ArgumentValidator interface
// MockArgumentValidator is a mock of ArgumentValidator interface.
type MockArgumentValidator struct {
ctrl *gomock.Controller
recorder *MockArgumentValidatorMockRecorder
}
// MockArgumentValidatorMockRecorder is the mock recorder for MockArgumentValidator
// MockArgumentValidatorMockRecorder is the mock recorder for MockArgumentValidator.
type MockArgumentValidatorMockRecorder struct {
mock *MockArgumentValidator
}
// NewMockArgumentValidator creates a new mock instance
// 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
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockArgumentValidator) EXPECT() *MockArgumentValidatorMockRecorder {
return m.recorder
}
// ValidateNamespace mocks base method
// FetchPV mocks base method.
func (m *MockArgumentValidator) FetchPV(arg0 context.Context, arg1 string) (*v1.PersistentVolume, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FetchPV", arg0, arg1)
ret0, _ := ret[0].(*v1.PersistentVolume)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FetchPV indicates an expected call of FetchPV.
func (mr *MockArgumentValidatorMockRecorder) FetchPV(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchPV", reflect.TypeOf((*MockArgumentValidator)(nil).FetchPV), arg0, arg1)
}
// 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)
@ -44,29 +61,44 @@ func (m *MockArgumentValidator) ValidateNamespace(arg0 context.Context, arg1 str
return ret0
}
// ValidateNamespace indicates an expected call of ValidateNamespace
// 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) {
// ValidatePVC mocks base method.
func (m *MockArgumentValidator) ValidatePVC(arg0 context.Context, arg1, arg2 string) (*v1.PersistentVolumeClaim, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateStorageClass", arg0, arg1)
ret0, _ := ret[0].(*v1.StorageClass)
ret := m.ctrl.Call(m, "ValidatePVC", arg0, arg1, arg2)
ret0, _ := ret[0].(*v1.PersistentVolumeClaim)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ValidateStorageClass indicates an expected call of ValidateStorageClass
// ValidatePVC indicates an expected call of ValidatePVC.
func (mr *MockArgumentValidatorMockRecorder) ValidatePVC(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatePVC", reflect.TypeOf((*MockArgumentValidator)(nil).ValidatePVC), arg0, arg1, arg2)
}
// ValidateStorageClass mocks base method.
func (m *MockArgumentValidator) ValidateStorageClass(arg0 context.Context, arg1 string) (*v10.StorageClass, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateStorageClass", arg0, arg1)
ret0, _ := ret[0].(*v10.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) {
// ValidateVolumeSnapshotClass mocks base method.
func (m *MockArgumentValidator) ValidateVolumeSnapshotClass(arg0 context.Context, arg1 string, arg2 *v11.GroupVersionForDiscovery) (*unstructured.Unstructured, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateVolumeSnapshotClass", arg0, arg1, arg2)
ret0, _ := ret[0].(*unstructured.Unstructured)
@ -74,7 +106,7 @@ func (m *MockArgumentValidator) ValidateVolumeSnapshotClass(arg0 context.Context
return ret0, ret1
}
// ValidateVolumeSnapshotClass indicates an expected call of ValidateVolumeSnapshotClass
// 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)

View file

@ -6,35 +6,36 @@ package mocks
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
reflect "reflect"
)
// MockCleaner is a mock of Cleaner interface
// MockCleaner is a mock of Cleaner interface.
type MockCleaner struct {
ctrl *gomock.Controller
recorder *MockCleanerMockRecorder
}
// MockCleanerMockRecorder is the mock recorder for MockCleaner
// MockCleanerMockRecorder is the mock recorder for MockCleaner.
type MockCleanerMockRecorder struct {
mock *MockCleaner
}
// NewMockCleaner creates a new mock instance
// 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
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockCleaner) EXPECT() *MockCleanerMockRecorder {
return m.recorder
}
// DeletePVC mocks base method
// 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)
@ -42,13 +43,13 @@ func (m *MockCleaner) DeletePVC(arg0 context.Context, arg1, arg2 string) error {
return ret0
}
// DeletePVC indicates an expected call of DeletePVC
// 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
// 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)
@ -56,13 +57,13 @@ func (m *MockCleaner) DeletePod(arg0 context.Context, arg1, arg2 string) error {
return ret0
}
// DeletePod indicates an expected call of DeletePod
// 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
// DeleteSnapshot mocks base method.
func (m *MockCleaner) DeleteSnapshot(arg0 context.Context, arg1, arg2 string, arg3 *v1.GroupVersionForDiscovery) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteSnapshot", arg0, arg1, arg2, arg3)
@ -70,7 +71,7 @@ func (m *MockCleaner) DeleteSnapshot(arg0 context.Context, arg1, arg2 string, ar
return ret0
}
// DeleteSnapshot indicates an expected call of DeleteSnapshot
// DeleteSnapshot indicates an expected call of DeleteSnapshot.
func (mr *MockCleanerMockRecorder) DeleteSnapshot(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSnapshot", reflect.TypeOf((*MockCleaner)(nil).DeleteSnapshot), arg0, arg1, arg2, arg3)

View file

@ -5,34 +5,35 @@
package mocks
import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockDataValidator is a mock of DataValidator interface
// MockDataValidator is a mock of DataValidator interface.
type MockDataValidator struct {
ctrl *gomock.Controller
recorder *MockDataValidatorMockRecorder
}
// MockDataValidatorMockRecorder is the mock recorder for MockDataValidator
// MockDataValidatorMockRecorder is the mock recorder for MockDataValidator.
type MockDataValidatorMockRecorder struct {
mock *MockDataValidator
}
// NewMockDataValidator creates a new mock instance
// 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
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockDataValidator) EXPECT() *MockDataValidatorMockRecorder {
return m.recorder
}
// FetchPodData mocks base method
// 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)
@ -41,7 +42,7 @@ func (m *MockDataValidator) FetchPodData(arg0, arg1 string) (string, error) {
return ret0, ret1
}
// FetchPodData indicates an expected call of FetchPodData
// 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)

View file

@ -0,0 +1,65 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: PortForwarder)
// Package mocks is a generated GoMock package.
package mocks
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
types "github.com/kastenhq/kubestr/pkg/csi/types"
rest "k8s.io/client-go/rest"
)
// MockPortForwarder is a mock of PortForwarder interface.
type MockPortForwarder struct {
ctrl *gomock.Controller
recorder *MockPortForwarderMockRecorder
}
// MockPortForwarderMockRecorder is the mock recorder for MockPortForwarder.
type MockPortForwarderMockRecorder struct {
mock *MockPortForwarder
}
// NewMockPortForwarder creates a new mock instance.
func NewMockPortForwarder(ctrl *gomock.Controller) *MockPortForwarder {
mock := &MockPortForwarder{ctrl: ctrl}
mock.recorder = &MockPortForwarderMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPortForwarder) EXPECT() *MockPortForwarderMockRecorder {
return m.recorder
}
// FetchRestConfig mocks base method.
func (m *MockPortForwarder) FetchRestConfig() (*rest.Config, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FetchRestConfig")
ret0, _ := ret[0].(*rest.Config)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FetchRestConfig indicates an expected call of FetchRestConfig.
func (mr *MockPortForwarderMockRecorder) FetchRestConfig() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FetchRestConfig", reflect.TypeOf((*MockPortForwarder)(nil).FetchRestConfig))
}
// PortForwardAPod mocks base method.
func (m *MockPortForwarder) PortForwardAPod(arg0 *types.PortForwardAPodRequest) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PortForwardAPod", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// PortForwardAPod indicates an expected call of PortForwardAPod.
func (mr *MockPortForwarderMockRecorder) PortForwardAPod(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PortForwardAPod", reflect.TypeOf((*MockPortForwarder)(nil).PortForwardAPod), arg0)
}

View file

@ -0,0 +1,111 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/kastenhq/kubestr/pkg/csi (interfaces: PVCBrowserStepper)
// Package mocks is a generated GoMock package.
package mocks
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
types "github.com/kastenhq/kubestr/pkg/csi/types"
v1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
v10 "k8s.io/api/core/v1"
v11 "k8s.io/api/storage/v1"
)
// MockPVCBrowserStepper is a mock of PVCBrowserStepper interface.
type MockPVCBrowserStepper struct {
ctrl *gomock.Controller
recorder *MockPVCBrowserStepperMockRecorder
}
// MockPVCBrowserStepperMockRecorder is the mock recorder for MockPVCBrowserStepper.
type MockPVCBrowserStepperMockRecorder struct {
mock *MockPVCBrowserStepper
}
// NewMockPVCBrowserStepper creates a new mock instance.
func NewMockPVCBrowserStepper(ctrl *gomock.Controller) *MockPVCBrowserStepper {
mock := &MockPVCBrowserStepper{ctrl: ctrl}
mock.recorder = &MockPVCBrowserStepperMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPVCBrowserStepper) EXPECT() *MockPVCBrowserStepperMockRecorder {
return m.recorder
}
// Cleanup mocks base method.
func (m *MockPVCBrowserStepper) Cleanup(arg0 context.Context, arg1 *v10.PersistentVolumeClaim, arg2 *v10.Pod, arg3 *v1.VolumeSnapshot) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Cleanup", arg0, arg1, arg2, arg3)
}
// Cleanup indicates an expected call of Cleanup.
func (mr *MockPVCBrowserStepperMockRecorder) Cleanup(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cleanup", reflect.TypeOf((*MockPVCBrowserStepper)(nil).Cleanup), arg0, arg1, arg2, arg3)
}
// CreateInspectorApplication mocks base method.
func (m *MockPVCBrowserStepper) CreateInspectorApplication(arg0 context.Context, arg1 *types.PVCBrowseArgs, arg2 *v1.VolumeSnapshot, arg3 *v11.StorageClass) (*v10.Pod, *v10.PersistentVolumeClaim, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateInspectorApplication", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(*v10.Pod)
ret1, _ := ret[1].(*v10.PersistentVolumeClaim)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// CreateInspectorApplication indicates an expected call of CreateInspectorApplication.
func (mr *MockPVCBrowserStepperMockRecorder) CreateInspectorApplication(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateInspectorApplication", reflect.TypeOf((*MockPVCBrowserStepper)(nil).CreateInspectorApplication), arg0, arg1, arg2, arg3)
}
// PortForwardAPod mocks base method.
func (m *MockPVCBrowserStepper) PortForwardAPod(arg0 context.Context, arg1 *v10.Pod, arg2 int) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PortForwardAPod", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// PortForwardAPod indicates an expected call of PortForwardAPod.
func (mr *MockPVCBrowserStepperMockRecorder) PortForwardAPod(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PortForwardAPod", reflect.TypeOf((*MockPVCBrowserStepper)(nil).PortForwardAPod), arg0, arg1, arg2)
}
// SnapshotPVC mocks base method.
func (m *MockPVCBrowserStepper) SnapshotPVC(arg0 context.Context, arg1 *types.PVCBrowseArgs, arg2 string) (*v1.VolumeSnapshot, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SnapshotPVC", arg0, arg1, arg2)
ret0, _ := ret[0].(*v1.VolumeSnapshot)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SnapshotPVC indicates an expected call of SnapshotPVC.
func (mr *MockPVCBrowserStepperMockRecorder) SnapshotPVC(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SnapshotPVC", reflect.TypeOf((*MockPVCBrowserStepper)(nil).SnapshotPVC), arg0, arg1, arg2)
}
// ValidateArgs mocks base method.
func (m *MockPVCBrowserStepper) ValidateArgs(arg0 context.Context, arg1 *types.PVCBrowseArgs) (*v11.StorageClass, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateArgs", arg0, arg1)
ret0, _ := ret[0].(*v11.StorageClass)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ValidateArgs indicates an expected call of ValidateArgs.
func (mr *MockPVCBrowserStepperMockRecorder) ValidateArgs(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateArgs", reflect.TypeOf((*MockPVCBrowserStepper)(nil).ValidateArgs), arg0, arg1)
}

View file

@ -6,67 +6,68 @@ package mocks
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
snapshot "github.com/kanisterio/kanister/pkg/kube/snapshot"
types "github.com/kastenhq/kubestr/pkg/csi/types"
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
reflect "reflect"
v1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
v10 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// MockSnapshotCreator is a mock of SnapshotCreator interface
// MockSnapshotCreator is a mock of SnapshotCreator interface.
type MockSnapshotCreator struct {
ctrl *gomock.Controller
recorder *MockSnapshotCreatorMockRecorder
}
// MockSnapshotCreatorMockRecorder is the mock recorder for MockSnapshotCreator
// MockSnapshotCreatorMockRecorder is the mock recorder for MockSnapshotCreator.
type MockSnapshotCreatorMockRecorder struct {
mock *MockSnapshotCreator
}
// NewMockSnapshotCreator creates a new mock instance
// 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
// 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, arg3 *v1.GroupVersionForDiscovery) error {
// CreateFromSourceCheck mocks base method.
func (m *MockSnapshotCreator) CreateFromSourceCheck(arg0 context.Context, arg1 snapshot.Snapshotter, arg2 *types.CreateFromSourceCheckArgs, arg3 *v10.GroupVersionForDiscovery) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateFromSourceCheck", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(error)
return ret0
}
// CreateFromSourceCheck indicates an expected call of CreateFromSourceCheck
// CreateFromSourceCheck indicates an expected call of CreateFromSourceCheck.
func (mr *MockSnapshotCreatorMockRecorder) CreateFromSourceCheck(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFromSourceCheck", reflect.TypeOf((*MockSnapshotCreator)(nil).CreateFromSourceCheck), arg0, arg1, arg2, arg3)
}
// CreateSnapshot mocks base method
func (m *MockSnapshotCreator) CreateSnapshot(arg0 context.Context, arg1 snapshot.Snapshotter, arg2 *types.CreateSnapshotArgs) (*snapv1.VolumeSnapshot, error) {
// CreateSnapshot mocks base method.
func (m *MockSnapshotCreator) CreateSnapshot(arg0 context.Context, arg1 snapshot.Snapshotter, arg2 *types.CreateSnapshotArgs) (*v1.VolumeSnapshot, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateSnapshot", arg0, arg1, arg2)
ret0, _ := ret[0].(*snapv1.VolumeSnapshot)
ret0, _ := ret[0].(*v1.VolumeSnapshot)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateSnapshot indicates an expected call of CreateSnapshot
// 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
// NewSnapshotter mocks base method.
func (m *MockSnapshotCreator) NewSnapshotter() (snapshot.Snapshotter, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "NewSnapshotter")
@ -75,7 +76,7 @@ func (m *MockSnapshotCreator) NewSnapshotter() (snapshot.Snapshotter, error) {
return ret0, ret1
}
// NewSnapshotter indicates an expected call of NewSnapshotter
// 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))

View file

@ -6,96 +6,97 @@ package mocks
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
types "github.com/kastenhq/kubestr/pkg/csi/types"
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
v1 "k8s.io/api/core/v1"
reflect "reflect"
v1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
v10 "k8s.io/api/core/v1"
)
// MockSnapshotRestoreStepper is a mock of SnapshotRestoreStepper interface
// MockSnapshotRestoreStepper is a mock of SnapshotRestoreStepper interface.
type MockSnapshotRestoreStepper struct {
ctrl *gomock.Controller
recorder *MockSnapshotRestoreStepperMockRecorder
}
// MockSnapshotRestoreStepperMockRecorder is the mock recorder for MockSnapshotRestoreStepper
// MockSnapshotRestoreStepperMockRecorder is the mock recorder for MockSnapshotRestoreStepper.
type MockSnapshotRestoreStepperMockRecorder struct {
mock *MockSnapshotRestoreStepper
}
// NewMockSnapshotRestoreStepper creates a new mock instance
// 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
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockSnapshotRestoreStepper) EXPECT() *MockSnapshotRestoreStepperMockRecorder {
return m.recorder
}
// Cleanup mocks base method
// 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
// 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) {
// CreateApplication mocks base method.
func (m *MockSnapshotRestoreStepper) CreateApplication(arg0 context.Context, arg1 *types.CSISnapshotRestoreArgs, arg2 string) (*v10.Pod, *v10.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)
ret0, _ := ret[0].(*v10.Pod)
ret1, _ := ret[1].(*v10.PersistentVolumeClaim)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// CreateApplication indicates an expected call of CreateApplication
// 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 *snapv1.VolumeSnapshot) (*v1.Pod, *v1.PersistentVolumeClaim, error) {
// RestoreApplication mocks base method.
func (m *MockSnapshotRestoreStepper) RestoreApplication(arg0 context.Context, arg1 *types.CSISnapshotRestoreArgs, arg2 *v1.VolumeSnapshot) (*v10.Pod, *v10.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)
ret0, _ := ret[0].(*v10.Pod)
ret1, _ := ret[1].(*v10.PersistentVolumeClaim)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// RestoreApplication indicates an expected call of RestoreApplication
// 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) (*snapv1.VolumeSnapshot, error) {
// SnapshotApplication mocks base method.
func (m *MockSnapshotRestoreStepper) SnapshotApplication(arg0 context.Context, arg1 *types.CSISnapshotRestoreArgs, arg2 *v10.PersistentVolumeClaim, arg3 string) (*v1.VolumeSnapshot, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SnapshotApplication", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(*snapv1.VolumeSnapshot)
ret0, _ := ret[0].(*v1.VolumeSnapshot)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SnapshotApplication indicates an expected call of SnapshotApplication
// 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
// 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)
@ -103,21 +104,21 @@ func (m *MockSnapshotRestoreStepper) ValidateArgs(arg0 context.Context, arg1 *ty
return ret0
}
// ValidateArgs indicates an expected call of ValidateArgs
// 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 {
// ValidateData mocks base method.
func (m *MockSnapshotRestoreStepper) ValidateData(arg0 context.Context, arg1 *v10.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
// 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)

296
pkg/csi/pvc_inspector.go Normal file
View file

@ -0,0 +1,296 @@
package csi
import (
"bytes"
"context"
"fmt"
"log"
"os"
"os/exec"
"os/signal"
"runtime"
"sync"
"syscall"
"time"
"github.com/kastenhq/kubestr/pkg/csi/types"
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
sv1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
)
type PVCBrowseRunner struct {
KubeCli kubernetes.Interface
DynCli dynamic.Interface
browserSteps PVCBrowserStepper
pvc *v1.PersistentVolumeClaim
pod *v1.Pod
snapshot *snapv1.VolumeSnapshot
}
func (r *PVCBrowseRunner) RunPVCBrowse(ctx context.Context, args *types.PVCBrowseArgs) error {
r.browserSteps = &pvcBrowserSteps{
validateOps: &validateOperations{
kubeCli: r.KubeCli,
dynCli: r.DynCli,
},
versionFetchOps: &apiVersionFetch{
kubeCli: r.KubeCli,
},
createAppOps: &applicationCreate{
kubeCli: r.KubeCli,
},
snapshotCreateOps: &snapshotCreate{
kubeCli: r.KubeCli,
dynCli: r.DynCli,
},
portForwardOps: &portforward{},
cleanerOps: &cleanse{
kubeCli: r.KubeCli,
dynCli: r.DynCli,
},
}
return r.RunPVCBrowseHelper(ctx, args)
}
func (r *PVCBrowseRunner) RunPVCBrowseHelper(ctx context.Context, args *types.PVCBrowseArgs) error {
defer func() {
fmt.Println("Cleaning up resources")
r.browserSteps.Cleanup(ctx, r.pvc, r.pod, r.snapshot)
}()
if r.KubeCli == nil || r.DynCli == nil {
return fmt.Errorf("cli uninitialized")
}
sc, err := r.browserSteps.ValidateArgs(ctx, args)
if err != nil {
return errors.Wrap(err, "Failed to validate arguments.")
}
fmt.Println("Taking a snapshot")
snapName := snapshotPrefix + time.Now().Format("20060102150405")
r.snapshot, err = r.browserSteps.SnapshotPVC(ctx, args, snapName)
if err != nil {
return errors.Wrap(err, "Failed to snapshot PVC.")
}
fmt.Println("Creating the file browser application.")
r.pod, r.pvc, err = r.browserSteps.CreateInspectorApplication(ctx, args, r.snapshot, sc)
if err != nil {
return errors.Wrap(err, "Failed to create inspector application.")
}
fmt.Println("Forwarding the port.")
err = r.browserSteps.PortForwardAPod(ctx, r.pod, args.LocalPort)
if err != nil {
return errors.Wrap(err, "Failed to port forward Pod.")
}
return nil
}
//go:generate go run github.com/golang/mock/mockgen -destination=mocks/mock_pvc_browser_stepper.go -package=mocks . PVCBrowserStepper
type PVCBrowserStepper interface {
ValidateArgs(ctx context.Context, args *types.PVCBrowseArgs) (*sv1.StorageClass, error)
SnapshotPVC(ctx context.Context, args *types.PVCBrowseArgs, snapshotName string) (*snapv1.VolumeSnapshot, error)
CreateInspectorApplication(ctx context.Context, args *types.PVCBrowseArgs, snapshot *snapv1.VolumeSnapshot, storageClass *sv1.StorageClass) (*v1.Pod, *v1.PersistentVolumeClaim, error)
PortForwardAPod(ctx context.Context, pod *v1.Pod, localPort int) error
Cleanup(ctx context.Context, pvc *v1.PersistentVolumeClaim, pod *v1.Pod, snapshot *snapv1.VolumeSnapshot)
}
type pvcBrowserSteps struct {
validateOps ArgumentValidator
versionFetchOps ApiVersionFetcher
createAppOps ApplicationCreator
snapshotCreateOps SnapshotCreator
portForwardOps PortForwarder
cleanerOps Cleaner
SnapshotGroupVersion *metav1.GroupVersionForDiscovery
}
func (p *pvcBrowserSteps) ValidateArgs(ctx context.Context, args *types.PVCBrowseArgs) (*sv1.StorageClass, error) {
if err := args.Validate(); err != nil {
return nil, errors.Wrap(err, "Failed to validate input arguments")
}
if err := p.validateOps.ValidateNamespace(ctx, args.Namespace); err != nil {
return nil, errors.Wrap(err, "Failed to validate Namespace")
}
pvc, err := p.validateOps.ValidatePVC(ctx, args.PVCName, args.Namespace)
if err != nil {
return nil, errors.Wrap(err, "Failed to validate PVC")
}
pvName := pvc.Spec.VolumeName
if pvName == "" {
return nil, errors.Errorf("PVC (%s) not bound. namespace - (%s)", pvc.Name, pvc.Namespace)
}
pv, err := p.validateOps.FetchPV(ctx, pvName)
if err != nil {
return nil, errors.Wrap(err, "Failed to fetch PV")
}
if pv.Spec.CSI == nil {
return nil, errors.New("PVC is not using a CSI volume")
}
sc, err := p.validateOps.ValidateStorageClass(ctx, *pvc.Spec.StorageClassName)
if err != nil {
return nil, errors.Wrap(err, "Failed to validate SC")
}
groupVersion, err := p.versionFetchOps.GetCSISnapshotGroupVersion()
if err != nil {
return nil, errors.Wrap(err, "Failed to fetch groupVersion")
}
p.SnapshotGroupVersion = groupVersion
uVSC, err := p.validateOps.ValidateVolumeSnapshotClass(ctx, args.VolumeSnapshotClass, groupVersion)
if err != nil {
return nil, errors.Wrap(err, "Failed to validate VolumeSnapshotClass")
}
vscDriver := getDriverNameFromUVSC(*uVSC, groupVersion.GroupVersion)
if sc.Provisioner != vscDriver {
return nil, fmt.Errorf("StorageClass provisioner (%s) and VolumeSnapshotClass driver (%s) are different.", sc.Provisioner, vscDriver)
}
return sc, nil
}
func (p *pvcBrowserSteps) SnapshotPVC(ctx context.Context, args *types.PVCBrowseArgs, snapshotName string) (*snapv1.VolumeSnapshot, error) {
snapshotter, err := p.snapshotCreateOps.NewSnapshotter()
if err != nil {
return nil, errors.Wrap(err, "Failed to load snapshotter")
}
createSnapshotArgs := &types.CreateSnapshotArgs{
Namespace: args.Namespace,
PVCName: args.PVCName,
VolumeSnapshotClass: args.VolumeSnapshotClass,
SnapshotName: snapshotName,
}
return p.snapshotCreateOps.CreateSnapshot(ctx, snapshotter, createSnapshotArgs)
}
func (p *pvcBrowserSteps) CreateInspectorApplication(ctx context.Context, args *types.PVCBrowseArgs, snapshot *snapv1.VolumeSnapshot, storageClass *sv1.StorageClass) (*v1.Pod, *v1.PersistentVolumeClaim, error) {
snapshotAPIGroup := "snapshot.storage.k8s.io"
snapshotKind := "VolumeSnapshot"
dataSource := &v1.TypedLocalObjectReference{
APIGroup: &snapshotAPIGroup,
Kind: snapshotKind,
Name: snapshot.Name,
}
pvcArgs := &types.CreatePVCArgs{
GenerateName: clonedPVCGenerateName,
StorageClass: storageClass.Name,
Namespace: args.Namespace,
DataSource: dataSource,
RestoreSize: snapshot.Status.RestoreSize,
}
pvc, err := p.createAppOps.CreatePVC(ctx, pvcArgs)
if err != nil {
return nil, nil, errors.Wrap(err, "Failed to restore PVC")
}
podArgs := &types.CreatePodArgs{
GenerateName: clonedPodGenerateName,
PVCName: pvc.Name,
Namespace: args.Namespace,
RunAsUser: args.RunAsUser,
ContainerImage: "filebrowser/filebrowser:v2",
ContainerArgs: []string{"--noauth", "-r", "/data"},
MountPath: "/data",
}
pod, err := p.createAppOps.CreatePod(ctx, podArgs)
if err != nil {
return nil, pvc, errors.Wrap(err, "Failed to create restored Pod")
}
if err = p.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 (p *pvcBrowserSteps) PortForwardAPod(ctx context.Context, pod *v1.Pod, localPort int) error {
var wg sync.WaitGroup
wg.Add(1)
stopChan, readyChan, errChan := make(chan struct{}, 1), make(chan struct{}, 1), make(chan string)
out, errOut := new(bytes.Buffer), new(bytes.Buffer)
cfg, err := p.portForwardOps.FetchRestConfig()
if err != nil {
return errors.New("Failed to fetch rest config")
}
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigs
fmt.Println("Stopping port forward")
close(stopChan)
wg.Done()
}()
go func() {
pfArgs := &types.PortForwardAPodRequest{
RestConfig: cfg,
Pod: pod,
LocalPort: localPort,
PodPort: 80,
OutStream: bytes.Buffer(*out),
ErrOutStream: bytes.Buffer(*errOut),
StopCh: stopChan,
ReadyCh: readyChan,
}
err = p.portForwardOps.PortForwardAPod(pfArgs)
if err != nil {
errChan <- fmt.Sprintf("Failed to port forward (%s)", err.Error())
}
}()
select {
case <-readyChan:
url := fmt.Sprintf("http://localhost:%d/", localPort)
fmt.Printf("Port forwarding is ready to get traffic. visit %s\n", url)
openbrowser(url)
wg.Wait()
case msg := <-errChan:
return errors.New(msg)
}
return nil
}
func (p *pvcBrowserSteps) Cleanup(ctx context.Context, pvc *v1.PersistentVolumeClaim, pod *v1.Pod, snapshot *snapv1.VolumeSnapshot) {
if pvc != nil {
err := p.cleanerOps.DeletePVC(ctx, pvc.Name, pvc.Namespace)
if err != nil {
fmt.Println("Failed to delete PVC", pvc)
}
}
if pod != nil {
err := p.cleanerOps.DeletePod(ctx, pod.Name, pod.Namespace)
if err != nil {
fmt.Println("Failed to delete Pod", pod)
}
}
if snapshot != nil {
err := p.cleanerOps.DeleteSnapshot(ctx, snapshot.Name, snapshot.Namespace, p.SnapshotGroupVersion)
if err != nil {
fmt.Println("Failed to delete Snapshot", snapshot)
}
}
}
func openbrowser(url string) {
var err error
switch runtime.GOOS {
case "linux":
err = exec.Command("xdg-open", url).Start()
case "windows":
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
case "darwin":
err = exec.Command("open", url).Start()
default:
err = fmt.Errorf("unsupported platform")
}
if err != nil {
log.Fatal(err)
}
}

View file

@ -0,0 +1,777 @@
package csi
import (
"context"
"fmt"
"github.com/golang/mock/gomock"
"github.com/kastenhq/kubestr/pkg/common"
"github.com/kastenhq/kubestr/pkg/csi/mocks"
"github.com/kastenhq/kubestr/pkg/csi/types"
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
. "gopkg.in/check.v1"
v1 "k8s.io/api/core/v1"
sv1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func (s *CSITestSuite) TestPvcBrowseValidateArgs(c *C) {
ctx := context.Background()
scName := "sc"
type fields struct {
validateOps *mocks.MockArgumentValidator
versionOps *mocks.MockApiVersionFetcher
}
for _, tc := range []struct {
args *types.PVCBrowseArgs
prepare func(f *fields)
errChecker Checker
}{
{ // valid args
args: &types.PVCBrowseArgs{
PVCName: "pvc",
VolumeSnapshotClass: "vsc",
Namespace: "ns",
},
prepare: func(f *fields) {
gomock.InOrder(
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), "pvc", "ns").Return(
&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc",
Namespace: "ns",
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "vol",
StorageClassName: &scName,
},
}, nil,
),
f.validateOps.EXPECT().FetchPV(gomock.Any(), "vol").Return(
&v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "vol",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{},
},
},
},
nil,
),
f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), "sc").Return(
&sv1.StorageClass{
Provisioner: "p1",
}, nil),
f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(
&metav1.GroupVersionForDiscovery{
GroupVersion: common.SnapshotAlphaVersion,
}, nil),
f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), "vsc", &metav1.GroupVersionForDiscovery{
GroupVersion: common.SnapshotAlphaVersion,
}).Return(&unstructured.Unstructured{
Object: map[string]interface{}{
common.VolSnapClassAlphaDriverKey: "p1",
},
}, nil),
)
},
errChecker: IsNil,
},
{ // driver mismatch
args: &types.PVCBrowseArgs{
PVCName: "pvc",
VolumeSnapshotClass: "vsc",
Namespace: "ns",
},
prepare: func(f *fields) {
gomock.InOrder(
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), "pvc", "ns").Return(
&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc",
Namespace: "ns",
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "vol",
StorageClassName: &scName,
},
}, nil,
),
f.validateOps.EXPECT().FetchPV(gomock.Any(), "vol").Return(
&v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "vol",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{},
},
},
},
nil,
),
f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(
&sv1.StorageClass{
Provisioner: "p1",
}, nil),
f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(
&metav1.GroupVersionForDiscovery{
GroupVersion: common.SnapshotAlphaVersion,
}, nil),
f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), "vsc", &metav1.GroupVersionForDiscovery{
GroupVersion: common.SnapshotAlphaVersion,
}).Return(&unstructured.Unstructured{
Object: map[string]interface{}{
common.VolSnapClassAlphaDriverKey: "p2",
},
}, nil),
)
},
errChecker: NotNil,
},
{ // vsc error
args: &types.PVCBrowseArgs{
PVCName: "pvc",
VolumeSnapshotClass: "vsc",
Namespace: "ns",
},
prepare: func(f *fields) {
gomock.InOrder(
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), gomock.Any(), gomock.Any()).Return(
&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc",
Namespace: "ns",
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "vol",
StorageClassName: &scName,
},
}, nil,
),
f.validateOps.EXPECT().FetchPV(gomock.Any(), "vol").Return(
&v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "vol",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{},
},
},
},
nil,
),
f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, nil),
f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, nil),
f.validateOps.EXPECT().ValidateVolumeSnapshotClass(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("vsc error")),
)
},
errChecker: NotNil,
},
{ // get driver versionn error
args: &types.PVCBrowseArgs{
PVCName: "pvc",
VolumeSnapshotClass: "vsc",
Namespace: "ns",
},
prepare: func(f *fields) {
gomock.InOrder(
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), gomock.Any(), gomock.Any()).Return(
&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc",
Namespace: "ns",
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "vol",
StorageClassName: &scName,
},
}, nil,
),
f.validateOps.EXPECT().FetchPV(gomock.Any(), "vol").Return(
&v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "vol",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{},
},
},
},
nil,
),
f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, nil),
f.versionOps.EXPECT().GetCSISnapshotGroupVersion().Return(nil, fmt.Errorf("driver version error")),
)
},
errChecker: NotNil,
},
{ // sc error
args: &types.PVCBrowseArgs{
PVCName: "pvc",
VolumeSnapshotClass: "vsc",
Namespace: "ns",
},
prepare: func(f *fields) {
gomock.InOrder(
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), gomock.Any(), gomock.Any()).Return(
&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc",
Namespace: "ns",
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "vol",
StorageClassName: &scName,
},
}, nil,
),
f.validateOps.EXPECT().FetchPV(gomock.Any(), "vol").Return(
&v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "vol",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{},
},
},
},
nil,
),
f.validateOps.EXPECT().ValidateStorageClass(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("sc error")),
)
},
errChecker: NotNil,
},
{ // non csi error
args: &types.PVCBrowseArgs{
PVCName: "pvc",
VolumeSnapshotClass: "vsc",
Namespace: "ns",
},
prepare: func(f *fields) {
gomock.InOrder(
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), gomock.Any(), gomock.Any()).Return(
&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc",
Namespace: "ns",
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "vol",
StorageClassName: &scName,
},
}, nil,
),
f.validateOps.EXPECT().FetchPV(gomock.Any(), "vol").Return(
&v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "vol",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{},
},
},
},
nil,
),
)
},
errChecker: NotNil,
},
{ // fetch pv error
args: &types.PVCBrowseArgs{
PVCName: "pvc",
VolumeSnapshotClass: "vsc",
Namespace: "ns",
},
prepare: func(f *fields) {
gomock.InOrder(
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), gomock.Any(), gomock.Any()).Return(
&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc",
Namespace: "ns",
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "vol",
StorageClassName: &scName,
},
}, nil,
),
f.validateOps.EXPECT().FetchPV(gomock.Any(), "vol").Return(nil, fmt.Errorf("pv fail")),
)
},
errChecker: NotNil,
},
{ // validate pvc error
args: &types.PVCBrowseArgs{
PVCName: "pvc",
VolumeSnapshotClass: "vsc",
Namespace: "ns",
},
prepare: func(f *fields) {
gomock.InOrder(
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(nil),
f.validateOps.EXPECT().ValidatePVC(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("validate pvc error")),
)
},
errChecker: NotNil,
},
{ // validate ns error
args: &types.PVCBrowseArgs{
PVCName: "pvc",
VolumeSnapshotClass: "vsc",
Namespace: "ns",
},
prepare: func(f *fields) {
gomock.InOrder(
f.validateOps.EXPECT().ValidateNamespace(gomock.Any(), "ns").Return(fmt.Errorf("validate ns error")),
)
},
errChecker: NotNil,
},
{ // validate pvc error
args: &types.PVCBrowseArgs{
PVCName: "",
VolumeSnapshotClass: "vsc",
Namespace: "ns",
},
errChecker: NotNil,
},
{ // validate vsc error
args: &types.PVCBrowseArgs{
PVCName: "dfd",
VolumeSnapshotClass: "",
Namespace: "ns",
},
errChecker: NotNil,
},
{ // validate ns error
args: &types.PVCBrowseArgs{
PVCName: "dfd",
VolumeSnapshotClass: "ddd",
Namespace: "",
},
errChecker: NotNil,
},
} {
ctrl := gomock.NewController(c)
defer ctrl.Finish()
f := fields{
validateOps: mocks.NewMockArgumentValidator(ctrl),
versionOps: mocks.NewMockApiVersionFetcher(ctrl),
}
if tc.prepare != nil {
tc.prepare(&f)
}
stepper := &pvcBrowserSteps{
validateOps: f.validateOps,
versionFetchOps: f.versionOps,
}
_, err := stepper.ValidateArgs(ctx, tc.args)
c.Check(err, tc.errChecker)
}
}
func (s *CSITestSuite) TestPvcBrowseSnapshotPVC(c *C) {
ctx := context.Background()
snapshotter := &fakeSnapshotter{name: "snapshotter"}
groupversion := &metav1.GroupVersionForDiscovery{
GroupVersion: "gv",
Version: "v",
}
type fields struct {
snapshotOps *mocks.MockSnapshotCreator
}
for _, tc := range []struct {
args *types.PVCBrowseArgs
snapshotName string
prepare func(f *fields)
errChecker Checker
snapChecker Checker
}{
{
args: &types.PVCBrowseArgs{
Namespace: "ns",
VolumeSnapshotClass: "vsc",
PVCName: "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(&snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "createdName",
},
}, nil),
)
},
errChecker: IsNil,
snapChecker: NotNil,
},
{
args: &types.PVCBrowseArgs{
Namespace: "ns",
VolumeSnapshotClass: "vsc",
PVCName: "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("error")),
)
},
errChecker: NotNil,
snapChecker: IsNil,
},
{
args: &types.PVCBrowseArgs{
Namespace: "ns",
VolumeSnapshotClass: "vsc",
PVCName: "pvc1",
},
snapshotName: "snap1",
prepare: func(f *fields) {
gomock.InOrder(
f.snapshotOps.EXPECT().NewSnapshotter().Return(nil, fmt.Errorf("error")),
)
},
errChecker: NotNil,
snapChecker: IsNil,
},
} {
ctrl := gomock.NewController(c)
defer ctrl.Finish()
f := fields{
snapshotOps: mocks.NewMockSnapshotCreator(ctrl),
}
if tc.prepare != nil {
tc.prepare(&f)
}
stepper := &pvcBrowserSteps{
snapshotCreateOps: f.snapshotOps,
SnapshotGroupVersion: groupversion,
}
snapshot, err := stepper.SnapshotPVC(ctx, tc.args, tc.snapshotName)
c.Check(err, tc.errChecker)
c.Check(snapshot, tc.snapChecker)
}
}
func (s *CSITestSuite) TestCreateInspectorApplication(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.PVCBrowseArgs
snapshot *snapv1.VolumeSnapshot
sc *sv1.StorageClass
prepare func(f *fields)
errChecker Checker
podChecker Checker
pvcChecker Checker
}{
{
args: &types.PVCBrowseArgs{
Namespace: "ns",
RunAsUser: 100,
},
sc: &sv1.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "sc",
},
},
snapshot: &snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "snap1",
},
Status: &snapv1.VolumeSnapshotStatus{
RestoreSize: &resourceQuantity,
},
},
prepare: func(f *fields) {
gomock.InOrder(
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), &types.CreatePVCArgs{
GenerateName: clonedPVCGenerateName,
StorageClass: "sc",
Namespace: "ns",
DataSource: &v1.TypedLocalObjectReference{
APIGroup: &snapshotAPIGroup,
Kind: "VolumeSnapshot",
Name: "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",
ContainerArgs: []string{"--noauth", "-r", "/data"},
MountPath: "/data",
RunAsUser: 100,
ContainerImage: "filebrowser/filebrowser:v2",
}).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.PVCBrowseArgs{
Namespace: "ns",
RunAsUser: 100,
},
sc: &sv1.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "sc",
},
},
snapshot: &snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "snap1",
},
Status: &snapv1.VolumeSnapshotStatus{
RestoreSize: &resourceQuantity,
},
},
prepare: func(f *fields) {
gomock.InOrder(
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), &types.CreatePVCArgs{
GenerateName: clonedPVCGenerateName,
StorageClass: "sc",
Namespace: "ns",
DataSource: &v1.TypedLocalObjectReference{
APIGroup: &snapshotAPIGroup,
Kind: "VolumeSnapshot",
Name: "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",
ContainerArgs: []string{"--noauth", "-r", "/data"},
MountPath: "/data",
RunAsUser: 100,
ContainerImage: "filebrowser/filebrowser:v2",
}).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.PVCBrowseArgs{
Namespace: "ns",
RunAsUser: 100,
},
sc: &sv1.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "sc",
},
},
snapshot: &snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "snap1",
},
Status: &snapv1.VolumeSnapshotStatus{
RestoreSize: &resourceQuantity,
},
},
prepare: func(f *fields) {
gomock.InOrder(
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), gomock.Any()).Return(&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc1",
},
}, nil),
f.createAppOps.EXPECT().CreatePod(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("pod error")),
)
},
errChecker: NotNil,
podChecker: IsNil,
pvcChecker: NotNil,
},
{
args: &types.PVCBrowseArgs{
Namespace: "ns",
RunAsUser: 100,
},
sc: &sv1.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "sc",
},
},
snapshot: &snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "snap1",
},
Status: &snapv1.VolumeSnapshotStatus{
RestoreSize: &resourceQuantity,
},
},
prepare: func(f *fields) {
gomock.InOrder(
f.createAppOps.EXPECT().CreatePVC(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("error")),
)
},
errChecker: NotNil,
podChecker: IsNil,
pvcChecker: IsNil,
},
} {
ctrl := gomock.NewController(c)
defer ctrl.Finish()
f := fields{
createAppOps: mocks.NewMockApplicationCreator(ctrl),
}
if tc.prepare != nil {
tc.prepare(&f)
}
stepper := &pvcBrowserSteps{
createAppOps: f.createAppOps,
}
pod, pvc, err := stepper.CreateInspectorApplication(ctx, tc.args, tc.snapshot, tc.sc)
c.Check(err, tc.errChecker)
c.Check(pod, tc.podChecker)
c.Check(pvc, tc.pvcChecker)
}
}
func (s *CSITestSuite) TestPVCBrowseCleanup(c *C) {
ctx := context.Background()
groupversion := &metav1.GroupVersionForDiscovery{
GroupVersion: "gv",
Version: "v",
}
type fields struct {
cleanerOps *mocks.MockCleaner
}
for _, tc := range []struct {
pvc *v1.PersistentVolumeClaim
pod *v1.Pod
snapshot *snapv1.VolumeSnapshot
prepare func(f *fields)
}{
{
pvc: &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc",
Namespace: "ns",
},
},
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod",
Namespace: "ns",
},
},
snapshot: &snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "snap1",
Namespace: "ns",
},
},
prepare: func(f *fields) {
gomock.InOrder(
f.cleanerOps.EXPECT().DeletePVC(ctx, "pvc", "ns").Return(nil),
f.cleanerOps.EXPECT().DeletePod(ctx, "pod", "ns").Return(nil),
f.cleanerOps.EXPECT().DeleteSnapshot(ctx, "snap1", "ns", groupversion).Return(nil),
)
},
},
{
pvc: &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc",
Namespace: "ns",
},
},
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod",
Namespace: "ns",
},
},
snapshot: &snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "snap1",
Namespace: "ns",
},
},
prepare: func(f *fields) {
gomock.InOrder(
f.cleanerOps.EXPECT().DeletePVC(ctx, "pvc", "ns").Return(fmt.Errorf("err")),
f.cleanerOps.EXPECT().DeletePod(ctx, "pod", "ns").Return(fmt.Errorf("err")),
f.cleanerOps.EXPECT().DeleteSnapshot(ctx, "snap1", "ns", groupversion).Return(fmt.Errorf("err")),
)
},
},
} {
ctrl := gomock.NewController(c)
defer ctrl.Finish()
f := fields{
cleanerOps: mocks.NewMockCleaner(ctrl),
}
if tc.prepare != nil {
tc.prepare(&f)
}
stepper := &pvcBrowserSteps{
cleanerOps: f.cleanerOps,
SnapshotGroupVersion: groupversion,
}
stepper.Cleanup(ctx, tc.pvc, tc.pod, tc.snapshot)
}
}

View file

@ -0,0 +1,213 @@
package csi
import (
"context"
"fmt"
"github.com/golang/mock/gomock"
"github.com/kastenhq/kubestr/pkg/csi/mocks"
"github.com/kastenhq/kubestr/pkg/csi/types"
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
. "gopkg.in/check.v1"
v1 "k8s.io/api/core/v1"
sv1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic"
fakedynamic "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
)
func (s *CSITestSuite) TestRunPVCBrowseHelper(c *C) {
ctx := context.Background()
type fields struct {
stepperOps *mocks.MockPVCBrowserStepper
}
for _, tc := range []struct {
kubeCli kubernetes.Interface
dynCli dynamic.Interface
args *types.PVCBrowseArgs
prepare func(f *fields)
errChecker Checker
}{
{
// success
kubeCli: fake.NewSimpleClientset(),
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
args: &types.PVCBrowseArgs{},
prepare: func(f *fields) {
gomock.InOrder(
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(
&sv1.StorageClass{}, nil,
),
f.stepperOps.EXPECT().SnapshotPVC(gomock.Any(), gomock.Any(), gomock.Any()).Return(
&snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "snap1",
Namespace: "ns",
}}, nil,
),
f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), gomock.Any(),
&snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "snap1",
Namespace: "ns",
},
}, &sv1.StorageClass{},
).Return(
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod1",
Namespace: "ns",
},
},
&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc1",
Namespace: "ns",
},
},
nil,
),
f.stepperOps.EXPECT().PortForwardAPod(gomock.Any(),
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod1",
Namespace: "ns",
},
}, gomock.Any(),
).Return(nil),
f.stepperOps.EXPECT().Cleanup(gomock.Any(),
&v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvc1",
Namespace: "ns",
},
},
&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod1",
Namespace: "ns",
},
},
&snapv1.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "snap1",
Namespace: "ns",
},
},
),
)
},
errChecker: IsNil,
},
{
// portforward failure
kubeCli: fake.NewSimpleClientset(),
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
args: &types.PVCBrowseArgs{},
prepare: func(f *fields) {
gomock.InOrder(
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil),
f.stepperOps.EXPECT().SnapshotPVC(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil),
f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, nil),
f.stepperOps.EXPECT().PortForwardAPod(gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("portforward error")),
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()),
)
},
errChecker: NotNil,
},
{
// createapp failure
kubeCli: fake.NewSimpleClientset(),
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
args: &types.PVCBrowseArgs{},
prepare: func(f *fields) {
gomock.InOrder(
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil),
f.stepperOps.EXPECT().SnapshotPVC(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil),
f.stepperOps.EXPECT().CreateInspectorApplication(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil, fmt.Errorf("createapp error")),
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()),
)
},
errChecker: NotNil,
},
{
// snapshot failure
kubeCli: fake.NewSimpleClientset(),
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
args: &types.PVCBrowseArgs{},
prepare: func(f *fields) {
gomock.InOrder(
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, nil),
f.stepperOps.EXPECT().SnapshotPVC(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("snapshot error")),
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()),
)
},
errChecker: NotNil,
},
{
// validate failure
kubeCli: fake.NewSimpleClientset(),
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
args: &types.PVCBrowseArgs{},
prepare: func(f *fields) {
gomock.InOrder(
f.stepperOps.EXPECT().ValidateArgs(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("snapshot error")),
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()),
)
},
errChecker: NotNil,
},
{
// emptycli failure
kubeCli: nil,
dynCli: fakedynamic.NewSimpleDynamicClient(runtime.NewScheme()),
args: &types.PVCBrowseArgs{},
prepare: func(f *fields) {
gomock.InOrder(
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()),
)
},
errChecker: NotNil,
},
{
// emptydyncli failure
kubeCli: fake.NewSimpleClientset(),
dynCli: nil,
args: &types.PVCBrowseArgs{},
prepare: func(f *fields) {
gomock.InOrder(
f.stepperOps.EXPECT().Cleanup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()),
)
},
errChecker: NotNil,
},
} {
ctrl := gomock.NewController(c)
defer ctrl.Finish()
f := fields{
stepperOps: mocks.NewMockPVCBrowserStepper(ctrl),
}
if tc.prepare != nil {
tc.prepare(&f)
}
runner := &PVCBrowseRunner{
KubeCli: tc.kubeCli,
DynCli: tc.dynCli,
browserSteps: f.stepperOps,
}
err := runner.RunPVCBrowseHelper(ctx, tc.args)
c.Check(err, tc.errChecker)
}
}
func (s *CSITestSuite) TestPVCBrowseRunner(c *C) {
ctx := context.Background()
r := &PVCBrowseRunner{
browserSteps: &pvcBrowserSteps{},
}
err := r.RunPVCBrowseHelper(ctx, nil)
c.Check(err, NotNil)
}

View file

@ -5,18 +5,13 @@ import (
"fmt"
"time"
kankube "github.com/kanisterio/kanister/pkg/kube"
kansnapshot "github.com/kanisterio/kanister/pkg/kube/snapshot"
"github.com/kastenhq/kubestr/pkg/common"
"github.com/kastenhq/kubestr/pkg/csi/types"
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
sv1 "k8s.io/api/storage/v1"
"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"
)
@ -114,7 +109,7 @@ func (r *SnapshotRestoreRunner) RunSnapshotRestoreHelper(ctx context.Context, ar
return results, err
}
//go:generate mockgen -destination=mocks/mock_snapshot_restore_stepper.go -package=mocks . SnapshotRestoreStepper
//go:generate go run github.com/golang/mock/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)
@ -178,9 +173,11 @@ func (s *snapshotRestoreSteps) CreateApplication(ctx context.Context, args *type
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,
Command: []string{"/bin/sh"},
ContainerArgs: []string{"-c", fmt.Sprintf("echo '%s' >> /data/out.txt; sync; tail -f /dev/null", genString)},
MountPath: "/data",
}
pod, err := s.createAppOps.CreatePod(ctx, podArgs)
if err != nil {
@ -254,9 +251,11 @@ func (s *snapshotRestoreSteps) RestoreApplication(ctx context.Context, args *typ
GenerateName: clonedPodGenerateName,
PVCName: pvc.Name,
Namespace: args.Namespace,
Cmd: "tail -f /dev/null",
RunAsUser: args.RunAsUser,
ContainerImage: args.ContainerImage,
Command: []string{"/bin/sh"},
ContainerArgs: []string{"-c", "tail -f /dev/null"},
MountPath: "/data",
}
pod, err := s.createAppOps.CreatePod(ctx, podArgs)
if err != nil {
@ -304,323 +303,6 @@ func (s *snapshotRestoreSteps) Cleanup(ctx context.Context, results *types.CSISn
}
}
//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: common.SnapGroupName, Version: groupVersion.Version, Resource: common.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 = common.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) (*snapv1.VolumeSnapshot, error)
CreateFromSourceCheck(ctx context.Context, snapshotter kansnapshot.Snapshotter, args *types.CreateFromSourceCheckArgs, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) 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) (*snapv1.VolumeSnapshot, error) {
if snapshotter == nil || args == nil {
return nil, fmt.Errorf("snapshotter or args are empty")
}
if err := args.Validate(); err != nil {
return nil, err
}
err := snapshotter.Create(ctx, args.SnapshotName, args.Namespace, args.PVCName, &args.VolumeSnapshotClass, true, nil)
if err != nil {
return nil, errors.Wrapf(err, "CSI Driver failed to create snapshot for PVC (%s) in Namespace (%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, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) error {
if c.dynCli == nil {
return fmt.Errorf("dynCli not initialized")
}
if SnapshotGroupVersion == nil || SnapshotGroupVersion.Version == "" {
return fmt.Errorf("snapshot group version not provided")
}
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(ctx, 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() {
VolSnapClassGVR := schema.GroupVersionResource{Group: common.SnapGroupName, Version: SnapshotGroupVersion.Version, Resource: common.VolumeSnapshotClassResourcePlural}
err := c.dynCli.Resource(VolSnapClassGVR).Delete(ctx, targetSnapClassName, metav1.DeleteOptions{})
if err != nil {
fmt.Printf("Delete VSC Error (%s) - (%v)\n", targetSnapClassName, err)
}
}()
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, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) 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, SnapshotGroupVersion *metav1.GroupVersionForDiscovery) error {
if c.dynCli == nil {
return fmt.Errorf("dynCli not initialized")
}
if SnapshotGroupVersion == nil || SnapshotGroupVersion.Version == "" {
return fmt.Errorf("snapshot group version not provided")
}
VolSnapGVR := schema.GroupVersionResource{Group: common.SnapGroupName, Version: SnapshotGroupVersion.Version, Resource: common.VolumeSnapshotResourcePlural}
return c.dynCli.Resource(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 == common.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

View file

@ -232,9 +232,11 @@ func (s *CSITestSuite) TestCreateApplication(c *C) {
GenerateName: originalPodGenerateName,
PVCName: "pvc1",
Namespace: "ns",
Cmd: "echo 'some string' >> /data/out.txt; sync; tail -f /dev/null",
Command: []string{"/bin/sh"},
ContainerArgs: []string{"-c", "echo 'some string' >> /data/out.txt; sync; tail -f /dev/null"},
RunAsUser: 100,
ContainerImage: "image",
MountPath: "/data",
}).Return(&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod1",
@ -270,9 +272,11 @@ func (s *CSITestSuite) TestCreateApplication(c *C) {
GenerateName: originalPodGenerateName,
PVCName: "pvc1",
Namespace: "ns",
Cmd: "echo 'some string' >> /data/out.txt; sync; tail -f /dev/null",
Command: []string{"/bin/sh"},
ContainerArgs: []string{"-c", "echo 'some string' >> /data/out.txt; sync; tail -f /dev/null"},
RunAsUser: 100,
ContainerImage: "image",
MountPath: "/data",
}).Return(&v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod1",
@ -573,7 +577,9 @@ func (s *CSITestSuite) TestRestoreApplication(c *C) {
GenerateName: clonedPodGenerateName,
PVCName: "pvc1",
Namespace: "ns",
Cmd: "tail -f /dev/null",
Command: []string{"/bin/sh"},
ContainerArgs: []string{"-c", "tail -f /dev/null"},
MountPath: "/data",
RunAsUser: 100,
ContainerImage: "image",
}).Return(&v1.Pod{
@ -624,7 +630,9 @@ func (s *CSITestSuite) TestRestoreApplication(c *C) {
GenerateName: clonedPodGenerateName,
PVCName: "pvc1",
Namespace: "ns",
Cmd: "tail -f /dev/null",
Command: []string{"/bin/sh"},
ContainerArgs: []string{"-c", "tail -f /dev/null"},
MountPath: "/data",
RunAsUser: 100,
ContainerImage: "image",
}).Return(&v1.Pod{

View file

@ -1,11 +1,13 @@
package types
import (
"bytes"
"fmt"
snapv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/client-go/rest"
)
type CSISnapshotRestoreArgs struct {
@ -52,13 +54,15 @@ type CreatePodArgs struct {
GenerateName string
PVCName string
Namespace string
Cmd string
RunAsUser int64
ContainerImage string
Command []string
ContainerArgs []string
MountPath string
}
func (c *CreatePodArgs) Validate() error {
if c.GenerateName == "" || c.PVCName == "" || c.Namespace == "" || c.Cmd == "" {
if c.GenerateName == "" || c.PVCName == "" || c.Namespace == "" {
return fmt.Errorf("Invalid CreatePodArgs (%v)", c)
}
return nil
@ -90,3 +94,36 @@ func (c *CreateFromSourceCheckArgs) Validate() error {
}
return nil
}
type PVCBrowseArgs struct {
PVCName string
Namespace string
VolumeSnapshotClass string
RunAsUser int64
LocalPort int
}
func (p *PVCBrowseArgs) Validate() error {
if p.PVCName == "" || p.Namespace == "" || p.VolumeSnapshotClass == "" {
return fmt.Errorf("Invalid PVCBrowseArgs (%v)", p)
}
return nil
}
type PortForwardAPodRequest struct {
// RestConfig is the kubernetes config
RestConfig *rest.Config
// Pod is the selected pod for this port forwarding
Pod *v1.Pod
// LocalPort is the local port that will be selected to expose the PodPort
LocalPort int
// PodPort is the target port for the pod
PodPort int
// Streams configures where to write or read input from
OutStream bytes.Buffer
ErrOutStream bytes.Buffer
// StopCh is the channel used to manage the port forward lifecycle
StopCh <-chan struct{}
// ReadyCh communicates when the tunnel is ready to receive traffic
ReadyCh chan struct{}
}

View file

@ -18,10 +18,10 @@ var CSIDriverList = []*CSIDriver{
{NameUrl: "[Bigtera VirtualStor (block)](https://github.com/bigtera-ce/ceph-csi)", DriverName: "csi.block.bigtera.com", Versions: "v0.3, v1.0.0, v1.1.0", Description: "A Container Storage Interface (CSI) Driver for Bigtera VirtualStor block storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion"},
{NameUrl: "[Bigtera VirtualStor (filesystem)](https://github.com/bigtera-ce/ceph-csi)", DriverName: "csi.fs.bigtera.com", Versions: "v0.3, v1.0.0, v1.1.0", Description: "A Container Storage Interface (CSI) Driver for Bigtera VirtualStor filesystem", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Expansion"},
{NameUrl: "[BizFlyCloud Block Storage](https://github.com/bizflycloud/csi-bizflycloud)", DriverName: "volume.csi.bizflycloud.vn", Versions: "v1.2", Description: "A Container Storage Interface (CSI) Driver for BizFly Cloud block storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion"},
{NameUrl: "[CephFS](https://github.com/ceph/ceph-csi)", DriverName: "cephfs.csi.ceph.com", Versions: "v0.3, v1.0.0, v1.1.0, v1.2.0", Description: "A Container Storage Interface (CSI) Driver for CephFS", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Expansion, Snapshot, Clone"},
{NameUrl: "[Ceph RBD](https://github.com/ceph/ceph-csi)", DriverName: "rbd.csi.ceph.com", Versions: "v0.3, v1.0.0, v1.1.0, v1.2.0", Description: "A Container Storage Interface (CSI) Driver for Ceph RBD", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Topology, Cloning"},
{NameUrl: "[CephFS](https://github.com/ceph/ceph-csi)", DriverName: "cephfs.csi.ceph.com", Versions: "v0.3, >=v1.0.0", Description: "A Container Storage Interface (CSI) Driver for CephFS", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Expansion, Snapshot, Cloning"},
{NameUrl: "[Ceph RBD](https://github.com/ceph/ceph-csi)", DriverName: "rbd.csi.ceph.com", Versions: "v0.3, >=v1.0.0", Description: "A Container Storage Interface (CSI) Driver for Ceph RBD", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Topology, Cloning"},
{NameUrl: "[ChubaoFS](https://github.com/chubaofs/chubaofs-csi)", DriverName: "csi.chubaofs.com", Versions: "v1.0.0", Description: "A Container Storage Interface (CSI) Driver for ChubaoFS Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
{NameUrl: "[Cinder](https://github.com/kubernetes/cloud-provider-openstack/tree/master/pkg/csi/cinder)", DriverName: "cinder.csi.openstack.org", Versions: "v0.3, v1.0, v1.1", Description: "A Container Storage Interface (CSI) Driver for OpenStack Cinder", Persistence: "Persistent and Ephemeral", AccessModes: "Depends on the storage backend used", DynamicProvisioning: "Yes, if storage backend supports it", Features: "Raw Block, Snapshot, Expansion"},
{NameUrl: "[Cinder](https://github.com/kubernetes/cloud-provider-openstack/tree/master/pkg/csi/cinder)", DriverName: "cinder.csi.openstack.org", Versions: "v0.3, v1.0, v1.1.0, v1.2.0, v1.3.0", Description: "A Container Storage Interface (CSI) Driver for OpenStack Cinder", Persistence: "Persistent and Ephemeral", AccessModes: "Depends on the storage backend used", DynamicProvisioning: "Yes, if storage backend supports it", Features: "Raw Block, Snapshot, Expansion, Cloning, Topology"},
{NameUrl: "[cloudscale.ch](https://github.com/cloudscale-ch/csi-cloudscale)", DriverName: "csi.cloudscale.ch", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for the [cloudscale.ch](https://www.cloudscale.ch/) IaaS platform", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
{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"},
@ -32,11 +32,10 @@ var CSIDriverList = []*CSIDriver{
{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 and Ephemeral", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning, Topology"},
{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 and Ephemeral", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning, Topology"},
{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, Cloning, Topology"},
{NameUrl: "[democratic-csi](https://github.com/democratic-csi/democratic-csi)", DriverName: "org.democratic-csi", 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", Versions: "v1.0,v1.1,v1.2,v1.3,v1.4,v1.5", 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/)), [Synology](https://www.synology.com/), and more", 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: "[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: "[Dothill-CSI](https://github.com/enix/dothill-csi)", DriverName: "dothill.csi.enix.io", Versions: "v1.3", Description: "Generic CSI plugin supporting [Seagate AssuredSan](https://www.seagate.com/fr/fr/support/dothill-san/assuredsan-pro-5000-series/) appliances such as [HPE MSA](https://www.hpe.com/us/en/storage/flash-hybrid.html), [Dell EMC PowerVault ME4](https://www.dell.com/fr-fr/work/shop/productdetailstxn/powervault-me4-series) and others ...", Persistence: "Persistent", AccessModes: "Read/Write Single Node", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion"},
{NameUrl: "[DriveScale](https://github.com/DriveScale/k8s-plugins)", DriverName: "csi.drivescale.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for DriveScale software composable infrastructure solution", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
{NameUrl: "[Ember CSI](https://ember-csi.io)", DriverName: "ember-csi.io", Versions: "v0.2, v0.3, v1.0", Description: "Multi-vendor CSI plugin supporting over 80 Drivers to provide block and mount storage to Container Orchestration systems.", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot"},
{NameUrl: "[Excelero NVMesh](https://github.com/Excelero/nvmesh-csi-driver)", DriverName: "nvmesh-csi.excelero.com", Versions: "v1.0, v1.1", Description: "A Container Storage Interface (CSI) Driver for Excelero NVMesh", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Expansion"},
{NameUrl: "[GCE Persistent Disk](https://github.com/kubernetes-sigs/gcp-compute-persistent-disk-csi-driver)", DriverName: "pd.csi.storage.gke.io", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for Google Compute Engine Persistent Disk (GCE PD)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Topology"},
@ -48,10 +47,11 @@ var CSIDriverList = []*CSIDriver{
{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: "[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.3", Description: "A [multi-platform](https://scod.hpedev.io/csi_driver) Container Storage Interface (CSI) driver. Supports [HPE Alletra](https://hpe.com/storage/alletra), [Nimble Storage](https://hpe.com/storage/nimble), [Primera](https://hpe.com/storage/primera) and [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 Ezmeral (MapR)](https://github.com/mapr/mapr-csi)", DriverName: "com.mapr.csi-kdf", Versions: "v1.3", Description: "A Container Storage Interface (CSI) Driver for HPE Ezmeral Data Fabric", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
{NameUrl: "[Huawei Storage CSI](https://github.com/Huawei/eSDK_K8S_Plugin)", DriverName: "csi.huawei.com", Versions: "v1.0, v1.1, v1.2", Description: "A Container Storage Interface (CSI) Driver for FusionStorage, OceanStor 100D, OceanStor Pacific, OceanStor Dorado V3, OceanStor Dorado V6, OceanStor V3, OceanStor V5", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pod", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Cloning"},
{NameUrl: "[HyperV CSI](https://github.com/Zetanova/hyperv-csi-driver)", DriverName: "eu.zetanova.csi.hyperv", Versions: "v1.0, v1.1", Description: "A Container Storage Interface (CSI) driver to manage hyperv hosts", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
{NameUrl: "[IBM Block Storage](https://github.com/ibm/ibm-block-csi-driver)", DriverName: "block.csi.ibm.com", Versions: "v1.0, v1.1, v1.2", Description: "A Container Storage Interface (CSI) [Driver](https://www.ibm.com/support/knowledgecenter/SSRQ8T) for IBM Spectrum Virtualize Family, IBM FlashSystem A9000 and A9000R, IBM DS8880 and DS8900.", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
{NameUrl: "[IBM Block Storage](https://github.com/ibm/ibm-block-csi-driver)", DriverName: "block.csi.ibm.com", Versions: "v1.0, v1.1, v1.2", Description: "A Container Storage Interface (CSI) [Driver](https://www.ibm.com/docs/en/stg-block-csi-driver) for IBM Spectrum Virtualize Family, IBM FlashSystem A9000 and A9000R, IBM DS8000 Family 8.x and higher.", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning, Topology"},
{NameUrl: "[IBM Spectrum Scale](https://github.com/IBM/ibm-spectrum-scale-csi)", DriverName: "spectrumscale.csi.ibm.com", Versions: "v1.0, v1.1", Description: "A Container Storage Interface (CSI) [Driver](https://www.ibm.com/docs/en/spectrum-scale-csi) for the IBM Spectrum Scale File System", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
{NameUrl: "[IBM Cloud Block Storage VPC CSI Driver](https://cloud.ibm.com/docs/containers?topic=containers-vpc-block)", DriverName: "vpc.block.csi.ibm.io", Versions: "v1.0", Description: "A Container Storage Interface (CSI) [Driver](https://cloud.ibm.com/docs/containers?topic=containers-vpc-block) for IBM Cloud Kubernetes Service and Red Hat OpenShift on IBM Cloud", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block"},
{NameUrl: "[Infinidat](https://github.com/Infinidat/infinibox-csi-driver)", DriverName: "infinibox-csi-driver", Versions: "v1.0, v1.1", Description: "A Container Storage Interface (CSI) Driver for Infinidat [InfiniBox](https://infinidat.com/en/products-technology/infinibox)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
@ -59,25 +59,26 @@ 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: "[Intelliflash Block Storage](https://github.com/DDNStorage/intelliflash-csi-block-driver)", DriverName: "intelliflash-csi-block-driver.intelliflash.com", Versions: "v1.0, v1.1, v1.2", Description: "A Container Storage Interface (CSI) Driver for Intelliflash Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Cloning, Topology"},
{NameUrl: "[Intelliflash File Storage](https://github.com/DDNStorage/intelliflash-csi-file-driver)", DriverName: "intelliflash-csi-file-driver.intelliflash.com", Versions: "v1.0, v1.1, v1.2", Description: "A Container Storage Interface (CSI) Driver for Intelliflash File Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Cloning, Topology"},
{NameUrl: "[ionir ](https://github.com/ionir-cloud)", DriverName: "ionir", Versions: "v1.2", Description: "A Container Storage Interface (CSI) Driver for [ionir](https://www.ionir.com/) Kubernetes-Native Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Cloning"},
{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: "[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: "[LINSTOR](https://github.com/piraeusdatastore/linstor-csi)", DriverName: "linstor.csi.linbit.com", Versions: "v1.2", 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: "Raw Block, Snapshot, Expansion, Cloning, Topology"},
{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.2", 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: "[MacroSAN](https://github.com/macrosan-csi/macrosan-csi-driver)", DriverName: "csi-macrosan", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for MacroSAN Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: ""},
{NameUrl: "[Manila](https://github.com/kubernetes/cloud-provider-openstack/tree/master/pkg/csi/manila)", DriverName: "manila.csi.openstack.org", Versions: "v1.1, v1.2", Description: "A Container Storage Interface (CSI) Driver for OpenStack Shared File System Service (Manila)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Topology"},
{NameUrl: "[MapR](https://github.com/mapr/mapr-csi)", DriverName: "com.mapr.csi-kdf", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for MapR Data Platform", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot"},
{NameUrl: "[MooseFS](https://github.com/moosefs/moosefs-csi)", DriverName: "com.tuxera.csi.moosefs", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for [MooseFS](https://moosefs.com/) clusters.", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: ""},
{NameUrl: "[NetApp](https://github.com/NetApp/trident)", DriverName: "csi.trident.netapp.io", Versions: "v1.0, v1.1, v1.2", Description: "A Container Storage Interface (CSI) Driver for NetApp's [Trident](https://netapp-trident.readthedocs.io/) container storage orchestrator", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning, Topology"},
{NameUrl: "[NetApp](https://github.com/NetApp/trident)", DriverName: "csi.trident.netapp.io", Versions: "v1.0, v1.1, v1.2, v1.3", Description: "A Container Storage Interface (CSI) Driver for NetApp's [Trident](https://netapp-trident.readthedocs.io/) container storage orchestrator", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning, Topology"},
{NameUrl: "[NexentaStor File Storage](https://github.com/Nexenta/nexentastor-csi-driver)", DriverName: "nexentastor-csi-driver.nexenta.com", Versions: "v1.0, v1.1, v1.2", Description: "A Container Storage Interface (CSI) Driver for NexentaStor File Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Cloning, Topology"},
{NameUrl: "[NexentaStor Block Storage](https://github.com/Nexenta/nexentastor-csi-driver-block)", DriverName: "nexentastor-block-csi-driver.nexenta.com", Versions: "v1.0, v1.1, v1.2", Description: "A Container Storage Interface (CSI) Driver for NexentaStor over iSCSI protocol", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Cloning, Topology, Raw block"},
{NameUrl: "[Nutanix](https://github.com/nutanix/csi-plugin)", DriverName: "com.nutanix.csi", Versions: "v0.3, v1.0, v1.2", Description: "A Container Storage Interface (CSI) Driver for Nutanix", Persistence: "Persistent", AccessModes: "Read/Write Single Pod with Nutanix Volumes and Read/Write Multiple Pods with Nutanix Files", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Cloning"},
{NameUrl: "[OpenEBS](https://github.com/openebs/csi)", DriverName: "cstor.csi.openebs.io", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for [OpenEBS](https://www.openebs.io/)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Expansion, 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: "[Open-Local](https://github.com/alibaba/open-local)", DriverName: "local.csi.alibaba.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Local Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Expansion, Snapshot"},
{NameUrl: "[Oracle Cloud Infrastructure(OCI) Block Storage](https://github.com/oracle/oci-cloud-controller-manager/blob/master/container-storage-interface.md)", DriverName: "blockvolume.csi.oraclecloud.com", Versions: "v1.1", Description: "A Container Storage Interface (CSI) Driver for Oracle Cloud Infrastructure (OCI) Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Topology"},
{NameUrl: "[oVirt](https://github.com/openshift/ovirt-csi-driver)", DriverName: "csi.ovirt.org", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for [oVirt](https://ovirt.org)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Block, File Storage"},
{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.portworx.com", Versions: "v1.4", Description: "A Container Storage Interface (CSI) Driver for [Portworx](https://docs.portworx.com/portworx-install-with-kubernetes/storage-operations/csi/)", Persistence: "Persistent and Ephemeral", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Raw Block, Cloning"},
{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: "Raw Block, Snapshot, Expansion, Cloning"},
{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"},
@ -87,7 +88,8 @@ var CSIDriverList = []*CSIDriver{
{NameUrl: "[Sangfor-EDS-File-Storage](https://github.com/evan37717/sangfor-eds-csi)", DriverName: "eds.csi.file.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: "Raw Block, Snapshot, Expansion, Cloning"},
{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: "Raw Block, Snapshot, Expansion, Cloning"},
{NameUrl: "[Scaleway CSI](https://github.com/scaleway/scaleway-csi)", DriverName: "csi.scaleway.com", Versions: "v1.2.0", Description: "Container Storage Interface (CSI) Driver for [Scaleway Block Storage](https://www.scaleway.com/block-storage/)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot, Expansion, Topology"},
{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: "Raw Block, Snapshot, Expansion, Topology"},
{NameUrl: "[Seagate Exos X](https://github.com/Seagate/seagate-exos-x-csi)", DriverName: "csi-exos-x.seagate.com", Versions: "v1.3", Description: "CSI driver for [Seagate Exos X](https://www.seagate.com/products/storage/data-storage-systems/raid/) and OEM systems", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Cloning"},
{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: "Snapshot, Expansion, Cloning"},
{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: "[SODA](https://github.com/sodafoundation/nbp/tree/master/csi)", DriverName: "csi-soda-plugin", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for [SODA](https://sodafoundation.io/)", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot"},
@ -95,6 +97,7 @@ var CSIDriverList = []*CSIDriver{
{NameUrl: "[StorageOS](https://docs.storageos.com/docs/platforms/kubernetes/install/)", DriverName: "storageos", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for [StorageOS](https://storageos.com/)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Raw Block, Snapshot"},
{NameUrl: "[Storidge](https://docs.storidge.com/kubernetes_storage/overview.html)", DriverName: "csi.cio.storidge.com", Versions: "v0.3, v1.0", Description: "A Container Storage Interface (CSI) Driver for [Storidge CIO](https://storidge.com/)", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion"},
{NameUrl: "[StorPool](https://kb.storpool.com/storpool_integrations/github/kubernetes.html)", DriverName: "csi-driver.storpool.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for [StorPool](https://storpool.com/)", Persistence: "Persistent and Ephemeral", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Expansion"},
{NameUrl: "[Synology](https://github.com/SynologyOpenSource/synology-csi)", DriverName: "csi.san.synology.com", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Synology NAS", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot, Expansion, Cloning"},
{NameUrl: "[Tencent Cloud Block Storage](https://github.com/TencentCloud/kubernetes-csi-tencentcloud)", DriverName: "com.tencent.cloud.csi.cbs", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Tencent Cloud Block Storage", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "Yes", Features: "Snapshot"},
{NameUrl: "[Tencent Cloud File Storage](https://github.com/TencentCloud/kubernetes-csi-tencentcloud)", DriverName: "com.tencent.cloud.csi.cfs", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Tencent Cloud File Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "Yes", Features: "Snapshot"},
{NameUrl: "[Tencent Cloud Object Storage](https://github.com/TencentCloud/kubernetes-csi-tencentcloud)", DriverName: "com.tencent.cloud.csi.cosfs", Versions: "v1.0", Description: "A Container Storage Interface (CSI) Driver for Tencent Cloud Object Storage", Persistence: "Persistent", AccessModes: "Read/Write Multiple Pods", DynamicProvisioning: "No", Features: "Snapshot"},
@ -103,6 +106,7 @@ 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-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: "[VDA](https://virtual-disk-array.readthedocs.io/en/latest/Introduction.html)", DriverName: "csi.vda.io", Versions: "v1.0", Description: "An open source block storage system base on SPDK", Persistence: "Persistent", AccessModes: "Read/Write Single Pod", DynamicProvisioning: "N/A", Features: ""},
{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: ""},