vault_mgmt_lib/
helpers.rs

1use k8s_openapi::api::{apps::v1::StatefulSet, core::v1::Pod};
2use kube::{api::ListParams, Api};
3use tokio::io::{AsyncRead, AsyncWrite};
4
5use crate::{BytesBody, HttpForwarderService};
6
7pub const LABEL_KEY_VAULT_ACTIVE: &str = "vault-active";
8pub const LABEL_KEY_VAULT_SEALED: &str = "vault-sealed";
9
10pub fn list_vault_pods() -> ListParams {
11    ListParams::default().labels("app.kubernetes.io/name=vault")
12}
13
14/// Check if the vault pod is sealed based on its labels
15/// Returns an error if the pod does not have the expected labels
16pub fn is_sealed(pod: &Pod) -> anyhow::Result<bool> {
17    match pod.metadata.labels.as_ref() {
18        None => Err(anyhow::anyhow!("pod does not have labels")),
19        Some(labels) => match labels.get(LABEL_KEY_VAULT_SEALED) {
20            Some(x) if x.as_str() == "true" => Ok(true),
21            Some(x) if x.as_str() == "false" => Ok(false),
22            _ => Err(anyhow::anyhow!(
23                "pod does not have a {} label",
24                LABEL_KEY_VAULT_SEALED
25            )),
26        },
27    }
28}
29
30/// Check if the vault pod is active based on its labels
31/// Returns an error if the pod does not have the expected labels
32pub fn is_active(pod: &Pod) -> anyhow::Result<bool> {
33    match pod.metadata.labels.as_ref() {
34        None => Err(anyhow::anyhow!("pod does not have labels")),
35        Some(labels) => match labels.get(LABEL_KEY_VAULT_ACTIVE) {
36            Some(x) if x.as_str() == "true" => Ok(true),
37            Some(x) if x.as_str() == "false" => Ok(false),
38            _ => Err(anyhow::anyhow!(
39                "pod does not have a {} label",
40                LABEL_KEY_VAULT_ACTIVE
41            )),
42        },
43    }
44}
45
46/// Wrapper around the kube::Api type for the Vault pod
47#[derive(Clone)]
48pub struct PodApi {
49    pub api: Api<Pod>,
50    tls: bool,
51    domain: String,
52}
53
54impl PodApi {
55    pub fn new(api: Api<Pod>, tls: bool, domain: String) -> Self {
56        Self { api, tls, domain }
57    }
58}
59
60impl PodApi {
61    /// Get a stream to a port on a pod
62    /// The stream can be used to send HTTP requests
63    pub async fn portforward(
64        &self,
65        pod: &str,
66        port: u16,
67    ) -> anyhow::Result<impl AsyncRead + AsyncWrite + Unpin> {
68        let mut pf = self.api.portforward(pod, &[port]).await?;
69        pf.take_stream(port).ok_or(anyhow::anyhow!(
70            "port {} is not available on pod {}",
71            port,
72            pod
73        ))
74    }
75
76    pub async fn http(
77        &self,
78        pod: &str,
79        port: u16,
80    ) -> anyhow::Result<HttpForwarderService<BytesBody>> {
81        let pf = self.portforward(pod, port).await?;
82
83        if self.tls {
84            return HttpForwarderService::https(&self.domain, pf).await;
85        }
86
87        HttpForwarderService::http(pf).await
88    }
89}
90
91/// Wrapper around the kube::Api type for the Vault statefulset
92pub struct StatefulSetApi {
93    pub api: Api<StatefulSet>,
94}
95
96impl From<Api<StatefulSet>> for StatefulSetApi {
97    fn from(api: Api<StatefulSet>) -> Self {
98        Self { api }
99    }
100}