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
55 changes: 53 additions & 2 deletions kube-client/src/api/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@ 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, CertificateSigningRequestCondition, CertificateSigningRequestStatus,
},
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 +42,49 @@ impl Api<Node> {
}
}

type CSRStatusType = &'static str;
static APPROVED: CSRStatusType = "Approved";
static DENIED: CSRStatusType = "Denied";

impl Api<CertificateSigningRequest> {
/// Approve the specified CertificateSigningRequest.
pub async fn approve(&self, name: &str, pp: &PatchParams) -> Result<CertificateSigningRequest> {
self.patch_approval(name, pp, &APPROVED).await
}

/// Deny the specified CertificateSigningRequest.
pub async fn deny(&self, name: &str, pp: &PatchParams) -> Result<CertificateSigningRequest> {
self.patch_approval(name, pp, &DENIED).await
}

/// Partially update approval of the specified CertificateSigningRequest.
async fn patch_approval(
&self,
name: &str,
pp: &PatchParams,
approval_type: &CSRStatusType,
) -> Result<CertificateSigningRequest> {
let patch = serde_json::json!({"status": 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()
}])
clux marked this conversation as resolved.
Show resolved Hide resolved
}});

let mut req = self
.request
.patch_subresource("approval", name, pp, &Patch::Strategic(&patch))
ChinYing-Li marked this conversation as resolved.
Show resolved Hide resolved
.map_err(Error::BuildRequest)?;
req.extensions_mut().insert("approval");
self.client.request::<CertificateSigningRequest>(req).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
43 changes: 43 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,46 @@ 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::{ListParams, PostParams};
use k8s_openapi::api::certificates::v1::CertificateSigningRequest;

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());

let _csr_after_approval = csr.approve(csr_name, &Default::default()).await?;
let csr_list = csr.list(&ListParams::default()).await?;

assert_eq!(csr_list.items.len(), 1);
assert_eq!(
csr_list.items[0]
ChinYing-Li marked this conversation as resolved.
Show resolved Hide resolved
.status
.as_ref()
.unwrap()
.conditions
.as_ref()
.unwrap()[0]
.type_,
"Approved".to_string()
);
csr.delete(csr_name, &DeleteParams::default()).await?;
Ok(())
}
}