pub use kube_client::*;
use std::path::PathBuf;
use thiserror::Error;
#[cfg(feature = "prometheus-client")]
mod metrics;
mod timeouts;
#[cfg(feature = "prometheus-client")]
pub use self::metrics::ClientMetricsFamilies;
pub use self::timeouts::ResponseHeadersTimeout;
#[derive(Clone, Debug, Default)]
#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
#[cfg_attr(feature = "clap", derive(clap::Parser))]
pub struct ClientArgs {
#[cfg_attr(feature = "clap", clap(long))]
pub cluster: Option<String>,
#[cfg_attr(feature = "clap", clap(long))]
pub context: Option<String>,
#[cfg_attr(feature = "clap", clap(long))]
pub user: Option<String>,
#[cfg_attr(feature = "clap", clap(long))]
pub kubeconfig: Option<PathBuf>,
#[cfg_attr(feature = "clap", clap(long = "as"))]
pub impersonate_user: Option<String>,
#[cfg_attr(feature = "clap", clap(long = "as-group"))]
pub impersonate_group: Option<String>,
#[cfg_attr(feature = "clap", clap(
long = "kube-api-response-headers-timeout",
default_value_t = ResponseHeadersTimeout::default(),
))]
pub response_headers_timeout: ResponseHeadersTimeout,
}
#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
pub struct ClientBuilder {
args: ClientArgs,
#[cfg(feature = "prometheus-client")]
metrics_families: Option<ClientMetricsFamilies>,
}
#[derive(Debug, Error)]
#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
#[non_exhaustive]
pub enum ConfigError {
#[error(transparent)]
Kubeconfig(#[from] config::KubeconfigError),
#[error(transparent)]
InCluster(#[from] config::InClusterError),
#[error(transparent)]
Client(#[from] Error),
}
impl ClientArgs {
pub async fn try_client(self) -> Result<Client, ConfigError> {
ClientBuilder::from_args(self).build().await
}
fn is_customized(&self) -> bool {
self.context.is_some()
|| self.cluster.is_some()
|| self.user.is_some()
|| self.impersonate_user.is_some()
|| self.impersonate_group.is_some()
|| self.kubeconfig.is_some()
}
async fn load_config(&self) -> Result<Config, ConfigError> {
match self.load_local_config().await {
Ok(config) => Ok(config),
Err(e) if self.is_customized() => Err(e),
Err(_) => Config::incluster().map_err(Into::into),
}
}
async fn load_local_config(&self) -> Result<Config, ConfigError> {
let options = config::KubeConfigOptions {
context: self.context.clone(),
cluster: self.cluster.clone(),
user: self.user.clone(),
};
let mut kubeconfig = match &self.kubeconfig {
Some(path) => config::Kubeconfig::read_from(path.as_path())?,
None => config::Kubeconfig::read()?,
};
if let Some(user) = &self.impersonate_user {
for auth in kubeconfig.auth_infos.iter_mut() {
if let Some(ai) = auth.auth_info.as_mut() {
ai.impersonate = Some(user.clone());
}
}
}
if let Some(group) = &self.impersonate_group {
for auth in kubeconfig.auth_infos.iter_mut() {
if let Some(ai) = auth.auth_info.as_mut() {
ai.impersonate_groups = Some(vec![group.clone()]);
}
}
}
Config::from_custom_kubeconfig(kubeconfig, &options)
.await
.map_err(Into::into)
}
}
impl ClientBuilder {
pub fn from_args(args: ClientArgs) -> Self {
Self {
args,
#[cfg(feature = "prometheus-client")]
metrics_families: None,
}
}
#[cfg(feature = "prometheus-client")]
#[cfg_attr(
docsrs,
doc(cfg(all(features = "client", feature = "prometheus-client")))
)]
pub fn with_metrics(mut self, metrics: ClientMetricsFamilies) -> Self {
self.metrics_families = Some(metrics);
self
}
pub async fn build(self) -> Result<Client, ConfigError> {
let config = self.args.load_config().await?;
#[cfg(feature = "prometheus-client")]
let metrics = self
.metrics_families
.map_or_else(Default::default, |m| m.metrics(&config));
let cb = kube_client::client::ClientBuilder::try_from(config)?
.with_layer(&timeouts::layer(self.args.response_headers_timeout));
#[cfg(feature = "prometheus-client")]
let cb = cb.with_layer(&metrics::layer(metrics));
Ok(cb.build())
}
}
mod svc {
pub use tower::{layer::layer_fn, layer::Layer, Service};
pub type BoxService = tower::util::BoxService<Request, Response, BoxError>;
pub type Request = hyper::Request<kube_client::client::Body>;
pub type Response = hyper::Response<BoxBody>;
pub type BoxBody =
Box<dyn hyper::body::Body<Data = bytes::Bytes, Error = BoxError> + Send + Unpin>;
pub type BoxError = tower::BoxError;
pub type BoxFuture = futures_util::future::BoxFuture<'static, Result<Response, BoxError>>;
}