-
-
Notifications
You must be signed in to change notification settings - Fork 326
/
Copy pathcrd_derive_multi.rs
128 lines (111 loc) · 4.91 KB
/
crd_derive_multi.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition;
use kube::{
api::{Api, Patch, PatchParams},
core::crd::merge_crds,
runtime::wait::{await_condition, conditions},
Client, CustomResource, CustomResourceExt, ResourceExt,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tracing::*;
mod v1 {
use super::*;
// spec that is forwards compatible with v2 (can upgrade by truncating)
#[derive(CustomResource, Serialize, Deserialize, Default, Debug, Clone, JsonSchema)]
#[kube(group = "kube.rs", version = "v1", kind = "ManyDerive", namespaced)]
pub struct ManyDeriveSpec {
pub name: String,
pub oldprop: u32,
}
}
mod v2 {
// spec that is NOT backwards compatible with v1 (cannot retrieve oldprop if truncated)
use super::*;
#[derive(CustomResource, Serialize, Deserialize, Default, Debug, Clone, JsonSchema)]
#[kube(group = "kube.rs", version = "v2", kind = "ManyDerive", namespaced)]
pub struct ManyDeriveSpec {
pub name: String,
pub extra: Option<String>,
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let client = Client::try_default().await?;
let ssapply = PatchParams::apply("crd_derive_multi").force();
let crd1 = v1::ManyDerive::crd();
let crd2 = v2::ManyDerive::crd();
let all_crds = vec![crd1.clone(), crd2.clone()];
// apply schema where v1 is the stored version
apply_crd(client.clone(), merge_crds(all_crds.clone(), "v1")?).await?;
// create apis
let v1api: Api<v1::ManyDerive> = Api::default_namespaced(client.clone());
let v2api: Api<v2::ManyDerive> = Api::default_namespaced(client.clone());
// create a v1 version
let v1m = v1::ManyDerive::new("old", v1::ManyDeriveSpec {
name: "i am old".into(),
oldprop: 5,
});
let oldvarv1 = v1api.patch("old", &ssapply, &Patch::Apply(&v1m)).await?;
info!("old instance on v1: {:?}", oldvarv1.spec);
let oldvarv2 = v2api.get("old").await?;
info!("old instance on v2 truncates: {:?}", oldvarv2.spec);
// create a v2 version
let v2m = v2::ManyDerive::new("new", v2::ManyDeriveSpec {
name: "i am new".into(),
extra: Some("hi".into()),
});
let newvarv2 = v2api.patch("new", &ssapply, &Patch::Apply(&v2m)).await?;
info!("new instance on v2 is force downgraded: {:?}", newvarv2.spec); // no extra field
let cannot_fetch_as_old = v1api.get("new").await.unwrap_err();
info!("cannot fetch new on v1: {:?}", cannot_fetch_as_old);
// apply schema upgrade
apply_crd(client.clone(), merge_crds(all_crds, "v2")?).await?;
// nothing changed with existing objects without conversion
//let oldvarv1_upg = v1api.get("old").await?;
//info!("old instance unchanged on v1: {:?}", oldvarv1_upg.spec);
//let oldvarv2_upg = v2api.get("old").await?;
//info!("old instance unchanged on v2: {:?}", oldvarv2_upg.spec);
// re-apply new now that v2 is stored gives us the extra properties
let newvarv2_2 = v2api.patch("new", &ssapply, &Patch::Apply(&v2m)).await?;
info!("new on v2 correct on reapply to v2: {:?}", newvarv2_2.spec);
// note we can apply old versions without them being truncated to the v2 schema
// in our case this means we cannot fetch them with our v1 schema (breaking change to not have oldprop)
let v1m2 = v1::ManyDerive::new("old", v1::ManyDeriveSpec {
name: "i am old2".into(),
oldprop: 5,
});
let v1err = v1api
.patch("old", &ssapply, &Patch::Apply(&v1m2))
.await
.unwrap_err();
info!("cannot get old on v1 anymore: {:?}", v1err); // mandatory field oldprop truncated
// ...but the change is still there:
let old_still_there = v2api.get("old").await?;
assert_eq!(old_still_there.spec.name, "i am old2");
cleanup(client.clone()).await?;
Ok(())
}
async fn apply_crd(client: Client, crd: CustomResourceDefinition) -> anyhow::Result<()> {
let crds: Api<CustomResourceDefinition> = Api::all(client.clone());
info!("Creating crd: {}", serde_yaml::to_string(&crd)?);
let ssapply = PatchParams::apply("crd_derive_multi").force();
crds.patch("manyderives.kube.rs", &ssapply, &Patch::Apply(&crd))
.await?;
let establish = await_condition(
crds.clone(),
"manyderives.kube.rs",
conditions::is_crd_established(),
);
let _ = tokio::time::timeout(std::time::Duration::from_secs(10), establish).await?;
Ok(())
}
async fn cleanup(client: Client) -> anyhow::Result<()> {
let crds: Api<CustomResourceDefinition> = Api::all(client.clone());
let obj = crds.delete("manyderives.kube.rs", &Default::default()).await?;
if let either::Either::Left(o) = obj {
let uid = o.uid().unwrap();
await_condition(crds, "manyderives.kube.rs", conditions::is_deleted(&uid)).await?;
}
Ok(())
}