kube_client/config/
file_loader.rs

1use super::{
2    file_config::{AuthInfo, Cluster, Context, Kubeconfig},
3    KubeconfigError,
4};
5
6/// KubeConfigOptions stores options used when loading kubeconfig file.
7#[derive(Default, Clone)]
8pub struct KubeConfigOptions {
9    /// The named context to load
10    pub context: Option<String>,
11    /// The cluster to load
12    pub cluster: Option<String>,
13    /// The user to load
14    pub user: Option<String>,
15}
16
17/// ConfigLoader loads current context, cluster, and authentication information
18/// from a kubeconfig file.
19#[derive(Clone, Debug)]
20pub struct ConfigLoader {
21    pub current_context: Context,
22    pub cluster: Cluster,
23    pub user: AuthInfo,
24}
25
26impl ConfigLoader {
27    /// Returns a config loader based on the cluster information from the kubeconfig file.
28    pub async fn new_from_options(options: &KubeConfigOptions) -> Result<Self, KubeconfigError> {
29        let config = Kubeconfig::read()?;
30        let loader = Self::load(
31            config,
32            options.context.as_ref(),
33            options.cluster.as_ref(),
34            options.user.as_ref(),
35        )
36        .await?;
37
38        Ok(loader)
39    }
40
41    pub async fn new_from_kubeconfig(
42        config: Kubeconfig,
43        options: &KubeConfigOptions,
44    ) -> Result<Self, KubeconfigError> {
45        let loader = Self::load(
46            config,
47            options.context.as_ref(),
48            options.cluster.as_ref(),
49            options.user.as_ref(),
50        )
51        .await?;
52
53        Ok(loader)
54    }
55
56    pub async fn load(
57        config: Kubeconfig,
58        context: Option<&String>,
59        cluster: Option<&String>,
60        user: Option<&String>,
61    ) -> Result<Self, KubeconfigError> {
62        let context_name = if let Some(name) = context {
63            name
64        } else if let Some(name) = &config.current_context {
65            name
66        } else {
67            return Err(KubeconfigError::CurrentContextNotSet);
68        };
69
70        let current_context = config
71            .contexts
72            .iter()
73            .find(|named_context| &named_context.name == context_name)
74            .and_then(|named_context| named_context.context.clone())
75            .ok_or_else(|| KubeconfigError::LoadContext(context_name.clone()))?;
76
77        let cluster_name = cluster.unwrap_or(&current_context.cluster);
78        let cluster = config
79            .clusters
80            .iter()
81            .find(|named_cluster| &named_cluster.name == cluster_name)
82            .and_then(|named_cluster| named_cluster.cluster.clone())
83            .ok_or_else(|| KubeconfigError::LoadClusterOfContext(cluster_name.clone()))?;
84
85        let user_name = user.or_else(|| current_context.user.as_ref());
86
87        // client-go doesn't fail on empty/missing user, so we don't either
88        // see https://github.com/kube-rs/kube/issues/1594
89        let mut auth_info = if let Some(user) = user_name {
90            config
91                .auth_infos
92                .iter()
93                .find(|named_user| &named_user.name == user)
94                .and_then(|named_user| named_user.auth_info.clone())
95                .unwrap_or_else(AuthInfo::default)
96        } else {
97            AuthInfo::default()
98        };
99
100        if let Some(exec_config) = &mut auth_info.exec {
101            if exec_config.provide_cluster_info {
102                exec_config.cluster = Some((&cluster).try_into()?);
103            }
104        }
105
106        Ok(ConfigLoader {
107            current_context,
108            cluster,
109            user: auth_info,
110        })
111    }
112
113    pub fn ca_bundle(&self) -> Result<Option<Vec<Vec<u8>>>, KubeconfigError> {
114        if let Some(bundle) = self.cluster.load_certificate_authority()? {
115            Ok(Some(
116                super::certs(&bundle).map_err(KubeconfigError::ParseCertificates)?,
117            ))
118        } else {
119            Ok(None)
120        }
121    }
122
123    pub fn proxy_url(&self) -> Result<Option<http::Uri>, KubeconfigError> {
124        let nonempty = |o: Option<String>| o.filter(|s| !s.is_empty());
125
126        if let Some(proxy) = nonempty(self.cluster.proxy_url.clone())
127            .or_else(|| nonempty(std::env::var("HTTPS_PROXY").ok()))
128            .or_else(|| nonempty(std::env::var("https_proxy").ok()))
129        {
130            Ok(Some(
131                proxy
132                    .parse::<http::Uri>()
133                    .map_err(KubeconfigError::ParseProxyUrl)?,
134            ))
135        } else {
136            Ok(None)
137        }
138    }
139}