mod kubeconfig;
use crate::k8s_types::K8sType;
use std::collections::HashMap;
use std::io;
use std::{
path::{Path, PathBuf},
time::Duration,
};
pub const DEFAULT_TRACKING_LABEL_NAME: &str = "app.kubernetes.io/instance";
pub const DEFAULT_OWNERSHIP_LABEL_NAME: &str = "app.kubernetes.io/managed-by";
const SERVICE_ACCOUNT_TOKEN_PATH: &str = "/var/run/secrets/kubernetes.io/serviceaccount/token";
const SERVICE_ACCOUNT_CA_PATH: &str = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
const API_SERVER_HOSTNAME: &str = "kubernetes.default.svc";
pub use self::kubeconfig::{KubeConfig, KubeConfigError};
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum UpdateStrategy {
Replace,
Recreate,
OnDelete,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ChildConfig {
pub update_strategy: UpdateStrategy,
}
impl ChildConfig {
pub fn new(update_strategy: UpdateStrategy) -> ChildConfig {
ChildConfig { update_strategy }
}
pub fn recreate() -> ChildConfig {
ChildConfig::new(UpdateStrategy::Recreate)
}
pub fn replace() -> ChildConfig {
ChildConfig::new(UpdateStrategy::Replace)
}
pub fn on_delete() -> ChildConfig {
ChildConfig::new(UpdateStrategy::OnDelete)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct OperatorConfig {
pub parent: &'static K8sType,
pub child_types: HashMap<&'static K8sType, ChildConfig>,
pub namespace: Option<String>,
pub operator_name: String,
pub tracking_label_name: String,
pub ownership_label_name: String,
pub server_port: u16,
pub expose_metrics: bool,
pub expose_health: bool,
pub max_error_backoff: Duration,
}
impl OperatorConfig {
pub fn new(operator_name: impl Into<String>, parent: &'static K8sType) -> OperatorConfig {
let operator_name = operator_name.into();
OperatorConfig {
parent,
operator_name,
child_types: HashMap::new(),
namespace: None,
tracking_label_name: DEFAULT_TRACKING_LABEL_NAME.to_owned(),
ownership_label_name: DEFAULT_OWNERSHIP_LABEL_NAME.to_owned(),
server_port: 8080,
expose_metrics: true,
expose_health: true,
max_error_backoff: Duration::from_secs(600),
}
}
pub fn within_namespace(mut self, namespace: impl Into<String>) -> Self {
self.namespace = Some(namespace.into());
self
}
pub fn with_child(mut self, child_type: &'static K8sType, config: ChildConfig) -> Self {
self.child_types.insert(child_type, config);
self
}
pub fn expose_health(mut self, expose_health: bool) -> Self {
self.expose_health = expose_health;
self
}
pub fn expose_metrics(mut self, expose_metrics: bool) -> Self {
self.expose_metrics = expose_metrics;
self
}
pub fn server_port(mut self, port: u16) -> Self {
self.server_port = port;
self
}
pub fn max_error_backoff(mut self, max_error_backoff: Duration) -> Self {
self.max_error_backoff = max_error_backoff;
self
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum CAData {
File(PathBuf),
Contents(String),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Credentials {
Header(String),
Pem {
certificate_base64: String,
private_key_base64: String,
},
PemPath {
certificate_path: String,
private_key_path: String,
},
}
impl Credentials {
pub fn raw_bearer_token(raw_token: impl AsRef<str>) -> Credentials {
let encoded = base64::encode(raw_token.as_ref());
Credentials::Header(format!("Bearer {}", encoded))
}
pub fn base64_bearer_token(base64_token: impl AsRef<str>) -> Credentials {
Credentials::Header(format!("Bearer {}", base64_token.as_ref()))
}
pub fn basic(raw_username: impl AsRef<str>, raw_password: impl AsRef<str>) -> Credentials {
let formatted = format!("{}:{}", raw_username.as_ref(), raw_password.as_ref());
let encoded = base64::encode(formatted.as_str());
Credentials::Header(format!("Basic {}", encoded))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ClientConfig {
pub api_server_endpoint: String,
pub credentials: Credentials,
pub ca_data: Option<CAData>,
pub user_agent: String,
pub verify_ssl_certs: bool,
pub impersonate: Option<String>,
pub impersonate_groups: Vec<String>,
}
impl ClientConfig {
pub fn from_service_account(user_agent: impl Into<String>) -> Result<ClientConfig, io::Error> {
use std::fs::File;
use std::io::Read;
let mut token_file = File::open(SERVICE_ACCOUNT_TOKEN_PATH)?;
let mut service_account_token = String::new();
token_file.read_to_string(&mut service_account_token)?;
let ca_file_path = Path::new(SERVICE_ACCOUNT_CA_PATH);
let ca_data = if ca_file_path.exists() {
Some(CAData::File(ca_file_path.to_owned()))
} else {
None
};
let api_server_endpoint = format!("https://{}", API_SERVER_HOSTNAME);
Ok(ClientConfig {
api_server_endpoint,
ca_data,
credentials: Credentials::Header(format!("Bearer {}", service_account_token)),
user_agent: user_agent.into(),
verify_ssl_certs: true,
impersonate: None,
impersonate_groups: Vec::new(),
})
}
pub fn from_kubeconfig(user_agent: impl Into<String>) -> Result<ClientConfig, KubeConfigError> {
self::kubeconfig::load_from_kubeconfig(user_agent.into())
}
}