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

Pod logs operation #50

Merged
merged 5 commits into from
Jul 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions examples/log_openapi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#[macro_use]
extern crate log;

use kube::{
api::{Api, Log, LogParams},
client::APIClient,
config,
};
use std::env;

fn main() -> Result<(), failure::Error> {
std::env::set_var("RUST_LOG", "info,kube=trace");
env_logger::init();
let config = config::load_kube_config().expect("failed to load kubeconfig");
let client = APIClient::new(config);

let mypod = match env::args().nth(1) {
Some(pod) => pod,
None => {
println!("Usage: log_openapi <pod>");
return Ok(());
}
};

// Manage pods
let pods = Api::v1Pod(client);;
let lp = LogParams::default();
let plog = pods.within("default").log(&mypod, &lp)?;
println!("Got pod {} log: {}", &mypod, &plog);

Ok(())
}
4 changes: 3 additions & 1 deletion src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ pub use raw::{
PatchParams,
DeleteParams,
PropagationPolicy,
PatchStrategy
PatchStrategy,
LogParams
};

mod typed;
Expand All @@ -33,6 +34,7 @@ pub use typed::{
Scale,
ScaleSpec,
ScaleStatus,
Log
};

mod resource;
Expand Down
3 changes: 3 additions & 0 deletions src/api/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::api::{
RawApi,
Api,
Object,
Log
};
use crate::client::{
APIClient,
Expand Down Expand Up @@ -58,6 +59,8 @@ impl Api<Object<PodSpec, PodStatus>> {
}
}

impl Log for Api<Object<PodSpec, PodStatus>> {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than having a one line impl for one resource object that every user would need to import the trait of, could we instead:

  • have log always public but unimplemented!() or {} by default
  • have an extra impl Api<Object<PodSpec, PodStatus>> that overrides log

Then you have one less import required for users.

Copy link
Contributor Author

@liberwang1013 liberwang1013 Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implements log operation in this way, because I hope trait Log can be reused by other developers when they implement their crds.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that makes sense. Either way is fine, it would just be easier for users of the Api to not have to import the extra trait.


use k8s_openapi::api::core::v1::{ServiceSpec, ServiceStatus};
impl Api<Object<ServiceSpec, ServiceStatus>> {
pub fn v1Service(client: APIClient) -> Self {
Expand Down
67 changes: 67 additions & 0 deletions src/api/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,30 @@ pub enum PropagationPolicy {
Foreground,
}

#[derive(Default, Clone, Debug)]
pub struct LogParams {
/// The container for which to stream logs. Defaults to only container if there is one container in the pod.
pub container: Option<String>,
/// Follow the log stream of the pod. Defaults to false.
pub follow: bool,
/// If set, the number of bytes to read from the server before terminating the log output.
/// This may not display a complete final line of logging, and may return slightly more or slightly less than the specified limit.
pub limit_bytes: Option<i64>,
/// If 'true', then the output is pretty printed.
pub pretty: bool,
/// Return previous terminated container logs. Defaults to false.
pub previous: bool,
/// A relative time in seconds before the current time from which to show logs.
/// If this value precedes the time a pod was started, only logs since the pod start will be returned.
/// If this value is in the future, no logs will be returned. Only one of sinceSeconds or sinceTime may be specified.
pub since_seconds: Option<i64>,
/// If set, the number of lines from the end of the logs to show.
/// If not specified, logs are shown from the creation of the container or sinceSeconds or sinceTime
pub tail_lines: Option<i64>,
/// If true, add an RFC3339 or RFC3339Nano timestamp at the beginning of every line of log output. Defaults to false.
pub timestamps: bool,
}

/// Convenience methods found from API conventions
impl RawApi {
/// List a collection of a resource
Expand Down Expand Up @@ -552,7 +576,50 @@ impl RawApi {
let mut req = http::Request::put(urlstr);
Ok(req.body(data).context(ErrorKind::RequestBuild)?)
}
}

impl RawApi {
/// Get a pod logs
pub fn log(&self, name: &str, lp: &LogParams) -> Result<http::Request<Vec<u8>>> {
let base_url = self.make_url() + "/" + name + "/" + "log";
let mut qp = url::form_urlencoded::Serializer::new(base_url);

if let Some(container) = &lp.container {
qp.append_pair("container", &container);
}

if lp.follow {
qp.append_pair("follow", "true");
}

if let Some(limitBytes) = &lp.limit_bytes {
qp.append_pair("limitBytes", &limitBytes.to_string());
}

if lp.pretty {
qp.append_pair("pretty", "true");
}

if lp.previous {
qp.append_pair("previous", "true");
}

if let Some(sinceSeconds) = &lp.since_seconds {
qp.append_pair("sinceSeconds", &sinceSeconds.to_string());
}

if let Some(tailLines) = &lp.tail_lines {
qp.append_pair("tailLines", &tailLines.to_string());
}

if lp.timestamps {
qp.append_pair("timestamps", "true");
}

let urlstr = qp.finish();
let mut req = http::Request::get(urlstr);
Ok(req.body(vec![]).context(ErrorKind::RequestBuild)?)
}
}

#[test]
Expand Down
22 changes: 21 additions & 1 deletion src/api/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use crate::api::{
PostParams,
DeleteParams,
ListParams,
PatchParams
PatchParams,
LogParams
};
use crate::api::resource::{
ObjectList, Object, WatchEvent, KubeObject,
Expand Down Expand Up @@ -41,6 +42,16 @@ pub struct Api<K> {
pub(in crate::api) phantom: PhantomData<K>,
}

pub trait LogOperation {
fn log_operation(&self, name: &str, lp: &LogParams) -> Result<String>;
}

pub trait Log: LogOperation {
fn log(&self, name: &str, lp: &LogParams) -> Result<String> {
Ok(self.log_operation(name, lp)?)
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice setup between private and public methods that looks like it allows you to very easily implement the Log trait. Cool pattern. Unfortunately, if you follow do my suggestion, this won't be needed.


/// Expose same interface as Api for controlling scope/group/versions/ns
impl<K> Api<K> {
pub fn within(mut self, ns: &str) -> Self {
Expand Down Expand Up @@ -107,6 +118,15 @@ impl<K> Api<K> where
}
}

impl<K> LogOperation for Api<K> where
K: Clone + DeserializeOwned + KubeObject
{
fn log_operation(&self, name: &str, lp: &LogParams) -> Result<String> {
let req = self.api.log(name, lp)?;
self.client.request_text(req)
}
}

/// Scale spec from api::autoscaling::v1
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct ScaleSpec {
Expand Down
12 changes: 12 additions & 0 deletions src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ impl APIClient {
})
}

pub fn request_text(&self, request: http::Request<Vec<u8>>) -> Result<String>
{
let mut res : reqwest::Response = self.send(request)?;
trace!("{} {}", res.status().as_str(), res.url());
//trace!("Response Headers: {:?}", res.headers());
let s = res.status();
let text = res.text().context(ErrorKind::RequestParse)?;
res.error_for_status().map_err(|e| make_api_error(&text, e, &s))?;

Ok(text)
}

pub fn request_status<T>(&self, request: http::Request<Vec<u8>>) -> Result<Either<T, Status>>
where
T: DeserializeOwned,
Expand Down