1 //! Support Krill upgrades, e.g.:
2 //! - Updating the format of commands or events
3 //! - Export / Import data
4
5 use std::{fmt, path::Path, str::FromStr, sync::Arc};
6
7 use serde::de::DeserializeOwned;
8
9 use crate::{
10 commons::{
11 api::Handle,
12 crypto::KrillSigner,
13 error::KrillIoError,
14 eventsourcing::{AggregateStoreError, CommandKey, KeyStoreKey, KeyValueError, KeyValueStore},
15 util::{file, KrillVersion},
16 },
17 daemon::{config::Config, krillserver::KrillServer},
18 pubd::RepositoryManager,
19 upgrades::v0_9_0::{CaObjectsMigration, PubdObjectsMigration},
20 };
21
22 pub mod v0_9_0;
23
24 pub type UpgradeResult<T> = Result<T, UpgradeError>;
25
26 pub const MIGRATION_SCOPE: &str = "migration";
27
28 //------------ UpgradeError --------------------------------------------------
29
30 #[derive(Debug)]
31 #[allow(clippy::large_enum_variant)]
32 pub enum UpgradeError {
33 AggregateStoreError(AggregateStoreError),
34 KeyStoreError(KeyValueError),
35 IoError(KrillIoError),
36 Unrecognised(String),
37 CannotLoadAggregate(Handle),
38 Custom(String),
39 }
40
41 impl fmt::Display for UpgradeError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result42 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43 match self {
44 UpgradeError::AggregateStoreError(e) => e.fmt(f),
45 UpgradeError::KeyStoreError(e) => e.fmt(f),
46 UpgradeError::IoError(e) => e.fmt(f),
47 UpgradeError::Unrecognised(s) => write!(f, "Unrecognised command summary: {}", s),
48 UpgradeError::CannotLoadAggregate(handle) => write!(f, "Cannot load: {}", handle),
49 UpgradeError::Custom(s) => s.fmt(f),
50 }
51 }
52 }
53 impl UpgradeError {
custom(msg: impl fmt::Display) -> Self54 pub fn custom(msg: impl fmt::Display) -> Self {
55 UpgradeError::Custom(msg.to_string())
56 }
57
unrecognised(msg: impl fmt::Display) -> Self58 pub fn unrecognised(msg: impl fmt::Display) -> Self {
59 UpgradeError::Unrecognised(msg.to_string())
60 }
61 }
62
63 impl From<AggregateStoreError> for UpgradeError {
from(e: AggregateStoreError) -> Self64 fn from(e: AggregateStoreError) -> Self {
65 UpgradeError::AggregateStoreError(e)
66 }
67 }
68
69 impl From<KeyValueError> for UpgradeError {
from(e: KeyValueError) -> Self70 fn from(e: KeyValueError) -> Self {
71 UpgradeError::KeyStoreError(e)
72 }
73 }
74
75 impl From<KrillIoError> for UpgradeError {
from(e: KrillIoError) -> Self76 fn from(e: KrillIoError) -> Self {
77 UpgradeError::IoError(e)
78 }
79 }
80
81 impl From<crate::commons::error::Error> for UpgradeError {
from(e: crate::commons::error::Error) -> Self82 fn from(e: crate::commons::error::Error) -> Self {
83 UpgradeError::Custom(e.to_string())
84 }
85 }
86
87 impl std::error::Error for UpgradeError {}
88
89 //------------ UpgradeStore --------------------------------------------------
90
91 /// Implement this for automatic upgrades to key stores
92 pub trait UpgradeStore {
needs_migrate(&self) -> Result<bool, UpgradeError>93 fn needs_migrate(&self) -> Result<bool, UpgradeError>;
migrate(&self) -> Result<(), UpgradeError>94 fn migrate(&self) -> Result<(), UpgradeError>;
95
version_before(kv: &KeyValueStore, later: KrillVersion) -> Result<bool, UpgradeError>96 fn version_before(kv: &KeyValueStore, later: KrillVersion) -> Result<bool, UpgradeError> {
97 kv.version_is_before(later).map_err(UpgradeError::KeyStoreError)
98 }
99
store(&self) -> &KeyValueStore100 fn store(&self) -> &KeyValueStore;
101
102 // Find all command keys and sort them by sequence.
103 // Then turn them back into key store keys for further processing.
command_keys(&self, scope: &str) -> Result<Vec<KeyStoreKey>, UpgradeError>104 fn command_keys(&self, scope: &str) -> Result<Vec<KeyStoreKey>, UpgradeError> {
105 let store = self.store();
106 let keys = store.keys(Some(scope.to_string()), "command--")?;
107 let mut cmd_keys: Vec<CommandKey> = vec![];
108 for key in keys {
109 let cmd_key = CommandKey::from_str(key.name()).map_err(|_| {
110 UpgradeError::Custom(format!("Found invalid command key: {} for ca: {}", key.name(), scope))
111 })?;
112 cmd_keys.push(cmd_key);
113 }
114 cmd_keys.sort_by_key(|k| k.sequence);
115 let cmd_keys = cmd_keys
116 .into_iter()
117 .map(|ck| KeyStoreKey::scoped(scope.to_string(), format!("{}.json", ck)))
118 .collect();
119
120 Ok(cmd_keys)
121 }
122
get<V: DeserializeOwned>(&self, key: &KeyStoreKey) -> Result<V, UpgradeError>123 fn get<V: DeserializeOwned>(&self, key: &KeyStoreKey) -> Result<V, UpgradeError> {
124 self.store()
125 .get(key)?
126 .ok_or_else(|| UpgradeError::Custom(format!("Cannot read key: {}", key)))
127 }
128
event_key(scope: &str, nr: u64) -> KeyStoreKey129 fn event_key(scope: &str, nr: u64) -> KeyStoreKey {
130 KeyStoreKey::scoped(scope.to_string(), format!("delta-{}.json", nr))
131 }
132
archive_snapshots(&self, scope: &str) -> Result<(), UpgradeError>133 fn archive_snapshots(&self, scope: &str) -> Result<(), UpgradeError> {
134 let snapshot_key = KeyStoreKey::scoped(scope.to_string(), "snapshot.json".to_string());
135 let snapshot_bk_key = KeyStoreKey::scoped(scope.to_string(), "snapshot-bk.json".to_string());
136
137 if self.store().has(&snapshot_key)? {
138 self.archive_to_migration_scope(&snapshot_key)?;
139 }
140
141 if self.store().has(&snapshot_bk_key)? {
142 self.archive_to_migration_scope(&snapshot_bk_key)?;
143 }
144
145 Ok(())
146 }
147
archive_to_migration_scope(&self, key: &KeyStoreKey) -> Result<(), UpgradeError>148 fn archive_to_migration_scope(&self, key: &KeyStoreKey) -> Result<(), UpgradeError> {
149 self.store()
150 .archive_to(key, MIGRATION_SCOPE)
151 .map_err(UpgradeError::KeyStoreError)
152 }
153
drop_migration_scope(&self, scope: &str) -> Result<(), UpgradeError>154 fn drop_migration_scope(&self, scope: &str) -> Result<(), UpgradeError> {
155 let scope = format!("{}/{}", scope, MIGRATION_SCOPE);
156 self.store().drop_scope(&scope).map_err(UpgradeError::KeyStoreError)
157 }
158 }
159
160 /// Should be called when Krill starts, before the KrillServer is initiated
pre_start_upgrade(config: Arc<Config>) -> Result<(), UpgradeError>161 pub fn pre_start_upgrade(config: Arc<Config>) -> Result<(), UpgradeError> {
162 upgrade_data_to_0_9_0(config)
163 }
164
165 /// Should be called when the KrillServer is initiated, before the webserver is started
166 /// and operators can make changes.
post_start_upgrade(config: &Config, server: &KrillServer) -> Result<(), UpgradeError>167 pub async fn post_start_upgrade(config: &Config, server: &KrillServer) -> Result<(), UpgradeError> {
168 if needs_upgrade(&config.data_dir, "cas", KrillVersion::candidate(0, 9, 3, 2)) {
169 info!("Reissue ROAs on upgrade to force short EE certificate subjects in the objects");
170 server.force_renew_roas().await.map_err(|e| e.into())
171 } else {
172 Ok(())
173 }
174 }
175
update_storage_version(work_dir: &Path) -> Result<(), UpgradeError>176 pub async fn update_storage_version(work_dir: &Path) -> Result<(), UpgradeError> {
177 let current = KrillVersion::current();
178
179 if needs_v0_9_0_upgrade(work_dir, "cas") {
180 debug!("Updating version file for cas");
181 file::save_json(¤t, &work_dir.join("cas/version"))?;
182 }
183
184 if needs_v0_9_0_upgrade(work_dir, "pubd") {
185 debug!("Updating version file for pubd");
186 file::save_json(¤t, &work_dir.join("pubd/version"))?;
187 }
188
189 Ok(())
190 }
191
upgrade_data_to_0_9_0(config: Arc<Config>) -> Result<(), UpgradeError>192 fn upgrade_data_to_0_9_0(config: Arc<Config>) -> Result<(), UpgradeError> {
193 let work_dir = &config.data_dir;
194 if needs_v0_9_0_upgrade(work_dir, "pubd") {
195 PubdObjectsMigration::migrate(config.clone())?;
196 }
197
198 if needs_v0_9_0_upgrade(work_dir, "cas") {
199 let signer = Arc::new(KrillSigner::build(work_dir)?);
200 let repo_manager = RepositoryManager::build(config.clone(), signer)?;
201
202 CaObjectsMigration::migrate(config, repo_manager)?;
203 }
204
205 Ok(())
206 }
207
needs_v0_9_0_upgrade(work_dir: &Path, ns: &str) -> bool208 fn needs_v0_9_0_upgrade(work_dir: &Path, ns: &str) -> bool {
209 needs_upgrade(work_dir, ns, KrillVersion::release(0, 9, 0))
210 }
211
needs_upgrade(work_dir: &Path, ns: &str, before: KrillVersion) -> bool212 fn needs_upgrade(work_dir: &Path, ns: &str, before: KrillVersion) -> bool {
213 let keystore_path = work_dir.join(ns);
214 if keystore_path.exists() {
215 let version_path = keystore_path.join("version");
216 let version_found = file::load_json(&version_path).unwrap_or_else(|_| KrillVersion::v0_5_0_or_before());
217 version_found < before
218 } else {
219 false
220 }
221 }
222
223 //------------ Tests ---------------------------------------------------------
224
225 #[cfg(test)]
226 mod tests {
227
228 use std::{fs, path::PathBuf};
229
230 use crate::commons::util::file;
231 use crate::test::tmp_dir;
232
233 use super::*;
234
235 #[test]
test_upgrade_0_8_1()236 fn test_upgrade_0_8_1() {
237 let work_dir = tmp_dir();
238 let source = PathBuf::from("test-resources/migrations/v0_8_1/");
239 file::backup_dir(&source, &work_dir).unwrap();
240
241 let config = Arc::new(Config::test(&work_dir, false, false, false));
242 let _ = config.init_logging();
243
244 upgrade_data_to_0_9_0(config).unwrap();
245
246 let _ = fs::remove_dir_all(work_dir);
247 }
248
249 #[test]
test_upgrade_0_6_0()250 fn test_upgrade_0_6_0() {
251 let work_dir = tmp_dir();
252 let source = PathBuf::from("test-resources/migrations/v0_6_0/");
253 file::backup_dir(&source, &work_dir).unwrap();
254
255 let config = Arc::new(Config::test(&work_dir, false, false, false));
256 let _ = config.init_logging();
257
258 upgrade_data_to_0_9_0(config).unwrap();
259
260 let _ = fs::remove_dir_all(work_dir);
261 }
262 }
263