From 671481941410fb23177f04f1f0ee136a357272e4 Mon Sep 17 00:00:00 2001 From: nikoshet Date: Wed, 16 Oct 2024 22:45:57 +0300 Subject: [PATCH] Restore multiple PVCs --- src/backup/backup_operator.rs | 25 ++- src/k8s_ops/pvc/persistent_volume_claims.rs | 32 ++- src/restore/restore_operator.rs | 213 +++++++++++--------- 3 files changed, 153 insertions(+), 117 deletions(-) diff --git a/src/backup/backup_operator.rs b/src/backup/backup_operator.rs index 00c3d71..9d67bbf 100644 --- a/src/backup/backup_operator.rs +++ b/src/backup/backup_operator.rs @@ -49,7 +49,7 @@ impl BackupOperatorImpl { volume_snapshot_class: String, pvc_name: String, include_all: bool, - volume_snapshot_name_prefix: String, + vs_name_prefix: String, ) -> Self { Self { region, @@ -57,7 +57,7 @@ impl BackupOperatorImpl { volume_snapshot_class, pvc_name, include_all, - volume_snapshot_name_prefix, + vs_name_prefix, } } } @@ -74,7 +74,7 @@ impl BackupOperator for BackupOperatorImpl { .unwrap(); // Define the VolumeSnapshot and VolumeSnapshotContent APIs - let source_kubernetes_apis_struct = SourceKubernetesApisStruct { + let restore_k8s_apis_struct = BackupKubernetesApisStruct { source_vs_api: Api::namespaced(k8s_client.clone(), &self.source_ns), source_pvcs_api: Api::namespaced(k8s_client.clone(), &self.source_ns), vsc_api: Api::all(k8s_client.clone()), @@ -82,7 +82,7 @@ impl BackupOperator for BackupOperatorImpl { // Check if we will backup all PVCs in the namespace let pvcs = if self.include_all { - get_pvcs_available(&source_kubernetes_apis_struct.source_pvcs_api).await? + get_pvcs_available(&restore_k8s_apis_struct.source_pvcs_api).await? } else { vec![self.pvc_name.clone()] }; @@ -90,11 +90,10 @@ impl BackupOperator for BackupOperatorImpl { // We will iterate over the PVCs vector and backup each PVC for pvc in pvcs { info!("Backing up PVC: {}", pvc); - let volume_snapshot_name = - format!("{}-{}", self.volume_snapshot_name_prefix.clone(), pvc); + let volume_snapshot_name = format!("{}-{}", self.vs_name_prefix.clone(), pvc); - // Check if the PVC exists - check_if_pvc_exists(&source_kubernetes_apis_struct.source_pvcs_api, &pvc).await?; + // Check if the PVC exists, it should exist + check_if_pvc_exists(&restore_k8s_apis_struct.source_pvcs_api, &pvc, true).await?; let vs_operator = VolumeSnapshotOperator::new( volume_snapshot_name.to_string(), @@ -108,7 +107,7 @@ impl BackupOperator for BackupOperatorImpl { vs_operator.construct_volume_snapshot_resource(None, None, VSCRetainPolicy::Delete); let pp = PostParams::default(); - let status: VolumeSnapshotStatus = match source_kubernetes_apis_struct + let status: VolumeSnapshotStatus = match restore_k8s_apis_struct .source_vs_api .create(&pp, &volume_snapshot) .await @@ -116,8 +115,8 @@ impl BackupOperator for BackupOperatorImpl { Ok(snapshot) => { info!("Created VolumeSnapshot: {:?}", snapshot); wait_untill_snapshot_is_ready( - &source_kubernetes_apis_struct.source_vs_api, - &source_kubernetes_apis_struct.vsc_api, + &restore_k8s_apis_struct.source_vs_api, + &restore_k8s_apis_struct.vsc_api, &ebs_client, &volume_snapshot_name, ) @@ -143,7 +142,7 @@ impl BackupOperator for BackupOperatorImpl { } /// A struct for holding the Kubernetes APIs for the backup operation -struct SourceKubernetesApisStruct { +struct BackupKubernetesApisStruct { source_vs_api: Api, source_pvcs_api: Api, vsc_api: Api, @@ -156,5 +155,5 @@ pub struct BackupOperatorImpl { volume_snapshot_class: String, pvc_name: String, include_all: bool, - volume_snapshot_name_prefix: String, + vs_name_prefix: String, } diff --git a/src/k8s_ops/pvc/persistent_volume_claims.rs b/src/k8s_ops/pvc/persistent_volume_claims.rs index 593ef22..6f0c3c8 100644 --- a/src/k8s_ops/pvc/persistent_volume_claims.rs +++ b/src/k8s_ops/pvc/persistent_volume_claims.rs @@ -112,19 +112,35 @@ pub async fn get_pvcs_available( pub async fn check_if_pvc_exists( target_pvc_api: &kube::Api, pvc_name: &str, -) -> Result { + should_exist: bool, +) -> Result> { match target_pvc_api.get(pvc_name).await { Ok(pvc) => { - info!( - "{}", - format!( + if should_exist { + info!( + "{}", + format!( + "PVC exists: {} on target namespace {:?}", + pvc.metadata.name.clone().unwrap(), + pvc.metadata.namespace.clone().unwrap() + ) + ); + Ok(Some(pvc)) + } else { + panic!( "PVC exists: {} on target namespace {:?}", pvc.metadata.name.clone().unwrap(), pvc.metadata.namespace.clone().unwrap() - ) - ); - Ok(pvc) + ); + } + } + Err(e) => { + if should_exist { + panic!("Failed to get PVC: {}", e); + } else { + info!("PVC does not exist: {}", pvc_name); + Ok(None) + } } - Err(e) => panic!("Failed to get PVC: {}", e), } } diff --git a/src/restore/restore_operator.rs b/src/restore/restore_operator.rs index 02f4cd8..6b8c50f 100644 --- a/src/restore/restore_operator.rs +++ b/src/restore/restore_operator.rs @@ -1,6 +1,7 @@ use crate::k8s_ops::{ pvc::{ - persistent_volume_claims::PVCOperator, persistent_volume_claims_payload::PVCOperatorPayload, + persistent_volume_claims::{check_if_pvc_exists, get_pvcs_available, PVCOperator}, + persistent_volume_claims_payload::PVCOperatorPayload, }, vs::volume_snapshots::VolumeSnapshotOperator, vsc::{ @@ -53,8 +54,8 @@ impl RestoreOperatorImpl { volume_snapshot_class: String, pvc_name: String, include_all: bool, - volume_snapshot_name: String, - target_snapshot_content_name: String, + vs_name_prefix: String, + vsc_name_prefix: String, storage_class_name: String, vsc_retain_policy: VSCRetainPolicy, ) -> Self { @@ -64,8 +65,8 @@ impl RestoreOperatorImpl { volume_snapshot_class, pvc_name, include_all, - volume_snapshot_name, - target_snapshot_content_name, + vs_name_prefix, + vsc_name_prefix, storage_class_name, vsc_retain_policy, } @@ -79,103 +80,122 @@ impl RestoreOperator for RestoreOperatorImpl { let k8s_client = Client::try_default().await?; // Define the VolumeSnapshot, VolumeSnapshotContent and PersistentVolumeClaim APIs - let target_kubernetes_apis_struct = TargetKubernetesApisStruct { + let restore_k8s_apis_struct = RestoreKubernetesApisStruct { source_vs_api: Api::namespaced(k8s_client.clone(), &self.source_ns), - target_pvcs_api: Api::namespaced(k8s_client.clone(), &self.target_ns), + source_pvcs_api: Api::namespaced(k8s_client.clone(), &self.source_ns), target_vs_api: Api::namespaced(k8s_client.clone(), &self.target_ns), + target_pvcs_api: Api::namespaced(k8s_client.clone(), &self.target_ns), vsc_api: Api::all(k8s_client.clone()), }; - let status: VolumeSnapshotStatus = match target_kubernetes_apis_struct - .source_vs_api - .get(&self.volume_snapshot_name) - .await - { - Ok(snapshot) => snapshot.status.unwrap(), - Err(e) => { - bail!("Failed to get VolumeSnapshot status: {}", e) - } + // Check if we will restore all PVCs in the namespace + let pvcs = if self.include_all { + get_pvcs_available(&restore_k8s_apis_struct.source_pvcs_api).await? + } else { + vec![self.pvc_name.clone()] }; - let bound_vsc_name = status.bound_volume_snapshot_content_name.unwrap(); - let restore_size = status.restore_size.unwrap(); - - let snapshot_handle = get_snapshot_handle( - target_kubernetes_apis_struct.vsc_api.clone(), - &bound_vsc_name, - ) - .await?; - - let vsc_operator = VolumeSnapshotContentOperator::new( - self.target_snapshot_content_name.clone(), - self.target_ns.clone(), - self.volume_snapshot_name.clone(), - Some(self.volume_snapshot_class.clone()), - Some(snapshot_handle.clone()), - self.vsc_retain_policy, - ); - - let snapshot_content = vsc_operator.construct_volume_snapshot_content_resource(); - - let pp = PostParams::default(); - match target_kubernetes_apis_struct - .vsc_api - .create(&pp, &snapshot_content) - .await - { - Ok(snapshot_content) => info!("Created VolumeSnapshotContent: {:?}", snapshot_content), - Err(e) => panic!("Failed to create VolumeSnapshotContent: {}", e), - } + // We will iterate over the PVCs vector and restore each PVC + + for pvc in pvcs { + info!("Restoring PVC: {}", pvc); + let volume_snapshot_name = format!("{}-{}", self.vs_name_prefix, pvc); + let volume_snapshot_content_name = format!("{}-{}", self.vsc_name_prefix, pvc); + + // Check if the PVC exists in the target namespace, it should not exist + check_if_pvc_exists(&restore_k8s_apis_struct.target_pvcs_api, &pvc, false).await?; + + let status: VolumeSnapshotStatus = match restore_k8s_apis_struct + .source_vs_api + .get(&volume_snapshot_name) + .await + { + Ok(snapshot) => snapshot.status.unwrap(), + Err(e) => { + bail!("Failed to get VolumeSnapshot status: {}", e) + } + }; + + let bound_vsc_name = status.bound_volume_snapshot_content_name.unwrap(); + let restore_size = status.restore_size.unwrap(); + + let snapshot_handle = + get_snapshot_handle(restore_k8s_apis_struct.vsc_api.clone(), &bound_vsc_name) + .await?; + + let vsc_operator = VolumeSnapshotContentOperator::new( + volume_snapshot_content_name.clone(), + self.target_ns.clone(), + volume_snapshot_name.clone(), + Some(self.volume_snapshot_class.clone()), + Some(snapshot_handle.clone()), + self.vsc_retain_policy, + ); + + let snapshot_content = vsc_operator.construct_volume_snapshot_content_resource(); + + let pp = PostParams::default(); + match restore_k8s_apis_struct + .vsc_api + .create(&pp, &snapshot_content) + .await + { + Ok(snapshot_content) => { + info!("Created VolumeSnapshotContent: {:?}", snapshot_content) + } + Err(e) => panic!("Failed to create VolumeSnapshotContent: {}", e), + } - let vs_operator = VolumeSnapshotOperator::new( - self.volume_snapshot_name.clone(), - self.target_ns.clone(), - self.volume_snapshot_class.clone(), - None, - Some(self.target_snapshot_content_name.to_string()), - ); - - let target_volume_snapshot = vs_operator.construct_volume_snapshot_resource( - Some(snapshot_handle.to_string()), - Some(restore_size.to_string()), - self.vsc_retain_policy, - ); - - info!("Creating VolumeSnapshot in the target namespace..."); - let pp = PostParams::default(); - match target_kubernetes_apis_struct - .target_vs_api - .create(&pp, &target_volume_snapshot) - .await - { - Ok(target_volume_snapshot) => { - info!("Created VolumeSnapshot: {:?}", target_volume_snapshot) + let vs_operator = VolumeSnapshotOperator::new( + volume_snapshot_name.clone(), + self.target_ns.clone(), + self.volume_snapshot_class.clone(), + None, + Some(volume_snapshot_content_name), + ); + + let target_volume_snapshot = vs_operator.construct_volume_snapshot_resource( + Some(snapshot_handle.to_string()), + Some(restore_size.to_string()), + self.vsc_retain_policy, + ); + + info!("Creating VolumeSnapshot in the target namespace..."); + let pp = PostParams::default(); + match restore_k8s_apis_struct + .target_vs_api + .create(&pp, &target_volume_snapshot) + .await + { + Ok(target_volume_snapshot) => { + info!("Created VolumeSnapshot: {:?}", target_volume_snapshot) + } + Err(e) => panic!("Failed to create VolumeSnapshot: {}", e), } - Err(e) => panic!("Failed to create VolumeSnapshot: {}", e), - } - // Restore the PVC for each pvc available - let pvc_payload = PVCOperatorPayload::new( - self.pvc_name.clone(), - self.target_ns.clone(), - Some(self.storage_class_name.clone()), - None, - self.volume_snapshot_name.clone(), - restore_size, - ); - - let pvc_operator = PVCOperator::new(pvc_payload); - let pvc = pvc_operator.construct_persistent_volume_claim_resource(); - - info!("Restoring PVC..."); - let pp = PostParams::default(); - match target_kubernetes_apis_struct - .target_pvcs_api - .create(&pp, &pvc) - .await - { - Ok(pvc) => info!("Restored PVC: {:?}", pvc), - Err(e) => panic!("Failed to restore PVC: {}", e), + // Restore the PVC for each pvc available + let pvc_payload = PVCOperatorPayload::new( + pvc, + self.target_ns.clone(), + Some(self.storage_class_name.clone()), + None, + volume_snapshot_name, + restore_size, + ); + + let pvc_operator = PVCOperator::new(pvc_payload); + let pvc = pvc_operator.construct_persistent_volume_claim_resource(); + + info!("Restoring PVC..."); + let pp = PostParams::default(); + match restore_k8s_apis_struct + .target_pvcs_api + .create(&pp, &pvc) + .await + { + Ok(pvc) => info!("Restored PVC: {:?}", pvc), + Err(e) => panic!("Failed to restore PVC: {}", e), + } } Ok(()) @@ -183,10 +203,11 @@ impl RestoreOperator for RestoreOperatorImpl { } /// A struct for holding the Kubernetes APIs for the restore operation -struct TargetKubernetesApisStruct { +struct RestoreKubernetesApisStruct { source_vs_api: Api, - target_pvcs_api: Api, + source_pvcs_api: Api, target_vs_api: Api, + target_pvcs_api: Api, vsc_api: Api, } @@ -197,8 +218,8 @@ pub struct RestoreOperatorImpl { volume_snapshot_class: String, pvc_name: String, include_all: bool, - volume_snapshot_name: String, - target_snapshot_content_name: String, + vs_name_prefix: String, + vsc_name_prefix: String, storage_class_name: String, vsc_retain_policy: VSCRetainPolicy, }