Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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",
Expand Down
13 changes: 11 additions & 2 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::common::{config_path, validate_password, validate_token_and_claims};
use crate::common::{config_path, validate_password, validate_token_and_claims, VERSION_CHECK};
use crate::middleware::TOKEN_EXPIRE_HOURS;
use crate::socket_client::*;
use actix_files::NamedFile;
Expand Down Expand Up @@ -113,7 +113,16 @@ impl Api {

pub async fn healthcheck() -> impl Responder {
debug!("healthcheck() called");
HttpResponse::Ok().finish()

if let Some(result) = VERSION_CHECK.get() {
if result.version_mismatch {
HttpResponse::ServiceUnavailable().json(result)
} else {
HttpResponse::Ok().json(result)
}
} else {
HttpResponse::Ok().body("No version check performed yet")
}
Comment on lines 117 to 129
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

healthcheck must be called in another PR during startup and handle the result

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be done all in one match

        match VERSION_CHECK.get() {
            Some(result) if result.version_mismatch => {
                HttpResponse::ServiceUnavailable().json(result)
            }
            Some(result) => HttpResponse::Ok().json(result),

            _ => HttpResponse::Ok().body("No version check performed yet"),
        }

}

pub async fn factory_reset(
Expand Down
38 changes: 37 additions & 1 deletion src/common.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
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};
use std::{fs, io::Write, path::Path, sync::OnceLock};

pub static VERSION_CHECK: OnceLock<VersionCheckResult> = OnceLock::new();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO use a lazy lock here: this would drop the case in the healthcheck route above for the case where VERSION_CHECK.get() returns None.

Copy link
Contributor

@JanZachmann JanZachmann May 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JoergZeidler @empwilli @ronny-standtke
I had a closer look and we shouldn't use any (std::sync::***Lock) at all. Therefore we should add a new function to Api and call get_status once and parse and save all data we need later on in Api calls. Thus we can get rid of further calls of get_status (e.g. for fleet_id) and we need no global static std::sync::***Lock

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed OnceLock handling. Imho refactoring with new get_status() handline should be done after refinement with @ronny-standtke and @JanZachmann


#[derive(Clone, Debug, Serialize)]
pub struct VersionCheckResult {
pub req_ods_version: String,
pub cur_ods_version: String,
pub version_mismatch: bool,
}

#[derive(Deserialize)]
pub struct RealmInfo {
Expand All @@ -30,6 +41,7 @@ pub struct StatusResponse {
#[derive(Deserialize)]
pub struct SystemInfo {
pub fleet_id: Option<String>,
omnect_device_service_version: String,
}

#[derive(Deserialize)]
Expand Down Expand Up @@ -199,3 +211,27 @@ 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(&current_version);

VERSION_CHECK.get_or_init(|| VersionCheckResult {
req_ods_version: REQ_ODS_VERSION.to_string(),
cur_ods_version: status_response
.system_info
.omnect_device_service_version
.clone(),
version_mismatch,
});

Ok(())
}
23 changes: 14 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -120,11 +122,22 @@ async fn main() {
.parse::<u64>()
.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
)
});
Comment on lines +126 to +131
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO we don't need this check. There might be cases where the unix domain socket exists and is deleted later on. We should instead ensure, that the open call to the unix domain socket does not attempt to create a file if the file does not exist, and handle the error on opening.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I just saw, that this is pre-existing code. Still: just drop the check :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@empwilli what do you mean by "...attempt to create a file..."?

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"));
Expand Down Expand Up @@ -166,19 +179,11 @@ async fn main() {
&centrifugo_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!()));

Expand Down
Loading