diff --git a/Cargo.lock b/Cargo.lock index f7905d18..68c16cf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -378,6 +378,12 @@ dependencies = [ "syn", ] +[[package]] +name = "lazy_errors" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38563710a0241a6df6b8bdd2319094df44181e93133e78c669c57f3052845baf" + [[package]] name = "leb128fmt" version = "0.1.0" @@ -693,6 +699,7 @@ dependencies = [ "glob", "im", "itertools", + "lazy_errors", "log", "nix", "regex", diff --git a/Cargo.toml b/Cargo.toml index cdd10462..9a43cd00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ env_logger = "0.11.0" glob = "0.3.1" im = { version = "15.1.0", features = ["serde"] } itertools = "0.14.0" +lazy_errors = "0.10.1" log = "0.4.17" nix = { version = "0.31.0", features = ["hostname", "user"] } regex = "1.11.1" diff --git a/crates/system-manager-engine/Cargo.toml b/crates/system-manager-engine/Cargo.toml index e89928ef..b2ad7a54 100644 --- a/crates/system-manager-engine/Cargo.toml +++ b/crates/system-manager-engine/Cargo.toml @@ -22,6 +22,7 @@ env_logger.workspace = true glob.workspace = true im.workspace = true itertools.workspace = true +lazy_errors.workspace = true log.workspace = true nix.workspace = true regex.workspace = true diff --git a/crates/system-manager-engine/src/activate.rs b/crates/system-manager-engine/src/activate.rs index fce3d8da..e10fa58a 100644 --- a/crates/system-manager-engine/src/activate.rs +++ b/crates/system-manager-engine/src/activate.rs @@ -4,18 +4,37 @@ mod tmp_files; pub(crate) mod users; use anyhow::{anyhow, Result}; +use lazy_errors::prelude::*; use serde::{Deserialize, Serialize}; use serde_json::error::Category; use std::collections::HashSet; use std::fs::DirBuilder; use std::io::Seek; use std::path::{Path, PathBuf}; -use std::{fs, io, process}; +use std::{fmt, fs, io, process}; use thiserror::Error; use crate::activate::etc_files::etc_tree::StateV0; use crate::{StorePath, STATE_FILE_NAME, SYSTEM_MANAGER_STATE_DIR}; +pub(crate) fn collect_activation_result_err( + res: ActivationResult, + err_stash: &mut ErrorStash, +) -> ActivationResult +where + M: fmt::Display, + F: FnOnce() -> M, +{ + res.map_err(|e| { + let ActivationError::WithPartialResult { + result: _, + ref source, + } = e; + err_stash.push(source.to_string()); + e + }) +} + #[derive(Error, Debug)] pub enum ActivationError { #[error("")] @@ -141,63 +160,75 @@ pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> { let state_file = &get_state_file()?; let old_state = StateV1::from_file(state_file)?; + let mut errs = ErrorStash::new(|| "Activation completed with errors"); log::info!("Activating etc files..."); - match etc_files::activate(store_path, old_state.file_tree, ephemeral) { + let etc_result = collect_activation_result_err( + etc_files::activate(store_path, old_state.file_tree, ephemeral), + &mut errs, + ); + if let Err(ref e) = etc_result { + log::error!("Error during activation: {e:?}"); + } + + // Only run daemon reload, userborn, tmpfiles, and services when etc files + // were fully applied. Partial etc results mean services may reference + // missing config files. + let (etc_tree, services) = match etc_result { Ok(etc_tree) => { log::info!("Restarting sysinit-reactivation.target..."); - services::restart_sysinit_reactivation_target()?; + let sysinit_result = services::restart_sysinit_reactivation_target(); + if let Err(ref e) = sysinit_result { + log::error!("Error restarting sysinit-reactivation.target: {e}"); + } else { + log::info!("Successfully restarted sysinit-reactivation.target"); + } + sysinit_result.or_stash(&mut errs); // Restart userborn before tmpfiles so users exist when tmpfiles runs - if let Err(e) = services::restart_userborn_if_exists() { + let userborn_result = services::restart_userborn_if_exists(); + if let Err(ref e) = userborn_result { log::error!("Error restarting userborn.service: {e}"); } + userborn_result.or_stash(&mut errs); log::info!("Activating tmp files..."); - let tmp_result = tmp_files::activate(&etc_tree.files); - if let Err(e) = &tmp_result { - log::error!("Error during activation of tmp files"); - log::error!("{e}"); + let tmp_result = + collect_activation_result_err(tmp_files::activate(&etc_tree.files), &mut errs); + if let Err(ref e) = tmp_result { + log::error!("Error during activation of tmp files: {e}"); } else { - log::debug!("Successfully created tmp files"); + log::info!("Successfully created tmp files"); } log::info!("Activating systemd services..."); - let final_state = match services::activate(store_path, old_state.services, ephemeral) { - Ok(services) => StateV1 { - file_tree: etc_tree, - services, - version: 1, - }, - Err(ActivationError::WithPartialResult { result, source }) => { - log::error!("Error during activation: {source:?}"); - StateV1 { - file_tree: etc_tree, - services: result, - version: 1, - } - } - }; - final_state.write_to_file(state_file)?; - - if let Err(e) = tmp_result { - return Err(e.into()); + let svc_result = collect_activation_result_err( + services::activate(store_path, old_state.services, ephemeral), + &mut errs, + ); + if let Err(ref e) = svc_result { + log::error!("Error during activation: {e:?}"); + } else { + log::info!("Successfully activated systemd services"); } - - Ok(()) - } - Err(ActivationError::WithPartialResult { result, source }) => { - log::error!("Error during activation: {source:?}"); - log::debug!("Resulting file tree: {:?}", result); - let final_state = StateV1 { - file_tree: result, - ..old_state + let services = match svc_result { + Ok(s) => s, + Err(ActivationError::WithPartialResult { result, .. }) => result, }; - final_state.write_to_file(state_file)?; - Ok(()) + (etc_tree, services) } - } + Err(ActivationError::WithPartialResult { result, .. }) => (result, old_state.services), + }; + + let final_state = StateV1 { + file_tree: etc_tree, + services, + version: 1, + }; + final_state.write_to_file(state_file).or_stash(&mut errs); + + Ok(Result::<(), _>::from(errs)?) } pub fn prepopulate(store_path: &StorePath, ephemeral: bool) -> Result<()> { @@ -213,38 +244,48 @@ pub fn prepopulate(store_path: &StorePath, ephemeral: bool) -> Result<()> { let state_file = &get_state_file()?; let old_state = StateV1::from_file(state_file)?; + let mut errs = ErrorStash::new(|| "Pre-population completed with errors"); log::info!("Activating etc files..."); - match etc_files::activate(store_path, old_state.file_tree, ephemeral) { + let etc_result = collect_activation_result_err( + etc_files::activate(store_path, old_state.file_tree, ephemeral), + &mut errs, + ); + if let Err(ref e) = etc_result { + log::error!("Error during activation: {e:?}"); + } + + // Only register services when etc files were fully applied, preserving + // old service state on etc failure to avoid persisting state from a + // partial run. + let (etc_tree, services) = match etc_result { Ok(etc_tree) => { log::info!("Registering systemd services..."); - match services::get_active_services(store_path, old_state.services) { - Ok(services) => StateV1 { - file_tree: etc_tree, - services, - version: 1, - }, - Err(ActivationError::WithPartialResult { result, source }) => { - log::error!("Error during activation: {source:?}"); - StateV1 { - file_tree: etc_tree, - services: result, - version: 1, - } - } - } - } - Err(ActivationError::WithPartialResult { result, source }) => { - log::error!("Error during activation: {source:?}"); - StateV1 { - file_tree: result, - ..old_state + let svc_result = collect_activation_result_err( + services::get_active_services(store_path, old_state.services), + &mut errs, + ); + if let Err(ref e) = svc_result { + log::error!("Error during activation: {e:?}"); } + let services = match svc_result { + Ok(s) => s, + Err(ActivationError::WithPartialResult { result, .. }) => result, + }; + (etc_tree, services) } - } - .write_to_file(state_file)?; - Ok(()) + Err(ActivationError::WithPartialResult { result, .. }) => (result, old_state.services), + }; + + let final_state = StateV1 { + file_tree: etc_tree, + services, + version: 1, + }; + final_state.write_to_file(state_file).or_stash(&mut errs); + + Ok(Result::<(), _>::from(errs)?) } fn run_preactivation_assertions(store_path: &StorePath) -> Result { @@ -267,3 +308,38 @@ pub(crate) fn get_state_file() -> Result { .create(SYSTEM_MANAGER_STATE_DIR)?; Ok(state_file) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty_stash_returns_ok() { + let errs = ErrorStash::new(|| "Activation completed with errors"); + let result: std::result::Result<(), lazy_errors::prelude::Error> = errs.into(); + assert!(result.is_ok()); + } + + #[test] + fn single_stashed_error_returns_err() { + let mut errs = ErrorStash::new(|| "Activation completed with errors"); + Err::<(), _>(anyhow::anyhow!("userborn failed")).or_stash(&mut errs); + let result: std::result::Result<(), lazy_errors::prelude::Error> = errs.into(); + let err = result.unwrap_err(); + let msg = format!("{err:#}"); + assert!(msg.contains("userborn failed"), "message was: {msg}"); + } + + #[test] + fn multiple_stashed_errors_returns_combined_err() { + let mut errs = ErrorStash::new(|| "Deactivation completed with errors"); + Err::<(), _>(anyhow::anyhow!("userborn failed")).or_stash(&mut errs); + Err::<(), _>(anyhow::anyhow!("tmpfiles failed")).or_stash(&mut errs); + let result: std::result::Result<(), lazy_errors::prelude::Error> = errs.into(); + let err = result.unwrap_err(); + let msg = format!("{err:#}"); + assert!(msg.contains("Deactivation"), "message was: {msg}"); + assert!(msg.contains("userborn failed"), "message was: {msg}"); + assert!(msg.contains("tmpfiles failed"), "message was: {msg}"); + } +} diff --git a/crates/system-manager-engine/src/activate/etc_files.rs b/crates/system-manager-engine/src/activate/etc_files.rs index 3904d87b..240d53bc 100644 --- a/crates/system-manager-engine/src/activate/etc_files.rs +++ b/crates/system-manager-engine/src/activate/etc_files.rs @@ -124,7 +124,14 @@ pub fn activate( .collect(); entries.append(&mut non_static_entries); // Create dirs and link/copy entries - new_state = create_etc_files(entries, new_state.clone(), &old_state, &etc_dir)?; + let mut errors = Vec::new(); + new_state = create_etc_files( + entries, + new_state.clone(), + &old_state, + &etc_dir, + &mut errors, + ); // Delete unecessary files let files_to_delete: HashSet = old_state .files @@ -132,7 +139,19 @@ pub fn activate( .map(|f| f.to_owned()) .collect(); new_state = delete_paths(&files_to_delete, new_state); - Ok(new_state) + if errors.is_empty() { + Ok(new_state) + } else { + let messages: Vec = errors.iter().map(|e| format!("{e:#}")).collect(); + Err(ActivationError::with_partial_result( + new_state, + anyhow::anyhow!( + "{} etc file error(s):\n{}", + messages.len(), + messages.join("\n") + ), + )) + } } pub fn deactivate(old_state: EtcFilesState) -> EtcActivationResult { @@ -282,19 +301,24 @@ fn create_etc_files( mut state: EtcFilesState, old_state: &EtcFilesState, etc_dir: &Path, -) -> EtcActivationResult { + errors: &mut Vec, +) -> EtcFilesState { files.sort_by(|a, b| a.target.cmp(&b.target)); for file in files { let target = file.target.clone(); state = match create_etc_file(file, state, old_state, etc_dir) { Ok(state) => state, Err(ActivationError::WithPartialResult { result, source }) => { - log::warn!("Can't link/copy {} to : {}", target.display(), source); + log::error!( + "Error while creating file in {}: {source:?}", + target.display() + ); + errors.push(source); result } } } - Ok(state) + state } /// Create a single etc file. @@ -358,10 +382,13 @@ fn create_etc_file( ); state = backup_and_link(&target, &file.source.store_path, state)?; } else { - log::warn!( - "Error while creating file in /etc: Unmanaged path already exists in filesystem, please remove it and run system-manager again: {}\nSet replaceExisting if you're willing to override it.", - target.display() - ); + return Err(ActivationError::with_partial_result( + state, + anyhow::anyhow!( + "Unmanaged path already exists in filesystem, please remove it or use replaceExisting and run system-manager again: {}.", + target.display() + ), + )); } } else { // Target do not exist on the filesystem diff --git a/crates/system-manager-engine/src/deactivate.rs b/crates/system-manager-engine/src/deactivate.rs index 3b80f2bc..e5e53d04 100644 --- a/crates/system-manager-engine/src/deactivate.rs +++ b/crates/system-manager-engine/src/deactivate.rs @@ -1,54 +1,55 @@ use anyhow::Result; +use lazy_errors::prelude::*; use crate::activate::etc_files; use crate::activate::services; use crate::activate::users; -use crate::activate::{get_state_file, ActivationError, StateV1}; +use crate::activate::{collect_activation_result_err, get_state_file, ActivationError, StateV1}; -/// Deactivates system-manager by locking managed users, removing etc files, -/// and stopping systemd services. pub fn deactivate() -> Result<()> { log::info!("Deactivating system-manager"); let state_file = &get_state_file()?; let old_state = StateV1::from_file(state_file)?; log::debug!("{old_state:?}"); + let mut errs = ErrorStash::new(|| "Deactivation completed with errors"); - if let Err(e) = users::lock_managed_users() { + let lock_result = users::lock_managed_users(); + if let Err(ref e) = lock_result { log::error!("Error locking managed user accounts: {e}"); } + lock_result.or_stash(&mut errs); if let Err(e) = users::restore_original_shells() { log::error!("Error restoring original shell paths: {e}"); } - match etc_files::deactivate(old_state.file_tree) { - Ok(etc_tree) => { - log::info!("Deactivating systemd services..."); - match services::deactivate(old_state.services) { - Ok(services) => StateV1 { - file_tree: etc_tree, - services, - version: Default::default(), - }, - Err(ActivationError::WithPartialResult { result, source }) => { - log::error!("Error during deactivation: {source:?}"); - StateV1 { - file_tree: etc_tree, - services: result, - version: Default::default(), - } - } - } - } - Err(ActivationError::WithPartialResult { result, source }) => { - log::error!("Error during deactivation: {source:?}"); - StateV1 { - file_tree: result, - ..old_state - } - } + let etc_result = + collect_activation_result_err(etc_files::deactivate(old_state.file_tree), &mut errs); + if let Err(ref e) = etc_result { + log::error!("Error during deactivation: {e:?}"); } - .write_to_file(state_file)?; + let etc_tree = match etc_result { + Ok(t) => t, + Err(ActivationError::WithPartialResult { result, .. }) => result, + }; - Ok(()) + log::info!("Deactivating systemd services..."); + let svc_result = + collect_activation_result_err(services::deactivate(old_state.services), &mut errs); + if let Err(ref e) = svc_result { + log::error!("Error during deactivation: {e:?}"); + } + let services = match svc_result { + Ok(s) => s, + Err(ActivationError::WithPartialResult { result, .. }) => result, + }; + + let final_state = StateV1 { + file_tree: etc_tree, + services, + version: 1, + }; + final_state.write_to_file(state_file).or_stash(&mut errs); + + Ok(Result::<(), _>::from(errs)?) } diff --git a/nix/lib.nix b/nix/lib.nix index 6e5c327e..03057c05 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -195,8 +195,8 @@ let action, }: '' - ${node}.succeed("RUST_LOG=debug ${profile}/bin/${action} 2>&1 | tee /tmp/output.log") - ${node}.succeed("! grep -F 'ERROR' /tmp/output.log") + output = ${node}.succeed("RUST_LOG=debug ${profile}/bin/${action} 2>&1") + assert "ERROR" not in output, f"${action} logged errors:\n{output}" ''; activateProfileSnippet = diff --git a/testFlake/container-tests/empty-config.nix b/testFlake/container-tests/empty-config.nix index 05834241..a50b8430 100644 --- a/testFlake/container-tests/empty-config.nix +++ b/testFlake/container-tests/empty-config.nix @@ -95,11 +95,7 @@ forEachDistro "empty-config" { with subtest("Snapshot /etc before activation"): before = snapshot_etc() - activation_logs = machine.activate() - with subtest("Activation produces no errors"): - for line in activation_logs.split("\n"): - assert "ERROR" not in line, f"Unexpected error in activation: {line}" - + machine.activate() machine.wait_for_unit("system-manager.target") with subtest("No unexpected changes to /etc after activation"): diff --git a/testFlake/container-tests/example.nix b/testFlake/container-tests/example.nix index f5972701..654f8dd7 100644 --- a/testFlake/container-tests/example.nix +++ b/testFlake/container-tests/example.nix @@ -16,9 +16,7 @@ forEachDistro "example" { # Nix is installed and profile is copied by the driver automatically # Activate system-manager def activate_and_check(): - activation_logs = machine.activate() - for line in activation_logs.split("\n"): - assert not "ERROR" in line, line + machine.activate() machine.wait_for_unit("system-manager.target") with subtest("Verify services are running"): diff --git a/testFlake/container-tests/existing-files.nix b/testFlake/container-tests/existing-files.nix index 80d5511a..a503d73e 100644 --- a/testFlake/container-tests/existing-files.nix +++ b/testFlake/container-tests/existing-files.nix @@ -60,12 +60,10 @@ forEachDistro "existing-files" { machine.succeed("mkdir -p /etc/systemd/system/timers.target.wants") machine.succeed("ln -sf /lib/systemd/system/fake-existing.timer /etc/systemd/system/timers.target.wants/existing.timer") - # Activate directly because the no-replace-test entry - # will produce an expected ERROR that machine.activate() would reject. - machine.succeed("${toplevel}/bin/activate 2>&1 | tee /tmp/output.log") - - # Verify that the no-replace entry produced the expected error - machine.succeed("grep -F 'File /etc/no-replace-test already exists' /tmp/output.log") + # Activate directly because the no-replace-test entry produces an + # expected error that causes a non-zero exit code. + output = machine.fail("${toplevel}/bin/activate 2>&1") + assert "File /etc/no-replace-test already exists" in output, f"Expected no-replace error, got: {output}" no_replace = machine.succeed("cat /etc/no-replace-test").strip() assert no_replace == "do not touch", f"Expected untouched file, got: {no_replace}" machine.fail("test -e /etc/no-replace-test.system-manager-backup") diff --git a/testFlake/container-tests/extra-init.nix b/testFlake/container-tests/extra-init.nix index 480b5371..b6999556 100644 --- a/testFlake/container-tests/extra-init.nix +++ b/testFlake/container-tests/extra-init.nix @@ -18,9 +18,7 @@ forEachDistro "extra-init" { machine.wait_for_unit("multi-user.target") - activation_logs = machine.activate() - for line in activation_logs.split("\n"): - assert not "ERROR" in line, line + machine.activate() machine.wait_for_unit("system-manager.target") with subtest("extraInit code is present in profile script"): diff --git a/testFlake/container-tests/nginx-dhparams.nix b/testFlake/container-tests/nginx-dhparams.nix index dacb8abd..004599a8 100644 --- a/testFlake/container-tests/nginx-dhparams.nix +++ b/testFlake/container-tests/nginx-dhparams.nix @@ -25,9 +25,7 @@ forEachDistro "nginx-dhparams" { machine.wait_for_unit("multi-user.target") - activation_logs = machine.activate() - for line in activation_logs.split("\n"): - assert not "ERROR" in line, line + machine.activate() machine.wait_for_unit("system-manager.target") with subtest("Verify nginx is running"): diff --git a/testFlake/container-tests/ssh.nix b/testFlake/container-tests/ssh.nix index 194f8f39..506d33c5 100644 --- a/testFlake/container-tests/ssh.nix +++ b/testFlake/container-tests/ssh.nix @@ -53,9 +53,7 @@ forEachDistro "ssh" { # It's generated as a postinstall hook, so let's run it again. machine.succeed("dpkg-reconfigure openssh-server") - activation_logs = machine.activate() - for line in activation_logs.split("\n"): - assert not "ERROR" in line, line + machine.activate() machine.wait_for_unit("system-manager.target") with subtest("ssh_known_hosts file exists"): diff --git a/testFlake/container-tests/state-v0-v1-activate-migration.nix b/testFlake/container-tests/state-v0-v1-activate-migration.nix index 395a89ff..732ceabe 100644 --- a/testFlake/container-tests/state-v0-v1-activate-migration.nix +++ b/testFlake/container-tests/state-v0-v1-activate-migration.nix @@ -72,9 +72,7 @@ forEachDistro "state-v0-v1-migration-activate" ( assert file.contains(content), f"{path} should contain {content}" # Let's activate the profile with a v0 state file (using an old system-manager checkout) - activation_logs = machine.succeed("${v0TopLevel}/bin/activate") - for line in activation_logs.split("\n"): - assert not "ERROR" in line, line + machine.succeed("${v0TopLevel}/bin/activate") machine.wait_for_unit("system-manager.target") with subtest("Verify correct files are created"): @@ -84,9 +82,7 @@ forEachDistro "state-v0-v1-migration-activate" ( check_file("/etc/b/link", "link") # Let's try to deactivate the machine with the new binary, making sure the state migration works. - activation_logs = machine.succeed("${toplevel}/bin/activate") - for line in activation_logs.split("\n"): - assert ((not "ERROR" in line) and (not "WARN" in line)), line + machine.succeed("${toplevel}/bin/activate") # Check the state backup works backup = machine.file("/var/lib/system-manager/state/system-manager-state.json.v0back") diff --git a/testFlake/container-tests/state-v0-v1-deactivate-migration.nix b/testFlake/container-tests/state-v0-v1-deactivate-migration.nix index 9318e2cd..21d1665f 100644 --- a/testFlake/container-tests/state-v0-v1-deactivate-migration.nix +++ b/testFlake/container-tests/state-v0-v1-deactivate-migration.nix @@ -72,9 +72,7 @@ forEachDistro "state-v0-v1-migration-deactivate" ( assert file.contains(content), f"{path} should contain {content}" # Let's activate the profile with a v0 state file (using an old system-manager checkout) - activation_logs = machine.succeed("${v0TopLevel}/bin/activate") - for line in activation_logs.split("\n"): - assert not "ERROR" in line, line + machine.succeed("${v0TopLevel}/bin/activate") machine.wait_for_unit("system-manager.target") with subtest("Verify correct files are created"): @@ -84,12 +82,10 @@ forEachDistro "state-v0-v1-migration-deactivate" ( check_file("/etc/b/link", "link") # Let's activate the profile with a v0 state file (using an old system-manager checkout) - activation_logs = machine.succeed("${v0TopLevel}/bin/activate") + machine.succeed("${v0TopLevel}/bin/activate") # Let's try to deactivate the machine with the new binary, making sure the state migration works. - deactivation_logs = machine.succeed("${toplevel}/bin/deactivate") - for line in activation_logs.split("\n"): - assert ((not "ERROR" in line) and (not "WARN" in line)), line + machine.succeed("${toplevel}/bin/deactivate") with subtest("v1 deactivation restores the backups from a v0 generated state"): machine.succeed("test -f /etc/b/bar") machine.succeed("test -f /etc/b/link") diff --git a/testFlake/vm-tests/example.nix b/testFlake/vm-tests/example.nix index 1a6b95f4..56163371 100644 --- a/testFlake/vm-tests/example.nix +++ b/testFlake/vm-tests/example.nix @@ -28,8 +28,8 @@ forEachUbuntuImage "example" { nobody_shell_before = vm.succeed("getent passwd nobody").strip().split(":")[-1] vm.succeed("touch /etc/foo_test") - vm.succeed("${toplevel}/bin/activate 2>&1 | tee /tmp/output.log") - vm.succeed("grep -F 'Error while creating file in /etc: Unmanaged path already exists in filesystem, please remove it and run system-manager again: /etc/foo_test' /tmp/output.log") + output = vm.fail("${toplevel}/bin/activate 2>&1") + assert "Unmanaged path already exists" in output, f"Expected unmanaged path error, got: {output}" vm.succeed("rm /etc/foo_test") ${system-manager.lib.activateProfileSnippet { @@ -79,7 +79,6 @@ forEachUbuntuImage "example" { node = "vm"; profile = newConfig; }} - print(vm.succeed("cat /tmp/output.log")) print(vm.succeed("cat /run/secrets/test")) diff --git a/testFlake/vm-tests/sudo.nix b/testFlake/vm-tests/sudo.nix index 187de41c..541f6722 100644 --- a/testFlake/vm-tests/sudo.nix +++ b/testFlake/vm-tests/sudo.nix @@ -41,12 +41,10 @@ forEachUbuntuImage "sudo" { # Test 2: Register and activate with --sudo should succeed # First register the profile (creates the symlink) - vm.succeed("su - testuser -c '${system-manager-cli}/bin/system-manager register --sudo --store-path ${toplevel} 2>&1' | tee /tmp/sudo-register.log") - vm.succeed("! grep -F 'ERROR' /tmp/sudo-register.log") + vm.succeed("su - testuser -c '${system-manager-cli}/bin/system-manager register --sudo --store-path ${toplevel} 2>&1'") # Then activate - vm.succeed("su - testuser -c '${system-manager-cli}/bin/system-manager activate --sudo --store-path ${toplevel} 2>&1' | tee /tmp/sudo-activate.log") - vm.succeed("! grep -F 'ERROR' /tmp/sudo-activate.log") + vm.succeed("su - testuser -c '${system-manager-cli}/bin/system-manager activate --sudo --store-path ${toplevel} 2>&1'") # Verify activation worked vm.wait_for_unit("system-manager.target") @@ -55,8 +53,7 @@ forEachUbuntuImage "sudo" { # Test 3: Deactivation with --sudo should also work # Now that the profile is registered, deactivate can find the engine - vm.succeed("su - testuser -c '${system-manager-cli}/bin/system-manager deactivate --sudo 2>&1' | tee /tmp/sudo-deactivate.log") - vm.succeed("! grep -F 'ERROR' /tmp/sudo-deactivate.log") + vm.succeed("su - testuser -c '${system-manager-cli}/bin/system-manager deactivate --sudo 2>&1'") # Verify deactivation worked vm.fail("systemctl status service-9.service")