1use std::{fmt, str::FromStr, sync::Arc};
19
20use log::{log_enabled, trace};
21use rpki::ca::idexchange::MyHandle;
22use serde::{Deserialize, Serialize};
23use url::Url;
24
25use crate::{
26 commons::{
27 actor::Actor,
28 error::Error,
29 eventsourcing::{
30 self, Aggregate, AggregateStore, Event, InitCommandDetails,
31 InitEvent, SentCommand, SentInitCommand, WithStorableDetails,
32 },
33 version::KrillVersion,
34 KrillResult,
35 },
36 constants::{ACTOR_DEF_KRILL, PROPERTIES_DFLT_NAME, PROPERTIES_NS},
37};
38use crate::api::history::CommandSummary;
39
40
41pub type PropertiesInitCommand =
44 SentInitCommand<PropertiesInitCommandDetails>;
45
46
47#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
50pub struct PropertiesInitCommandDetails {
51 pub krill_version: KrillVersion,
52}
53
54impl fmt::Display for PropertiesInitCommandDetails {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 self.store().fmt(f)
57 }
58}
59
60impl InitCommandDetails for PropertiesInitCommandDetails {
61 type StorableDetails = StorablePropertiesCommand;
62
63 fn store(&self) -> Self::StorableDetails {
64 StorablePropertiesCommand::make_init()
65 }
66}
67
68
69pub type PropertiesCommand = SentCommand<PropertiesCommandDetails>;
72
73
74#[derive(Clone, Debug)]
77pub enum PropertiesCommandDetails {
78 UpgradeTo { krill_version: KrillVersion },
79}
80
81impl fmt::Display for PropertiesCommandDetails {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 StorablePropertiesCommand::from(self).fmt(f)
84 }
85}
86
87
88#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
92#[serde(rename_all = "snake_case")]
93#[serde(tag = "type")]
94pub enum StorablePropertiesCommand {
95 Init,
96 UpgradeTo { krill_version: KrillVersion },
97}
98
99impl fmt::Display for StorablePropertiesCommand {
100 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101 match self {
102 Self::Init => {
103 write!(f, "initialise properties")
104 }
105 Self::UpgradeTo {
106 krill_version: version,
107 } => {
108 write!(f, "upgrade Krill to {version}")
109 }
110 }
111 }
112}
113
114impl eventsourcing::CommandDetails for PropertiesCommandDetails {
115 type Event = PropertiesEvent;
116
117 type StorableDetails = StorablePropertiesCommand;
118
119 fn store(&self) -> Self::StorableDetails {
120 self.into()
121 }
122}
123
124impl From<&PropertiesCommandDetails> for StorablePropertiesCommand {
125 fn from(details: &PropertiesCommandDetails) -> Self {
126 match details {
127 PropertiesCommandDetails::UpgradeTo { krill_version } => {
128 StorablePropertiesCommand::UpgradeTo {
129 krill_version: krill_version.clone(),
130 }
131 }
132 }
133 }
134}
135
136impl eventsourcing::WithStorableDetails for StorablePropertiesCommand {
137 fn summary(&self) -> crate::api::history::CommandSummary {
138 match self {
139 StorablePropertiesCommand::Init => {
140 CommandSummary::new("cmd-properties-init", self)
141 }
142 StorablePropertiesCommand::UpgradeTo { krill_version } => {
143 CommandSummary::new("cmd-properties-krill-upgrade", self)
144 .arg("version", krill_version)
145 }
146 }
147 }
148
149 fn make_init() -> Self {
150 Self::Init
151 }
152}
153
154
155#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
159#[serde(rename_all = "snake_case")]
160#[serde(tag = "type")]
161pub enum PropertiesEvent {
162 KrillVersionUpgraded {
163 old: KrillVersion,
164 new: KrillVersion,
165 },
166}
167
168impl Event for PropertiesEvent {}
169
170impl fmt::Display for PropertiesEvent {
171 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
172 match self {
173 PropertiesEvent::KrillVersionUpgraded { old, new } => {
174 write!(f, "upgraded Krill from {old} to {new}")
175 }
176 }
177 }
178}
179
180
181#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
185pub struct PropertiesInitEvent {
186 krill_version: KrillVersion,
187}
188
189impl InitEvent for PropertiesInitEvent {}
190
191impl fmt::Display for PropertiesInitEvent {
192 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
193 write!(f, "initialised Krill version {}", self.krill_version)
194 }
195}
196
197
198#[derive(Clone, Debug, Deserialize, Serialize)]
204pub struct Properties {
205 handle: MyHandle,
206 version: u64,
207
208 krill_version: KrillVersion,
209}
210
211impl Aggregate for Properties {
212 type Command = PropertiesCommand;
213 type StorableCommandDetails = StorablePropertiesCommand;
214 type Event = PropertiesEvent;
215
216 type InitCommand = PropertiesInitCommand;
217 type InitEvent = PropertiesInitEvent;
218
219 type Error = Error;
220
221 fn init(handle: &MyHandle, event: PropertiesInitEvent) -> Self {
222 Properties {
223 handle: handle.clone(),
224 version: 1, krill_version: event.krill_version,
226 }
227 }
228
229 fn process_init_command(
230 command: PropertiesInitCommand,
231 ) -> Result<Self::InitEvent, Self::Error> {
232 Ok(PropertiesInitEvent {
233 krill_version: command.into_details().krill_version,
234 })
235 }
236
237 fn version(&self) -> u64 {
238 self.version
239 }
240
241 fn increment_version(&mut self) {
242 self.version += 1;
243 }
244
245 fn apply(&mut self, event: Self::Event) {
246 match event {
247 PropertiesEvent::KrillVersionUpgraded { new, .. } => {
248 self.krill_version = new
249 }
250 }
251 }
252
253 fn process_command(
254 &self,
255 command: Self::Command,
256 ) -> Result<Vec<Self::Event>, Self::Error> {
257 if log_enabled!(log::Level::Trace) {
258 trace!(
259 "Sending command to Properties '{}', version: {}: {}",
260 self.handle,
261 self.version,
262 command
263 );
264 }
265
266 match command.into_details() {
267 PropertiesCommandDetails::UpgradeTo { krill_version } => {
268 if krill_version > self.krill_version {
270 Ok(vec![PropertiesEvent::KrillVersionUpgraded {
271 old: self.krill_version.clone(),
272 new: krill_version,
273 }])
274 } else {
275 Err(Error::Custom(format!(
276 "Can only upgrade Krill to newer versions. Current version: {}, Requested version: {}",
277 self.krill_version, krill_version
278 )))
279 }
280 }
281 }
282 }
283}
284
285pub struct PropertiesManager {
289 store: AggregateStore<Properties>,
290 main_key: MyHandle,
291
292 system_actor: Actor,
295}
296
297impl PropertiesManager {
298 pub fn create(
299 storage_uri: &Url,
300 use_history_cache: bool,
301 ) -> KrillResult<Self> {
302 let main_key = MyHandle::from_str(PROPERTIES_DFLT_NAME).unwrap();
303 AggregateStore::create(storage_uri, PROPERTIES_NS, use_history_cache)
304 .map(|store| PropertiesManager {
305 store,
306 main_key,
307 system_actor: ACTOR_DEF_KRILL,
308 })
309 .map_err(Error::AggregateStoreError)
310 }
311
312 pub fn is_initialized(&self) -> bool {
313 self.store.has(&self.main_key).unwrap_or_default()
314 }
315
316 pub fn init(
317 &self,
318 krill_version: KrillVersion,
319 ) -> KrillResult<Arc<Properties>> {
320 let cmd = PropertiesInitCommand::new(
321 self.main_key.clone(),
322 PropertiesInitCommandDetails { krill_version },
323 &self.system_actor,
324 );
325 self.store.add(cmd)
326 }
327
328 pub fn current_krill_version(&self) -> KrillResult<KrillVersion> {
330 self.properties().map(|p| p.krill_version.clone())
331 }
332
333 pub fn upgrade_krill_version(
335 &self,
336 krill_version: KrillVersion,
337 ) -> KrillResult<()> {
338 let cmd = PropertiesCommand::new(
339 self.main_key.clone(),
340 None,
341 PropertiesCommandDetails::UpgradeTo { krill_version },
342 &self.system_actor,
343 );
344 self.store.command(cmd)?;
345 Ok(())
346 }
347
348 fn properties(&self) -> KrillResult<Arc<Properties>> {
349 self.store.get_latest(&self.main_key)
350 }
351}
352
353
354#[cfg(test)]
357mod tests {
358 use crate::commons::test;
359 use super::*;
360
361 #[test]
362 fn init_properties() {
363 test::test_in_memory(|storage_uri| {
364 let properties_mgr =
365 PropertiesManager::create(storage_uri, false).unwrap();
366
367 assert!(!properties_mgr.is_initialized());
369
370 let init_version = KrillVersion::release(0, 13, 0);
372 properties_mgr.init(init_version.clone()).unwrap();
373 assert_eq!(
374 init_version,
375 properties_mgr.current_krill_version().unwrap()
376 );
377
378 let updated_version = KrillVersion::release(0, 14, 0);
380 properties_mgr
381 .upgrade_krill_version(updated_version.clone())
382 .unwrap();
383 assert_eq!(
384 updated_version,
385 properties_mgr.current_krill_version().unwrap()
386 );
387
388 let downgrade_version = KrillVersion::release(0, 13, 99);
390 assert!(
391 downgrade_version
392 < properties_mgr.current_krill_version().unwrap()
393 );
394 assert!(properties_mgr
395 .upgrade_krill_version(downgrade_version)
396 .is_err());
397 })
398 }
399}
400