Skip to content

Commit ee83f52

Browse files
authored
Migrate to DeserializeGuard (#293)
* Migrate to DeserializeGuard Some cases have been migrated to PartialObjectMeta instead where only the metadata is used anyway. Part of stackabletech/issues#211. Fixes #237. * Changelog * Avoid requesting CM/secret data entirely Instead of requesting it but throwing it away.
1 parent c379651 commit ee83f52

File tree

4 files changed

+93
-36
lines changed

4 files changed

+93
-36
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ All notable changes to this project will be documented in this file.
1616
### Fixed
1717

1818
- BREAKING: The fields `connection` and `host` on `S3Connection` as well as `bucketName` on `S3Bucket`are now mandatory. Previously operators errored out in case these fields where missing ([#283]).
19+
- Failing to parse one `ZookeeperCluster`/`ZookeeperZnode` should no longer cause the whole operator to stop functioning ([#293]).
20+
- The StatefulSet restarter service now only retrieves metadata for ConfigMaps and Secrets, rather than full objects ([#293]).
1921

2022
[#283]: https://github.com/stackabletech/commons-operator/pull/283
2123
[#285]: https://github.com/stackabletech/commons-operator/pull/285
2224
[#290]: https://github.com/stackabletech/commons-operator/pull/290
25+
[#293]: https://github.com/stackabletech/commons-operator/pull/293
2326

2427
## [24.7.0] - 2024-07-24
2528

rust/operator-binary/src/pod_enrichment_controller.rs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use snafu::{ResultExt, Snafu};
55
use stackable_operator::{
66
k8s_openapi::api::core::v1::{Node, Pod},
77
kube::{
8-
core::ObjectMeta,
8+
core::{error_boundary, DeserializeGuard, ObjectMeta},
99
runtime::{controller, reflector::ObjectRef, watcher, Controller},
10+
Resource,
1011
},
1112
logging::controller::{report_controller_reconciled, ReconcilerError},
1213
namespace::WatchNamespace,
@@ -23,11 +24,17 @@ struct Ctx {
2324
#[derive(Snafu, Debug, EnumDiscriminants)]
2425
#[strum_discriminants(derive(IntoStaticStr))]
2526
pub enum Error {
27+
#[snafu(display("Pod object is invalid"))]
28+
InvalidPod {
29+
source: error_boundary::InvalidObject,
30+
},
31+
2632
#[snafu(display("failed to get {node} for Pod"))]
2733
GetNode {
2834
source: stackable_operator::client::Error,
2935
node: ObjectRef<Node>,
3036
},
37+
3138
#[snafu(display("failed to update Pod"))]
3239
UpdatePod {
3340
source: stackable_operator::client::Error,
@@ -41,6 +48,7 @@ impl ReconcilerError for Error {
4148

4249
fn secondary_object(&self) -> Option<ObjectRef<stackable_operator::kube::core::DynamicObject>> {
4350
match self {
51+
Error::InvalidPod { source: _ } => None,
4452
Error::GetNode { node, .. } => Some(node.clone().erase()),
4553
Error::UpdatePod { source: _ } => None,
4654
}
@@ -49,20 +57,23 @@ impl ReconcilerError for Error {
4957

5058
pub async fn start(client: &stackable_operator::client::Client, watch_namespace: &WatchNamespace) {
5159
let controller = Controller::new(
52-
watch_namespace.get_api::<Pod>(client),
60+
watch_namespace.get_api::<DeserializeGuard<Pod>>(client),
5361
watcher::Config::default().labels("enrichment.stackable.tech/enabled=true"),
5462
);
5563
let pods = controller.store();
5664
controller
5765
.watches(
58-
client.get_all_api::<Node>(),
66+
client.get_all_api::<DeserializeGuard<Node>>(),
5967
watcher::Config::default(),
6068
move |node| {
6169
pods.state()
6270
.into_iter()
6371
.filter(move |pod| {
72+
let Ok(pod) = &pod.0 else {
73+
return false;
74+
};
6475
pod.spec.as_ref().and_then(|s| s.node_name.as_deref())
65-
== node.metadata.name.as_deref()
76+
== node.meta().name.as_deref()
6677
})
6778
.map(|pod| ObjectRef::from_obj(&*pod))
6879
},
@@ -86,7 +97,17 @@ pub enum NodeAddressType {
8697
InternalIP,
8798
}
8899

89-
async fn reconcile(pod: Arc<Pod>, ctx: Arc<Ctx>) -> Result<controller::Action, Error> {
100+
async fn reconcile(
101+
pod: Arc<DeserializeGuard<Pod>>,
102+
ctx: Arc<Ctx>,
103+
) -> Result<controller::Action, Error> {
104+
tracing::info!("Starting reconcile");
105+
let pod = pod
106+
.0
107+
.as_ref()
108+
.map_err(error_boundary::InvalidObject::clone)
109+
.context(InvalidPodSnafu)?;
110+
90111
let node_name = pod.spec.as_ref().and_then(|s| s.node_name.as_deref());
91112
let node = if let Some(node_name) = node_name {
92113
ctx.client
@@ -133,6 +154,15 @@ async fn reconcile(pod: Arc<Pod>, ctx: Arc<Ctx>) -> Result<controller::Action, E
133154
Ok(controller::Action::await_change())
134155
}
135156

136-
fn error_policy(_obj: Arc<Pod>, _error: &Error, _ctx: Arc<Ctx>) -> controller::Action {
137-
controller::Action::requeue(Duration::from_secs(5))
157+
fn error_policy(
158+
_obj: Arc<DeserializeGuard<Pod>>,
159+
error: &Error,
160+
_ctx: Arc<Ctx>,
161+
) -> controller::Action {
162+
match error {
163+
// root object is invalid, will be requeued when modified anyway
164+
Error::InvalidPod { .. } => controller::Action::await_change(),
165+
166+
_ => controller::Action::requeue(Duration::from_secs(5)),
167+
}
138168
}

rust/operator-binary/src/restart_controller/pod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use stackable_operator::{
1010
},
1111
kube::{
1212
self,
13-
api::EvictParams,
13+
api::{EvictParams, PartialObjectMeta},
1414
core::DynamicObject,
1515
runtime::{controller::Action, reflector::ObjectRef, watcher, Controller},
1616
},
@@ -63,7 +63,7 @@ impl ReconcilerError for Error {
6363

6464
pub async fn start(client: &Client, watch_namespace: &WatchNamespace) {
6565
let controller = Controller::new(
66-
watch_namespace.get_api::<Pod>(client),
66+
watch_namespace.get_api::<PartialObjectMeta<Pod>>(client),
6767
watcher::Config::default(),
6868
);
6969
controller
@@ -80,7 +80,7 @@ pub async fn start(client: &Client, watch_namespace: &WatchNamespace) {
8080
.await;
8181
}
8282

83-
async fn reconcile(pod: Arc<Pod>, ctx: Arc<Ctx>) -> Result<Action, Error> {
83+
async fn reconcile(pod: Arc<PartialObjectMeta<Pod>>, ctx: Arc<Ctx>) -> Result<Action, Error> {
8484
tracing::info!("Starting reconciliation ..");
8585
if pod.metadata.deletion_timestamp.is_some() {
8686
// Object is already being deleted, no point trying again
@@ -163,6 +163,6 @@ async fn reconcile(pod: Arc<Pod>, ctx: Arc<Ctx>) -> Result<Action, Error> {
163163
}
164164
}
165165

166-
fn error_policy(_obj: Arc<Pod>, _error: &Error, _ctx: Arc<Ctx>) -> Action {
166+
fn error_policy(_obj: Arc<PartialObjectMeta<Pod>>, _error: &Error, _ctx: Arc<Ctx>) -> Action {
167167
Action::requeue(Duration::from_secs(5))
168168
}

rust/operator-binary/src/restart_controller/statefulset.rs

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,36 @@ use stackable_operator::k8s_openapi::api::core::v1::{
1212
ConfigMap, EnvFromSource, EnvVar, PodSpec, Secret, Volume,
1313
};
1414
use stackable_operator::kube;
15-
use stackable_operator::kube::api::{Patch, PatchParams};
16-
use stackable_operator::kube::core::DynamicObject;
15+
use stackable_operator::kube::api::{PartialObjectMeta, Patch, PatchParams};
16+
use stackable_operator::kube::core::{error_boundary, DeserializeGuard, DynamicObject};
1717
use stackable_operator::kube::runtime::controller::{
1818
trigger_self, trigger_with, Action, ReconcileRequest,
1919
};
2020
use stackable_operator::kube::runtime::reflector::{ObjectRef, Store};
21-
use stackable_operator::kube::runtime::{applier, reflector, watcher, Config, WatchStreamExt};
21+
use stackable_operator::kube::runtime::{
22+
applier, metadata_watcher, reflector, watcher, Config, WatchStreamExt,
23+
};
2224
use stackable_operator::kube::{Resource, ResourceExt};
2325
use stackable_operator::logging::controller::{report_controller_reconciled, ReconcilerError};
2426
use stackable_operator::namespace::WatchNamespace;
2527
use strum::{EnumDiscriminants, IntoStaticStr};
2628

2729
struct Ctx {
2830
kube: kube::Client,
29-
cms: Store<ConfigMap>,
31+
cms: Store<PartialObjectMeta<ConfigMap>>,
3032
cms_inited: Arc<AtomicBool>,
31-
secrets: Store<Secret>,
33+
secrets: Store<PartialObjectMeta<Secret>>,
3234
secrets_inited: Arc<AtomicBool>,
3335
}
3436

3537
#[derive(Snafu, Debug, EnumDiscriminants)]
3638
#[strum_discriminants(derive(IntoStaticStr))]
3739
enum Error {
40+
#[snafu(display("StatefulSet object is invalid"))]
41+
InvalidStatefulSet {
42+
source: error_boundary::InvalidObject,
43+
},
44+
3845
#[snafu(display("failed to patch object {obj_ref}"))]
3946
PatchFailed {
4047
source: kube::Error,
@@ -55,6 +62,7 @@ impl ReconcilerError for Error {
5562

5663
fn secondary_object(&self) -> Option<ObjectRef<DynamicObject>> {
5764
match self {
65+
Error::InvalidStatefulSet { source: _ } => None,
5866
Error::PatchFailed { obj_ref, .. } => Some(*obj_ref.clone()),
5967
Error::ConfigMapsUninitialized => None,
6068
Error::SecretsUninitialized => None,
@@ -63,12 +71,12 @@ impl ReconcilerError for Error {
6371
}
6472

6573
pub async fn start(client: &Client, watch_namespace: &WatchNamespace) {
66-
let stses = watch_namespace.get_api::<StatefulSet>(client);
74+
let stses = watch_namespace.get_api::<DeserializeGuard<StatefulSet>>(client);
6775
let cms = watch_namespace.get_api::<ConfigMap>(client);
6876
let secrets = watch_namespace.get_api::<Secret>(client);
69-
let sts_store = reflector::store::Writer::new(());
70-
let cm_store = reflector::store::Writer::new(());
71-
let secret_store = reflector::store::Writer::new(());
77+
let sts_store = reflector::store::Writer::<DeserializeGuard<StatefulSet>>::new(());
78+
let cm_store = reflector::store::Writer::<PartialObjectMeta<ConfigMap>>::new(());
79+
let secret_store = reflector::store::Writer::<PartialObjectMeta<Secret>>::new(());
7280
let cms_inited = Arc::new(AtomicBool::from(false));
7381
let secrets_inited = Arc::new(AtomicBool::from(false));
7482

@@ -86,17 +94,18 @@ pub async fn start(client: &Client, watch_namespace: &WatchNamespace) {
8694
stream::select(
8795
stream::select(
8896
trigger_all(
89-
reflector(cm_store, watcher(cms, watcher::Config::default()))
97+
reflector(cm_store, metadata_watcher(cms, watcher::Config::default()))
9098
.inspect(|_| cms_inited.store(true, std::sync::atomic::Ordering::SeqCst))
9199
.touched_objects(),
92100
sts_store.as_reader(),
93101
),
94102
trigger_all(
95-
reflector(secret_store, watcher(secrets, watcher::Config::default()))
96-
.inspect(|_| {
97-
secrets_inited.store(true, std::sync::atomic::Ordering::SeqCst)
98-
})
99-
.touched_objects(),
103+
reflector(
104+
secret_store,
105+
metadata_watcher(secrets, watcher::Config::default()),
106+
)
107+
.inspect(|_| secrets_inited.store(true, std::sync::atomic::Ordering::SeqCst))
108+
.touched_objects(),
100109
sts_store.as_reader(),
101110
),
102111
),
@@ -161,7 +170,17 @@ fn find_pod_refs<'a, K: Resource + 'a>(
161170
.chain(container_env_from_refs)
162171
}
163172

164-
async fn reconcile(sts: Arc<StatefulSet>, ctx: Arc<Ctx>) -> Result<Action, Error> {
173+
async fn reconcile(
174+
sts: Arc<DeserializeGuard<StatefulSet>>,
175+
ctx: Arc<Ctx>,
176+
) -> Result<Action, Error> {
177+
tracing::info!("Starting reconcile");
178+
let sts = sts
179+
.0
180+
.as_ref()
181+
.map_err(error_boundary::InvalidObject::clone)
182+
.context(InvalidStatefulSetSnafu)?;
183+
165184
if !ctx.cms_inited.load(std::sync::atomic::Ordering::SeqCst) {
166185
return ConfigMapsUninitializedSnafu.fail();
167186
}
@@ -181,12 +200,12 @@ async fn reconcile(sts: Arc<StatefulSet>, ctx: Arc<Ctx>) -> Result<Action, Error
181200
find_pod_refs(
182201
pod_spec,
183202
|volume| {
184-
Some(ObjectRef::<ConfigMap>::new(
203+
Some(ObjectRef::<PartialObjectMeta<ConfigMap>>::new(
185204
&volume.config_map.as_ref()?.name,
186205
))
187206
},
188207
|env_var| {
189-
Some(ObjectRef::<ConfigMap>::new(
208+
Some(ObjectRef::<PartialObjectMeta<ConfigMap>>::new(
190209
&env_var
191210
.value_from
192211
.as_ref()?
@@ -196,7 +215,7 @@ async fn reconcile(sts: Arc<StatefulSet>, ctx: Arc<Ctx>) -> Result<Action, Error
196215
))
197216
},
198217
|env_from| {
199-
Some(ObjectRef::<ConfigMap>::new(
218+
Some(ObjectRef::<PartialObjectMeta<ConfigMap>>::new(
200219
&env_from.config_map_ref.as_ref()?.name,
201220
))
202221
},
@@ -225,17 +244,17 @@ async fn reconcile(sts: Arc<StatefulSet>, ctx: Arc<Ctx>) -> Result<Action, Error
225244
find_pod_refs(
226245
pod_spec,
227246
|volume| {
228-
Some(ObjectRef::<Secret>::new(
247+
Some(ObjectRef::<PartialObjectMeta<Secret>>::new(
229248
volume.secret.as_ref()?.secret_name.as_deref()?,
230249
))
231250
},
232251
|env_var| {
233-
Some(ObjectRef::<Secret>::new(
252+
Some(ObjectRef::<PartialObjectMeta<Secret>>::new(
234253
&env_var.value_from.as_ref()?.secret_key_ref.as_ref()?.name,
235254
))
236255
},
237256
|env_from| {
238-
Some(ObjectRef::<Secret>::new(
257+
Some(ObjectRef::<PartialObjectMeta<Secret>>::new(
239258
&env_from.secret_ref.as_ref()?.name,
240259
))
241260
},
@@ -290,11 +309,16 @@ async fn reconcile(sts: Arc<StatefulSet>, ctx: Arc<Ctx>) -> Result<Action, Error
290309
)
291310
.await
292311
.context(PatchFailedSnafu {
293-
obj_ref: ObjectRef::from_obj(sts.as_ref()).erase(),
312+
obj_ref: ObjectRef::from_obj(sts).erase(),
294313
})?;
295314
Ok(Action::await_change())
296315
}
297316

298-
fn error_policy(_obj: Arc<StatefulSet>, _error: &Error, _ctx: Arc<Ctx>) -> Action {
299-
Action::requeue(Duration::from_secs(5))
317+
fn error_policy(_obj: Arc<DeserializeGuard<StatefulSet>>, error: &Error, _ctx: Arc<Ctx>) -> Action {
318+
match error {
319+
// root object is invalid, will be requeued when modified anyway
320+
Error::InvalidStatefulSet { .. } => Action::await_change(),
321+
322+
_ => Action::requeue(Duration::from_secs(5)),
323+
}
300324
}

0 commit comments

Comments
 (0)