#[macro_use] extern crate log;
#[macro_use] extern crate serde_derive;
use either::Either::{Left, Right};
use serde_json::json;
use kube::{
api::{Api, PostParams, DeleteParams, ListParams, Object, PatchParams},
client::{APIClient},
config,
};
#[derive(Deserialize, Serialize, Clone)]
pub struct FooSpec {
name: String,
info: String,
replicas: i32,
}
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct FooStatus {
is_bad: bool,
replicas: i32,
}
type Foo = Object<FooSpec, FooStatus>;
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 crds = Api::v1beta1CustomResourceDefinition(client.clone());
let dp = DeleteParams::default();
let _ = crds.delete("foos.clux.dev", &dp).map(|res| {
res.map_left(|o| {
info!("Deleted {}: ({:?})", o.metadata.name,
o.status.unwrap().conditions.unwrap().last());
std::thread::sleep(std::time::Duration::from_millis(1000));
}).map_right(|s| {
info!("Deleted foos.clux.dev: ({:?})", s);
})
});
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": {},
"scale": {
"specReplicasPath": ".spec.replicas",
"statusReplicasPath": ".status.replicas",
}
}
},
});
info!("Creating CRD foos.clux.dev");
let pp = PostParams::default();
let patch_params = PatchParams::default();
match crds.create(&pp, serde_json::to_vec(&foocrd)?) {
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 : Api<Foo> = Api::customResource(client, "foos")
.version("v1")
.group("clux.dev")
.within("default");
info!("Creating Foo instance baz");
let f1 = json!({
"apiVersion": "clux.dev/v1",
"kind": "Foo",
"metadata": { "name": "baz" },
"spec": { "name": "baz", "info": "old baz", "replicas": 1 },
});
let o = foos.create(&pp, serde_json::to_vec(&f1)?)?;
assert_eq!(f1["metadata"]["name"], o.metadata.name);
info!("Created {}", o.metadata.name);
info!("Get Foo baz");
let f1cpy = 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", "replicas": 1 },
});
let f1_replaced = foos.replace("baz", &pp, serde_json::to_vec(&foo_replace)?)?;
assert_eq!(f1_replaced.spec.name, "baz");
assert_eq!(f1_replaced.spec.info, "new baz");
assert!(f1_replaced.status.is_none());
foos.delete("baz", &dp)?.map_left(|f1del| {
assert_eq!(f1del.spec.info, "old baz");
});
info!("Create Foo instance qux");
let f2 = json!({
"apiVersion": "clux.dev/v1",
"kind": "Foo",
"metadata": { "name": "qux" },
"spec": FooSpec { name: "qux".into(), replicas: 0, info: "unpatched qux".into() },
"status": FooStatus::default(),
});
let o = foos.create(&pp, serde_json::to_vec(&f2)?)?;
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, replicas: 0 }
});
let o = foos.replace_status("qux", &pp, serde_json::to_vec(&fs)?)?;
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, replicas: 1 }
});
let o = foos.patch_status("qux", &patch_params, serde_json::to_vec(&fs)?)?;
info!("Patched status {:?} for {}", o.status, o.metadata.name);
assert!(!o.status.unwrap().is_bad);
info!("Get Status on Foo instance qux");
let o = foos.get_status("qux")?;
info!("Got status {:?} for {}", o.status, o.metadata.name);
assert!(!o.status.unwrap().is_bad);
info!("Get Scale on Foo instance qux");
let scale = foos.get_scale("qux")?;
info!("Got scale {:?} - {:?}", scale.spec, scale.status);
assert_eq!(scale.status.unwrap().replicas, 1);
let fs = json!({
"spec": { "replicas": 2 }
});
let o = foos.patch_scale("qux", &patch_params, serde_json::to_vec(&fs)?)?;
info!("Patched scale {:?} for {}", o.spec, o.metadata.name);
assert_eq!(o.status.unwrap().replicas, 1);
assert_eq!(o.spec.replicas.unwrap(), 2);
info!("Patch Foo instance qux");
let patch = json!({
"spec": { "info": "patched qux" }
});
let o = foos.patch("qux", &patch_params, serde_json::to_vec(&patch)?)?;
info!("Patched {} with new name: {}", o.metadata.name, o.spec.name);
assert_eq!(o.spec.info, "patched qux");
assert_eq!(o.spec.name, "qux");
let lp = ListParams::default();
let res = foos.list(&lp)?;
assert_eq!(res.items.len(), 1);
assert!(foos.delete("qux", &dp)?.is_right());
match foos.delete_collection(&lp)? {
Left(list) => {
let deleted = list.items.into_iter().map(|i| i.metadata.name).collect::<Vec<_>>();
info!("Deleted collection of foos: {:?}", deleted);
},
Right(status) => {
info!("Deleted collection of crds: status={:?}", status);
}
}
Ok(())
}