From 0739690dcb4c8ec285fee3787625b786d78efc4d Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 1 Jul 2021 16:51:03 -0600 Subject: [PATCH] appmgr inspect is back --- appmgr/src/bin/embassy-cli.rs | 17 ++++++++-- appmgr/src/bin/embassy-sdk.rs | 18 ++++++++-- appmgr/src/context/mod.rs | 12 +++++++ appmgr/src/context/rpc.rs | 9 +++++ appmgr/src/db/model.rs | 2 +- appmgr/src/error.rs | 2 +- appmgr/src/inspect.rs | 64 +++++++++++++++++++++++++++++++++++ appmgr/src/install/mod.rs | 35 ++++++++++++++++--- appmgr/src/lib.rs | 13 +++++-- appmgr/src/s9pk/reader.rs | 2 +- 10 files changed, 160 insertions(+), 14 deletions(-) create mode 100644 appmgr/src/inspect.rs diff --git a/appmgr/src/bin/embassy-cli.rs b/appmgr/src/bin/embassy-cli.rs index 083f6a492..61d5008d0 100644 --- a/appmgr/src/bin/embassy-cli.rs +++ b/appmgr/src/bin/embassy-cli.rs @@ -2,6 +2,8 @@ use clap::Arg; use embassy::context::{CliContext, EitherContext}; use embassy::Error; use rpc_toolkit::run_cli; +use rpc_toolkit::yajrc::RpcError; +use serde_json::Value; fn inner_main() -> Result<(), Error> { run_cli!( @@ -33,8 +35,19 @@ fn inner_main() -> Result<(), Error> { }); EitherContext::Cli(CliContext::init(matches)?) }, - |code| if code < 0 { 1 } else { code } - ) + |e: RpcError| { + match e.data { + Some(Value::String(s)) => eprintln!("{}: {}", e.message, s), + Some(Value::Object(o)) => if let Some(Value::String(s)) = o.get("message") { + eprintln!("{}: {}", e.message, s) + } + Some(a) => eprintln!("{}: {}", e.message, a), + None => eprintln!("{}", e.message), + } + std::process::exit(e.code); + } + ); + Ok(()) } fn main() { diff --git a/appmgr/src/bin/embassy-sdk.rs b/appmgr/src/bin/embassy-sdk.rs index f4d32f9bf..914db6fbc 100644 --- a/appmgr/src/bin/embassy-sdk.rs +++ b/appmgr/src/bin/embassy-sdk.rs @@ -1,7 +1,8 @@ -use clap::Arg; use embassy::context::{CliContext, EitherContext}; use embassy::Error; use rpc_toolkit::run_cli; +use rpc_toolkit::yajrc::RpcError; +use serde_json::Value; fn inner_main() -> Result<(), Error> { run_cli!( @@ -31,8 +32,19 @@ fn inner_main() -> Result<(), Error> { }); EitherContext::Cli(CliContext::init(matches)?) }, - |code| if code < 0 { 1 } else { code } - ) + |e: RpcError| { + match e.data { + Some(Value::String(s)) => eprintln!("{}: {}", e.message, s), + Some(Value::Object(o)) => if let Some(Value::String(s)) = o.get("message") { + eprintln!("{}: {}", e.message, s) + } + Some(a) => eprintln!("{}: {}", e.message, a), + None => eprintln!("{}", e.message), + } + std::process::exit(e.code); + } + ); + Ok(()) } fn main() { diff --git a/appmgr/src/context/mod.rs b/appmgr/src/context/mod.rs index 6936538e1..1c92b16e9 100644 --- a/appmgr/src/context/mod.rs +++ b/appmgr/src/context/mod.rs @@ -74,6 +74,18 @@ impl EitherContext { _ => None, } } + pub fn to_cli(self) -> Option { + match self { + EitherContext::Cli(a) => Some(a), + _ => None, + } + } + pub fn to_rpc(self) -> Option { + match self { + EitherContext::Rpc(a) => Some(a), + _ => None, + } + } } impl Context for EitherContext { fn host(&self) -> Host<&str> { diff --git a/appmgr/src/context/rpc.rs b/appmgr/src/context/rpc.rs index 62fa0c247..a20a88eca 100644 --- a/appmgr/src/context/rpc.rs +++ b/appmgr/src/context/rpc.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use bollard::Docker; use patch_db::PatchDb; +use reqwest::Url; use rpc_toolkit::url::Host; use rpc_toolkit::Context; use serde::Deserialize; @@ -83,6 +84,14 @@ impl RpcContext { }); Ok(Self(seed)) } + pub async fn package_registry_url(&self) -> Result { + Ok(crate::db::DatabaseModel::new() + .server_info() + .registry() + .get(&mut self.db.handle()) + .await? + .to_owned()) + } } impl Context for RpcContext { fn host(&self) -> Host<&str> { diff --git a/appmgr/src/db/model.rs b/appmgr/src/db/model.rs index 2b2794b27..433077fdc 100644 --- a/appmgr/src/db/model.rs +++ b/appmgr/src/db/model.rs @@ -43,7 +43,7 @@ impl Database { .parse() .unwrap(), updating: false, - registry: "https://registry.start9.com".parse().unwrap(), + registry: "https://beta-registry-0-3.start9labs.com".parse().unwrap(), unread_notification_count: 0, }, package_data: AllPackageData::default(), diff --git a/appmgr/src/error.rs b/appmgr/src/error.rs index 94a44fecd..06b426bd2 100644 --- a/appmgr/src/error.rs +++ b/appmgr/src/error.rs @@ -183,7 +183,7 @@ impl From for Error { impl From for RpcError { fn from(e: Error) -> Self { let mut data_object = serde_json::Map::with_capacity(2); - data_object.insert("message".to_owned(), format!("{}", e).into()); + data_object.insert("message".to_owned(), format!("{}", e.source).into()); data_object.insert( "revision".to_owned(), match serde_json::to_value(&e.revision) { diff --git a/appmgr/src/inspect.rs b/appmgr/src/inspect.rs new file mode 100644 index 000000000..5de24ce9f --- /dev/null +++ b/appmgr/src/inspect.rs @@ -0,0 +1,64 @@ +use std::path::PathBuf; + +use rpc_toolkit::command; + +use crate::context::{CliContext, EitherContext}; +use crate::s9pk::manifest::Manifest; +use crate::s9pk::reader::S9pkReader; +use crate::util::{display_none, display_serializable, IoFormat}; +use crate::Error; + +#[command(subcommands(manifest, license, icon, instructions, docker_images))] +pub fn inspect(#[context] ctx: EitherContext) -> Result<(), Error> { + Ok(()) +} + +#[command(cli_only, display(display_serializable))] +pub async fn manifest( + #[arg] path: PathBuf, + #[allow(unused_variables)] + #[arg(long = "format")] + format: Option, +) -> Result { + S9pkReader::open(path).await?.manifest().await +} + +#[command(cli_only, display(display_none))] +pub async fn license(#[arg] path: PathBuf) -> Result<(), Error> { + tokio::io::copy( + &mut S9pkReader::open(path).await?.license().await?, + &mut tokio::io::stdout(), + ) + .await?; + Ok(()) +} + +#[command(cli_only, display(display_none))] +pub async fn icon(#[arg] path: PathBuf) -> Result<(), Error> { + tokio::io::copy( + &mut S9pkReader::open(path).await?.icon().await?, + &mut tokio::io::stdout(), + ) + .await?; + Ok(()) +} + +#[command(cli_only, display(display_none))] +pub async fn instructions(#[arg] path: PathBuf) -> Result<(), Error> { + tokio::io::copy( + &mut S9pkReader::open(path).await?.instructions().await?, + &mut tokio::io::stdout(), + ) + .await?; + Ok(()) +} + +#[command(cli_only, display(display_none), rename = "docker-images")] +pub async fn docker_images(#[arg] path: PathBuf) -> Result<(), Error> { + tokio::io::copy( + &mut S9pkReader::open(path).await?.docker_images().await?, + &mut tokio::io::stdout(), + ) + .await?; + Ok(()) +} diff --git a/appmgr/src/install/mod.rs b/appmgr/src/install/mod.rs index bd5aa87da..ab57ea916 100644 --- a/appmgr/src/install/mod.rs +++ b/appmgr/src/install/mod.rs @@ -9,6 +9,7 @@ use std::task::{Context, Poll}; use std::time::Duration; use anyhow::anyhow; +use emver::VersionRange; use futures::TryStreamExt; use http::HeaderMap; use indexmap::{IndexMap, IndexSet}; @@ -17,6 +18,7 @@ use patch_db::{ DbHandle, HasModel, MapModel, Model, ModelData, OptionModel, PatchDbHandle, Revision, }; use reqwest::Response; +use rpc_toolkit::command; use serde::{Deserialize, Serialize}; use serde_json::Value; use sha2::{Digest, Sha256}; @@ -24,23 +26,48 @@ use tokio::fs::{File, OpenOptions}; use tokio::io::{AsyncRead, AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt}; use self::progress::{InstallProgress, InstallProgressTracker}; -use crate::context::RpcContext; +use crate::context::{EitherContext, ExtendedContext, RpcContext}; use crate::db::model::{ CurrentDependencyInfo, InstalledPackageDataEntry, PackageDataEntry, StaticFiles, }; use crate::s9pk::manifest::{Manifest, PackageId}; use crate::s9pk::reader::S9pkReader; use crate::status::{DependencyErrors, MainStatus, Status}; -use crate::util::{AsyncFileExt, Version}; -use crate::Error; +use crate::util::{display_none, AsyncFileExt, Version}; +use crate::{Error, ResultExt}; pub mod progress; pub const PKG_CACHE: &'static str = "/mnt/embassy-os/cache/packages"; pub const PKG_PUBLIC_DIR: &'static str = "/mnt/embassy-os/public/package-data"; +#[command(display(display_none))] +pub async fn install(#[context] ctx: EitherContext, #[arg] id: String) -> Result<(), Error> { + let rpc_ctx = ctx.as_rpc().unwrap(); + let (pkg_id, version_str) = if let Some(split) = id.split_once("@") { + split + } else { + (id.as_str(), "*") + }; + let version: VersionRange = version_str.parse()?; + let reg_url = rpc_ctx.package_registry_url().await?; + let (man_res, s9pk) = tokio::try_join!( + reqwest::get(format!( + "{}/packages/manifest/{}?version={}", + reg_url, pkg_id, version + )), + reqwest::get(format!( + "{}/packages/{}.s9pk?version={}", + reg_url, pkg_id, version + )) + ) + .with_kind(crate::ErrorKind::Registry)?; + let man = man_res.json().await.with_kind(crate::ErrorKind::Registry)?; + download_install_s9pk(rpc_ctx, &man, s9pk).await +} + pub async fn download_install_s9pk( - ctx: RpcContext, + ctx: &RpcContext, temp_manifest: &Manifest, s9pk: Response, ) -> Result<(), Error> { diff --git a/appmgr/src/lib.rs b/appmgr/src/lib.rs index 18102137e..96271f85c 100644 --- a/appmgr/src/lib.rs +++ b/appmgr/src/lib.rs @@ -25,6 +25,7 @@ pub mod dependencies; pub mod developer; pub mod error; pub mod id; +pub mod inspect; pub mod install; pub mod migration; pub mod net; @@ -53,13 +54,21 @@ pub fn echo(#[context] _ctx: EitherContext, #[arg] message: String) -> Result Result { Ok(ctx) } -#[command(subcommands(version::git_info, s9pk::pack, s9pk::verify, developer::init))] +#[command(subcommands( + version::git_info, + s9pk::pack, + s9pk::verify, + developer::init, + inspect::inspect +))] pub fn portable_api(#[context] ctx: EitherContext) -> Result { Ok(ctx) } diff --git a/appmgr/src/s9pk/reader.rs b/appmgr/src/s9pk/reader.rs index a228831a6..5c4d6a8d3 100644 --- a/appmgr/src/s9pk/reader.rs +++ b/appmgr/src/s9pk/reader.rs @@ -71,7 +71,6 @@ impl S9pkReader { } pub async fn from_reader(mut rdr: R) -> Result { let header = Header::deserialize(&mut rdr).await?; - let pos = rdr.stream_position().await?; let mut hasher = Sha512::new(); let mut buf = [0; 1024]; @@ -86,6 +85,7 @@ impl S9pkReader { header .pubkey .verify_prehashed(hasher, Some(SIG_CONTEXT), &header.signature)?; + let pos = rdr.stream_position().await?; Ok(S9pkReader { hash_string: base32::encode(