tatara_lisp_script/stdlib/
kube.rs1use std::sync::Arc;
29
30use tatara_lisp_eval::{Arity, EvalError, Interpreter, Value};
31
32use crate::script_ctx::ScriptCtx;
33
34const SA_DIR: &str = "/var/run/secrets/kubernetes.io/serviceaccount";
35
36pub fn install(interp: &mut Interpreter<ScriptCtx>) {
37 interp.register_fn(
38 "kube-in-cluster?",
39 Arity::Exact(0),
40 |_args: &[Value], _ctx: &mut ScriptCtx, _sp| Ok(Value::Bool(in_cluster())),
41 );
42
43 interp.register_fn(
44 "kube-bearer-token",
45 Arity::Exact(0),
46 |_args: &[Value], _ctx: &mut ScriptCtx, sp| {
47 read_sa_file("token")
48 .map(|s| Value::Str(Arc::from(s.trim_end().to_string())))
49 .map_err(|e| {
50 EvalError::native_fn("kube-bearer-token", format!("read SA token: {e}"), sp)
51 })
52 },
53 );
54
55 interp.register_fn(
56 "kube-ca-cert",
57 Arity::Exact(0),
58 |_args: &[Value], _ctx: &mut ScriptCtx, sp| {
59 read_sa_file("ca.crt")
60 .map(|s| Value::Str(Arc::from(s)))
61 .map_err(|e| {
62 EvalError::native_fn("kube-ca-cert", format!("read SA ca.crt: {e}"), sp)
63 })
64 },
65 );
66
67 interp.register_fn(
68 "kube-namespace",
69 Arity::Exact(0),
70 |_args: &[Value], _ctx: &mut ScriptCtx, sp| {
71 if let Ok(ns) = std::env::var("POD_NAMESPACE") {
73 return Ok(Value::Str(Arc::from(ns)));
74 }
75 match read_sa_file("namespace") {
76 Ok(s) => Ok(Value::Str(Arc::from(s.trim_end().to_string()))),
77 Err(_) => {
78 if !in_cluster() {
79 return Ok(Value::Str(Arc::from("default")));
80 }
81 Err(EvalError::native_fn(
82 "kube-namespace",
83 "no namespace from env or SA file",
84 sp,
85 ))
86 }
87 }
88 },
89 );
90
91 interp.register_fn(
92 "kube-api-base",
93 Arity::Exact(0),
94 |_args: &[Value], _ctx: &mut ScriptCtx, _sp| {
95 if let Ok(url) = std::env::var("KUBE_API_BASE") {
97 return Ok(Value::Str(Arc::from(url)));
98 }
99 let host = std::env::var("KUBERNETES_SERVICE_HOST")
102 .unwrap_or_else(|_| "kubernetes.default.svc".to_string());
103 let port =
104 std::env::var("KUBERNETES_SERVICE_PORT").unwrap_or_else(|_| "443".to_string());
105 Ok(Value::Str(Arc::from(format!("https://{host}:{port}"))))
106 },
107 );
108
109 interp.register_fn(
110 "kube-pod-name",
111 Arity::Exact(0),
112 |_args: &[Value], _ctx: &mut ScriptCtx, _sp| {
113 if let Ok(pod) = std::env::var("POD_NAME") {
117 return Ok(Value::Str(Arc::from(pod)));
118 }
119 let host = hostname_safe();
120 Ok(Value::Str(Arc::from(host)))
121 },
122 );
123
124 interp.register_fn(
125 "kube-cluster-name",
126 Arity::Exact(0),
127 |_args: &[Value], _ctx: &mut ScriptCtx, _sp| {
128 Ok(Value::Str(Arc::from(
132 std::env::var("CLUSTER_NAME").unwrap_or_else(|_| "unknown".to_string()),
133 )))
134 },
135 );
136}
137
138fn read_sa_file(name: &str) -> Result<String, std::io::Error> {
139 let path = format!("{SA_DIR}/{name}");
140 std::fs::read_to_string(path)
141}
142
143fn in_cluster() -> bool {
144 std::path::Path::new(SA_DIR).is_dir() && std::fs::metadata(format!("{SA_DIR}/token")).is_ok()
145}
146
147fn hostname_safe() -> String {
148 if let Ok(s) = std::fs::read_to_string("/etc/hostname") {
151 return s.trim_end().to_string();
152 }
153 std::env::var("HOSTNAME").unwrap_or_else(|_| "unknown".to_string())
154}