Skip to content

Commit

Permalink
feat: precondition checks as a feature (#322)
Browse files Browse the repository at this point in the history
* feat: preconditions check of disk space

* Merge branch 'main' into preconditions

* fix: use the right term

* fix: merge
  • Loading branch information
Devdutt Shenoi authored Feb 20, 2024
1 parent 4d0cf3b commit 5018130
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 1 deletion.
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

0 comments on commit 5018130

Please sign in to comment.