diff --git a/kube-client/src/api/subresource.rs b/kube-client/src/api/subresource.rs index d05a037ca..fec7e6256 100644 --- a/kube-client/src/api/subresource.rs +++ b/kube-client/src/api/subresource.rs @@ -60,6 +60,54 @@ where } } +/// Arbitrary subresources +impl Api +where + K: Clone + DeserializeOwned + Debug, +{ + /// Display one or many sub-resources. + pub async fn get_subresource(&self, subresource_name: &str, name: &str) -> Result { + let mut req = self + .request + .get_subresource(subresource_name, name) + .map_err(Error::BuildRequest)?; + req.extensions_mut().insert("get_subresource"); + self.client.request::(req).await + } + + /// Patch an instance of the subresource + pub async fn patch_subresource( + &self, + subresource_name: &str, + name: &str, + pp: &PatchParams, + patch: &Patch

, + ) -> Result { + let mut req = self + .request + .patch_subresource(subresource_name, name, pp, patch) + .map_err(Error::BuildRequest)?; + req.extensions_mut().insert("patch_subresource"); + self.client.request::(req).await + } + + /// Replace an instance of the subresource + pub async fn replace_subresource( + &self, + subresource_name: &str, + name: &str, + pp: &PostParams, + data: Vec, + ) -> Result { + let mut req = self + .request + .replace_subresource(subresource_name, name, pp, data) + .map_err(Error::BuildRequest)?; + req.extensions_mut().insert("replace_subresource"); + self.client.request::(req).await + } +} + // ---------------------------------------------------------------------------- // TODO: Replace examples with owned custom resources. Bad practice to write to owned objects diff --git a/kube-client/src/api/util.rs b/kube-client/src/api/util.rs index f6e5b319f..0b173c8f7 100644 --- a/kube-client/src/api/util.rs +++ b/kube-client/src/api/util.rs @@ -2,8 +2,11 @@ use crate::{ api::{Api, Resource}, Error, Result, }; -use k8s_openapi::api::core::v1::Node; -use kube_core::util::Restart; +use k8s_openapi::api::{certificates::v1::CertificateSigningRequest, core::v1::Node}; +use kube_core::{ + params::{Patch, PatchParams}, + util::Restart, +}; use serde::de::DeserializeOwned; impl Api @@ -34,6 +37,28 @@ impl Api { } } +impl Api { + /// Partially update approval of the specified CertificateSigningRequest. + pub async fn patch_approval( + &self, + name: &str, + pp: &PatchParams, + patch: &Patch

