Skip to content

Commit

Permalink
Restore multiple PVCs
Browse files Browse the repository at this point in the history
  • Loading branch information
nikoshet committed Oct 16, 2024
1 parent 64db6e8 commit 6714819
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 117 deletions.
25 changes: 12 additions & 13 deletions src/backup/backup_operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ impl BackupOperatorImpl {
volume_snapshot_class: String,
pvc_name: String,
include_all: bool,
volume_snapshot_name_prefix: String,
vs_name_prefix: String,
) -> Self {
Self {
region,
source_ns,
volume_snapshot_class,
pvc_name,
include_all,
volume_snapshot_name_prefix,
vs_name_prefix,
}
}
}
Expand All @@ -74,27 +74,26 @@ 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()),
};

// 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()]
};

// 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(),
Expand All @@ -108,16 +107,16 @@ 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
{
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,
)
Expand All @@ -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<VolumeSnapshot>,
source_pvcs_api: Api<PersistentVolumeClaim>,
vsc_api: Api<VolumeSnapshotContent>,
Expand All @@ -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,
}
32 changes: 24 additions & 8 deletions src/k8s_ops/pvc/persistent_volume_claims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,35 @@ pub async fn get_pvcs_available(
pub async fn check_if_pvc_exists(
target_pvc_api: &kube::Api<PersistentVolumeClaim>,
pvc_name: &str,
) -> Result<PersistentVolumeClaim> {
should_exist: bool,
) -> Result<Option<PersistentVolumeClaim>> {
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),
}
}
213 changes: 117 additions & 96 deletions src/restore/restore_operator.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
}
Expand All @@ -79,114 +80,134 @@ 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(())
}
}

/// A struct for holding the Kubernetes APIs for the restore operation
struct TargetKubernetesApisStruct {
struct RestoreKubernetesApisStruct {
source_vs_api: Api<VolumeSnapshot>,
target_pvcs_api: Api<PersistentVolumeClaim>,
source_pvcs_api: Api<PersistentVolumeClaim>,
target_vs_api: Api<VolumeSnapshot>,
target_pvcs_api: Api<PersistentVolumeClaim>,
vsc_api: Api<VolumeSnapshotContent>,
}

Expand All @@ -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,
}

0 comments on commit 6714819

Please sign in to comment.