take marketplace as an argument from the frontend

This commit is contained in:
Aiden McClelland
2022-01-27 18:30:01 -07:00
committed by Aiden McClelland
parent 9d3f0a9d2b
commit 5c7c23459d
12 changed files with 259 additions and 255 deletions

View File

@@ -18,7 +18,6 @@ use super::PackageBackupReport;
use crate::auth::check_password_against_db;
use crate::backup::{BackupReport, ServerBackupReport};
use crate::context::RpcContext;
use crate::db::model::ServerStatus;
use crate::db::util::WithRevision;
use crate::disk::mount::backup::BackupMountGuard;
use crate::disk::mount::guard::TmpMountGuard;
@@ -134,7 +133,10 @@ pub async fn backup_all(
let revision = assure_backing_up(&mut db).await?;
tokio::task::spawn(async move {
let backup_res = perform_backup(&ctx, &mut db, backup_guard).await;
let status_model = crate::db::DatabaseModel::new().server_info().status();
let status_model = crate::db::DatabaseModel::new()
.server_info()
.status_info()
.backing_up();
status_model
.clone()
.lock(&mut db, LockType::Write)
@@ -203,7 +205,7 @@ pub async fn backup_all(
}
}
status_model
.put(&mut db, &ServerStatus::Running)
.put(&mut db, &false)
.await
.expect("failed to change server status");
});
@@ -216,33 +218,21 @@ pub async fn backup_all(
#[instrument(skip(db))]
async fn assure_backing_up(db: &mut PatchDbHandle) -> Result<Option<Arc<Revision>>, Error> {
let mut tx = db.begin().await?;
let mut info = crate::db::DatabaseModel::new()
let mut backing_up = crate::db::DatabaseModel::new()
.server_info()
.status_info()
.backing_up()
.get_mut(&mut tx)
.await?;
match &info.status {
ServerStatus::Updating => {
return Err(Error::new(
eyre!("Server is updating!"),
crate::ErrorKind::InvalidRequest,
))
}
ServerStatus::Updated => {
return Err(Error::new(
eyre!("Server is updated and needs to be reset"),
crate::ErrorKind::InvalidRequest,
))
}
ServerStatus::BackingUp => {
return Err(Error::new(
eyre!("Server is already backing up!"),
crate::ErrorKind::InvalidRequest,
))
}
ServerStatus::Running => (),
if *backing_up {
return Err(Error::new(
eyre!("Server is already backing up!"),
crate::ErrorKind::InvalidRequest,
));
}
info.status = ServerStatus::BackingUp;
info.save(&mut tx).await?;
*backing_up = true;
backing_up.save(&mut tx).await?;
Ok(tx.commit(None).await?)
}

View File

@@ -377,7 +377,7 @@ async fn restore_package<'a>(
Ok((
progress.clone(),
async move {
download_install_s9pk(&ctx, &manifest, progress, file).await?;
download_install_s9pk(&ctx, &manifest, None, progress, file).await?;
guard.unmount().await?;

View File

@@ -23,7 +23,7 @@ use tracing::instrument;
use crate::core::rpc_continuations::{RequestGuid, RpcContinuation};
use crate::db::model::{Database, InstalledPackageDataEntry, PackageDataEntry};
use crate::hostname::{derive_hostname, derive_id, get_hostname, get_id, get_product_key};
use crate::hostname::{derive_hostname, derive_id, get_product_key};
use crate::install::cleanup::{cleanup_failed, uninstall};
use crate::manager::ManagerMap;
use crate::middleware::auth::HashSessionToken;
@@ -230,31 +230,6 @@ impl RpcContext {
tracing::info!("Initialized Package Managers");
Ok(res)
}
#[instrument(skip(self))]
pub async fn package_registry_url(&self) -> Result<Url, Error> {
Ok(
if let Some(market) = crate::db::DatabaseModel::new()
.server_info()
.package_marketplace()
.get(&mut self.db.handle(), false)
.await?
.to_owned()
{
market
} else {
self.eos_registry_url().await?
},
)
}
#[instrument(skip(self))]
pub async fn eos_registry_url(&self) -> Result<Url, Error> {
Ok(crate::db::DatabaseModel::new()
.server_info()
.eos_marketplace()
.get(&mut self.db.handle(), false)
.await?
.to_owned())
}
#[instrument(skip(self, db))]
pub async fn set_nginx_conf<Db: DbHandle>(&self, db: &mut Db) -> Result<(), Error> {
tokio::fs::write("/etc/nginx/sites-available/default", {
@@ -266,11 +241,6 @@ impl RpcContext {
include_str!("../nginx/main-ui.conf.template"),
lan_hostname = info.lan_address.host_str().unwrap(),
tor_hostname = info.tor_address.host_str().unwrap(),
eos_marketplace = info.eos_marketplace,
package_marketplace = info
.package_marketplace
.as_ref()
.unwrap_or(&info.eos_marketplace),
)
})
.await

View File

@@ -18,7 +18,7 @@ use tracing::instrument;
use url::Host;
use crate::db::model::Database;
use crate::hostname::{derive_hostname, derive_id, get_hostname, get_id, get_product_key};
use crate::hostname::{derive_hostname, derive_id, get_product_key};
use crate::net::tor::os_key;
use crate::setup::{password_hash, RecoveryStatus};
use crate::util::io::from_toml_async_reader;

View File

@@ -50,12 +50,11 @@ impl Database {
tor_address: format!("http://{}", tor_key.public().get_onion_address())
.parse()
.unwrap(),
status: ServerStatus::Running {},
#[cfg(not(feature = "beta"))]
eos_marketplace: "https://marketplace.start9.com".parse().unwrap(),
#[cfg(feature = "beta")]
eos_marketplace: "https://beta-registry-0-3.start9labs.com".parse().unwrap(),
package_marketplace: None,
status_info: ServerStatus {
backing_up: false,
updated: false,
update_progress: None,
},
wifi: WifiInfo {
ssids: Vec::new(),
connected: None,
@@ -67,7 +66,6 @@ impl Database {
clearnet: Vec::new(),
},
share_stats: false,
update_progress: None,
password_hash,
},
package_data: AllPackageData::default(),
@@ -93,25 +91,23 @@ pub struct ServerInfo {
pub eos_version_compat: VersionRange,
pub lan_address: Url,
pub tor_address: Url,
pub status: ServerStatus,
pub eos_marketplace: Url,
pub package_marketplace: Option<Url>, // None implies use eos_marketplace
#[model]
#[serde(default)]
pub status_info: ServerStatus,
pub wifi: WifiInfo,
pub unread_notification_count: u64,
pub connection_addresses: ConnectionAddresses,
pub share_stats: bool,
#[model]
pub update_progress: Option<UpdateProgress>,
pub password_hash: String,
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Default, Deserialize, Serialize, HasModel)]
#[serde(rename_all = "kebab-case")]
pub enum ServerStatus {
Running,
Updating,
Updated,
BackingUp,
pub struct ServerStatus {
pub backing_up: bool,
pub updated: bool,
#[model]
pub update_progress: Option<UpdateProgress>,
}
#[derive(Debug, Deserialize, Serialize, HasModel)]
@@ -257,6 +253,9 @@ impl PackageDataEntryModel {
pub struct InstalledPackageDataEntry {
#[model]
pub status: Status,
pub marketplace_url: Option<Url>,
#[serde(default)]
pub developer_key: ed25519_dalek::PublicKey,
#[model]
pub manifest: Manifest,
pub last_backup: Option<DateTime<Utc>>,

View File

@@ -93,15 +93,11 @@ pub async fn init(cfg: &RpcContextConfig, product_key: &str) -> Result<(), Error
.server_info()
.get_mut(&mut handle)
.await?;
match info.status {
ServerStatus::Running | ServerStatus::Updated | ServerStatus::BackingUp => {
info.status = ServerStatus::Running;
}
ServerStatus::Updating => {
info.update_progress = None;
info.status = ServerStatus::Running;
}
}
info.status_info = ServerStatus {
backing_up: false,
updated: false,
update_progress: None,
};
info.save(&mut handle).await?;
crate::version::init(&mut handle).await?;

View File

@@ -15,6 +15,7 @@ use http::header::CONTENT_LENGTH;
use http::{Request, Response, StatusCode};
use hyper::Body;
use patch_db::{DbHandle, LockType};
use reqwest::Url;
use rpc_toolkit::yajrc::RpcError;
use rpc_toolkit::{command, Context};
use tokio::fs::{File, OpenOptions};
@@ -85,18 +86,23 @@ pub async fn list(#[context] ctx: RpcContext) -> Result<Vec<(PackageId, Version)
pub async fn install(
#[context] ctx: RpcContext,
#[arg] id: String,
#[arg(rename = "version-spec")] version_spec: Option<String>,
#[arg(short = "m", long = "marketplace-url", rename = "marketplace-url")]
marketplace_url: Option<Url>,
#[arg(short = "v", long = "version-spec", rename = "version-spec")] version_spec: Option<
String,
>,
) -> Result<WithRevision<()>, Error> {
let version_str = match &version_spec {
None => "*",
Some(v) => &*v,
};
let version: VersionRange = version_str.parse()?;
let reg_url = ctx.package_registry_url().await?;
let marketplace_url =
marketplace_url.unwrap_or_else(|| crate::DEFAULT_MARKETPLACE.parse().unwrap());
let (man_res, s9pk) = tokio::try_join!(
reqwest::get(format!(
"{}/package/manifest/{}?spec={}&eos-version-compat={}&arch={}",
reg_url,
marketplace_url,
id,
version,
Current::new().compat(),
@@ -104,7 +110,7 @@ pub async fn install(
)),
reqwest::get(format!(
"{}/package/{}.s9pk?spec={}&eos-version-compat={}&arch={}",
reg_url,
marketplace_url,
id,
version,
Current::new().compat(),
@@ -112,7 +118,15 @@ pub async fn install(
))
)
.with_kind(crate::ErrorKind::Registry)?;
let man: Manifest = man_res.json().await.with_kind(crate::ErrorKind::Registry)?;
let man: Manifest = man_res
.error_for_status()
.with_kind(crate::ErrorKind::Registry)?
.json()
.await
.with_kind(crate::ErrorKind::Registry)?;
let s9pk = s9pk
.error_for_status()
.with_kind(crate::ErrorKind::Registry)?;
if man.id.as_str() != id || !man.version.satisfies(&version) {
return Err(Error::new(
@@ -166,6 +180,7 @@ pub async fn install(
if let Err(e) = download_install_s9pk(
&ctx,
&man,
Some(marketplace_url),
InstallProgress::new(s9pk.content_length()),
tokio_util::io::StreamReader::new(s9pk.bytes_stream().map_err(|e| {
std::io::Error::new(
@@ -286,6 +301,7 @@ pub async fn sideload(
download_install_s9pk(
&new_ctx,
&manifest,
None,
progress,
tokio_util::io::StreamReader::new(req.into_body().map_err(|e| {
std::io::Error::new(
@@ -330,6 +346,7 @@ pub async fn sideload(
async fn cli_install(
ctx: CliContext,
target: String,
marketplace_url: Option<Url>,
version_spec: Option<String>,
) -> Result<(), RpcError> {
if target.ends_with(".s9pk") {
@@ -373,7 +390,9 @@ async fn cli_install(
}
} else {
let params = match (target.split_once("@"), version_spec) {
(Some((pkg, v)), None) => serde_json::json!({ "id": pkg, "version-spec": v }),
(Some((pkg, v)), None) => {
serde_json::json!({ "id": pkg, "marketplace-url": marketplace_url, "version-spec": v })
}
(Some(_), Some(_)) => {
return Err(crate::Error::new(
eyre!("Invalid package id {}", target),
@@ -381,8 +400,10 @@ async fn cli_install(
)
.into())
}
(None, Some(v)) => serde_json::json!({ "id": target, "version-spec": v }),
(None, None) => serde_json::json!({ "id": target }),
(None, Some(v)) => {
serde_json::json!({ "id": target, "marketplace-url": marketplace_url, "version-spec": v })
}
(None, None) => serde_json::json!({ "id": target, "marketplace-url": marketplace_url }),
};
tracing::debug!("calling package.install");
rpc_toolkit::command_helpers::call_remote(
@@ -489,6 +510,7 @@ pub async fn uninstall_impl(ctx: RpcContext, id: PackageId) -> Result<WithRevisi
pub async fn download_install_s9pk(
ctx: &RpcContext,
temp_manifest: &Manifest,
marketplace_url: Option<Url>,
progress: Arc<InstallProgress>,
mut s9pk: impl AsyncRead + Unpin,
) -> Result<(), Error> {
@@ -537,7 +559,15 @@ pub async fn download_install_s9pk(
})
.await?;
install_s9pk(&ctx, pkg_id, version, &mut s9pk_reader, progress).await?;
install_s9pk(
&ctx,
pkg_id,
version,
marketplace_url,
&mut s9pk_reader,
progress,
)
.await?;
Ok(())
}
@@ -563,11 +593,13 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
ctx: &RpcContext,
pkg_id: &PackageId,
version: &Version,
marketplace_url: Option<Url>,
rdr: &mut S9pkReader<InstallProgressTracker<R>>,
progress: Arc<InstallProgress>,
) -> Result<(), Error> {
rdr.validate().await?;
rdr.validated();
let developer_key = rdr.developer_key().clone();
rdr.reset().await?;
let model = crate::db::DatabaseModel::new()
.package_data()
@@ -582,78 +614,97 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
tracing::info!("Install {}@{}: Fetching Dependency Info", pkg_id, version);
let mut dependency_info = BTreeMap::new();
let reg_url = ctx.package_registry_url().await?;
for (dep, info) in &manifest.dependencies.0 {
let manifest: Option<Manifest> = match reqwest::get(format!(
"{}/package/manifest/{}?spec={}&eos-version-compat={}&arch={}",
reg_url,
dep,
info.version,
Current::new().compat(),
platforms::TARGET_ARCH,
))
.await
.with_kind(crate::ErrorKind::Registry)?
.error_for_status()
let manifest: Option<Manifest> = if let Some(local_man) = crate::db::DatabaseModel::new()
.package_data()
.idx_model(dep)
.map::<_, Manifest>(|pde| pde.manifest())
.get(&mut ctx.db.handle(), false)
.await?
.into_owned()
{
Ok(a) => Ok(Some(
a.json()
Some(local_man)
} else if let Some(marketplace_url) = &marketplace_url {
match reqwest::get(format!(
"{}/package/manifest/{}?spec={}&eos-version-compat={}&arch={}",
marketplace_url,
dep,
info.version,
Current::new().compat(),
platforms::TARGET_ARCH,
))
.await
.with_kind(crate::ErrorKind::Registry)?
.error_for_status()
{
Ok(a) => Ok(Some(
a.json()
.await
.with_kind(crate::ErrorKind::Deserialization)?,
)),
Err(e) if e.status() == Some(StatusCode::BAD_REQUEST) => Ok(None),
Err(e) => Err(e),
}
.with_kind(crate::ErrorKind::Registry)?
} else {
None
};
if let Some(marketplace_url) = &marketplace_url {
if let Some(manifest) = &manifest {
let dir = ctx
.datadir
.join(PKG_PUBLIC_DIR)
.join(&manifest.id)
.join(manifest.version.as_str());
let icon_path = dir.join(format!("icon.{}", manifest.assets.icon_type()));
if tokio::fs::metadata(&icon_path).await.is_err() {
tokio::fs::create_dir_all(&dir).await?;
let icon = reqwest::get(format!(
"{}/package/icon/{}?spec={}&eos-version-compat={}&arch={}",
marketplace_url,
dep,
info.version,
Current::new().compat(),
platforms::TARGET_ARCH,
))
.await
.with_kind(crate::ErrorKind::Deserialization)?,
)),
Err(e) if e.status() == Some(StatusCode::BAD_REQUEST) => Ok(None),
Err(e) => Err(e),
}
.with_kind(crate::ErrorKind::Registry)?;
if let Some(manifest) = manifest {
let dir = ctx
.datadir
.join(PKG_PUBLIC_DIR)
.join(&manifest.id)
.join(manifest.version.as_str());
let icon_path = dir.join(format!("icon.{}", manifest.assets.icon_type()));
if tokio::fs::metadata(&icon_path).await.is_err() {
tokio::fs::create_dir_all(&dir).await?;
let icon = reqwest::get(format!(
"{}/package/icon/{}?spec={}&eos-version-compat={}&arch={}",
reg_url,
dep,
info.version,
Current::new().compat(),
platforms::TARGET_ARCH,
))
.await
.with_kind(crate::ErrorKind::Registry)?;
let mut dst = File::create(&icon_path).await?;
tokio::io::copy(
&mut tokio_util::io::StreamReader::new(icon.bytes_stream().map_err(|e| {
std::io::Error::new(
if e.is_connect() {
std::io::ErrorKind::ConnectionRefused
} else if e.is_timeout() {
std::io::ErrorKind::TimedOut
} else {
std::io::ErrorKind::Other
},
e,
)
})),
&mut dst,
)
.await?;
dst.sync_all().await?;
.with_kind(crate::ErrorKind::Registry)?;
let mut dst = File::create(&icon_path).await?;
tokio::io::copy(
&mut tokio_util::io::StreamReader::new(icon.bytes_stream().map_err(|e| {
std::io::Error::new(
if e.is_connect() {
std::io::ErrorKind::ConnectionRefused
} else if e.is_timeout() {
std::io::ErrorKind::TimedOut
} else {
std::io::ErrorKind::Other
},
e,
)
})),
&mut dst,
)
.await?;
dst.sync_all().await?;
}
}
dependency_info.insert(
dep.clone(),
StaticDependencyInfo {
icon: format!(
"/public/package-data/{}/{}/icon.{}",
manifest.id,
manifest.version,
manifest.assets.icon_type()
),
manifest: Some(manifest),
icon: if let Some(manifest) = &manifest {
format!(
"/public/package-data/{}/{}/icon.{}",
manifest.id,
manifest.version,
manifest.assets.icon_type()
)
} else {
"/assets/img/package-icon.png".to_owned()
},
manifest,
},
);
}
@@ -842,13 +893,36 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
})
.collect();
let current_dependents = {
// search required dependencies
let mut deps = BTreeMap::new();
for package in crate::db::DatabaseModel::new()
.package_data()
.keys(&mut tx, true)
.await?
{
// update dependency_info on dependents
if let Some(dep_info_model) = crate::db::DatabaseModel::new()
.package_data()
.idx_model(&package)
.expect(&mut tx)
.await?
.installed()
.and_then(|i| i.dependency_info().idx_model(pkg_id))
.check(&mut tx)
.await?
{
let mut dep_info = dep_info_model.get_mut(&mut tx).await?;
*dep_info = StaticDependencyInfo {
icon: format!(
"/public/package-data/{}/{}/icon.{}",
manifest.id,
manifest.version,
manifest.assets.icon_type()
),
manifest: Some(manifest.clone()),
};
}
// search required dependencies
if let Some(dep) = crate::db::DatabaseModel::new()
.package_data()
.idx_model(&package)
@@ -877,6 +951,8 @@ pub async fn install_s9pk<R: AsyncRead + AsyncSeek + Unpin>(
main: MainStatus::Stopped,
dependency_errors: DependencyErrors::default(),
},
marketplace_url,
developer_key,
manifest: manifest.clone(),
last_backup: match &*pde {
PackageDataEntry::Updating {

View File

@@ -1,4 +1,8 @@
pub const CONFIG_PATH: &'static str = "/etc/embassy/config.toml";
pub const CONFIG_PATH: &str = "/etc/embassy/config.yaml";
#[cfg(not(feature = "beta"))]
pub const DEFAULT_MARKETPLACE: &str = "https://marketplace.start9.com";
#[cfg(feature = "beta")]
pub const DEFAULT_MARKETPLACE: &str = "https://beta-registry-0-3.start9labs.com";
pub const BUFFER_SIZE: usize = 1024;
pub const HOST_IP: [u8; 4] = [172, 18, 0, 1];
@@ -78,7 +82,6 @@ pub fn main_api() -> Result<(), RpcError> {
shutdown::restart,
shutdown::rebuild,
update::update_system,
marketplace::set_eos_url,
))]
pub fn server() -> Result<(), RpcError> {
Ok(())
@@ -98,7 +101,6 @@ pub fn server() -> Result<(), RpcError> {
properties::properties,
dependencies::dependency,
backup::package_backup,
marketplace::set_package_url,
))]
pub fn package() -> Result<(), RpcError> {
Ok(())

View File

@@ -1,43 +1,35 @@
use patch_db::{DbHandle, LockType};
use reqwest::Url;
use color_eyre::eyre::eyre;
use reqwest::{StatusCode, Url};
use rpc_toolkit::command;
use serde_json::Value;
use crate::context::RpcContext;
use crate::util::display_none;
use crate::Error;
use crate::{Error, ResultExt};
#[command(rename = "set-marketplace", display(display_none))]
pub async fn set_eos_url(#[context] ctx: RpcContext, #[arg] url: Url) -> Result<(), Error> {
let mut db = ctx.db.handle();
let mut tx = db.begin().await?;
crate::db::DatabaseModel::new()
.server_info()
.lock(&mut tx, LockType::Write)
.await?;
crate::db::DatabaseModel::new()
.server_info()
.eos_marketplace()
.put(&mut tx, &url)
.await?;
ctx.set_nginx_conf(&mut tx).await?;
tx.commit(None).await?;
#[command(subcommands(get))]
pub fn marketplace() -> Result<(), Error> {
Ok(())
}
#[command(rename = "set-marketplace", display(display_none))]
pub async fn set_package_url(#[context] ctx: RpcContext, #[arg] url: Url) -> Result<(), Error> {
let mut db = ctx.db.handle();
let mut tx = db.begin().await?;
crate::db::DatabaseModel::new()
.server_info()
.lock(&mut tx, LockType::Write)
.await?;
crate::db::DatabaseModel::new()
.server_info()
.package_marketplace()
.put(&mut tx, &Some(url))
.await?;
ctx.set_nginx_conf(&mut tx).await?;
tx.commit(None).await?;
Ok(())
#[command]
pub async fn get(#[arg] url: Url) -> Result<Value, Error> {
let response = reqwest::get(url)
.await
.with_kind(crate::ErrorKind::Network)?;
let status = response.status();
if status.is_success() {
response
.json()
.await
.with_kind(crate::ErrorKind::Deserialization)
} else {
let message = response.text().await.with_kind(crate::ErrorKind::Network)?;
Err(Error::new(
eyre!("{}", message),
match status {
StatusCode::BAD_REQUEST => crate::ErrorKind::InvalidRequest,
StatusCode::NOT_FOUND => crate::ErrorKind::NotFound,
_ => crate::ErrorKind::Registry,
},
))
}
}

View File

@@ -45,18 +45,6 @@ server {{
proxy_pass http://127.0.0.1:5961/;
}}
location /marketplace/eos/ {{
# ns1.digitalocean.com, ns2.digitalocean.com, ns3.digitalocean.com
resolver 173.245.58.51 173.245.59.41 198.41.222.173;
proxy_pass {eos_marketplace}eos/;
}}
location /marketplace/package/ {{
# ns1.digitalocean.com, ns2.digitalocean.com, ns3.digitalocean.com
resolver 173.245.58.51 173.245.59.41 198.41.222.173;
proxy_pass {package_marketplace}package/;
}}
location / {{
try_files $uri $uri/ =404;
}}
@@ -111,18 +99,6 @@ server {{
proxy_pass http://127.0.0.1:5961/;
}}
location /marketplace/eos/ {{
# ns1.digitalocean.com, ns2.digitalocean.com, ns3.digitalocean.com
resolver 173.245.58.51 173.245.59.41 198.41.222.173;
proxy_pass {eos_marketplace}eos/;
}}
location /marketplace/package/ {{
# ns1.digitalocean.com, ns2.digitalocean.com, ns3.digitalocean.com
resolver 173.245.58.51 173.245.59.41 198.41.222.173;
proxy_pass {package_marketplace}package/;
}}
location / {{
try_files $uri $uri/ =404;
}}

View File

@@ -4,6 +4,7 @@ use std::pin::Pin;
use std::task::{Context, Poll};
use digest::Output;
use ed25519_dalek::PublicKey;
use sha2::{Digest, Sha512};
use tokio::fs::File;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, ReadBuf, Take};
@@ -47,6 +48,7 @@ impl<'a, R: AsyncRead + AsyncSeek + Unpin> AsyncRead for ReadHandle<'a, R> {
pub struct S9pkReader<R: AsyncRead + AsyncSeek + Unpin = File> {
hash: Option<Output<Sha512>>,
hash_string: Option<String>,
developer_key: PublicKey,
toc: TableOfContents,
pos: u64,
rdr: R,
@@ -105,6 +107,7 @@ impl<R: AsyncRead + AsyncSeek + Unpin> S9pkReader<R> {
Ok(S9pkReader {
hash_string,
hash,
developer_key: header.pubkey,
toc: header.table_of_contents,
pos,
rdr,
@@ -119,6 +122,10 @@ impl<R: AsyncRead + AsyncSeek + Unpin> S9pkReader<R> {
self.hash_string.as_ref().map(|s| s.as_str())
}
pub fn developer_key(&self) -> &PublicKey {
&self.developer_key
}
pub async fn reset(&mut self) -> Result<(), Error> {
self.rdr.seek(SeekFrom::Start(0)).await?;
Ok(())

View File

@@ -23,7 +23,7 @@ use tokio_stream::StreamExt;
use tracing::instrument;
use crate::context::RpcContext;
use crate::db::model::{ServerStatus, UpdateProgress};
use crate::db::model::UpdateProgress;
use crate::db::util::WithRevision;
use crate::disk::mount::filesystem::block_dev::BlockDev;
use crate::disk::mount::filesystem::FileSystem;
@@ -48,6 +48,7 @@ lazy_static! {
#[instrument(skip(ctx))]
pub async fn update_system(
#[context] ctx: RpcContext,
#[arg(rename = "marketplace-url")] marketplace_url: Url,
) -> Result<WithRevision<UpdateResult>, Error> {
let noop = WithRevision {
response: UpdateResult::NoUpdates,
@@ -56,7 +57,7 @@ pub async fn update_system(
if UPDATED.load(Ordering::SeqCst) {
return Ok(noop);
}
match maybe_do_update(ctx).await? {
match maybe_do_update(ctx, marketplace_url).await? {
None => Ok(noop),
Some(r) => Ok(WithRevision {
response: UpdateResult::Updating,
@@ -127,11 +128,14 @@ lazy_static! {
}
#[instrument(skip(ctx))]
async fn maybe_do_update(ctx: RpcContext) -> Result<Option<Arc<Revision>>, Error> {
async fn maybe_do_update(
ctx: RpcContext,
marketplace_url: Url,
) -> Result<Option<Arc<Revision>>, Error> {
let mut db = ctx.db.handle();
let latest_version = reqwest::get(format!(
"{}/eos/latest?eos-version={}&arch={}",
ctx.eos_registry_url().await?,
marketplace_url,
Current::new().semver(),
platforms::TARGET_ARCH,
))
@@ -154,67 +158,58 @@ async fn maybe_do_update(ctx: RpcContext) -> Result<Option<Arc<Revision>>, Error
return Ok(None);
}
let mut tx = db.begin().await?;
let mut info = crate::db::DatabaseModel::new()
let mut status = crate::db::DatabaseModel::new()
.server_info()
.status_info()
.get_mut(&mut tx)
.await?;
match &info.status {
ServerStatus::Updating => {
return Err(Error::new(
eyre!("Server is already updating!"),
crate::ErrorKind::InvalidRequest,
))
}
ServerStatus::Updated => {
return Ok(None);
}
ServerStatus::BackingUp => {
return Err(Error::new(
eyre!("Server is backing up!"),
crate::ErrorKind::InvalidRequest,
))
}
ServerStatus::Running => (),
if status.update_progress.is_some() {
return Err(Error::new(
eyre!("Server is already updating!"),
crate::ErrorKind::InvalidRequest,
));
}
if status.updated {
return Ok(None);
}
let (new_label, _current_label) = query_mounted_label().await?;
let (size, download) = download_file(
ctx.db.handle(),
&EosUrl {
base: info.eos_marketplace.clone(),
base: marketplace_url,
version: latest_version.clone(),
},
new_label,
)
.await?;
info.status = ServerStatus::Updating;
info.update_progress = Some(UpdateProgress {
status.update_progress = Some(UpdateProgress {
size,
downloaded: 0,
});
info.save(&mut tx).await?;
status.save(&mut tx).await?;
let rev = tx.commit(None).await?;
tokio::spawn(async move {
let mut db = ctx.db.handle();
let res = do_update(download, new_label).await;
let mut info = crate::db::DatabaseModel::new()
let mut status = crate::db::DatabaseModel::new()
.server_info()
.status_info()
.get_mut(&mut db)
.await
.expect("could not access status");
info.update_progress = None;
status.update_progress = None;
match res {
Ok(()) => {
info.status = ServerStatus::Updated;
info.save(&mut db).await.expect("could not save status");
status.updated = true;
status.save(&mut db).await.expect("could not save status");
BEP.play().await.expect("could not bep");
BEP.play().await.expect("could not bep");
BEP.play().await.expect("could not bep");
}
Err(e) => {
info.status = ServerStatus::Running;
info.save(&mut db).await.expect("could not save status");
status.save(&mut db).await.expect("could not save status");
ctx.notification_manager
.notify(
&mut db,
@@ -365,6 +360,7 @@ async fn write_stream_to_label<Db: DbHandle>(
last_progress_update = Instant::now();
crate::db::DatabaseModel::new()
.server_info()
.status_info()
.update_progress()
.put(db, &UpdateProgress { size, downloaded })
.await?;