diff --git a/Cargo.lock b/Cargo.lock index e0029474..b5d78df2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1859,7 +1859,7 @@ dependencies = [ [[package]] name = "omnect-ui" -version = "0.13.3" +version = "0.14.0" dependencies = [ "actix-files", "actix-http", @@ -1883,6 +1883,7 @@ dependencies = [ "reqwest", "rustls", "rustls-pemfile", + "semver", "serde", "serde_json", "serde_repr", diff --git a/Cargo.toml b/Cargo.toml index 009ea90e..13919987 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" name = "omnect-ui" readme = "README.md" repository = "git@github.com:omnect/omnect-ui.git" -version = "0.13.3" +version = "0.14.0" build = "src/build.rs" [dependencies] @@ -50,6 +50,7 @@ rustls = { version = "0.23", default-features = false, features = [ rustls-pemfile = { version = "2.2", default-features = false, features = [ "std", ] } +semver = { version = "1.0", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = { version = "1.0", default-features = false, features = [ "raw_value", diff --git a/src/api.rs b/src/api.rs index 34b513f4..06c070e4 100644 --- a/src/api.rs +++ b/src/api.rs @@ -81,6 +81,13 @@ pub enum FactoryResetMode { Mode4 = 4, } +#[derive(Clone, Debug, Serialize)] +pub struct VersionCheckResult { + pub req_ods_version: String, + pub cur_ods_version: String, + pub version_mismatch: bool, +} + #[derive(Clone)] pub struct Api { pub ods_socket_path: String, @@ -89,6 +96,7 @@ pub struct Api { pub index_html: PathBuf, pub keycloak_public_key_url: String, pub tenant: String, + pub version_check_result: VersionCheckResult, } impl Api { @@ -111,9 +119,14 @@ impl Api { Ok(NamedFile::open(config_path!("app_config.js"))?) } - pub async fn healthcheck() -> impl Responder { + pub async fn healthcheck(config: web::Data) -> impl Responder { debug!("healthcheck() called"); - HttpResponse::Ok().finish() + + if config.version_check_result.version_mismatch { + HttpResponse::ServiceUnavailable().json(&config.version_check_result) + } else { + HttpResponse::Ok().json(&config.version_check_result) + } } pub async fn factory_reset( diff --git a/src/common.rs b/src/common.rs index d6f6654b..5cd9c6c4 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,9 +1,12 @@ +use crate::api::VersionCheckResult; +use crate::REQ_ODS_VERSION; use actix_web::body::MessageBody; use anyhow::{anyhow, bail, Context, Result}; use argon2::{Argon2, PasswordHash, PasswordVerifier}; use base64::{prelude::BASE64_STANDARD, Engine}; use jwt_simple::prelude::{RS256PublicKey, RSAPublicKeyLike}; use reqwest::blocking::get; +use semver::{Version, VersionReq}; use serde::{Deserialize, Serialize}; use std::{fs, io::Write, path::Path}; @@ -30,6 +33,7 @@ pub struct StatusResponse { #[derive(Deserialize)] pub struct SystemInfo { pub fleet_id: Option, + omnect_device_service_version: String, } #[derive(Deserialize)] @@ -199,3 +203,25 @@ pub fn create_frontend_config_file(keycloak_url: &str) -> Result<()> { Ok(()) } + +pub async fn check_and_store_ods_version(ods_socket_path: &str) -> Result { + let status_response = get_status(ods_socket_path) + .await + .context("failed to get status from socket client")?; + + let version_req = VersionReq::parse(REQ_ODS_VERSION) + .map_err(|e| anyhow!("failed to parse REQ_ODS_VERSION: {e}"))?; + let current_version = + Version::parse(&status_response.system_info.omnect_device_service_version) + .map_err(|e| anyhow!("failed to parse omnect_device_service_version: {e}"))?; + let version_mismatch = !version_req.matches(¤t_version); + + Ok(VersionCheckResult { + req_ods_version: REQ_ODS_VERSION.to_string(), + cur_ods_version: status_response + .system_info + .omnect_device_service_version + .clone(), + version_mismatch, + }) +} diff --git a/src/main.rs b/src/main.rs index bb27a0e1..f8b1006d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,8 @@ use tokio::{ }; use uuid::Uuid; +pub const REQ_ODS_VERSION: &str = ">=0.39.0"; + const UPLOAD_LIMIT_BYTES: usize = 250 * 1024 * 1024; const MEMORY_LIMIT_BYTES: usize = 10 * 1024 * 1024; @@ -120,11 +122,22 @@ async fn main() { .parse::() .expect("UI_PORT format"); + let ods_socket_path = std::env::var("SOCKET_PATH").expect("env SOCKET_PATH is missing"); + fs::exists(&ods_socket_path).unwrap_or_else(|_| { + panic!( + "omnect device service socket file {} does not exist", + &ods_socket_path + ) + }); + let version_check_result = common::check_and_store_ods_version(&ods_socket_path) + .await + .expect("failed to check and store ods version"); + CryptoProvider::install_default(default_provider()).expect("failed to install crypto provider"); certificate::create_module_certificate(&cert_path!(), &key_path!()) .await - .expect("Failed to create module certificate"); + .expect("failed to create module certificate"); let mut tls_certs = std::io::BufReader::new(std::fs::File::open(cert_path!()).expect("read certs_file")); @@ -166,19 +179,11 @@ async fn main() { ¢rifugo_http_server_port!(), ); - let ods_socket_path = std::env::var("SOCKET_PATH").expect("env SOCKET_PATH is missing"); let index_html = std::fs::canonicalize("static/index.html").expect("static/index.html not found"); let tenant = std::env::var("TENANT").expect("env TENANT is missing"); - fs::exists(&ods_socket_path).unwrap_or_else(|_| { - panic!( - "omnect device service socket file {} does not exist", - &ods_socket_path - ) - }); - fs::exists(&update_os_path!()) .unwrap_or_else(|_| panic!("path {} for os update does not exist", &update_os_path!())); @@ -194,6 +199,7 @@ async fn main() { index_html, keycloak_public_key_url: keycloak_url!(), tenant, + version_check_result, }; let session_key = Key::generate();