253: Monero rpc installer for windows r=rishflab a=rishflab

I could not find an async zip archive extractor. The download is still async. One potential follow up could be to spawn the extraction of the zip in the windows path.

Closes #228 

Co-authored-by: rishflab <rishflab@hotmail.com>
This commit is contained in:
bors[bot] 2021-03-02 04:29:31 +00:00 committed by GitHub
commit 31475ab07d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 169 additions and 36 deletions

View File

@ -44,12 +44,14 @@ jobs:
build:
strategy:
matrix:
target: [ x86_64-unknown-linux-gnu, x86_64-apple-darwin ]
target: [ x86_64-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc ]
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-apple-darwin
os: macos-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
runs-on: ${{ matrix.os }}
steps:

View File

@ -14,6 +14,8 @@ jobs:
os: ubuntu-latest
- target: x86_64-apple-darwin
os: macos-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
runs-on: ${{ matrix.os }}
steps:
- name: Checkout tagged commit

66
Cargo.lock generated
View File

@ -1,5 +1,11 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aead"
version = "0.3.2"
@ -121,7 +127,7 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b72c1f1154e234325b50864a349b9c8e56939e266a4c307c0f159812df2f9537"
dependencies = [
"bzip2",
"bzip2 0.4.2",
"futures-core",
"memchr",
"pin-project-lite",
@ -498,6 +504,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
[[package]]
name = "bzip2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2"
version = "0.4.2"
@ -1068,6 +1084,18 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
[[package]]
name = "flate2"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42"
dependencies = [
"cfg-if 0.1.10",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -1979,6 +2007,15 @@ dependencies = [
"serde",
]
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "mio"
version = "0.7.7"
@ -3580,7 +3617,7 @@ dependencies = [
"tempfile",
"testcontainers 0.12.0",
"thiserror",
"time",
"time 0.2.24",
"tokio",
"tokio-tar",
"tokio-tungstenite",
@ -3593,6 +3630,7 @@ dependencies = [
"url",
"uuid",
"void",
"zip",
]
[[package]]
@ -3722,6 +3760,16 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
"winapi 0.3.9",
]
[[package]]
name = "time"
version = "0.2.24"
@ -4452,3 +4500,17 @@ dependencies = [
"syn",
"synstructure",
]
[[package]]
name = "zip"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a8977234acab718eb2820494b2f96cbb16004c19dddf88b7445b27381450997"
dependencies = [
"byteorder",
"bzip2 0.3.3",
"crc32fast",
"flate2",
"thiserror",
"time 0.1.43",
]

View File

@ -2,6 +2,7 @@ status = [
"static_analysis",
"build (x86_64-unknown-linux-gnu)",
"build (x86_64-apple-darwin)",
"build (x86_64-pc-windows-msvc)",
"test (x86_64-unknown-linux-gnu)",
"test (x86_64-apple-darwin)",
"docker_tests (happy_path)",

View File

@ -51,7 +51,6 @@ strum = { version = "0.20", features = ["derive"] }
thiserror = "1"
time = "0.2"
tokio = { version = "1.0", features = ["rt-multi-thread", "time", "macros", "sync", "process", "fs"] }
tokio-tar = { path = "../tokio-tar" }
tokio-tungstenite = { version = "0.13", features = [ "tls" ] }
tokio-util = { version = "0.6.3", features = ["io"] }
toml = "0.5"
@ -63,6 +62,12 @@ url = { version = "2.1", features = ["serde"] }
uuid = { version = "0.8", features = ["serde", "v4"] }
void = "1"
[target.'cfg(not(windows))'.dependencies]
tokio-tar = { path = "../tokio-tar" }
[target.'cfg(windows)'.dependencies]
zip = "0.5"
[dev-dependencies]
bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs" }
get-port = "3"

View File

@ -1,6 +1,5 @@
use ::monero::Network;
use anyhow::{Context, Result};
use async_compression::tokio::bufread::BzDecoder;
use big_bytes::BigByte;
use futures::{StreamExt, TryStreamExt};
use reqwest::{header::CONTENT_LENGTH, Url};
@ -14,23 +13,33 @@ use tokio::{
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
process::{Child, Command},
};
use tokio_tar::Archive;
use tokio_util::{
codec::{BytesCodec, FramedRead},
io::StreamReader,
};
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
compile_error!("unsupported operating system");
#[cfg(target_os = "macos")]
const DOWNLOAD_URL: &str = "http://downloads.getmonero.org/cli/monero-mac-x64-v0.17.1.9.tar.bz2";
#[cfg(target_os = "linux")]
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-linux-x64-v0.17.1.9.tar.bz2";
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
compile_error!("unsupported operating system");
#[cfg(target_os = "windows")]
const DOWNLOAD_URL: &str = "https://downloads.getmonero.org/cli/monero-win-x64-v0.17.1.9.zip";
#[cfg(any(target_os = "macos", target_os = "linux"))]
const PACKED_FILE: &str = "monero-wallet-rpc";
#[cfg(target_os = "windows")]
const PACKED_FILE: &str = "monero-wallet-rpc.exe";
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("monero wallet rpc executable not found in downloaded archive")]
pub struct ExecutableNotFoundInArchive;
pub struct WalletRpcProcess {
_child: Child,
port: u16,
@ -59,8 +68,8 @@ impl WalletRpc {
working_dir: working_dir.to_path_buf(),
};
if monero_wallet_rpc.tar_path().exists() {
remove_file(monero_wallet_rpc.tar_path()).await?;
if monero_wallet_rpc.archive_path().exists() {
remove_file(monero_wallet_rpc.archive_path()).await?;
}
if !monero_wallet_rpc.exec_path().exists() {
@ -69,7 +78,7 @@ impl WalletRpc {
.read(true)
.write(true)
.create_new(true)
.open(monero_wallet_rpc.tar_path())
.open(monero_wallet_rpc.archive_path())
.await?;
let response = reqwest::get(DOWNLOAD_URL).await?;
@ -88,43 +97,28 @@ impl WalletRpc {
.bytes_stream()
.map_err(|err| std::io::Error::new(ErrorKind::Other, err));
#[cfg(not(target_os = "windows"))]
let mut stream = FramedRead::new(
BzDecoder::new(StreamReader::new(byte_stream)),
async_compression::tokio::bufread::BzDecoder::new(StreamReader::new(byte_stream)),
BytesCodec::new(),
)
.map_ok(|bytes| bytes.freeze());
#[cfg(target_os = "windows")]
let mut stream = FramedRead::new(StreamReader::new(byte_stream), BytesCodec::new())
.map_ok(|bytes| bytes.freeze());
while let Some(chunk) = stream.next().await {
file.write(&chunk?).await?;
}
file.flush().await?;
let mut options = OpenOptions::new();
let file = options
.read(true)
.open(monero_wallet_rpc.tar_path())
.await?;
let mut ar = Archive::new(file);
let mut entries = ar.entries()?;
while let Some(file) = entries.next().await {
let mut f = file?;
if f.path()?
.to_str()
.context("Could not find convert path to str in tar ball")?
.contains(PACKED_FILE)
{
f.unpack(monero_wallet_rpc.exec_path()).await?;
}
}
remove_file(monero_wallet_rpc.tar_path()).await?;
Self::extract_archive(&monero_wallet_rpc).await?;
}
Ok(monero_wallet_rpc)
}
pub async fn run(&self, network: Network, daemon_host: &str) -> Result<WalletRpcProcess> {
let port = tokio::net::TcpListener::bind("127.0.0.1:0")
.await?
@ -169,11 +163,78 @@ impl WalletRpc {
})
}
fn tar_path(&self) -> PathBuf {
self.working_dir.join("monero-cli-wallet.tar")
fn archive_path(&self) -> PathBuf {
self.working_dir.join("monero-cli-wallet.archive")
}
fn exec_path(&self) -> PathBuf {
self.working_dir.join(PACKED_FILE)
}
#[cfg(not(target_os = "windows"))]
async fn extract_archive(monero_wallet_rpc: &Self) -> Result<()> {
use anyhow::bail;
use tokio_tar::Archive;
let mut options = OpenOptions::new();
let file = options
.read(true)
.open(monero_wallet_rpc.archive_path())
.await?;
let mut ar = Archive::new(file);
let mut entries = ar.entries()?;
loop {
match entries.next().await {
Some(file) => {
let mut f = file?;
if f.path()?
.to_str()
.context("Could not find convert path to str in tar ball")?
.contains(PACKED_FILE)
{
f.unpack(monero_wallet_rpc.exec_path()).await?;
break;
}
}
None => bail!(ExecutableNotFoundInArchive),
}
}
remove_file(monero_wallet_rpc.archive_path()).await?;
Ok(())
}
#[cfg(target_os = "windows")]
async fn extract_archive(monero_wallet_rpc: &Self) -> Result<()> {
use std::fs::File;
use tokio::task::JoinHandle;
use zip::ZipArchive;
let archive_path = monero_wallet_rpc.archive_path();
let exec_path = monero_wallet_rpc.exec_path();
let extract: JoinHandle<Result<()>> = tokio::task::spawn_blocking(|| {
let file = File::open(archive_path)?;
let mut zip = ZipArchive::new(file)?;
let name = zip
.file_names()
.find(|name| name.contains(PACKED_FILE))
.context(ExecutableNotFoundInArchive)?
.to_string();
let mut rpc = zip.by_name(&name)?;
let mut file = File::create(exec_path)?;
std::io::copy(&mut rpc, &mut file)?;
Ok(())
});
extract.await??;
remove_file(monero_wallet_rpc.archive_path()).await?;
Ok(())
}
}