Skip to content

Commit

Permalink
documentation fixes, renames and caveats to kubelet interface
Browse files Browse the repository at this point in the history
since it shows up in `Client::` impls it should have caveats.
have linked to alternatives, prefixed the names with kubelet_
(which is a little redundant, but it helps here).

also documented and split up a few related things i found with just doc.

Signed-off-by: clux <[email protected]>
  • Loading branch information
clux committed Mar 22, 2024
1 parent 02ad7f3 commit 237e78d
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 61 deletions.
1 change: 1 addition & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ rustls-tls = ["kube/client", "kube/rustls-tls"]
runtime = ["kube/runtime", "kube/unstable-runtime"]
socks5 = ["kube/socks5"]
refresh = ["kube/oauth", "kube/oidc"]
kubelet-debug = ["kube/kubelet-debug"]
ws = ["kube/ws"]
latest = ["k8s-openapi/latest"]

Expand Down
41 changes: 21 additions & 20 deletions examples/pod_log_kubelet_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use futures::AsyncBufReadExt;
use hyper::Uri;
use kube::{
api::{Api, DeleteParams, ResourceExt},
core::{node_proxy::KubeletDebugParams, subresource::LogParams},
core::{kubelet_debug::KubeletDebugParams, subresource::LogParams},
Client, Config,
};
use serde_json::json;
Expand All @@ -16,7 +16,7 @@ async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();

let client = Client::try_default().await?;
let pods: Api<Pod> = Api::default_namespaced(client);
let pods: Api<Pod> = Api::namespaced(client, "default");

