Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Api fns for arbitrary subresources and approval subresource for CertificateSigningRequest #773

Merged
merged 11 commits into from
Jan 9, 2022
48 changes: 48 additions & 0 deletions kube-client/src/api/subresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,54 @@ where
}
}

/// Arbitrary subresources
ChinYing-Li marked this conversation as resolved.
Show resolved Hide resolved
impl<K> Api<K>
where
K: Clone + DeserializeOwned + Debug,
{
/// Display one or many sub-resources.
pub async fn get_subresource(&self, subresource_name: &str, name: &str) -> Result<K> {
let mut req = self
.request
.get_subresource(subresource_name, name)
.map_err(Error::BuildRequest)?;
req.extensions_mut().insert("get_subresource");
self.client.request::<K>(req).await
}

/// Patch an instance of the subresource
pub async fn patch_subresource<P: serde::Serialize + Debug>(
&self,
subresource_name: &str,
name: &str,
pp: &PatchParams,
patch: &Patch<P>,
) -> Result<K> {
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::<K>(req).await
}

/// Replace an instance of the subresource
pub async fn replace_subresource(
&self,
subresource_name: &str,
name: &str,
pp: &PostParams,
data: Vec<u8>,
) -> Result<K> {
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::<K>(req).await
}
}

// ----------------------------------------------------------------------------

// TODO: Replace examples with owned custom resources. Bad practice to write to owned objects
Expand Down
29 changes: 27 additions & 2 deletions kube-client/src/api/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<K> Api<K>
Expand Down Expand Up @@ -34,6 +37,28 @@ impl Api<Node> {
}
}

impl Api<CertificateSigningRequest> {
/// Partially update approval of the specified CertificateSigningRequest.
pub async fn patch_approval<P: serde::Serialize>(
&self,
name: &str,
pp: &PatchParams,
patch: &Patch<P>,
) -> Result<CertificateSigningRequest> {
let mut req = self
.request
.patch_subresource("approval", name, pp, patch)
.map_err(Error::BuildRequest)?;
req.extensions_mut().insert("approval");
self.client.request::<CertificateSigningRequest>(req).await
}

/// Get the CertificateSigningRequest. May differ from get(name)
pub async fn get_approval(&self, name: &str) -> Result<CertificateSigningRequest> {
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)]
Expand Down
60 changes: 60 additions & 0 deletions kube-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -462,4 +463,63 @@ mod test {

Ok(())
}

#[tokio::test]
#[ignore] // needs cluster (will create a CertificateSigningRequest)
async fn csr_can_be_approved() -> Result<(), Box<dyn std::error::Error>> {
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<CertificateSigningRequest> = Api::all(client.clone());
assert!(csr.create(&PostParams::default(), &dummy_csr).await.is_ok());

// Patch the approval and approve the CSR
let approval_type = "Approved";
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(csr_name).await?;
ChinYing-Li marked this conversation as resolved.
Show resolved Hide resolved

assert_eq!(
csr_after_approval
.status
.as_ref()
.unwrap()
.conditions
.as_ref()
.unwrap()[0]
.type_,
"Approved".to_string()
);
csr.delete(csr_name, &DeleteParams::default()).await?;
Ok(())
}
}