Skip to content

Commit

Permalink
Handle the volumes created by the CSI driver
Browse files Browse the repository at this point in the history
Current VolumeSnapshotter works with volumes allocated by the "in-tree" storage driver. Kubernetes is moving to the CSI driver for Azure and will deprecate the in-tree driver, however we may not be able to get CSI snapshots in Velero to GA before that happens. As an interim solution, we handle volumes created by the CSI driver with the existing VolumeSnapshotter.

Fixes vmware-tanzu/velero#4109

Signed-off-by: Wenkai Yin(尹文开) <yinw@vmware.com>
  • Loading branch information
ywk253100 committed Nov 16, 2021
1 parent 9621c88 commit 54d435b
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 8 deletions.
36 changes: 29 additions & 7 deletions velero-plugin-for-microsoft-azure/volume_snapshotter.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ const (

snapshotsResource = "snapshots"
disksResource = "disks"

diskCSIDriver = "disk.csi.azure.com"
)

type VolumeSnapshotter struct {
Expand Down Expand Up @@ -358,8 +360,11 @@ func getComputeResourceName(subscription, resourceGroup, resource, name string)
return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/%s/%s", subscription, resourceGroup, resource, name)
}

var snapshotURIRegexp = regexp.MustCompile(
`^\/subscriptions\/(?P<subscription>.*)\/resourceGroups\/(?P<resourceGroup>.*)\/providers\/Microsoft.Compute\/snapshots\/(?P<snapshotName>.*)$`)
var (
snapshotURIRegexp = regexp.MustCompile(
`^\/subscriptions\/(?P<subscription>.*)\/resourceGroups\/(?P<resourceGroup>.*)\/providers\/Microsoft.Compute\/snapshots\/(?P<snapshotName>.*)$`)
diskURIRegexp = regexp.MustCompile(`\/Microsoft.Compute\/disks\/.*$`)
)

// parseFullSnapshotName takes a fully-qualified snapshot name and returns
// a snapshot identifier or an error if the snapshot name does not match the
Expand Down Expand Up @@ -396,6 +401,13 @@ func (b *VolumeSnapshotter) GetVolumeID(unstructuredPV runtime.Unstructured) (st
return "", errors.WithStack(err)
}

if pv.Spec.CSI != nil {
if pv.Spec.CSI.Driver == diskCSIDriver {
return strings.TrimPrefix(diskURIRegexp.FindString(pv.Spec.CSI.VolumeHandle), "/Microsoft.Compute/disks/"), nil
}
b.log.Infof("Unable to handle CSI driver: %s", pv.Spec.CSI.Driver)
}

if pv.Spec.AzureDisk == nil {
return "", nil
}
Expand All @@ -413,12 +425,22 @@ func (b *VolumeSnapshotter) SetVolumeID(unstructuredPV runtime.Unstructured, vol
return nil, errors.WithStack(err)
}

if pv.Spec.AzureDisk == nil {
return nil, errors.New("spec.azureDisk not found")
}
if pv.Spec.CSI != nil {
if pv.Spec.CSI.Driver == diskCSIDriver {
pv.Spec.CSI.VolumeHandle = getComputeResourceName(b.disksSubscription, b.disksResourceGroup, disksResource, volumeID)
if _, exist := pv.Spec.CSI.VolumeAttributes["csi.storage.k8s.io/pv/name"]; exist {
pv.Spec.CSI.VolumeAttributes["csi.storage.k8s.io/pv/name"] = volumeID
}
} else {
return nil, fmt.Errorf("unable to handle CSI driver: %s", pv.Spec.CSI.Driver)
}

pv.Spec.AzureDisk.DiskName = volumeID
pv.Spec.AzureDisk.DataDiskURI = getComputeResourceName(b.disksSubscription, b.disksResourceGroup, disksResource, volumeID)
} else if pv.Spec.AzureDisk != nil {
pv.Spec.AzureDisk.DiskName = volumeID
pv.Spec.AzureDisk.DataDiskURI = getComputeResourceName(b.disksSubscription, b.disksResourceGroup, disksResource, volumeID)
} else {
return nil, errors.New("spec.csi and spec.azureDisk not found")
}

res, err := runtime.DefaultUnstructuredConverter.ToUnstructured(pv)
if err != nil {
Expand Down
52 changes: 51 additions & 1 deletion velero-plugin-for-microsoft-azure/volume_snapshotter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package main
import (
"testing"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
Expand All @@ -27,7 +28,9 @@ import (
)

func TestGetVolumeID(t *testing.T) {
b := &VolumeSnapshotter{}
b := &VolumeSnapshotter{
log: logrus.New(),
}

pv := &unstructured.Unstructured{
Object: map[string]interface{}{},
Expand All @@ -52,6 +55,26 @@ func TestGetVolumeID(t *testing.T) {
volumeID, err = b.GetVolumeID(pv)
assert.NoError(t, err)
assert.Equal(t, "foo", volumeID)

// CSI driver: unknown driver name
csi := map[string]interface{}{
"driver": "unknown.csi.azure.com",
"volumeHandle": " /subscriptions/subscription-id/resourceGroups/resource-group-name/providers/Microsoft.Compute/disks/bar",
}
pv.Object["spec"].(map[string]interface{})["csi"] = csi
volumeID, err = b.GetVolumeID(pv)
assert.NoError(t, err)
assert.Equal(t, "foo", volumeID)

// CSI driver: pass
csi = map[string]interface{}{
"driver": "disk.csi.azure.com",
"volumeHandle": " /subscriptions/subscription-id/resourceGroups/resource-group-name/providers/Microsoft.Compute/disks/bar",
}
pv.Object["spec"].(map[string]interface{})["csi"] = csi
volumeID, err = b.GetVolumeID(pv)
assert.NoError(t, err)
assert.Equal(t, "bar", volumeID)
}

func TestSetVolumeID(t *testing.T) {
Expand Down Expand Up @@ -92,6 +115,33 @@ func TestSetVolumeID(t *testing.T) {
require.NotNil(t, res.Spec.AzureDisk)
assert.Equal(t, "revised", res.Spec.AzureDisk.DiskName)
assert.Equal(t, "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Compute/disks/revised", res.Spec.AzureDisk.DataDiskURI)

// CSI driver: unknown driver name
csi := map[string]interface{}{
"driver": "unknown.csi.azure.com",
"volumeHandle": " /subscriptions/subscription-id/resourceGroups/resource-group-name/providers/Microsoft.Compute/disks/foo",
}
pv.Object["spec"].(map[string]interface{})["csi"] = csi
_, err = b.SetVolumeID(pv, "updated")
require.Error(t, err)

// CSI driver: pass
csi = map[string]interface{}{
"driver": "disk.csi.azure.com",
"volumeHandle": " /subscriptions/subscription-id/resourceGroups/resource-group-name/providers/Microsoft.Compute/disks/foo",
"volumeAttributes": map[string]string{
"csi.storage.k8s.io/pv/name": "foo",
},
}
pv.Object["spec"].(map[string]interface{})["csi"] = csi
updatedPV, err = b.SetVolumeID(pv, "updated")
require.NoError(t, err)

res = new(v1.PersistentVolume)
require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(updatedPV.UnstructuredContent(), res))
require.NotNil(t, res.Spec.CSI)
assert.Equal(t, "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Compute/disks/updated", res.Spec.CSI.VolumeHandle)
assert.Equal(t, "updated", res.Spec.CSI.VolumeAttributes["csi.storage.k8s.io/pv/name"])
}

func TestParseFullSnapshotName(t *testing.T) {
Expand Down

0 comments on commit 54d435b

Please sign in to comment.