Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config: New incluster and incluster_dns constructors #1001

Merged
merged 10 commits into from Sep 9, 2022
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Expand Up @@ -8,7 +8,7 @@

// These extensions are loaded for all users by default.
"extensions": [
"matklad.rust-analyzer",
"rust-lang.rust-analyzer",
clux marked this conversation as resolved.
Show resolved Hide resolved
"NathanRidley.autotrim",
"samverschueren.final-newline",
"tamasfe.even-better-toml",
Expand Down
43 changes: 43 additions & 0 deletions kube-client/src/config/incluster_config.rs
Expand Up @@ -16,6 +16,10 @@ pub enum Error {
#[error("failed to read a certificate bundle: {0}")]
ReadCertificateBundle(#[source] std::io::Error),

/// Failed to parse cluster port value
#[error("failed to parse cluster port: {0}")]
ParseClusterPort(#[source] std::num::ParseIntError),

/// Failed to parse cluster url
#[error("failed to parse cluster url: {0}")]
ParseClusterUrl(#[source] http::uri::InvalidUri),
Expand All @@ -29,6 +33,45 @@ pub fn kube_dns() -> http::Uri {
http::Uri::from_static("https://kubernetes.default.svc/")
}

pub fn try_kube_from_legacy_env_or_dns() -> Result<http::Uri, Error> {
// client-go requires that both environment variables are set, so we do too.
let host = match std::env::var("KUBERNETES_SERVICE_HOST") {
Ok(h) => h,
Err(_) => return Ok(kube_dns()),
};
let port = match std::env::var("KUBERNETES_SERVICE_PORT") {
Ok(p) => p.parse::<u16>().map_err(Error::ParseClusterPort)?,
Err(_) => return Ok(kube_dns()),
};

// Format a host and, if not using 443, a port.
//
// Ensure that IPv6 addresses are properly bracketed.
let uri = match host.parse::<std::net::IpAddr>() {
Ok(ip) => {
if port == 443 {
if ip.is_ipv6() {
format!("https://[{ip}]")
} else {
format!("https://{ip}")
}
} else {
let addr = std::net::SocketAddr::new(ip, port);
format!("https://{addr}")
}
}
Err(_) => {
if port == 443 {
format!("https://{host}")
} else {
format!("https://{host}:{port}")
}
}
};

uri.parse().map_err(Error::ParseClusterUrl)
olix0r marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn token_file() -> String {
SERVICE_TOKENFILE.to_owned()
}
Expand Down
44 changes: 36 additions & 8 deletions kube-client/src/config/mod.rs
Expand Up @@ -195,7 +195,7 @@ impl Config {
"no local config found, falling back to local in-cluster config"
);

Self::from_cluster_env().map_err(|in_cluster_err| InferConfigError {
Self::load_in_cluster().map_err(|in_cluster_err| InferConfigError {
in_cluster: in_cluster_err,
kubeconfig: kubeconfig_err,
})?
Expand All @@ -206,13 +206,42 @@ impl Config {
Ok(config)
}

/// Create configuration from the cluster's environment variables
///
/// This follows the standard [API Access from a Pod](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod)
/// and relies on you having the service account's token mounted,
/// as well as having given the service account rbac access to do what you need.
/// Replaced by [`Self::load_in_cluster`].
#[deprecated(since = "0.75.0", note = "use Config::load_in_cluster")]
pub fn from_cluster_env() -> Result<Self, InClusterError> {
let cluster_url = incluster_config::kube_dns();
Self::load_in_cluster()
}
olix0r marked this conversation as resolved.
Show resolved Hide resolved

/// Load the default in-cluster config.
///
/// This follows the standard [API Access from a Pod][docs] policy of using
/// `https://kubernetes.default.svc` to connect to the Kubernetes API
/// server. A service account's token must be available in
/// `/var/run/secrets/kubernetes.io/serviceaccount/`.
///
/// [docs]: https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod
pub fn load_in_cluster() -> Result<Self, InClusterError> {
Self::load_in_cluster_with_uri(incluster_config::kube_dns())
}

/// Load an in-cluster config use the legacy `KUBERNETES_SERVICE_HOST` and
/// `KUBERNETES_SERVICE_PORT` environment variables.
///
/// This matches the behavior of [client-go], but it will fallback to using
/// `kubernetes.default.svc` if the environment is not set.
///
/// A service account's token must be available in
/// `/var/run/secrets/kubernetes.io/serviceaccount/`.
///
/// This will be deprecated/removed when client-go's behavior changes.
///
/// [client-go]: https://github.com/kubernetes/kubernetes/blob/67bde9a1023d1805e33d698b28aa6fad991dfb39/staging/src/k8s.io/client-go/rest/config.go#L507-L541
pub fn load_in_cluster_from_legacy_env() -> Result<Self, InClusterError> {
let uri = incluster_config::try_kube_from_legacy_env_or_dns()?;
Self::load_in_cluster_with_uri(uri)
}

fn load_in_cluster_with_uri(cluster_url: http::uri::Uri) -> Result<Self, InClusterError> {
let default_namespace = incluster_config::load_default_ns()?;
let root_cert = incluster_config::load_cert()?;

Expand Down Expand Up @@ -378,7 +407,6 @@ pub use file_config::{
NamedContext, NamedExtension, Preferences,
};


#[cfg(test)]
mod tests {
#[cfg(not(feature = "client"))] // want to ensure this works without client features
Expand Down