Skip to content

Commit

Permalink
Merge pull request #178 from anshulahuja98/vscselect
Browse files Browse the repository at this point in the history
Add support for Multiple VolumeSnapshotClasses
  • Loading branch information
blackpiglet committed Jul 19, 2023
2 parents cf77ddf + 56ccb24 commit 77ee0b4
Show file tree
Hide file tree
Showing 5 changed files with 341 additions and 15 deletions.
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,57 @@ Below is a listing of plugin versions and respective Velero versions that are co
| v0.3.0 | v1.9.x |
| v0.2.0 | v1.7.x, v1.8.x |

### Choosing VolumeSnapshotClass For snapshotting (>=0.6.0)
#### Default Behavior
You can simply create a VolumeSnapshotClass for a particular driver and put a label on it to indicate that it is the default VolumeSnapshotClass for that driver. For example, if you want to create a VolumeSnapshotClass for the CSI driver `disk.csi.cloud.com` for taking snapshots of disks created with `disk.csi.cloud.com` based storage classes, you can create a VolumeSnapshotClass like this:
```yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: test-snapclass
labels:
velero.io/csi-volumesnapshot-class: "true"
driver: disk.csi.cloud.com
```
> Note: For each driver type, there should only be 1 VolumeSnapshotClass with the label `velero.io/csi-volumesnapshot-class: "true"`.

#### Choose VolumeSnapshotClass for a particular Backup Or Schedule
If you want to use a particular VolumeSnapshotClass for a particular backup or schedule, you can add a annotation to the backup or schedule to indicate which VolumeSnapshotClass to use. For example, if you want to use the VolumeSnapshotClass `test-snapclass` for a particular backup for snapshotting PVCs of `disk.csi.cloud.com`, you can create a backup like this:
```yaml
apiVersion: velero.io/v1
kind: Backup
metadata:
name: test-backup
annotations:
velero.io/csi-volumesnapshot-class/disk.csi.cloud.com: "test-snapclass"
spec:
includedNamespaces:
- default
```

> Note: Please ensure all your annotations are in lowercase. And follow the following format: `velero.io/csi-volumesnapshot-class/<driver name> = <VolumeSnapshotClass Name>`

#### Choosing VolumeSnapshotClass for a particular PVC
If you want to use a particular VolumeSnapshotClass for a particular PVC, you can add a annotation to the PVC to indicate which VolumeSnapshotClass to use. This overrides any annotation added to backup or schedule. For example, if you want to use the VolumeSnapshotClass `test-snapclass` for a particular PVC, you can create a PVC like this:
```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
annotations:
velero.io/csi-volumesnapshot-class: "test-snapclass"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: disk.csi.cloud.com
```

> Note: Please ensure all your annotations are in lowercase. And follow the following format: `velero.io/csi-volumesnapshot-class = <VolumeSnapshotClass Name>`

## Filing issues

If you would like to file a GitHub issue for the plugin, please open the issue on the [core Velero repo][103]
Expand Down
2 changes: 1 addition & 1 deletion internal/backup/pvc_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (p *PVCBackupItemAction) Execute(item runtime.Unstructured, backup *velerov
return nil, nil, "", nil, errors.Wrap(err, "error getting storage class")
}
p.Log.Debugf("Fetching volumesnapshot class for %s", storageClass.Provisioner)
snapshotClass, err := util.GetVolumeSnapshotClassForStorageClass(storageClass.Provisioner, p.SnapshotClient.SnapshotV1())
snapshotClass, err := util.GetVolumeSnapshotClass(storageClass.Provisioner, backup, &pvc, p.Log, p.SnapshotClient.SnapshotV1())
if err != nil {
return nil, nil, "", nil, errors.Wrapf(err, "failed to get volumesnapshotclass for storageclass %s", storageClass.Name)
}
Expand Down
18 changes: 10 additions & 8 deletions internal/util/labels_annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ limitations under the License.
package util

