mod file_config;
mod file_loader;
mod incluster_config;
mod utils;
use crate::{error::ConfigError, Result};
use file_loader::ConfigLoader;
pub use file_loader::KubeConfigOptions;
pub(crate) use utils::read_file_to_string;
use http::header::HeaderMap;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct Config {
pub cluster_url: url::Url,
pub default_ns: String,
pub root_cert: Option<Vec<Vec<u8>>>,
pub headers: HeaderMap,
pub timeout: Option<std::time::Duration>,
pub accept_invalid_certs: bool,
pub(crate) identity: Option<(Vec<u8>, String)>,
pub(crate) auth_info: AuthInfo,
}
impl Config {
pub fn new(cluster_url: url::Url) -> Self {
Self {
cluster_url,
default_ns: String::from("default"),
root_cert: None,
headers: HeaderMap::new(),
timeout: Some(DEFAULT_TIMEOUT),
accept_invalid_certs: false,
identity: None,
auth_info: AuthInfo::default(),
}
}
pub async fn infer() -> Result<Self> {
match Self::from_cluster_env() {
Err(cluster_env_err) => {
trace!("No in-cluster config found: {}", cluster_env_err);
trace!("Falling back to local kubeconfig");
let config = Self::from_kubeconfig(&KubeConfigOptions::default())
.await
.map_err(|kubeconfig_err| ConfigError::ConfigInferenceExhausted {
cluster_env: Box::new(cluster_env_err),
kubeconfig: Box::new(kubeconfig_err),
})?;
Ok(config)
}
success => success,
}
}
pub fn from_cluster_env() -> Result<Self> {
let cluster_url = incluster_config::kube_server().ok_or(ConfigError::MissingInClusterVariables {
hostenv: incluster_config::SERVICE_HOSTENV,
portenv: incluster_config::SERVICE_PORTENV,
})?;
let cluster_url = url::Url::parse(&cluster_url)?;
let default_ns = incluster_config::load_default_ns()
.map_err(Box::new)
.map_err(ConfigError::InvalidInClusterNamespace)?;
let root_cert = incluster_config::load_cert()?;
let token = incluster_config::load_token()
.map_err(Box::new)
.map_err(ConfigError::InvalidInClusterToken)?;
Ok(Self {
cluster_url,
default_ns,
root_cert: Some(root_cert),
headers: HeaderMap::new(),
timeout: Some(DEFAULT_TIMEOUT),
accept_invalid_certs: false,
identity: None,
auth_info: AuthInfo {
token: Some(token),
..Default::default()
},
})
}
pub async fn from_kubeconfig(options: &KubeConfigOptions) -> Result<Self> {
let loader = ConfigLoader::new_from_options(options).await?;
Self::new_from_loader(loader).await
}
pub async fn from_custom_kubeconfig(kubeconfig: Kubeconfig, options: &KubeConfigOptions) -> Result<Self> {
let loader = ConfigLoader::new_from_kubeconfig(kubeconfig, options).await?;
Self::new_from_loader(loader).await
}
async fn new_from_loader(loader: ConfigLoader) -> Result<Self> {
let cluster_url = url::Url::parse(&loader.cluster.server)?;
let default_ns = loader
.current_context
.namespace
.clone()
.unwrap_or_else(|| String::from("default"));
let mut accept_invalid_certs = false;
let mut root_cert = None;
let mut identity_pem = None;
if let Some(ca_bundle) = loader.ca_bundle()? {
for ca in &ca_bundle {
accept_invalid_certs = hacky_cert_lifetime_for_macos(&ca);
}
root_cert = Some(ca_bundle);
}
match loader.identity_pem() {
Ok(id) => identity_pem = Some(id),
Err(e) => {
debug!("failed to load client identity from kubeconfig: {}", e);
if let Some(true) = loader.cluster.insecure_skip_tls_verify {
accept_invalid_certs = true;
}
}
}
Ok(Self {
cluster_url,
default_ns,
root_cert,
headers: HeaderMap::new(),
timeout: Some(DEFAULT_TIMEOUT),
accept_invalid_certs,
identity: identity_pem.map(|i| (i, String::from(IDENTITY_PASSWORD))),
auth_info: loader.user,
})
}
}
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(295);
const IDENTITY_PASSWORD: &str = " ";
#[cfg(all(target_os = "macos", feature = "native-tls"))]
fn hacky_cert_lifetime_for_macos(ca: &[u8]) -> bool {
use openssl::x509::X509;
let ca = X509::from_der(ca).expect("valid der is a der");
ca.not_before()
.diff(ca.not_after())
.map(|d| d.days.abs() > 824)
.unwrap_or(false)
}
#[cfg(any(not(target_os = "macos"), not(feature = "native-tls")))]
fn hacky_cert_lifetime_for_macos(_: &[u8]) -> bool {
false
}
pub use file_config::{
AuthInfo, AuthProviderConfig, Cluster, Context, ExecConfig, Kubeconfig, NamedAuthInfo, NamedCluster,
NamedContext, NamedExtension, Preferences,
};