kube_client/config/
incluster_config.rs

1use std::env;
2use thiserror::Error;
3
4const SERVICE_HOSTENV: &str = "KUBERNETES_SERVICE_HOST";
5const SERVICE_PORTENV: &str = "KUBERNETES_SERVICE_PORT";
6
7// Mounted credential files
8const SERVICE_TOKENFILE: &str = "/var/run/secrets/kubernetes.io/serviceaccount/token";
9const SERVICE_CERTFILE: &str = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
10const SERVICE_DEFAULT_NS: &str = "/var/run/secrets/kubernetes.io/serviceaccount/namespace";
11
12/// Errors from loading in-cluster config
13#[derive(Error, Debug)]
14pub enum Error {
15    /// Failed to read the default namespace for the service account
16    #[error("failed to read the default namespace: {0}")]
17    ReadDefaultNamespace(#[source] std::io::Error),
18
19    /// Failed to read the in-cluster environment variables
20    #[error("failed to read an incluster environment variable: {0}")]
21    ReadEnvironmentVariable(#[source] env::VarError),
22
23    /// Failed to read a certificate bundle
24    #[error("failed to read a certificate bundle: {0}")]
25    ReadCertificateBundle(#[source] std::io::Error),
26
27    /// Failed to parse cluster port value
28    #[error("failed to parse cluster port: {0}")]
29    ParseClusterPort(#[source] std::num::ParseIntError),
30
31    /// Failed to parse cluster url
32    #[error("failed to parse cluster url: {0}")]
33    ParseClusterUrl(#[source] http::uri::InvalidUri),
34
35    /// Failed to parse PEM-encoded certificates
36    #[error("failed to parse PEM-encoded certificates: {0}")]
37    ParseCertificates(#[source] pem::PemError),
38}
39
40/// Returns the URI of the Kubernetes API server using the in-cluster DNS name
41/// `kubernetes.default.svc`.
42pub(super) fn kube_dns() -> http::Uri {
43    http::Uri::from_static("https://kubernetes.default.svc/")
44}
45
46/// Returns the URI of the Kubernetes API server by reading the
47/// `KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT` environment
48/// variables.
49pub(super) fn try_kube_from_env() -> Result<http::Uri, Error> {
50    // client-go requires that both environment variables are set.
51    let host = env::var(SERVICE_HOSTENV).map_err(Error::ReadEnvironmentVariable)?;
52    let port = env::var(SERVICE_PORTENV)
53        .map_err(Error::ReadEnvironmentVariable)?
54        .parse::<u16>()
55        .map_err(Error::ParseClusterPort)?;
56
57    try_uri(&host, port)
58}
59
60fn try_uri(host: &str, port: u16) -> Result<http::Uri, Error> {
61    // Format a host and, if not using 443, a port.
62    //
63    // Ensure that IPv6 addresses are properly bracketed.
64    const HTTPS: &str = "https";
65    let uri = match host.parse::<std::net::IpAddr>() {
66        Ok(ip) => {
67            if port == 443 {
68                if ip.is_ipv6() {
69                    format!("{HTTPS}://[{ip}]")
70                } else {
71                    format!("{HTTPS}://{ip}")
72                }
73            } else {
74                let addr = std::net::SocketAddr::new(ip, port);
75                format!("{HTTPS}://{addr}")
76            }
77        }
78        Err(_) => {
79            if port == 443 {
80                format!("{HTTPS}://{host}")
81            } else {
82                format!("{HTTPS}://{host}:{port}")
83            }
84        }
85    };
86
87    uri.parse().map_err(Error::ParseClusterUrl)
88}
89
90pub fn token_file() -> String {
91    SERVICE_TOKENFILE.to_owned()
92}
93
94/// Returns certification from specified path in cluster.
95pub fn load_cert() -> Result<Vec<Vec<u8>>, Error> {
96    let certs = std::fs::read(SERVICE_CERTFILE).map_err(Error::ReadCertificateBundle)?;
97    super::certs(&certs).map_err(Error::ParseCertificates)
98}
99
100/// Returns the default namespace from specified path in cluster.
101pub fn load_default_ns() -> Result<String, Error> {
102    std::fs::read_to_string(SERVICE_DEFAULT_NS).map_err(Error::ReadDefaultNamespace)
103}
104
105#[test]
106fn test_kube_name() {
107    assert_eq!(
108        try_uri("fake.io", 8080).unwrap().to_string(),
109        "https://fake.io:8080/"
110    );
111}
112
113#[test]
114fn test_kube_name_default_port() {
115    assert_eq!(try_uri("kubernetes.default.svc", 443).unwrap(), kube_dns())
116}
117
118#[test]
119fn test_kube_ipv4() {
120    assert_eq!(
121        try_uri("10.11.12.13", 6443).unwrap().to_string(),
122        "https://10.11.12.13:6443/"
123    );
124}
125
126#[test]
127fn test_kube_ipv4_default_port() {
128    assert_eq!(
129        try_uri("10.11.12.13", 443).unwrap().to_string(),
130        "https://10.11.12.13/"
131    );
132}
133
134#[test]
135fn test_kube_ipv6() {
136    assert_eq!(
137        try_uri("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 6443)
138            .unwrap()
139            .to_string(),
140        "https://[2001:db8:85a3::8a2e:370:7334]:6443/"
141    );
142}
143
144#[test]
145fn test_kube_ipv6_default_port() {
146    assert_eq!(
147        try_uri("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 443)
148            .unwrap()
149            .to_string(),
150        "https://[2001:db8:85a3::8a2e:370:7334]/"
151    );
152}