feat: Add verification of opnsense package installation, fix opnsense-config tests, add log file to tui
This commit is contained in:
@@ -8,7 +8,7 @@ use crate::{
|
||||
tftp::TftpConfig,
|
||||
},
|
||||
};
|
||||
use log::{info, trace};
|
||||
use log::{debug, info, trace, warn};
|
||||
use opnsense_config_xml::OPNsense;
|
||||
use russh::client;
|
||||
|
||||
@@ -57,6 +57,30 @@ impl Config {
|
||||
self.shell.upload_folder(source, destination).await
|
||||
}
|
||||
|
||||
/// Checks in config file if system.firmware.plugins csv field contains the specified package
|
||||
/// name.
|
||||
///
|
||||
/// Given this
|
||||
/// ```xml
|
||||
/// <opnsense>
|
||||
/// <system>
|
||||
/// <firmware>
|
||||
/// <plugins>os-haproxy,os-iperf,os-cpu-microcode-intel</plugins>
|
||||
/// </firmware>
|
||||
/// </system>
|
||||
/// </opnsense>
|
||||
/// ```
|
||||
///
|
||||
/// is_package_installed("os-cpu"); // false
|
||||
/// is_package_installed("os-haproxy"); // true
|
||||
/// is_package_installed("os-cpu-microcode-intel"); // true
|
||||
pub fn is_package_installed(&self, package_name: &str) -> bool {
|
||||
match &self.opnsense.system.firmware.plugins.content {
|
||||
Some(plugins) => is_package_in_csv(plugins, package_name),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
// Here maybe we should take ownership of `mut self` instead of `&mut self`
|
||||
// I don't think there can be faulty pointers to previous versions of the config but I have a
|
||||
// hard time wrapping my head around it right now :
|
||||
@@ -70,11 +94,35 @@ impl Config {
|
||||
// read-only reference across the &mut call
|
||||
pub async fn install_package(&mut self, package_name: &str) -> Result<(), Error> {
|
||||
info!("Installing opnsense package {package_name}");
|
||||
self.check_pkg_opnsense_org_connection().await?;
|
||||
|
||||
let output = self.shell
|
||||
.exec(&format!("/bin/sh -c \"export LOCKFILE=/dev/stdout && /usr/local/opnsense/scripts/firmware/install.sh {package_name}\""))
|
||||
.await?;
|
||||
info!("Installation output {output}");
|
||||
|
||||
self.reload_config().await?;
|
||||
let is_installed = self.is_package_installed(package_name);
|
||||
debug!("Verifying package installed successfully {is_installed}");
|
||||
|
||||
if !is_installed {
|
||||
info!("Installation successful for {package_name}");
|
||||
Ok(())
|
||||
} else {
|
||||
let msg = format!("Package installation failed for {package_name}, see above logs");
|
||||
warn!("{}", msg);
|
||||
Err(Error::Unexpected(msg))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn check_pkg_opnsense_org_connection(&mut self) -> Result<(), Error> {
|
||||
let pkg_url = "https://pkg.opnsense.org";
|
||||
info!("Verifying connection to {pkg_url}");
|
||||
let output = self
|
||||
.shell
|
||||
.exec(&format!("/bin/sh -c \"curl -v {pkg_url}\""))
|
||||
.await?;
|
||||
info!("{}", output);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -150,8 +198,8 @@ mod tests {
|
||||
async fn test_load_config_from_local_file() {
|
||||
for path in vec![
|
||||
"src/tests/data/config-vm-test.xml",
|
||||
"src/tests/data/config-full-1.xml",
|
||||
"src/tests/data/config-structure.xml",
|
||||
"src/tests/data/config-full-1.xml",
|
||||
] {
|
||||
let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
test_file_path.push(path);
|
||||
@@ -217,3 +265,53 @@ mod tests {
|
||||
assert_eq!(expected_config_file_str, serialized);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a given package name exists in a comma-separated list of packages.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `csv_string` - A string containing comma-separated package names.
|
||||
/// * `package_name` - The package name to search for.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `true` if the package name is found in the CSV string, `false` otherwise.
|
||||
fn is_package_in_csv(csv_string: &str, package_name: &str) -> bool {
|
||||
package_name.len() > 0 && csv_string.split(',').any(|pkg| pkg.trim() == package_name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests_2 {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_is_package_in_csv() {
|
||||
let csv_string = "os-haproxy,os-iperf,os-cpu-microcode-intel";
|
||||
|
||||
assert!(is_package_in_csv(csv_string, "os-haproxy"));
|
||||
assert!(is_package_in_csv(csv_string, "os-iperf"));
|
||||
assert!(is_package_in_csv(csv_string, "os-cpu-microcode-intel"));
|
||||
|
||||
assert!(!is_package_in_csv(csv_string, "os-cpu"));
|
||||
assert!(!is_package_in_csv(csv_string, "non-existent-package"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_package_in_csv_empty() {
|
||||
let csv_string = "";
|
||||
|
||||
assert!(!is_package_in_csv(csv_string, "os-haproxy"));
|
||||
assert!(!is_package_in_csv(csv_string, ""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_package_in_csv_whitespace() {
|
||||
let csv_string = " os-haproxy , os-iperf , os-cpu-microcode-intel ";
|
||||
|
||||
assert!(is_package_in_csv(csv_string, "os-haproxy"));
|
||||
assert!(is_package_in_csv(csv_string, "os-iperf"));
|
||||
assert!(is_package_in_csv(csv_string, "os-cpu-microcode-intel"));
|
||||
|
||||
assert!(!is_package_in_csv(csv_string, " os-haproxy "));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user