Skip to content

Commit

Permalink
Make SetVolumeID function support cross-project scenario.
Browse files Browse the repository at this point in the history
Signed-off-by: Xun Jiang <[email protected]>
  • Loading branch information
Xun Jiang committed Sep 20, 2023
1 parent 5b8e5e8 commit 89e8abe
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 24 deletions.
2 changes: 1 addition & 1 deletion examples/backup_at_b_restore_at_a.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ These steps are heavily inspired by the [gcp documentation](https://cloud.google
Assume the following...

- Project A [project-a]: GCP project we want to restore TO
- Project B [project-b]: GCP Project we want to restore FROM
- Project B [project-b]: GCP Project we want to backup FROM

The steps below assume that you have not setup Velero yet. So make sure to skip any steps you've already completed.

Expand Down
2 changes: 1 addition & 1 deletion examples/velero_at_a_br_at_other.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Assume the following...

- Project A [project-a]: The project where the Velero's service account is located, and the Velero service account is granted to have enough permission to do backup and restore in the other projects.
- Project B [project-b]: The GCP project we want to restore TO.
- Project C [project-c]: The GCP project we want to restore FROM.
- Project C [project-c]: The GCP project we want to backup FROM.

## Set up Velero with permission in projects
* In **project-a**
Expand Down
19 changes: 19 additions & 0 deletions velero-plugin-for-gcp/volume_snapshotter.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,10 @@ func (b *VolumeSnapshotter) SetVolumeID(unstructuredPV runtime.Unstructured, vol
return nil, fmt.Errorf("invalid volumeHandle for restore with CSI driver:%s, expected projects/{project}/zones/{zone}/disks/{name}, got %s",
pdCSIDriver, handle)
}
if b.IsVolumeCreatedCrossProjects(handle) == true {
projectRE := regexp.MustCompile(`projects\/[^\/]+\/`)
handle = projectRE.ReplaceAllString(handle, "projects/"+b.volumeProject+"/")
}
pv.Spec.CSI.VolumeHandle = handle[:strings.LastIndex(handle, "/")+1] + volumeID
} else {
return nil, fmt.Errorf("unable to handle CSI driver: %s", driver)
Expand All @@ -433,3 +437,18 @@ func (b *VolumeSnapshotter) SetVolumeID(unstructuredPV runtime.Unstructured, vol

return &unstructured.Unstructured{Object: res}, nil
}

func (b *VolumeSnapshotter) IsVolumeCreatedCrossProjects(volumeHandle string) bool {
// Get project ID from volume handle
parsedStr := strings.Split(volumeHandle, "/")
if len(parsedStr) < 2 {
return false
}
projectID := parsedStr[1]

if projectID != b.volumeProject {
return true
}

return false
}
103 changes: 81 additions & 22 deletions velero-plugin-for-gcp/volume_snapshotter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package main
import (
"encoding/json"
"os"
"strings"
"testing"

"github.com/pkg/errors"
Expand Down Expand Up @@ -156,15 +155,13 @@ func TestSetVolumeID(t *testing.T) {
}

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

cases := []struct {
name string
csiJSON string
volumeID string
wantErr bool
name string
csiJSON string
volumeID string
wantErr bool
volumeProject string
wantedVolumeID string
}{
{
name: "set ID to CSI with GKE pd CSI driver",
Expand All @@ -173,8 +170,10 @@ func TestSetVolumeIDForCSI(t *testing.T) {
"fsType": "ext4",
"volumeHandle": "projects/velero-gcp/zones/us-central1-f/disks/pvc-a970184f-6cc1-4769-85ad-61dcaf8bf51d"
}`,
volumeID: "restore-fd9729b5-868b-4544-9568-1c5d9121dabc",
wantErr: false,
volumeID: "restore-fd9729b5-868b-4544-9568-1c5d9121dabc",
wantErr: false,
volumeProject: "velero-gcp",
wantedVolumeID: "projects/velero-gcp/zones/us-central1-f/disks/restore-fd9729b5-868b-4544-9568-1c5d9121dabc",
},
{
name: "set ID to CSI with GKE pd CSI driver, but the volumeHandle is invalid",
Expand All @@ -183,22 +182,41 @@ func TestSetVolumeIDForCSI(t *testing.T) {
"fsType": "ext4",
"volumeHandle": "pvc-a970184f-6cc1-4769-85ad-61dcaf8bf51d"
}`,
volumeID: "restore-fd9729b5-868b-4544-9568-1c5d9121dabc",
wantErr: true,
volumeID: "restore-fd9729b5-868b-4544-9568-1c5d9121dabc",
wantErr: true,
volumeProject: "velero-gcp",
},
{
name: "set ID to CSI with unknown driver",
csiJSON: `"{
csiJSON: `{
"driver": "xxx.csi.storage.gke.io",
"fsType": "ext4",
"volumeHandle": "projects/velero-gcp/zones/us-central1-f/disks/pvc-a970184f-6cc1-4769-85ad-61dcaf8bf51d"
}`,
volumeID: "restore-fd9729b5-868b-4544-9568-1c5d9121dabc",
wantErr: true,
volumeID: "restore-fd9729b5-868b-4544-9568-1c5d9121dabc",
wantErr: true,
volumeProject: "velero-gcp",
},
{
name: "volume project is different from original handle project",
csiJSON: `{
"driver": "pd.csi.storage.gke.io",
"fsType": "ext4",
"volumeHandle": "projects/velero-gcp/zones/us-central1-f/disks/pvc-a970184f-6cc1-4769-85ad-61dcaf8bf51d"
}`,
volumeID: "restore-fd9729b5-868b-4544-9568-1c5d9121dabc",
wantErr: false,
volumeProject: "velero-gcp-2",
wantedVolumeID: "projects/velero-gcp-2/zones/us-central1-f/disks/restore-fd9729b5-868b-4544-9568-1c5d9121dabc",
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
b := &VolumeSnapshotter{
log: logrus.New(),
volumeProject: tt.volumeProject,
}

res := &unstructured.Unstructured{
Object: map[string]interface{}{},
}
Expand All @@ -207,17 +225,16 @@ func TestSetVolumeIDForCSI(t *testing.T) {
res.Object["spec"] = map[string]interface{}{
"csi": csi,
}
originalVolHanle, _ := csi["volumeHandle"].(string)
newRes, err := b.SetVolumeID(res, tt.volumeID)
if tt.wantErr {
assert.Error(t, err)
require.Error(t, err)
} else {
assert.NoError(t, err)
require.NoError(t, err)
newPV := new(v1.PersistentVolume)
require.NoError(t, runtime.DefaultUnstructuredConverter.FromUnstructured(newRes.UnstructuredContent(), newPV))
ind := strings.LastIndex(newPV.Spec.CSI.VolumeHandle, "/")
assert.Equal(t, tt.volumeID, newPV.Spec.CSI.VolumeHandle[ind+1:])
assert.Equal(t, originalVolHanle[:ind], newPV.Spec.CSI.VolumeHandle[:ind])
if tt.wantedVolumeID != "" {
require.Equal(t, tt.wantedVolumeID, newPV.Spec.CSI.VolumeHandle)
}
}
})
}
Expand Down Expand Up @@ -421,3 +438,45 @@ func TestInit(t *testing.T) {
err = os.Remove(default_credential_file_name)
require.NoError(t, err)
}

func TestIsVolumeCreatedCrossProjects(t *testing.T) {
tests := []struct {
name string
volumeSnapshotter VolumeSnapshotter
volumeHandle string
expectedResult bool
}{
{
name: "Invalid Volume handle",
volumeSnapshotter: VolumeSnapshotter{
log: logrus.New(),
},
volumeHandle: "InvalidHandle",
expectedResult: false,
},
{
name: "Volume is created cross-project",
volumeSnapshotter: VolumeSnapshotter{
log: logrus.New(),
volumeProject: "velero-gcp-2",
},
volumeHandle: "projects/velero-gcp/zones/us-central1-f/disks/pvc-a970184f-6cc1-4769-85ad-61dcaf8bf51d",
expectedResult: true,
},
{
name: "Volume is not created cross-project",
volumeSnapshotter: VolumeSnapshotter{
log: logrus.New(),
volumeProject: "velero-gcp",
},
volumeHandle: "projects/velero-gcp/zones/us-central1-f/disks/pvc-a970184f-6cc1-4769-85ad-61dcaf8bf51d",
expectedResult: false,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require.Equal(t, test.expectedResult, test.volumeSnapshotter.IsVolumeCreatedCrossProjects(test.volumeHandle))
})
}
}

0 comments on commit 89e8abe

Please sign in to comment.