Skip to content

Commit 638887a

Browse files
committed
Rework Scaler status to complex enum
1 parent 4b8eb51 commit 638887a

3 files changed

Lines changed: 39 additions & 65 deletions

File tree

crates/stackable-operator/src/crd/scaler/mod.rs

Lines changed: 21 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::borrow::Cow;
2-
31
use k8s_openapi::apimachinery::pkg::apis::meta::v1::Time;
42
use kube::CustomResource;
53
use schemars::JsonSchema;
@@ -64,20 +62,15 @@ pub struct ScalerStatus {
6462
// and others to be typed as objects. We therefore encode the variant data in a separate details
6563
// key/object. With this, all variants can be encoded as strings, while the status can still contain
6664
// additional data in an extra field when needed.
67-
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, strum::Display)]
68-
#[serde(
69-
tag = "state",
70-
content = "details",
71-
rename_all = "PascalCase",
72-
rename_all_fields = "camelCase"
73-
)]
74-
#[strum(serialize_all = "PascalCase")]
65+
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, JsonSchema, strum::Display)]
66+
#[serde(rename_all = "camelCase", rename_all_fields = "camelCase")]
67+
#[strum(serialize_all = "camelCase")]
7568
pub enum ScalerState {
7669
/// No scaling operation is in progress.
77-
Idle,
70+
Idle {},
7871

7972
/// Running the `pre_scale` hook (e.g. data offload).
80-
PreScaling,
73+
PreScaling {},
8174

8275
/// Waiting for the StatefulSet to converge to the new replica count.
8376
///
@@ -104,41 +97,6 @@ pub enum ScalerState {
10497
},
10598
}
10699

107-
// We manually implement the JSON schema instead of deriving it, because kube's schema transformer
108-
// cannot handle the derived JsonSchema and proceeds to hit the following error: "Property "state"
109-
// has the schema ... but was already defined as ... in another subschema. The schemas for a
110-
// property used in multiple subschemas must be identical".
111-
impl JsonSchema for ScalerState {
112-
fn schema_name() -> Cow<'static, str> {
113-
"ScalerState".into()
114-
}
115-
116-
fn json_schema(generator: &mut schemars::generate::SchemaGenerator) -> schemars::Schema {
117-
schemars::json_schema!({
118-
"type": "object",
119-
"required": ["state"],
120-
"properties": {
121-
"state": {
122-
"type": "string",
123-
"enum": ["idle", "preScaling", "scaling", "postScaling", "failed"]
124-
},
125-
"details": {
126-
"type": "object",
127-
"properties": {
128-
"failedIn": generator.subschema_for::<FailedInState>(),
129-
"previous_replicas": {
130-
"type": "uint16",
131-
"minimum": u16::MIN,
132-
"maximum": u16::MAX
133-
},
134-
"reason": { "type": "string" }
135-
}
136-
}
137-
}
138-
})
139-
}
140-
}
141-
142100
/// In which state the scaling operation failed.
143101
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
144102
#[serde(rename_all = "PascalCase")]
@@ -158,19 +116,18 @@ mod tests {
158116
use rstest::rstest;
159117

160118
use super::*;
161-
use crate::utils::yaml_from_str_singleton_map;
119+
use crate::{
120+
test_utils::serialize_to_yaml_with_singleton_map, utils::yaml_from_str_singleton_map,
121+
};
162122

163123
#[rstest]
164-
#[case::idle("state: Idle", ScalerState::Idle { })]
165-
#[case::pre_scaling("state: PreScaling", ScalerState::PreScaling { })]
166-
#[case::scaling("state: Scaling
167-
details:
124+
#[case::idle("idle: {}", ScalerState::Idle { })]
125+
#[case::pre_scaling("preScaling: {}", ScalerState::PreScaling { })]
126+
#[case::scaling("scaling:
168127
previousReplicas: 42", ScalerState::Scaling { previous_replicas: 42 })]
169-
#[case::post_scaling("state: PostScaling
170-
details:
128+
#[case::post_scaling("postScaling:
171129
previousReplicas: 42", ScalerState::PostScaling { previous_replicas: 42 })]
172-
#[case::failed("state: Failed
173-
details:
130+
#[case::failed("failed:
174131
failedIn: PreScaling
175132
reason: bruh moment", ScalerState::Failed {
176133
failed_in: FailedInState::PreScaling,
@@ -183,20 +140,19 @@ details:
183140
}
184141

185142
#[rstest]
186-
#[case::idle(ScalerState::Idle { }, "state: Idle\n")]
187-
#[case::pre_scaling(ScalerState::PreScaling { }, "state: PreScaling\n")]
188-
#[case::scaling(ScalerState::Scaling { previous_replicas: 42 }, "state: Scaling
189-
details:
143+
#[case::idle(ScalerState::Idle { }, "idle: {}\n")]
144+
#[case::pre_scaling(ScalerState::PreScaling { }, "preScaling: {}\n")]
145+
#[case::scaling(ScalerState::Scaling { previous_replicas: 42 }, "scaling:
190146
previousReplicas: 42\n")]
191-
#[case::post_scaling(ScalerState::PostScaling { previous_replicas: 42 }, "state: PostScaling
192-
details:
147+
#[case::post_scaling(ScalerState::PostScaling { previous_replicas: 42 }, "postScaling:
193148
previousReplicas: 42\n")]
194-
#[case::failed(ScalerState::Failed { failed_in: FailedInState::PreScaling, reason: "bruh moment".to_owned() }, "state: Failed
195-
details:
149+
#[case::failed(ScalerState::Failed { failed_in: FailedInState::PreScaling, reason: "bruh moment".to_owned() }, "failed:
196150
failedIn: PreScaling
197151
reason: bruh moment\n")]
198152
fn serialize_state(#[case] input: ScalerState, #[case] expected: &str) {
199-
let serialized = serde_yaml::to_string(&input).expect("serialization always passes");
153+
let serialized =
154+
serialize_to_yaml_with_singleton_map(&input).expect("serialization always passes");
155+
200156
assert_eq!(serialized, expected);
201157
}
202158
}

crates/stackable-operator/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub mod product_config_utils;
3131
pub mod product_logging;
3232
pub mod role_utils;
3333
pub mod status;
34+
pub mod test_utils;
3435
pub mod utils;
3536
pub mod validation;
3637

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// Please use only in tests, as we have non-ideal error handling in case serde_yaml produced
2+
/// non-utf8 output.
3+
pub fn serialize_to_yaml_with_singleton_map<S>(input: &S) -> Result<String, serde_yaml::Error>
4+
where
5+
S: serde::Serialize,
6+
{
7+
use serde::ser::Error as _;
8+
9+
let mut buffer = Vec::new();
10+
let mut serializer = serde_yaml::Serializer::new(&mut buffer);
11+
serde_yaml::with::singleton_map_recursive::serialize(input, &mut serializer)?;
12+
String::from_utf8(buffer).map_err(|err| {
13+
serde_yaml::Error::custom(format!(
14+
"For *some* reason, serde_yaml produced non-utf8 output: {err}"
15+
))
16+
})
17+
}

0 commit comments

Comments
 (0)