Skip to content

Commit

Permalink
Add create_subresource method to Api and create_token_request m…
Browse files Browse the repository at this point in the history
…ethod to `Api<ServiceAccount>` (#989)

* Add create_subresource method to Request

Signed-off-by: pbzweihander <pbzweihander@gmail.com>

* Add create_subresource method to Api

Signed-off-by: pbzweihander <pbzweihander@gmail.com>

* Add create_token_request method to Api<ServiceAccount>

Signed-off-by: pbzweihander <pbzweihander@gmail.com>

* Add test for Request::create_subresource

Signed-off-by: pbzweihander <pbzweihander@gmail.com>

* Add intergration test for Api::<ServiceAccount>::create_token_request

Signed-off-by: pbzweihander <pbzweihander@gmail.com>

* Create and delete ServiceAccount for integration test

Signed-off-by: pbzweihander <pbzweihander@gmail.com>

Signed-off-by: pbzweihander <pbzweihander@gmail.com>
  • Loading branch information
pbzweihander authored Aug 19, 2022
1 parent b4a5938 commit 251728e
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 3 deletions.
19 changes: 19 additions & 0 deletions kube-client/src/api/subresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,25 @@ where
self.client.request::<K>(req).await
}

/// Create an instance of the subresource
pub async fn create_subresource<T>(
&self,
subresource_name: &str,
name: &str,
pp: &PostParams,
data: Vec<u8>,
) -> Result<T>
where
T: DeserializeOwned,
{
let mut req = self
.request
.create_subresource(subresource_name, name, pp, data)
.map_err(Error::BuildRequest)?;
req.extensions_mut().insert("create_subresource");
self.client.request::<T>(req).await
}

/// Patch an instance of the subresource
pub async fn patch_subresource<P: serde::Serialize + Debug>(
&self,
Expand Down
98 changes: 95 additions & 3 deletions kube-client/src/api/util/mod.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::{
authentication::v1::TokenRequest,
core::v1::{Node, ServiceAccount},
};
use kube_core::{params::PostParams, util::Restart};
use serde::de::DeserializeOwned;

k8s_openapi::k8s_if_ge_1_19! {
Expand Down Expand Up @@ -38,6 +41,24 @@ impl Api<Node> {
}
}

impl Api<ServiceAccount> {
/// Create a TokenRequest of a ServiceAccount
pub async fn create_token_request(
&self,
name: &str,
pp: &PostParams,
token_request: &TokenRequest,
) -> Result<TokenRequest> {
let bytes = serde_json::to_vec(token_request).map_err(Error::SerdeError)?;
let mut req = self
.request
.create_subresource("token", name, pp, bytes)
.map_err(Error::BuildRequest)?;
req.extensions_mut().insert("create_token_request");
self.client.request::<TokenRequest>(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 All @@ -47,7 +68,10 @@ mod test {
api::{Api, DeleteParams, ListParams, PostParams},
Client,
};
use k8s_openapi::api::core::v1::Node;
use k8s_openapi::api::{
authentication::v1::{TokenRequest, TokenRequestSpec, TokenReview, TokenReviewSpec},
core::v1::{Node, ServiceAccount},
};
use serde_json::json;

#[tokio::test]
Expand Down Expand Up @@ -81,4 +105,72 @@ mod test {
nodes.delete(node_name, &DeleteParams::default()).await?;
Ok(())
}

#[tokio::test]
#[ignore] // requires a cluster
async fn create_token_request() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::try_default().await?;

let serviceaccount_name = "fakesa";
let serviceaccount_namespace = "default";
let audiences = vec!["api".to_string()];

let serviceaccounts: Api<ServiceAccount> = Api::namespaced(client.clone(), serviceaccount_namespace);
let tokenreviews: Api<TokenReview> = Api::all(client);

// Create ServiceAccount
let fake_sa = serde_json::from_value(json!({
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": {
"name": serviceaccount_name,
},
}))?;
serviceaccounts.create(&PostParams::default(), &fake_sa).await?;

// Create TokenRequest
let tokenrequest = serviceaccounts
.create_token_request(serviceaccount_name, &PostParams::default(), &TokenRequest {
metadata: Default::default(),
spec: TokenRequestSpec {
audiences: audiences.clone(),
bound_object_ref: None,
expiration_seconds: None,
},
status: None,
})
.await?;
let token = tokenrequest.status.unwrap().token;
assert!(!token.is_empty());

// Check created token is valid with TokenReview
let tokenreview = tokenreviews
.create(&PostParams::default(), &TokenReview {
metadata: Default::default(),
spec: TokenReviewSpec {
audiences: Some(audiences.clone()),
token: Some(token),
},
status: None,
})
.await?;
let tokenreviewstatus = tokenreview.status.unwrap();
assert_eq!(tokenreviewstatus.audiences, Some(audiences));
assert_eq!(tokenreviewstatus.authenticated, Some(true));
assert_eq!(tokenreviewstatus.error, None);
assert_eq!(
tokenreviewstatus.user.unwrap().username,
Some(format!(
"system:serviceaccount:{}:{}",
serviceaccount_namespace, serviceaccount_name
))
);

// Cleanup ServiceAccount
serviceaccounts
.delete(serviceaccount_name, &DeleteParams::default())
.await?;

Ok(())
}
}
29 changes: 29 additions & 0 deletions kube-core/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,24 @@ impl Request {
req.body(vec![]).map_err(Error::BuildRequest)
}

/// Create an instance of the subresource
pub fn create_subresource(
&self,
subresource_name: &str,
name: &str,
pp: &PostParams,
data: Vec<u8>,
) -> Result<http::Request<Vec<u8>>, Error> {
let target = format!("{}/{}/{}?", self.url_path, name, subresource_name);
let mut qp = form_urlencoded::Serializer::new(target);
if pp.dry_run {
qp.append_pair("dryRun", "All");
}
let urlstr = qp.finish();
let req = http::Request::post(urlstr).header(http::header::CONTENT_TYPE, JSON_MIME);
req.body(data).map_err(Error::BuildRequest)
}

/// Patch an instance of the subresource
pub fn patch_subresource<P: serde::Serialize>(
&self,
Expand Down Expand Up @@ -529,6 +547,17 @@ mod test {
assert_eq!(req.method(), "PUT");
}

#[test]
fn create_subresource_path() {
let url = corev1::ServiceAccount::url_path(&(), Some("ns"));
let pp = PostParams::default();
let data = vec![];
let req = Request::new(url)
.create_subresource("token", "sa", &pp, data)
.unwrap();
assert_eq!(req.uri(), "/api/v1/namespaces/ns/serviceaccounts/sa/token");
}

// TODO: reinstate if we get scoping in trait
//#[test]
//#[should_panic]
Expand Down

0 comments on commit 251728e

Please sign in to comment.