From 3fecec224c24d9a83db010c73a0f38e99b30ce41 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 15:08:45 +0100 Subject: [PATCH 01/22] update CODEOWNERS --- .github/CODEOWNERS | 15 +++++++++++++++ core/model/src/version.rs | 0 core/version/src/service.rs | 0 3 files changed, 15 insertions(+) create mode 100644 core/model/src/version.rs create mode 100644 core/version/src/service.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index aa2ea445ab..e8b0f0fdef 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,19 +2,34 @@ .github/ @prekucki .github/workflows/rust.yml @golemfactory/ya-integration +# Activity core/activity/ @golemfactory/exe-unit core/model/src/activity.rs @golemfactory/exe-unit + +core/sgx/ @golemfactory/exe-unit +core/model/src/sgx.rs @golemfactory/exe-unit + exe-unit/ @golemfactory/exe-unit +# Market core/market/ @golemfactory/ya-market core/model/src/market.rs @golemfactory/ya-market +core/metrics/ @golemfactory/ya-market + +core/version/ @golemfactory/ya-market +core/model/src/version.rs @golemfactory/ya-market +utils/compile-tome-utils @golemfactory/ya-market + +# Net service-bus/ @golemfactory/ya-net core/net/ @golemfactory/ya-net core/model/src/net.rs @golemfactory/ya-net +# Payment core/payment-driver @golemfactory/ya-payment core/payment @golemfactory/ya-payment core/model/src/payment.rs @golemfactory/ya-payment +# Provider agent/provider @golemfactory/ya-provider diff --git a/core/model/src/version.rs b/core/model/src/version.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core/version/src/service.rs b/core/version/src/service.rs new file mode 100644 index 0000000000..e69de29bb2 From 968e2602afa34043928c7c0b65be22b5890540d3 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 15:09:58 +0100 Subject: [PATCH 02/22] main Cargo.toml cleanup --- Cargo.lock | 8 ++++++++ Cargo.toml | 14 +++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 82b412ba06..de1737d84b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7887,16 +7887,24 @@ dependencies = [ name = "ya-version" version = "0.0.1" dependencies = [ + "actix-web 3.3.2", "anyhow", "chrono", "diesel", "diesel_migrations", "log", + "metrics", "self_update", "serde", "serde_json", + "structopt", "tokio 0.2.24", + "ya-compile-time-utils", + "ya-core-model", "ya-persistence", + "ya-service-api", + "ya-service-api-interfaces", + "ya-service-bus", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d54cd2e552..fb59e42ad5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -145,17 +145,18 @@ members = [ [patch.crates-io] ## SERVICES +ya-identity = { path = "core/identity" } +ya-net = { path = "core/net" } +ya-market = { path = "core/market" } +ya-market-resolver = { path = "core/market/resolver" } ya-activity = { path = "core/activity" } +ya-sgx = { path = "core/sgx" } +ya-payment = { path = "core/payment" } ya-payment-driver = { path = "core/payment-driver/base" } ya-dummy-driver = { path = "core/payment-driver/dummy" } ya-gnt-driver = { path = "core/payment-driver/gnt" } ya-zksync-driver = { path = "core/payment-driver/zksync" } -ya-identity = { path = "core/identity" } -ya-market = { path = "core/market" } -ya-market-resolver = { path = "core/market/resolver" } -ya-net = { path = "core/net" } -ya-payment = { path = "core/payment" } -ya-sgx = { path = "core/sgx" } +ya-version = { path = "core/version" } ## CORE UTILS ya-core-model = { path = "core/model" } @@ -194,4 +195,3 @@ ya-utils-path = { path = "utils/path"} ya-utils-process = { path = "utils/process"} ya-diesel-utils = { path = "utils/diesel-utils"} ya-metrics = { path = "core/metrics" } -ya-version = { path = "core/version" } From 1b8ce8069f8ffe0c146cd51f75a1acbd54df6b95 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 15:10:32 +0100 Subject: [PATCH 03/22] [core model] added version module --- core/model/Cargo.toml | 4 +++- core/model/src/lib.rs | 3 +++ core/model/src/version.rs | 41 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/core/model/Cargo.toml b/core/model/Cargo.toml index 7e2e146123..cb7305afb8 100644 --- a/core/model/Cargo.toml +++ b/core/model/Cargo.toml @@ -20,7 +20,8 @@ full = [ 'net', 'payment', 'gftp', - 'sgx' + 'sgx', + 'version', ] activity = [] appkey = [] @@ -31,6 +32,7 @@ market = [] net = [] payment = ['bigdecimal', 'bitflags'] sgx = ['graphene-sgx'] +version = [] [dependencies] ya-client-model = "0.2" diff --git a/core/model/src/lib.rs b/core/model/src/lib.rs index 3f9d00119b..eee62f1e95 100644 --- a/core/model/src/lib.rs +++ b/core/model/src/lib.rs @@ -28,6 +28,9 @@ pub mod gftp; #[cfg(feature = "sgx")] pub mod sgx; +#[cfg(feature = "version")] +pub mod version; + use derive_more::Display; use serde::{Deserialize, Serialize}; pub use ya_client_model::NodeId; diff --git a/core/model/src/version.rs b/core/model/src/version.rs index e69de29bb2..878fd37cc2 100644 --- a/core/model/src/version.rs +++ b/core/model/src/version.rs @@ -0,0 +1,41 @@ +//! Version handling service bus API. + +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; + +use ya_client_model::ErrorMessage; +use ya_service_bus::RpcMessage; + +pub const BUS_ID: &'static str = "/local/version"; + +/// Skip upgrading to the latest Yagna release. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Skip(); + +impl RpcMessage for Skip { + const ID: &'static str = "skip"; + type Item = Option; + type Error = ErrorMessage; +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Check(); + +impl RpcMessage for Check { + const ID: &'static str = "check"; + type Item = Option; + type Error = ErrorMessage; +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Release { + pub version: String, + pub name: String, + pub seen: bool, + pub release_ts: NaiveDateTime, + pub insertion_ts: Option, + pub update_ts: Option, +} From 89cc6202e710d92c8fa4ce1c488da85d0f629d81 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 15:12:14 +0100 Subject: [PATCH 04/22] ya-version as Yagna Service --- core/serv/src/main.rs | 3 + core/version/Cargo.toml | 8 +++ core/version/src/db.rs | 33 +++++++++ core/version/src/lib.rs | 1 + core/version/src/service.rs | 136 ++++++++++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+) diff --git a/core/serv/src/main.rs b/core/serv/src/main.rs index 361a47cca5..062e238487 100644 --- a/core/serv/src/main.rs +++ b/core/serv/src/main.rs @@ -30,6 +30,7 @@ use ya_service_api_web::{ use ya_sgx::SgxService; use ya_utils_path::data_dir::DataDir; use ya_utils_process::lock::ProcLock; +use ya_version::service::VersionService; mod autocomplete; use autocomplete::CompleteCommand; @@ -187,6 +188,8 @@ enum Services { // other services to initialize counters and other metrics. #[enable(gsb, rest)] Metrics(MetricsService), + #[enable(gsb, rest, cli)] + Upgrade(VersionService), #[enable(gsb, cli(flatten))] Identity(IdentityService), #[enable(gsb)] diff --git a/core/version/Cargo.toml b/core/version/Cargo.toml index 0c7c885265..11f887b3b9 100644 --- a/core/version/Cargo.toml +++ b/core/version/Cargo.toml @@ -6,14 +6,22 @@ authors = ["Golem Factory "] edition = "2018" [dependencies] +ya-compile-time-utils = "0.1" +ya-core-model = { version = "0.2.0", features = ["version"] } ya-persistence = "0.2" +ya-service-api = "0.1" +ya-service-api-interfaces = "0.1" +ya-service-bus = "0.2.2" +actix-web = "3.2" anyhow = "1.0" chrono = { version = "0.4", features = ["serde"] } diesel = { version = "1.4", features = ["chrono", "sqlite", "r2d2"] } diesel_migrations = "1.4" log = "0.4" +metrics = "0.12" self_update = "0.23" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +structopt = "0.3.21" tokio = { version = "0.2", features = ["time", "sync"] } diff --git a/core/version/src/db.rs b/core/version/src/db.rs index a939fda88b..b4e8ffc3c3 100644 --- a/core/version/src/db.rs +++ b/core/version/src/db.rs @@ -60,6 +60,26 @@ pub(crate) mod dao { }) .await } + + pub async fn skip_pending_release(&self) -> Result> { + let mut pending_release = match self.pending_release().await? { + Some(r) => r, + None => return Ok(None), + }; + + do_with_transaction(self.pool, move |conn| { + let num_updated = diesel::update(version_release.find(&pending_release.version)) + .set(release::seen.eq(true)) + .execute(conn)?; + pending_release.seen = true; + match num_updated { + 0 => anyhow::bail!("no release updated: {}", pending_release), + 1 => Ok(Some(pending_release)), + _ => anyhow::bail!("more than one release updated: {}", pending_release), + } + }) + .await + } } } pub(crate) mod model { @@ -88,5 +108,18 @@ pub(crate) mod model { ) } } + + impl From for ya_core_model::version::Release { + fn from(r: Release) -> Self { + Self { + version: r.version, + name: r.name, + seen: r.seen, + release_ts: r.release_ts, + insertion_ts: r.insertion_ts, + update_ts: r.update_ts, + } + } + } } pub(crate) mod schema; diff --git a/core/version/src/lib.rs b/core/version/src/lib.rs index 0197a964b3..96946a3a79 100644 --- a/core/version/src/lib.rs +++ b/core/version/src/lib.rs @@ -2,6 +2,7 @@ extern crate diesel; pub(crate) mod db; +pub mod service; pub mod notifier { use std::time::Duration; diff --git a/core/version/src/service.rs b/core/version/src/service.rs index e69de29bb2..beb3a15727 100644 --- a/core/version/src/service.rs +++ b/core/version/src/service.rs @@ -0,0 +1,136 @@ +use actix_web::{web, HttpResponse, Responder}; +use metrics::counter; +use std::time::Duration; +use structopt::StructOpt; + +use ya_core_model::version; +use ya_persistence::executor::DbExecutor; +use ya_service_api::{CliCtx, CommandOutput}; +use ya_service_api_interfaces::{Provider, Service}; +use ya_service_bus::{typed as bus, RpcEndpoint, RpcMessage}; +pub type RpcMessageResult = Result<::Item, ::Error>; + +// TODO: uncomment when added +// use crate::{db::migrations}; +use crate::db::dao::ReleaseDAO; + +/// Yagna version management. +#[derive(StructOpt, Debug)] +pub enum UpgradeCLI { + /// Stop logging warnings about latest Yagna release availability. + Skip, + + /// Checks if there is new Yagna version available and shows it. + Check, +} + +impl UpgradeCLI { + pub async fn run_command(self, _ctx: &CliCtx) -> anyhow::Result { + match self { + UpgradeCLI::Skip => match bus::service(version::BUS_ID) + .send(version::Skip {}) + .await?? + { + Some(r) => CommandOutput::object(format!("skipped: {:?}", r)), + None => CommandOutput::object("not skipped"), + }, + UpgradeCLI::Check => CommandOutput::object("not checked"), + // Ok(Some(release)) => release, + // Ok(None) => CommandOutput::object(format!( + // "Your Yagna is up to date: {}", + // ya_compile_time_utils::version_describe!() + // )), + } + } +} + +pub struct VersionService; + +impl Service for VersionService { + type Cli = UpgradeCLI; +} + +impl VersionService { + pub async fn gsb>(ctx: &C) -> anyhow::Result<()> { + let db = ctx.component(); + // db.apply_migration(migrations::run_with_output)?; + bind_gsb(&db); + + // TODO: make interval configurable + let mut log_interval = tokio::time::interval(Duration::from_secs(30)); + tokio::task::spawn_local(async move { + loop { + let release_dao = db.as_dao::(); + log_interval.tick().await; + match release_dao.pending_release().await { + Ok(Some(release)) => { + if !release.seen { + log::warn!( + "New Yagna version {} ({}) is available. TODO: add howto here", + release.name, + release.version + ) + } + } + Ok(None) => log::warn!("no new version yet :-/"), + Err(e) => log::error!("while fetching pending release: {}", e), + } + } + }); + + Ok(()) + } + + pub fn rest>(ctx: &C) -> actix_web::Scope { + let db: DbExecutor = ctx.component(); + web::scope("").data(db).service(show_version) + } +} + +#[actix_web::get("/version")] +async fn show_version(db: web::Data) -> impl Responder { + match db.as_dao::().pending_release().await { + Ok(Some(release)) => HttpResponse::Ok().json(release), + Ok(None) => HttpResponse::Ok().json(ya_compile_time_utils::semver_str()), + Err(e) => HttpResponse::InternalServerError().json(e.to_string()), + } +} + +pub fn bind_gsb(db: &DbExecutor) { + // public for remote requestors interactions + bus::ServiceBinder::new(version::BUS_ID, db, ()) + .bind(skip_version_gsb) + .bind(check_version_gsb); + + // Initialize counters to 0 value. Otherwise they won't appear on metrics endpoint + // until first change to value will be made. + counter!("version.new", 0); + counter!("version.skip", 0); +} + +async fn skip_version_gsb( + db: DbExecutor, + _caller: String, + _msg: version::Skip, +) -> RpcMessageResult { + match db.as_dao::().skip_pending_release().await { + Ok(r) => Ok(r.map(|r| r.into())), + Err(e) => Err(e.to_string().into()), + } +} + +async fn check_version_gsb( + db: DbExecutor, + _caller: String, + _msg: version::Check, +) -> RpcMessageResult { + crate::notifier::check_release() + .await + .map_err(|e| e.to_string())?; + + db.as_dao::() + .pending_release() + .await + .map(|r| r.map(|r| r.into())) + .map_err(|e| e.to_string().into()) +} From 2c7afa4f7614e2b5ec404393f4e2d3afc193418b Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 15:53:23 +0100 Subject: [PATCH 05/22] [web-middleware] allow /version wo auth --- core/serv-api/web/src/middleware/auth/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/serv-api/web/src/middleware/auth/mod.rs b/core/serv-api/web/src/middleware/auth/mod.rs index 0db861c1a2..1f2ad98c03 100644 --- a/core/serv-api/web/src/middleware/auth/mod.rs +++ b/core/serv-api/web/src/middleware/auth/mod.rs @@ -81,7 +81,9 @@ where let service = self.service.clone(); // TODO: remove this hack; possibly by enabling creation of arbitrary appkey from CLI - if req.uri().to_string().starts_with("/metrics-api") { + if req.uri().to_string().starts_with("/metrics-api") + || req.uri().to_string().starts_with("/version") + { log::debug!("skipping authorization for uri={}", req.uri()); return Box::pin(service.borrow_mut().call(req)); } From 05c970c0e60834263e2f22fbf38519030f4bae5a Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 16:10:34 +0100 Subject: [PATCH 06/22] [ya-version] all parts fastened together --- Cargo.lock | 6 +++- core/serv/src/main.rs | 6 +--- core/version/Cargo.toml | 3 +- core/version/src/db.rs | 6 ++++ core/version/src/lib.rs | 57 ++++++++++++++++++++++++---------- core/version/src/rest.rs | 29 +++++------------ core/version/src/service.rs | 62 ++++++++++--------------------------- 7 files changed, 77 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5813b7d27e..f92576c298 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7908,16 +7908,20 @@ dependencies = [ "diesel", "diesel_migrations", "log", + "metrics", "self_update", "serde", "serde_json", + "structopt", "thiserror", "tokio 0.2.24", "ya-client", "ya-compile-time-utils", + "ya-core-model", "ya-persistence", + "ya-service-api", "ya-service-api-interfaces", - "ya-service-api-web", + "ya-service-bus", ] [[package]] diff --git a/core/serv/src/main.rs b/core/serv/src/main.rs index baeb8a73b3..0db76a25c7 100644 --- a/core/serv/src/main.rs +++ b/core/serv/src/main.rs @@ -19,8 +19,6 @@ use ya_market::MarketService; use ya_metrics::{MetricsPusherOpts, MetricsService}; use ya_net::Net as NetService; use ya_payment::{accounts as payment_accounts, PaymentService}; -use ya_version::VersionService; - use ya_persistence::executor::DbExecutor; use ya_sb_proto::{DEFAULT_GSB_URL, GSB_URL_ENV_VAR}; use ya_service_api::{CliCtx, CommandOutput}; @@ -191,11 +189,9 @@ enum Services { #[enable(gsb, rest)] Metrics(MetricsService), #[enable(gsb, rest, cli)] - Upgrade(VersionService), + Version(VersionService), #[enable(gsb, cli(flatten))] Identity(IdentityService), - #[enable(rest)] - Version(VersionService), #[enable(gsb)] Net(NetService), #[enable(gsb, rest)] diff --git a/core/version/Cargo.toml b/core/version/Cargo.toml index 864d932a04..92bcf177ec 100644 --- a/core/version/Cargo.toml +++ b/core/version/Cargo.toml @@ -6,13 +6,12 @@ authors = ["Golem Factory "] edition = "2018" [dependencies] -#ya-client = "0.4" +ya-client = "0.4" ya-compile-time-utils = "0.1" ya-core-model = { version = "0.2.0", features = ["version"] } ya-persistence = "0.2" ya-service-api = "0.1" ya-service-api-interfaces = "0.1" -#ya-service-api-web = "0.1" ya-service-bus = "0.2.2" actix-web = "3.2" diff --git a/core/version/src/db.rs b/core/version/src/db.rs index 162c53d51f..bd5c35d6e3 100644 --- a/core/version/src/db.rs +++ b/core/version/src/db.rs @@ -141,3 +141,9 @@ pub(crate) mod model { } } pub(crate) mod schema; + +#[allow(dead_code)] +pub mod migrations { + #[derive(EmbedMigrations)] + struct _Dummy; +} diff --git a/core/version/src/lib.rs b/core/version/src/lib.rs index 5a381ce19f..6a1e94d11c 100644 --- a/core/version/src/lib.rs +++ b/core/version/src/lib.rs @@ -1,20 +1,20 @@ #[macro_use] extern crate diesel; +#[macro_use] +extern crate diesel_migrations; pub(crate) mod db; mod rest; pub mod service; -pub use rest::VersionService; - pub mod notifier { + use metrics::counter; use std::time::Duration; - use tokio::time; use ya_persistence::executor::DbExecutor; - const UPDATE_CURL: &'static str = "curl -sSf https://join.golem.network/as-provider | bash -"; - const SILENCE_CMD: &'static str = "yagna update skip"; + use crate::db::dao::ReleaseDAO; + pub(crate) const DEFAULT_RELEASE_TS: &'static str = "2015-10-13T15:43:00GMT+2"; pub async fn check_release( @@ -39,8 +39,13 @@ pub mod notifier { .collect()) } - pub async fn on_start(db: DbExecutor) -> anyhow::Result<()> { - let release_dao = db.as_dao::(); + pub async fn on_start(db: &DbExecutor) -> anyhow::Result<()> { + let worker_db = db.clone(); + tokio::task::spawn_local(async move { crate::notifier::worker(worker_db).await }); + let pinger_db = db.clone(); + tokio::task::spawn_local(async move { crate::notifier::pinger(pinger_db).await }); + + let release_dao = db.as_dao::(); release_dao .new_release(self_update::update::Release { name: "".into(), @@ -49,11 +54,16 @@ pub mod notifier { body: None, assets: vec![], }) - .await?; + .await + .map_err(|e| { + anyhow::anyhow!("Storing current Yagna version as Release to DB: {}", e) + })?; Ok(()) } - pub async fn worker(db: DbExecutor) { - let mut interval = time::interval(Duration::from_secs(3600 * 24)); + + pub(crate) async fn worker(db: DbExecutor) { + // TODO: make interval configurable + let mut interval = tokio::time::interval(Duration::from_secs(3600 * 24)); let release_dao = db.as_dao::(); loop { interval.tick().await; @@ -64,10 +74,11 @@ pub mod notifier { ), Ok(releases) => { for r in releases.into_iter() { + counter!("version.new", 1); release_dao .new_release(r) .await - .map_err(|e| log::debug!("Problem storing new release. {}", e)) + .map_err(|e| log::error!("Storing new Yagna release to DB. {}", e)) .ok(); } } @@ -75,14 +86,26 @@ pub mod notifier { } } - pub async fn pinger(db: DbExecutor) { - let mut interval = time::interval(Duration::from_secs(60 * 24)); - let release_dao = db.as_dao::(); + pub(crate) async fn pinger(db: DbExecutor) -> ! { + // TODO: make interval configurable + // TODO: after test make it 30min instead 30sec + let mut interval = tokio::time::interval(Duration::from_secs(30)); loop { + let release_dao = db.as_dao::(); interval.tick().await; - if let Ok(Some(db_release)) = release_dao.pending_release().await { - log::warn!("New version of yagna available {}! Close yagna and run `{}` to install or `{}` to mute this notification", db_release, UPDATE_CURL, SILENCE_CMD); - }; + match release_dao.pending_release().await { + Ok(Some(release)) => { + if !release.seen { + log::warn!( + "New Yagna version {} ({}) is available. TODO: add howto here", + release.name, + release.version + ) + } + } + Ok(None) => log::trace!("Your Yagna is up to date"), + Err(e) => log::error!("Fetching new Yagna release from DB: {}", e), + } } } } diff --git a/core/version/src/rest.rs b/core/version/src/rest.rs index a14ae11680..ffe6704207 100644 --- a/core/version/src/rest.rs +++ b/core/version/src/rest.rs @@ -3,8 +3,6 @@ use crate::db::model::Release; use ya_client::model::ErrorMessage; use ya_persistence::executor::DbExecutor; -use ya_service_api_interfaces::{Provider, Service}; -use ya_service_api_web::middleware::Identity; use actix_web::{web, HttpResponse, ResponseError}; use anyhow::anyhow; @@ -12,33 +10,20 @@ use serde::{Deserialize, Serialize}; pub const VERSION_API_PATH: &str = ""; -pub struct VersionService; - -impl Service for VersionService { - type Cli = (); -} - -impl VersionService { - pub fn rest>(ctx: &Context) -> actix_web::Scope { - actix_web::web::scope(VERSION_API_PATH) - .data(ctx.component()) - .service(get_version) - } -} - #[derive(Serialize, Deserialize)] struct VersionInfo { pub current: Release, pub pending: Option, } -#[actix_web::get("/version")] -async fn get_version( - db: web::Data, - _id: Identity, -) -> Result { - // TODO: Should we validate identity?? +pub fn web_scope(db: DbExecutor) -> actix_web::Scope { + actix_web::web::scope(VERSION_API_PATH) + .data(db) + .service(get_version) +} +#[actix_web::get("/version")] +async fn get_version(db: web::Data) -> Result { Ok(HttpResponse::Ok().json(VersionInfo { current: db .as_dao::() diff --git a/core/version/src/service.rs b/core/version/src/service.rs index beb3a15727..cee1e7d821 100644 --- a/core/version/src/service.rs +++ b/core/version/src/service.rs @@ -1,6 +1,4 @@ -use actix_web::{web, HttpResponse, Responder}; use metrics::counter; -use std::time::Duration; use structopt::StructOpt; use ya_core_model::version; @@ -10,9 +8,7 @@ use ya_service_api_interfaces::{Provider, Service}; use ya_service_bus::{typed as bus, RpcEndpoint, RpcMessage}; pub type RpcMessageResult = Result<::Item, ::Error>; -// TODO: uncomment when added -// use crate::{db::migrations}; -use crate::db::dao::ReleaseDAO; +use crate::db::{dao::ReleaseDAO, migrations}; /// Yagna version management. #[derive(StructOpt, Debug)] @@ -34,12 +30,18 @@ impl UpgradeCLI { Some(r) => CommandOutput::object(format!("skipped: {:?}", r)), None => CommandOutput::object("not skipped"), }, - UpgradeCLI::Check => CommandOutput::object("not checked"), - // Ok(Some(release)) => release, - // Ok(None) => CommandOutput::object(format!( - // "Your Yagna is up to date: {}", - // ya_compile_time_utils::version_describe!() - // )), + UpgradeCLI::Check => match bus::service(version::BUS_ID) + .send(version::Check {}) + .await?? + { + Some(r) => { + CommandOutput::object(format!("New Yagna release is available: {:?}", r)) + } + None => CommandOutput::object(format!( + "Your Yagna is up to date -- {}", + ya_compile_time_utils::version_describe!() + )), + }, } } } @@ -53,46 +55,15 @@ impl Service for VersionService { impl VersionService { pub async fn gsb>(ctx: &C) -> anyhow::Result<()> { let db = ctx.component(); - // db.apply_migration(migrations::run_with_output)?; + db.apply_migration(migrations::run_with_output)?; + crate::notifier::on_start(&db).await?; bind_gsb(&db); - // TODO: make interval configurable - let mut log_interval = tokio::time::interval(Duration::from_secs(30)); - tokio::task::spawn_local(async move { - loop { - let release_dao = db.as_dao::(); - log_interval.tick().await; - match release_dao.pending_release().await { - Ok(Some(release)) => { - if !release.seen { - log::warn!( - "New Yagna version {} ({}) is available. TODO: add howto here", - release.name, - release.version - ) - } - } - Ok(None) => log::warn!("no new version yet :-/"), - Err(e) => log::error!("while fetching pending release: {}", e), - } - } - }); - Ok(()) } pub fn rest>(ctx: &C) -> actix_web::Scope { - let db: DbExecutor = ctx.component(); - web::scope("").data(db).service(show_version) - } -} - -#[actix_web::get("/version")] -async fn show_version(db: web::Data) -> impl Responder { - match db.as_dao::().pending_release().await { - Ok(Some(release)) => HttpResponse::Ok().json(release), - Ok(None) => HttpResponse::Ok().json(ya_compile_time_utils::semver_str()), - Err(e) => HttpResponse::InternalServerError().json(e.to_string()), + crate::rest::web_scope(ctx.component()) } } @@ -113,6 +84,7 @@ async fn skip_version_gsb( _caller: String, _msg: version::Skip, ) -> RpcMessageResult { + counter!("version.skip", 1); match db.as_dao::().skip_pending_release().await { Ok(r) => Ok(r.map(|r| r.into())), Err(e) => Err(e.to_string().into()), From 1d3ab106302b367fa44e4f7e6f01228fc657b416 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 16:18:32 +0100 Subject: [PATCH 07/22] [ya-version] messages better UX + counter fix --- core/version/src/db.rs | 4 ++-- core/version/src/service.rs | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/version/src/db.rs b/core/version/src/db.rs index bd5c35d6e3..1902fdf113 100644 --- a/core/version/src/db.rs +++ b/core/version/src/db.rs @@ -91,9 +91,9 @@ pub(crate) mod dao { .execute(conn)?; pending_release.seen = true; match num_updated { - 0 => anyhow::bail!("no release updated: {}", pending_release), + 0 => anyhow::bail!("Release not skipped: {}", pending_release), 1 => Ok(Some(pending_release)), - _ => anyhow::bail!("more than one release updated: {}", pending_release), + _ => anyhow::bail!("More than one Release skipped: {}", pending_release), } }) .await diff --git a/core/version/src/service.rs b/core/version/src/service.rs index cee1e7d821..f097158743 100644 --- a/core/version/src/service.rs +++ b/core/version/src/service.rs @@ -27,7 +27,10 @@ impl UpgradeCLI { .send(version::Skip {}) .await?? { - Some(r) => CommandOutput::object(format!("skipped: {:?}", r)), + Some(r) => { + counter!("version.skip", 1); + CommandOutput::object(format!("skipped: {:?}", r)) + } None => CommandOutput::object("not skipped"), }, UpgradeCLI::Check => match bus::service(version::BUS_ID) @@ -84,7 +87,6 @@ async fn skip_version_gsb( _caller: String, _msg: version::Skip, ) -> RpcMessageResult { - counter!("version.skip", 1); match db.as_dao::().skip_pending_release().await { Ok(r) => Ok(r.map(|r| r.into())), Err(e) => Err(e.to_string().into()), From 0a51a7d3da06a4c00f39ef698aba50023d33bb39 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 18:44:25 +0100 Subject: [PATCH 08/22] [ya-version] check only latest release and save it + msgs & logs UX --- core/version/src/db.rs | 34 +++++++------- core/version/src/lib.rs | 89 ++++++++++++++++++++----------------- core/version/src/rest.rs | 6 +-- core/version/src/service.rs | 17 ++++--- 4 files changed, 80 insertions(+), 66 deletions(-) diff --git a/core/version/src/db.rs b/core/version/src/db.rs index 1902fdf113..3e1472d19d 100644 --- a/core/version/src/db.rs +++ b/core/version/src/db.rs @@ -1,11 +1,12 @@ pub(crate) mod dao { - use anyhow::Result; use chrono::NaiveDateTime; use diesel::dsl::{exists, select}; use diesel::prelude::*; + use self_update::update::Release; + use ya_persistence::executor::{do_with_transaction, readonly_transaction, AsDao, PoolType}; - use crate::db::model::Release as DBRelease; + use crate::db::model::DBRelease; use crate::db::schema::version_release::dsl as release; use crate::db::schema::version_release::dsl::version_release; @@ -19,16 +20,16 @@ pub(crate) mod dao { } impl<'c> ReleaseDAO<'c> { - pub async fn new_release(&self, r: self_update::update::Release) -> Result<()> { + pub async fn new_release(&self, r: &Release) -> anyhow::Result { let db_release = DBRelease { - version: r.version, - name: r.name, + version: r.version.clone(), + name: r.name.clone(), seen: false, release_ts: NaiveDateTime::parse_from_str(&r.date, "%Y-%m-%dT%H:%M:%S%Z")?, insertion_ts: None, update_ts: None, }; - Ok(do_with_transaction(self.pool, move |conn| { + do_with_transaction(self.pool, move |conn| { if !select(exists( version_release.filter(release::version.eq(&db_release.version)), )) @@ -38,12 +39,12 @@ pub(crate) mod dao { .values(&db_release) .execute(conn)?; }; - Result::<()>::Ok(()) + Ok(db_release) }) - .await?) + .await } - pub async fn pending_release(&self) -> Result> { + pub async fn pending_release(&self) -> anyhow::Result> { readonly_transaction(self.pool, move |conn| { let query = version_release .filter(release::seen.eq(false)) @@ -69,7 +70,7 @@ pub(crate) mod dao { .await } - pub async fn current_release(&self) -> Result> { + pub async fn current_release(&self) -> anyhow::Result> { readonly_transaction(self.pool, move |conn| { Ok(version_release .filter(release::version.eq(ya_compile_time_utils::semver_str())) @@ -79,7 +80,8 @@ pub(crate) mod dao { .await } - pub async fn skip_pending_release(&self) -> Result> { + pub async fn skip_pending_release(&self) -> anyhow::Result> { + log::debug!("Skipping latest pending Yagna release"); let mut pending_release = match self.pending_release().await? { Some(r) => r, None => return Ok(None), @@ -93,7 +95,7 @@ pub(crate) mod dao { match num_updated { 0 => anyhow::bail!("Release not skipped: {}", pending_release), 1 => Ok(Some(pending_release)), - _ => anyhow::bail!("More than one Release skipped: {}", pending_release), + _ => anyhow::bail!("More than one release skipped: {}", pending_release), } }) .await @@ -108,7 +110,7 @@ pub(crate) mod model { #[derive(Clone, Debug, Identifiable, Insertable, Queryable, Serialize, Deserialize)] #[primary_key(version)] #[table_name = "version_release"] - pub struct Release { + pub struct DBRelease { pub version: String, pub name: String, pub seen: bool, @@ -117,7 +119,7 @@ pub(crate) mod model { pub update_ts: Option, } - impl std::fmt::Display for Release { + impl std::fmt::Display for DBRelease { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -127,8 +129,8 @@ pub(crate) mod model { } } - impl From for ya_core_model::version::Release { - fn from(r: Release) -> Self { + impl From for ya_core_model::version::Release { + fn from(r: DBRelease) -> Self { Self { version: r.version, name: r.name, diff --git a/core/version/src/lib.rs b/core/version/src/lib.rs index 6a1e94d11c..047072d080 100644 --- a/core/version/src/lib.rs +++ b/core/version/src/lib.rs @@ -8,35 +8,58 @@ mod rest; pub mod service; pub mod notifier { + use anyhow::anyhow; use metrics::counter; + use self_update::backends::github::UpdateBuilder; + use self_update::update::Release; + use self_update::version; use std::time::Duration; use ya_persistence::executor::DbExecutor; use crate::db::dao::ReleaseDAO; + use crate::db::model::DBRelease; pub(crate) const DEFAULT_RELEASE_TS: &'static str = "2015-10-13T15:43:00GMT+2"; - pub async fn check_release( - ) -> Result, self_update::errors::Error> { - log::trace!("Market release checker started"); - let releases = self_update::backends::github::ReleaseList::configure() + #[derive(thiserror::Error, Debug, Clone)] + #[error("New Yagna release named '{}' (v{}) is available", .0.name, .0.version)] + pub(crate) enum ReleaseMessage<'a> { + Available(&'a ya_core_model::version::Release), + AvailableDB(&'a DBRelease), + #[error("Release '{}' (v{}) skipped", .0.name, .0.version)] + Skipped(&'a ya_core_model::version::Release), + } + + pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result<()> { + log::trace!("Checking latest Yagna release"); + let release = UpdateBuilder::new() .repo_owner("golemfactory") .repo_name("yagna") + .bin_name("") // seems required by builder but unused + .current_version("") // similar as above .build()? - .fetch()?; - log::trace!("Market release checker done"); - Ok(releases - .into_iter() - .filter(|r| { - self_update::version::bump_is_greater( - ya_compile_time_utils::semver_str(), - r.version.as_str(), - ) - .map_err(|e| log::warn!("Github version parse error. {}", e)) - .unwrap_or(false) - }) - .collect()) + .get_latest_release()?; + log::trace!( + "Got latest Yagna release: '{}' (v{})", + release.name, + release.version + ); + if version::bump_is_greater( + ya_compile_time_utils::semver_str(), + release.version.as_str(), + ) + .map_err(|e| anyhow!("Github release `{:?}` parse error: {}", release, e))? + { + match db.as_dao::().new_release(&release).await { + Err(e) => log::error!("Storing new Yagna release `{:?}` to DB. {}", release, e), + Ok(r) => { + counter!("version.new", 1); + new_version_log(&r); + } + } + }; + Ok(()) } pub async fn on_start(db: &DbExecutor) -> anyhow::Result<()> { @@ -47,7 +70,7 @@ pub mod notifier { let release_dao = db.as_dao::(); release_dao - .new_release(self_update::update::Release { + .new_release(&Release { name: "".into(), version: ya_compile_time_utils::semver_str().into(), date: DEFAULT_RELEASE_TS.into(), @@ -56,7 +79,7 @@ pub mod notifier { }) .await .map_err(|e| { - anyhow::anyhow!("Storing current Yagna version as Release to DB: {}", e) + anyhow::anyhow!("Storing current Yagna version as release to DB: {}", e) })?; Ok(()) } @@ -64,24 +87,10 @@ pub mod notifier { pub(crate) async fn worker(db: DbExecutor) { // TODO: make interval configurable let mut interval = tokio::time::interval(Duration::from_secs(3600 * 24)); - let release_dao = db.as_dao::(); loop { interval.tick().await; - match check_release().await { - Err(e) => log::debug!( - "Problem encountered during checking for new releases: {}", - e - ), - Ok(releases) => { - for r in releases.into_iter() { - counter!("version.new", 1); - release_dao - .new_release(r) - .await - .map_err(|e| log::error!("Storing new Yagna release to DB. {}", e)) - .ok(); - } - } + if let Err(e) = check_latest_release(&db).await { + log::error!("Failed to check for new Yagna release: {}", e); }; } } @@ -96,11 +105,7 @@ pub mod notifier { match release_dao.pending_release().await { Ok(Some(release)) => { if !release.seen { - log::warn!( - "New Yagna version {} ({}) is available. TODO: add howto here", - release.name, - release.version - ) + new_version_log(&release) } } Ok(None) => log::trace!("Your Yagna is up to date"), @@ -108,6 +113,10 @@ pub mod notifier { } } } + + fn new_version_log(release: &DBRelease) { + log::warn!("{}", ReleaseMessage::AvailableDB(release)) + } } #[cfg(test)] diff --git a/core/version/src/rest.rs b/core/version/src/rest.rs index ffe6704207..33229df096 100644 --- a/core/version/src/rest.rs +++ b/core/version/src/rest.rs @@ -1,5 +1,5 @@ use crate::db::dao::ReleaseDAO; -use crate::db::model::Release; +use crate::db::model::DBRelease; use ya_client::model::ErrorMessage; use ya_persistence::executor::DbExecutor; @@ -12,8 +12,8 @@ pub const VERSION_API_PATH: &str = ""; #[derive(Serialize, Deserialize)] struct VersionInfo { - pub current: Release, - pub pending: Option, + pub current: DBRelease, + pub pending: Option, } pub fn web_scope(db: DbExecutor) -> actix_web::Scope { diff --git a/core/version/src/service.rs b/core/version/src/service.rs index f097158743..dc68ac2bea 100644 --- a/core/version/src/service.rs +++ b/core/version/src/service.rs @@ -9,6 +9,7 @@ use ya_service_bus::{typed as bus, RpcEndpoint, RpcMessage}; pub type RpcMessageResult = Result<::Item, ::Error>; use crate::db::{dao::ReleaseDAO, migrations}; +use crate::notifier::ReleaseMessage; /// Yagna version management. #[derive(StructOpt, Debug)] @@ -29,17 +30,15 @@ impl UpgradeCLI { { Some(r) => { counter!("version.skip", 1); - CommandOutput::object(format!("skipped: {:?}", r)) + CommandOutput::object(ReleaseMessage::Skipped(&r).to_string()) } - None => CommandOutput::object("not skipped"), + None => CommandOutput::object("No pending release to skip."), }, UpgradeCLI::Check => match bus::service(version::BUS_ID) .send(version::Check {}) .await?? { - Some(r) => { - CommandOutput::object(format!("New Yagna release is available: {:?}", r)) - } + Some(r) => CommandOutput::object(ReleaseMessage::Available(&r).to_string()), None => CommandOutput::object(format!( "Your Yagna is up to date -- {}", ya_compile_time_utils::version_describe!() @@ -88,7 +87,11 @@ async fn skip_version_gsb( _msg: version::Skip, ) -> RpcMessageResult { match db.as_dao::().skip_pending_release().await { - Ok(r) => Ok(r.map(|r| r.into())), + Ok(r) => Ok(r.map(|r| { + let r = r.into(); + log::info!("{}", ReleaseMessage::Skipped(&r)); + r + })), Err(e) => Err(e.to_string().into()), } } @@ -98,7 +101,7 @@ async fn check_version_gsb( _caller: String, _msg: version::Check, ) -> RpcMessageResult { - crate::notifier::check_release() + crate::notifier::check_latest_release(&db) .await .map_err(|e| e.to_string())?; From 17597e59539b332017c55a37eb3c3d4481db1f4e Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 19:10:00 +0100 Subject: [PATCH 09/22] [ya-version] splitted modules into files --- core/serv/src/main.rs | 2 +- core/version/src/db.rs | 148 +------------------------ core/version/src/db/dao.rs | 102 +++++++++++++++++ core/version/src/db/model.rs | 38 +++++++ core/version/src/lib.rs | 142 +----------------------- core/version/src/notifier.rs | 131 ++++++++++++++++++++++ core/version/src/service.rs | 98 ++-------------- core/version/src/service/cli.rs | 44 ++++++++ core/version/src/service/gsb.rs | 54 +++++++++ core/version/src/{ => service}/rest.rs | 0 10 files changed, 384 insertions(+), 375 deletions(-) create mode 100644 core/version/src/db/dao.rs create mode 100644 core/version/src/db/model.rs create mode 100644 core/version/src/notifier.rs create mode 100644 core/version/src/service/cli.rs create mode 100644 core/version/src/service/gsb.rs rename core/version/src/{ => service}/rest.rs (100%) diff --git a/core/serv/src/main.rs b/core/serv/src/main.rs index 0db76a25c7..8d78a9d037 100644 --- a/core/serv/src/main.rs +++ b/core/serv/src/main.rs @@ -30,7 +30,7 @@ use ya_service_api_web::{ use ya_sgx::SgxService; use ya_utils_path::data_dir::DataDir; use ya_utils_process::lock::ProcLock; -use ya_version::service::VersionService; +use ya_version::VersionService; mod autocomplete; use autocomplete::CompleteCommand; diff --git a/core/version/src/db.rs b/core/version/src/db.rs index 3e1472d19d..01cbc9b505 100644 --- a/core/version/src/db.rs +++ b/core/version/src/db.rs @@ -1,151 +1,9 @@ -pub(crate) mod dao { - use chrono::NaiveDateTime; - use diesel::dsl::{exists, select}; - use diesel::prelude::*; - use self_update::update::Release; - - use ya_persistence::executor::{do_with_transaction, readonly_transaction, AsDao, PoolType}; - - use crate::db::model::DBRelease; - use crate::db::schema::version_release::dsl as release; - use crate::db::schema::version_release::dsl::version_release; - - pub struct ReleaseDAO<'c> { - pool: &'c PoolType, - } - impl<'a> AsDao<'a> for ReleaseDAO<'a> { - fn as_dao(pool: &'a PoolType) -> Self { - Self { pool } - } - } - - impl<'c> ReleaseDAO<'c> { - pub async fn new_release(&self, r: &Release) -> anyhow::Result { - let db_release = DBRelease { - version: r.version.clone(), - name: r.name.clone(), - seen: false, - release_ts: NaiveDateTime::parse_from_str(&r.date, "%Y-%m-%dT%H:%M:%S%Z")?, - insertion_ts: None, - update_ts: None, - }; - do_with_transaction(self.pool, move |conn| { - if !select(exists( - version_release.filter(release::version.eq(&db_release.version)), - )) - .get_result(conn)? - { - diesel::insert_into(version_release) - .values(&db_release) - .execute(conn)?; - }; - Ok(db_release) - }) - .await - } - - pub async fn pending_release(&self) -> anyhow::Result> { - readonly_transaction(self.pool, move |conn| { - let query = version_release - .filter(release::seen.eq(false)) - .order(release::release_ts.desc()) - .into_boxed(); - - match query.first::(conn).optional()? { - Some(r) => { - if !self_update::version::bump_is_greater( - ya_compile_time_utils::semver_str(), - r.version.as_str(), - ) - .map_err(|e| log::warn!("Stored version parse error. {}", e)) - .unwrap_or(false) - { - return Ok(None); - } - Ok(Some(r)) - } - None => Ok(None), - } - }) - .await - } - - pub async fn current_release(&self) -> anyhow::Result> { - readonly_transaction(self.pool, move |conn| { - Ok(version_release - .filter(release::version.eq(ya_compile_time_utils::semver_str())) - .first::(conn) - .optional()?) - }) - .await - } - - pub async fn skip_pending_release(&self) -> anyhow::Result> { - log::debug!("Skipping latest pending Yagna release"); - let mut pending_release = match self.pending_release().await? { - Some(r) => r, - None => return Ok(None), - }; - - do_with_transaction(self.pool, move |conn| { - let num_updated = diesel::update(version_release.find(&pending_release.version)) - .set(release::seen.eq(true)) - .execute(conn)?; - pending_release.seen = true; - match num_updated { - 0 => anyhow::bail!("Release not skipped: {}", pending_release), - 1 => Ok(Some(pending_release)), - _ => anyhow::bail!("More than one release skipped: {}", pending_release), - } - }) - .await - } - } -} -pub(crate) mod model { - use crate::db::schema::version_release; - use chrono::NaiveDateTime; - use serde::{Deserialize, Serialize}; - - #[derive(Clone, Debug, Identifiable, Insertable, Queryable, Serialize, Deserialize)] - #[primary_key(version)] - #[table_name = "version_release"] - pub struct DBRelease { - pub version: String, - pub name: String, - pub seen: bool, - pub release_ts: NaiveDateTime, - pub insertion_ts: Option, - pub update_ts: Option, - } - - impl std::fmt::Display for DBRelease { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{} {} released {}", - self.version, self.name, self.release_ts - ) - } - } - - impl From for ya_core_model::version::Release { - fn from(r: DBRelease) -> Self { - Self { - version: r.version, - name: r.name, - seen: r.seen, - release_ts: r.release_ts, - insertion_ts: r.insertion_ts, - update_ts: r.update_ts, - } - } - } -} +pub(crate) mod dao; +pub(crate) mod model; pub(crate) mod schema; #[allow(dead_code)] -pub mod migrations { +pub(crate) mod migrations { #[derive(EmbedMigrations)] struct _Dummy; } diff --git a/core/version/src/db/dao.rs b/core/version/src/db/dao.rs new file mode 100644 index 0000000000..fd3d76b740 --- /dev/null +++ b/core/version/src/db/dao.rs @@ -0,0 +1,102 @@ +use chrono::NaiveDateTime; +use diesel::dsl::{exists, select}; +use diesel::prelude::*; +use self_update::update::Release; + +use ya_persistence::executor::{do_with_transaction, readonly_transaction, AsDao, PoolType}; + +use crate::db::model::DBRelease; +use crate::db::schema::version_release::dsl as release; +use crate::db::schema::version_release::dsl::version_release; + +pub struct ReleaseDAO<'c> { + pool: &'c PoolType, +} +impl<'a> AsDao<'a> for ReleaseDAO<'a> { + fn as_dao(pool: &'a PoolType) -> Self { + Self { pool } + } +} + +impl<'c> ReleaseDAO<'c> { + pub async fn new_release(&self, r: &Release) -> anyhow::Result { + let db_release = DBRelease { + version: r.version.clone(), + name: r.name.clone(), + seen: false, + release_ts: NaiveDateTime::parse_from_str(&r.date, "%Y-%m-%dT%H:%M:%S%Z")?, + insertion_ts: None, + update_ts: None, + }; + do_with_transaction(self.pool, move |conn| { + if !select(exists( + version_release.filter(release::version.eq(&db_release.version)), + )) + .get_result(conn)? + { + diesel::insert_into(version_release) + .values(&db_release) + .execute(conn)?; + }; + Ok(db_release) + }) + .await + } + + pub async fn pending_release(&self) -> anyhow::Result> { + readonly_transaction(self.pool, move |conn| { + let query = version_release + .filter(release::seen.eq(false)) + .order(release::release_ts.desc()) + .into_boxed(); + + match query.first::(conn).optional()? { + Some(r) => { + if !self_update::version::bump_is_greater( + ya_compile_time_utils::semver_str(), + r.version.as_str(), + ) + .map_err(|e| log::warn!("Stored version parse error. {}", e)) + .unwrap_or(false) + { + return Ok(None); + } + Ok(Some(r)) + } + None => Ok(None), + } + }) + .await + } + + pub async fn current_release(&self) -> anyhow::Result> { + readonly_transaction(self.pool, move |conn| { + Ok(version_release + .filter(release::version.eq(ya_compile_time_utils::semver_str())) + .first::(conn) + .optional()?) + }) + .await + } + + pub async fn skip_pending_release(&self) -> anyhow::Result> { + log::debug!("Skipping latest pending Yagna release"); + let mut pending_release = match self.pending_release().await? { + Some(r) => r, + None => return Ok(None), + }; + + do_with_transaction(self.pool, move |conn| { + let num_updated = diesel::update(version_release.find(&pending_release.version)) + .set(release::seen.eq(true)) + .execute(conn)?; + pending_release.seen = true; + match num_updated { + 0 => anyhow::bail!("Release not skipped: {}", pending_release), + 1 => Ok(Some(pending_release)), + _ => anyhow::bail!("More than one release skipped: {}", pending_release), + } + }) + .await + } +} diff --git a/core/version/src/db/model.rs b/core/version/src/db/model.rs new file mode 100644 index 0000000000..8b8587a344 --- /dev/null +++ b/core/version/src/db/model.rs @@ -0,0 +1,38 @@ +use crate::db::schema::version_release; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Identifiable, Insertable, Queryable, Serialize, Deserialize)] +#[primary_key(version)] +#[table_name = "version_release"] +pub struct DBRelease { + pub version: String, + pub name: String, + pub seen: bool, + pub release_ts: NaiveDateTime, + pub insertion_ts: Option, + pub update_ts: Option, +} + +impl std::fmt::Display for DBRelease { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} {} released {}", + self.version, self.name, self.release_ts + ) + } +} + +impl From for ya_core_model::version::Release { + fn from(r: DBRelease) -> Self { + Self { + version: r.version, + name: r.name, + seen: r.seen, + release_ts: r.release_ts, + insertion_ts: r.insertion_ts, + update_ts: r.update_ts, + } + } +} diff --git a/core/version/src/lib.rs b/core/version/src/lib.rs index 047072d080..7d6be474ed 100644 --- a/core/version/src/lib.rs +++ b/core/version/src/lib.rs @@ -3,142 +3,8 @@ extern crate diesel; #[macro_use] extern crate diesel_migrations; -pub(crate) mod db; -mod rest; -pub mod service; +mod db; +mod notifier; +mod service; -pub mod notifier { - use anyhow::anyhow; - use metrics::counter; - use self_update::backends::github::UpdateBuilder; - use self_update::update::Release; - use self_update::version; - use std::time::Duration; - - use ya_persistence::executor::DbExecutor; - - use crate::db::dao::ReleaseDAO; - use crate::db::model::DBRelease; - - pub(crate) const DEFAULT_RELEASE_TS: &'static str = "2015-10-13T15:43:00GMT+2"; - - #[derive(thiserror::Error, Debug, Clone)] - #[error("New Yagna release named '{}' (v{}) is available", .0.name, .0.version)] - pub(crate) enum ReleaseMessage<'a> { - Available(&'a ya_core_model::version::Release), - AvailableDB(&'a DBRelease), - #[error("Release '{}' (v{}) skipped", .0.name, .0.version)] - Skipped(&'a ya_core_model::version::Release), - } - - pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result<()> { - log::trace!("Checking latest Yagna release"); - let release = UpdateBuilder::new() - .repo_owner("golemfactory") - .repo_name("yagna") - .bin_name("") // seems required by builder but unused - .current_version("") // similar as above - .build()? - .get_latest_release()?; - log::trace!( - "Got latest Yagna release: '{}' (v{})", - release.name, - release.version - ); - if version::bump_is_greater( - ya_compile_time_utils::semver_str(), - release.version.as_str(), - ) - .map_err(|e| anyhow!("Github release `{:?}` parse error: {}", release, e))? - { - match db.as_dao::().new_release(&release).await { - Err(e) => log::error!("Storing new Yagna release `{:?}` to DB. {}", release, e), - Ok(r) => { - counter!("version.new", 1); - new_version_log(&r); - } - } - }; - Ok(()) - } - - pub async fn on_start(db: &DbExecutor) -> anyhow::Result<()> { - let worker_db = db.clone(); - tokio::task::spawn_local(async move { crate::notifier::worker(worker_db).await }); - let pinger_db = db.clone(); - tokio::task::spawn_local(async move { crate::notifier::pinger(pinger_db).await }); - - let release_dao = db.as_dao::(); - release_dao - .new_release(&Release { - name: "".into(), - version: ya_compile_time_utils::semver_str().into(), - date: DEFAULT_RELEASE_TS.into(), - body: None, - assets: vec![], - }) - .await - .map_err(|e| { - anyhow::anyhow!("Storing current Yagna version as release to DB: {}", e) - })?; - Ok(()) - } - - pub(crate) async fn worker(db: DbExecutor) { - // TODO: make interval configurable - let mut interval = tokio::time::interval(Duration::from_secs(3600 * 24)); - loop { - interval.tick().await; - if let Err(e) = check_latest_release(&db).await { - log::error!("Failed to check for new Yagna release: {}", e); - }; - } - } - - pub(crate) async fn pinger(db: DbExecutor) -> ! { - // TODO: make interval configurable - // TODO: after test make it 30min instead 30sec - let mut interval = tokio::time::interval(Duration::from_secs(30)); - loop { - let release_dao = db.as_dao::(); - interval.tick().await; - match release_dao.pending_release().await { - Ok(Some(release)) => { - if !release.seen { - new_version_log(&release) - } - } - Ok(None) => log::trace!("Your Yagna is up to date"), - Err(e) => log::error!("Fetching new Yagna release from DB: {}", e), - } - } - } - - fn new_version_log(release: &DBRelease) { - log::warn!("{}", ReleaseMessage::AvailableDB(release)) - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - use chrono::NaiveDateTime; - - #[test] - fn test_default_release_ts() -> Result<()> { - NaiveDateTime::parse_from_str(&crate::notifier::DEFAULT_RELEASE_TS, "%Y-%m-%dT%H:%M:%S%Z")?; - Ok(()) - } - - /* - - #[tokio::test] - async fn test_check_release() -> Result<()> { - let result = crate::notifier::check_release().await?; - println!("Check version result: {:#?}", result); - println!("Current version: {}", ya_compile_time_utils::semver_str()); - assert!(false); - Ok(()) - } - */ -} +pub use service::VersionService; diff --git a/core/version/src/notifier.rs b/core/version/src/notifier.rs new file mode 100644 index 0000000000..095323ca81 --- /dev/null +++ b/core/version/src/notifier.rs @@ -0,0 +1,131 @@ +use anyhow::anyhow; +use metrics::counter; +use self_update::backends::github::UpdateBuilder; +use self_update::update::Release; +use self_update::version; +use std::time::Duration; + +use ya_persistence::executor::DbExecutor; + +use crate::db::dao::ReleaseDAO; +use crate::db::model::DBRelease; + +pub(crate) const DEFAULT_RELEASE_TS: &'static str = "2015-10-13T15:43:00GMT+2"; + +#[derive(thiserror::Error, Debug, Clone)] +#[error("New Yagna release named '{}' (v{}) is available", .0.name, .0.version)] +pub(crate) enum ReleaseMessage<'a> { + Available(&'a ya_core_model::version::Release), + AvailableDB(&'a DBRelease), + #[error("Release '{}' (v{}) skipped", .0.name, .0.version)] + Skipped(&'a ya_core_model::version::Release), +} + +pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result<()> { + log::trace!("Checking latest Yagna release"); + let release = UpdateBuilder::new() + .repo_owner("golemfactory") + .repo_name("yagna") + .bin_name("") // seems required by builder but unused + .current_version("") // similar as above + .build()? + .get_latest_release()?; + log::trace!( + "Got latest Yagna release: '{}' (v{})", + release.name, + release.version + ); + if version::bump_is_greater( + ya_compile_time_utils::semver_str(), + release.version.as_str(), + ) + .map_err(|e| anyhow!("Github release `{:?}` parse error: {}", release, e))? + { + match db.as_dao::().new_release(&release).await { + Err(e) => log::error!("Storing new Yagna release `{:?}` to DB. {}", release, e), + Ok(r) => { + counter!("version.new", 1); + new_version_log(&r); + } + } + }; + Ok(()) +} + +pub async fn on_start(db: &DbExecutor) -> anyhow::Result<()> { + let worker_db = db.clone(); + tokio::task::spawn_local(async move { crate::notifier::worker(worker_db).await }); + let pinger_db = db.clone(); + tokio::task::spawn_local(async move { crate::notifier::pinger(pinger_db).await }); + + let release_dao = db.as_dao::(); + release_dao + .new_release(&Release { + name: "".into(), + version: ya_compile_time_utils::semver_str().into(), + date: DEFAULT_RELEASE_TS.into(), + body: None, + assets: vec![], + }) + .await + .map_err(|e| anyhow::anyhow!("Storing current Yagna version as release to DB: {}", e))?; + Ok(()) +} + +pub(crate) async fn worker(db: DbExecutor) { + // TODO: make interval configurable + let mut interval = tokio::time::interval(Duration::from_secs(3600 * 24)); + loop { + interval.tick().await; + if let Err(e) = check_latest_release(&db).await { + log::error!("Failed to check for new Yagna release: {}", e); + }; + } +} + +pub(crate) async fn pinger(db: DbExecutor) -> ! { + // TODO: make interval configurable + // TODO: after test make it 30min instead 30sec + let mut interval = tokio::time::interval(Duration::from_secs(30)); + loop { + let release_dao = db.as_dao::(); + interval.tick().await; + match release_dao.pending_release().await { + Ok(Some(release)) => { + if !release.seen { + new_version_log(&release) + } + } + Ok(None) => log::trace!("Your Yagna is up to date"), + Err(e) => log::error!("Fetching new Yagna release from DB: {}", e), + } + } +} + +fn new_version_log(release: &DBRelease) { + log::warn!("{}", ReleaseMessage::AvailableDB(release)) +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use chrono::NaiveDateTime; + + #[test] + fn test_default_release_ts() -> Result<()> { + NaiveDateTime::parse_from_str(&crate::notifier::DEFAULT_RELEASE_TS, "%Y-%m-%dT%H:%M:%S%Z")?; + Ok(()) + } + + /* + + #[tokio::test] + async fn test_check_release() -> Result<()> { + let result = crate::notifier::check_release().await?; + println!("Check version result: {:#?}", result); + println!("Current version: {}", ya_compile_time_utils::semver_str()); + assert!(false); + Ok(()) + } + */ +} diff --git a/core/version/src/service.rs b/core/version/src/service.rs index dc68ac2bea..9e4dbe6e8a 100644 --- a/core/version/src/service.rs +++ b/core/version/src/service.rs @@ -1,57 +1,16 @@ -use metrics::counter; -use structopt::StructOpt; - -use ya_core_model::version; use ya_persistence::executor::DbExecutor; -use ya_service_api::{CliCtx, CommandOutput}; use ya_service_api_interfaces::{Provider, Service}; -use ya_service_bus::{typed as bus, RpcEndpoint, RpcMessage}; -pub type RpcMessageResult = Result<::Item, ::Error>; - -use crate::db::{dao::ReleaseDAO, migrations}; -use crate::notifier::ReleaseMessage; -/// Yagna version management. -#[derive(StructOpt, Debug)] -pub enum UpgradeCLI { - /// Stop logging warnings about latest Yagna release availability. - Skip, - - /// Checks if there is new Yagna version available and shows it. - Check, -} +use crate::db::migrations; -impl UpgradeCLI { - pub async fn run_command(self, _ctx: &CliCtx) -> anyhow::Result { - match self { - UpgradeCLI::Skip => match bus::service(version::BUS_ID) - .send(version::Skip {}) - .await?? - { - Some(r) => { - counter!("version.skip", 1); - CommandOutput::object(ReleaseMessage::Skipped(&r).to_string()) - } - None => CommandOutput::object("No pending release to skip."), - }, - UpgradeCLI::Check => match bus::service(version::BUS_ID) - .send(version::Check {}) - .await?? - { - Some(r) => CommandOutput::object(ReleaseMessage::Available(&r).to_string()), - None => CommandOutput::object(format!( - "Your Yagna is up to date -- {}", - ya_compile_time_utils::version_describe!() - )), - }, - } - } -} +mod cli; +mod gsb; +mod rest; pub struct VersionService; impl Service for VersionService { - type Cli = UpgradeCLI; + type Cli = cli::UpgradeCLI; } impl VersionService { @@ -59,55 +18,12 @@ impl VersionService { let db = ctx.component(); db.apply_migration(migrations::run_with_output)?; crate::notifier::on_start(&db).await?; - bind_gsb(&db); + gsb::bind_gsb(&db); Ok(()) } pub fn rest>(ctx: &C) -> actix_web::Scope { - crate::rest::web_scope(ctx.component()) + rest::web_scope(ctx.component()) } } - -pub fn bind_gsb(db: &DbExecutor) { - // public for remote requestors interactions - bus::ServiceBinder::new(version::BUS_ID, db, ()) - .bind(skip_version_gsb) - .bind(check_version_gsb); - - // Initialize counters to 0 value. Otherwise they won't appear on metrics endpoint - // until first change to value will be made. - counter!("version.new", 0); - counter!("version.skip", 0); -} - -async fn skip_version_gsb( - db: DbExecutor, - _caller: String, - _msg: version::Skip, -) -> RpcMessageResult { - match db.as_dao::().skip_pending_release().await { - Ok(r) => Ok(r.map(|r| { - let r = r.into(); - log::info!("{}", ReleaseMessage::Skipped(&r)); - r - })), - Err(e) => Err(e.to_string().into()), - } -} - -async fn check_version_gsb( - db: DbExecutor, - _caller: String, - _msg: version::Check, -) -> RpcMessageResult { - crate::notifier::check_latest_release(&db) - .await - .map_err(|e| e.to_string())?; - - db.as_dao::() - .pending_release() - .await - .map(|r| r.map(|r| r.into())) - .map_err(|e| e.to_string().into()) -} diff --git a/core/version/src/service/cli.rs b/core/version/src/service/cli.rs new file mode 100644 index 0000000000..8ffaa739f5 --- /dev/null +++ b/core/version/src/service/cli.rs @@ -0,0 +1,44 @@ +use metrics::counter; +use structopt::StructOpt; + +use ya_core_model::version; +use ya_service_api::{CliCtx, CommandOutput}; +use ya_service_bus::{typed as bus, RpcEndpoint}; + +use crate::notifier::ReleaseMessage; + +// Yagna version management. +#[derive(StructOpt, Debug)] +pub enum UpgradeCLI { + /// Stop logging warnings about latest Yagna release availability. + Skip, + /// Checks if there is new Yagna version available and shows it. + Check, +} + +impl UpgradeCLI { + pub async fn run_command(self, _ctx: &CliCtx) -> anyhow::Result { + match self { + UpgradeCLI::Skip => match bus::service(version::BUS_ID) + .send(version::Skip {}) + .await?? + { + Some(r) => { + counter!("version.skip", 1); + CommandOutput::object(ReleaseMessage::Skipped(&r).to_string()) + } + None => CommandOutput::object("No pending release to skip."), + }, + UpgradeCLI::Check => match bus::service(version::BUS_ID) + .send(version::Check {}) + .await?? + { + Some(r) => CommandOutput::object(ReleaseMessage::Available(&r).to_string()), + None => CommandOutput::object(format!( + "Your Yagna is up to date -- {}", + ya_compile_time_utils::version_describe!() + )), + }, + } + } +} diff --git a/core/version/src/service/gsb.rs b/core/version/src/service/gsb.rs new file mode 100644 index 0000000000..92d039949b --- /dev/null +++ b/core/version/src/service/gsb.rs @@ -0,0 +1,54 @@ +use metrics::counter; + +use ya_core_model::version; +use ya_persistence::executor::DbExecutor; + +use crate::db::dao::ReleaseDAO; +use crate::notifier::ReleaseMessage; + +use ya_service_bus::{typed as bus, RpcMessage}; + +pub type RpcMessageResult = Result<::Item, ::Error>; + +pub fn bind_gsb(db: &DbExecutor) { + // public for remote requestors interactions + bus::ServiceBinder::new(version::BUS_ID, db, ()) + .bind(skip_version_gsb) + .bind(check_version_gsb); + + // Initialize counters to 0 value. Otherwise they won't appear on metrics endpoint + // until first change to value will be made. + counter!("version.new", 0); + counter!("version.skip", 0); +} + +async fn skip_version_gsb( + db: DbExecutor, + _caller: String, + _msg: version::Skip, +) -> RpcMessageResult { + match db.as_dao::().skip_pending_release().await { + Ok(r) => Ok(r.map(|r| { + let r = r.into(); + log::info!("{}", ReleaseMessage::Skipped(&r)); + r + })), + Err(e) => Err(e.to_string().into()), + } +} + +async fn check_version_gsb( + db: DbExecutor, + _caller: String, + _msg: version::Check, +) -> RpcMessageResult { + crate::notifier::check_latest_release(&db) + .await + .map_err(|e| e.to_string())?; + + db.as_dao::() + .pending_release() + .await + .map(|r| r.map(|r| r.into())) + .map_err(|e| e.to_string().into()) +} diff --git a/core/version/src/rest.rs b/core/version/src/service/rest.rs similarity index 100% rename from core/version/src/rest.rs rename to core/version/src/service/rest.rs From 68d2d9479b54ee4ae9de89ab6e30f58816c9ef86 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 21:21:18 +0100 Subject: [PATCH 10/22] [ya-version] rest and cli json unification + show command added + dao tuneup --- core/model/src/version.rs | 25 ++++++++-- core/version/src/db/dao.rs | 82 +++++++++++++++++++------------- core/version/src/service/cli.rs | 34 ++++++++----- core/version/src/service/gsb.rs | 19 ++++---- core/version/src/service/rest.rs | 39 ++------------- 5 files changed, 106 insertions(+), 93 deletions(-) diff --git a/core/model/src/version.rs b/core/model/src/version.rs index 878fd37cc2..de25980a46 100644 --- a/core/model/src/version.rs +++ b/core/model/src/version.rs @@ -21,11 +21,23 @@ impl RpcMessage for Skip { #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct Check(); +pub struct Get { + pub check: bool, +} + +impl Get { + pub fn show_only() -> Self { + Get { check: false } + } -impl RpcMessage for Check { + pub fn with_check() -> Self { + Get { check: true } + } +} + +impl RpcMessage for Get { const ID: &'static str = "check"; - type Item = Option; + type Item = VersionInfo; type Error = ErrorMessage; } @@ -39,3 +51,10 @@ pub struct Release { pub insertion_ts: Option, pub update_ts: Option, } + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct VersionInfo { + pub current: Release, + pub pending: Option, +} diff --git a/core/version/src/db/dao.rs b/core/version/src/db/dao.rs index fd3d76b740..212b36a8e4 100644 --- a/core/version/src/db/dao.rs +++ b/core/version/src/db/dao.rs @@ -1,13 +1,17 @@ +use anyhow::anyhow; use chrono::NaiveDateTime; use diesel::dsl::{exists, select}; use diesel::prelude::*; use self_update::update::Release; -use ya_persistence::executor::{do_with_transaction, readonly_transaction, AsDao, PoolType}; +use ya_persistence::executor::{ + do_with_transaction, readonly_transaction, AsDao, ConnType, PoolType, +}; use crate::db::model::DBRelease; use crate::db::schema::version_release::dsl as release; use crate::db::schema::version_release::dsl::version_release; +use ya_core_model::version::VersionInfo; pub struct ReleaseDAO<'c> { pool: &'c PoolType, @@ -44,49 +48,29 @@ impl<'c> ReleaseDAO<'c> { } pub async fn pending_release(&self) -> anyhow::Result> { - readonly_transaction(self.pool, move |conn| { - let query = version_release - .filter(release::seen.eq(false)) - .order(release::release_ts.desc()) - .into_boxed(); - - match query.first::(conn).optional()? { - Some(r) => { - if !self_update::version::bump_is_greater( - ya_compile_time_utils::semver_str(), - r.version.as_str(), - ) - .map_err(|e| log::warn!("Stored version parse error. {}", e)) - .unwrap_or(false) - { - return Ok(None); - } - Ok(Some(r)) - } - None => Ok(None), - } - }) - .await + readonly_transaction(self.pool, move |conn| get_pending_release(conn)).await } - pub async fn current_release(&self) -> anyhow::Result> { + pub async fn version(&self) -> anyhow::Result { + log::debug!("Getting Yagna version: current and pending from DB"); readonly_transaction(self.pool, move |conn| { - Ok(version_release - .filter(release::version.eq(ya_compile_time_utils::semver_str())) - .first::(conn) - .optional()?) + Ok(VersionInfo { + current: get_current_release(conn)? + .ok_or(anyhow!("Can't determine current release."))? + .into(), + pending: get_pending_release(conn)?.map(|r| r.into()), + }) }) .await } pub async fn skip_pending_release(&self) -> anyhow::Result> { log::debug!("Skipping latest pending Yagna release"); - let mut pending_release = match self.pending_release().await? { - Some(r) => r, - None => return Ok(None), - }; - do_with_transaction(self.pool, move |conn| { + let mut pending_release = match get_pending_release(conn)? { + Some(r) => r, + None => return Ok(None), + }; let num_updated = diesel::update(version_release.find(&pending_release.version)) .set(release::seen.eq(true)) .execute(conn)?; @@ -100,3 +84,33 @@ impl<'c> ReleaseDAO<'c> { .await } } + +fn get_current_release(conn: &ConnType) -> anyhow::Result> { + Ok(version_release + .filter(release::version.eq(ya_compile_time_utils::semver_str())) + .first::(conn) + .optional()?) +} + +fn get_pending_release(conn: &ConnType) -> anyhow::Result> { + let query = version_release + .filter(release::seen.eq(false)) + .order(release::release_ts.desc()) + .into_boxed(); + + match query.first::(conn).optional()? { + Some(r) => { + if !self_update::version::bump_is_greater( + ya_compile_time_utils::semver_str(), + r.version.as_str(), + ) + .map_err(|e| log::warn!("Stored version parse error. {}", e)) + .unwrap_or(false) + { + return Ok(None); + } + Ok(Some(r)) + } + None => Ok(None), + } +} diff --git a/core/version/src/service/cli.rs b/core/version/src/service/cli.rs index 8ffaa739f5..96a1c7283d 100644 --- a/core/version/src/service/cli.rs +++ b/core/version/src/service/cli.rs @@ -10,15 +10,19 @@ use crate::notifier::ReleaseMessage; // Yagna version management. #[derive(StructOpt, Debug)] pub enum UpgradeCLI { - /// Stop logging warnings about latest Yagna release availability. - Skip, + /// Show current Yagna version and updates if available. + Show, /// Checks if there is new Yagna version available and shows it. Check, + /// Stop logging warnings about latest Yagna release availability. + Skip, } impl UpgradeCLI { - pub async fn run_command(self, _ctx: &CliCtx) -> anyhow::Result { + pub async fn run_command(self, ctx: &CliCtx) -> anyhow::Result { match self { + UpgradeCLI::Show => UpgradeCLI::show(version::Get::show_only(), ctx).await, + UpgradeCLI::Check => UpgradeCLI::show(version::Get::with_check(), ctx).await, UpgradeCLI::Skip => match bus::service(version::BUS_ID) .send(version::Skip {}) .await?? @@ -29,16 +33,20 @@ impl UpgradeCLI { } None => CommandOutput::object("No pending release to skip."), }, - UpgradeCLI::Check => match bus::service(version::BUS_ID) - .send(version::Check {}) - .await?? - { - Some(r) => CommandOutput::object(ReleaseMessage::Available(&r).to_string()), - None => CommandOutput::object(format!( - "Your Yagna is up to date -- {}", - ya_compile_time_utils::version_describe!() - )), - }, + } + } + + async fn show(msg: version::Get, ctx: &CliCtx) -> anyhow::Result { + let version_info = bus::service(version::BUS_ID).send(msg).await??; + if ctx.json_output { + return CommandOutput::object(version_info); + } + match &version_info.pending { + Some(r) => CommandOutput::object(ReleaseMessage::Available(r).to_string()), + None => CommandOutput::object(format!( + "Your Yagna is up to date -- {}", + ya_compile_time_utils::version_describe!() + )), } } } diff --git a/core/version/src/service/gsb.rs b/core/version/src/service/gsb.rs index 92d039949b..28b65a7164 100644 --- a/core/version/src/service/gsb.rs +++ b/core/version/src/service/gsb.rs @@ -14,7 +14,7 @@ pub fn bind_gsb(db: &DbExecutor) { // public for remote requestors interactions bus::ServiceBinder::new(version::BUS_ID, db, ()) .bind(skip_version_gsb) - .bind(check_version_gsb); + .bind(get_version_gsb); // Initialize counters to 0 value. Otherwise they won't appear on metrics endpoint // until first change to value will be made. @@ -37,18 +37,19 @@ async fn skip_version_gsb( } } -async fn check_version_gsb( +async fn get_version_gsb( db: DbExecutor, _caller: String, - _msg: version::Check, -) -> RpcMessageResult { - crate::notifier::check_latest_release(&db) - .await - .map_err(|e| e.to_string())?; + msg: version::Get, +) -> RpcMessageResult { + if msg.check { + crate::notifier::check_latest_release(&db) + .await + .map_err(|e| e.to_string())?; + } db.as_dao::() - .pending_release() + .version() .await - .map(|r| r.map(|r| r.into())) .map_err(|e| e.to_string().into()) } diff --git a/core/version/src/service/rest.rs b/core/version/src/service/rest.rs index 33229df096..6f83028eeb 100644 --- a/core/version/src/service/rest.rs +++ b/core/version/src/service/rest.rs @@ -1,21 +1,12 @@ use crate::db::dao::ReleaseDAO; -use crate::db::model::DBRelease; use ya_client::model::ErrorMessage; use ya_persistence::executor::DbExecutor; -use actix_web::{web, HttpResponse, ResponseError}; -use anyhow::anyhow; -use serde::{Deserialize, Serialize}; +use actix_web::{web, HttpResponse, Responder}; pub const VERSION_API_PATH: &str = ""; -#[derive(Serialize, Deserialize)] -struct VersionInfo { - pub current: DBRelease, - pub pending: Option, -} - pub fn web_scope(db: DbExecutor) -> actix_web::Scope { actix_web::web::scope(VERSION_API_PATH) .data(db) @@ -23,29 +14,9 @@ pub fn web_scope(db: DbExecutor) -> actix_web::Scope { } #[actix_web::get("/version")] -async fn get_version(db: web::Data) -> Result { - Ok(HttpResponse::Ok().json(VersionInfo { - current: db - .as_dao::() - .current_release() - .await - .map_err(VersionError::from)? - .ok_or(anyhow!("Can't determine current version.")) - .map_err(VersionError::from)?, - pending: db - .as_dao::() - .pending_release() - .await - .map_err(VersionError::from)?, - })) -} - -#[derive(thiserror::Error, Debug)] -#[error("Error querying version. {0}.")] -pub struct VersionError(#[from] anyhow::Error); - -impl ResponseError for VersionError { - fn error_response(&self) -> HttpResponse { - HttpResponse::InternalServerError().json(ErrorMessage::new(self.to_string())) +async fn get_version(db: web::Data) -> impl Responder { + match db.as_dao::().version().await { + Ok(v) => HttpResponse::Ok().json(v), + Err(e) => HttpResponse::InternalServerError().json(ErrorMessage::new(e.to_string())), } } From 9bbbc7ba1d58ff1bb2a393b5251cb6dca0dd711d Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 22:21:08 +0100 Subject: [PATCH 11/22] [ya-version] pending release sorted by release_ts and version --- core/version/src/db/dao.rs | 2 +- core/version/src/service/cli.rs | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/version/src/db/dao.rs b/core/version/src/db/dao.rs index 212b36a8e4..b7a015dd3a 100644 --- a/core/version/src/db/dao.rs +++ b/core/version/src/db/dao.rs @@ -95,7 +95,7 @@ fn get_current_release(conn: &ConnType) -> anyhow::Result> { fn get_pending_release(conn: &ConnType) -> anyhow::Result> { let query = version_release .filter(release::seen.eq(false)) - .order(release::release_ts.desc()) + .order((release::release_ts.desc(), release::version.desc())) .into_boxed(); match query.first::(conn).optional()? { diff --git a/core/version/src/service/cli.rs b/core/version/src/service/cli.rs index 96a1c7283d..84468624eb 100644 --- a/core/version/src/service/cli.rs +++ b/core/version/src/service/cli.rs @@ -21,8 +21,8 @@ pub enum UpgradeCLI { impl UpgradeCLI { pub async fn run_command(self, ctx: &CliCtx) -> anyhow::Result { match self { - UpgradeCLI::Show => UpgradeCLI::show(version::Get::show_only(), ctx).await, - UpgradeCLI::Check => UpgradeCLI::show(version::Get::with_check(), ctx).await, + UpgradeCLI::Show => show(version::Get::show_only(), ctx).await, + UpgradeCLI::Check => show(version::Get::with_check(), ctx).await, UpgradeCLI::Skip => match bus::service(version::BUS_ID) .send(version::Skip {}) .await?? @@ -35,18 +35,18 @@ impl UpgradeCLI { }, } } +} - async fn show(msg: version::Get, ctx: &CliCtx) -> anyhow::Result { - let version_info = bus::service(version::BUS_ID).send(msg).await??; - if ctx.json_output { - return CommandOutput::object(version_info); - } - match &version_info.pending { - Some(r) => CommandOutput::object(ReleaseMessage::Available(r).to_string()), - None => CommandOutput::object(format!( - "Your Yagna is up to date -- {}", - ya_compile_time_utils::version_describe!() - )), - } +async fn show(msg: version::Get, ctx: &CliCtx) -> anyhow::Result { + let version_info = bus::service(version::BUS_ID).send(msg).await??; + if ctx.json_output { + return CommandOutput::object(version_info); + } + match &version_info.pending { + Some(r) => CommandOutput::object(ReleaseMessage::Available(r).to_string()), + None => CommandOutput::object(format!( + "Your Yagna is up to date -- {}", + ya_compile_time_utils::version_describe!() + )), } } From c829ff4e1ade038e0407022c9d53d7d62a0628d2 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 22:22:20 +0100 Subject: [PATCH 12/22] [golemsp] new version notification in status --- golem_cli/Cargo.toml | 25 +++++++++++++------------ golem_cli/src/command/yagna.rs | 11 +++++++++-- golem_cli/src/status.rs | 8 ++++++++ 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/golem_cli/Cargo.toml b/golem_cli/Cargo.toml index 1b3db72b5a..ef8b4097fe 100644 --- a/golem_cli/Cargo.toml +++ b/golem_cli/Cargo.toml @@ -1,37 +1,38 @@ [package] name = "golemsp" -description = "User friedly CLI for running provider" +description = "User friedly CLI for running Golem Provider" version = "0.1.0" authors = ["Golem Factory "] edition = "2018" [dependencies] -actix-rt="1.1" ya-compile-time-utils = "0.1" +ya-core-model = { version = "0.2", features=["payment", "version"] } +ya-utils-path = "0.1.0" +ya-utils-process = { version = "0.1.0", features = ["lock"] } + +actix-rt="1.1" +ansi_term="0.12.1" anyhow = "1.0" bigdecimal = { version = "0.1.0"} byte-unit = "4.0" chrono = { version = "0.4", features=["serde"] } +crossterm="0.18.0" +directories = "2.0.2" dotenv = "0.15" env_logger = "0.7" futures = "0.3" log = "0.4" +names = "0.10.0" +prettytable-rs = "0.8.0" +promptly = "0.3.0" +rustyline="6.3.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" strip-ansi-escapes = "0.1" structopt = "0.3" tokio = { version = "0.2", features = ["process", "rt-core", "signal", "time", "io-util", "io-std"] } url = "2.1" -directories = "2.0.2" -promptly = "0.3.0" -names = "0.10.0" -crossterm="0.18.0" -rustyline="6.3.0" -prettytable-rs = "0.8.0" -ansi_term="0.12.1" -ya-core-model = { version = "0.2.1", path="../core/model", features=["payment"] } -ya-utils-path = "0.1.0" -ya-utils-process = { version = "0.1.0", features = ["lock"] } [target.'cfg(target_family = "unix")'.dependencies] libc="0.2.73" diff --git a/golem_cli/src/command/yagna.rs b/golem_cli/src/command/yagna.rs index a4a82d485f..f7c5444f0d 100644 --- a/golem_cli/src/command/yagna.rs +++ b/golem_cli/src/command/yagna.rs @@ -9,6 +9,7 @@ use std::process::Stdio; use tokio::process::{Child, Command}; use ya_core_model::payment::local::{InvoiceStats, InvoiceStatusNotes, StatusNotes, StatusResult}; +use ya_core_model::version::VersionInfo; pub struct PaymentType { pub platform: &'static str, @@ -17,11 +18,11 @@ pub struct PaymentType { impl PaymentType { pub const ZK: PaymentType = PaymentType { - platform: "ZK-NGNT", + platform: "zksync-rinkeby-tglm", driver: "zksync", }; pub const PLAIN: PaymentType = PaymentType { - platform: "NGNT", + platform: "erc20-rinkeby-tglm", driver: "ngnt", }; } @@ -106,6 +107,7 @@ impl YagnaCommand { .output() .await?; if output.status.success() { + log::trace!("{}", String::from_utf8_lossy(&output.stdout)); Ok(serde_json::from_slice(&output.stdout)?) } else { Err(anyhow::anyhow!( @@ -121,6 +123,11 @@ impl YagnaCommand { output.map_err(anyhow::Error::msg) } + pub async fn version(mut self) -> anyhow::Result { + self.cmd.args(&["--json", "version", "show"]); + self.run().await + } + pub async fn payment_status( mut self, address: Option<&str>, diff --git a/golem_cli/src/status.rs b/golem_cli/src/status.rs index 54b30e68c0..101fa53fc4 100644 --- a/golem_cli/src/status.rs +++ b/golem_cli/src/status.rs @@ -82,7 +82,15 @@ pub async fn run() -> Result { Style::new().fg(Colour::Red).paint("is not running") ]); } + table.add_row(row!["Version", ya_compile_time_utils::version_describe!()]); + if let Some(pending) = cmd.yagna()?.version().await?.pending { + let ver = format!("{} released!", pending.version); + table.add_row(row![ + "New Version", + Style::new().fg(Colour::Fixed(220)).paint(ver) + ]); + } table.add_empty_row(); table.add_row(row!["Node Name", &config.node_name.unwrap_or_default()]); From b5fa96d7ecd5b74fa95074eeb4e7dcc68fb2ae7d Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 23:05:41 +0100 Subject: [PATCH 13/22] [ya-version] further UX improovements --- core/version/src/db/model.rs | 2 +- core/version/src/notifier.rs | 18 ++++-------- core/version/src/service.rs | 2 +- core/version/src/service/cli.rs | 49 +++++++++++++++++++++------------ core/version/src/service/gsb.rs | 5 ++-- 5 files changed, 40 insertions(+), 36 deletions(-) diff --git a/core/version/src/db/model.rs b/core/version/src/db/model.rs index 8b8587a344..4dd3f1f318 100644 --- a/core/version/src/db/model.rs +++ b/core/version/src/db/model.rs @@ -18,7 +18,7 @@ impl std::fmt::Display for DBRelease { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "{} {} released {}", + "{} '{}' released @ {}", self.version, self.name, self.release_ts ) } diff --git a/core/version/src/notifier.rs b/core/version/src/notifier.rs index 095323ca81..13c8735030 100644 --- a/core/version/src/notifier.rs +++ b/core/version/src/notifier.rs @@ -9,18 +9,10 @@ use ya_persistence::executor::DbExecutor; use crate::db::dao::ReleaseDAO; use crate::db::model::DBRelease; +use crate::service::cli::ReleaseMessage; pub(crate) const DEFAULT_RELEASE_TS: &'static str = "2015-10-13T15:43:00GMT+2"; -#[derive(thiserror::Error, Debug, Clone)] -#[error("New Yagna release named '{}' (v{}) is available", .0.name, .0.version)] -pub(crate) enum ReleaseMessage<'a> { - Available(&'a ya_core_model::version::Release), - AvailableDB(&'a DBRelease), - #[error("Release '{}' (v{}) skipped", .0.name, .0.version)] - Skipped(&'a ya_core_model::version::Release), -} - pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result<()> { log::trace!("Checking latest Yagna release"); let release = UpdateBuilder::new() @@ -45,7 +37,7 @@ pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result<()> { Err(e) => log::error!("Storing new Yagna release `{:?}` to DB. {}", release, e), Ok(r) => { counter!("version.new", 1); - new_version_log(&r); + new_version_log(r); } } }; @@ -93,7 +85,7 @@ pub(crate) async fn pinger(db: DbExecutor) -> ! { match release_dao.pending_release().await { Ok(Some(release)) => { if !release.seen { - new_version_log(&release) + new_version_log(release) } } Ok(None) => log::trace!("Your Yagna is up to date"), @@ -102,8 +94,8 @@ pub(crate) async fn pinger(db: DbExecutor) -> ! { } } -fn new_version_log(release: &DBRelease) { - log::warn!("{}", ReleaseMessage::AvailableDB(release)) +fn new_version_log(release: DBRelease) { + log::warn!("{}", ReleaseMessage::Available(&release.into())) } #[cfg(test)] diff --git a/core/version/src/service.rs b/core/version/src/service.rs index 9e4dbe6e8a..2b65714756 100644 --- a/core/version/src/service.rs +++ b/core/version/src/service.rs @@ -3,7 +3,7 @@ use ya_service_api_interfaces::{Provider, Service}; use crate::db::migrations; -mod cli; +pub(crate) mod cli; mod gsb; mod rest; diff --git a/core/version/src/service/cli.rs b/core/version/src/service/cli.rs index 84468624eb..9b09e0a825 100644 --- a/core/version/src/service/cli.rs +++ b/core/version/src/service/cli.rs @@ -5,7 +5,21 @@ use ya_core_model::version; use ya_service_api::{CliCtx, CommandOutput}; use ya_service_bus::{typed as bus, RpcEndpoint}; -use crate::notifier::ReleaseMessage; +const UPDATE_CURL: &'static str = "curl -sSf https://join.golem.network/as-provider | bash -"; +const SILENCE_CMD: &'static str = "yagna version skip"; + +#[derive(thiserror::Error, Debug, Clone)] +pub(crate) enum ReleaseMessage<'a> { + #[error("New Yagna release is available -- '{}' (v{}).\n\ + Update via `{}` or skip `{}`", .0.name, .0.version, UPDATE_CURL, SILENCE_CMD)] + Available(&'a version::Release), + #[error("Your Yagna is up to date -- '{}' (v{})", .0.name, .0.version)] + UpToDate(&'a version::Release), + #[error("Release skipped -- '{}' (v{})", .0.name, .0.version)] + Skipped(&'a version::Release), + #[error("No pending release to skip")] + NotSkipped, +} // Yagna version management. #[derive(StructOpt, Debug)] @@ -23,16 +37,18 @@ impl UpgradeCLI { match self { UpgradeCLI::Show => show(version::Get::show_only(), ctx).await, UpgradeCLI::Check => show(version::Get::with_check(), ctx).await, - UpgradeCLI::Skip => match bus::service(version::BUS_ID) - .send(version::Skip {}) - .await?? - { - Some(r) => { - counter!("version.skip", 1); - CommandOutput::object(ReleaseMessage::Skipped(&r).to_string()) - } - None => CommandOutput::object("No pending release to skip."), - }, + UpgradeCLI::Skip => CommandOutput::object( + match bus::service(version::BUS_ID) + .send(version::Skip()) + .await?? + { + Some(r) => { + counter!("version.skip", 1); + ReleaseMessage::Skipped(&r).to_string() + } + None => ReleaseMessage::NotSkipped.to_string(), + }, + ), } } } @@ -42,11 +58,8 @@ async fn show(msg: version::Get, ctx: &CliCtx) -> anyhow::Result if ctx.json_output { return CommandOutput::object(version_info); } - match &version_info.pending { - Some(r) => CommandOutput::object(ReleaseMessage::Available(r).to_string()), - None => CommandOutput::object(format!( - "Your Yagna is up to date -- {}", - ya_compile_time_utils::version_describe!() - )), - } + CommandOutput::object(match &version_info.pending { + Some(r) => ReleaseMessage::Available(r).to_string(), + None => ReleaseMessage::UpToDate(&version_info.current).to_string(), + }) } diff --git a/core/version/src/service/gsb.rs b/core/version/src/service/gsb.rs index 28b65a7164..5518c49ee6 100644 --- a/core/version/src/service/gsb.rs +++ b/core/version/src/service/gsb.rs @@ -2,11 +2,10 @@ use metrics::counter; use ya_core_model::version; use ya_persistence::executor::DbExecutor; +use ya_service_bus::{typed as bus, RpcMessage}; use crate::db::dao::ReleaseDAO; -use crate::notifier::ReleaseMessage; - -use ya_service_bus::{typed as bus, RpcMessage}; +use crate::service::cli::ReleaseMessage; pub type RpcMessageResult = Result<::Item, ::Error>; From 8de9732719f0f8d7ae388770e23f53d7f8b528bf Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 23:25:37 +0100 Subject: [PATCH 14/22] [ya-version] tuneup creation of (initial) release from binary-hardcoded-version --- core/version/src/notifier.rs | 30 ++++++++++-------------------- core/version/src/service/cli.rs | 6 +++--- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/core/version/src/notifier.rs b/core/version/src/notifier.rs index 13c8735030..090047db42 100644 --- a/core/version/src/notifier.rs +++ b/core/version/src/notifier.rs @@ -11,8 +11,6 @@ use crate::db::dao::ReleaseDAO; use crate::db::model::DBRelease; use crate::service::cli::ReleaseMessage; -pub(crate) const DEFAULT_RELEASE_TS: &'static str = "2015-10-13T15:43:00GMT+2"; - pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result<()> { log::trace!("Checking latest Yagna release"); let release = UpdateBuilder::new() @@ -53,9 +51,11 @@ pub async fn on_start(db: &DbExecutor) -> anyhow::Result<()> { let release_dao = db.as_dao::(); release_dao .new_release(&Release { - name: "".into(), + name: ya_compile_time_utils::build_number_str() + .map(|n| format!("{} build #{}", ya_compile_time_utils::git_rev(), n)) + .unwrap_or(ya_compile_time_utils::git_rev().into()), version: ya_compile_time_utils::semver_str().into(), - date: DEFAULT_RELEASE_TS.into(), + date: format!("{}T00:00:00Z", ya_compile_time_utils::build_date()), body: None, assets: vec![], }) @@ -100,24 +100,14 @@ fn new_version_log(release: DBRelease) { #[cfg(test)] mod tests { - use anyhow::Result; use chrono::NaiveDateTime; #[test] - fn test_default_release_ts() -> Result<()> { - NaiveDateTime::parse_from_str(&crate::notifier::DEFAULT_RELEASE_TS, "%Y-%m-%dT%H:%M:%S%Z")?; - Ok(()) - } - - /* - - #[tokio::test] - async fn test_check_release() -> Result<()> { - let result = crate::notifier::check_release().await?; - println!("Check version result: {:#?}", result); - println!("Current version: {}", ya_compile_time_utils::semver_str()); - assert!(false); - Ok(()) + fn test_default_release_ts() { + NaiveDateTime::parse_from_str( + &format!("{}T00:00:00Z", ya_compile_time_utils::build_date()), + "%Y-%m-%dT%H:%M:%S%Z", + ) + .unwrap(); } - */ } diff --git a/core/version/src/service/cli.rs b/core/version/src/service/cli.rs index 9b09e0a825..e0388b784e 100644 --- a/core/version/src/service/cli.rs +++ b/core/version/src/service/cli.rs @@ -10,12 +10,12 @@ const SILENCE_CMD: &'static str = "yagna version skip"; #[derive(thiserror::Error, Debug, Clone)] pub(crate) enum ReleaseMessage<'a> { - #[error("New Yagna release is available -- '{}' (v{}).\n\ - Update via `{}` or skip `{}`", .0.name, .0.version, UPDATE_CURL, SILENCE_CMD)] + #[error("New Yagna release is available: '{}' (v{}).\n\ + Update via\n\t`{}`\nor skip\n\t`{}`", .0.name, .0.version, UPDATE_CURL, SILENCE_CMD)] Available(&'a version::Release), #[error("Your Yagna is up to date -- '{}' (v{})", .0.name, .0.version)] UpToDate(&'a version::Release), - #[error("Release skipped -- '{}' (v{})", .0.name, .0.version)] + #[error("Release skipped: '{}' (v{})", .0.name, .0.version)] Skipped(&'a version::Release), #[error("No pending release to skip")] NotSkipped, From ad5fa7e880aa85db2d2f6a3e9e868db0e7d87cac Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Fri, 22 Jan 2021 23:33:20 +0100 Subject: [PATCH 15/22] [ya-version] log warning interval 30min --- core/version/src/notifier.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/version/src/notifier.rs b/core/version/src/notifier.rs index 090047db42..014574923b 100644 --- a/core/version/src/notifier.rs +++ b/core/version/src/notifier.rs @@ -77,8 +77,7 @@ pub(crate) async fn worker(db: DbExecutor) { pub(crate) async fn pinger(db: DbExecutor) -> ! { // TODO: make interval configurable - // TODO: after test make it 30min instead 30sec - let mut interval = tokio::time::interval(Duration::from_secs(30)); + let mut interval = tokio::time::interval(Duration::from_secs(30 * 60)); loop { let release_dao = db.as_dao::(); interval.tick().await; From 1eb4cdc7b3d2bcd517144a244b98c11be49b0d7e Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Sat, 23 Jan 2021 17:28:21 +0100 Subject: [PATCH 16/22] [ya-version] v0.1 + github fns in separate module --- Cargo.lock | 2 +- Cargo.toml | 2 +- core/model/src/version.rs | 3 +- core/version/Cargo.toml | 2 +- core/version/src/db/dao.rs | 93 ++++++++++++++++----------------- core/version/src/db/model.rs | 55 +++++++++++++------ core/version/src/github.rs | 89 +++++++++++++++++++++++++++++++ core/version/src/lib.rs | 1 + core/version/src/notifier.rs | 64 ++++------------------- core/version/src/service/gsb.rs | 2 +- 10 files changed, 190 insertions(+), 123 deletions(-) create mode 100644 core/version/src/github.rs diff --git a/Cargo.lock b/Cargo.lock index f92576c298..b2b8de6f7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7900,7 +7900,7 @@ dependencies = [ [[package]] name = "ya-version" -version = "0.0.1" +version = "0.1.0" dependencies = [ "actix-web 3.3.2", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index fb59e42ad5..ed93d1c970 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ ya-sgx = "0.1" ya-utils-path = "0.1" ya-utils-futures = "0.1" ya-utils-process = { version = "0.1", features = ["lock"] } -ya-version = "0.0.1" +ya-version = "0.1" actix-rt = "1.0" actix-service = "1.0" diff --git a/core/model/src/version.rs b/core/model/src/version.rs index de25980a46..fcc05345ab 100644 --- a/core/model/src/version.rs +++ b/core/model/src/version.rs @@ -41,8 +41,9 @@ impl RpcMessage for Get { type Error = ErrorMessage; } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, thiserror::Error)] #[serde(rename_all = "camelCase")] +#[error("Version {version} '{name}' released @ {release_ts}")] pub struct Release { pub version: String, pub name: String, diff --git a/core/version/Cargo.toml b/core/version/Cargo.toml index 92bcf177ec..a689f352b3 100644 --- a/core/version/Cargo.toml +++ b/core/version/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ya-version" -version = "0.0.1" +version = "0.1.0" description = "Version handling" authors = ["Golem Factory "] edition = "2018" diff --git a/core/version/src/db/dao.rs b/core/version/src/db/dao.rs index b7a015dd3a..b66f392bf4 100644 --- a/core/version/src/db/dao.rs +++ b/core/version/src/db/dao.rs @@ -1,9 +1,7 @@ use anyhow::anyhow; -use chrono::NaiveDateTime; -use diesel::dsl::{exists, select}; use diesel::prelude::*; -use self_update::update::Release; +use ya_core_model::version::{Release, VersionInfo}; use ya_persistence::executor::{ do_with_transaction, readonly_transaction, AsDao, ConnType, PoolType, }; @@ -11,7 +9,7 @@ use ya_persistence::executor::{ use crate::db::model::DBRelease; use crate::db::schema::version_release::dsl as release; use crate::db::schema::version_release::dsl::version_release; -use ya_core_model::version::VersionInfo; +use self_update::version::bump_is_greater; pub struct ReleaseDAO<'c> { pool: &'c PoolType, @@ -23,31 +21,22 @@ impl<'a> AsDao<'a> for ReleaseDAO<'a> { } impl<'c> ReleaseDAO<'c> { - pub async fn new_release(&self, r: &Release) -> anyhow::Result { - let db_release = DBRelease { - version: r.version.clone(), - name: r.name.clone(), - seen: false, - release_ts: NaiveDateTime::parse_from_str(&r.date, "%Y-%m-%dT%H:%M:%S%Z")?, - insertion_ts: None, - update_ts: None, - }; + pub async fn save(&self, db_rel: DBRelease) -> anyhow::Result { do_with_transaction(self.pool, move |conn| { - if !select(exists( - version_release.filter(release::version.eq(&db_release.version)), - )) - .get_result(conn)? - { - diesel::insert_into(version_release) - .values(&db_release) - .execute(conn)?; - }; - Ok(db_release) + match get_release(conn, &db_rel.version)? { + Some(rel) => Ok(rel), + None => { + diesel::insert_into(version_release) + .values(&db_rel) + .execute(conn)?; + Ok(db_rel.into()) + } + } }) .await } - pub async fn pending_release(&self) -> anyhow::Result> { + pub async fn pending_release(&self) -> anyhow::Result> { readonly_transaction(self.pool, move |conn| get_pending_release(conn)).await } @@ -55,61 +44,69 @@ impl<'c> ReleaseDAO<'c> { log::debug!("Getting Yagna version: current and pending from DB"); readonly_transaction(self.pool, move |conn| { Ok(VersionInfo { - current: get_current_release(conn)? - .ok_or(anyhow!("Can't determine current release."))? - .into(), - pending: get_pending_release(conn)?.map(|r| r.into()), + current: get_current_release(conn)?.ok_or(anyhow!("Can't get current release."))?, + pending: get_pending_release(conn)?, }) }) .await } - pub async fn skip_pending_release(&self) -> anyhow::Result> { + pub async fn skip_pending_release(&self) -> anyhow::Result> { log::debug!("Skipping latest pending Yagna release"); do_with_transaction(self.pool, move |conn| { - let mut pending_release = match get_pending_release(conn)? { - Some(r) => r, + let mut pending_rel = match get_pending_release(conn)? { + Some(rel) => rel, None => return Ok(None), }; - let num_updated = diesel::update(version_release.find(&pending_release.version)) + let num_updated = diesel::update(version_release.find(&pending_rel.version)) .set(release::seen.eq(true)) .execute(conn)?; - pending_release.seen = true; + pending_rel.seen = true; match num_updated { - 0 => anyhow::bail!("Release not skipped: {}", pending_release), - 1 => Ok(Some(pending_release)), - _ => anyhow::bail!("More than one release skipped: {}", pending_release), + 0 => anyhow::bail!("Release not skipped: {}", pending_rel), + 1 => Ok(Some(pending_rel)), + _ => anyhow::bail!("More than one release skipped: {}", pending_rel), } }) .await } } -fn get_current_release(conn: &ConnType) -> anyhow::Result> { +fn get_current_release(conn: &ConnType) -> anyhow::Result> { + get_release(conn, ya_compile_time_utils::semver_str()) +} + +fn get_release(conn: &ConnType, ver: &str) -> anyhow::Result> { Ok(version_release - .filter(release::version.eq(ya_compile_time_utils::semver_str())) + .filter(release::version.eq(&ver)) .first::(conn) - .optional()?) + .optional()? + .map(|db_rel| db_rel.into())) } -fn get_pending_release(conn: &ConnType) -> anyhow::Result> { +fn get_pending_release(conn: &ConnType) -> anyhow::Result> { let query = version_release .filter(release::seen.eq(false)) .order((release::release_ts.desc(), release::version.desc())) .into_boxed(); match query.first::(conn).optional()? { - Some(r) => { - if !self_update::version::bump_is_greater( - ya_compile_time_utils::semver_str(), - r.version.as_str(), - ) - .map_err(|e| log::warn!("Stored version parse error. {}", e)) - .unwrap_or(false) + Some(db_rel) => { + let running_ver = ya_compile_time_utils::semver_str(); + if !bump_is_greater(running_ver, &db_rel.version) + .map_err(|e| { + log::error!( + "Failed to compare if version {} > {}: {}", + running_ver, + db_rel.version, + e + ) + }) + .unwrap_or(false) { return Ok(None); } - Ok(Some(r)) + Ok(Some(db_rel.into())) } None => Ok(None), } diff --git a/core/version/src/db/model.rs b/core/version/src/db/model.rs index 4dd3f1f318..9213762476 100644 --- a/core/version/src/db/model.rs +++ b/core/version/src/db/model.rs @@ -1,6 +1,9 @@ -use crate::db::schema::version_release; use chrono::NaiveDateTime; +use self_update::update::Release; use serde::{Deserialize, Serialize}; +use std::convert::TryFrom; + +use crate::db::schema::version_release; #[derive(Clone, Debug, Identifiable, Insertable, Queryable, Serialize, Deserialize)] #[primary_key(version)] @@ -14,25 +17,47 @@ pub struct DBRelease { pub update_ts: Option, } -impl std::fmt::Display for DBRelease { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{} '{}' released @ {}", - self.version, self.name, self.release_ts - ) +impl DBRelease { + pub(crate) fn current() -> anyhow::Result { + Ok(DBRelease { + version: ya_compile_time_utils::semver_str().into(), + name: ya_compile_time_utils::build_number_str() + .map(|n| format!("{} build #{}", ya_compile_time_utils::git_rev(), n)) + .unwrap_or(ya_compile_time_utils::git_rev().into()), + seen: false, + release_ts: NaiveDateTime::parse_from_str( + ya_compile_time_utils::build_date(), + "%Y-%m-%d", + )?, + insertion_ts: None, + update_ts: None, + }) } } impl From for ya_core_model::version::Release { - fn from(r: DBRelease) -> Self { + fn from(db_rel: DBRelease) -> Self { Self { - version: r.version, - name: r.name, - seen: r.seen, - release_ts: r.release_ts, - insertion_ts: r.insertion_ts, - update_ts: r.update_ts, + version: db_rel.version, + name: db_rel.name, + seen: db_rel.seen, + release_ts: db_rel.release_ts, + insertion_ts: db_rel.insertion_ts, + update_ts: db_rel.update_ts, } } } + +impl TryFrom for DBRelease { + type Error = anyhow::Error; + fn try_from(rel: Release) -> Result { + Ok(Self { + version: rel.version.clone(), + name: rel.name.clone(), + seen: false, + release_ts: NaiveDateTime::parse_from_str(&rel.date, "%Y-%m-%dT%H:%M:%S%Z")?, + insertion_ts: None, + update_ts: None, + }) + } +} diff --git a/core/version/src/github.rs b/core/version/src/github.rs new file mode 100644 index 0000000000..3f9ae2cb3b --- /dev/null +++ b/core/version/src/github.rs @@ -0,0 +1,89 @@ +use anyhow::anyhow; +use metrics::counter; +use self_update::backends::github::UpdateBuilder; + +use ya_core_model::version::Release; +use ya_persistence::executor::DbExecutor; + +use crate::db::dao::ReleaseDAO; +use crate::db::model::DBRelease; +use crate::service::cli::ReleaseMessage; +use std::convert::TryFrom; + +const REPO_OWNER: &'static str = "golemfactory"; +const REPO_NAME: &'static str = "yagna"; + +pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result { + log::trace!("Checking latest Yagna release"); + let gh_rel = UpdateBuilder::new() + .repo_owner(REPO_OWNER) + .repo_name(REPO_NAME) + .bin_name("") // seems required by builder but unused + .current_version("") // similar as above + .build()? + .get_latest_release()?; + + let db_rel = DBRelease::try_from(gh_rel)?; + let rel = Release::from(db_rel.clone()); + log::trace!("Got latest Yagna release {}", rel); + if self_update::version::bump_is_greater(ya_compile_time_utils::semver_str(), &rel.version) + .map_err(|e| { + anyhow!( + "Github release version `{}` parse error: {}", + rel.version, + e + ) + })? + { + match db.as_dao::().save(db_rel).await { + Err(e) => log::error!("Storing new Yagna release {} to DB. {}", rel, e), + Ok(r) => { + counter!("version.new", 1); + log::warn!("{}", ReleaseMessage::Available(&r)); + } + } + }; + Ok(rel) +} + +pub(crate) async fn check_running_release(db: &DbExecutor) -> anyhow::Result { + let running_tag = ya_compile_time_utils::git_tag(); + log::debug!("Checking release for running tag: {}", running_tag); + + let release = match UpdateBuilder::new() + .repo_owner(REPO_OWNER) + .repo_name(REPO_NAME) + .bin_name("") // seems required by builder but unused + .current_version("") // similar as above + .build()? + .get_release_version(running_tag) + { + Ok(gh_rel) => { + let db_rel = DBRelease::try_from(gh_rel)?; + let rel = Release::from(db_rel.clone()); + log::trace!("Got currently running release: '{}'", rel); + db_rel + } + Err(e) => { + log::trace!( + "Failed to get release for running tag: '{}': {}", + running_tag, + e + ); + DBRelease::current()? + } + }; + + let rel = match db.as_dao::().save(release.clone()).await { + Err(e) => { + let r = release.into(); + log::error!("Storing running Yagna release {} to DB: {}", r, e); + r + } + Ok(r) => { + log::info!("Stored currently running Yagna release {} to DB", r); + r + } + }; + Ok(rel) +} diff --git a/core/version/src/lib.rs b/core/version/src/lib.rs index 7d6be474ed..8fa17aa8f9 100644 --- a/core/version/src/lib.rs +++ b/core/version/src/lib.rs @@ -4,6 +4,7 @@ extern crate diesel; extern crate diesel_migrations; mod db; +mod github; mod notifier; mod service; diff --git a/core/version/src/notifier.rs b/core/version/src/notifier.rs index 014574923b..df902d2a5e 100644 --- a/core/version/src/notifier.rs +++ b/core/version/src/notifier.rs @@ -1,66 +1,24 @@ -use anyhow::anyhow; -use metrics::counter; -use self_update::backends::github::UpdateBuilder; -use self_update::update::Release; -use self_update::version; use std::time::Duration; use ya_persistence::executor::DbExecutor; use crate::db::dao::ReleaseDAO; -use crate::db::model::DBRelease; +use crate::github; +use crate::github::check_running_release; use crate::service::cli::ReleaseMessage; -pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result<()> { - log::trace!("Checking latest Yagna release"); - let release = UpdateBuilder::new() - .repo_owner("golemfactory") - .repo_name("yagna") - .bin_name("") // seems required by builder but unused - .current_version("") // similar as above - .build()? - .get_latest_release()?; - log::trace!( - "Got latest Yagna release: '{}' (v{})", - release.name, - release.version - ); - if version::bump_is_greater( - ya_compile_time_utils::semver_str(), - release.version.as_str(), - ) - .map_err(|e| anyhow!("Github release `{:?}` parse error: {}", release, e))? - { - match db.as_dao::().new_release(&release).await { - Err(e) => log::error!("Storing new Yagna release `{:?}` to DB. {}", release, e), - Ok(r) => { - counter!("version.new", 1); - new_version_log(r); - } - } +pub async fn on_start(db: &DbExecutor) -> anyhow::Result<()> { + if let Err(e) = github::check_latest_release(&db).await { + log::error!("Failed to check for new Yagna release: {}", e); }; - Ok(()) -} -pub async fn on_start(db: &DbExecutor) -> anyhow::Result<()> { + check_running_release(&db).await?; + let worker_db = db.clone(); tokio::task::spawn_local(async move { crate::notifier::worker(worker_db).await }); let pinger_db = db.clone(); tokio::task::spawn_local(async move { crate::notifier::pinger(pinger_db).await }); - let release_dao = db.as_dao::(); - release_dao - .new_release(&Release { - name: ya_compile_time_utils::build_number_str() - .map(|n| format!("{} build #{}", ya_compile_time_utils::git_rev(), n)) - .unwrap_or(ya_compile_time_utils::git_rev().into()), - version: ya_compile_time_utils::semver_str().into(), - date: format!("{}T00:00:00Z", ya_compile_time_utils::build_date()), - body: None, - assets: vec![], - }) - .await - .map_err(|e| anyhow::anyhow!("Storing current Yagna version as release to DB: {}", e))?; Ok(()) } @@ -69,7 +27,7 @@ pub(crate) async fn worker(db: DbExecutor) { let mut interval = tokio::time::interval(Duration::from_secs(3600 * 24)); loop { interval.tick().await; - if let Err(e) = check_latest_release(&db).await { + if let Err(e) = github::check_latest_release(&db).await { log::error!("Failed to check for new Yagna release: {}", e); }; } @@ -84,7 +42,7 @@ pub(crate) async fn pinger(db: DbExecutor) -> ! { match release_dao.pending_release().await { Ok(Some(release)) => { if !release.seen { - new_version_log(release) + log::warn!("{}", ReleaseMessage::Available(&release.into())) } } Ok(None) => log::trace!("Your Yagna is up to date"), @@ -93,10 +51,6 @@ pub(crate) async fn pinger(db: DbExecutor) -> ! { } } -fn new_version_log(release: DBRelease) { - log::warn!("{}", ReleaseMessage::Available(&release.into())) -} - #[cfg(test)] mod tests { use chrono::NaiveDateTime; diff --git a/core/version/src/service/gsb.rs b/core/version/src/service/gsb.rs index 5518c49ee6..de23de0ecd 100644 --- a/core/version/src/service/gsb.rs +++ b/core/version/src/service/gsb.rs @@ -42,7 +42,7 @@ async fn get_version_gsb( msg: version::Get, ) -> RpcMessageResult { if msg.check { - crate::notifier::check_latest_release(&db) + crate::github::check_latest_release(&db) .await .map_err(|e| e.to_string())?; } From 0032bb27f256df4a63f8efd5b69465081ca51efd Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Mon, 25 Jan 2021 09:26:38 +0100 Subject: [PATCH 17/22] Revert "update CODEOWNERS"; moved to separate PR #963 This reverts commit 3fecec224c24d9a83db010c73a0f38e99b30ce41. --- .github/CODEOWNERS | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e8b0f0fdef..aa2ea445ab 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,34 +2,19 @@ .github/ @prekucki .github/workflows/rust.yml @golemfactory/ya-integration -# Activity core/activity/ @golemfactory/exe-unit core/model/src/activity.rs @golemfactory/exe-unit - -core/sgx/ @golemfactory/exe-unit -core/model/src/sgx.rs @golemfactory/exe-unit - exe-unit/ @golemfactory/exe-unit -# Market core/market/ @golemfactory/ya-market core/model/src/market.rs @golemfactory/ya-market -core/metrics/ @golemfactory/ya-market - -core/version/ @golemfactory/ya-market -core/model/src/version.rs @golemfactory/ya-market -utils/compile-tome-utils @golemfactory/ya-market - -# Net service-bus/ @golemfactory/ya-net core/net/ @golemfactory/ya-net core/model/src/net.rs @golemfactory/ya-net -# Payment core/payment-driver @golemfactory/ya-payment core/payment @golemfactory/ya-payment core/model/src/payment.rs @golemfactory/ya-payment -# Provider agent/provider @golemfactory/ya-provider From 98dea9f0e56f1925c62a62226ab853094ba259f7 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Mon, 25 Jan 2021 10:09:40 +0100 Subject: [PATCH 18/22] get current release on start, to not request GH unnecessary --- core/model/src/version.rs | 51 ++++++++++++++++++++++++++++++++- core/version/src/db/dao.rs | 4 +++ core/version/src/db/model.rs | 1 + core/version/src/github.rs | 5 ++++ core/version/src/service/cli.rs | 44 ++++++++++++++++++++++++---- 5 files changed, 98 insertions(+), 7 deletions(-) diff --git a/core/model/src/version.rs b/core/model/src/version.rs index fcc05345ab..846483fcb0 100644 --- a/core/model/src/version.rs +++ b/core/model/src/version.rs @@ -43,10 +43,14 @@ impl RpcMessage for Get { #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, thiserror::Error)] #[serde(rename_all = "camelCase")] -#[error("Version {version} '{name}' released @ {release_ts}")] +#[error("Version {version}{} '{name}' released {}", + gitrev.as_ref().map(|r| format!(" ({})",r)).unwrap_or("".into()), + release_ts.format("%Y-%m-%d") +)] pub struct Release { pub version: String, pub name: String, + pub gitrev: Option, pub seen: bool, pub release_ts: NaiveDateTime, pub insertion_ts: Option, @@ -59,3 +63,48 @@ pub struct VersionInfo { pub current: Release, pub pending: Option, } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_release_no_gitrev_to_string() { + let now = NaiveDateTime::parse_from_str("2015-10-13T15:43:00GMT+2", "%Y-%m-%dT%H:%M:%S%Z") + .unwrap(); + let r = Release { + version: "0.6.1".to_string(), + name: "some code name".to_string(), + gitrev: None, + seen: false, + release_ts: now, + insertion_ts: None, + update_ts: None, + }; + + assert_eq!( + r.to_string(), + "Version 0.6.1 'some code name' released 2015-10-13" + ); + } + + #[test] + fn test_release_gitrev_to_string() { + let now = NaiveDateTime::parse_from_str("2015-10-13T15:43:00GMT+2", "%Y-%m-%dT%H:%M:%S%Z") + .unwrap(); + let r = Release { + version: "0.6.1".to_string(), + name: "some code name".to_string(), + gitrev: Some("0032bb27".into()), + seen: false, + release_ts: now, + insertion_ts: None, + update_ts: None, + }; + + assert_eq!( + r.to_string(), + "Version 0.6.1 (0032bb27) 'some code name' released 2015-10-13" + ); + } +} diff --git a/core/version/src/db/dao.rs b/core/version/src/db/dao.rs index b66f392bf4..2f72f65ea4 100644 --- a/core/version/src/db/dao.rs +++ b/core/version/src/db/dao.rs @@ -36,6 +36,10 @@ impl<'c> ReleaseDAO<'c> { .await } + pub async fn current_release(&self) -> anyhow::Result> { + readonly_transaction(self.pool, move |conn| get_current_release(conn)).await + } + pub async fn pending_release(&self) -> anyhow::Result> { readonly_transaction(self.pool, move |conn| get_pending_release(conn)).await } diff --git a/core/version/src/db/model.rs b/core/version/src/db/model.rs index 9213762476..100b567dc0 100644 --- a/core/version/src/db/model.rs +++ b/core/version/src/db/model.rs @@ -40,6 +40,7 @@ impl From for ya_core_model::version::Release { Self { version: db_rel.version, name: db_rel.name, + gitrev: None, seen: db_rel.seen, release_ts: db_rel.release_ts, insertion_ts: db_rel.insertion_ts, diff --git a/core/version/src/github.rs b/core/version/src/github.rs index 3f9ae2cb3b..010b419854 100644 --- a/core/version/src/github.rs +++ b/core/version/src/github.rs @@ -48,6 +48,11 @@ pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result { pub(crate) async fn check_running_release(db: &DbExecutor) -> anyhow::Result { let running_tag = ya_compile_time_utils::git_tag(); + + if let Some(release) = db.as_dao::().current_release().await? { + return Ok(release); + } + log::debug!("Checking release for running tag: {}", running_tag); let release = match UpdateBuilder::new() diff --git a/core/version/src/service/cli.rs b/core/version/src/service/cli.rs index e0388b784e..1d3449db7f 100644 --- a/core/version/src/service/cli.rs +++ b/core/version/src/service/cli.rs @@ -5,17 +5,16 @@ use ya_core_model::version; use ya_service_api::{CliCtx, CommandOutput}; use ya_service_bus::{typed as bus, RpcEndpoint}; -const UPDATE_CURL: &'static str = "curl -sSf https://join.golem.network/as-provider | bash -"; -const SILENCE_CMD: &'static str = "yagna version skip"; +const UPDATE_CMD: &'static str = "curl -sSf https://join.golem.network/as-provider | bash -"; +const SKIP_CMD: &'static str = "yagna version skip"; #[derive(thiserror::Error, Debug, Clone)] pub(crate) enum ReleaseMessage<'a> { - #[error("New Yagna release is available: '{}' (v{}).\n\ - Update via\n\t`{}`\nor skip\n\t`{}`", .0.name, .0.version, UPDATE_CURL, SILENCE_CMD)] + #[error("New Yagna {0} is available!\nUpdate via\n\t`{UPDATE_CMD}`\nor skip\n\t`{SKIP_CMD}`")] Available(&'a version::Release), - #[error("Your Yagna is up to date -- '{}' (v{})", .0.name, .0.version)] + #[error("Your Yagna is up to date: {0}")] UpToDate(&'a version::Release), - #[error("Release skipped: '{}' (v{})", .0.name, .0.version)] + #[error("Release skipped: {0}")] Skipped(&'a version::Release), #[error("No pending release to skip")] NotSkipped, @@ -63,3 +62,36 @@ async fn show(msg: version::Get, ctx: &CliCtx) -> anyhow::Result None => ReleaseMessage::UpToDate(&version_info.current).to_string(), }) } + +#[cfg(test)] +mod test { + use super::*; + use chrono::NaiveDateTime; + use ya_core_model::version::Release; + + #[test] + fn test_release_available_to_string() { + let now = NaiveDateTime::parse_from_str("2015-10-13T15:43:00GMT+2", "%Y-%m-%dT%H:%M:%S%Z") + .unwrap(); + let r = Release { + version: "0.6.1".to_string(), + name: "some code name".to_string(), + gitrev: None, + seen: false, + release_ts: now, + insertion_ts: None, + update_ts: None, + }; + + assert_eq!( + ReleaseMessage::Available(&r).to_string(), + format!( + "New Yagna Version 0.6.1 'some code name' released 2015-10-13 is available!\n\ + Update via\n\ + \t`curl -sSf https://join.golem.network/as-provider | bash -`\n\ + or skip\n\ + \t`yagna version skip`" + ) + ); + } +} From 3ff8f0699d47d0a659d9d67a0d5bcc8d86cfcf86 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Mon, 25 Jan 2021 12:03:31 +0100 Subject: [PATCH 19/22] [ya-version] show help message for the main cli entry + hide skip option --- core/version/src/service/cli.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/version/src/service/cli.rs b/core/version/src/service/cli.rs index 1d3449db7f..a4d735d194 100644 --- a/core/version/src/service/cli.rs +++ b/core/version/src/service/cli.rs @@ -1,16 +1,15 @@ use metrics::counter; -use structopt::StructOpt; +use structopt::{clap::AppSettings, StructOpt}; use ya_core_model::version; use ya_service_api::{CliCtx, CommandOutput}; use ya_service_bus::{typed as bus, RpcEndpoint}; const UPDATE_CMD: &'static str = "curl -sSf https://join.golem.network/as-provider | bash -"; -const SKIP_CMD: &'static str = "yagna version skip"; #[derive(thiserror::Error, Debug, Clone)] pub(crate) enum ReleaseMessage<'a> { - #[error("New Yagna {0} is available!\nUpdate via\n\t`{UPDATE_CMD}`\nor skip\n\t`{SKIP_CMD}`")] + #[error("New Yagna {0} is available!\nUpdate via\n\t`{UPDATE_CMD}`")] Available(&'a version::Release), #[error("Your Yagna is up to date: {0}")] UpToDate(&'a version::Release), @@ -20,7 +19,7 @@ pub(crate) enum ReleaseMessage<'a> { NotSkipped, } -// Yagna version management. +/// Yagna version management. #[derive(StructOpt, Debug)] pub enum UpgradeCLI { /// Show current Yagna version and updates if available. @@ -28,6 +27,7 @@ pub enum UpgradeCLI { /// Checks if there is new Yagna version available and shows it. Check, /// Stop logging warnings about latest Yagna release availability. + #[structopt(setting = AppSettings::Hidden)] Skip, } @@ -88,9 +88,7 @@ mod test { format!( "New Yagna Version 0.6.1 'some code name' released 2015-10-13 is available!\n\ Update via\n\ - \t`curl -sSf https://join.golem.network/as-provider | bash -`\n\ - or skip\n\ - \t`yagna version skip`" + \t`curl -sSf https://join.golem.network/as-provider | bash -`" ) ); } From d1bdc1b5ca0d54d6a55f475bee537106dc8ff9e9 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Mon, 25 Jan 2021 12:29:48 +0100 Subject: [PATCH 20/22] [ya-version] first check running release and then latest --- core/version/src/github.rs | 3 +-- core/version/src/notifier.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/version/src/github.rs b/core/version/src/github.rs index 010b419854..227f46bd4b 100644 --- a/core/version/src/github.rs +++ b/core/version/src/github.rs @@ -47,12 +47,11 @@ pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result { } pub(crate) async fn check_running_release(db: &DbExecutor) -> anyhow::Result { - let running_tag = ya_compile_time_utils::git_tag(); - if let Some(release) = db.as_dao::().current_release().await? { return Ok(release); } + let running_tag = ya_compile_time_utils::git_tag(); log::debug!("Checking release for running tag: {}", running_tag); let release = match UpdateBuilder::new() diff --git a/core/version/src/notifier.rs b/core/version/src/notifier.rs index df902d2a5e..6e1df5a5cd 100644 --- a/core/version/src/notifier.rs +++ b/core/version/src/notifier.rs @@ -8,12 +8,12 @@ use crate::github::check_running_release; use crate::service::cli::ReleaseMessage; pub async fn on_start(db: &DbExecutor) -> anyhow::Result<()> { + check_running_release(&db).await?; + if let Err(e) = github::check_latest_release(&db).await { log::error!("Failed to check for new Yagna release: {}", e); }; - check_running_release(&db).await?; - let worker_db = db.clone(); tokio::task::spawn_local(async move { crate::notifier::worker(worker_db).await }); let pinger_db = db.clone(); From cca57ae554ff3e08eb1e7f1b2c8828de65e66a72 Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Mon, 25 Jan 2021 14:01:42 +0100 Subject: [PATCH 21/22] [ya-version] apply review comments by @jiivan: fixed release_ts ordering --- core/version/src/db/dao.rs | 2 +- core/version/src/db/model.rs | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/core/version/src/db/dao.rs b/core/version/src/db/dao.rs index 2f72f65ea4..6580991aad 100644 --- a/core/version/src/db/dao.rs +++ b/core/version/src/db/dao.rs @@ -91,7 +91,7 @@ fn get_release(conn: &ConnType, ver: &str) -> anyhow::Result> { fn get_pending_release(conn: &ConnType) -> anyhow::Result> { let query = version_release .filter(release::seen.eq(false)) - .order((release::release_ts.desc(), release::version.desc())) + .order(release::release_ts.desc()) .into_boxed(); match query.first::(conn).optional()? { diff --git a/core/version/src/db/model.rs b/core/version/src/db/model.rs index 100b567dc0..6429c796e0 100644 --- a/core/version/src/db/model.rs +++ b/core/version/src/db/model.rs @@ -1,4 +1,4 @@ -use chrono::NaiveDateTime; +use chrono::{NaiveDateTime, Utc}; use self_update::update::Release; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; @@ -21,14 +21,9 @@ impl DBRelease { pub(crate) fn current() -> anyhow::Result { Ok(DBRelease { version: ya_compile_time_utils::semver_str().into(), - name: ya_compile_time_utils::build_number_str() - .map(|n| format!("{} build #{}", ya_compile_time_utils::git_rev(), n)) - .unwrap_or(ya_compile_time_utils::git_rev().into()), - seen: false, - release_ts: NaiveDateTime::parse_from_str( - ya_compile_time_utils::build_date(), - "%Y-%m-%d", - )?, + name: ya_compile_time_utils::version_describe!().into(), + seen: true, + release_ts: Utc::now().naive_utc(), insertion_ts: None, update_ts: None, }) From 104b707ecec38bfbb35da2fe3ab638359111257d Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Mon, 25 Jan 2021 15:06:53 +0100 Subject: [PATCH 22/22] [ya-version] fixed tag stripping + aux order by insertion_ts --- core/version/src/db/dao.rs | 5 ++-- core/version/src/db/model.rs | 27 ++++++++++++++------ core/version/src/github.rs | 38 +++++++++++++++-------------- utils/compile-time-utils/src/lib.rs | 6 ++++- 4 files changed, 48 insertions(+), 28 deletions(-) diff --git a/core/version/src/db/dao.rs b/core/version/src/db/dao.rs index 6580991aad..37d5d9abb6 100644 --- a/core/version/src/db/dao.rs +++ b/core/version/src/db/dao.rs @@ -21,7 +21,7 @@ impl<'a> AsDao<'a> for ReleaseDAO<'a> { } impl<'c> ReleaseDAO<'c> { - pub async fn save(&self, db_rel: DBRelease) -> anyhow::Result { + pub async fn save_new(&self, db_rel: DBRelease) -> anyhow::Result { do_with_transaction(self.pool, move |conn| { match get_release(conn, &db_rel.version)? { Some(rel) => Ok(rel), @@ -91,7 +91,8 @@ fn get_release(conn: &ConnType, ver: &str) -> anyhow::Result> { fn get_pending_release(conn: &ConnType) -> anyhow::Result> { let query = version_release .filter(release::seen.eq(false)) - .order(release::release_ts.desc()) + // insertion_ts is to distinguish among fake-entries of `DBRelease::current` + .order((release::release_ts.desc(), release::insertion_ts.desc())) .into_boxed(); match query.first::(conn).optional()? { diff --git a/core/version/src/db/model.rs b/core/version/src/db/model.rs index 6429c796e0..66491c2809 100644 --- a/core/version/src/db/model.rs +++ b/core/version/src/db/model.rs @@ -1,9 +1,11 @@ -use chrono::{NaiveDateTime, Utc}; -use self_update::update::Release; +use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; use crate::db::schema::version_release; +use ya_compile_time_utils::tag2semver; + +pub(crate) const DEFAULT_RELEASE_TS: &'static str = "2015-10-13T15:43:00GMT+2"; #[derive(Clone, Debug, Identifiable, Insertable, Queryable, Serialize, Deserialize)] #[primary_key(version)] @@ -21,9 +23,16 @@ impl DBRelease { pub(crate) fn current() -> anyhow::Result { Ok(DBRelease { version: ya_compile_time_utils::semver_str().into(), - name: ya_compile_time_utils::version_describe!().into(), + name: format!( + "{} {}{}", + ya_compile_time_utils::semver_str(), + ya_compile_time_utils::build_date(), + ya_compile_time_utils::build_number_str() + .map(|bn| format!(" build #{}", bn)) + .unwrap_or("".into()) + ), seen: true, - release_ts: Utc::now().naive_utc(), + release_ts: parse_release_ts(DEFAULT_RELEASE_TS)?, insertion_ts: None, update_ts: None, }) @@ -46,14 +55,18 @@ impl From for ya_core_model::version::Release { impl TryFrom for DBRelease { type Error = anyhow::Error; - fn try_from(rel: Release) -> Result { + fn try_from(rel: self_update::update::Release) -> Result { Ok(Self { - version: rel.version.clone(), + version: tag2semver(&rel.version).into(), name: rel.name.clone(), seen: false, - release_ts: NaiveDateTime::parse_from_str(&rel.date, "%Y-%m-%dT%H:%M:%S%Z")?, + release_ts: parse_release_ts(&rel.date)?, insertion_ts: None, update_ts: None, }) } } + +fn parse_release_ts(ts: &str) -> anyhow::Result { + Ok(NaiveDateTime::parse_from_str(&ts, "%Y-%m-%dT%H:%M:%S%Z")?) +} diff --git a/core/version/src/github.rs b/core/version/src/github.rs index 227f46bd4b..c7df59de24 100644 --- a/core/version/src/github.rs +++ b/core/version/src/github.rs @@ -14,7 +14,7 @@ const REPO_OWNER: &'static str = "golemfactory"; const REPO_NAME: &'static str = "yagna"; pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result { - log::trace!("Checking latest Yagna release"); + log::debug!("Checking latest Yagna release"); let gh_rel = UpdateBuilder::new() .repo_owner(REPO_OWNER) .repo_name(REPO_NAME) @@ -23,9 +23,18 @@ pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result { .build()? .get_latest_release()?; + log::trace!("Got latest Yagna release {:?}", gh_rel); + let db_rel = DBRelease::try_from(gh_rel)?; - let rel = Release::from(db_rel.clone()); - log::trace!("Got latest Yagna release {}", rel); + let rel = match db.as_dao::().save_new(db_rel.clone()).await { + Err(e) => { + let r = db_rel.into(); + log::error!("Storing new Yagna release {} to DB. {}", r, e); + r + } + Ok(r) => r, + }; + if self_update::version::bump_is_greater(ya_compile_time_utils::semver_str(), &rel.version) .map_err(|e| { anyhow!( @@ -35,13 +44,8 @@ pub async fn check_latest_release(db: &DbExecutor) -> anyhow::Result { ) })? { - match db.as_dao::().save(db_rel).await { - Err(e) => log::error!("Storing new Yagna release {} to DB. {}", rel, e), - Ok(r) => { - counter!("version.new", 1); - log::warn!("{}", ReleaseMessage::Available(&r)); - } - } + counter!("version.new", 1); + log::warn!("{}", ReleaseMessage::Available(&rel)); }; Ok(rel) } @@ -54,7 +58,7 @@ pub(crate) async fn check_running_release(db: &DbExecutor) -> anyhow::Result anyhow::Result { - let db_rel = DBRelease::try_from(gh_rel)?; - let rel = Release::from(db_rel.clone()); - log::trace!("Got currently running release: '{}'", rel); - db_rel + log::trace!("Got currently running release: {:?}", gh_rel); + DBRelease::try_from(gh_rel)? } Err(e) => { log::trace!( - "Failed to get release for running tag: '{}': {}", + "Failed to get release for running tag: '{}': {}. Using current", running_tag, e ); @@ -78,9 +80,9 @@ pub(crate) async fn check_running_release(db: &DbExecutor) -> anyhow::Result().save(release.clone()).await { + let rel = match db.as_dao::().save_new(db_rel.clone()).await { Err(e) => { - let r = release.into(); + let r = db_rel.into(); log::error!("Storing running Yagna release {} to DB: {}", r, e); r } diff --git a/utils/compile-time-utils/src/lib.rs b/utils/compile-time-utils/src/lib.rs index 93196455f1..ac1f6de247 100644 --- a/utils/compile-time-utils/src/lib.rs +++ b/utils/compile-time-utils/src/lib.rs @@ -31,7 +31,11 @@ pub fn build_number() -> Option { /// convert tag to a semantic version pub fn semver_str() -> &'static str { - let mut version = git_tag(); + tag2semver(git_tag()) +} + +pub fn tag2semver(tag: &str) -> &str { + let mut version = tag; for prefix in ["pre-rel-", "v"].iter() { if version.starts_with(prefix) { version = &version[prefix.len()..];