scl_lib/api_objects/
controller.rs1use crate::api_objects::{Error, MetaData, SclName, SclObject, TransitionError};
3use serde::{Deserialize, Serialize};
4
5#[cfg(feature = "openapi")]
6use aide::OperationIo;
7#[cfg(feature = "openapi")]
8use schemars::JsonSchema;
9
10#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
11#[cfg_attr(feature = "openapi", derive(OperationIo, JsonSchema))]
12#[cfg_attr(feature = "openapi", aide(output))]
13#[serde(rename_all = "camelCase")]
14pub struct Controller {
15 #[serde(flatten)]
16 pub metadata: MetaData,
17 pub kind: ControllerKind,
18 #[serde(default)]
21 pub status: ControllerStatus,
22}
23
24impl Controller {
25 pub fn new(name: SclName, kind: ControllerKind) -> Self {
26 Controller {
27 metadata: MetaData::new(name),
28 kind,
29 status: ControllerStatus::default(),
30 }
31 }
32
33 pub fn update_last_interaction(&mut self) {
34 self.status.last_interaction = std::time::SystemTime::now()
35 .duration_since(std::time::UNIX_EPOCH)
36 .unwrap()
37 .as_secs()
38 .to_string();
39 }
40}
41
42impl SclObject for Controller {
43 const PREFIX: &'static str = "/controllers";
45
46 fn name(&self) -> &SclName {
47 &self.metadata.name
48 }
49
50 fn api_endpoint<'a>(
64 _sc: impl Into<Option<&'a str>>,
65 name: impl Into<Option<&'a str>>,
66 ) -> String {
67 if let Some(name) = name.into() {
68 format!("{}/{}", Self::PREFIX, name)
69 } else {
70 Self::PREFIX.into()
71 }
72 }
73
74 fn db_key<'a>(_sc: impl Into<Option<&'a str>>, name: impl Into<Option<&'a str>>) -> String {
88 if let Some(name) = name.into() {
89 format!("{}/{}", Self::PREFIX, name)
90 } else {
91 Self::PREFIX.into()
92 }
93 }
94
95 fn metadata(&self) -> &MetaData {
96 &self.metadata
97 }
98
99 fn metadata_mut(&mut self) -> &mut MetaData {
100 &mut self.metadata
101 }
102
103 fn referenced_db_keys(&self) -> Vec<String> {
104 vec![]
105 }
106
107 fn validate_fields_before_create(&self) -> Result<(), Error> {
108 self.metadata.validate_fields_before_create()
109 }
110
111 fn validate_fields_before_update(
112 current_db_state: &Self,
113 proposed_new_state: &Self,
114 ) -> Result<(), Error> {
115 MetaData::validate_fields_before_regular_update(
116 ¤t_db_state.metadata,
117 &proposed_new_state.metadata,
118 )?;
119
120 if current_db_state.kind != proposed_new_state.kind {
121 Err(TransitionError::Other("Field kind may not be changed!".to_string()).into())
122 } else {
123 Ok(())
124 }
125 }
126}
127
128#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
129#[cfg_attr(feature = "openapi", derive(OperationIo, JsonSchema))]
130#[cfg_attr(feature = "openapi", aide(output))]
131#[serde(rename_all = "camelCase")]
132pub enum ControllerKind {
133 SchedulerController,
134 StorageController,
135 NetworkController,
136 VmController,
137}
138
139#[derive(Clone, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
140#[cfg_attr(feature = "openapi", derive(OperationIo, JsonSchema))]
141#[cfg_attr(feature = "openapi", aide(output))]
142#[serde(rename_all = "camelCase")]
143pub struct ControllerStatus {
144 pub last_interaction: String, alive: bool,
146}
147
148#[cfg(test)]
149mod test {
150 use super::Controller;
151 use crate::api_objects::test::{
152 detect_invalid_metadata_create_mutations, detect_invalid_metadata_update_mutations,
153 parse_json_dir,
154 };
155 use crate::api_objects::{ControllerKind, ControllerStatus, MetaData, SclName, SclObject};
156 use std::path::Path;
157
158 #[test]
159 fn parse_sample_json() {
160 parse_json_dir::<Controller>(Path::new("../test/sample_json/controllers"));
161 }
162
163 fn example_controller() -> Controller {
164 Controller {
165 metadata: MetaData::new(SclName::try_from("example").unwrap()),
166 kind: ControllerKind::SchedulerController,
167 status: ControllerStatus {
168 last_interaction: "Never".to_string(),
169 alive: false,
170 },
171 }
172 }
173
174 #[test]
175 fn validate_fields_before_update() {
176 let current = example_controller();
177 let mut proposed = current.clone();
178 assert!(Controller::validate_fields_before_update(¤t, &proposed).is_ok());
179 proposed.kind = ControllerKind::NetworkController;
180 assert!(Controller::validate_fields_before_update(¤t, &proposed).is_err());
181 }
182
183 #[test]
184 fn detect_invalid_metadata_mutations() {
185 let t = example_controller();
186 detect_invalid_metadata_create_mutations(&t);
187 detect_invalid_metadata_update_mutations(&t);
188 }
189}