Skip to content

Commit

Permalink
feature wrap config and client for #525
Browse files Browse the repository at this point in the history
  • Loading branch information
clux committed May 19, 2021
1 parent dcfde48 commit 6a928b5
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 100 deletions.
2 changes: 1 addition & 1 deletion examples/admission_controller.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use kube_core::{
use kube::core::{
admission::{AdmissionRequest, AdmissionResponse, AdmissionReview},
DynamicObject, ResourceExt,
};
Expand Down
2 changes: 2 additions & 0 deletions kube-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub use request::Request;
mod resource;
pub use resource::{Resource, ResourceExt};

pub mod response;

pub mod subresource;

#[macro_use] extern crate log;
Expand Down
69 changes: 69 additions & 0 deletions kube-core/src/response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use serde::Deserialize;
// TODO: replace with Status in k8s openapi?

/// A Kubernetes status object
#[allow(missing_docs)]
#[derive(Deserialize, Debug)]
pub struct Status {
// TODO: typemeta
// TODO: metadata that can be completely empty (listmeta...)
#[serde(default, skip_serializing_if = "String::is_empty")]
pub status: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub message: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub reason: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub details: Option<StatusDetails>,
#[serde(default, skip_serializing_if = "num::Zero::is_zero")]
pub code: u16,
}

/// Status details object on the [`Status`] object
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
#[allow(missing_docs)]
pub struct StatusDetails {
#[serde(default, skip_serializing_if = "String::is_empty")]
pub name: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub group: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub kind: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub uid: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub causes: Vec<StatusCause>,
#[serde(default, skip_serializing_if = "num::Zero::is_zero")]
pub retry_after_seconds: u32,
}

/// Status cause object on the [`StatusDetails`] object
#[derive(Deserialize, Debug)]
#[allow(missing_docs)]
pub struct StatusCause {
#[serde(default, skip_serializing_if = "String::is_empty")]
pub reason: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub message: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub field: String,
}


#[cfg(test)]
mod test {
use super::Status;

// ensure our status schema is sensible
#[test]
fn delete_deserialize_test() {
let statusresp = r#"{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Success","details":{"name":"some-app","group":"clux.dev","kind":"foos","uid":"1234-some-uid"}}"#;
let s: Status = serde_json::from_str::<Status>(statusresp).unwrap();
assert_eq!(s.details.unwrap().name, "some-app");

let statusnoname = r#"{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Success","details":{"group":"clux.dev","kind":"foos","uid":"1234-some-uid"}}"#;
let s2: Status = serde_json::from_str::<Status>(statusnoname).unwrap();
assert_eq!(s2.details.unwrap().name, ""); // optional probably better..
}
}
14 changes: 7 additions & 7 deletions kube-derive/src/custom_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
impl #rootident {
pub fn new(name: &str, spec: #ident) -> Self {
Self {
api_version: <#rootident as kube_core::Resource>::api_version(&()).to_string(),
kind: <#rootident as kube_core::Resource>::kind(&()).to_string(),
api_version: <#rootident as kube::Resource>::api_version(&()).to_string(),
kind: <#rootident as kube::Resource>::kind(&()).to_string(),
metadata: k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta {
name: Some(name.to_string()),
..Default::default()
Expand All @@ -184,7 +184,7 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea

let api_ver = format!("{}/{}", group, version);
let impl_resource = quote! {
impl kube_core::Resource for #rootident {
impl kube::Resource for #rootident {
type DynamicType = ();

fn group(_: &()) -> std::borrow::Cow<'_, str> {
Expand Down Expand Up @@ -223,8 +223,8 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
impl Default for #rootident {
fn default() -> Self {
Self {
api_version: <#rootident as kube_core::Resource>::api_version(&()).to_string(),
kind: <#rootident as kube_core::Resource>::kind(&()).to_string(),
api_version: <#rootident as kube::Resource>::api_version(&()).to_string(),
kind: <#rootident as kube::Resource>::kind(&()).to_string(),
metadata: k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta::default(),
spec: Default::default(),
#statusdef
Expand Down Expand Up @@ -358,8 +358,8 @@ pub(crate) fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStrea
.expect("valid custom resource from #[kube(attrs..)]")
}

pub fn api_resource() -> kube_core::api_resource::ApiResource {
kube_core::api_resource::ApiResource::erase::<Self>(&())
pub fn api_resource() -> kube::core::ApiResource {
kube::core::ApiResource::erase::<Self>(&())
}
}
};
Expand Down
20 changes: 11 additions & 9 deletions kube/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,23 @@ default = ["native-tls"]
native-tls = ["openssl", "hyper-tls", "tokio-native-tls"]
rustls-tls = ["hyper-rustls", "tokio-rustls"]
derive = ["kube-derive"]
client = ["hyper", "tower", "hyper-timeout", "pin-project", "chrono", "jsonpath_lib"]
config = ["pem", "dirs"]
jsonpatch = ["json-patch"]
ws = ["tokio-tungstenite", "rand", "kube-core/ws"]
oauth = ["tame-oauth"]
gzip = ["async-compression"]
admission = ["json-patch", "kube-core/admission"]

[package.metadata.docs.rs]
features = ["derive", "ws", "oauth", "jsonpatch"]
features = ["derive", "client", "config", "ws", "oauth", "jsonpatch"]
# Define the configuration attribute `docsrs`. Used to enable `doc_cfg` feature.
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
base64 = "0.13.0"
chrono = "0.4.19"
dirs = { package = "dirs-next", version = "2.0.0" }
chrono = { version = "0.4.19", optional = true }
dirs = { package = "dirs-next", optional = true, version = "2.0.0" }
serde = { version = "1.0.118", features = ["derive"] }
serde_json = "1.0.61"
serde_yaml = "0.8.17"
Expand All @@ -44,7 +46,7 @@ log = "0.4.11"
either = "1.6.1"
thiserror = "1.0.23"
futures = "0.3.8"
pem = "0.8.2"
pem = { version = "0.8.2", optional = true }
openssl = { version = "0.10.32", optional = true }
tokio-native-tls = { version = "0.3.0", optional = true }
tokio-rustls = { version = "0.22.0", features = ["dangerous_configuration"], optional = true }
Expand All @@ -53,18 +55,18 @@ tokio = { version = "1.0.1", features = ["time", "signal", "sync"] }
static_assertions = "1.1.0"
kube-derive = { path = "../kube-derive", version = "^0.54.0", optional = true }
kube-core = { path = "../kube-core", version = "^0.54.0"}
jsonpath_lib = "0.2.6"
jsonpath_lib = { version = "0.2.6", optional = true }
tokio-util = { version = "0.6.0", features = ["io", "codec"] }
json-patch = { version = "0.2.6", optional = true }
hyper = { version = "0.14.2", features = ["client", "http1", "stream", "tcp"] }
hyper = { version = "0.14.2", optional = true, features = ["client", "http1", "stream", "tcp"] }
hyper-tls = { version = "0.5.0", optional = true }
hyper-rustls = { version = "0.22.1", optional = true }
tokio-tungstenite = { version = "0.14.0", optional = true }
tower = { version = "0.4.6", features = ["buffer", "util"] }
tower = { version = "0.4.6", optional = true, features = ["buffer", "util"] }
async-compression = { version = "0.3.7", features = ["gzip", "tokio"], optional = true }
hyper-timeout = "0.4.1"
hyper-timeout = {version = "0.4.1", optional = true }
tame-oauth = { version = "0.4.7", features = ["gcp"], optional = true }
pin-project = "1.0.4"
pin-project = { version = "1.0.4", optional = true }
rand = { version = "0.8.3", optional = true }
tracing = "0.1.25"

Expand Down
4 changes: 2 additions & 2 deletions kube/src/api/core_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use serde::{de::DeserializeOwned, Serialize};
use std::fmt::Debug;
use tracing::instrument;

use crate::{api::Api, client::Status, Result};
use kube_core::{object::ObjectList, params::*, WatchEvent};
use crate::{api::Api, Result};
use kube_core::{object::ObjectList, params::*, response::Status, WatchEvent};

/// PUSH/PUT/POST/GET abstractions
impl<K> Api<K>
Expand Down
2 changes: 1 addition & 1 deletion kube/src/api/subresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use tracing::instrument;

use crate::{
api::{Api, Patch, PatchParams, PostParams},
client::Status,
Result,
};

use kube_core::response::Status;
pub use kube_core::subresource::{EvictParams, LogParams};

#[cfg(feature = "ws")] pub use kube_core::subresource::AttachParams;
Expand Down
71 changes: 2 additions & 69 deletions kube/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use futures::{self, Stream, StreamExt, TryStream, TryStreamExt};
use http::{self, Request, Response, StatusCode};
use hyper::Body;
use k8s_openapi::apimachinery::pkg::apis::meta::v1 as k8s_meta_v1;
use serde::{de::DeserializeOwned, Deserialize};
use kube_core::response::Status;
use serde::de::DeserializeOwned;
use serde_json::{self, Value};
use tokio_util::{
codec::{FramedRead, LinesCodec, LinesCodecError},
Expand Down Expand Up @@ -351,74 +352,6 @@ impl TryFrom<Config> for Client {
}
}

// TODO: replace with Status in k8s openapi?

/// A Kubernetes status object
#[allow(missing_docs)]
#[derive(Deserialize, Debug)]
pub struct Status {
// TODO: typemeta
// TODO: metadata that can be completely empty (listmeta...)
#[serde(default, skip_serializing_if = "String::is_empty")]
pub status: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub message: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub reason: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub details: Option<StatusDetails>,
#[serde(default, skip_serializing_if = "num::Zero::is_zero")]
pub code: u16,
}

/// Status details object on the [`Status`] object
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
#[allow(missing_docs)]
pub struct StatusDetails {
#[serde(default, skip_serializing_if = "String::is_empty")]
pub name: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub group: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub kind: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub uid: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub causes: Vec<StatusCause>,
#[serde(default, skip_serializing_if = "num::Zero::is_zero")]
pub retry_after_seconds: u32,
}

/// Status cause object on the [`StatusDetails`] object
#[derive(Deserialize, Debug)]
#[allow(missing_docs)]
pub struct StatusCause {
#[serde(default, skip_serializing_if = "String::is_empty")]
pub reason: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub message: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub field: String,
}

#[cfg(test)]
mod test {
use super::Status;

// ensure our status schema is sensible
#[test]
fn delete_deserialize_test() {
let statusresp = r#"{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Success","details":{"name":"some-app","group":"clux.dev","kind":"foos","uid":"1234-some-uid"}}"#;
let s: Status = serde_json::from_str::<Status>(statusresp).unwrap();
assert_eq!(s.details.unwrap().name, "some-app");

let statusnoname = r#"{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Success","details":{"group":"clux.dev","kind":"foos","uid":"1234-some-uid"}}"#;
let s2: Status = serde_json::from_str::<Status>(statusnoname).unwrap();
assert_eq!(s2.details.unwrap().name, ""); // optional probably better..
}
}

#[cfg(feature = "ws")]
// Verify upgrade response according to RFC6455.
// Based on `tungstenite` and added subprotocol verification.
Expand Down
2 changes: 1 addition & 1 deletion kube/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod utils;
use crate::{error::ConfigError, Result};
use file_loader::ConfigLoader;
pub use file_loader::KubeConfigOptions;
pub(crate) use utils::read_file_to_string;
#[allow(unused_imports)] pub(crate) use utils::read_file_to_string;

use http::header::HeaderMap;

Expand Down
7 changes: 6 additions & 1 deletion kube/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Error handling in [`kube`][crate]
#![allow(unused_imports)]
use http::header::InvalidHeaderValue;
pub use kube_core::ErrorResponse;
use std::path::PathBuf;
Expand All @@ -22,9 +22,11 @@ pub enum Error {
Connection(std::io::Error),

/// Hyper error
#[cfg(feature = "client")]
#[error("HyperError: {0}")]
HyperError(#[from] hyper::Error),
/// Service error
#[cfg(feature = "client")]
#[error("ServiceError: {0}")]
Service(tower::BoxError),

Expand Down Expand Up @@ -71,6 +73,7 @@ pub enum Error {
RequestValidation(String),

/// Configuration error
#[cfg(feature = "config")]
#[error("Error loading kubeconfig: {0}")]
Kubeconfig(#[from] ConfigError),

Expand Down Expand Up @@ -117,6 +120,7 @@ pub enum Error {
SecWebSocketProtocolMismatch,
}

#[cfg(feature = "config")]
#[derive(Error, Debug)]
// Redundant with the error messages and machine names
#[allow(missing_docs)]
Expand Down Expand Up @@ -167,6 +171,7 @@ pub enum ConfigError {
#[error("exec-plugin response did not contain a status")]
ExecPluginFailed,

#[cfg(feature = "client")]
#[error("Malformed token expiration date: {0}")]
MalformedTokenExpirationDate(#[source] chrono::ParseError),

Expand Down
Loading

0 comments on commit 6a928b5

Please sign in to comment.