use std::sync::Arc;
use tatara_lisp_eval::{Arity, EvalError, Interpreter, Value};
use crate::script_ctx::ScriptCtx;
const SA_DIR: &str = "/var/run/secrets/kubernetes.io/serviceaccount";
pub fn install(interp: &mut Interpreter<ScriptCtx>) {
interp.register_fn(
"kube-in-cluster?",
Arity::Exact(0),
|_args: &[Value], _ctx: &mut ScriptCtx, _sp| Ok(Value::Bool(in_cluster())),
);
interp.register_fn(
"kube-bearer-token",
Arity::Exact(0),
|_args: &[Value], _ctx: &mut ScriptCtx, sp| {
read_sa_file("token")
.map(|s| Value::Str(Arc::from(s.trim_end().to_string())))
.map_err(|e| {
EvalError::native_fn("kube-bearer-token", format!("read SA token: {e}"), sp)
})
},
);
interp.register_fn(
"kube-ca-cert",
Arity::Exact(0),
|_args: &[Value], _ctx: &mut ScriptCtx, sp| {
read_sa_file("ca.crt")
.map(|s| Value::Str(Arc::from(s)))
.map_err(|e| {
EvalError::native_fn("kube-ca-cert", format!("read SA ca.crt: {e}"), sp)
})
},
);
interp.register_fn(
"kube-namespace",
Arity::Exact(0),
|_args: &[Value], _ctx: &mut ScriptCtx, sp| {
if let Ok(ns) = std::env::var("POD_NAMESPACE") {
return Ok(Value::Str(Arc::from(ns)));
}
match read_sa_file("namespace") {
Ok(s) => Ok(Value::Str(Arc::from(s.trim_end().to_string()))),
Err(_) => {
if !in_cluster() {
return Ok(Value::Str(Arc::from("default")));
}
Err(EvalError::native_fn(
"kube-namespace",
"no namespace from env or SA file",
sp,
))
}
}
},
);
interp.register_fn(
"kube-api-base",
Arity::Exact(0),
|_args: &[Value], _ctx: &mut ScriptCtx, _sp| {
if let Ok(url) = std::env::var("KUBE_API_BASE") {
return Ok(Value::Str(Arc::from(url)));
}
let host = std::env::var("KUBERNETES_SERVICE_HOST")
.unwrap_or_else(|_| "kubernetes.default.svc".to_string());
let port =
std::env::var("KUBERNETES_SERVICE_PORT").unwrap_or_else(|_| "443".to_string());
Ok(Value::Str(Arc::from(format!("https://{host}:{port}"))))
},
);
interp.register_fn(
"kube-pod-name",
Arity::Exact(0),
|_args: &[Value], _ctx: &mut ScriptCtx, _sp| {
if let Ok(pod) = std::env::var("POD_NAME") {
return Ok(Value::Str(Arc::from(pod)));
}
let host = hostname_safe();
Ok(Value::Str(Arc::from(host)))
},
);
interp.register_fn(
"kube-cluster-name",
Arity::Exact(0),
|_args: &[Value], _ctx: &mut ScriptCtx, _sp| {
Ok(Value::Str(Arc::from(
std::env::var("CLUSTER_NAME").unwrap_or_else(|_| "unknown".to_string()),
)))
},
);
}
fn read_sa_file(name: &str) -> Result<String, std::io::Error> {
let path = format!("{SA_DIR}/{name}");
std::fs::read_to_string(path)
}
fn in_cluster() -> bool {
std::path::Path::new(SA_DIR).is_dir() && std::fs::metadata(format!("{SA_DIR}/token")).is_ok()
}
fn hostname_safe() -> String {
if let Ok(s) = std::fs::read_to_string("/etc/hostname") {
return s.trim_end().to_string();
}
std::env::var("HOSTNAME").unwrap_or_else(|_| "unknown".to_string())
}