-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #156 from blackpiglet/cherry_pick_cross_project_to…
…_1.11 Cherry pick cross project to 1.11
- Loading branch information
Showing
5 changed files
with
238 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# 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 backup 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] | ||
* Assign [sa-b] "Storage Object Admin" permissions to [bucket-a](need to run after sa-b created in project-b) | ||
* Assign [sa-c] "Storage Object Admin" permissions to [bucket-a](need to run after sa-c created in project-c) | ||
|
||
|
||
* 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** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,7 @@ package main | |
|
||
import ( | ||
"encoding/json" | ||
"strings" | ||
"os" | ||
"testing" | ||
|
||
"github.com/pkg/errors" | ||
|
@@ -155,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", | ||
|
@@ -172,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", | ||
|
@@ -182,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{}{}, | ||
} | ||
|
@@ -206,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) | ||
} | ||
} | ||
}) | ||
} | ||
|
@@ -354,3 +372,111 @@ 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":"[email protected]","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) | ||
} | ||
|
||
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)) | ||
}) | ||
} | ||
} |