Skip to main content

kube_utils/
lib.rs

1//!Minimal client for kubernetes internal API
2//!
3//!If you need full fledged API client, then prefer to use [kube-client](https://crates.io/crates/kube-client)
4
5#![warn(missing_docs)]
6#![allow(clippy::style)]
7
8use std::{io, env};
9use std::sync::RwLock;
10
11pub mod data;
12mod config;
13pub use config::*;
14
15pub use ureq::http::Uri;
16
17const MAX_BODY_SIZE: u64 = 10 * 1024 * 1024;
18
19///Returns pod name using following environment variables:
20///- POD_NAME
21///- HOSTNAME
22///
23///Returns None, if neither of variable available
24pub fn pod_name() -> Option<String> {
25    env::var("POD_NAME").or_else(|_| env::var("HOSTNAME")).ok()
26}
27
28struct State {
29    auth_token: RwLock<ClusterToken>,
30    namespace: String,
31    uri: Uri,
32}
33
34#[derive(Clone)]
35///Client for internal API (i.e. to be used within cluster)
36pub struct Client {
37    http: ureq::Agent,
38    state: std::sync::Arc<State>
39}
40
41impl Client {
42    ///Creates new client
43    pub fn new(http: HttpConfig, KubeConfig { auth_token, uri, certs, namespace }: KubeConfig) -> Self {
44        let state = State {
45            auth_token: RwLock::new(auth_token),
46            uri,
47            namespace
48        };
49
50        let ca = ureq::tls::RootCerts::Specific(std::sync::Arc::new(certs));
51        let tls_config = ureq::tls::TlsConfig::builder().use_sni(false).root_certs(ca).build();
52        let http = ureq::Agent::config_builder().timeout_per_call(Some(http.timeout)).tls_config(tls_config).build();
53        let http = ureq::Agent::new_with_config(http);
54        Self {
55            http,
56            state: std::sync::Arc::new(state)
57        }
58    }
59
60    ///Gets pod information in the same namespace as current client
61    pub fn get_pod(&self, pod_name: &str) -> Result<data::Pod, ureq::Error> {
62        let auth_token = self.state.auth_token.read().expect("internal error");
63        let bearer = if auth_token.is_expired() {
64            drop(auth_token);
65            let mut auth_token = self.state.auth_token.write().expect("internal error");
66            auth_token.refresh();
67            format!("Bearer {}", auth_token.token())
68        } else {
69            let result = format!("Bearer {}", auth_token.token());
70            drop(auth_token);
71            result
72        };
73
74        let uri = format!("{}/api/v1/namespaces/{}/pods/{pod_name}", self.state.uri, self.state.namespace);
75        let response = self.http.get(&uri).header("Authorization", bearer).call()?;
76        let body = response.into_body().into_with_config().limit(MAX_BODY_SIZE).reader();
77        serde_json::from_reader(body).map_err(|error| {
78            if error.is_io() || error.is_eof() {
79                ureq::Error::Io(io::Error::other(error))
80            } else {
81                ureq::Error::Other(Box::new(error))
82            }
83        })
84    }
85}