Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: precondition checks as a feature #322

Merged
merged 7 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions uplink/src/collector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod installer;
pub mod journalctl;
#[cfg(target_os = "android")]
pub mod logcat;
pub mod preconditions;
pub mod process;
pub mod script_runner;
pub mod simulator;
Expand Down
84 changes: 84 additions & 0 deletions uplink/src/collector/preconditions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::{fs::metadata, os::unix::fs::MetadataExt, sync::Arc};

use flume::Receiver;
use human_bytes::human_bytes;
use log::debug;
use serde::Deserialize;

use crate::{base::bridge::BridgeTx, Action, ActionResponse, Config};

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("File io Error: {0}")]
Io(#[from] std::io::Error),
#[error("Disk space is insufficient: {0}")]
InsufficientDisk(String),
}

#[derive(Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct Preconditions {
#[serde(alias = "content-length")]
content_length: usize,
#[serde(alias = "uncompressed-size")]
uncompressed_length: usize,
}

pub struct PreconditionChecker {
config: Arc<Config>,
actions_rx: Receiver<Action>,
bridge_tx: BridgeTx,
}

impl PreconditionChecker {
pub fn new(config: Arc<Config>, actions_rx: Receiver<Action>, bridge_tx: BridgeTx) -> Self {
Self { config, actions_rx, bridge_tx }
}

#[tokio::main]
pub async fn start(self) {
while let Ok(action) = self.actions_rx.recv() {
let preconditions: Preconditions = match serde_json::from_str(&action.payload) {
Ok(c) => c,
Err(e) => {
let response = ActionResponse::failure(&action.action_id, e.to_string());
self.bridge_tx.send_action_response(response).await;
continue;
}
};

if let Err(e) = self.check_disk_size(preconditions) {
let response = ActionResponse::failure(&action.action_id, e.to_string());
self.bridge_tx.send_action_response(response).await;
continue;
}

let response = ActionResponse::progress(&action.action_id, "Checked OK", 100);
self.bridge_tx.send_action_response(response).await;
}
}

// Fails if there isn't enough space to download and/or install update
// NOTE: both download and installation could happen in the same partition
fn check_disk_size(&self, preconditions: Preconditions) -> Result<(), Error> {
let disk_free_space =
fs2::free_space(&self.config.precondition_checks.as_ref().unwrap().path)? as usize;
let mut required_free_space = preconditions.uncompressed_length;

// Check if download and installation paths are on the same partition, if yes add download file size to required
if metadata(&self.config.downloader.path)?.dev()
== metadata(&self.config.precondition_checks.as_ref().unwrap().path)?.dev()
{
required_free_space += preconditions.content_length;
}

let req_size = human_bytes(required_free_space as f64);
let free_size = human_bytes(disk_free_space as f64);
debug!("The installation requires {req_size}; Disk free space is {free_size}");

if required_free_space > disk_free_space {
return Err(Error::InsufficientDisk(free_size));
}

Ok(())
}
}
7 changes: 7 additions & 0 deletions uplink/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ impl Default for DeviceShadowConfig {
}
}

#[derive(Debug, Clone, Deserialize)]
pub struct PreconditionCheckerConfig {
pub path: PathBuf,
pub actions: Vec<ActionRoute>,
}

#[derive(Debug, Clone, Deserialize, Default)]
pub struct Config {
pub project_id: String,
Expand Down Expand Up @@ -274,4 +280,5 @@ pub struct Config {
pub logging: Option<JournalCtlConfig>,
#[cfg(target_os = "android")]
pub logging: Option<LogcatConfig>,
pub precondition_checks: Option<PreconditionCheckerConfig>,
}
9 changes: 8 additions & 1 deletion uplink/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ use collector::installer::OTAInstaller;
use collector::journalctl::JournalCtl;
#[cfg(target_os = "android")]
use collector::logcat::Logcat;
use collector::preconditions::PreconditionChecker;
use collector::process::ProcessHandler;
use collector::script_runner::ScriptRunner;
use collector::systemstats::StatCollector;
Expand Down Expand Up @@ -294,14 +295,20 @@ impl Uplink {

if !self.config.script_runner.is_empty() {
let actions_rx = bridge.register_action_routes(&self.config.script_runner)?;
let script_runner = ScriptRunner::new(actions_rx, bridge_tx);
let script_runner = ScriptRunner::new(actions_rx, bridge_tx.clone());
spawn_named_thread("Script Runner", || {
if let Err(e) = script_runner.start() {
error!("Script runner stopped!! Error = {:?}", e);
}
});
}

if let Some(checker_config) = &self.config.precondition_checks {
let actions_rx = bridge.register_action_routes(&checker_config.actions)?;
let checker = PreconditionChecker::new(self.config.clone(), actions_rx, bridge_tx);
spawn_named_thread("Logger", || checker.start());
}

Ok(())
}

Expand Down
Loading