|
| 1 | +use http; |
| 2 | +use k8s_openapi::api::core::v1::Node; |
| 3 | +use kube::{ |
| 4 | + Api, |
| 5 | + api::{ListParams, ResourceExt}, |
| 6 | + client::Client, |
| 7 | +}; |
| 8 | +use serde::Deserialize; |
| 9 | +use snafu::{OptionExt, ResultExt, Snafu}; |
| 10 | + |
| 11 | +use crate::commons::networking::DomainName; |
| 12 | + |
| 13 | +#[derive(Debug, Snafu)] |
| 14 | +pub enum Error { |
| 15 | + #[snafu(display("failed to list nodes"))] |
| 16 | + ListNodes { source: kube::Error }, |
| 17 | + |
| 18 | + #[snafu(display("failed to build request for url path \"{url_path}\""))] |
| 19 | + BuildConfigzRequest { |
| 20 | + source: http::Error, |
| 21 | + url_path: String, |
| 22 | + }, |
| 23 | + |
| 24 | + #[snafu(display("failed to fetch kubelet config from node {node:?}"))] |
| 25 | + FetchNodeKubeletConfig { source: kube::Error, node: String }, |
| 26 | + |
| 27 | + #[snafu(display("failed to fetch `kubeletconfig` JSON key from configz response"))] |
| 28 | + KubeletConfigJsonKey, |
| 29 | + |
| 30 | + #[snafu(display("failed to deserialize kubelet config JSON"))] |
| 31 | + KubeletConfigJson { source: serde_json::Error }, |
| 32 | + |
| 33 | + #[snafu(display( |
| 34 | + "empty Kubernetes nodes list. At least one node is required to fetch the cluster domain from the kubelet config" |
| 35 | + ))] |
| 36 | + EmptyKubernetesNodesList, |
| 37 | +} |
| 38 | + |
| 39 | +#[derive(Debug, Deserialize)] |
| 40 | +#[serde(rename_all = "camelCase")] |
| 41 | +struct ProxyConfigResponse { |
| 42 | + kubeletconfig: KubeletConfig, |
| 43 | +} |
| 44 | + |
| 45 | +#[derive(Debug, Deserialize)] |
| 46 | +#[serde(rename_all = "camelCase")] |
| 47 | +pub struct KubeletConfig { |
| 48 | + pub cluster_domain: DomainName, |
| 49 | +} |
| 50 | + |
| 51 | +impl KubeletConfig { |
| 52 | + /// Fetches the kubelet configuration from the "first" node in the Kubernetes cluster. |
| 53 | + pub async fn fetch(client: &Client) -> Result<Self, Error> { |
| 54 | + let api: Api<Node> = Api::all(client.clone()); |
| 55 | + let nodes = api |
| 56 | + .list(&ListParams::default()) |
| 57 | + .await |
| 58 | + .context(ListNodesSnafu)?; |
| 59 | + let node = nodes.iter().next().context(EmptyKubernetesNodesListSnafu)?; |
| 60 | + let node_name = node.name_any(); |
| 61 | + |
| 62 | + let url_path = format!("/api/v1/nodes/{node_name}/proxy/configz"); |
| 63 | + let req = http::Request::get(url_path.clone()) |
| 64 | + .body(Default::default()) |
| 65 | + .context(BuildConfigzRequestSnafu { url_path })?; |
| 66 | + |
| 67 | + let resp = client |
| 68 | + .request::<ProxyConfigResponse>(req) |
| 69 | + .await |
| 70 | + .context(FetchNodeKubeletConfigSnafu { node: node_name })?; |
| 71 | + |
| 72 | + Ok(resp.kubeletconfig) |
| 73 | + } |
| 74 | +} |
0 commit comments