const (
VolumeSnapshotLabel = "velero.io/volume-snapshot-name"
VolumeSnapshotHandleAnnotation = "velero.io/csi-volumesnapshot-handle"
VolumeSnapshotRestoreSize = "velero.io/vsi-volumesnapshot-restore-size"
CSIDriverNameAnnotation = "velero.io/csi-driver-name"
CSIDeleteSnapshotSecretName = "velero.io/csi-deletesnapshotsecret-name"
CSIDeleteSnapshotSecretNamespace = "velero.io/csi-deletesnapshotsecret-namespace"
CSIVSCDeletionPolicy = "velero.io/csi-vsc-deletion-policy"
VolumeSnapshotClassSelectorLabel = "velero.io/csi-volumesnapshot-class"
VolumeSnapshotLabel = "velero.io/volume-snapshot-name"
VolumeSnapshotHandleAnnotation = "velero.io/csi-volumesnapshot-handle"
VolumeSnapshotRestoreSize = "velero.io/vsi-volumesnapshot-restore-size"
CSIDriverNameAnnotation = "velero.io/csi-driver-name"
CSIDeleteSnapshotSecretName = "velero.io/csi-deletesnapshotsecret-name"
CSIDeleteSnapshotSecretNamespace = "velero.io/csi-deletesnapshotsecret-namespace"
CSIVSCDeletionPolicy = "velero.io/csi-vsc-deletion-policy"
VolumeSnapshotClassSelectorLabel = "velero.io/csi-volumesnapshot-class"
VolumeSnapshotClassDriverBackupAnnotationPrefix = "velero.io/csi-volumesnapshot-class"
VolumeSnapshotClassDriverPVCAnnotation = "velero.io/csi-volumesnapshot-class"

// There is no release w/ these constants exported. Using the strings for now.
// CSI Labels volumesnapshotclass
Expand Down
68 changes: 65 additions & 3 deletions internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,75 @@ func IsPVCDefaultToFSBackup(pvcNamespace, pvcName string, podClient corev1client

return false, nil
}

// GetVolumeSnapshotClassForStorageClass returns a VolumeSnapshotClass for the supplied volume provisioner/ driver name.
func GetVolumeSnapshotClassForStorageClass(provisioner string, snapshotClient snapshotter.SnapshotV1Interface) (*snapshotv1api.VolumeSnapshotClass, error) {
func GetVolumeSnapshotClass(provisioner string, backup *velerov1api.Backup, pvc *corev1api.PersistentVolumeClaim, log logrus.FieldLogger, snapshotClient snapshotter.SnapshotV1Interface) (*snapshotv1api.VolumeSnapshotClass, error) {
snapshotClasses, err := snapshotClient.VolumeSnapshotClasses().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "error listing volumesnapshot classes")
}
// If a snapshot class is sent for provider in PVC annotations, use that
snapshotClass, err := GetVolumeSnapshotClassFromPVCAnnotationsForDriver(pvc, provisioner, snapshotClasses)
if err != nil {
log.Debugf("Didn't find VolumeSnapshotClass from PVC annotations: %v", err)
}
if snapshotClass != nil {
return snapshotClass, nil
}

// If there is no annotation in PVC, attempt to fetch it from backup annotations
snapshotClass, err = GetVolumeSnapshotClassFromBackupAnnotationsForDriver(backup, provisioner, snapshotClasses)
if err != nil {
log.Debugf("Didn't find VolumeSnapshotClass from Backup annotations: %v", err)
}
if snapshotClass != nil {
return snapshotClass, nil
}

// fallback to default behaviour of fetching snapshot class based on label
snapshotClass, err = GetVolumeSnapshotClassForStorageClass(provisioner, snapshotClasses)
if err != nil || snapshotClass == nil {
return nil, errors.Wrap(err, "error getting volumesnapshotclass")
}

return snapshotClass, nil
}

func GetVolumeSnapshotClassFromPVCAnnotationsForDriver(pvc *corev1api.PersistentVolumeClaim, provisioner string, snapshotClasses *snapshotv1api.VolumeSnapshotClassList) (*snapshotv1api.VolumeSnapshotClass, error) {
annotationKey := VolumeSnapshotClassDriverPVCAnnotation
snapshotClassName, ok := pvc.ObjectMeta.Annotations[annotationKey]
if !ok {
return nil, nil
}
for _, sc := range snapshotClasses.Items {
if strings.EqualFold(snapshotClassName, sc.ObjectMeta.Name) {
if !strings.EqualFold(sc.Driver, provisioner) {
return nil, errors.Errorf("Incorrect volumesnapshotclass, snapshot class %s is not for driver %s", sc.ObjectMeta.Name, provisioner)
}
return &sc, nil
}
}
return nil, errors.Errorf("No CSI VolumeSnapshotClass found with name %s for provisioner %s for PVC %s", snapshotClassName, provisioner, pvc.Name)
}

// GetVolumeSnapshotClassFromAnnotationsForDriver returns a VolumeSnapshotClass for the supplied volume provisioner/ driver name from the annotation of the backup
func GetVolumeSnapshotClassFromBackupAnnotationsForDriver(backup *velerov1api.Backup, provisioner string, snapshotClasses *snapshotv1api.VolumeSnapshotClassList) (*snapshotv1api.VolumeSnapshotClass, error) {
annotationKey := fmt.Sprintf("%s/%s", VolumeSnapshotClassDriverBackupAnnotationPrefix, strings.ToLower(provisioner))
snapshotClassName, ok := backup.ObjectMeta.Annotations[annotationKey]
if !ok {
return nil, nil
}
for _, sc := range snapshotClasses.Items {
if strings.EqualFold(snapshotClassName, sc.ObjectMeta.Name) {
if !strings.EqualFold(sc.Driver, provisioner) {
return nil, errors.Errorf("Incorrect volumesnapshotclass, snapshot class %s is not for driver %s for backup %s", sc.ObjectMeta.Name, provisioner, backup.Name)
}
return &sc, nil
}
}
return nil, errors.Errorf("No CSI VolumeSnapshotClass found with name %s for driver %s for backup %s", snapshotClassName, provisioner, backup.Name)
}

// GetVolumeSnapshotClassForStorageClass returns a VolumeSnapshotClass for the supplied volume provisioner/ driver name.
func GetVolumeSnapshotClassForStorageClass(provisioner string, snapshotClasses *snapshotv1api.VolumeSnapshotClassList) (*snapshotv1api.VolumeSnapshotClass, error) {
n := 0
var vsclass snapshotv1api.VolumeSnapshotClass
// We pick the volumesnapshotclass that matches the CSI driver name and has a 'velero.io/csi-volumesnapshot-class'
Expand Down
Loading

0 comments on commit 77ee0b4

Please sign in to comment.