From 7fc4cb175c56fa0faa366af903d7fe5e45039cc4 Mon Sep 17 00:00:00 2001 From: Justin Miller Date: Sat, 9 Oct 2021 15:24:01 -0600 Subject: [PATCH] feat: Make the start clear Have messaging to let user know of update complete/ failure --- Makefile | 3 +- appmgr/src/bin/embassy-init.rs | 23 ++++++++++++- appmgr/src/db/model.rs | 1 + appmgr/src/notifications.rs | 12 +++---- appmgr/src/update/mod.rs | 61 ++++++++++++++++++++++++++-------- 5 files changed, 78 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 3f0c0c293..922e46d05 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ APPMGR_SRC := $(shell find appmgr/src) appmgr/Cargo.toml appmgr/Cargo.lock UI_SRC := $(shell find ui/src) SETUP_WIZARD_SRC := $(shell find setup-wizard/src) DIAGNOSTIC_UI_SRC := $(shell find diagnostic-ui/src) -PATCH_DB_CLIENT_SRC = $(shell find patch-db/client) +PATCH_DB_CLIENT_SRC = $(shell find patch-db/client -not -path patch-db/client/dist) all: eos.img @@ -71,6 +71,7 @@ patch-db/client/node_modules: patch-db/client/package.json npm --prefix patch-db/client install patch-db/client/dist: $(PATCH_DB_CLIENT_SRC) patch-db/client/node_modules + ! test -d patch-db/client/dist || rm -rf patch-db/client/dist npm --prefix patch-db/client run build ui: $(EMBASSY_UIS) diff --git a/appmgr/src/bin/embassy-init.rs b/appmgr/src/bin/embassy-init.rs index 1e7d3ab30..88788d966 100644 --- a/appmgr/src/bin/embassy-init.rs +++ b/appmgr/src/bin/embassy-init.rs @@ -3,6 +3,8 @@ use std::sync::Arc; use embassy::context::rpc::RpcContextConfig; use embassy::context::{DiagnosticContext, SetupContext}; +use embassy::db::model::ServerStatus; +use embassy::db::DatabaseModel; use embassy::disk::main::DEFAULT_PASSWORD; use embassy::hostname::get_product_key; use embassy::middleware::cors::cors; @@ -11,7 +13,7 @@ use embassy::middleware::encrypt::encrypt; #[cfg(feature = "avahi")] use embassy::net::mdns::MdnsController; use embassy::sound::MARIO_COIN; -use embassy::util::Invoke; +use embassy::util::{Invoke, Version}; use embassy::{Error, ResultExt}; use http::StatusCode; use rpc_toolkit::rpc_server; @@ -144,6 +146,25 @@ async fn init(cfg_path: Option<&str>) -> Result<(), Error> { log::info!("Enabled nginx public dir"); embassy::net::wifi::synchronize_wpa_supplicant_conf(&cfg.datadir().join("main")).await?; + let db = cfg.db(&secret_store).await?; + let mut handle = db.handle(); + let mut info = embassy::db::DatabaseModel::new() + .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.version = emver::Version::new(0, 3, 0, 0).into(); + // TODO: run migrations + info.save(&mut handle).await?; + Ok(()) } diff --git a/appmgr/src/db/model.rs b/appmgr/src/db/model.rs index 59e02521d..176a6916b 100644 --- a/appmgr/src/db/model.rs +++ b/appmgr/src/db/model.rs @@ -88,6 +88,7 @@ pub struct ServerInfo { pub enum ServerStatus { Running, Updating, + Updated, BackingUp, } diff --git a/appmgr/src/notifications.rs b/appmgr/src/notifications.rs index 42a8d68b5..853e6f936 100644 --- a/appmgr/src/notifications.rs +++ b/appmgr/src/notifications.rs @@ -213,9 +213,9 @@ pub enum NotificationSubtype { } impl NotificationSubtype { fn to_json(&self) -> serde_json::Value { - match &self { - &NotificationSubtype::General => serde_json::Value::Null, - &NotificationSubtype::BackupReport { + match self { + NotificationSubtype::General => serde_json::Value::Null, + NotificationSubtype::BackupReport { server_attempted, server_error, packages, @@ -241,9 +241,9 @@ impl NotificationSubtype { } } fn code(&self) -> u32 { - match &self { - &Self::General => 0, - &Self::BackupReport { + match self { + Self::General => 0, + Self::BackupReport { server_attempted: _, server_error: _, packages: _, diff --git a/appmgr/src/update/mod.rs b/appmgr/src/update/mod.rs index 3017b723c..3fad02c69 100644 --- a/appmgr/src/update/mod.rs +++ b/appmgr/src/update/mod.rs @@ -1,8 +1,9 @@ use std::future::Future; use std::path::{Path, PathBuf}; use std::sync::Arc; +use std::time::Duration; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, Result}; use clap::ArgMatches; use digest::Digest; use emver::Version; @@ -13,21 +14,31 @@ use regex::Regex; use reqwest::Url; use rpc_toolkit::command; use sha2::Sha256; +use std::sync::atomic::{AtomicBool, Ordering}; use tokio::io::AsyncWriteExt; use tokio::pin; use tokio::process::Command; +use tokio::time::Instant; use tokio_stream::StreamExt; use crate::context::RpcContext; use crate::db::model::{ServerStatus, UpdateProgress}; +use crate::notifications::{NotificationLevel, NotificationSubtype}; use crate::update::latest_information::LatestInformation; use crate::util::Invoke; use crate::{Error, ErrorKind, ResultExt}; +lazy_static! { + static ref UPDATED: AtomicBool = AtomicBool::new(false); +} + /// An user/ daemon would call this to update the system to the latest version and do the updates available, /// and this will return something if there is an update, and in that case there will need to be a restart. #[command(rename = "update", display(display_properties))] pub async fn update_system(#[context] ctx: RpcContext) -> Result { + if UPDATED.load(Ordering::SeqCst) { + return Ok(UpdateSystem::NoUpdates); + } if let None = maybe_do_update(ctx).await? { return Ok(UpdateSystem::Updated); } @@ -52,7 +63,7 @@ fn display_properties(status: UpdateSystem, _: &ArgMatches<'_>) { } } -const HEADER_KEY: &str = "CHECKSUM"; +const HEADER_KEY: &str = "x-eos-hash"; mod latest_information; #[derive(Debug, Clone, Copy)] @@ -170,13 +181,16 @@ async fn maybe_do_update(ctx: RpcContext) -> Result>, Error .get_mut(&mut tx) .await?; match &info.status { - ServerStatus::Updating { .. } => { + ServerStatus::Updating => { return Err(Error::new( anyhow!("Server is already updating!"), crate::ErrorKind::InvalidRequest, )) } - ServerStatus::BackingUp {} => { + ServerStatus::Updated => { + return Ok(None); + } + ServerStatus::BackingUp => { return Err(Error::new( anyhow!("Server is backing up!"), crate::ErrorKind::InvalidRequest, @@ -191,7 +205,7 @@ async fn maybe_do_update(ctx: RpcContext) -> Result>, Error ctx.db.handle(), &EosUrl { base: info.eos_marketplace.clone(), - version: latest_version, + version: latest_version.clone(), }, new_label, ) @@ -212,13 +226,26 @@ async fn maybe_do_update(ctx: RpcContext) -> Result>, Error .get_mut(&mut db) .await .expect("could not access status"); - info.status = ServerStatus::Running; info.update_progress = None; - info.save(&mut db).await.expect("could not save status"); match res { - Ok(()) => todo!("issue notification"), + Ok(()) => { + info.status = ServerStatus::Updated; + info.save(&mut db).await.expect("could not save status"); + } Err(e) => { - todo!("{}, issue notification", e) + info.status = ServerStatus::Running; + info.save(&mut db).await.expect("could not save status"); + drop(db); + ctx.notification_manager + .notify( + None, + NotificationLevel::Error, + "EmbassyOS Update Failed".to_owned(), + format!("Update was not successful because of {}", e), + NotificationSubtype::General, + ) + .await + .expect("") } } }); @@ -321,16 +348,20 @@ async fn write_stream_to_label( let mut hasher = Sha256::new(); pin!(stream_download); let mut downloaded = 0; + let mut last_progress_update = Instant::now(); while let Some(Ok(item)) = stream_download.next().await { file.write_all(&item) .await .with_kind(ErrorKind::Filesystem)?; downloaded += item.len() as u64; - crate::db::DatabaseModel::new() - .server_info() - .update_progress() - .put(db, &UpdateProgress { size, downloaded }) - .await?; + if last_progress_update.elapsed() > Duration::from_secs(1) { + last_progress_update = Instant::now(); + crate::db::DatabaseModel::new() + .server_info() + .update_progress() + .put(db, &UpdateProgress { size, downloaded }) + .await?; + } hasher.update(item); } file.flush().await.with_kind(ErrorKind::Filesystem)?; @@ -373,6 +404,8 @@ async fn swap_boot_label( .arg(mounted_boot.value.mount_folder().join("cmdline.txt")) .output() .await?; + + UPDATED.store(true, Ordering::SeqCst); Ok(()) }