, + ) -> Result { + let mut req = self + .request + .patch_subresource("approval", name, pp, patch) + .map_err(Error::BuildRequest)?; + req.extensions_mut().insert("approval"); + self.client.request::(req).await + } + + /// Get the CertificateSigningRequest. May differ from get(name) + pub async fn get_approval(&self, name: &str) -> Result { + self.get_subresource("approval", name).await + } +} + // Tests that require a cluster and the complete feature set // Can be run with `cargo test -p kube-client --lib -- --ignored` #[cfg(test)] diff --git a/kube-client/src/lib.rs b/kube-client/src/lib.rs index 882538b7d..213e5752c 100644 --- a/kube-client/src/lib.rs +++ b/kube-client/src/lib.rs @@ -137,6 +137,7 @@ mod test { }; use futures::{StreamExt, TryStreamExt}; use k8s_openapi::api::core::v1::Pod; + use kube_core::params::{DeleteParams, Patch}; use serde_json::json; use tower::ServiceBuilder; @@ -462,4 +463,63 @@ mod test { Ok(()) } + + #[tokio::test] + #[ignore] // needs cluster (will create a CertificateSigningRequest) + async fn csr_can_be_approved() -> Result<(), Box> { + use crate::api::PostParams; + use k8s_openapi::api::certificates::v1::{ + CertificateSigningRequest, CertificateSigningRequestCondition, CertificateSigningRequestStatus, + }; + + let csr_name = "fake"; + let dummy_csr: CertificateSigningRequest = serde_json::from_value(json!({ + "apiVersion": "certificates.k8s.io/v1", + "kind": "CertificateSigningRequest", + "metadata": { "name": csr_name }, + "spec": { + "request": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1ZqQ0NBVDRDQVFBd0VURVBNQTBHQTFVRUF3d0dZVzVuWld4aE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRgpBQU9DQVE4QU1JSUJDZ0tDQVFFQTByczhJTHRHdTYxakx2dHhWTTJSVlRWMDNHWlJTWWw0dWluVWo4RElaWjBOCnR2MUZtRVFSd3VoaUZsOFEzcWl0Qm0wMUFSMkNJVXBGd2ZzSjZ4MXF3ckJzVkhZbGlBNVhwRVpZM3ExcGswSDQKM3Z3aGJlK1o2MVNrVHF5SVBYUUwrTWM5T1Nsbm0xb0R2N0NtSkZNMUlMRVI3QTVGZnZKOEdFRjJ6dHBoaUlFMwpub1dtdHNZb3JuT2wzc2lHQ2ZGZzR4Zmd4eW8ybmlneFNVekl1bXNnVm9PM2ttT0x1RVF6cXpkakJ3TFJXbWlECklmMXBMWnoyalVnald4UkhCM1gyWnVVV1d1T09PZnpXM01LaE8ybHEvZi9DdS8wYk83c0x0MCt3U2ZMSU91TFcKcW90blZtRmxMMytqTy82WDNDKzBERHk5aUtwbXJjVDBnWGZLemE1dHJRSURBUUFCb0FBd0RRWUpLb1pJaHZjTgpBUUVMQlFBRGdnRUJBR05WdmVIOGR4ZzNvK21VeVRkbmFjVmQ1N24zSkExdnZEU1JWREkyQTZ1eXN3ZFp1L1BVCkkwZXpZWFV0RVNnSk1IRmQycVVNMjNuNVJsSXJ3R0xuUXFISUh5VStWWHhsdnZsRnpNOVpEWllSTmU3QlJvYXgKQVlEdUI5STZXT3FYbkFvczFqRmxNUG5NbFpqdU5kSGxpT1BjTU1oNndLaTZzZFhpVStHYTJ2RUVLY01jSVUyRgpvU2djUWdMYTk0aEpacGk3ZnNMdm1OQUxoT045UHdNMGM1dVJVejV4T0dGMUtCbWRSeEgvbUNOS2JKYjFRQm1HCkkwYitEUEdaTktXTU0xMzhIQXdoV0tkNjVoVHdYOWl4V3ZHMkh4TG1WQzg0L1BHT0tWQW9FNkpsYWFHdTlQVmkKdjlOSjVaZlZrcXdCd0hKbzZXdk9xVlA3SVFjZmg3d0drWm89Ci0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=", + "signerName": "kubernetes.io/kube-apiserver-client", + "expirationSeconds": 86400, + "usages": ["client auth"] + } + }))?; + + let client = Client::try_default().await?; + let csr: Api = Api::all(client.clone()); + assert!(csr.create(&PostParams::default(), &dummy_csr).await.is_ok()); + + // Patch the approval and approve the CSR + let approval_type = "ApprovedFake"; + let csr_status: CertificateSigningRequestStatus = CertificateSigningRequestStatus { + certificate: None, + conditions: Some(vec![CertificateSigningRequestCondition { + type_: approval_type.to_string(), + last_update_time: None, + last_transition_time: None, + message: Some(format!("{} {}", approval_type, "by kube-rs client")), + reason: Some("kube-rsClient".to_string()), + status: "True".to_string(), + }]), + }; + let csr_status_patch = Patch::Merge(serde_json::json!({ "status": csr_status })); + let _ = csr + .patch_approval(csr_name, &Default::default(), &csr_status_patch) + .await?; + let csr_after_approval = csr.get_approval(csr_name).await?; + + assert_eq!( + csr_after_approval + .status + .as_ref() + .unwrap() + .conditions + .as_ref() + .unwrap()[0] + .type_, + approval_type.to_string() + ); + csr.delete(csr_name, &DeleteParams::default()).await?; + Ok(()) + } }