Skip to content

Commit

Permalink
client: Expose a Config constructor to support legacy configurations
Browse files Browse the repository at this point in the history
The `Config::from_cluster_env` constructor is misleadingly named: it
doesn't use the environment, it uses the default cluster configurations.

This change deprecates the `Config::from_cluster_env` constructor in
favor of `Config::load_in_cluster`. An additional constructor,
`Config::load_in_cluster_from_legacy_env`, uses the
`KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT` environment
variables to match client-go's behavior.

This changes does NOT alter the default inferred configuration in any
way. It simply allows users to opt-in to using the old behavior.

Related to kubernetes/kubernetes#112263
Closes kube-rs#1000

Signed-off-by: Oliver Gould <ver@buoyant.io>
  • Loading branch information
olix0r committed Sep 7, 2022
1 parent 95397a5 commit 4e3743e
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 9 deletions.
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",
"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)
}

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()
}

/// 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

0 comments on commit 4e3743e

Please sign in to comment.