Skip to main content

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
94pub fn cert_file() -> &'static str {
95    SERVICE_CERTFILE
96}
97
98/// Returns certification from specified path in cluster.
99pub fn load_cert() -> Result<Vec<Vec<u8>>, Error> {
100    let certs = std::fs::read(SERVICE_CERTFILE).map_err(Error::ReadCertificateBundle)?;
101    super::certs(&certs).map_err(Error::ParseCertificates)
102}
103
104/// Returns the default namespace from specified path in cluster.
105pub fn load_default_ns() -> Result<String, Error> {
106    std::fs::read_to_string(SERVICE_DEFAULT_NS).map_err(Error::ReadDefaultNamespace)
107}
108
109#[test]
110fn test_kube_name() {
111    assert_eq!(
112        try_uri("fake.io", 8080).unwrap().to_string(),
113        "https://fake.io:8080/"
114    );
115}
116
117#[test]
118fn test_kube_name_default_port() {
119    assert_eq!(try_uri("kubernetes.default.svc", 443).unwrap(), kube_dns())
120}
121
122#[test]
123fn test_kube_ipv4() {
124    assert_eq!(
125        try_uri("10.11.12.13", 6443).unwrap().to_string(),
126        "https://10.11.12.13:6443/"
127    );
128}
129
130#[test]
131fn test_kube_ipv4_default_port() {
132    assert_eq!(
133        try_uri("10.11.12.13", 443).unwrap().to_string(),
134        "https://10.11.12.13/"
135    );
136}
137
138#[test]
139fn test_kube_ipv6() {
140    assert_eq!(
141        try_uri("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 6443)
142            .unwrap()
143            .to_string(),
144        "https://[2001:db8:85a3::8a2e:370:7334]:6443/"
145    );
146}
147
148#[test]
149fn test_kube_ipv6_default_port() {
150    assert_eq!(
151        try_uri("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 443)
152            .unwrap()
153            .to_string(),
154        "https://[2001:db8:85a3::8a2e:370:7334]/"
155    );
156}