// create busybox pod that's alive for at most 30s
let p: Pod = serde_json::from_value(json!({
Expand Down Expand Up @@ -46,35 +46,36 @@ async fn main() -> anyhow::Result<()> {
// wait for container to finish
tokio::time::sleep(std::time::Duration::from_secs(2)).await;

// Grab logs directly via the kubelet_debug interface
kubelet_log().await?;

// Delete it
info!("deleting");
let _ = pods.delete("example", &DeleteParams::default()).await?;

Ok(())
}

async fn kubelet_log() -> anyhow::Result<()> {
// Create a client for node proxy
// This uses an insecure configuration to talk to the kubelet directly
// and assumes 10250 is a reachable kubelet port (k3d default)
let mut config = Config::infer().await?;
config.accept_invalid_certs = true;
config.cluster_url = "https://localhost:10250".to_string().parse::<Uri>().unwrap();
let client: Client = config.try_into()?;

// Get logs directly from the node, bypassing the kube-apiserver
let kp = KubeletDebugParams {
name: "example",
namespace: "default",
..Default::default()
};
let lp = LogParams::default();
let mut logs_stream = client
.node_logs(
&KubeletDebugParams {
name: "example",
namespace: "default",
..Default::default()
},
"busybox",
&lp,
)
.await?
.lines();

let mut logs_stream = client.kubelet_node_logs(&kp, "busybox", &lp).await?.lines();

while let Some(line) = logs_stream.try_next().await? {
println!("{line}");
}

// Delete it
info!("deleting");
let _ = pods.delete("example", &DeleteParams::default()).await?;

Ok(())
}
12 changes: 12 additions & 0 deletions kube-client/src/api/subresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ where
// ----------------------------------------------------------------------------

/// Marker trait for objects that support the ephemeral containers sub resource.
///
/// See [`Api::get_ephemeral_containers`] et al.
pub trait Ephemeral {}

impl Ephemeral for k8s_openapi::api::core::v1::Pod {}
Expand Down Expand Up @@ -384,6 +386,8 @@ fn log_path() {
}

/// Marker trait for objects that has logs
///
/// See [`Api::logs`] and [`Api::log_stream`] for usage.
pub trait Log {}

impl Log for k8s_openapi::api::core::v1::Pod {}
Expand Down Expand Up @@ -446,6 +450,8 @@ fn evict_path() {
}

/// Marker trait for objects that can be evicted
///
/// See [`Api::evic`] for usage
pub trait Evict {}

impl Evict for k8s_openapi::api::core::v1::Pod {}
Expand Down Expand Up @@ -484,6 +490,8 @@ fn attach_path() {
}

/// Marker trait for objects that has attach
///
/// See [`Api::attach`] for usage
#[cfg(feature = "ws")]
#[cfg_attr(docsrs, doc(cfg(feature = "ws")))]
pub trait Attach {}
Expand Down Expand Up @@ -530,6 +538,8 @@ fn exec_path() {
}

/// Marker trait for objects that has exec
///
/// See [`Api::exec`] for usage.
#[cfg(feature = "ws")]
#[cfg_attr(docsrs, doc(cfg(feature = "ws")))]
pub trait Execute {}
Expand Down Expand Up @@ -577,6 +587,8 @@ fn portforward_path() {
}

/// Marker trait for objects that has portforward
///
/// See [`Api::portforward`] for usage.
#[cfg(feature = "ws")]
pub trait Portforward {}

Expand Down
50 changes: 26 additions & 24 deletions kube-client/src/client/kubelet_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,40 @@ use std::fmt::Debug;

/// Methods to access debug endpoints directly on `kubelet`
///
/// These are analogous to the `Pod` api methods for [`Execute`], [`Attach`], and [`Portforward`].
/// These provide analogous methods to the `Pod` api methods for [`Execute`](crate::api::Exec), [`Attach`](crate::api::Attach), and [`Portforward`](crate::api::Portforward).
/// Service account must have `nodes/proxy` access, and
/// the debug handlers must be enabled either via `--enable-debugging-handlers ` or in the [kubelet config](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/#kubelet-config-k8s-io-v1beta1-KubeletConfiguration).
/// See the [kubelet source](https://github.com/kubernetes/kubernetes/blob/b3926d137cd2964cd3a04088ded30845910547b1/pkg/kubelet/server/server.go#L454), and [kubelet reference](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) for more info.
///
/// ## Warning
/// These methods require direct and insecure access to `kubelet` and is only available under the `kubelet_debug` feature.
/// End-to-end usage is explored in the [pod_log_node_proxy](./examples/pod_log_node_proxy.rs) example.
/// These methods require direct, and **insecure access** to `kubelet` and is only available under the `kubelet_debug` feature.
/// End-to-end usage is explored in the [pod_log_kubelet_debug](https://github.com/kube-rs/kube/blob/main/examples/pod_log_kubelet_debug.rs) example.
#[cfg(feature = "kubelet-debug")]
impl Client {
/// Attach to pod directly from the node
///
/// ## Warning
/// This method uses the insecure `kubelet_debug` interface.
pub async fn node_attach(
/// This method uses the insecure `kubelet_debug` interface. See [`Api::attach`](crate::Api::attach) for the normal interface.
pub async fn kubelet_node_attach(
&self,
kubelet_debug_params: &KubeletDebugParams<'_>,
kubelet_params: &KubeletDebugParams<'_>,
container: &str,
ap: &AttachParams,
) -> Result<AttachedProcess> {
let mut req =
Request::node_attach(kubelet_debug_params, container, ap).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("node_attach");
Request::kubelet_node_attach(kubelet_params, container, ap).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("kubelet_node_attach");
let stream = self.connect(req).await?;
Ok(AttachedProcess::new(stream, ap))
}

/// Execute a command in a pod directly from the node
///
/// ## Warning
/// This method uses the insecure `kubelet_debug` interface.
pub async fn node_exec<I, T>(
/// This method uses the insecure `kubelet_debug` interface. See [`Api::exec`](crate::Api::exec) for the normal interface.
pub async fn kubelet_node_exec<I, T>(
&self,
kubelet_debug_params: &KubeletDebugParams<'_>,
kubelet_params: &KubeletDebugParams<'_>,
container: &str,
command: I,
ap: &AttachParams,
Expand All @@ -50,40 +50,42 @@ impl Client {
I: IntoIterator<Item = T> + Debug,
T: Into<String>,
{
let mut req =
Request::node_exec(kubelet_debug_params, container, command, ap).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("node_exec");
let mut req = Request::kubelet_node_exec(kubelet_params, container, command, ap)
.map_err(Error::BuildRequest)?;
req.extensions_mut().insert("kubelet_node_exec");
let stream = self.connect(req).await?;
Ok(AttachedProcess::new(stream, ap))

Check warning on line 57 in kube-client/src/client/kubelet_debug.rs

View check run for this annotation

Codecov / codecov/patch

kube-client/src/client/kubelet_debug.rs#L53-L57

Added lines #L53 - L57 were not covered by tests
}

/// Forward ports of a pod directly from the node
///
/// ## Warning
/// This method uses the insecure `kubelet_debug` interface.
pub async fn node_portforward(
/// This method uses the insecure `kubelet_debug` interface. See [`Api::portforward`](crate::Api::portforward) for the normal interface.
pub async fn kubelet_node_portforward(
&self,
kubelet_debug_params: &KubeletDebugParams<'_>,
kubelet_params: &KubeletDebugParams<'_>,
ports: &[u16],
) -> Result<Portforwarder> {
let mut req = Request::node_portforward(kubelet_debug_params, ports).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("node_portforward");
let mut req =
Request::kubelet_node_portforward(kubelet_params, ports).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("kubelet_node_portforward");
let stream = self.connect(req).await?;
Ok(Portforwarder::new(stream, ports))
}

/// Stream logs directly from node
///
/// ## Warning
/// This method uses the insecure `kubelet_debug` interface.
pub async fn node_logs(
/// This method uses the insecure `kubelet_debug` interface. See [`Api::log_stream`](crate::Api::log_stream) for the normal interface.
pub async fn kubelet_node_logs(
&self,
kubelet_debug_params: &KubeletDebugParams<'_>,
kubelet_params: &KubeletDebugParams<'_>,
container: &str,
lp: &LogParams,
) -> Result<impl AsyncBufRead> {
let mut req = Request::node_logs(kubelet_debug_params, container, lp).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("node_log");
let mut req =
Request::kubelet_node_logs(kubelet_params, container, lp).map_err(Error::BuildRequest)?;
req.extensions_mut().insert("kubelet_node_log");
self.request_stream(req).await
}
}
5 changes: 4 additions & 1 deletion kube-client/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ pub use auth::Error as AuthError;
pub use config_ext::ConfigExt;
pub mod middleware;

#[cfg(feature = "kubelet-debug")] mod kubelet_debug;
#[cfg(any(feature = "rustls-tls", feature = "openssl-tls"))] mod tls;

#[cfg(feature = "openssl-tls")]
Expand All @@ -54,6 +53,10 @@ pub use auth::oidc_errors;

#[cfg(feature = "ws")] pub use upgrade::UpgradeConnectionError;

#[cfg(feature = "kubelet-debug")]
#[cfg_attr(docsrs, doc(cfg(feature = "kubelet-debug")))]
mod kubelet_debug;

pub use builder::{ClientBuilder, DynBody};

/// Client for connecting with a Kubernetes cluster.
Expand Down
17 changes: 9 additions & 8 deletions kube-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ pub use kube_core as core;
#[cfg(test)]
mod test {
#![allow(unused_imports)]

Check warning on line 133 in kube-client/src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

item has both inner and outer attributes

warning: item has both inner and outer attributes --> kube-client/src/lib.rs:130:1 | 130 | / #[cfg(all(feature = "client", feature = "config"))] 131 | | #[cfg(test)] 132 | | mod test { 133 | | #![allow(unused_imports)] | |_____________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mixed_attributes_style = note: `#[warn(clippy::mixed_attributes_style)]` on by default
#[cfg(feature = "kubelet-debug")]
use crate::core::kubelet_debug::KubeletDebugParams;
use crate::{
api::{AttachParams, AttachedProcess},
client::ConfigExt,
Expand Down Expand Up @@ -767,7 +765,10 @@ mod test {
#[ignore = "needs kubelet debug methods"]
#[cfg(feature = "kubelet-debug")]
async fn pod_can_exec_and_write_to_stdin_from_node_proxy() -> Result<(), Box<dyn std::error::Error>> {
use crate::api::{DeleteParams, ListParams, Patch, PatchParams, WatchEvent};
use crate::{
api::{DeleteParams, ListParams, Patch, PatchParams, WatchEvent},
core::kubelet_debug::KubeletDebugParams,
};

let client = Client::try_default().await?;
let pods: Api<Pod> = Api::default_namespaced(client);
Expand Down Expand Up @@ -820,12 +821,12 @@ mod test {
let mut config = Config::infer().await?;
config.accept_invalid_certs = true;
config.cluster_url = "https://localhost:10250".to_string().parse::<Uri>().unwrap();
let client: Client = config.try_into()?;
let kubelet_client: Client = config.try_into()?;

// Verify exec works and we can get the output
{
let mut attached = client
.node_exec(
let mut attached = kubelet_client
.kubelet_node_exec(
&KubeletDebugParams {
name: "busybox-kube2",
namespace: "default",
Expand All @@ -850,8 +851,8 @@ mod test {
// Verify we can write to Stdin
{
use tokio::io::AsyncWriteExt;
let mut attached = client
.node_exec(
let mut attached = kubelet_client
.kubelet_node_exec(
&KubeletDebugParams {
name: "busybox-kube2",
namespace: "default",
Expand Down
16 changes: 8 additions & 8 deletions kube-core/src/kubelet_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl KubeletDebugParams<'_> {

impl Request {
/// Attach to pod directly from the node
pub fn node_attach(
pub fn kubelet_node_attach(
kubelet_debug_params: &KubeletDebugParams<'_>,
container: &str,
ap: &AttachParams,
Expand All @@ -49,7 +49,7 @@ impl Request {
}

/// Execute a command in a pod directly from the node
pub fn node_exec<I, T>(
pub fn kubelet_node_exec<I, T>(
kubelet_debug_params: &KubeletDebugParams<'_>,
container: &str,
command: I,
Expand All @@ -74,7 +74,7 @@ impl Request {
}

/// Forward ports of a pod directly from the node
pub fn node_portforward(
pub fn kubelet_node_portforward(
kubelet_debug_params: &KubeletDebugParams<'_>,
ports: &[u16],
) -> Result<http::Request<Vec<u8>>, Error> {
Expand Down Expand Up @@ -110,7 +110,7 @@ impl Request {
}

/// Stream logs directly from node
pub fn node_logs(
pub fn kubelet_node_logs(
kubelet_debug_params: &KubeletDebugParams<'_>,
container: &str,
lp: &LogParams,
Expand Down Expand Up @@ -169,7 +169,7 @@ mod test {
};
#[test]
fn node_attach_test() {
let req = Request::node_attach(
let req = Request::kubelet_node_attach(
&KubeletDebugParams {
name: "some-name",
namespace: "some-namespace",
Expand All @@ -187,7 +187,7 @@ mod test {

#[test]
fn node_exec_test() {
let req = Request::node_exec(
let req = Request::kubelet_node_exec(
&KubeletDebugParams {
name: "some-name",
namespace: "some-namespace",
Expand All @@ -212,7 +212,7 @@ mod test {
timestamps: true,
..Default::default()
};
let req = Request::node_logs(
let req = Request::kubelet_node_logs(
&KubeletDebugParams {
name: "some-name",
namespace: "some-namespace",
Expand All @@ -230,7 +230,7 @@ mod test {

#[test]
fn node_portforward_test() {
let req = Request::node_portforward(
let req = Request::kubelet_node_portforward(
&KubeletDebugParams {
name: "some-name",
namespace: "some-namespace",
Expand Down

0 comments on commit 237e78d

Please sign in to comment.