Rename EtcTree -> FileTree.

This commit is contained in:
r-vdp 2023-04-13 12:07:09 +02:00
parent a642fe94dd
commit af853c014c
No known key found for this signature in database
5 changed files with 85 additions and 79 deletions

View file

@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
use std::{fs, io, process};
use thiserror::Error;
use crate::activate::etc_files::EtcTree;
use crate::activate::etc_files::FileTree;
use crate::{StorePath, STATE_FILE_NAME, SYSTEM_MANAGER_STATE_DIR};
#[derive(Error, Debug)]
@ -34,7 +34,7 @@ pub type ActivationResult<R> = Result<R, ActivationError<R>>;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct State {
etc_tree: EtcTree,
file_tree: FileTree,
services: services::Services,
}
@ -78,15 +78,18 @@ pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
log::info!("Activating etc files...");
match etc_files::activate(store_path, old_state.etc_tree, ephemeral) {
match etc_files::activate(store_path, old_state.file_tree, ephemeral) {
Ok(etc_tree) => {
log::info!("Activating systemd services...");
match services::activate(store_path, old_state.services, ephemeral) {
Ok(services) => State { etc_tree, services },
Ok(services) => State {
file_tree: etc_tree,
services,
},
Err(ActivationError::WithPartialResult { result, source }) => {
log::error!("Error during activation: {source:?}");
State {
etc_tree,
file_tree: etc_tree,
services: result,
}
}
@ -95,7 +98,7 @@ pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
Err(ActivationError::WithPartialResult { result, source }) => {
log::error!("Error during activation: {source:?}");
State {
etc_tree: result,
file_tree: result,
..old_state
}
}
@ -111,15 +114,18 @@ pub fn deactivate() -> Result<()> {
let old_state = State::from_file(state_file)?;
log::debug!("{old_state:?}");
match etc_files::deactivate(old_state.etc_tree) {
match etc_files::deactivate(old_state.file_tree) {
Ok(etc_tree) => {
log::info!("Deactivating systemd services...");
match services::deactivate(old_state.services) {
Ok(services) => State { etc_tree, services },
Ok(services) => State {
file_tree: etc_tree,
services,
},
Err(ActivationError::WithPartialResult { result, source }) => {
log::error!("Error during deactivation: {source:?}");
State {
etc_tree,
file_tree: etc_tree,
services: result,
}
}
@ -128,7 +134,7 @@ pub fn deactivate() -> Result<()> {
Err(ActivationError::WithPartialResult { result, source }) => {
log::error!("Error during deactivation: {source:?}");
State {
etc_tree: result,
file_tree: result,
..old_state
}
}

View file

@ -9,7 +9,7 @@ use std::path;
use std::path::{Path, PathBuf};
use std::{fs, io};
use self::etc_tree::EtcFileStatus;
use self::etc_tree::FileStatus;
use super::ActivationResult;
use crate::activate::ActivationError;
use crate::{
@ -17,9 +17,9 @@ use crate::{
SYSTEM_MANAGER_STATIC_NAME,
};
pub use etc_tree::EtcTree;
pub use etc_tree::FileTree;
type EtcActivationResult = ActivationResult<EtcTree>;
type EtcActivationResult = ActivationResult<FileTree>;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -81,7 +81,7 @@ fn read_config(store_path: &StorePath) -> anyhow::Result<EtcFilesConfig> {
pub fn activate(
store_path: &StorePath,
old_state: EtcTree,
old_state: FileTree,
ephemeral: bool,
) -> EtcActivationResult {
let config = read_config(store_path)
@ -90,7 +90,7 @@ pub fn activate(
let etc_dir = etc_dir(ephemeral);
log::info!("Creating /etc entries in {}", etc_dir.display());
let initial_state = EtcTree::root_node();
let initial_state = FileTree::root_node();
let state = create_etc_static_link(
SYSTEM_MANAGER_STATIC_NAME,
@ -108,15 +108,15 @@ pub fn activate(
Ok(final_state)
}
pub fn deactivate(old_state: EtcTree) -> EtcActivationResult {
pub fn deactivate(old_state: FileTree) -> EtcActivationResult {
let final_state = old_state.deactivate(&try_delete_path).unwrap_or_default();
log::info!("Done");
Ok(final_state)
}
fn try_delete_path(path: &Path, status: &EtcFileStatus) -> bool {
fn do_try_delete(path: &Path, status: &EtcFileStatus) -> anyhow::Result<()> {
fn try_delete_path(path: &Path, status: &FileStatus) -> bool {
fn do_try_delete(path: &Path, status: &FileStatus) -> anyhow::Result<()> {
// exists() returns false for broken symlinks
if path.exists() || path.is_symlink() {
if path.is_symlink() {
@ -127,7 +127,7 @@ fn try_delete_path(path: &Path, status: &EtcFileStatus) -> bool {
if path.read_dir()?.next().is_none() {
remove_dir(path)
} else {
if let EtcFileStatus::Managed = status {
if let FileStatus::Managed = status {
log::warn!("Managed directory not empty, ignoring: {}", path.display());
}
Ok(())
@ -153,9 +153,9 @@ fn try_delete_path(path: &Path, status: &EtcFileStatus) -> bool {
fn create_etc_links<'a, E>(
entries: E,
etc_dir: &Path,
state: EtcTree,
old_state: &EtcTree,
) -> EtcTree
state: FileTree,
old_state: &FileTree,
) -> FileTree
where
E: Iterator<Item = &'a EtcFile>,
{
@ -178,7 +178,7 @@ fn create_etc_static_link(
static_dir_name: &str,
store_path: &StorePath,
etc_dir: &Path,
state: EtcTree,
state: FileTree,
) -> EtcActivationResult {
let static_path = etc_dir.join(static_dir_name);
let new_state = create_dir_recursively(static_path.parent().unwrap(), state);
@ -193,8 +193,8 @@ fn create_etc_static_link(
fn create_etc_link<P>(
link_target: &P,
etc_dir: &Path,
state: EtcTree,
old_state: &EtcTree,
state: FileTree,
old_state: &FileTree,
) -> EtcActivationResult
where
P: AsRef<Path>,
@ -203,13 +203,13 @@ where
link_target: &Path,
absolute_target: &Path,
etc_dir: &Path,
state: EtcTree,
old_state: &EtcTree,
state: FileTree,
old_state: &FileTree,
upwards_path: &Path,
) -> EtcActivationResult {
let link_path = etc_dir.join(link_target);
if link_path.is_dir() && absolute_target.is_dir() {
log::info!("Entering into directory...");
log::debug!("Entering into directory {}...", link_path.display());
Ok(absolute_target
.read_dir()
.expect("Error reading the directory.")
@ -250,8 +250,8 @@ where
fn go(
link_target: &Path,
etc_dir: &Path,
state: EtcTree,
old_state: &EtcTree,
state: FileTree,
old_state: &FileTree,
upwards_path: &Path,
) -> EtcActivationResult {
let link_path = etc_dir.join(link_target);
@ -272,6 +272,7 @@ where
} else if link_path.is_symlink()
&& link_path.read_link().expect("Error reading link.") == target
{
log::debug!("Link {} up to date.", link_path.display());
Ok(dir_state.register_managed_entry(&link_path))
} else {
let result = if link_path.exists() {
@ -304,8 +305,8 @@ where
fn create_etc_entry(
entry: &EtcFile,
etc_dir: &Path,
state: EtcTree,
old_state: &EtcTree,
state: FileTree,
old_state: &FileTree,
) -> EtcActivationResult {
if entry.mode == "symlink" {
if let Some(path::Component::Normal(link_target)) = entry.target.components().next() {
@ -331,7 +332,7 @@ fn create_etc_entry(
}
}
fn create_dir_recursively(dir: &Path, state: EtcTree) -> EtcActivationResult {
fn create_dir_recursively(dir: &Path, state: FileTree) -> EtcActivationResult {
use itertools::FoldWhile::{Continue, Done};
use path::Component;
@ -382,7 +383,7 @@ fn create_dir_recursively(dir: &Path, state: EtcTree) -> EtcActivationResult {
new_state
}
fn copy_file(source: &Path, target: &Path, mode: &str, old_state: &EtcTree) -> anyhow::Result<()> {
fn copy_file(source: &Path, target: &Path, mode: &str, old_state: &FileTree) -> anyhow::Result<()> {
let exists = target.try_exists()?;
if !exists || old_state.is_managed(target) {
log::debug!(

View file

@ -7,14 +7,14 @@ use std::path::{Path, PathBuf};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EtcFileStatus {
pub enum FileStatus {
Managed,
Unmanaged,
}
impl EtcFileStatus {
impl FileStatus {
fn merge(&self, other: &Self) -> Self {
use EtcFileStatus::*;
use FileStatus::*;
match (self, other) {
(Unmanaged, Unmanaged) => Unmanaged,
@ -25,22 +25,22 @@ impl EtcFileStatus {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EtcTree {
status: EtcFileStatus,
pub struct FileTree {
status: FileStatus,
path: PathBuf,
// TODO directories and files are now both represented as a string associated with a nested
// map. For files the nested map is simple empty.
// We could potentially optimise this.
nested: HashMap<String, EtcTree>,
nested: HashMap<String, FileTree>,
}
impl AsRef<EtcTree> for EtcTree {
fn as_ref(&self) -> &EtcTree {
impl AsRef<FileTree> for FileTree {
fn as_ref(&self) -> &FileTree {
self
}
}
impl Default for EtcTree {
impl Default for FileTree {
fn default() -> Self {
Self::root_node()
}
@ -56,12 +56,12 @@ impl Default for EtcTree {
/// 3. Import from a file
/// 4. Add a path to the tree, that will from then on be considered as managed
/// 5.
impl EtcTree {
impl FileTree {
fn new(path: PathBuf) -> Self {
Self::with_status(path, EtcFileStatus::Unmanaged)
Self::with_status(path, FileStatus::Unmanaged)
}
fn with_status(path: PathBuf, status: EtcFileStatus) -> Self {
fn with_status(path: PathBuf, status: FileStatus) -> Self {
Self {
status,
path,
@ -73,8 +73,8 @@ impl EtcTree {
Self::new(PathBuf::from(path::MAIN_SEPARATOR_STR))
}
pub fn get_status<'a>(&'a self, path: &Path) -> &'a EtcFileStatus {
fn go<'a, 'b, C>(tree: &'a EtcTree, mut components: C, path: &Path) -> &'a EtcFileStatus
pub fn get_status<'a>(&'a self, path: &Path) -> &'a FileStatus {
fn go<'a, 'b, C>(tree: &'a FileTree, mut components: C, path: &Path) -> &'a FileStatus
where
C: Iterator<Item = path::Component<'b>>,
{
@ -84,7 +84,7 @@ impl EtcTree {
.nested
.get(name.to_string_lossy().as_ref())
.map(|subtree| go(subtree, components, path))
.unwrap_or(&EtcFileStatus::Unmanaged),
.unwrap_or(&FileStatus::Unmanaged),
path::Component::RootDir => go(tree, components, path),
_ => todo!(),
}
@ -97,13 +97,13 @@ impl EtcTree {
}
pub fn is_managed(&self, path: &Path) -> bool {
*self.get_status(path) == EtcFileStatus::Managed
*self.get_status(path) == FileStatus::Managed
}
// TODO is recursion OK here?
// Should we convert to CPS and use a crate like tramp to TCO this?
pub fn register_managed_entry(self, path: &Path) -> Self {
fn go<'a, C>(mut tree: EtcTree, mut components: Peekable<C>, path: PathBuf) -> EtcTree
fn go<'a, C>(mut tree: FileTree, mut components: Peekable<C>, path: PathBuf) -> FileTree
where
C: Iterator<Item = path::Component<'a>>,
{
@ -115,15 +115,13 @@ impl EtcTree {
|maybe_subtree| {
Some(go(
maybe_subtree.unwrap_or_else(|| {
EtcTree::with_status(
FileTree::with_status(
new_path.to_owned(),
// We only label as managed the final path entry,
// to label intermediate nodes as managed, we should
// call this function for every one of them separately.
components
.peek()
.map_or(EtcFileStatus::Managed, |_| {
EtcFileStatus::Unmanaged
components.peek().map_or(FileStatus::Managed, |_| {
FileStatus::Unmanaged
}),
)
}),
@ -151,9 +149,9 @@ impl EtcTree {
go(self, path.components().peekable(), PathBuf::new())
}
pub fn deactivate<F>(self, delete_action: &F) -> Option<EtcTree>
pub fn deactivate<F>(self, delete_action: &F) -> Option<FileTree>
where
F: Fn(&Path, &EtcFileStatus) -> bool,
F: Fn(&Path, &FileStatus) -> bool,
{
let new_tree = self.nested.keys().fold(self.clone(), |mut new_tree, name| {
new_tree.nested = new_tree.nested.alter(
@ -168,7 +166,7 @@ impl EtcTree {
// are not responsible for cleaning them up (we don't run the delete_action
// closure on their paths).
if new_tree.nested.is_empty() {
if let EtcFileStatus::Managed = new_tree.status {
if let FileStatus::Managed = new_tree.status {
if delete_action(&new_tree.path, &new_tree.status) {
None
} else {
@ -184,7 +182,7 @@ impl EtcTree {
pub fn update_state<F>(self, other: Self, delete_action: &F) -> Option<Self>
where
F: Fn(&Path, &EtcFileStatus) -> bool,
F: Fn(&Path, &FileStatus) -> bool,
{
let to_deactivate = other
.nested
@ -225,7 +223,7 @@ impl EtcTree {
// If our invariants are properly maintained, then we should never end up
// here with dangling unmanaged nodes.
debug_assert!(!merged.nested.is_empty() || merged.status == EtcFileStatus::Managed);
debug_assert!(!merged.nested.is_empty() || merged.status == FileStatus::Managed);
Some(merged)
}
@ -236,20 +234,20 @@ mod tests {
use super::*;
use itertools::Itertools;
impl EtcTree {
impl FileTree {
pub fn deactivate_managed_entry<F>(self, path: &Path, delete_action: &F) -> Self
where
F: Fn(&Path, &EtcFileStatus) -> bool,
F: Fn(&Path, &FileStatus) -> bool,
{
fn go<'a, C, F>(
mut tree: EtcTree,
mut tree: FileTree,
path: PathBuf,
mut components: Peekable<C>,
delete_action: &F,
) -> EtcTree
) -> FileTree
where
C: Iterator<Item = path::Component<'a>>,
F: Fn(&Path, &EtcFileStatus) -> bool,
F: Fn(&Path, &FileStatus) -> bool,
{
log::debug!("Deactivating {}", path.display());
@ -296,8 +294,8 @@ mod tests {
}
#[test]
fn etc_tree_get_status() {
let tree1 = EtcTree::root_node()
fn get_status() {
let tree1 = FileTree::root_node()
.register_managed_entry(&PathBuf::from("/").join("foo").join("bar"))
.register_managed_entry(&PathBuf::from("/").join("foo2"))
.register_managed_entry(&PathBuf::from("/").join("foo2").join("baz"))
@ -319,8 +317,8 @@ mod tests {
}
#[test]
fn etc_tree_register() {
let tree = EtcTree::root_node()
fn register() {
let tree = FileTree::root_node()
.register_managed_entry(&PathBuf::from("/").join("foo").join("bar"))
.register_managed_entry(&PathBuf::from("/").join("foo2").join("baz").join("bar"))
.register_managed_entry(&PathBuf::from("/").join("foo2").join("baz2").join("bar"));
@ -345,8 +343,8 @@ mod tests {
}
#[test]
fn etc_tree_deactivate() {
let tree1 = EtcTree::root_node()
fn deactivate() {
let tree1 = FileTree::root_node()
.register_managed_entry(&PathBuf::from("/").join("foo").join("bar"))
.register_managed_entry(&PathBuf::from("/").join("foo2"))
.register_managed_entry(&PathBuf::from("/").join("foo2").join("baz"))
@ -406,8 +404,8 @@ mod tests {
}
#[test]
fn etc_tree_update_state() {
let tree1 = EtcTree::root_node()
fn update_state() {
let tree1 = FileTree::root_node()
.register_managed_entry(&PathBuf::from("/").join("foo").join("bar"))
.register_managed_entry(&PathBuf::from("/").join("foo2"))
.register_managed_entry(&PathBuf::from("/").join("foo2").join("baz"))
@ -415,7 +413,7 @@ mod tests {
.register_managed_entry(&PathBuf::from("/").join("foo2").join("baz2"))
.register_managed_entry(&PathBuf::from("/").join("foo2").join("baz2").join("bar"))
.register_managed_entry(&PathBuf::from("/").join("foo3").join("baz2").join("bar"));
let tree2 = EtcTree::root_node()
let tree2 = FileTree::root_node()
.register_managed_entry(&PathBuf::from("/").join("foo").join("bar"))
.register_managed_entry(&PathBuf::from("/").join("foo3").join("bar"))
.register_managed_entry(&PathBuf::from("/").join("foo4"))

View file

@ -295,7 +295,7 @@ where
.iter()
.fold(HashSet::new(), |mut set, unit| match action(unit) {
Ok(_) => {
log::info!("Unit {}: {}...", unit, log_action);
log::debug!("Unit {}: {}...", unit, log_action);
set.insert(JobId {
id: (*unit).to_owned(),
});

View file

@ -201,7 +201,8 @@ impl ServiceManager {
let total_jobs = waiting_for.len();
if total_jobs > 0 {
log::info!("Waiting for jobs to finish... (0/{})", total_jobs);
log::info!("Waiting for jobs to finish...");
log::debug!("Waiting for jobs to finish... (0/{})", total_jobs);
}
while !waiting_for.is_empty() {
@ -218,7 +219,7 @@ impl ServiceManager {
waiting_for = waiting_for.relative_complement(job_names.clone());
*job_names = im::HashSet::new();
if !waiting_for.is_empty() {
log::info!(
log::debug!(
"Waiting for jobs to finish... ({}/{})",
total_jobs - waiting_for.len(),
total_jobs