mithril_aggregator/commands/
tools_command.rs1use anyhow::Context;
2use clap::{Parser, Subcommand};
3use config::{ConfigBuilder, Map, Value, builder::DefaultState};
4use serde::{Deserialize, Serialize};
5use slog::{Logger, debug};
6use std::{collections::HashMap, path::PathBuf, sync::Arc};
7
8use mithril_common::StdResult;
9use mithril_doc::{Documenter, StructDoc};
10use mithril_persistence::sqlite::{SqliteCleaner, SqliteCleaningTask};
11
12use crate::{
13 ConfigurationSource, ExecutionEnvironment,
14 database::repository::{CertificateRepository, SignedEntityStore},
15 dependency_injection::DependenciesBuilder,
16 extract_all,
17 tools::CertificatesHashMigrator,
18};
19
20#[derive(Debug, Clone, Serialize, Deserialize, Documenter)]
21pub struct ToolsCommandConfiguration {
22 #[example = "`./mithril-aggregator/stores`"]
24 pub data_stores_directory: PathBuf,
25}
26
27impl ConfigurationSource for ToolsCommandConfiguration {
28 fn environment(&self) -> ExecutionEnvironment {
29 ExecutionEnvironment::Production
30 }
31
32 fn data_stores_directory(&self) -> PathBuf {
33 self.data_stores_directory.clone()
34 }
35}
36
37#[derive(Parser, Debug, Clone)]
39pub struct ToolsCommand {
40 #[clap(subcommand)]
42 pub genesis_subcommand: ToolsSubCommand,
43}
44
45impl ToolsCommand {
46 pub async fn execute(
47 &self,
48 root_logger: Logger,
49 config_builder: ConfigBuilder<DefaultState>,
50 ) -> StdResult<()> {
51 self.genesis_subcommand.execute(root_logger, config_builder).await
52 }
53
54 pub fn extract_config(command_path: String) -> HashMap<String, StructDoc> {
55 extract_all!(
56 command_path,
57 ToolsSubCommand,
58 RecomputeCertificatesHash = { RecomputeCertificatesHashCommand },
59 )
60 }
61}
62
63#[derive(Debug, Clone, Subcommand)]
65pub enum ToolsSubCommand {
66 RecomputeCertificatesHash(RecomputeCertificatesHashCommand),
72}
73
74impl ToolsSubCommand {
75 pub async fn execute(
76 &self,
77 root_logger: Logger,
78 config_builder: ConfigBuilder<DefaultState>,
79 ) -> StdResult<()> {
80 match self {
81 Self::RecomputeCertificatesHash(cmd) => cmd.execute(root_logger, config_builder).await,
82 }
83 }
84}
85
86#[derive(Parser, Debug, Clone)]
88pub struct RecomputeCertificatesHashCommand {}
89
90impl RecomputeCertificatesHashCommand {
91 pub async fn execute(
92 &self,
93 root_logger: Logger,
94 config_builder: ConfigBuilder<DefaultState>,
95 ) -> StdResult<()> {
96 let config: ToolsCommandConfiguration = config_builder
97 .build()
98 .with_context(|| "configuration build error")?
99 .try_deserialize()
100 .with_context(|| "configuration deserialize error")?;
101 debug!(root_logger, "RECOMPUTE CERTIFICATES HASH command"; "config" => format!("{config:?}"));
102 println!("Recomputing all certificate hash",);
103 let mut dependencies_builder =
104 DependenciesBuilder::new(root_logger.clone(), Arc::new(config.clone()));
105
106 let dependencies_container = dependencies_builder
107 .create_tools_command_container()
108 .await
109 .with_context(|| "Failed to create the tools command dependencies container")?;
110
111 let migrator = CertificatesHashMigrator::new(
112 CertificateRepository::new(dependencies_container.db_connection.clone()),
113 Arc::new(SignedEntityStore::new(
114 dependencies_container.db_connection.clone(),
115 )),
116 root_logger,
117 );
118
119 migrator
120 .migrate()
121 .await
122 .with_context(|| "recompute-certificates-hash: database migration error")?;
123
124 SqliteCleaner::new(&dependencies_container.db_connection)
125 .with_tasks(&[SqliteCleaningTask::Vacuum])
126 .run()
127 .with_context(|| "recompute-certificates-hash: database vacuum error")?;
128
129 Ok(())
130 }
131
132 pub fn extract_config(command_path: String) -> HashMap<String, StructDoc> {
133 HashMap::from([(command_path, ToolsCommandConfiguration::extract())])
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use std::sync::Arc;
140
141 use mithril_common::temp_dir;
142
143 use crate::test_tools::TestLogger;
144
145 use super::*;
146
147 #[tokio::test]
148 async fn create_container_does_not_panic() {
149 let config = ToolsCommandConfiguration {
150 data_stores_directory: temp_dir!().join("stores"),
151 };
152 let mut dependencies_builder =
153 DependenciesBuilder::new(TestLogger::stdout(), Arc::new(config));
154
155 dependencies_builder
156 .create_tools_command_container()
157 .await
158 .expect("Expected container creation to succeed without panicking");
159 }
160}