From be7eb6dada00460cc0d0a486bd49e8e2adde2603 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 24 Jul 2023 21:04:58 +0800 Subject: [PATCH] Add Volume Project setting to support the scenario describe in issue #92 Signed-off-by: Xun Jiang --- examples/README.md | 3 +- ...rojects.md => backup_at_b_restore_at_a.md} | 2 +- examples/velero_at_a_br_at_other.md | 48 +++++++++++++ velero-plugin-for-gcp/volume_snapshotter.go | 9 ++- .../volume_snapshotter_test.go | 67 +++++++++++++++++++ 5 files changed, 125 insertions(+), 4 deletions(-) rename examples/{gcp-projects.md => backup_at_b_restore_at_a.md} (97%) create mode 100644 examples/velero_at_a_br_at_other.md diff --git a/examples/README.md b/examples/README.md index c38796b3..961d9321 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,3 +1,4 @@ # Examples -- [Restore snapshots from GCP across projects](./gcp-projects.md) +- [Backup at project B, and restore at project A](./backup_at_b_restore_at_a.md) +- [Velero at project A, backup and restore at other projects](./velero_at_a_br_at_other.md) diff --git a/examples/gcp-projects.md b/examples/backup_at_b_restore_at_a.md similarity index 97% rename from examples/gcp-projects.md rename to examples/backup_at_b_restore_at_a.md index 304f4ea5..04165b5a 100644 --- a/examples/gcp-projects.md +++ b/examples/backup_at_b_restore_at_a.md @@ -1,4 +1,4 @@ -# Restore snapshots from GCP across projects +# Backup at project B, and restore at project A These steps are heavily inspired by the [gcp documentation](https://cloud.google.com/compute/docs/images/sharing-images-across-projects). diff --git a/examples/velero_at_a_br_at_other.md b/examples/velero_at_a_br_at_other.md new file mode 100644 index 00000000..78293e2f --- /dev/null +++ b/examples/velero_at_a_br_at_other.md @@ -0,0 +1,48 @@ +# Velero at project A, backup and restore at other projects + +This scenario is introduced in [issue 4806](https://github.com/vmware-tanzu/velero/issues/4806). + +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. + +## Set up Velero with permission in projects +* In **project-a** + * Create "Velero Server" IAM role **role-a** with required role permissions. + * Create ServiceAccount **sa-a**. + * Assign **sa-a** with **role-a**. + * Assign **sa-a** with **role-b**(need to run after role-b created in project-b). + * Assign **sa-a** with **role-c**(need to run after role-c created in project-c). + * Create a bucket **bucket-a**. + * Assign [sa-a] "Storage Object Admin" permissions to [bucket-a] + +* In **project-b** + * Add the ServiceAccount **sa-a** into project **project-b** according to [Granting service accounts access to your projects](https://cloud.google.com/marketplace/docs/grant-service-account-access). + * Create ServiceAccount **sa-b**. + * Create "Velero Server" IAM role **role-b** with required role permissions. + * Assign **sa-b** with **role-b**. + +* In **project-c** + * Add the ServiceAccount **sa-a** into project **project-c** according to [Granting service accounts access to your projects](https://cloud.google.com/marketplace/docs/grant-service-account-access). + * Create ServiceAccount **sa-c**. + * Create "Velero Server" IAM role **role-c** with required role permissions. + * Assign **sa-c** with **role-c**. + +## Backup at project C +* In **project-c** + * Install Velero on the k8s cluster in this project with configurations: + * SecretFile: **sa-a** + * SnapshotLocation: project=**project-a** and volumeProject=**project-c** + * Bucket: **bucket-a** + * Create Velero backup **backup-c** with the PVC snapshots desired. + +## Restore at project B +* In **project-b** + * NOTE: Make sure to disable any scheduled backups. + * Install Velero on the k8s cluster in this project with configurations + * SecretFile: **sa-a** + * SnapshotLocation: project=**project-a** and volumeProject=**project-b** + * Bucket: **bucket-a** + * Create Velero restore **restore-b** from backup **backup-c** \ No newline at end of file diff --git a/velero-plugin-for-gcp/volume_snapshotter.go b/velero-plugin-for-gcp/volume_snapshotter.go index b984b189..6e20229f 100644 --- a/velero-plugin-for-gcp/volume_snapshotter.go +++ b/velero-plugin-for-gcp/volume_snapshotter.go @@ -43,6 +43,7 @@ const ( zoneSeparator = "__" projectKey = "project" snapshotLocationKey = "snapshotLocation" + volumeProjectKey = "volumeProject" ) var pdCSIDriver = map[string]bool{ @@ -65,7 +66,8 @@ func newVolumeSnapshotter(logger logrus.FieldLogger) *VolumeSnapshotter { } func (b *VolumeSnapshotter) Init(config map[string]string) error { - if err := veleroplugin.ValidateVolumeSnapshotterConfigKeys(config, snapshotLocationKey, projectKey, credentialsFileConfigKey); err != nil { + if err := veleroplugin.ValidateVolumeSnapshotterConfigKeys(config, + snapshotLocationKey, projectKey, credentialsFileConfigKey, volumeProjectKey); err != nil { return err } @@ -102,7 +104,10 @@ func (b *VolumeSnapshotter) Init(config map[string]string) error { b.snapshotLocation = config[snapshotLocationKey] - b.volumeProject = creds.ProjectID + b.volumeProject = config[volumeProjectKey] + if b.volumeProject == "" { + b.volumeProject = creds.ProjectID + } // get snapshot project from 'project' config key if specified, // otherwise from the credentials file diff --git a/velero-plugin-for-gcp/volume_snapshotter_test.go b/velero-plugin-for-gcp/volume_snapshotter_test.go index 4094652f..20fd492d 100644 --- a/velero-plugin-for-gcp/volume_snapshotter_test.go +++ b/velero-plugin-for-gcp/volume_snapshotter_test.go @@ -18,6 +18,7 @@ package main import ( "encoding/json" + "os" "strings" "testing" @@ -397,3 +398,69 @@ func TestRegionHelpers(t *testing.T) { }) } } + +func TestInit(t *testing.T) { + credential_file_name := "./credential_file" + default_credential_file_name := "./default_credential" + os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", default_credential_file_name) + credential_content := `{"type": "service_account","project_id": "project-a","private_key_id":"id","private_key":"key","client_email":"a@b.com","client_id":"id","auth_uri":"uri","token_uri":"uri","auth_provider_x509_cert_url":"url","client_x509_cert_url":"url"}` + f, err := os.Create(credential_file_name) + require.NoError(t, err) + _, err = f.Write([]byte(credential_content)) + require.NoError(t, err) + + f, err = os.Create(default_credential_file_name) + require.NoError(t, err) + _, err = f.Write([]byte(credential_content)) + require.NoError(t, err) + + tests := []struct { + name string + config map[string]string + expectedVolumeSnapshotter VolumeSnapshotter + }{ + { + name: "Init with Credential files.", + config: map[string]string{ + "project": "project-a", + "credentialsFile": credential_file_name, + "snapshotLocation": "default", + "volumeProject": "project-b", + }, + expectedVolumeSnapshotter: VolumeSnapshotter{ + snapshotLocation: "default", + volumeProject: "project-b", + snapshotProject: "project-a", + }, + }, + { + name: "Init without Credential files.", + config: map[string]string{ + "project": "project-a", + "snapshotLocation": "default", + "volumeProject": "project-b", + }, + expectedVolumeSnapshotter: VolumeSnapshotter{ + snapshotLocation: "default", + volumeProject: "project-b", + snapshotProject: "project-a", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + volumeSnapshotter := newVolumeSnapshotter(logrus.StandardLogger()) + err := volumeSnapshotter.Init(test.config) + require.NoError(t, err) + require.Equal(t, test.expectedVolumeSnapshotter.snapshotLocation, volumeSnapshotter.snapshotLocation) + require.Equal(t, test.expectedVolumeSnapshotter.volumeProject, volumeSnapshotter.volumeProject) + require.Equal(t, test.expectedVolumeSnapshotter.snapshotProject, volumeSnapshotter.snapshotProject) + }) + } + + err = os.Remove(credential_file_name) + require.NoError(t, err) + err = os.Remove(default_credential_file_name) + require.NoError(t, err) +}