vector/sources/kubernetes_logs/
path_helpers.rs

1//! Simple helpers for building and parsing k8s paths.
2//!
3//! Loosely based on <https://github.com/kubernetes/kubernetes/blob/31305966789525fca49ec26c289e565467d1f1c4/pkg/kubelet/kuberuntime/helpers.go>.
4
5#![deny(missing_docs)]
6
7use std::path::PathBuf;
8
9/// The root directory for pod logs.
10const K8S_LOGS_DIR: &str = "/var/log/pods";
11
12/// The delimiter used in the log path.
13const LOG_PATH_DELIMITER: &str = "_";
14
15/// Builds absolute log directory path for a pod sandbox.
16///
17/// Based on <https://github.com/kubernetes/kubernetes/blob/31305966789525fca49ec26c289e565467d1f1c4/pkg/kubelet/kuberuntime/helpers.go#L178>
18pub(super) fn build_pod_logs_directory(
19    pod_namespace: &str,
20    pod_name: &str,
21    pod_uid: &str,
22) -> PathBuf {
23    [
24        K8S_LOGS_DIR,
25        &[pod_namespace, pod_name, pod_uid].join(LOG_PATH_DELIMITER),
26    ]
27    .join("/")
28    .into()
29}
30
31/// Parses pod log file path and returns the log file info.
32///
33/// Assumes the input is a valid pod log file name.
34///
35/// Inspired by <https://github.com/kubernetes/kubernetes/blob/31305966789525fca49ec26c289e565467d1f1c4/pkg/kubelet/kuberuntime/helpers.go#L186>
36pub(super) fn parse_log_file_path(path: &str) -> Option<LogFileInfo<'_>> {
37    let mut components = path.rsplit(std::path::MAIN_SEPARATOR);
38
39    let _log_file_name = components.next()?;
40    let container_name = components.next()?;
41    let pod_dir = components.next()?;
42
43    let mut pod_dir_components = pod_dir.rsplit(LOG_PATH_DELIMITER);
44
45    let pod_uid = pod_dir_components.next()?;
46    let pod_name = pod_dir_components.next()?;
47    let pod_namespace = pod_dir_components.next()?;
48
49    Some(LogFileInfo {
50        pod_namespace,
51        pod_name,
52        pod_uid,
53        container_name,
54    })
55}
56
57/// Contains the information extracted from the pod log file path.
58#[derive(Debug, Clone, PartialEq, Eq, Hash)]
59pub struct LogFileInfo<'a> {
60    pub pod_namespace: &'a str,
61    pub pod_name: &'a str,
62    pub pod_uid: &'a str,
63    pub container_name: &'a str,
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_build_pod_logs_directory() {
72        let path = format!(
73            "{}{}",
74            std::path::MAIN_SEPARATOR,
75            [
76                "var",
77                "log",
78                "pods",
79                "sandbox0-ns_sandbox0-name_sandbox0-uid",
80            ]
81            .iter()
82            .collect::<PathBuf>()
83            .into_os_string()
84            .into_string()
85            .unwrap()
86        );
87        let s_path = path.as_str();
88        let cases = vec![
89            // Valid inputs.
90            (("sandbox0-ns", "sandbox0-name", "sandbox0-uid"), s_path),
91            // Invalid inputs.
92            (("", "", ""), "/var/log/pods/__"),
93        ];
94
95        for ((in_namespace, in_name, in_uid), expected) in cases.into_iter() {
96            assert_eq!(
97                build_pod_logs_directory(in_namespace, in_name, in_uid),
98                PathBuf::from(expected)
99            );
100        }
101    }
102
103    #[test]
104    fn test_parse_log_file_path() {
105        let path = format!(
106            "{}{}",
107            std::path::MAIN_SEPARATOR,
108            [
109                "var",
110                "log",
111                "pods",
112                "sandbox0-ns_sandbox0-name_sandbox0-uid",
113                "sandbox0-container0-name",
114                "1.log",
115            ]
116            .iter()
117            .collect::<PathBuf>()
118            .into_os_string()
119            .into_string()
120            .unwrap()
121        );
122        let s_path = path.as_str();
123        let cases = vec![
124            // Valid inputs.
125            (
126                s_path,
127                Some(LogFileInfo {
128                    pod_namespace: "sandbox0-ns",
129                    pod_name: "sandbox0-name",
130                    pod_uid: "sandbox0-uid",
131                    container_name: "sandbox0-container0-name",
132                }),
133            ),
134            // Invalid inputs.
135            ("/var/log/pods/other", None),
136            ("qwe", None),
137            ("", None),
138        ];
139
140        for (input, expected) in cases.into_iter() {
141            assert_eq!(parse_log_file_path(input), expected);
142        }
143    }
144}