Skip to content

Commit e60cd61

Browse files
committed
feat(client-cli): implement download of Cardano node distribution to retrieve the snapshot-converter binary
1 parent b532d2f commit e60cd61

File tree

12 files changed

+567
-6
lines changed

12 files changed

+567
-6
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-client-cli/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ indicatif = { version = "0.17.11", features = ["tokio"] }
3838
mithril-cli-helper = { path = "../internal/mithril-cli-helper" }
3939
mithril-client = { path = "../mithril-client", features = ["fs", "unstable"] }
4040
mithril-doc = { path = "../internal/mithril-doc" }
41+
reqwest = { workspace = true, features = [
42+
"default",
43+
"gzip",
44+
"zstd",
45+
"deflate",
46+
"brotli"
47+
] }
4148
serde = { workspace = true }
4249
serde_json = { workspace = true }
4350
slog = { workspace = true, features = [
@@ -52,3 +59,4 @@ tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
5259

5360
[dev-dependencies]
5461
mithril-common = { path = "../mithril-common", features = ["test_tools"] }
62+
mockall = { workspace = true }

mithril-client-cli/src/commands/tools/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
66
mod snapshot_converter;
77

8-
use mithril_client::MithrilResult;
98
pub use snapshot_converter::*;
109

1110
use clap::Subcommand;
11+
use mithril_client::MithrilResult;
1212

1313
/// Tools commands
1414
#[derive(Subcommand, Debug, Clone)]
Lines changed: 226 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,236 @@
1-
use clap::Parser;
1+
use std::{
2+
env, fmt,
3+
path::{Path, PathBuf},
4+
};
5+
6+
use anyhow::{anyhow, Context};
7+
use clap::{Parser, ValueEnum};
28

39
use mithril_client::MithrilResult;
410

11+
use crate::utils::{
12+
GitHubReleaseRetriever, HttpDownloader, ReqwestGitHubApiClient, ReqwestHttpDownloader,
13+
};
14+
15+
const GITHUB_ORGANIZATION: &str = "IntersectMBO";
16+
const GITHUB_REPOSITORY: &str = "cardano-node";
17+
18+
const LATEST_DISTRIBUTION_TAG: &str = "latest";
19+
const PRERELEASE_DISTRIBUTION_TAG: &str = "prerelease";
20+
21+
const CARDANO_DISTRIBUTION_TEMP_DIR: &str = "cardano-node-distribution-tmp";
22+
23+
#[derive(Debug, Clone, ValueEnum)]
24+
enum UTxOHDFlavor {
25+
#[clap(name = "Legacy")]
26+
Legacy,
27+
#[clap(name = "LMDB")]
28+
Lmdb,
29+
}
30+
31+
impl fmt::Display for UTxOHDFlavor {
32+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33+
match self {
34+
Self::Legacy => write!(f, "Legacy"),
35+
Self::Lmdb => write!(f, "LMDB"),
36+
}
37+
}
38+
}
39+
40+
/// Clap command to convert a restored `InMemory` Mithril snapshot to another flavor.
541
#[derive(Parser, Debug, Clone)]
6-
pub struct SnapshotConverterCommand {}
42+
pub struct SnapshotConverterCommand {
43+
/// Path to the Cardano node database directory.
44+
#[clap(long)]
45+
db_directory: PathBuf,
46+
47+
/// Cardano node version of the Mithril signed snapshot.
48+
///
49+
/// `latest` and `prerelease` are also supported to download the latest or preprelease distribution.
50+
#[clap(long)]
51+
cardano_node_version: String,
52+
53+
/// UTxO-HD flavor to convert the ledger snapshot to.
54+
#[clap(long)]
55+
utxo_hd_flavor: UTxOHDFlavor,
56+
}
757

858
impl SnapshotConverterCommand {
959
/// Main command execution
1060
pub async fn execute(&self) -> MithrilResult<()> {
11-
todo!()
61+
let distribution_temp_dir = self.db_directory.join(CARDANO_DISTRIBUTION_TEMP_DIR);
62+
std::fs::create_dir(&distribution_temp_dir).with_context(|| {
63+
format!(
64+
"Failed to create directory: {}",
65+
distribution_temp_dir.display()
66+
)
67+
})?;
68+
69+
let archive_path = Self::download_cardano_node_distribution(
70+
ReqwestGitHubApiClient::new()?,
71+
ReqwestHttpDownloader::new()?,
72+
&self.cardano_node_version,
73+
&distribution_temp_dir,
74+
)
75+
.await
76+
.with_context(|| {
77+
"Failed to download 'snapshot-converter' binary from Cardano node distribution"
78+
})?;
79+
80+
Ok(())
81+
}
82+
83+
async fn download_cardano_node_distribution(
84+
github_api_client: impl GitHubReleaseRetriever,
85+
http_downloader: impl HttpDownloader,
86+
tag: &str,
87+
target_dir: &Path,
88+
) -> MithrilResult<PathBuf> {
89+
let release = match tag {
90+
LATEST_DISTRIBUTION_TAG => github_api_client
91+
.get_latest_release(GITHUB_ORGANIZATION, GITHUB_REPOSITORY)
92+
.await
93+
.with_context(|| "Failed to get latest release")?,
94+
PRERELEASE_DISTRIBUTION_TAG => github_api_client
95+
.get_prerelease(GITHUB_ORGANIZATION, GITHUB_REPOSITORY)
96+
.await
97+
.with_context(|| "Failed to get prerelease")?,
98+
_ => github_api_client
99+
.get_release_by_tag(GITHUB_ORGANIZATION, GITHUB_REPOSITORY, tag)
100+
.await
101+
.with_context(|| format!("Failed to get release by tag: {}", tag))?,
102+
};
103+
104+
let asset = release
105+
.get_asset_for_os(env::consts::OS)?
106+
.ok_or_else(|| anyhow!("No asset found for platform: {}", env::consts::OS))
107+
.with_context(|| {
108+
format!(
109+
"Failed to find asset for current platform: {}",
110+
env::consts::OS
111+
)
112+
})?;
113+
114+
let archive_path = http_downloader
115+
.download_file(asset.browser_download_url.parse()?, target_dir, &asset.name)
116+
.await?;
117+
118+
Ok(archive_path)
119+
}
120+
}
121+
122+
#[cfg(test)]
123+
mod tests {
124+
use mockall::predicate::eq;
125+
use reqwest::Url;
126+
127+
use mithril_common::temp_dir_create;
128+
129+
use crate::utils::{GitHubRelease, MockGitHubReleaseRetriever, MockHttpDownloader};
130+
131+
use super::*;
132+
133+
#[tokio::test]
134+
async fn call_get_latest_release_with_latest_tag() {
135+
let temp_dir = temp_dir_create!();
136+
let release = GitHubRelease::dummy_with_all_supported_assets();
137+
let asset = release.get_asset_for_os(env::consts::OS).unwrap().unwrap();
138+
139+
let cloned_release = release.clone();
140+
let mut github_api_client = MockGitHubReleaseRetriever::new();
141+
github_api_client
142+
.expect_get_latest_release()
143+
.with(eq(GITHUB_ORGANIZATION), eq(GITHUB_REPOSITORY))
144+
.returning(move |_, _| Ok(cloned_release.clone()));
145+
146+
let mut http_downloader = MockHttpDownloader::new();
147+
http_downloader
148+
.expect_download_file()
149+
.with(
150+
eq(Url::parse(&asset.browser_download_url).unwrap()),
151+
eq(temp_dir.clone()),
152+
eq(asset.name.clone()),
153+
)
154+
.returning(|_, _, _| Ok(PathBuf::new()));
155+
156+
SnapshotConverterCommand::download_cardano_node_distribution(
157+
github_api_client,
158+
http_downloader,
159+
LATEST_DISTRIBUTION_TAG,
160+
&temp_dir,
161+
)
162+
.await
163+
.unwrap();
164+
}
165+
166+
#[tokio::test]
167+
async fn call_get_prerelease_with_prerelease_tag() {
168+
let temp_dir = temp_dir_create!();
169+
let release = GitHubRelease::dummy_with_all_supported_assets();
170+
let asset = release.get_asset_for_os(env::consts::OS).unwrap().unwrap();
171+
172+
let cloned_release = release.clone();
173+
let mut github_api_client = MockGitHubReleaseRetriever::new();
174+
github_api_client
175+
.expect_get_prerelease()
176+
.with(eq(GITHUB_ORGANIZATION), eq(GITHUB_REPOSITORY))
177+
.returning(move |_, _| Ok(cloned_release.clone()));
178+
179+
let mut http_downloader = MockHttpDownloader::new();
180+
http_downloader
181+
.expect_download_file()
182+
.with(
183+
eq(Url::parse(&asset.browser_download_url).unwrap()),
184+
eq(temp_dir.clone()),
185+
eq(asset.name.clone()),
186+
)
187+
.returning(|_, _, _| Ok(PathBuf::new()));
188+
189+
SnapshotConverterCommand::download_cardano_node_distribution(
190+
github_api_client,
191+
http_downloader,
192+
PRERELEASE_DISTRIBUTION_TAG,
193+
&temp_dir,
194+
)
195+
.await
196+
.unwrap();
197+
}
198+
199+
#[tokio::test]
200+
async fn call_get_release_by_tag_with_specific_cardano_node_version() {
201+
let cardano_node_version = "10.3.1";
202+
let temp_dir = temp_dir_create!();
203+
let release = GitHubRelease::dummy_with_all_supported_assets();
204+
let asset = release.get_asset_for_os(env::consts::OS).unwrap().unwrap();
205+
206+
let cloned_release = release.clone();
207+
let mut github_api_client = MockGitHubReleaseRetriever::new();
208+
github_api_client
209+
.expect_get_release_by_tag()
210+
.with(
211+
eq(GITHUB_ORGANIZATION),
212+
eq(GITHUB_REPOSITORY),
213+
eq(cardano_node_version),
214+
)
215+
.returning(move |_, _, _| Ok(cloned_release.clone()));
216+
217+
let mut http_downloader = MockHttpDownloader::new();
218+
http_downloader
219+
.expect_download_file()
220+
.with(
221+
eq(Url::parse(&asset.browser_download_url).unwrap()),
222+
eq(temp_dir.clone()),
223+
eq(asset.name.clone()),
224+
)
225+
.returning(|_, _, _| Ok(PathBuf::new()));
226+
227+
SnapshotConverterCommand::download_cardano_node_distribution(
228+
github_api_client,
229+
http_downloader,
230+
cardano_node_version,
231+
&temp_dir,
232+
)
233+
.await
234+
.unwrap();
12235
}
13236
}

mithril-client-cli/src/main.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,6 @@ mod tests {
298298
"snapshot-converter",
299299
"--db-directory",
300300
"whatever",
301-
"--cardano-network",
302-
"preview",
303301
"--cardano-node-version",
304302
"1.2.3",
305303
"--utxo-hd-flavor",

0 commit comments

Comments
 (0)