#[macro_use] extern crate log;
#[macro_use] extern crate serde_derive;
use either::Either::{Left, Right};
use serde_json::json;
use kube::{
api::{RawApi, PostParams, DeleteParams, ListParams, Object, ObjectList, Void, PatchParams},
client::APIClient,
config,
};
use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1beta1::{
CustomResourceDefinitionSpec as CrdSpec,
CustomResourceDefinitionStatus as CrdStatus,
};
#[derive(Deserialize, Serialize, Clone)]
pub struct FooSpec {
name: String,
info: String,
}
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct FooStatus {
is_bad: bool,
}
type Foo = Object<FooSpec, FooStatus>;
type FooMeta = Object<Void, Void>;
type FullCrd = Object<CrdSpec, CrdStatus>;
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 namespace = std::env::var("NAMESPACE").unwrap_or("default".into());
let crds = RawApi::v1beta1CustomResourceDefinition();
let dp = DeleteParams::default();
let req = crds.delete("foos.clux.dev", &dp)?;
let _ = client.request_status::<FullCrd>(req).map(|res| match res {
Left(res) => {
info!("Deleted {}: ({:?})", res.metadata.name,
res.status.unwrap().conditions.unwrap().last());
std::thread::sleep(std::time::Duration::from_millis(1000));
},
Right(status) => {
info!("Deleted foos.clux.dev: {:?}", status)
}
});
let foocrd = json!({
"metadata": {
"name": "foos.clux.dev"
},
"spec": {
"group": "clux.dev",
"version": "v1",
"scope": "Namespaced",
"names": {
"plural": "foos",
"singular": "foo",
"kind": "Foo",
},
"subresources": {
"status": {}
},
},
});
info!("Creating CRD foos.clux.dev");
let pp = PostParams::default();
let patch_params = PatchParams::default();
let req = crds.create(&pp, serde_json::to_vec(&foocrd)?)?;
match client.request::<FullCrd>(req) {
Ok(o) => {
info!("Created {} ({:?})", o.metadata.name, o.status);
debug!("Created CRD: {:?}", o.spec);
},
Err(e) => {
if let Some(ae) = e.api_error() {
assert_eq!(ae.code, 409); } else {
return Err(e.into()) }
},
}
let foos = RawApi::customResource("foos")
.version("v1")
.group("clux.dev")
.within(&namespace);
info!("Creating Foo instance baz");
let f1 = json!({
"apiVersion": "clux.dev/v1",
"kind": "Foo",
"metadata": { "name": "baz" },
"spec": { "name": "baz", "info": "old baz" },
});
let req = foos.create(&pp, serde_json::to_vec(&f1)?)?;
let o = client.request::<FooMeta>(req)?;
info!("Created {}", o.metadata.name);
info!("Get Foo baz");
let f1cpy = client.request::<Foo>(foos.get("baz")?)?;
assert_eq!(f1cpy.spec.info, "old baz");
info!("Replace Foo baz");
let foo_replace = json!({
"apiVersion": "clux.dev/v1",
"kind": "Foo",
"metadata": {
"name": "baz",
"resourceVersion": f1cpy.metadata.resourceVersion,
},
"spec": { "name": "baz", "info": "new baz" },
});
let req = foos.replace("baz", &pp, serde_json::to_vec(&foo_replace)?)?;
let f1_replaced = client.request::<Foo>(req)?;
assert_eq!(f1_replaced.spec.name, "baz");
assert_eq!(f1_replaced.spec.info, "new baz");
assert!(f1_replaced.status.is_none());
info!("Create Foo instance qux");
let f2 = json!({
"apiVersion": "clux.dev/v1",
"kind": "Foo",
"metadata": { "name": "qux" },
"spec": FooSpec { name: "qux".into(), info: "unpatched qux".into() },
"status": FooStatus::default(),
});
let req = foos.create(&pp, serde_json::to_vec(&f2)?)?;
let o = client.request::<Foo>(req)?;
info!("Created {}", o.metadata.name);
info!("Replace Status on Foo instance qux");
let fs = json!({
"apiVersion": "clux.dev/v1",
"kind": "Foo",
"metadata": {
"name": "qux",
"resourceVersion": o.metadata.resourceVersion,
},
"status": FooStatus { is_bad: true }
});
let req = foos.replace_status("qux", &pp, serde_json::to_vec(&fs)?)?;
let o = client.request::<Foo>(req)?;
info!("Replaced status {:?} for {}", o.status, o.metadata.name);
assert!(o.status.unwrap().is_bad);
info!("Patch Status on Foo instance qux");
let fs = json!({
"status": FooStatus { is_bad: false }
});
let req = foos.patch_status("qux", &patch_params, serde_json::to_vec(&fs)?)?;
let o = client.request::<Foo>(req)?;
info!("Patched status {:?} for {}", o.status, o.metadata.name);
assert!(!o.status.unwrap().is_bad);
info!("Get Status on Foo instance qux");
let req = foos.get_status("qux")?;
let o = client.request::<Foo>(req)?;
info!("Got status {:?} for {}", o.status, o.metadata.name);
assert!(!o.status.unwrap().is_bad);
info!("Patch Foo instance qux");
let patch = json!({
"spec": { "info": "patched qux" }
});
let req = foos.patch("qux", &patch_params, serde_json::to_vec(&patch)?)?;
let o = client.request::<Foo>(req)?;
info!("Patched {} with new name: {}", o.metadata.name, o.spec.name);
assert_eq!(o.spec.info, "patched qux");
assert_eq!(o.spec.name, "qux");
client.request_status::<Foo>(foos.delete("baz", &dp)?)?.map_left(|f1del| {
assert_eq!(f1del.spec.info, "old baz");
});
let lp = ListParams::default();
let req = foos.list(&lp)?;
let res = client.request::<ObjectList<Foo>>(req)?;
assert_eq!(res.items.len(), 1);
let req = foos.delete_collection(&lp)?;
match client.request_status::<ObjectList<Foo>>(req)? {
Left(res) => {
let deleted = res.items.into_iter().map(|i| i.metadata.name).collect::<Vec<_>>();
info!("Deleted collection of foos: {:?}", deleted);
},
Right(status) => {
info!("Deleted collection: {:?}", status)
}
}
Ok(())
}