diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8571479..101119a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,14 +24,26 @@ jobs: format-and-clippy: uses: ./.github/workflows/format-workflow.yaml secrets: inherit + tests: + uses: ./.github/workflows/tests-workflow.yaml + secrets: inherit build: runs-on: ubuntu-latest - needs: [format-and-clippy] + needs: [format-and-clippy, tests] + strategy: + fail-fast: true + matrix: + include: + - name: "library" + path: "." + - name: "client" + path: "pvc-snapshotter-client" steps: - uses: actions/checkout@v4 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: components: rustfmt, clippy toolchain: ${{ env.RUST_VERSION }} - - name: Build + - name: Cargo Build ${{ matrix.name }} run: cargo build --verbose + working-directory: ${{ matrix.path }} diff --git a/.github/workflows/tests-workflow.yaml b/.github/workflows/tests-workflow.yaml new file mode 100644 index 0000000..82adc44 --- /dev/null +++ b/.github/workflows/tests-workflow.yaml @@ -0,0 +1,24 @@ +name: Tests Pipeline + +on: + workflow_call: + +env: + RUST_VERSION: 1.81.0 + +jobs: + tests: + name: Run Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: rustfmt, clippy + toolchain: ${{ env.RUST_VERSION }} + - name: Install cargo-nextest + uses: baptiste0928/cargo-install@v3 + with: + crate: cargo-nextest + - name: Run tests + run: cargo nextest run --all diff --git a/Cargo.lock b/Cargo.lock index 8f3101e..adebcbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -769,6 +769,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "dyn-clone" version = "1.0.17" @@ -838,6 +844,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + [[package]] name = "futures" version = "0.3.30" @@ -1517,6 +1529,32 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "mockall" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1739,6 +1777,32 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "pretty_assertions" version = "1.4.1" @@ -1771,6 +1835,7 @@ dependencies = [ "k8s-openapi", "kube", "kube-custom-resources-rs", + "mockall", "pretty_assertions", "schemars", "serde", @@ -1793,6 +1858,7 @@ dependencies = [ "k8s-openapi", "kube", "kube-custom-resources-rs", + "mockall", "pretty_assertions", "pvc-snapshotter", "schemars", @@ -2297,6 +2363,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "thiserror" version = "1.0.64" diff --git a/Cargo.toml b/Cargo.toml index 4abfc56..055e3d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ tokio = { version = "1", features = ["full"] } tracing = "0.1.40" tracing-subscriber = "0.3.18" pvc-snapshotter = { path = ".", version = "0.1" } +mockall = "0.13" [dependencies] anyhow.workspace = true @@ -50,6 +51,7 @@ serde_json.workspace = true tokio.workspace = true tracing.workspace = true tracing-subscriber.workspace = true +mockall.workspace = true [lib] test = true diff --git a/README.md b/README.md index 05d1caa..3f28927 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,6 @@ The tool supports three primary modes of operation: Restore: Restore PVCs from existing snapshots. Full: Run both backup and restore operations in a single process. - -
-> [!NOTE] -> Average percentage of time saved by PVC-Snapshotter compared to [Velero](https://github.com/vmware-tanzu/velero): **X%**. -
- ## Features - **Backup**: Create Kubernetes VolumeSnapshots from existing PVCs - **Restore**: Restore PVCs to any namespace from a VolumeSnapshot diff --git a/pvc-snapshotter-client/Cargo.toml b/pvc-snapshotter-client/Cargo.toml index a11dedf..14ac038 100644 --- a/pvc-snapshotter-client/Cargo.toml +++ b/pvc-snapshotter-client/Cargo.toml @@ -28,6 +28,7 @@ tokio.workspace = true tracing.workspace = true tracing-subscriber.workspace = true pvc-snapshotter.workspace = true +mockall = "0.13.0" [features] default = ["full"] diff --git a/src/backup/backup_operator.rs b/src/backup/backup_operator.rs index 11e805b..c936d2f 100644 --- a/src/backup/backup_operator.rs +++ b/src/backup/backup_operator.rs @@ -2,14 +2,15 @@ use super::backup_payload::BackupPayload; use crate::{ aws_ops::ebs::create_ebs_client, k8s_ops::{ - pvc::persistent_volume_claims::{check_if_pvc_exists, get_pvcs_available}, - vs::volume_snapshots::wait_untill_snapshot_is_ready, - vs::volume_snapshots_operator::VolumeSnapshotOperator, + pvc::persistent_volume_claims::{check_if_pvc_exists, get_pvcs_available, KubePvcApi}, + vs::{ + volume_snapshots::wait_untill_snapshot_is_ready, + volume_snapshots_operator::VolumeSnapshotOperator, + }, vsc::retain_policy::VSCRetainPolicy, }, }; use anyhow::{bail, Result}; -use k8s_openapi::api::core::v1::PersistentVolumeClaim; use kube::{api::PostParams, Api, Client}; use kube_custom_resources_rs::snapshot_storage_k8s_io::v1::{ volumesnapshotcontents::VolumeSnapshotContent, @@ -34,7 +35,9 @@ impl BackupOperator { // Define the VolumeSnapshot and VolumeSnapshotContent APIs let restore_k8s_apis_struct = BackupKubernetesApisStruct { source_vs_api: Api::namespaced(k8s_client.clone(), backup_payload.source_ns()), - source_pvcs_api: Api::namespaced(k8s_client.clone(), backup_payload.source_ns()), + source_pvcs_api: KubePvcApi { + api: Api::namespaced(k8s_client.clone(), backup_payload.source_ns()), + }, vsc_api: Api::all(k8s_client.clone()), }; @@ -102,6 +105,6 @@ impl BackupOperator { /// A struct for holding the Kubernetes APIs for the backup operation struct BackupKubernetesApisStruct { source_vs_api: Api, - source_pvcs_api: Api, + source_pvcs_api: KubePvcApi, vsc_api: Api, } diff --git a/src/k8s_ops/pvc/persistent_volume_claims.rs b/src/k8s_ops/pvc/persistent_volume_claims.rs index 2b1fb9c..f2435b7 100644 --- a/src/k8s_ops/pvc/persistent_volume_claims.rs +++ b/src/k8s_ops/pvc/persistent_volume_claims.rs @@ -1,14 +1,51 @@ use anyhow::Result; +use async_trait::async_trait; use k8s_openapi::api::core::v1::PersistentVolumeClaim; -use kube::api::ListParams; +use kube::{api::ListParams, Api}; use tracing::info; +#[cfg(test)] +use mockall::automock; + +#[cfg_attr(test, automock)] +#[async_trait] +pub trait PvcApiTrait { + async fn list_pvcs(&self) -> Result>; + async fn get(&self, name: &str) -> Result; + async fn create(&self, pvc: PersistentVolumeClaim) -> Result; +} + +pub struct KubePvcApi { + pub api: Api, +} + +/// Implement the PvcApi trait for PVC Api +/// This will allow us to call the functions defined in the PvcApi trait on an instance of KubePvcApi. +/// This is useful for testing, as we can mock the KubePvcApi struct and implement the PvcApi trait +/// to return mock data. +/// This way, we can test the functions that use the KubePvcApi struct without actually making +/// calls to the Kubernetes API. +#[async_trait] +impl PvcApiTrait for KubePvcApi { + async fn list_pvcs(&self) -> Result> { + let pvcs = self.api.list(&ListParams::default()).await?; + Ok(pvcs.items) + } + + async fn get(&self, name: &str) -> Result { + let pvc = self.api.get(name).await?; + Ok(pvc) + } + + async fn create(&self, pvc: PersistentVolumeClaim) -> Result { + let pvc = self.api.create(&Default::default(), &pvc).await?; + Ok(pvc) + } +} + /// Get the list of PersistentVolumeClaims available -pub async fn get_pvcs_available( - target_pvc_api: &kube::Api, -) -> Result> { - let lp = ListParams::default(); - let pvc_list: Vec<_> = match target_pvc_api.list(&lp).await { +pub async fn get_pvcs_available(pvc_api: &impl PvcApiTrait) -> Result> { + let pvc_list: Vec<_> = match pvc_api.list_pvcs().await { Ok(pvc) => pvc, Err(e) => panic!("Failed to list PVCs: {}", e), } @@ -20,7 +57,8 @@ pub async fn get_pvcs_available( } pub async fn check_if_pvc_exists( - target_pvc_api: &kube::Api, + target_pvc_api: &impl PvcApiTrait, + //&kube::Api, pvc_name: &str, should_exist: bool, ) -> Result> { @@ -38,7 +76,7 @@ pub async fn check_if_pvc_exists( Ok(Some(pvc)) } else { panic!( - "PVC exists: {} on target namespace {:?}", + "PVC does not exist: {} on target namespace {:?}", pvc.metadata.name.clone().unwrap(), pvc.metadata.namespace.clone().unwrap() ); diff --git a/src/k8s_ops/pvc/persistent_volume_claims_operator.rs b/src/k8s_ops/pvc/persistent_volume_claims_operator.rs index affb297..7ca81c1 100644 --- a/src/k8s_ops/pvc/persistent_volume_claims_operator.rs +++ b/src/k8s_ops/pvc/persistent_volume_claims_operator.rs @@ -9,6 +9,26 @@ use k8s_openapi::{ use kube::api::ObjectMeta; use std::collections::BTreeMap; +enum PVCResourceValues { + AccessModes, + K8sKind, + ApiGroup, + VolumeMode, + StorageClass, +} + +impl PVCResourceValues { + pub fn get_value(&self) -> String { + match self { + PVCResourceValues::AccessModes => "ReadWriteOnce".to_string(), + PVCResourceValues::K8sKind => "VolumeSnapshot".to_string(), + PVCResourceValues::ApiGroup => "snapshot.storage.k8s.io".to_string(), + PVCResourceValues::VolumeMode => "Filesystem".to_string(), + PVCResourceValues::StorageClass => "gp3".to_string(), + } + } +} + pub struct PVCOperator { pvc_operator_payload: PVCOperatorPayload, } @@ -36,13 +56,11 @@ impl PVCOperator { /// PersistentVolumeClaim resource pub fn construct_persistent_volume_claim_resource(&self) -> PersistentVolumeClaim { // Create a base labels map - let mut labels = BTreeMap::new(); - // Always add the VSc name - labels.insert( + let labels = BTreeMap::from([( "pvc-snapshotter/volume-snapshot-name".to_string(), self.pvc_operator_payload.pvc_name().to_string(), - ); + )]); PersistentVolumeClaim { metadata: ObjectMeta { @@ -55,26 +73,26 @@ impl PVCOperator { access_modes: Some( self.pvc_operator_payload .access_modes() - .unwrap_or(vec!["ReadWriteOnce".to_string()]), + .unwrap_or(vec![PVCResourceValues::AccessModes.get_value()]), ), storage_class_name: Some( self.pvc_operator_payload .storage_class() - .unwrap_or("gp3") + .unwrap_or(&PVCResourceValues::StorageClass.get_value()) .to_string(), ), data_source: Some(TypedLocalObjectReference { name: String::from(self.pvc_operator_payload.volume_snapshot_name()), - kind: "VolumeSnapshot".to_string(), - api_group: Some("snapshot.storage.k8s.io".to_string()), + kind: PVCResourceValues::K8sKind.get_value(), + api_group: Some(PVCResourceValues::ApiGroup.get_value()), }), data_source_ref: Some(TypedObjectReference { name: String::from(self.pvc_operator_payload.volume_snapshot_name()), - kind: "VolumeSnapshot".to_string(), - api_group: Some("snapshot.storage.k8s.io".to_string()), + kind: PVCResourceValues::K8sKind.get_value(), + api_group: Some(PVCResourceValues::ApiGroup.get_value()), namespace: Some(String::from(self.pvc_operator_payload.namespace())), }), - volume_mode: Some("Filesystem".to_string()), + volume_mode: Some(PVCResourceValues::VolumeMode.get_value()), volume_name: Default::default(), resources: Some(VolumeResourceRequirements { requests: Some(BTreeMap::from([( diff --git a/src/k8s_ops/pvc/persistent_volume_claims_tests.rs b/src/k8s_ops/pvc/persistent_volume_claims_tests.rs index 4b8088a..44a8d80 100644 --- a/src/k8s_ops/pvc/persistent_volume_claims_tests.rs +++ b/src/k8s_ops/pvc/persistent_volume_claims_tests.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use crate::k8s_ops::pvc::{ - persistent_volume_claims::get_pvcs_available, + persistent_volume_claims::{MockPvcApiTrait, PvcApiTrait}, persistent_volume_claims_operator::PVCOperator, persistent_volume_claims_payload::PVCOperatorPayload, }; @@ -12,18 +12,19 @@ mod tests { }, apimachinery::pkg::api::resource::Quantity, }; - use kube::{api::ObjectMeta, Api, Client}; + use kube::api::ObjectMeta; + use mockall::predicate; use pretty_assertions::assert_eq; use std::collections::BTreeMap; #[test] fn test_construct_persistent_volume_claim_resource() { let pvc_operator_payload = PVCOperatorPayload::new( - "test-pvc".to_string(), - "test-ns".to_string(), - "gp3".to_string(), - Some(vec!["ReadWriteOnce".to_string()]), - "test-vs".to_string(), + String::from("test-pvc"), + String::from("test-ns"), + String::from("gp3"), + Some(vec![String::from("ReadWriteOnce")]), + String::from("test-vs"), "1Gi".to_string(), ); @@ -31,11 +32,10 @@ mod tests { let pvc = pvc_operator.construct_persistent_volume_claim_resource(); - let mut labels = BTreeMap::new(); - labels.insert( + let labels = BTreeMap::from([( "pvc-snapshotter/volume-snapshot-name".to_string(), "test-pvc".to_string(), - ); + )]); let expected_pvc = PersistentVolumeClaim { metadata: ObjectMeta { @@ -77,18 +77,111 @@ mod tests { } #[tokio::test] - async fn test_get_pvcs_available() { - let client = Client::try_default().await.unwrap(); - let api: Api = Api::namespaced(client, "test-ns"); - let pvcs = get_pvcs_available(&api).await.unwrap(); - assert!(pvcs.is_empty()); + async fn test_create_pvc() { + let mut mock_pvc_api = MockPvcApiTrait::new(); + + let pvc = PersistentVolumeClaim { + metadata: ObjectMeta { + name: Some("test-pvc".to_string()), + namespace: Some("test-ns".to_string()), + ..Default::default() + }, + ..Default::default() + }; + + mock_pvc_api + .expect_create() + .with(predicate::eq(pvc.clone())) + .times(1) + .returning(|_| { + Ok(PersistentVolumeClaim { + metadata: ObjectMeta { + name: Some("test-pvc".to_string()), + namespace: Some("test-ns".to_string()), + ..Default::default() + }, + ..Default::default() + }) + }); + + let result = mock_pvc_api.create(pvc).await; + assert!(result.is_ok()); + assert_eq!( + result.as_ref().cloned().unwrap().metadata.name.unwrap(), + "test-pvc" + ); + assert_eq!(result.unwrap().metadata.namespace.unwrap(), "test-ns"); } #[tokio::test] - async fn test_check_if_pvc_exists() { - let client = Client::try_default().await.unwrap(); - let api: Api = Api::namespaced(client, "test-ns"); - let pvc = api.get("test-pvc").await; - assert!(pvc.is_err()); + async fn test_get_pvc() { + let mut mock_pvc_api = MockPvcApiTrait::new(); + + mock_pvc_api + .expect_get() + .with(predicate::eq("test-pvc")) + .times(1) + .returning(|_| { + Ok(PersistentVolumeClaim { + metadata: ObjectMeta { + name: Some("test-pvc".to_string()), + namespace: Some("test-ns".to_string()), + ..Default::default() + }, + ..Default::default() + }) + }); + + let result = mock_pvc_api.get("test-pvc").await; + assert!(result.is_ok()); + assert_eq!( + result.as_ref().cloned().unwrap().metadata.name.unwrap(), + "test-pvc" + ); + assert_eq!(result.unwrap().metadata.namespace.unwrap(), "test-ns"); + } + + #[tokio::test] + async fn test_list_pvcs() { + let mut mock_pvc_api = MockPvcApiTrait::new(); + + mock_pvc_api.expect_list_pvcs().times(1).returning(|| { + Ok(vec![PersistentVolumeClaim { + metadata: ObjectMeta { + name: Some("test-pvc".to_string()), + namespace: Some("test-ns".to_string()), + ..Default::default() + }, + ..Default::default() + }]) + }); + + let result = mock_pvc_api.list_pvcs().await; + + assert!(result.is_ok()); + assert_eq!(result.as_ref().unwrap().len(), 1); + assert_eq!( + result + .as_ref() + .unwrap() + .get(0) + .unwrap() + .metadata + .name + .clone() + .unwrap(), + "test-pvc" + ); + assert_eq!( + result + .unwrap() + .get(0) + .unwrap() + .metadata + .namespace + .clone() + .unwrap(), + "test-ns" + ); } } diff --git a/src/k8s_ops/vs/volume_snapshots_operator.rs b/src/k8s_ops/vs/volume_snapshots_operator.rs index a8ad22b..2635c96 100644 --- a/src/k8s_ops/vs/volume_snapshots_operator.rs +++ b/src/k8s_ops/vs/volume_snapshots_operator.rs @@ -6,6 +6,20 @@ use kube_custom_resources_rs::snapshot_storage_k8s_io::v1::volumesnapshots::{ VolumeSnapshot, VolumeSnapshotSource, VolumeSnapshotSpec, }; +enum VSResourceValues { + Finalizers, +} + +impl VSResourceValues { + pub fn get_value(&self) -> String { + match self { + VSResourceValues::Finalizers => { + "snapshot.storage.kubernetes.io/volumesnapshot-bound-protection".to_string() + } + } + } +} + pub struct VolumeSnapshotOperator { pub name: String, pub namespace: String, @@ -52,50 +66,43 @@ impl VolumeSnapshotOperator { restore_size: Option, vsc_retain_policy: VSCRetainPolicy, ) -> VolumeSnapshot { - // Create a base annotations map - let mut annotations = BTreeMap::new(); - // Always add the CSI driver annotation - annotations.insert( - "pvc-snapshotter/csi-driver-name".to_string(), - "ebs.csi.aws.com".to_string(), - ); - // Always add the VSC deletion policy annotation - annotations.insert( - "pvc-snapshotter/csi-vsc-deletion-policy".to_string(), - vsc_retain_policy.to_string(), - ); + // Create a base annotations map with always-included entries + let mut annotations = BTreeMap::from([ + ( + "pvc-snapshotter/csi-driver-name".into(), + "ebs.csi.aws.com".into(), + ), + ( + "pvc-snapshotter/csi-vsc-deletion-policy".into(), + vsc_retain_policy.to_string(), + ), + ]); // If a snapshot handle is provided, insert the corresponding annotation if let Some(handle) = snapshot_handle { - annotations.insert( - "pvc-snapshotter/csi-volumesnapshot-handle".to_string(), - handle, - ); + annotations.insert("pvc-snapshotter/csi-volumesnapshot-handle".into(), handle); } // If a restore size is provided, insert the corresponding annotation if let Some(size) = restore_size { annotations.insert( - "pvc-snapshotter/csi-volumesnapshot-restore-size".to_string(), + "pvc-snapshotter/csi-volumesnapshot-restore-size".into(), size, ); } // Create a base labels map - let mut labels = BTreeMap::new(); // Always add the namespace name - labels.insert( + let labels = BTreeMap::from([( "app.kubernetes.io/instance".to_string(), self.namespace.clone(), - ); + )]); VolumeSnapshot { metadata: ObjectMeta { name: Some(self.name.clone()), namespace: Some(self.namespace.clone()), annotations: Some(annotations), - finalizers: Some(vec![ - "snapshot.storage.kubernetes.io/volumesnapshot-bound-protection".to_string(), - ]), + finalizers: Some(vec![VSResourceValues::Finalizers.get_value()]), labels: Some(labels), ..Default::default() }, diff --git a/src/k8s_ops/vsc/volume_snapshot_contents_operator.rs b/src/k8s_ops/vsc/volume_snapshot_contents_operator.rs index b2859d3..7e0200c 100644 --- a/src/k8s_ops/vsc/volume_snapshot_contents_operator.rs +++ b/src/k8s_ops/vsc/volume_snapshot_contents_operator.rs @@ -1,10 +1,27 @@ +use super::retain_policy::VSCRetainPolicy; use kube::api::ObjectMeta; use kube_custom_resources_rs::snapshot_storage_k8s_io::v1::volumesnapshotcontents::{ VolumeSnapshotContent, VolumeSnapshotContentSource, VolumeSnapshotContentSpec, VolumeSnapshotContentStatus, VolumeSnapshotContentVolumeSnapshotRef, }; -use super::retain_policy::VSCRetainPolicy; +enum VSCResourceValues { + ApiVersion, + Kind, + Driver, + SourceVolumeMode, +} + +impl VSCResourceValues { + pub fn get_value(&self) -> String { + match self { + VSCResourceValues::ApiVersion => "snapshot.storage.k8s.io/v1".to_string(), + VSCResourceValues::Kind => "VolumeSnapshot".to_string(), + VSCResourceValues::Driver => "ebs.csi.aws.com".to_string(), + VSCResourceValues::SourceVolumeMode => "Filesystem".to_string(), + } + } +} pub struct VolumeSnapshotContentOperator { pub name: String, @@ -56,8 +73,8 @@ impl VolumeSnapshotContentOperator { }, spec: VolumeSnapshotContentSpec { volume_snapshot_ref: VolumeSnapshotContentVolumeSnapshotRef { - api_version: Some("snapshot.storage.k8s.io/v1".to_string()), - kind: Some("VolumeSnapshot".to_string()), + api_version: Some(VSCResourceValues::ApiVersion.get_value()), + kind: Some(VSCResourceValues::Kind.get_value()), name: Some(self.volume_snapshot_name.clone()), namespace: Some(self.namespace.clone()), field_path: Default::default(), @@ -65,13 +82,13 @@ impl VolumeSnapshotContentOperator { uid: Default::default(), }, deletion_policy: self.vsc_retain_policy.into(), - driver: "ebs.csi.aws.com".to_string(), + driver: VSCResourceValues::Driver.get_value(), source: VolumeSnapshotContentSource { snapshot_handle: self.source_volume_handle.clone(), ..Default::default() }, volume_snapshot_class_name: self.volume_snapshot_class.clone(), - source_volume_mode: Some("Filesystem".to_string()), + source_volume_mode: Some(VSCResourceValues::SourceVolumeMode.get_value()), }, status: Some(VolumeSnapshotContentStatus { snapshot_handle: self.source_volume_handle.clone(), diff --git a/src/restore/restore_operator.rs b/src/restore/restore_operator.rs index ad17086..ab1d2cd 100644 --- a/src/restore/restore_operator.rs +++ b/src/restore/restore_operator.rs @@ -1,15 +1,16 @@ use crate::k8s_ops::{ pvc::{ - persistent_volume_claims::{check_if_pvc_exists, get_pvcs_available}, + persistent_volume_claims::{check_if_pvc_exists, get_pvcs_available, KubePvcApi}, persistent_volume_claims_operator::PVCOperator, persistent_volume_claims_payload::PVCOperatorPayload, }, vs::volume_snapshots_operator::VolumeSnapshotOperator, - vsc::volume_snapshot_contents::get_snapshot_handle, - vsc::volume_snapshot_contents_operator::VolumeSnapshotContentOperator, + vsc::{ + volume_snapshot_contents::get_snapshot_handle, + volume_snapshot_contents_operator::VolumeSnapshotContentOperator, + }, }; use anyhow::{bail, Result}; -use k8s_openapi::api::core::v1::PersistentVolumeClaim; use kube::{api::PostParams, Api, Client}; use kube_custom_resources_rs::snapshot_storage_k8s_io::v1::{ volumesnapshotcontents::VolumeSnapshotContent, @@ -31,9 +32,13 @@ impl RestoreOperator { // Define the VolumeSnapshot, VolumeSnapshotContent and PersistentVolumeClaim APIs let restore_k8s_apis_struct = RestoreKubernetesApisStruct { source_vs_api: Api::namespaced(k8s_client.clone(), restore_payload.source_ns()), - source_pvcs_api: Api::namespaced(k8s_client.clone(), restore_payload.source_ns()), + source_pvcs_api: KubePvcApi { + api: Api::namespaced(k8s_client.clone(), restore_payload.source_ns()), + }, target_vs_api: Api::namespaced(k8s_client.clone(), restore_payload.target_ns()), - target_pvcs_api: Api::namespaced(k8s_client.clone(), restore_payload.target_ns()), + target_pvcs_api: KubePvcApi { + api: Api::namespaced(k8s_client.clone(), restore_payload.target_ns()), + }, vsc_api: Api::all(k8s_client.clone()), }; @@ -140,6 +145,7 @@ impl RestoreOperator { let pp = PostParams::default(); match restore_k8s_apis_struct .target_pvcs_api + .api .create(&pp, &pvc) .await { @@ -155,8 +161,8 @@ impl RestoreOperator { /// A struct for holding the Kubernetes APIs for the restore operation struct RestoreKubernetesApisStruct { source_vs_api: Api, - source_pvcs_api: Api, + source_pvcs_api: KubePvcApi, target_vs_api: Api, - target_pvcs_api: Api, + target_pvcs_api: KubePvcApi, vsc_api: Api, }