parent
16e886f53b
commit
4ce8d94ac7
|
@ -5,11 +5,8 @@ use nix::unistd::{chown, Group, User};
|
||||||
|
|
||||||
use tokio::fs::{create_dir, remove_dir_all};
|
use tokio::fs::{create_dir, remove_dir_all};
|
||||||
|
|
||||||
use crate::action::StatefulAction;
|
use crate::action::{Action, ActionDescription, ActionState};
|
||||||
use crate::{
|
use crate::action::{ActionError, StatefulAction};
|
||||||
action::{Action, ActionDescription, ActionState},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Create a directory at the given location, optionally with an owning user, group, and mode.
|
/** Create a directory at the given location, optionally with an owning user, group, and mode.
|
||||||
|
|
||||||
|
@ -33,16 +30,16 @@ impl CreateDirectory {
|
||||||
group: impl Into<Option<String>>,
|
group: impl Into<Option<String>>,
|
||||||
mode: impl Into<Option<u32>>,
|
mode: impl Into<Option<u32>>,
|
||||||
force_prune_on_revert: bool,
|
force_prune_on_revert: bool,
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let user = user.into();
|
let user = user.into();
|
||||||
let group = group.into();
|
let group = group.into();
|
||||||
let mode = mode.into();
|
let mode = mode.into();
|
||||||
|
|
||||||
let action_state = if path.exists() {
|
let action_state = if path.exists() {
|
||||||
let metadata = tokio::fs::metadata(path).await.map_err(|e| {
|
let metadata = tokio::fs::metadata(path)
|
||||||
CreateDirectoryError::GettingMetadata(path.to_path_buf(), e).boxed()
|
.await
|
||||||
})?;
|
.map_err(|e| ActionError::GettingMetadata(path.to_path_buf(), e))?;
|
||||||
if metadata.is_dir() {
|
if metadata.is_dir() {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"Creating directory `{}` already complete, skipping",
|
"Creating directory `{}` already complete, skipping",
|
||||||
|
@ -51,14 +48,7 @@ impl CreateDirectory {
|
||||||
// TODO: Validate owner/group...
|
// TODO: Validate owner/group...
|
||||||
ActionState::Skipped
|
ActionState::Skipped
|
||||||
} else {
|
} else {
|
||||||
return Err(CreateDirectoryError::Exists(std::io::Error::new(
|
return Err(ActionError::Exists(path.to_owned()));
|
||||||
std::io::ErrorKind::AlreadyExists,
|
|
||||||
format!(
|
|
||||||
"Path `{}` already exists and is not directory",
|
|
||||||
path.display()
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.boxed());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ActionState::Uncompleted
|
ActionState::Uncompleted
|
||||||
|
@ -94,7 +84,7 @@ impl Action for CreateDirectory {
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
path,
|
path,
|
||||||
user,
|
user,
|
||||||
|
@ -106,8 +96,8 @@ impl Action for CreateDirectory {
|
||||||
let gid = if let Some(group) = group {
|
let gid = if let Some(group) = group {
|
||||||
Some(
|
Some(
|
||||||
Group::from_name(group.as_str())
|
Group::from_name(group.as_str())
|
||||||
.map_err(|e| CreateDirectoryError::GroupId(group.clone(), e).boxed())?
|
.map_err(|e| ActionError::GroupId(group.clone(), e))?
|
||||||
.ok_or(CreateDirectoryError::NoGroup(group.clone()).boxed())?
|
.ok_or(ActionError::NoGroup(group.clone()))?
|
||||||
.gid,
|
.gid,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -116,8 +106,8 @@ impl Action for CreateDirectory {
|
||||||
let uid = if let Some(user) = user {
|
let uid = if let Some(user) = user {
|
||||||
Some(
|
Some(
|
||||||
User::from_name(user.as_str())
|
User::from_name(user.as_str())
|
||||||
.map_err(|e| CreateDirectoryError::UserId(user.clone(), e).boxed())?
|
.map_err(|e| ActionError::UserId(user.clone(), e))?
|
||||||
.ok_or(CreateDirectoryError::NoUser(user.clone()).boxed())?
|
.ok_or(ActionError::NoUser(user.clone()))?
|
||||||
.uid,
|
.uid,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -126,15 +116,13 @@ impl Action for CreateDirectory {
|
||||||
|
|
||||||
create_dir(path.clone())
|
create_dir(path.clone())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateDirectoryError::Creating(path.clone(), e).boxed())?;
|
.map_err(|e| ActionError::CreateDirectory(path.clone(), e))?;
|
||||||
chown(path, uid, gid).map_err(|e| CreateDirectoryError::Chown(path.clone(), e).boxed())?;
|
chown(path, uid, gid).map_err(|e| ActionError::Chown(path.clone(), e))?;
|
||||||
|
|
||||||
if let Some(mode) = mode {
|
if let Some(mode) = mode {
|
||||||
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
|
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| ActionError::SetPermissions(*mode, path.to_owned(), e))?;
|
||||||
CreateDirectoryError::SetPermissions(*mode, path.to_owned(), e).boxed()
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -168,7 +156,7 @@ impl Action for CreateDirectory {
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
path,
|
path,
|
||||||
user: _,
|
user: _,
|
||||||
|
@ -179,13 +167,13 @@ impl Action for CreateDirectory {
|
||||||
|
|
||||||
let is_empty = path
|
let is_empty = path
|
||||||
.read_dir()
|
.read_dir()
|
||||||
.map_err(|e| CreateDirectoryError::ReadDir(path.clone(), e).boxed())?
|
.map_err(|e| ActionError::Read(path.clone(), e))?
|
||||||
.next()
|
.next()
|
||||||
.is_some();
|
.is_some();
|
||||||
match (is_empty, force_prune_on_revert) {
|
match (is_empty, force_prune_on_revert) {
|
||||||
(true, _) | (false, true) => remove_dir_all(path.clone())
|
(true, _) | (false, true) => remove_dir_all(path.clone())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateDirectoryError::Removing(path.clone(), e).boxed())?,
|
.map_err(|e| ActionError::Remove(path.clone(), e))?,
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
tracing::debug!("Not removing `{}`, the folder is not empty", path.display());
|
tracing::debug!("Not removing `{}`, the folder is not empty", path.display());
|
||||||
},
|
},
|
||||||
|
@ -194,29 +182,3 @@ impl Action for CreateDirectory {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum CreateDirectoryError {
|
|
||||||
#[error(transparent)]
|
|
||||||
Exists(std::io::Error),
|
|
||||||
#[error("Creating directory `{0}`")]
|
|
||||||
Creating(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Removing directory `{0}`")]
|
|
||||||
Removing(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Getting metadata for {0}`")]
|
|
||||||
GettingMetadata(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Reading directory `{0}``")]
|
|
||||||
ReadDir(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Set mode `{0}` on `{1}`")]
|
|
||||||
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Chowning directory `{0}`")]
|
|
||||||
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
|
|
||||||
#[error("Getting uid for user `{0}`")]
|
|
||||||
UserId(String, #[source] nix::errno::Errno),
|
|
||||||
#[error("Getting user `{0}`")]
|
|
||||||
NoUser(String),
|
|
||||||
#[error("Getting gid for group `{0}`")]
|
|
||||||
GroupId(String, #[source] nix::errno::Errno),
|
|
||||||
#[error("Getting group `{0}`")]
|
|
||||||
NoGroup(String),
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,10 +6,7 @@ use tokio::{
|
||||||
io::AsyncWriteExt,
|
io::AsyncWriteExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||||
action::{Action, ActionDescription, StatefulAction},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Create a file at the given location with the provided `buf`,
|
/** Create a file at the given location with the provided `buf`,
|
||||||
optionally with an owning user, group, and mode.
|
optionally with an owning user, group, and mode.
|
||||||
|
@ -36,11 +33,11 @@ impl CreateFile {
|
||||||
mode: impl Into<Option<u32>>,
|
mode: impl Into<Option<u32>>,
|
||||||
buf: String,
|
buf: String,
|
||||||
force: bool,
|
force: bool,
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let path = path.as_ref().to_path_buf();
|
let path = path.as_ref().to_path_buf();
|
||||||
|
|
||||||
if path.exists() && !force {
|
if path.exists() && !force {
|
||||||
return Err(CreateFileError::Exists(path.to_path_buf()).boxed());
|
return Err(ActionError::Exists(path.to_path_buf()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -71,7 +68,7 @@ impl Action for CreateFile {
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
path,
|
path,
|
||||||
user,
|
user,
|
||||||
|
@ -91,17 +88,17 @@ impl Action for CreateFile {
|
||||||
let mut file = options
|
let mut file = options
|
||||||
.open(&path)
|
.open(&path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateFileError::OpenFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Open(path.to_owned(), e))?;
|
||||||
|
|
||||||
file.write_all(buf.as_bytes())
|
file.write_all(buf.as_bytes())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateFileError::WriteFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Write(path.to_owned(), e))?;
|
||||||
|
|
||||||
let gid = if let Some(group) = group {
|
let gid = if let Some(group) = group {
|
||||||
Some(
|
Some(
|
||||||
Group::from_name(group.as_str())
|
Group::from_name(group.as_str())
|
||||||
.map_err(|e| CreateFileError::GroupId(group.clone(), e).boxed())?
|
.map_err(|e| ActionError::GroupId(group.clone(), e))?
|
||||||
.ok_or(CreateFileError::NoGroup(group.clone()).boxed())?
|
.ok_or(ActionError::NoGroup(group.clone()))?
|
||||||
.gid,
|
.gid,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -110,14 +107,14 @@ impl Action for CreateFile {
|
||||||
let uid = if let Some(user) = user {
|
let uid = if let Some(user) = user {
|
||||||
Some(
|
Some(
|
||||||
User::from_name(user.as_str())
|
User::from_name(user.as_str())
|
||||||
.map_err(|e| CreateFileError::UserId(user.clone(), e).boxed())?
|
.map_err(|e| ActionError::UserId(user.clone(), e))?
|
||||||
.ok_or(CreateFileError::NoUser(user.clone()).boxed())?
|
.ok_or(ActionError::NoUser(user.clone()))?
|
||||||
.uid,
|
.uid,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
chown(path, uid, gid).map_err(|e| CreateFileError::Chown(path.clone(), e).boxed())?;
|
chown(path, uid, gid).map_err(|e| ActionError::Chown(path.clone(), e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -144,7 +141,7 @@ impl Action for CreateFile {
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
path,
|
path,
|
||||||
user: _,
|
user: _,
|
||||||
|
@ -156,30 +153,8 @@ impl Action for CreateFile {
|
||||||
|
|
||||||
remove_file(&path)
|
remove_file(&path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateFileError::RemoveFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Remove(path.to_owned(), e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum CreateFileError {
|
|
||||||
#[error("File exists `{0}`")]
|
|
||||||
Exists(std::path::PathBuf),
|
|
||||||
#[error("Remove file `{0}`")]
|
|
||||||
RemoveFile(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Open file `{0}`")]
|
|
||||||
OpenFile(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Write file `{0}`")]
|
|
||||||
WriteFile(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Getting uid for user `{0}`")]
|
|
||||||
UserId(String, #[source] nix::errno::Errno),
|
|
||||||
#[error("Getting user `{0}`")]
|
|
||||||
NoUser(String),
|
|
||||||
#[error("Getting gid for group `{0}`")]
|
|
||||||
GroupId(String, #[source] nix::errno::Errno),
|
|
||||||
#[error("Getting group `{0}`")]
|
|
||||||
NoGroup(String),
|
|
||||||
#[error("Chowning directory `{0}`")]
|
|
||||||
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::action::ActionError;
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||||
action::{Action, ActionDescription, StatefulAction},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Create an operating system level user group
|
Create an operating system level user group
|
||||||
|
@ -43,7 +41,7 @@ impl Action for CreateGroup {
|
||||||
user = self.name,
|
user = self.name,
|
||||||
gid = self.gid,
|
gid = self.gid,
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { name, gid } = self;
|
let Self { name, gid } = self;
|
||||||
|
|
||||||
use target_lexicon::OperatingSystem;
|
use target_lexicon::OperatingSystem;
|
||||||
|
@ -60,7 +58,8 @@ impl Action for CreateGroup {
|
||||||
.stdin(std::process::Stdio::null())
|
.stdin(std::process::Stdio::null())
|
||||||
.stdout(std::process::Stdio::null())
|
.stdout(std::process::Stdio::null())
|
||||||
.status()
|
.status()
|
||||||
.await?
|
.await
|
||||||
|
.map_err(ActionError::Command)?
|
||||||
.success()
|
.success()
|
||||||
{
|
{
|
||||||
()
|
()
|
||||||
|
@ -80,7 +79,7 @@ impl Action for CreateGroup {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateGroupError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -91,7 +90,7 @@ impl Action for CreateGroup {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateGroupError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,7 +111,7 @@ impl Action for CreateGroup {
|
||||||
user = self.name,
|
user = self.name,
|
||||||
gid = self.gid,
|
gid = self.gid,
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { name, gid: _ } = self;
|
let Self { name, gid: _ } = self;
|
||||||
|
|
||||||
use target_lexicon::OperatingSystem;
|
use target_lexicon::OperatingSystem;
|
||||||
|
@ -142,16 +141,10 @@ impl Action for CreateGroup {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateGroupError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum CreateGroupError {
|
|
||||||
#[error("Failed to execute command")]
|
|
||||||
Command(#[source] std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,10 +10,7 @@ use tokio::{
|
||||||
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
|
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||||
action::{Action, ActionDescription, StatefulAction},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Create a file at the given location with the provided `buf`,
|
/** Create a file at the given location with the provided `buf`,
|
||||||
optionally with an owning user, group, and mode.
|
optionally with an owning user, group, and mode.
|
||||||
|
@ -40,7 +37,7 @@ impl CreateOrAppendFile {
|
||||||
group: impl Into<Option<String>>,
|
group: impl Into<Option<String>>,
|
||||||
mode: impl Into<Option<u32>>,
|
mode: impl Into<Option<u32>>,
|
||||||
buf: String,
|
buf: String,
|
||||||
) -> Result<StatefulAction<Self>, CreateOrAppendFileError> {
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let path = path.as_ref().to_path_buf();
|
let path = path.as_ref().to_path_buf();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -71,7 +68,7 @@ impl Action for CreateOrAppendFile {
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
path,
|
path,
|
||||||
user,
|
user,
|
||||||
|
@ -86,21 +83,21 @@ impl Action for CreateOrAppendFile {
|
||||||
.read(true)
|
.read(true)
|
||||||
.open(&path)
|
.open(&path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateOrAppendFileError::OpenFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Open(path.to_owned(), e))?;
|
||||||
|
|
||||||
file.seek(SeekFrom::End(0))
|
file.seek(SeekFrom::End(0))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Seek(path.to_owned(), e))?;
|
||||||
|
|
||||||
file.write_all(buf.as_bytes())
|
file.write_all(buf.as_bytes())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateOrAppendFileError::WriteFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Write(path.to_owned(), e))?;
|
||||||
|
|
||||||
let gid = if let Some(group) = group {
|
let gid = if let Some(group) = group {
|
||||||
Some(
|
Some(
|
||||||
Group::from_name(group.as_str())
|
Group::from_name(group.as_str())
|
||||||
.map_err(|e| CreateOrAppendFileError::GroupId(group.clone(), e).boxed())?
|
.map_err(|e| ActionError::GroupId(group.clone(), e))?
|
||||||
.ok_or(CreateOrAppendFileError::NoGroup(group.clone()).boxed())?
|
.ok_or(ActionError::NoGroup(group.clone()))?
|
||||||
.gid,
|
.gid,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -109,8 +106,8 @@ impl Action for CreateOrAppendFile {
|
||||||
let uid = if let Some(user) = user {
|
let uid = if let Some(user) = user {
|
||||||
Some(
|
Some(
|
||||||
User::from_name(user.as_str())
|
User::from_name(user.as_str())
|
||||||
.map_err(|e| CreateOrAppendFileError::UserId(user.clone(), e).boxed())?
|
.map_err(|e| ActionError::UserId(user.clone(), e))?
|
||||||
.ok_or(CreateOrAppendFileError::NoUser(user.clone()).boxed())?
|
.ok_or(ActionError::NoUser(user.clone()))?
|
||||||
.uid,
|
.uid,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -120,13 +117,10 @@ impl Action for CreateOrAppendFile {
|
||||||
if let Some(mode) = mode {
|
if let Some(mode) = mode {
|
||||||
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
|
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| ActionError::SetPermissions(*mode, path.to_owned(), e))?;
|
||||||
CreateOrAppendFileError::SetPermissions(*mode, path.to_owned(), e).boxed()
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chown(path, uid, gid)
|
chown(path, uid, gid).map_err(|e| ActionError::Chown(path.clone(), e))?;
|
||||||
.map_err(|e| CreateOrAppendFileError::Chown(path.clone(), e).boxed())?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -154,7 +148,7 @@ impl Action for CreateOrAppendFile {
|
||||||
group = self.group,
|
group = self.group,
|
||||||
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
path,
|
path,
|
||||||
user: _,
|
user: _,
|
||||||
|
@ -168,12 +162,12 @@ impl Action for CreateOrAppendFile {
|
||||||
.read(true)
|
.read(true)
|
||||||
.open(&path)
|
.open(&path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateOrAppendFileError::ReadFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Read(path.to_owned(), e))?;
|
||||||
|
|
||||||
let mut file_contents = String::default();
|
let mut file_contents = String::default();
|
||||||
file.read_to_string(&mut file_contents)
|
file.read_to_string(&mut file_contents)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Seek(path.to_owned(), e))?;
|
||||||
|
|
||||||
if let Some(start) = file_contents.rfind(buf.as_str()) {
|
if let Some(start) = file_contents.rfind(buf.as_str()) {
|
||||||
let end = start + buf.len();
|
let end = start + buf.len();
|
||||||
|
@ -183,41 +177,15 @@ impl Action for CreateOrAppendFile {
|
||||||
if buf.is_empty() {
|
if buf.is_empty() {
|
||||||
remove_file(&path)
|
remove_file(&path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateOrAppendFileError::RemoveFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Remove(path.to_owned(), e))?;
|
||||||
} else {
|
} else {
|
||||||
file.seek(SeekFrom::Start(0))
|
file.seek(SeekFrom::Start(0))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Seek(path.to_owned(), e))?;
|
||||||
file.write_all(file_contents.as_bytes())
|
file.write_all(file_contents.as_bytes())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateOrAppendFileError::WriteFile(path.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Write(path.to_owned(), e))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum CreateOrAppendFileError {
|
|
||||||
#[error("Remove file `{0}`")]
|
|
||||||
RemoveFile(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Remove file `{0}`")]
|
|
||||||
ReadFile(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Open file `{0}`")]
|
|
||||||
OpenFile(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Write file `{0}`")]
|
|
||||||
WriteFile(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Seek file `{0}`")]
|
|
||||||
SeekFile(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Getting uid for user `{0}`")]
|
|
||||||
UserId(String, #[source] nix::errno::Errno),
|
|
||||||
#[error("Getting user `{0}`")]
|
|
||||||
NoUser(String),
|
|
||||||
#[error("Getting gid for group `{0}`")]
|
|
||||||
GroupId(String, #[source] nix::errno::Errno),
|
|
||||||
#[error("Getting group `{0}`")]
|
|
||||||
NoGroup(String),
|
|
||||||
#[error("Set mode `{0}` on `{1}`")]
|
|
||||||
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Chowning directory `{0}`")]
|
|
||||||
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::action::ActionError;
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||||
action::{Action, ActionDescription, StatefulAction},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Create an operating system level user in the given group
|
Create an operating system level user in the given group
|
||||||
|
@ -55,7 +53,7 @@ impl Action for CreateUser {
|
||||||
groupname = self.groupname,
|
groupname = self.groupname,
|
||||||
gid = self.gid,
|
gid = self.gid,
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
name,
|
name,
|
||||||
uid,
|
uid,
|
||||||
|
@ -80,7 +78,8 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null())
|
.stdin(std::process::Stdio::null())
|
||||||
.stdout(std::process::Stdio::null())
|
.stdout(std::process::Stdio::null())
|
||||||
.status()
|
.status()
|
||||||
.await?
|
.await
|
||||||
|
.map_err(ActionError::Command)?
|
||||||
.success()
|
.success()
|
||||||
{
|
{
|
||||||
()
|
()
|
||||||
|
@ -92,7 +91,7 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("/usr/bin/dscl")
|
Command::new("/usr/bin/dscl")
|
||||||
.process_group(0)
|
.process_group(0)
|
||||||
|
@ -106,7 +105,7 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("/usr/bin/dscl")
|
Command::new("/usr/bin/dscl")
|
||||||
.process_group(0)
|
.process_group(0)
|
||||||
|
@ -120,7 +119,7 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("/usr/bin/dscl")
|
Command::new("/usr/bin/dscl")
|
||||||
.process_group(0)
|
.process_group(0)
|
||||||
|
@ -134,7 +133,7 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("/usr/bin/dscl")
|
Command::new("/usr/bin/dscl")
|
||||||
.process_group(0)
|
.process_group(0)
|
||||||
|
@ -148,7 +147,7 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("/usr/bin/dscl")
|
Command::new("/usr/bin/dscl")
|
||||||
.process_group(0)
|
.process_group(0)
|
||||||
|
@ -162,7 +161,7 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("/usr/bin/dscl")
|
Command::new("/usr/bin/dscl")
|
||||||
.process_group(0)
|
.process_group(0)
|
||||||
|
@ -170,7 +169,7 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("/usr/sbin/dseditgroup")
|
Command::new("/usr/sbin/dseditgroup")
|
||||||
.process_group(0)
|
.process_group(0)
|
||||||
|
@ -183,7 +182,7 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -212,7 +211,7 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +235,7 @@ impl Action for CreateUser {
|
||||||
uid = self.uid,
|
uid = self.uid,
|
||||||
gid = self.gid,
|
gid = self.gid,
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
name,
|
name,
|
||||||
uid: _,
|
uid: _,
|
||||||
|
@ -271,16 +270,10 @@ impl Action for CreateUser {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateUserError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum CreateUserError {
|
|
||||||
#[error("Failed to execute command")]
|
|
||||||
Command(#[source] std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,12 +3,7 @@ use std::path::PathBuf;
|
||||||
use bytes::Buf;
|
use bytes::Buf;
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
|
|
||||||
use tokio::task::JoinError;
|
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
action::{Action, ActionDescription, StatefulAction},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Fetch a URL to the given path
|
Fetch a URL to the given path
|
||||||
|
@ -21,7 +16,7 @@ pub struct FetchAndUnpackNix {
|
||||||
|
|
||||||
impl FetchAndUnpackNix {
|
impl FetchAndUnpackNix {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(url: Url, dest: PathBuf) -> Result<StatefulAction<Self>, FetchUrlError> {
|
pub async fn plan(url: Url, dest: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
// TODO(@hoverbear): Check URL exists?
|
// TODO(@hoverbear): Check URL exists?
|
||||||
// TODO(@hoverbear): Check tempdir exists
|
// TODO(@hoverbear): Check tempdir exists
|
||||||
|
|
||||||
|
@ -44,16 +39,16 @@ impl Action for FetchAndUnpackNix {
|
||||||
url = %self.url,
|
url = %self.url,
|
||||||
dest = %self.dest.display(),
|
dest = %self.dest.display(),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { url, dest } = self;
|
let Self { url, dest } = self;
|
||||||
|
|
||||||
let res = reqwest::get(url.clone())
|
let res = reqwest::get(url.clone())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| FetchUrlError::Reqwest(e).boxed())?;
|
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?;
|
||||||
let bytes = res
|
let bytes = res
|
||||||
.bytes()
|
.bytes()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| FetchUrlError::Reqwest(e).boxed())?;
|
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Reqwest(e))))?;
|
||||||
// TODO(@Hoverbear): Pick directory
|
// TODO(@Hoverbear): Pick directory
|
||||||
tracing::trace!("Unpacking tar.xz");
|
tracing::trace!("Unpacking tar.xz");
|
||||||
let dest_clone = dest.clone();
|
let dest_clone = dest.clone();
|
||||||
|
@ -62,7 +57,7 @@ impl Action for FetchAndUnpackNix {
|
||||||
let mut archive = tar::Archive::new(decoder);
|
let mut archive = tar::Archive::new(decoder);
|
||||||
archive
|
archive
|
||||||
.unpack(&dest_clone)
|
.unpack(&dest_clone)
|
||||||
.map_err(|e| FetchUrlError::Unarchive(e).boxed())?;
|
.map_err(|e| ActionError::Custom(Box::new(FetchUrlError::Unarchive(e))))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -75,7 +70,7 @@ impl Action for FetchAndUnpackNix {
|
||||||
url = %self.url,
|
url = %self.url,
|
||||||
dest = %self.dest.display(),
|
dest = %self.dest.display(),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { url: _, dest: _ } = self;
|
let Self { url: _, dest: _ } = self;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -84,12 +79,6 @@ impl Action for FetchAndUnpackNix {
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum FetchUrlError {
|
pub enum FetchUrlError {
|
||||||
#[error("Joining spawned async task")]
|
|
||||||
Join(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
JoinError,
|
|
||||||
),
|
|
||||||
#[error("Request error")]
|
#[error("Request error")]
|
||||||
Reqwest(
|
Reqwest(
|
||||||
#[from]
|
#[from]
|
||||||
|
|
|
@ -9,11 +9,11 @@ mod fetch_and_unpack_nix;
|
||||||
mod move_unpacked_nix;
|
mod move_unpacked_nix;
|
||||||
mod setup_default_profile;
|
mod setup_default_profile;
|
||||||
|
|
||||||
pub use create_directory::{CreateDirectory, CreateDirectoryError};
|
pub use create_directory::CreateDirectory;
|
||||||
pub use create_file::{CreateFile, CreateFileError};
|
pub use create_file::CreateFile;
|
||||||
pub use create_group::{CreateGroup, CreateGroupError};
|
pub use create_group::CreateGroup;
|
||||||
pub use create_or_append_file::{CreateOrAppendFile, CreateOrAppendFileError};
|
pub use create_or_append_file::CreateOrAppendFile;
|
||||||
pub use create_user::{CreateUser, CreateUserError};
|
pub use create_user::CreateUser;
|
||||||
pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError};
|
pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError};
|
||||||
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
|
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
|
||||||
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};
|
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||||
action::{Action, ActionDescription, StatefulAction},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
const DEST: &str = "/nix/store";
|
const DEST: &str = "/nix/store";
|
||||||
|
|
||||||
|
@ -17,7 +14,7 @@ pub struct MoveUnpackedNix {
|
||||||
|
|
||||||
impl MoveUnpackedNix {
|
impl MoveUnpackedNix {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(src: PathBuf) -> Result<StatefulAction<Self>, MoveUnpackedNixError> {
|
pub async fn plan(src: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
// Note: Do NOT try to check for the src/dest since the installer creates those
|
// Note: Do NOT try to check for the src/dest since the installer creates those
|
||||||
Ok(Self { src }.into())
|
Ok(Self { src }.into())
|
||||||
}
|
}
|
||||||
|
@ -44,14 +41,14 @@ impl Action for MoveUnpackedNix {
|
||||||
src = %self.src.display(),
|
src = %self.src.display(),
|
||||||
dest = DEST,
|
dest = DEST,
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { src } = self;
|
let Self { src } = self;
|
||||||
|
|
||||||
// TODO(@Hoverbear): I would like to make this less awful
|
// TODO(@Hoverbear): I would like to make this less awful
|
||||||
let found_nix_paths = glob::glob(&format!("{}/nix-*", src.display()))
|
let found_nix_paths = glob::glob(&format!("{}/nix-*", src.display()))
|
||||||
.map_err(|e| e.boxed())?
|
.map_err(|e| ActionError::Custom(Box::new(e)))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|e| e.boxed())?;
|
.map_err(|e| ActionError::Custom(Box::new(e)))?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
found_nix_paths.len(),
|
found_nix_paths.len(),
|
||||||
1,
|
1,
|
||||||
|
@ -63,13 +60,11 @@ impl Action for MoveUnpackedNix {
|
||||||
tracing::trace!(src = %src_store.display(), dest = %dest.display(), "Renaming");
|
tracing::trace!(src = %src_store.display(), dest = %dest.display(), "Renaming");
|
||||||
tokio::fs::rename(src_store.clone(), dest)
|
tokio::fs::rename(src_store.clone(), dest)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| ActionError::Rename(src_store.clone(), dest.to_owned(), e))?;
|
||||||
MoveUnpackedNixError::Rename(src_store.clone(), dest.to_owned(), e).boxed()
|
|
||||||
})?;
|
|
||||||
|
|
||||||
tokio::fs::remove_dir_all(src)
|
tokio::fs::remove_dir_all(src)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| MoveUnpackedNixError::Rename(src_store, dest.to_owned(), e).boxed())?;
|
.map_err(|e| ActionError::Rename(src_store, dest.to_owned(), e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -82,7 +77,7 @@ impl Action for MoveUnpackedNix {
|
||||||
src = %self.src.display(),
|
src = %self.src.display(),
|
||||||
dest = DEST,
|
dest = DEST,
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
// Noop
|
// Noop
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -102,10 +97,4 @@ pub enum MoveUnpackedNixError {
|
||||||
#[source]
|
#[source]
|
||||||
glob::GlobError,
|
glob::GlobError,
|
||||||
),
|
),
|
||||||
#[error("Rename `{0}` to `{1}`")]
|
|
||||||
Rename(
|
|
||||||
std::path::PathBuf,
|
|
||||||
std::path::PathBuf,
|
|
||||||
#[source] std::io::Error,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::{action::StatefulAction, execute_command, set_env, BoxableError};
|
use crate::{
|
||||||
|
action::{ActionError, StatefulAction},
|
||||||
|
execute_command, set_env,
|
||||||
|
};
|
||||||
|
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
|
|
||||||
|
@ -16,9 +19,7 @@ pub struct SetupDefaultProfile {
|
||||||
|
|
||||||
impl SetupDefaultProfile {
|
impl SetupDefaultProfile {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(channels: Vec<String>) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
channels: Vec<String>,
|
|
||||||
) -> Result<StatefulAction<Self>, SetupDefaultProfileError> {
|
|
||||||
Ok(Self { channels }.into())
|
Ok(Self { channels }.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,15 +38,15 @@ impl Action for SetupDefaultProfile {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
channels = %self.channels.join(","),
|
channels = %self.channels.join(","),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { channels } = self;
|
let Self { channels } = self;
|
||||||
|
|
||||||
// Find an `nix` package
|
// Find an `nix` package
|
||||||
let nix_pkg_glob = "/nix/store/*-nix-*";
|
let nix_pkg_glob = "/nix/store/*-nix-*";
|
||||||
let mut found_nix_pkg = None;
|
let mut found_nix_pkg = None;
|
||||||
for entry in
|
for entry in glob(nix_pkg_glob).map_err(|e| {
|
||||||
glob(nix_pkg_glob).map_err(|e| SetupDefaultProfileError::GlobPatternError(e).boxed())?
|
ActionError::Custom(Box::new(SetupDefaultProfileError::GlobPatternError(e)))
|
||||||
{
|
})? {
|
||||||
match entry {
|
match entry {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
// TODO(@Hoverbear): Should probably ensure is unique
|
// TODO(@Hoverbear): Should probably ensure is unique
|
||||||
|
@ -58,15 +59,17 @@ impl Action for SetupDefaultProfile {
|
||||||
let nix_pkg = if let Some(nix_pkg) = found_nix_pkg {
|
let nix_pkg = if let Some(nix_pkg) = found_nix_pkg {
|
||||||
nix_pkg
|
nix_pkg
|
||||||
} else {
|
} else {
|
||||||
return Err(Box::new(SetupDefaultProfileError::NoNssCacert)); // TODO(@hoverbear): Fix this error
|
return Err(ActionError::Custom(Box::new(
|
||||||
|
SetupDefaultProfileError::NoNix,
|
||||||
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Find an `nss-cacert` package, add it too.
|
// Find an `nss-cacert` package, add it too.
|
||||||
let nss_ca_cert_pkg_glob = "/nix/store/*-nss-cacert-*";
|
let nss_ca_cert_pkg_glob = "/nix/store/*-nss-cacert-*";
|
||||||
let mut found_nss_ca_cert_pkg = None;
|
let mut found_nss_ca_cert_pkg = None;
|
||||||
for entry in glob(nss_ca_cert_pkg_glob)
|
for entry in glob(nss_ca_cert_pkg_glob).map_err(|e| {
|
||||||
.map_err(|e| SetupDefaultProfileError::GlobPatternError(e).boxed())?
|
ActionError::Custom(Box::new(SetupDefaultProfileError::GlobPatternError(e)))
|
||||||
{
|
})? {
|
||||||
match entry {
|
match entry {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
// TODO(@Hoverbear): Should probably ensure is unique
|
// TODO(@Hoverbear): Should probably ensure is unique
|
||||||
|
@ -79,7 +82,9 @@ impl Action for SetupDefaultProfile {
|
||||||
let nss_ca_cert_pkg = if let Some(nss_ca_cert_pkg) = found_nss_ca_cert_pkg {
|
let nss_ca_cert_pkg = if let Some(nss_ca_cert_pkg) = found_nss_ca_cert_pkg {
|
||||||
nss_ca_cert_pkg
|
nss_ca_cert_pkg
|
||||||
} else {
|
} else {
|
||||||
return Err(Box::new(SetupDefaultProfileError::NoNssCacert));
|
return Err(ActionError::Custom(Box::new(
|
||||||
|
SetupDefaultProfileError::NoNssCacert,
|
||||||
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Install `nix` itself into the store
|
// Install `nix` itself into the store
|
||||||
|
@ -93,7 +98,9 @@ impl Action for SetupDefaultProfile {
|
||||||
.stdin(std::process::Stdio::null())
|
.stdin(std::process::Stdio::null())
|
||||||
.env(
|
.env(
|
||||||
"HOME",
|
"HOME",
|
||||||
dirs::home_dir().ok_or_else(|| SetupDefaultProfileError::NoRootHome.boxed())?,
|
dirs::home_dir().ok_or_else(|| {
|
||||||
|
ActionError::Custom(Box::new(SetupDefaultProfileError::NoRootHome))
|
||||||
|
})?,
|
||||||
)
|
)
|
||||||
.env(
|
.env(
|
||||||
"NIX_SSL_CERT_FILE",
|
"NIX_SSL_CERT_FILE",
|
||||||
|
@ -101,7 +108,7 @@ impl Action for SetupDefaultProfile {
|
||||||
), /* This is apparently load bearing... */
|
), /* This is apparently load bearing... */
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| SetupDefaultProfileError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
|
|
||||||
// Install `nss-cacert` into the store
|
// Install `nss-cacert` into the store
|
||||||
// execute_command(
|
// execute_command(
|
||||||
|
@ -136,7 +143,7 @@ impl Action for SetupDefaultProfile {
|
||||||
|
|
||||||
execute_command(&mut command)
|
execute_command(&mut command)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| SetupDefaultProfileError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -152,7 +159,7 @@ impl Action for SetupDefaultProfile {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
channels = %self.channels.join(","),
|
channels = %self.channels.join(","),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
std::env::remove_var("NIX_SSL_CERT_FILE");
|
std::env::remove_var("NIX_SSL_CERT_FILE");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -175,8 +182,8 @@ pub enum SetupDefaultProfileError {
|
||||||
),
|
),
|
||||||
#[error("Unarchived Nix store did not appear to include a `nss-cacert` location")]
|
#[error("Unarchived Nix store did not appear to include a `nss-cacert` location")]
|
||||||
NoNssCacert,
|
NoNssCacert,
|
||||||
#[error("Failed to execute command")]
|
#[error("Unarchived Nix store did not appear to include a `nix` location")]
|
||||||
Command(#[source] std::io::Error),
|
NoNix,
|
||||||
#[error("No root home found to place channel configuration in")]
|
#[error("No root home found to place channel configuration in")]
|
||||||
NoRootHome,
|
NoRootHome,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,10 @@ use crate::{
|
||||||
base::SetupDefaultProfile,
|
base::SetupDefaultProfile,
|
||||||
common::{ConfigureShellProfile, PlaceChannelConfiguration, PlaceNixConfiguration},
|
common::{ConfigureShellProfile, PlaceChannelConfiguration, PlaceNixConfiguration},
|
||||||
linux::ConfigureNixDaemonService,
|
linux::ConfigureNixDaemonService,
|
||||||
Action, ActionDescription, StatefulAction,
|
Action, ActionDescription, ActionError, StatefulAction,
|
||||||
},
|
},
|
||||||
channel_value::ChannelValue,
|
channel_value::ChannelValue,
|
||||||
settings::CommonSettings,
|
settings::CommonSettings,
|
||||||
BoxableError,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
|
@ -26,9 +25,7 @@ pub struct ConfigureNix {
|
||||||
|
|
||||||
impl ConfigureNix {
|
impl ConfigureNix {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
settings: &CommonSettings,
|
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
let channels: Vec<(String, Url)> = settings
|
let channels: Vec<(String, Url)> = settings
|
||||||
.channels
|
.channels
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -36,9 +33,7 @@ impl ConfigureNix {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let setup_default_profile =
|
let setup_default_profile =
|
||||||
SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect())
|
SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect()).await?;
|
||||||
.await
|
|
||||||
.map_err(|e| e.boxed())?;
|
|
||||||
|
|
||||||
let configure_shell_profile = if settings.modify_profile {
|
let configure_shell_profile = if settings.modify_profile {
|
||||||
Some(ConfigureShellProfile::plan().await?)
|
Some(ConfigureShellProfile::plan().await?)
|
||||||
|
@ -93,7 +88,7 @@ impl Action for ConfigureNix {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
setup_default_profile,
|
setup_default_profile,
|
||||||
configure_nix_daemon_service,
|
configure_nix_daemon_service,
|
||||||
|
@ -143,7 +138,7 @@ impl Action for ConfigureNix {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
setup_default_profile,
|
setup_default_profile,
|
||||||
configure_nix_daemon_service,
|
configure_nix_daemon_service,
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::action::base::{CreateOrAppendFile, CreateOrAppendFileError};
|
use crate::action::base::CreateOrAppendFile;
|
||||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||||
use crate::BoxableError;
|
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tokio::task::{JoinError, JoinSet};
|
use tokio::task::JoinSet;
|
||||||
|
|
||||||
const PROFILE_TARGETS: &[&str] = &[
|
const PROFILE_TARGETS: &[&str] = &[
|
||||||
"/etc/bashrc",
|
"/etc/bashrc",
|
||||||
|
@ -25,7 +24,7 @@ pub struct ConfigureShellProfile {
|
||||||
|
|
||||||
impl ConfigureShellProfile {
|
impl ConfigureShellProfile {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let mut create_or_append_files = Vec::default();
|
let mut create_or_append_files = Vec::default();
|
||||||
for profile_target in PROFILE_TARGETS {
|
for profile_target in PROFILE_TARGETS {
|
||||||
let path = Path::new(profile_target);
|
let path = Path::new(profile_target);
|
||||||
|
@ -42,11 +41,8 @@ impl ConfigureShellProfile {
|
||||||
# End Nix\n
|
# End Nix\n
|
||||||
\n",
|
\n",
|
||||||
);
|
);
|
||||||
create_or_append_files.push(
|
create_or_append_files
|
||||||
CreateOrAppendFile::plan(path, None, None, 0o0644, buf)
|
.push(CreateOrAppendFile::plan(path, None, None, 0o0644, buf).await?);
|
||||||
.await
|
|
||||||
.map_err(|e| e.boxed())?,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -71,7 +67,7 @@ impl Action for ConfigureShellProfile {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
create_or_append_files,
|
create_or_append_files,
|
||||||
} = self;
|
} = self;
|
||||||
|
@ -83,10 +79,7 @@ impl Action for ConfigureShellProfile {
|
||||||
let mut create_or_append_file_clone = create_or_append_file.clone();
|
let mut create_or_append_file_clone = create_or_append_file.clone();
|
||||||
let _abort_handle = set.spawn(async move {
|
let _abort_handle = set.spawn(async move {
|
||||||
create_or_append_file_clone.try_execute().await?;
|
create_or_append_file_clone.try_execute().await?;
|
||||||
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((
|
Result::<_, ActionError>::Ok((idx, create_or_append_file_clone))
|
||||||
idx,
|
|
||||||
create_or_append_file_clone,
|
|
||||||
))
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,8 +88,8 @@ impl Action for ConfigureShellProfile {
|
||||||
Ok(Ok((idx, create_or_append_file))) => {
|
Ok(Ok((idx, create_or_append_file))) => {
|
||||||
create_or_append_files[idx] = create_or_append_file
|
create_or_append_files[idx] = create_or_append_file
|
||||||
},
|
},
|
||||||
Ok(Err(e)) => errors.push(e),
|
Ok(Err(e)) => errors.push(Box::new(e)),
|
||||||
Err(e) => return Err(e.boxed()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +97,7 @@ impl Action for ConfigureShellProfile {
|
||||||
if errors.len() == 1 {
|
if errors.len() == 1 {
|
||||||
return Err(errors.into_iter().next().unwrap().into());
|
return Err(errors.into_iter().next().unwrap().into());
|
||||||
} else {
|
} else {
|
||||||
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed());
|
return Err(ActionError::Children(errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,22 +112,19 @@ impl Action for ConfigureShellProfile {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
create_or_append_files,
|
create_or_append_files,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let mut set = JoinSet::new();
|
let mut set = JoinSet::new();
|
||||||
let mut errors = Vec::default();
|
let mut errors: Vec<Box<ActionError>> = Vec::default();
|
||||||
|
|
||||||
for (idx, create_or_append_file) in create_or_append_files.iter().enumerate() {
|
for (idx, create_or_append_file) in create_or_append_files.iter().enumerate() {
|
||||||
let mut create_or_append_file_clone = create_or_append_file.clone();
|
let mut create_or_append_file_clone = create_or_append_file.clone();
|
||||||
let _abort_handle = set.spawn(async move {
|
let _abort_handle = set.spawn(async move {
|
||||||
create_or_append_file_clone.try_revert().await?;
|
create_or_append_file_clone.try_revert().await?;
|
||||||
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((
|
Result::<_, _>::Ok((idx, create_or_append_file_clone))
|
||||||
idx,
|
|
||||||
create_or_append_file_clone,
|
|
||||||
))
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,8 +133,8 @@ impl Action for ConfigureShellProfile {
|
||||||
Ok(Ok((idx, create_or_append_file))) => {
|
Ok(Ok((idx, create_or_append_file))) => {
|
||||||
create_or_append_files[idx] = create_or_append_file
|
create_or_append_files[idx] = create_or_append_file
|
||||||
},
|
},
|
||||||
Ok(Err(e)) => errors.push(e),
|
Ok(Err(e)) => errors.push(Box::new(e)),
|
||||||
Err(e) => return Err(e.boxed()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,34 +142,10 @@ impl Action for ConfigureShellProfile {
|
||||||
if errors.len() == 1 {
|
if errors.len() == 1 {
|
||||||
return Err(errors.into_iter().next().unwrap().into());
|
return Err(errors.into_iter().next().unwrap().into());
|
||||||
} else {
|
} else {
|
||||||
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed());
|
return Err(ActionError::Children(errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum ConfigureShellProfileError {
|
|
||||||
#[error("Creating or appending to file")]
|
|
||||||
CreateOrAppendFile(
|
|
||||||
#[from]
|
|
||||||
#[source]
|
|
||||||
CreateOrAppendFileError,
|
|
||||||
),
|
|
||||||
#[error("Multiple errors: {}", .0.iter().map(|v| {
|
|
||||||
if let Some(source) = v.source() {
|
|
||||||
format!("{v} ({source})")
|
|
||||||
} else {
|
|
||||||
format!("{v}")
|
|
||||||
}
|
|
||||||
}).collect::<Vec<_>>().join(" & "))]
|
|
||||||
MultipleCreateOrAppendFile(Vec<Box<dyn std::error::Error + Send + Sync>>),
|
|
||||||
#[error("Joining spawned async task")]
|
|
||||||
Join(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
JoinError,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::action::base::{CreateDirectory, CreateDirectoryError};
|
use crate::action::base::CreateDirectory;
|
||||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||||
|
|
||||||
const PATHS: &[&str] = &[
|
const PATHS: &[&str] = &[
|
||||||
"/nix/var",
|
"/nix/var",
|
||||||
|
@ -27,7 +27,7 @@ pub struct CreateNixTree {
|
||||||
|
|
||||||
impl CreateNixTree {
|
impl CreateNixTree {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let mut create_directories = Vec::default();
|
let mut create_directories = Vec::default();
|
||||||
for path in PATHS {
|
for path in PATHS {
|
||||||
// We use `create_dir` over `create_dir_all` to ensure we always set permissions right
|
// We use `create_dir` over `create_dir_all` to ensure we always set permissions right
|
||||||
|
@ -65,7 +65,7 @@ impl Action for CreateNixTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { create_directories } = self;
|
let Self { create_directories } = self;
|
||||||
|
|
||||||
// Just do sequential since parallelizing this will have little benefit
|
// Just do sequential since parallelizing this will have little benefit
|
||||||
|
@ -97,7 +97,7 @@ impl Action for CreateNixTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { create_directories } = self;
|
let Self { create_directories } = self;
|
||||||
|
|
||||||
// Just do sequential since parallelizing this will have little benefit
|
// Just do sequential since parallelizing this will have little benefit
|
||||||
|
@ -108,13 +108,3 @@ impl Action for CreateNixTree {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum CreateNixTreeError {
|
|
||||||
#[error("Creating directory")]
|
|
||||||
CreateDirectory(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
CreateDirectoryError,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
base::{CreateGroup, CreateGroupError, CreateUser, CreateUserError},
|
base::{CreateGroup, CreateUser},
|
||||||
Action, ActionDescription, StatefulAction,
|
Action, ActionDescription, ActionError, StatefulAction,
|
||||||
},
|
},
|
||||||
settings::CommonSettings,
|
settings::CommonSettings,
|
||||||
BoxableError,
|
|
||||||
};
|
};
|
||||||
use tokio::task::{JoinError, JoinSet};
|
use tokio::task::JoinSet;
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateUsersAndGroups {
|
pub struct CreateUsersAndGroups {
|
||||||
|
@ -21,9 +20,7 @@ pub struct CreateUsersAndGroups {
|
||||||
|
|
||||||
impl CreateUsersAndGroups {
|
impl CreateUsersAndGroups {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(settings: CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
settings: CommonSettings,
|
|
||||||
) -> Result<StatefulAction<Self>, CreateUsersAndGroupsError> {
|
|
||||||
// TODO(@hoverbear): CHeck if it exist, error if so
|
// TODO(@hoverbear): CHeck if it exist, error if so
|
||||||
let create_group = CreateGroup::plan(
|
let create_group = CreateGroup::plan(
|
||||||
settings.nix_build_group_name.clone(),
|
settings.nix_build_group_name.clone(),
|
||||||
|
@ -101,7 +98,7 @@ impl Action for CreateUsersAndGroups {
|
||||||
nix_build_user_prefix = self.nix_build_user_prefix,
|
nix_build_user_prefix = self.nix_build_user_prefix,
|
||||||
nix_build_user_id_base = self.nix_build_user_id_base,
|
nix_build_user_id_base = self.nix_build_user_id_base,
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
create_users,
|
create_users,
|
||||||
create_group,
|
create_group,
|
||||||
|
@ -130,7 +127,7 @@ impl Action for CreateUsersAndGroups {
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let mut set = JoinSet::new();
|
let mut set = JoinSet::new();
|
||||||
let mut errors: Vec<Box<dyn std::error::Error + Send + Sync>> = Vec::new();
|
let mut errors: Vec<Box<ActionError>> = Vec::new();
|
||||||
for (idx, create_user) in create_users.iter_mut().enumerate() {
|
for (idx, create_user) in create_users.iter_mut().enumerate() {
|
||||||
let mut create_user_clone = create_user.clone();
|
let mut create_user_clone = create_user.clone();
|
||||||
let _abort_handle = set.spawn(async move {
|
let _abort_handle = set.spawn(async move {
|
||||||
|
@ -142,8 +139,8 @@ impl Action for CreateUsersAndGroups {
|
||||||
while let Some(result) = set.join_next().await {
|
while let Some(result) = set.join_next().await {
|
||||||
match result {
|
match result {
|
||||||
Ok(Ok((idx, success))) => create_users[idx] = success,
|
Ok(Ok((idx, success))) => create_users[idx] = success,
|
||||||
Ok(Err(e)) => errors.push(e),
|
Ok(Err(e)) => errors.push(Box::new(e)),
|
||||||
Err(e) => return Err(e)?,
|
Err(e) => return Err(ActionError::Join(e))?,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +148,7 @@ impl Action for CreateUsersAndGroups {
|
||||||
if errors.len() == 1 {
|
if errors.len() == 1 {
|
||||||
return Err(errors.into_iter().next().unwrap().into());
|
return Err(errors.into_iter().next().unwrap().into());
|
||||||
} else {
|
} else {
|
||||||
return Err(CreateUsersAndGroupsError::CreateUsers(errors).boxed());
|
return Err(ActionError::Children(errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -198,7 +195,7 @@ impl Action for CreateUsersAndGroups {
|
||||||
nix_build_user_prefix = self.nix_build_user_prefix,
|
nix_build_user_prefix = self.nix_build_user_prefix,
|
||||||
nix_build_user_id_base = self.nix_build_user_id_base,
|
nix_build_user_id_base = self.nix_build_user_id_base,
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
create_users,
|
create_users,
|
||||||
create_group,
|
create_group,
|
||||||
|
@ -216,15 +213,15 @@ impl Action for CreateUsersAndGroups {
|
||||||
let mut create_user_clone = create_user.clone();
|
let mut create_user_clone = create_user.clone();
|
||||||
let _abort_handle = set.spawn(async move {
|
let _abort_handle = set.spawn(async move {
|
||||||
create_user_clone.try_revert().await?;
|
create_user_clone.try_revert().await?;
|
||||||
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((idx, create_user_clone))
|
Result::<_, ActionError>::Ok((idx, create_user_clone))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(result) = set.join_next().await {
|
while let Some(result) = set.join_next().await {
|
||||||
match result {
|
match result {
|
||||||
Ok(Ok((idx, success))) => create_users[idx] = success,
|
Ok(Ok((idx, success))) => create_users[idx] = success,
|
||||||
Ok(Err(e)) => errors.push(e),
|
Ok(Err(e)) => errors.push(Box::new(e)),
|
||||||
Err(e) => return Err(e.boxed())?,
|
Err(e) => return Err(ActionError::Join(e))?,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +229,7 @@ impl Action for CreateUsersAndGroups {
|
||||||
if errors.len() == 1 {
|
if errors.len() == 1 {
|
||||||
return Err(errors.into_iter().next().unwrap().into());
|
return Err(errors.into_iter().next().unwrap().into());
|
||||||
} else {
|
} else {
|
||||||
return Err(CreateUsersAndGroupsError::CreateUsers(errors).boxed());
|
return Err(ActionError::Children(errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,33 +239,3 @@ impl Action for CreateUsersAndGroups {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum CreateUsersAndGroupsError {
|
|
||||||
#[error("Creating user")]
|
|
||||||
CreateUser(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
CreateUserError,
|
|
||||||
),
|
|
||||||
#[error("Multiple errors: {}", .0.iter().map(|v| {
|
|
||||||
if let Some(source) = v.source() {
|
|
||||||
format!("{v} ({source})")
|
|
||||||
} else {
|
|
||||||
format!("{v}")
|
|
||||||
}
|
|
||||||
}).collect::<Vec<_>>().join(" & "))]
|
|
||||||
CreateUsers(Vec<Box<dyn std::error::Error + Send + Sync>>),
|
|
||||||
#[error("Creating group")]
|
|
||||||
CreateGroup(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
CreateGroupError,
|
|
||||||
),
|
|
||||||
#[error("Joining spawned async task")]
|
|
||||||
Join(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
JoinError,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ mod provision_nix;
|
||||||
|
|
||||||
pub use configure_nix::ConfigureNix;
|
pub use configure_nix::ConfigureNix;
|
||||||
pub use configure_shell_profile::ConfigureShellProfile;
|
pub use configure_shell_profile::ConfigureShellProfile;
|
||||||
pub use create_nix_tree::{CreateNixTree, CreateNixTreeError};
|
pub use create_nix_tree::CreateNixTree;
|
||||||
pub use create_users_and_groups::{CreateUsersAndGroups, CreateUsersAndGroupsError};
|
pub use create_users_and_groups::CreateUsersAndGroups;
|
||||||
pub use place_channel_configuration::{PlaceChannelConfiguration, PlaceChannelConfigurationError};
|
pub use place_channel_configuration::{PlaceChannelConfiguration, PlaceChannelConfigurationError};
|
||||||
pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationError};
|
pub use place_nix_configuration::PlaceNixConfiguration;
|
||||||
pub use provision_nix::{ProvisionNix, ProvisionNixError};
|
pub use provision_nix::ProvisionNix;
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::action::base::{CreateFile, CreateFileError};
|
use crate::action::base::CreateFile;
|
||||||
use crate::{
|
use crate::action::ActionError;
|
||||||
action::{Action, ActionDescription, StatefulAction},
|
use crate::action::{Action, ActionDescription, StatefulAction};
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,7 +17,7 @@ impl PlaceChannelConfiguration {
|
||||||
pub async fn plan(
|
pub async fn plan(
|
||||||
channels: Vec<(String, Url)>,
|
channels: Vec<(String, Url)>,
|
||||||
force: bool,
|
force: bool,
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let buf = channels
|
let buf = channels
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, url)| format!("{} {}", url, name))
|
.map(|(name, url)| format!("{} {}", url, name))
|
||||||
|
@ -27,7 +25,9 @@ impl PlaceChannelConfiguration {
|
||||||
.join("\n");
|
.join("\n");
|
||||||
let create_file = CreateFile::plan(
|
let create_file = CreateFile::plan(
|
||||||
dirs::home_dir()
|
dirs::home_dir()
|
||||||
.ok_or_else(|| PlaceChannelConfigurationError::NoRootHome.boxed())?
|
.ok_or_else(|| {
|
||||||
|
ActionError::Custom(Box::new(PlaceChannelConfigurationError::NoRootHome))
|
||||||
|
})?
|
||||||
.join(".nix-channels"),
|
.join(".nix-channels"),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
@ -61,7 +61,7 @@ impl Action for PlaceChannelConfiguration {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::<Vec<_>>().join(", "),
|
channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::<Vec<_>>().join(", "),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
create_file,
|
create_file,
|
||||||
channels: _,
|
channels: _,
|
||||||
|
@ -85,7 +85,7 @@ impl Action for PlaceChannelConfiguration {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::<Vec<_>>().join(", "),
|
channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::<Vec<_>>().join(", "),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
create_file,
|
create_file,
|
||||||
channels: _,
|
channels: _,
|
||||||
|
@ -99,12 +99,6 @@ impl Action for PlaceChannelConfiguration {
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum PlaceChannelConfigurationError {
|
pub enum PlaceChannelConfigurationError {
|
||||||
#[error("Creating file")]
|
|
||||||
CreateFile(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
CreateFileError,
|
|
||||||
),
|
|
||||||
#[error("No root home found to place channel configuration in")]
|
#[error("No root home found to place channel configuration in")]
|
||||||
NoRootHome,
|
NoRootHome,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::action::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
|
use crate::action::base::{CreateDirectory, CreateFile};
|
||||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||||
|
|
||||||
const NIX_CONF_FOLDER: &str = "/etc/nix";
|
const NIX_CONF_FOLDER: &str = "/etc/nix";
|
||||||
const NIX_CONF: &str = "/etc/nix/nix.conf";
|
const NIX_CONF: &str = "/etc/nix/nix.conf";
|
||||||
|
@ -19,7 +19,7 @@ impl PlaceNixConfiguration {
|
||||||
nix_build_group_name: String,
|
nix_build_group_name: String,
|
||||||
extra_conf: Vec<String>,
|
extra_conf: Vec<String>,
|
||||||
force: bool,
|
force: bool,
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let buf = format!(
|
let buf = format!(
|
||||||
"\
|
"\
|
||||||
{extra_conf}\n\
|
{extra_conf}\n\
|
||||||
|
@ -61,7 +61,7 @@ impl Action for PlaceNixConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
create_file,
|
create_file,
|
||||||
create_directory,
|
create_directory,
|
||||||
|
@ -84,7 +84,7 @@ impl Action for PlaceNixConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
create_file,
|
create_file,
|
||||||
create_directory,
|
create_directory,
|
||||||
|
@ -96,19 +96,3 @@ impl Action for PlaceNixConfiguration {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum PlaceNixConfigurationError {
|
|
||||||
#[error("Creating file")]
|
|
||||||
CreateFile(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
CreateFileError,
|
|
||||||
),
|
|
||||||
#[error("Creating directory")]
|
|
||||||
CreateDirectory(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
CreateDirectoryError,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
|
use super::{CreateNixTree, CreateUsersAndGroups};
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{
|
action::{
|
||||||
base::{
|
base::{FetchAndUnpackNix, MoveUnpackedNix},
|
||||||
CreateDirectoryError, FetchAndUnpackNix, FetchUrlError, MoveUnpackedNix,
|
Action, ActionDescription, ActionError, StatefulAction,
|
||||||
MoveUnpackedNixError,
|
|
||||||
},
|
|
||||||
Action, ActionDescription, StatefulAction,
|
|
||||||
},
|
},
|
||||||
settings::CommonSettings,
|
settings::CommonSettings,
|
||||||
BoxableError,
|
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tokio::task::JoinError;
|
|
||||||
|
|
||||||
use super::{CreateNixTree, CreateNixTreeError, CreateUsersAndGroups, CreateUsersAndGroupsError};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Place Nix and it's requirements onto the target
|
Place Nix and it's requirements onto the target
|
||||||
|
@ -27,22 +21,16 @@ pub struct ProvisionNix {
|
||||||
|
|
||||||
impl ProvisionNix {
|
impl ProvisionNix {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
settings: &CommonSettings,
|
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
let fetch_nix = FetchAndUnpackNix::plan(
|
let fetch_nix = FetchAndUnpackNix::plan(
|
||||||
settings.nix_package_url.clone(),
|
settings.nix_package_url.clone(),
|
||||||
PathBuf::from("/nix/temp-install-dir"),
|
PathBuf::from("/nix/temp-install-dir"),
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.map_err(|e| e.boxed())?;
|
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone()).await?;
|
||||||
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone())
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.boxed())?;
|
|
||||||
let create_nix_tree = CreateNixTree::plan().await?;
|
let create_nix_tree = CreateNixTree::plan().await?;
|
||||||
let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir"))
|
let move_unpacked_nix =
|
||||||
.await
|
MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir")).await?;
|
||||||
.map_err(|e| e.boxed())?;
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
fetch_nix,
|
fetch_nix,
|
||||||
create_users_and_group,
|
create_users_and_group,
|
||||||
|
@ -78,7 +66,7 @@ impl Action for ProvisionNix {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
fetch_nix,
|
fetch_nix,
|
||||||
create_nix_tree,
|
create_nix_tree,
|
||||||
|
@ -90,13 +78,13 @@ impl Action for ProvisionNix {
|
||||||
let mut fetch_nix_clone = fetch_nix.clone();
|
let mut fetch_nix_clone = fetch_nix.clone();
|
||||||
let fetch_nix_handle = tokio::task::spawn(async {
|
let fetch_nix_handle = tokio::task::spawn(async {
|
||||||
fetch_nix_clone.try_execute().await?;
|
fetch_nix_clone.try_execute().await?;
|
||||||
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok(fetch_nix_clone)
|
Result::<_, ActionError>::Ok(fetch_nix_clone)
|
||||||
});
|
});
|
||||||
|
|
||||||
create_users_and_group.try_execute().await?;
|
create_users_and_group.try_execute().await?;
|
||||||
create_nix_tree.try_execute().await?;
|
create_nix_tree.try_execute().await?;
|
||||||
|
|
||||||
*fetch_nix = fetch_nix_handle.await.map_err(|e| e.boxed())??;
|
*fetch_nix = fetch_nix_handle.await.map_err(ActionError::Join)??;
|
||||||
move_unpacked_nix.try_execute().await?;
|
move_unpacked_nix.try_execute().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -119,7 +107,7 @@ impl Action for ProvisionNix {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
fetch_nix,
|
fetch_nix,
|
||||||
create_nix_tree,
|
create_nix_tree,
|
||||||
|
@ -131,7 +119,7 @@ impl Action for ProvisionNix {
|
||||||
let mut fetch_nix_clone = fetch_nix.clone();
|
let mut fetch_nix_clone = fetch_nix.clone();
|
||||||
let fetch_nix_handle = tokio::task::spawn(async {
|
let fetch_nix_handle = tokio::task::spawn(async {
|
||||||
fetch_nix_clone.try_revert().await?;
|
fetch_nix_clone.try_revert().await?;
|
||||||
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok(fetch_nix_clone)
|
Result::<_, ActionError>::Ok(fetch_nix_clone)
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Err(err) = create_users_and_group.try_revert().await {
|
if let Err(err) = create_users_and_group.try_revert().await {
|
||||||
|
@ -143,49 +131,9 @@ impl Action for ProvisionNix {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
*fetch_nix = fetch_nix_handle.await.map_err(|e| e.boxed())??;
|
*fetch_nix = fetch_nix_handle.await.map_err(ActionError::Join)??;
|
||||||
move_unpacked_nix.try_revert().await?;
|
move_unpacked_nix.try_revert().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum ProvisionNixError {
|
|
||||||
#[error("Fetching Nix")]
|
|
||||||
FetchNix(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
FetchUrlError,
|
|
||||||
),
|
|
||||||
#[error("Joining spawned async task")]
|
|
||||||
Join(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
JoinError,
|
|
||||||
),
|
|
||||||
#[error("Creating directory")]
|
|
||||||
CreateDirectory(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
CreateDirectoryError,
|
|
||||||
),
|
|
||||||
#[error("Creating users and group")]
|
|
||||||
CreateUsersAndGroup(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
CreateUsersAndGroupsError,
|
|
||||||
),
|
|
||||||
#[error("Creating nix tree")]
|
|
||||||
CreateNixTree(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
CreateNixTreeError,
|
|
||||||
),
|
|
||||||
#[error("Moving unpacked nix")]
|
|
||||||
MoveUnpackedNix(
|
|
||||||
#[source]
|
|
||||||
#[from]
|
|
||||||
MoveUnpackedNixError,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,13 +2,10 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::action::StatefulAction;
|
use crate::action::{ActionError, StatefulAction};
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription};
|
||||||
action::{Action, ActionDescription},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Bootstrap and kickstart an APFS volume
|
Bootstrap and kickstart an APFS volume
|
||||||
|
@ -20,9 +17,7 @@ pub struct BootstrapApfsVolume {
|
||||||
|
|
||||||
impl BootstrapApfsVolume {
|
impl BootstrapApfsVolume {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(path: impl AsRef<Path>) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
path: impl AsRef<Path>,
|
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path: path.as_ref().to_path_buf(),
|
path: path.as_ref().to_path_buf(),
|
||||||
}
|
}
|
||||||
|
@ -44,7 +39,7 @@ impl Action for BootstrapApfsVolume {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
path = %self.path.display(),
|
path = %self.path.display(),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { path } = self;
|
let Self { path } = self;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -55,7 +50,7 @@ impl Action for BootstrapApfsVolume {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("launchctl")
|
Command::new("launchctl")
|
||||||
.process_group(0)
|
.process_group(0)
|
||||||
|
@ -63,7 +58,7 @@ impl Action for BootstrapApfsVolume {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -78,7 +73,7 @@ impl Action for BootstrapApfsVolume {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
path = %self.path.display(),
|
path = %self.path.display(),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { path } = self;
|
let Self { path } = self;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -89,7 +84,7 @@ impl Action for BootstrapApfsVolume {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,10 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::action::StatefulAction;
|
use crate::action::{ActionError, StatefulAction};
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription};
|
||||||
action::{Action, ActionDescription},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateApfsVolume {
|
pub struct CreateApfsVolume {
|
||||||
|
@ -23,7 +20,7 @@ impl CreateApfsVolume {
|
||||||
disk: impl AsRef<Path>,
|
disk: impl AsRef<Path>,
|
||||||
name: String,
|
name: String,
|
||||||
case_sensitive: bool,
|
case_sensitive: bool,
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
disk: disk.as_ref().to_path_buf(),
|
disk: disk.as_ref().to_path_buf(),
|
||||||
name,
|
name,
|
||||||
|
@ -53,7 +50,7 @@ impl Action for CreateApfsVolume {
|
||||||
name = %self.name,
|
name = %self.name,
|
||||||
case_sensitive = %self.case_sensitive,
|
case_sensitive = %self.case_sensitive,
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
disk,
|
disk,
|
||||||
name,
|
name,
|
||||||
|
@ -78,7 +75,7 @@ impl Action for CreateApfsVolume {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateVolumeError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -99,7 +96,7 @@ impl Action for CreateApfsVolume {
|
||||||
name = %self.name,
|
name = %self.name,
|
||||||
case_sensitive = %self.case_sensitive,
|
case_sensitive = %self.case_sensitive,
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
disk: _,
|
disk: _,
|
||||||
name,
|
name,
|
||||||
|
@ -113,7 +110,7 @@ impl Action for CreateApfsVolume {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateVolumeError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
use crate::{
|
use crate::action::{
|
||||||
action::{
|
base::{CreateFile, CreateOrAppendFile},
|
||||||
base::{CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError},
|
|
||||||
darwin::{
|
darwin::{
|
||||||
BootstrapApfsVolume, BootstrapVolumeError, CreateApfsVolume, CreateSyntheticObjects,
|
BootstrapApfsVolume, CreateApfsVolume, CreateSyntheticObjects, EnableOwnership,
|
||||||
CreateSyntheticObjectsError, CreateVolumeError, EnableOwnership, EnableOwnershipError,
|
EncryptApfsVolume, UnmountApfsVolume,
|
||||||
EncryptApfsVolume, EncryptVolumeError, UnmountApfsVolume, UnmountVolumeError,
|
|
||||||
},
|
},
|
||||||
Action, ActionDescription, StatefulAction,
|
Action, ActionDescription, ActionError, StatefulAction,
|
||||||
},
|
|
||||||
BoxableError,
|
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
@ -43,7 +39,7 @@ impl CreateNixVolume {
|
||||||
name: String,
|
name: String,
|
||||||
case_sensitive: bool,
|
case_sensitive: bool,
|
||||||
encrypt: bool,
|
encrypt: bool,
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let disk = disk.as_ref();
|
let disk = disk.as_ref();
|
||||||
let create_or_append_synthetic_conf = CreateOrAppendFile::plan(
|
let create_or_append_synthetic_conf = CreateOrAppendFile::plan(
|
||||||
"/etc/synthetic.conf",
|
"/etc/synthetic.conf",
|
||||||
|
@ -53,7 +49,7 @@ impl CreateNixVolume {
|
||||||
"nix\n".into(), /* The newline is required otherwise it segfaults */
|
"nix\n".into(), /* The newline is required otherwise it segfaults */
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.boxed())?;
|
.map_err(|e| ActionError::Child(Box::new(e)))?;
|
||||||
|
|
||||||
let create_synthetic_objects = CreateSyntheticObjects::plan().await?;
|
let create_synthetic_objects = CreateSyntheticObjects::plan().await?;
|
||||||
|
|
||||||
|
@ -69,7 +65,7 @@ impl CreateNixVolume {
|
||||||
format!("NAME=\"{name}\" /nix apfs rw,noauto,nobrowse,suid,owners"),
|
format!("NAME=\"{name}\" /nix apfs rw,noauto,nobrowse,suid,owners"),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.boxed())?;
|
.map_err(|e| ActionError::Child(Box::new(e)))?;
|
||||||
|
|
||||||
let encrypt_volume = if encrypt {
|
let encrypt_volume = if encrypt {
|
||||||
Some(EncryptApfsVolume::plan(disk, &name).await?)
|
Some(EncryptApfsVolume::plan(disk, &name).await?)
|
||||||
|
@ -155,7 +151,7 @@ impl Action for CreateNixVolume {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(destination,))]
|
#[tracing::instrument(skip_all, fields(destination,))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
disk: _,
|
disk: _,
|
||||||
name: _,
|
name: _,
|
||||||
|
@ -193,7 +189,7 @@ impl Action for CreateNixVolume {
|
||||||
.stdout(std::process::Stdio::null())
|
.stdout(std::process::Stdio::null())
|
||||||
.status()
|
.status()
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CreateApfsVolumeError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
if status.success() || retry_tokens == 0 {
|
if status.success() || retry_tokens == 0 {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,7 +214,7 @@ impl Action for CreateNixVolume {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields(disk, name))]
|
#[tracing::instrument(skip_all, fields(disk, name))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {
|
let Self {
|
||||||
disk: _,
|
disk: _,
|
||||||
name: _,
|
name: _,
|
||||||
|
@ -253,25 +249,3 @@ impl Action for CreateNixVolume {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum CreateApfsVolumeError {
|
|
||||||
#[error(transparent)]
|
|
||||||
CreateFile(#[from] CreateFileError),
|
|
||||||
#[error(transparent)]
|
|
||||||
DarwinBootstrapVolume(#[from] BootstrapVolumeError),
|
|
||||||
#[error(transparent)]
|
|
||||||
DarwinCreateSyntheticObjects(#[from] CreateSyntheticObjectsError),
|
|
||||||
#[error(transparent)]
|
|
||||||
DarwinCreateVolume(#[from] CreateVolumeError),
|
|
||||||
#[error(transparent)]
|
|
||||||
DarwinEnableOwnership(#[from] EnableOwnershipError),
|
|
||||||
#[error(transparent)]
|
|
||||||
DarwinEncryptVolume(#[from] EncryptVolumeError),
|
|
||||||
#[error(transparent)]
|
|
||||||
DarwinUnmountVolume(#[from] UnmountVolumeError),
|
|
||||||
#[error(transparent)]
|
|
||||||
CreateOrAppendFile(#[from] CreateOrAppendFileError),
|
|
||||||
#[error("Failed to execute command")]
|
|
||||||
Command(#[source] std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use tokio::process::Command;
|
||||||
|
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::action::{Action, ActionDescription, StatefulAction};
|
use crate::action::{Action, ActionDescription, ActionError, StatefulAction};
|
||||||
|
|
||||||
/// Create the synthetic objects defined in `/etc/syntethic.conf`
|
/// Create the synthetic objects defined in `/etc/syntethic.conf`
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
@ -10,7 +10,7 @@ pub struct CreateSyntheticObjects;
|
||||||
|
|
||||||
impl CreateSyntheticObjects {
|
impl CreateSyntheticObjects {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||||
Ok(Self.into())
|
Ok(Self.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ impl Action for CreateSyntheticObjects {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields())]
|
#[tracing::instrument(skip_all, fields())]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
// Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261
|
// Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
|
Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
|
||||||
|
@ -60,7 +60,7 @@ impl Action for CreateSyntheticObjects {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, fields())]
|
#[tracing::instrument(skip_all, fields())]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
// Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261
|
// Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
|
Command::new("/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util")
|
||||||
|
|
|
@ -3,14 +3,11 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::action::StatefulAction;
|
use crate::action::{ActionError, StatefulAction};
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
|
use crate::action::{Action, ActionDescription};
|
||||||
use crate::os::darwin::DiskUtilOutput;
|
use crate::os::darwin::DiskUtilOutput;
|
||||||
use crate::{
|
|
||||||
action::{Action, ActionDescription},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Enable ownership on a volume
|
Enable ownership on a volume
|
||||||
|
@ -22,9 +19,7 @@ pub struct EnableOwnership {
|
||||||
|
|
||||||
impl EnableOwnership {
|
impl EnableOwnership {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(path: impl AsRef<Path>) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
path: impl AsRef<Path>,
|
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
path: path.as_ref().to_path_buf(),
|
path: path.as_ref().to_path_buf(),
|
||||||
}
|
}
|
||||||
|
@ -46,7 +41,7 @@ impl Action for EnableOwnership {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
path = %self.path.display(),
|
path = %self.path.display(),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { path } = self;
|
let Self { path } = self;
|
||||||
|
|
||||||
let should_enable_ownership = {
|
let should_enable_ownership = {
|
||||||
|
@ -57,7 +52,8 @@ impl Action for EnableOwnership {
|
||||||
.arg(&path)
|
.arg(&path)
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await?
|
.await
|
||||||
|
.map_err(ActionError::Command)?
|
||||||
.stdout;
|
.stdout;
|
||||||
let the_plist: DiskUtilOutput = plist::from_reader(Cursor::new(buf)).unwrap();
|
let the_plist: DiskUtilOutput = plist::from_reader(Cursor::new(buf)).unwrap();
|
||||||
|
|
||||||
|
@ -73,7 +69,7 @@ impl Action for EnableOwnership {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| EnableOwnershipError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -86,7 +82,7 @@ impl Action for EnableOwnership {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
path = %self.path.display(),
|
path = %self.path.display(),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
// noop
|
// noop
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
action::{darwin::NIX_VOLUME_MOUNTD_DEST, Action, ActionDescription, StatefulAction},
|
action::{
|
||||||
|
darwin::NIX_VOLUME_MOUNTD_DEST, Action, ActionDescription, ActionError, StatefulAction,
|
||||||
|
},
|
||||||
execute_command,
|
execute_command,
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
@ -20,7 +22,7 @@ impl EncryptApfsVolume {
|
||||||
pub async fn plan(
|
pub async fn plan(
|
||||||
disk: impl AsRef<Path>,
|
disk: impl AsRef<Path>,
|
||||||
name: impl AsRef<str>,
|
name: impl AsRef<str>,
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let name = name.as_ref().to_owned();
|
let name = name.as_ref().to_owned();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name,
|
name,
|
||||||
|
@ -48,7 +50,7 @@ impl Action for EncryptApfsVolume {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
disk = %self.disk.display(),
|
disk = %self.disk.display(),
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { disk, name } = self;
|
let Self { disk, name } = self;
|
||||||
|
|
||||||
// Generate a random password.
|
// Generate a random password.
|
||||||
|
@ -69,7 +71,9 @@ impl Action for EncryptApfsVolume {
|
||||||
|
|
||||||
let disk_str = disk.to_str().expect("Could not turn disk into string"); /* Should not reasonably ever fail */
|
let disk_str = disk.to_str().expect("Could not turn disk into string"); /* Should not reasonably ever fail */
|
||||||
|
|
||||||
execute_command(Command::new("/usr/sbin/diskutil").arg("mount").arg(&name)).await?;
|
execute_command(Command::new("/usr/sbin/diskutil").arg("mount").arg(&name))
|
||||||
|
.await
|
||||||
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
// Add the password to the user keychain so they can unlock it later.
|
// Add the password to the user keychain so they can unlock it later.
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -99,7 +103,8 @@ impl Action for EncryptApfsVolume {
|
||||||
"/Library/Keychains/System.keychain",
|
"/Library/Keychains/System.keychain",
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
// Encrypt the mounted volume
|
// Encrypt the mounted volume
|
||||||
execute_command(Command::new("/usr/sbin/diskutil").process_group(0).args([
|
execute_command(Command::new("/usr/sbin/diskutil").process_group(0).args([
|
||||||
|
@ -111,7 +116,8 @@ impl Action for EncryptApfsVolume {
|
||||||
"-passphrase",
|
"-passphrase",
|
||||||
password.as_str(),
|
password.as_str(),
|
||||||
]))
|
]))
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("/usr/sbin/diskutil")
|
Command::new("/usr/sbin/diskutil")
|
||||||
|
@ -120,7 +126,8 @@ impl Action for EncryptApfsVolume {
|
||||||
.arg("force")
|
.arg("force")
|
||||||
.arg(&name),
|
.arg(&name),
|
||||||
)
|
)
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -138,7 +145,7 @@ impl Action for EncryptApfsVolume {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
disk = %self.disk.display(),
|
disk = %self.disk.display(),
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { disk, name } = self;
|
let Self { disk, name } = self;
|
||||||
|
|
||||||
let disk_str = disk.to_str().expect("Could not turn disk into string"); /* Should not reasonably ever fail */
|
let disk_str = disk.to_str().expect("Could not turn disk into string"); /* Should not reasonably ever fail */
|
||||||
|
@ -162,14 +169,9 @@ impl Action for EncryptApfsVolume {
|
||||||
.as_str(),
|
.as_str(),
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum EncryptVolumeError {
|
|
||||||
#[error("Failed to execute command")]
|
|
||||||
Command(#[source] std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::action::StatefulAction;
|
use crate::action::{ActionError, StatefulAction};
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription};
|
||||||
action::{Action, ActionDescription},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Kickstart a `launchctl` service
|
Kickstart a `launchctl` service
|
||||||
|
@ -18,9 +15,7 @@ pub struct KickstartLaunchctlService {
|
||||||
|
|
||||||
impl KickstartLaunchctlService {
|
impl KickstartLaunchctlService {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(unit: String) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
unit: String,
|
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
Ok(Self { unit }.into())
|
Ok(Self { unit }.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +35,7 @@ impl Action for KickstartLaunchctlService {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
unit = %self.unit,
|
unit = %self.unit,
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { unit } = self;
|
let Self { unit } = self;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -52,7 +47,7 @@ impl Action for KickstartLaunchctlService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| KickstartLaunchctlServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -64,14 +59,8 @@ impl Action for KickstartLaunchctlService {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
unit = %self.unit,
|
unit = %self.unit,
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
// noop
|
// noop
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum KickstartLaunchctlServiceError {
|
|
||||||
#[error("Failed to execute command")]
|
|
||||||
Command(#[source] std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,9 +12,9 @@ mod unmount_apfs_volume;
|
||||||
|
|
||||||
pub use bootstrap_apfs_volume::{BootstrapApfsVolume, BootstrapVolumeError};
|
pub use bootstrap_apfs_volume::{BootstrapApfsVolume, BootstrapVolumeError};
|
||||||
pub use create_apfs_volume::{CreateApfsVolume, CreateVolumeError};
|
pub use create_apfs_volume::{CreateApfsVolume, CreateVolumeError};
|
||||||
pub use create_nix_volume::{CreateApfsVolumeError, CreateNixVolume, NIX_VOLUME_MOUNTD_DEST};
|
pub use create_nix_volume::{CreateNixVolume, NIX_VOLUME_MOUNTD_DEST};
|
||||||
pub use create_synthetic_objects::{CreateSyntheticObjects, CreateSyntheticObjectsError};
|
pub use create_synthetic_objects::{CreateSyntheticObjects, CreateSyntheticObjectsError};
|
||||||
pub use enable_ownership::{EnableOwnership, EnableOwnershipError};
|
pub use enable_ownership::{EnableOwnership, EnableOwnershipError};
|
||||||
pub use encrypt_apfs_volume::{EncryptApfsVolume, EncryptVolumeError};
|
pub use encrypt_apfs_volume::EncryptApfsVolume;
|
||||||
pub use kickstart_launchctl_service::{KickstartLaunchctlService, KickstartLaunchctlServiceError};
|
pub use kickstart_launchctl_service::KickstartLaunchctlService;
|
||||||
pub use unmount_apfs_volume::{UnmountApfsVolume, UnmountVolumeError};
|
pub use unmount_apfs_volume::UnmountApfsVolume;
|
||||||
|
|
|
@ -2,13 +2,10 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::action::StatefulAction;
|
use crate::action::{ActionError, StatefulAction};
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription};
|
||||||
action::{Action, ActionDescription},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Unmount an APFS volume
|
Unmount an APFS volume
|
||||||
|
@ -24,7 +21,7 @@ impl UnmountApfsVolume {
|
||||||
pub async fn plan(
|
pub async fn plan(
|
||||||
disk: impl AsRef<Path>,
|
disk: impl AsRef<Path>,
|
||||||
name: String,
|
name: String,
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
let disk = disk.as_ref().to_owned();
|
let disk = disk.as_ref().to_owned();
|
||||||
Ok(Self { disk, name }.into())
|
Ok(Self { disk, name }.into())
|
||||||
}
|
}
|
||||||
|
@ -45,7 +42,7 @@ impl Action for UnmountApfsVolume {
|
||||||
disk = %self.disk.display(),
|
disk = %self.disk.display(),
|
||||||
name = %self.name,
|
name = %self.name,
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { disk: _, name } = self;
|
let Self { disk: _, name } = self;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -56,7 +53,7 @@ impl Action for UnmountApfsVolume {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| UnmountVolumeError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -69,7 +66,7 @@ impl Action for UnmountApfsVolume {
|
||||||
disk = %self.disk.display(),
|
disk = %self.disk.display(),
|
||||||
name = %self.name,
|
name = %self.name,
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { disk: _, name } = self;
|
let Self { disk: _, name } = self;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -80,14 +77,8 @@ impl Action for UnmountApfsVolume {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| UnmountVolumeError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Command(e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum UnmountVolumeError {
|
|
||||||
#[error("Failed to execute command")]
|
|
||||||
Command(#[source] std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,13 +4,10 @@ use target_lexicon::OperatingSystem;
|
||||||
use tokio::fs::remove_file;
|
use tokio::fs::remove_file;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::action::StatefulAction;
|
use crate::action::{ActionError, StatefulAction};
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription};
|
||||||
action::{Action, ActionDescription},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service";
|
const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service";
|
||||||
const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket";
|
const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket";
|
||||||
|
@ -26,7 +23,7 @@ pub struct ConfigureNixDaemonService {}
|
||||||
|
|
||||||
impl ConfigureNixDaemonService {
|
impl ConfigureNixDaemonService {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||||
match OperatingSystem::host() {
|
match OperatingSystem::host() {
|
||||||
OperatingSystem::MacOSX {
|
OperatingSystem::MacOSX {
|
||||||
major: _,
|
major: _,
|
||||||
|
@ -36,7 +33,9 @@ impl ConfigureNixDaemonService {
|
||||||
| OperatingSystem::Darwin => (),
|
| OperatingSystem::Darwin => (),
|
||||||
_ => {
|
_ => {
|
||||||
if !Path::new("/run/systemd/system").exists() {
|
if !Path::new("/run/systemd/system").exists() {
|
||||||
return Err(ConfigureNixDaemonServiceError::InitNotSupported.boxed());
|
return Err(ActionError::Custom(Box::new(
|
||||||
|
ConfigureNixDaemonServiceError::InitNotSupported,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -64,7 +63,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self {} = self;
|
let Self {} = self;
|
||||||
|
|
||||||
match OperatingSystem::host() {
|
match OperatingSystem::host() {
|
||||||
|
@ -78,12 +77,11 @@ impl Action for ConfigureNixDaemonService {
|
||||||
tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST)
|
tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ConfigureNixDaemonServiceError::Copy(
|
ActionError::Copy(
|
||||||
src.to_path_buf(),
|
src.to_path_buf(),
|
||||||
PathBuf::from(DARWIN_NIX_DAEMON_DEST),
|
PathBuf::from(DARWIN_NIX_DAEMON_DEST),
|
||||||
e,
|
e,
|
||||||
)
|
)
|
||||||
.boxed()
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -94,19 +92,18 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
|
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
|
||||||
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
|
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ConfigureNixDaemonServiceError::Symlink(
|
ActionError::Symlink(
|
||||||
PathBuf::from(TMPFILES_SRC),
|
PathBuf::from(TMPFILES_SRC),
|
||||||
PathBuf::from(TMPFILES_DEST),
|
PathBuf::from(TMPFILES_DEST),
|
||||||
e,
|
e,
|
||||||
)
|
)
|
||||||
.boxed()
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -117,7 +114,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
|
@ -127,7 +124,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
|
@ -137,7 +134,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
|
@ -146,7 +143,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
|
@ -156,7 +153,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.arg("nix-daemon.socket"),
|
.arg("nix-daemon.socket"),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,7 +173,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
match OperatingSystem::host() {
|
match OperatingSystem::host() {
|
||||||
OperatingSystem::MacOSX {
|
OperatingSystem::MacOSX {
|
||||||
major: _,
|
major: _,
|
||||||
|
@ -191,7 +188,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.arg(DARWIN_NIX_DAEMON_DEST),
|
.arg(DARWIN_NIX_DAEMON_DEST),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
// We separate stop and disable (instead of using `--now`) to avoid cases where the service isn't started, but is enabled.
|
// We separate stop and disable (instead of using `--now`) to avoid cases where the service isn't started, but is enabled.
|
||||||
|
@ -209,7 +206,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if socket_is_enabled {
|
if socket_is_enabled {
|
||||||
|
@ -220,7 +217,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if service_is_active {
|
if service_is_active {
|
||||||
|
@ -231,7 +228,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if service_is_enabled {
|
if service_is_enabled {
|
||||||
|
@ -242,7 +239,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -253,12 +250,11 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
|
|
||||||
remove_file(TMPFILES_DEST).await.map_err(|e| {
|
remove_file(TMPFILES_DEST)
|
||||||
ConfigureNixDaemonServiceError::RemoveFile(PathBuf::from(TMPFILES_DEST), e)
|
.await
|
||||||
.boxed()
|
.map_err(|e| ActionError::Remove(PathBuf::from(TMPFILES_DEST), e))?;
|
||||||
})?;
|
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
|
@ -267,7 +263,7 @@ impl Action for ConfigureNixDaemonService {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ConfigureNixDaemonServiceError::Command(e).boxed())?;
|
.map_err(ActionError::Command)?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -277,34 +273,17 @@ impl Action for ConfigureNixDaemonService {
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum ConfigureNixDaemonServiceError {
|
pub enum ConfigureNixDaemonServiceError {
|
||||||
#[error("Symlinking from `{0}` to `{1}`")]
|
|
||||||
Symlink(
|
|
||||||
std::path::PathBuf,
|
|
||||||
std::path::PathBuf,
|
|
||||||
#[source] std::io::Error,
|
|
||||||
),
|
|
||||||
#[error("Set mode `{0}` on `{1}`")]
|
|
||||||
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Command failed to execute")]
|
|
||||||
Command(#[source] std::io::Error),
|
|
||||||
#[error("Remove file `{0}`")]
|
|
||||||
RemoveFile(std::path::PathBuf, #[source] std::io::Error),
|
|
||||||
#[error("Copying file `{0}` to `{1}`")]
|
|
||||||
Copy(
|
|
||||||
std::path::PathBuf,
|
|
||||||
std::path::PathBuf,
|
|
||||||
#[source] std::io::Error,
|
|
||||||
),
|
|
||||||
#[error("No supported init system found")]
|
#[error("No supported init system found")]
|
||||||
InitNotSupported,
|
InitNotSupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_active(unit: &str) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
async fn is_active(unit: &str) -> Result<bool, ActionError> {
|
||||||
let output = Command::new("systemctl")
|
let output = Command::new("systemctl")
|
||||||
.arg("is-active")
|
.arg("is-active")
|
||||||
.arg(unit)
|
.arg(unit)
|
||||||
.output()
|
.output()
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(ActionError::Command)?;
|
||||||
if String::from_utf8(output.stdout)?.starts_with("active") {
|
if String::from_utf8(output.stdout)?.starts_with("active") {
|
||||||
tracing::trace!(%unit, "Is active");
|
tracing::trace!(%unit, "Is active");
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
@ -314,12 +293,13 @@ async fn is_active(unit: &str) -> Result<bool, Box<dyn std::error::Error + Send
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_enabled(unit: &str) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
async fn is_enabled(unit: &str) -> Result<bool, ActionError> {
|
||||||
let output = Command::new("systemctl")
|
let output = Command::new("systemctl")
|
||||||
.arg("is-enabled")
|
.arg("is-enabled")
|
||||||
.arg(unit)
|
.arg(unit)
|
||||||
.output()
|
.output()
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(ActionError::Command)?;
|
||||||
let stdout = String::from_utf8(output.stdout)?;
|
let stdout = String::from_utf8(output.stdout)?;
|
||||||
if stdout.starts_with("enabled") || stdout.starts_with("linked") {
|
if stdout.starts_with("enabled") || stdout.starts_with("linked") {
|
||||||
tracing::trace!(%unit, "Is enabled");
|
tracing::trace!(%unit, "Is enabled");
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::action::{ActionState, StatefulAction};
|
use crate::action::{ActionError, ActionState, StatefulAction};
|
||||||
use crate::execute_command;
|
use crate::execute_command;
|
||||||
|
|
||||||
use crate::{
|
use crate::action::{Action, ActionDescription};
|
||||||
action::{Action, ActionDescription},
|
|
||||||
BoxableError,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Start a given systemd unit
|
Start a given systemd unit
|
||||||
|
@ -18,9 +15,7 @@ pub struct StartSystemdUnit {
|
||||||
|
|
||||||
impl StartSystemdUnit {
|
impl StartSystemdUnit {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan(
|
pub async fn plan(unit: impl AsRef<str>) -> Result<StatefulAction<Self>, ActionError> {
|
||||||
unit: impl AsRef<str>,
|
|
||||||
) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
Ok(StatefulAction {
|
Ok(StatefulAction {
|
||||||
action: Self {
|
action: Self {
|
||||||
unit: unit.as_ref().to_string(),
|
unit: unit.as_ref().to_string(),
|
||||||
|
@ -44,7 +39,7 @@ impl Action for StartSystemdUnit {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
unit = %self.unit,
|
unit = %self.unit,
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { unit, .. } = self;
|
let Self { unit, .. } = self;
|
||||||
|
|
||||||
// TODO(@Hoverbear): Handle proxy vars
|
// TODO(@Hoverbear): Handle proxy vars
|
||||||
|
@ -57,7 +52,7 @@ impl Action for StartSystemdUnit {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Custom(Box::new(StartSystemdUnitError::Command(e))))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -72,7 +67,7 @@ impl Action for StartSystemdUnit {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
unit = %self.unit,
|
unit = %self.unit,
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
let Self { unit, .. } = self;
|
let Self { unit, .. } = self;
|
||||||
|
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -83,7 +78,7 @@ impl Action for StartSystemdUnit {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Custom(Box::new(StartSystemdUnitError::Command(e))))?;
|
||||||
|
|
||||||
// We do both to avoid an error doing `disable --now` if the user did stop it already somehow.
|
// We do both to avoid an error doing `disable --now` if the user did stop it already somehow.
|
||||||
execute_command(
|
execute_command(
|
||||||
|
@ -94,7 +89,7 @@ impl Action for StartSystemdUnit {
|
||||||
.stdin(std::process::Stdio::null()),
|
.stdin(std::process::Stdio::null()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
|
.map_err(|e| ActionError::Custom(Box::new(StartSystemdUnitError::Command(e))))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ use harmonic::{
|
||||||
InstallPlan,
|
InstallPlan,
|
||||||
settings::{CommonSettings, InstallSettingsError},
|
settings::{CommonSettings, InstallSettingsError},
|
||||||
planner::{Planner, PlannerError, linux::SteamDeck},
|
planner::{Planner, PlannerError, linux::SteamDeck},
|
||||||
action::{Action, StatefulAction, ActionDescription},
|
action::{Action, ActionError, StatefulAction, ActionDescription},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
@ -59,7 +59,7 @@ pub struct MyAction {}
|
||||||
|
|
||||||
impl MyAction {
|
impl MyAction {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub async fn plan() -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
|
||||||
Ok(Self {}.into())
|
Ok(Self {}.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ impl Action for MyAction {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
// Tracing fields...
|
// Tracing fields...
|
||||||
))]
|
))]
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn execute(&mut self) -> Result<(), ActionError> {
|
||||||
// Execute steps ...
|
// Execute steps ...
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ impl Action for MyAction {
|
||||||
#[tracing::instrument(skip_all, fields(
|
#[tracing::instrument(skip_all, fields(
|
||||||
// Tracing fields...
|
// Tracing fields...
|
||||||
))]
|
))]
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
async fn revert(&mut self) -> Result<(), ActionError> {
|
||||||
// Revert steps...
|
// Revert steps...
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -158,6 +158,8 @@ pub mod linux;
|
||||||
mod stateful;
|
mod stateful;
|
||||||
|
|
||||||
pub use stateful::{ActionState, StatefulAction};
|
pub use stateful::{ActionState, StatefulAction};
|
||||||
|
use std::error::Error;
|
||||||
|
use tokio::task::JoinError;
|
||||||
|
|
||||||
/// An action which can be reverted or completed, with an action state
|
/// An action which can be reverted or completed, with an action state
|
||||||
///
|
///
|
||||||
|
@ -186,13 +188,13 @@ pub trait Action: Send + Sync + std::fmt::Debug + dyn_clone::DynClone {
|
||||||
/// If this action calls sub-[`Action`]s, care should be taken to call [`try_execute`][StatefulAction::try_execute], not [`execute`][Action::execute], so that [`ActionState`] is handled correctly and tracing is done.
|
/// If this action calls sub-[`Action`]s, care should be taken to call [`try_execute`][StatefulAction::try_execute], not [`execute`][Action::execute], so that [`ActionState`] is handled correctly and tracing is done.
|
||||||
///
|
///
|
||||||
/// This is called by [`InstallPlan::install`](crate::InstallPlan::install) through [`StatefulAction::try_execute`] which handles tracing as well as if the action needs to execute based on its `action_state`.
|
/// This is called by [`InstallPlan::install`](crate::InstallPlan::install) through [`StatefulAction::try_execute`] which handles tracing as well as if the action needs to execute based on its `action_state`.
|
||||||
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
async fn execute(&mut self) -> Result<(), ActionError>;
|
||||||
/// Perform any revert steps
|
/// Perform any revert steps
|
||||||
///
|
///
|
||||||
/// If this action calls sub-[`Action`]s, care should be taken to call [`try_revert`][StatefulAction::try_revert], not [`revert`][Action::revert], so that [`ActionState`] is handled correctly and tracing is done.
|
/// If this action calls sub-[`Action`]s, care should be taken to call [`try_revert`][StatefulAction::try_revert], not [`revert`][Action::revert], so that [`ActionState`] is handled correctly and tracing is done.
|
||||||
///
|
///
|
||||||
/// /// This is called by [`InstallPlan::uninstall`](crate::InstallPlan::uninstall) through [`StatefulAction::try_revert`] which handles tracing as well as if the action needs to revert based on its `action_state`.
|
/// /// This is called by [`InstallPlan::uninstall`](crate::InstallPlan::uninstall) through [`StatefulAction::try_revert`] which handles tracing as well as if the action needs to revert based on its `action_state`.
|
||||||
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
|
async fn revert(&mut self) -> Result<(), ActionError>;
|
||||||
|
|
||||||
fn stateful(self) -> StatefulAction<Self>
|
fn stateful(self) -> StatefulAction<Self>
|
||||||
where
|
where
|
||||||
|
@ -203,7 +205,7 @@ pub trait Action: Send + Sync + std::fmt::Debug + dyn_clone::DynClone {
|
||||||
state: ActionState::Uncompleted,
|
state: ActionState::Uncompleted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// They should also have an `async fn plan(args...) -> Result<StatefulAction<Self>, Box<dyn std::error::Error + Send + Sync>>;`
|
// They should also have an `async fn plan(args...) -> Result<StatefulAction<Self>, ActionError>;`
|
||||||
}
|
}
|
||||||
|
|
||||||
dyn_clone::clone_trait_object!(Action);
|
dyn_clone::clone_trait_object!(Action);
|
||||||
|
@ -225,3 +227,85 @@ impl ActionDescription {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error occurring during an action
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum ActionError {
|
||||||
|
/// A custom error
|
||||||
|
#[error(transparent)]
|
||||||
|
Custom(Box<dyn std::error::Error + Send + Sync>),
|
||||||
|
/// A child error
|
||||||
|
#[error(transparent)]
|
||||||
|
Child(#[from] Box<ActionError>),
|
||||||
|
/// Several child errors
|
||||||
|
#[error("Multiple errors: {}", .0.iter().map(|v| {
|
||||||
|
if let Some(source) = v.source() {
|
||||||
|
format!("{v} ({source})")
|
||||||
|
} else {
|
||||||
|
format!("{v}")
|
||||||
|
}
|
||||||
|
}).collect::<Vec<_>>().join(" & "))]
|
||||||
|
Children(Vec<Box<ActionError>>),
|
||||||
|
/// The path already exists
|
||||||
|
#[error("Path exists `{0}`")]
|
||||||
|
Exists(std::path::PathBuf),
|
||||||
|
#[error("Getting metadata for {0}`")]
|
||||||
|
GettingMetadata(std::path::PathBuf, #[source] std::io::Error),
|
||||||
|
#[error("Creating directory `{0}`")]
|
||||||
|
CreateDirectory(std::path::PathBuf, #[source] std::io::Error),
|
||||||
|
#[error("Symlinking from `{0}` to `{1}`")]
|
||||||
|
Symlink(
|
||||||
|
std::path::PathBuf,
|
||||||
|
std::path::PathBuf,
|
||||||
|
#[source] std::io::Error,
|
||||||
|
),
|
||||||
|
#[error("Set mode `{0}` on `{1}`")]
|
||||||
|
SetPermissions(u32, std::path::PathBuf, #[source] std::io::Error),
|
||||||
|
#[error("Remove file `{0}`")]
|
||||||
|
Remove(std::path::PathBuf, #[source] std::io::Error),
|
||||||
|
#[error("Copying file `{0}` to `{1}`")]
|
||||||
|
Copy(
|
||||||
|
std::path::PathBuf,
|
||||||
|
std::path::PathBuf,
|
||||||
|
#[source] std::io::Error,
|
||||||
|
),
|
||||||
|
#[error("Rename `{0}` to `{1}`")]
|
||||||
|
Rename(
|
||||||
|
std::path::PathBuf,
|
||||||
|
std::path::PathBuf,
|
||||||
|
#[source] std::io::Error,
|
||||||
|
),
|
||||||
|
#[error("Remove path `{0}`")]
|
||||||
|
Read(std::path::PathBuf, #[source] std::io::Error),
|
||||||
|
#[error("Open path `{0}`")]
|
||||||
|
Open(std::path::PathBuf, #[source] std::io::Error),
|
||||||
|
#[error("Write path `{0}`")]
|
||||||
|
Write(std::path::PathBuf, #[source] std::io::Error),
|
||||||
|
#[error("Seek path `{0}`")]
|
||||||
|
Seek(std::path::PathBuf, #[source] std::io::Error),
|
||||||
|
#[error("Getting uid for user `{0}`")]
|
||||||
|
UserId(String, #[source] nix::errno::Errno),
|
||||||
|
#[error("Getting user `{0}`")]
|
||||||
|
NoUser(String),
|
||||||
|
#[error("Getting gid for group `{0}`")]
|
||||||
|
GroupId(String, #[source] nix::errno::Errno),
|
||||||
|
#[error("Getting group `{0}`")]
|
||||||
|
NoGroup(String),
|
||||||
|
#[error("Chowning path `{0}`")]
|
||||||
|
Chown(std::path::PathBuf, #[source] nix::errno::Errno),
|
||||||
|
/// Failed to execute command
|
||||||
|
#[error("Failed to execute command")]
|
||||||
|
Command(#[source] std::io::Error),
|
||||||
|
#[error("Joining spawned async task")]
|
||||||
|
Join(
|
||||||
|
#[source]
|
||||||
|
#[from]
|
||||||
|
JoinError,
|
||||||
|
),
|
||||||
|
#[error("String from UTF-8 error")]
|
||||||
|
FromUtf8(
|
||||||
|
#[source]
|
||||||
|
#[from]
|
||||||
|
std::string::FromUtf8Error,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{Action, ActionDescription};
|
use super::{Action, ActionDescription, ActionError};
|
||||||
|
|
||||||
/// A wrapper around an [`Action`](crate::action::Action) which tracks the [`ActionState`] and
|
/// A wrapper around an [`Action`](crate::action::Action) which tracks the [`ActionState`] and
|
||||||
/// handles some tracing output
|
/// handles some tracing output
|
||||||
|
@ -44,7 +44,7 @@ impl StatefulAction<Box<dyn Action>> {
|
||||||
/// Perform any execution steps
|
/// Perform any execution steps
|
||||||
///
|
///
|
||||||
/// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing
|
/// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing
|
||||||
pub async fn try_execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn try_execute(&mut self) -> Result<(), ActionError> {
|
||||||
match self.state {
|
match self.state {
|
||||||
ActionState::Completed => {
|
ActionState::Completed => {
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
|
@ -70,7 +70,7 @@ impl StatefulAction<Box<dyn Action>> {
|
||||||
/// Perform any revert steps
|
/// Perform any revert steps
|
||||||
///
|
///
|
||||||
/// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing
|
/// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing
|
||||||
pub async fn try_revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn try_revert(&mut self) -> Result<(), ActionError> {
|
||||||
match self.state {
|
match self.state {
|
||||||
ActionState::Uncompleted => {
|
ActionState::Uncompleted => {
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
|
@ -129,7 +129,7 @@ where
|
||||||
/// Perform any execution steps
|
/// Perform any execution steps
|
||||||
///
|
///
|
||||||
/// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing
|
/// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing
|
||||||
pub async fn try_execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn try_execute(&mut self) -> Result<(), ActionError> {
|
||||||
match self.state {
|
match self.state {
|
||||||
ActionState::Completed => {
|
ActionState::Completed => {
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
|
@ -155,7 +155,7 @@ where
|
||||||
/// Perform any revert steps
|
/// Perform any revert steps
|
||||||
///
|
///
|
||||||
/// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing
|
/// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing
|
||||||
pub async fn try_revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
pub async fn try_revert(&mut self) -> Result<(), ActionError> {
|
||||||
match self.state {
|
match self.state {
|
||||||
ActionState::Uncompleted => {
|
ActionState::Uncompleted => {
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::{planner::PlannerError, settings::InstallSettingsError};
|
use crate::{action::ActionError, planner::PlannerError, settings::InstallSettingsError};
|
||||||
|
|
||||||
/// An error occurring during a call defined in this crate
|
/// An error occurring during a call defined in this crate
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
@ -10,7 +10,7 @@ pub enum HarmonicError {
|
||||||
Action(
|
Action(
|
||||||
#[source]
|
#[source]
|
||||||
#[from]
|
#[from]
|
||||||
Box<dyn std::error::Error + Send + Sync>,
|
ActionError,
|
||||||
),
|
),
|
||||||
/// An error while writing the [`InstallPlan`](crate::InstallPlan)
|
/// An error while writing the [`InstallPlan`](crate::InstallPlan)
|
||||||
#[error("Recording install receipt")]
|
#[error("Recording install receipt")]
|
||||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -112,14 +112,3 @@ fn set_env(k: impl AsRef<OsStr>, v: impl AsRef<OsStr>) {
|
||||||
tracing::trace!("Setting env");
|
tracing::trace!("Setting env");
|
||||||
std::env::set_var(k.as_ref(), v.as_ref());
|
std::env::set_var(k.as_ref(), v.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
trait BoxableError: std::error::Error + Send + Sync {
|
|
||||||
fn boxed(self) -> Box<dyn std::error::Error + Send + Sync>
|
|
||||||
where
|
|
||||||
Self: Sized + 'static,
|
|
||||||
{
|
|
||||||
Box::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> BoxableError for E where E: std::error::Error + Send + Sized + Sync {}
|
|
||||||
|
|
|
@ -80,7 +80,9 @@ pub mod linux;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
action::StatefulAction, settings::InstallSettingsError, Action, HarmonicError, InstallPlan,
|
action::{ActionError, StatefulAction},
|
||||||
|
settings::InstallSettingsError,
|
||||||
|
Action, HarmonicError, InstallPlan,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Something which can be used to plan out an [`InstallPlan`]
|
/// Something which can be used to plan out an [`InstallPlan`]
|
||||||
|
@ -165,7 +167,11 @@ pub enum PlannerError {
|
||||||
UnsupportedArchitecture(target_lexicon::Triple),
|
UnsupportedArchitecture(target_lexicon::Triple),
|
||||||
/// Error executing action
|
/// Error executing action
|
||||||
#[error("Error executing action")]
|
#[error("Error executing action")]
|
||||||
Action(#[source] Box<dyn std::error::Error + Send + Sync>),
|
Action(
|
||||||
|
#[source]
|
||||||
|
#[from]
|
||||||
|
ActionError,
|
||||||
|
),
|
||||||
/// An [`InstallSettingsError`]
|
/// An [`InstallSettingsError`]
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
InstallSettings(#[from] InstallSettingsError),
|
InstallSettings(#[from] InstallSettingsError),
|
||||||
|
|
Loading…
Reference in a new issue