Box up errors, dyn Actionables

Signed-off-by: Ana Hobden <operator@hoverbear.org>
This commit is contained in:
Ana Hobden 2022-10-26 14:14:53 -07:00
parent 706af47714
commit cc1cbe109a
35 changed files with 394 additions and 854 deletions

View file

@ -7,7 +7,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
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";
@ -21,7 +21,7 @@ pub struct ConfigureNixDaemonService {
impl ConfigureNixDaemonService {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<Self, ConfigureNixDaemonServiceError> {
pub async fn plan() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
match OperatingSystem::host() {
OperatingSystem::MacOSX {
major: _,
@ -31,7 +31,7 @@ impl ConfigureNixDaemonService {
| OperatingSystem::Darwin => (),
_ => {
if !Path::new("/run/systemd/system").exists() {
return Err(ConfigureNixDaemonServiceError::InitNotSupported);
return Err(ConfigureNixDaemonServiceError::InitNotSupported.boxed());
}
},
};
@ -43,9 +43,8 @@ impl ConfigureNixDaemonService {
}
#[async_trait::async_trait]
#[typetag::serde(name = "configure-nix-daemon")]
impl Actionable for ConfigureNixDaemonService {
type Error = ConfigureNixDaemonServiceError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -63,7 +62,7 @@ impl Actionable for ConfigureNixDaemonService {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { action_state } = self;
if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Configuring nix daemon service");
@ -85,11 +84,12 @@ impl Actionable for ConfigureNixDaemonService {
tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST)
.await
.map_err(|e| {
Self::Error::Copy(
ConfigureNixDaemonServiceError::Copy(
src.to_path_buf(),
PathBuf::from(DARWIN_NIX_DAEMON_DEST),
e,
)
.boxed()
})?;
execute_command(
@ -98,18 +98,19 @@ impl Actionable for ConfigureNixDaemonService {
.arg(DARWIN_NIX_DAEMON_DEST),
)
.await
.map_err(Self::Error::CommandFailed)?;
.map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
},
_ => {
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
.await
.map_err(|e| {
Self::Error::Symlink(
ConfigureNixDaemonServiceError::Symlink(
PathBuf::from(TMPFILES_SRC),
PathBuf::from(TMPFILES_DEST),
e,
)
.boxed()
})?;
execute_command(
@ -118,19 +119,19 @@ impl Actionable for ConfigureNixDaemonService {
.arg("--prefix=/nix/var/nix"),
)
.await
.map_err(Self::Error::CommandFailed)?;
.map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
execute_command(Command::new("systemctl").arg("link").arg(SERVICE_SRC))
.await
.map_err(Self::Error::CommandFailed)?;
.map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
execute_command(Command::new("systemctl").arg("link").arg(SOCKET_SRC))
.await
.map_err(Self::Error::CommandFailed)?;
.map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
execute_command(Command::new("systemctl").arg("daemon-reload"))
.await
.map_err(Self::Error::CommandFailed)?;
.map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
},
};
@ -156,7 +157,7 @@ impl Actionable for ConfigureNixDaemonService {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { action_state } = self;
if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Unconfiguring nix daemon service");
@ -167,11 +168,11 @@ impl Actionable for ConfigureNixDaemonService {
// We don't need to do this! Systemd does it for us! (In fact, it's an error if we try to do this...)
execute_command(Command::new("systemctl").args(["disable", SOCKET_SRC]))
.await
.map_err(Self::Error::CommandFailed)?;
.map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
execute_command(Command::new("systemctl").args(["disable", SERVICE_SRC]))
.await
.map_err(Self::Error::CommandFailed)?;
.map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
execute_command(
Command::new("systemd-tmpfiles")
@ -179,15 +180,15 @@ impl Actionable for ConfigureNixDaemonService {
.arg("--prefix=/nix/var/nix"),
)
.await
.map_err(Self::Error::CommandFailed)?;
.map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
remove_file(TMPFILES_DEST)
.await
.map_err(|e| Self::Error::RemoveFile(PathBuf::from(TMPFILES_DEST), e))?;
remove_file(TMPFILES_DEST).await.map_err(|e| {
ConfigureNixDaemonServiceError::RemoveFile(PathBuf::from(TMPFILES_DEST), e).boxed()
})?;
execute_command(Command::new("systemctl").arg("daemon-reload"))
.await
.map_err(Self::Error::CommandFailed)?;
.map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
tracing::trace!("Unconfigured nix daemon service");
*action_state = ActionState::Uncompleted;
@ -195,12 +196,6 @@ impl Actionable for ConfigureNixDaemonService {
}
}
impl From<ConfigureNixDaemonService> for Action {
fn from(v: ConfigureNixDaemonService) -> Self {
Action::ConfigureNixDaemonService(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum ConfigureNixDaemonServiceError {
#[error("Symlinking from `{0}` to `{1}`")]

View file

@ -5,7 +5,7 @@ use nix::unistd::{chown, Group, User};
use serde::Serialize;
use tokio::fs::{create_dir, remove_dir_all};
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateDirectory {
@ -25,16 +25,16 @@ impl CreateDirectory {
group: impl Into<Option<String>>,
mode: impl Into<Option<u32>>,
force_prune_on_revert: bool,
) -> Result<Self, CreateDirectoryError> {
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let path = path.as_ref();
let user = user.into();
let group = group.into();
let mode = mode.into();
let action_state = if path.exists() {
let metadata = tokio::fs::metadata(path)
.await
.map_err(|e| CreateDirectoryError::GettingMetadata(path.to_path_buf(), e))?;
let metadata = tokio::fs::metadata(path).await.map_err(|e| {
CreateDirectoryError::GettingMetadata(path.to_path_buf(), e).boxed()
})?;
if metadata.is_dir() {
// TODO: Validate owner/group...
ActionState::Completed
@ -45,7 +45,8 @@ impl CreateDirectory {
"Path `{}` already exists and is not directory",
path.display()
),
)));
))
.boxed());
}
} else {
ActionState::Uncompleted
@ -63,9 +64,8 @@ impl CreateDirectory {
}
#[async_trait::async_trait]
#[typetag::serde(name = "create-directory")]
impl Actionable for CreateDirectory {
type Error = CreateDirectoryError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
path,
@ -91,7 +91,7 @@ impl Actionable for CreateDirectory {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
path,
user,
@ -109,8 +109,8 @@ impl Actionable for CreateDirectory {
let gid = if let Some(group) = group {
Some(
Group::from_name(group.as_str())
.map_err(|e| Self::Error::GroupId(group.clone(), e))?
.ok_or(Self::Error::NoGroup(group.clone()))?
.map_err(|e| CreateDirectoryError::GroupId(group.clone(), e).boxed())?
.ok_or(CreateDirectoryError::NoGroup(group.clone()).boxed())?
.gid,
)
} else {
@ -119,8 +119,8 @@ impl Actionable for CreateDirectory {
let uid = if let Some(user) = user {
Some(
User::from_name(user.as_str())
.map_err(|e| Self::Error::UserId(user.clone(), e))?
.ok_or(Self::Error::NoUser(user.clone()))?
.map_err(|e| CreateDirectoryError::UserId(user.clone(), e).boxed())?
.ok_or(CreateDirectoryError::NoUser(user.clone()).boxed())?
.uid,
)
} else {
@ -129,13 +129,15 @@ impl Actionable for CreateDirectory {
create_dir(path.clone())
.await
.map_err(|e| Self::Error::Creating(path.clone(), e))?;
chown(path, uid, gid).map_err(|e| Self::Error::Chown(path.clone(), e))?;
.map_err(|e| CreateDirectoryError::Creating(path.clone(), e).boxed())?;
chown(path, uid, gid).map_err(|e| CreateDirectoryError::Chown(path.clone(), e).boxed())?;
if let Some(mode) = mode {
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
.await
.map_err(|e| Self::Error::SetPermissions(*mode, path.to_owned(), e))?;
.map_err(|e| {
CreateDirectoryError::SetPermissions(*mode, path.to_owned(), e).boxed()
})?;
}
tracing::trace!("Created directory");
@ -176,7 +178,7 @@ impl Actionable for CreateDirectory {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
path,
user: _,
@ -195,13 +197,13 @@ impl Actionable for CreateDirectory {
let is_empty = path
.read_dir()
.map_err(|e| CreateDirectoryError::ReadDir(path.clone(), e))?
.map_err(|e| CreateDirectoryError::ReadDir(path.clone(), e).boxed())?
.next()
.is_some();
match (is_empty, force_prune_on_revert) {
(true, _) | (false, true) => remove_dir_all(path.clone())
.await
.map_err(|e| Self::Error::Removing(path.clone(), e))?,
.map_err(|e| CreateDirectoryError::Removing(path.clone(), e).boxed())?,
(false, false) => {},
};
@ -211,12 +213,6 @@ impl Actionable for CreateDirectory {
}
}
impl From<CreateDirectory> for Action {
fn from(v: CreateDirectory) -> Self {
Action::CreateDirectory(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateDirectoryError {
#[error(transparent)]

View file

@ -6,7 +6,7 @@ use tokio::{
io::AsyncWriteExt,
};
use crate::actions::{Action, ActionState};
use crate::actions::{ActionError, ActionState};
use crate::actions::{ActionDescription, Actionable};
@ -30,11 +30,11 @@ impl CreateFile {
mode: impl Into<Option<u32>>,
buf: String,
force: bool,
) -> Result<Self, CreateFileError> {
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let path = path.as_ref().to_path_buf();
if path.exists() && !force {
return Err(CreateFileError::Exists(path.to_path_buf()));
return Err(CreateFileError::Exists(path.to_path_buf()).boxed());
}
Ok(Self {
@ -50,9 +50,8 @@ impl CreateFile {
}
#[async_trait::async_trait]
#[typetag::serde(name = "create-file")]
impl Actionable for CreateFile {
type Error = CreateFileError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
path,
@ -79,7 +78,7 @@ impl Actionable for CreateFile {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
path,
user,
@ -105,17 +104,17 @@ impl Actionable for CreateFile {
let mut file = options
.open(&path)
.await
.map_err(|e| Self::Error::OpenFile(path.to_owned(), e))?;
.map_err(|e| CreateFileError::OpenFile(path.to_owned(), e).boxed())?;
file.write_all(buf.as_bytes())
.await
.map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?;
.map_err(|e| CreateFileError::WriteFile(path.to_owned(), e).boxed())?;
let gid = if let Some(group) = group {
Some(
Group::from_name(group.as_str())
.map_err(|e| Self::Error::GroupId(group.clone(), e))?
.ok_or(Self::Error::NoGroup(group.clone()))?
.map_err(|e| CreateFileError::GroupId(group.clone(), e).boxed())?
.ok_or(CreateFileError::NoGroup(group.clone()).boxed())?
.gid,
)
} else {
@ -124,14 +123,14 @@ impl Actionable for CreateFile {
let uid = if let Some(user) = user {
Some(
User::from_name(user.as_str())
.map_err(|e| Self::Error::UserId(user.clone(), e))?
.ok_or(Self::Error::NoUser(user.clone()))?
.map_err(|e| CreateFileError::UserId(user.clone(), e).boxed())?
.ok_or(CreateFileError::NoUser(user.clone()).boxed())?
.uid,
)
} else {
None
};
chown(path, uid, gid).map_err(|e| Self::Error::Chown(path.clone(), e))?;
chown(path, uid, gid).map_err(|e| CreateFileError::Chown(path.clone(), e).boxed())?;
tracing::trace!("Created file");
*action_state = ActionState::Completed;
@ -164,7 +163,7 @@ impl Actionable for CreateFile {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
path,
user: _,
@ -182,7 +181,7 @@ impl Actionable for CreateFile {
remove_file(&path)
.await
.map_err(|e| Self::Error::RemoveFile(path.to_owned(), e))?;
.map_err(|e| CreateFileError::RemoveFile(path.to_owned(), e).boxed())?;
tracing::trace!("Deleted file");
*action_state = ActionState::Uncompleted;
@ -190,12 +189,6 @@ impl Actionable for CreateFile {
}
}
impl From<CreateFile> for Action {
fn from(v: CreateFile) -> Self {
Action::CreateFile(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateFileError {
#[error("File exists `{0}`")]

View file

@ -3,7 +3,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateGroup {
@ -24,9 +24,8 @@ impl CreateGroup {
}
#[async_trait::async_trait]
#[typetag::serde(name = "create-group")]
impl Actionable for CreateGroup {
type Error = CreateGroupError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
name,
@ -49,7 +48,7 @@ impl Actionable for CreateGroup {
user = self.name,
gid = self.gid,
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
name,
gid,
@ -79,7 +78,7 @@ impl Actionable for CreateGroup {
name.as_str(),
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateGroupError::Command(e).boxed())?;
},
_ => {
execute_command(Command::new("groupadd").args([
@ -89,7 +88,7 @@ impl Actionable for CreateGroup {
&name,
]))
.await
.map_err(CreateGroupError::Command)?;
.map_err(|e| CreateGroupError::Command(e).boxed())?;
},
};
@ -120,7 +119,7 @@ impl Actionable for CreateGroup {
user = self.name,
gid = self.gid,
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
name,
gid: _,
@ -142,12 +141,12 @@ impl Actionable for CreateGroup {
| OperatingSystem::Darwin => {
execute_command(Command::new("groupdel").arg(&name))
.await
.map_err(CreateGroupError::Command)?;
.map_err(|e| CreateGroupError::Command(e).boxed())?;
},
_ => {
execute_command(Command::new("userdel").args([&name.to_string()]))
execute_command(Command::new("groupdel").arg(&name))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateGroupError::Command(e).boxed())?;
},
};
@ -157,12 +156,6 @@ impl Actionable for CreateGroup {
}
}
impl From<CreateGroup> for Action {
fn from(v: CreateGroup) -> Self {
Action::CreateGroup(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateGroupError {
#[error("Failed to execute command")]

View file

@ -10,7 +10,7 @@ use tokio::{
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
};
use crate::actions::{Action, ActionState};
use crate::actions::{ActionError, ActionState};
use crate::actions::{ActionDescription, Actionable};
@ -47,9 +47,8 @@ impl CreateOrAppendFile {
}
#[async_trait::async_trait]
#[typetag::serde(name = "create-or-append-file")]
impl Actionable for CreateOrAppendFile {
type Error = CreateOrAppendFileError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
path,
@ -75,7 +74,7 @@ impl Actionable for CreateOrAppendFile {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
path,
user,
@ -96,21 +95,21 @@ impl Actionable for CreateOrAppendFile {
.read(true)
.open(&path)
.await
.map_err(|e| Self::Error::OpenFile(path.to_owned(), e))?;
.map_err(|e| CreateOrAppendFileError::OpenFile(path.to_owned(), e).boxed())?;
file.seek(SeekFrom::End(0))
.await
.map_err(|e| Self::Error::SeekFile(path.to_owned(), e))?;
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
file.write_all(buf.as_bytes())
.await
.map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?;
.map_err(|e| CreateOrAppendFileError::WriteFile(path.to_owned(), e).boxed())?;
let gid = if let Some(group) = group {
Some(
Group::from_name(group.as_str())
.map_err(|e| Self::Error::GroupId(group.clone(), e))?
.ok_or(Self::Error::NoGroup(group.clone()))?
.map_err(|e| CreateOrAppendFileError::GroupId(group.clone(), e).boxed())?
.ok_or(CreateOrAppendFileError::NoGroup(group.clone()).boxed())?
.gid,
)
} else {
@ -119,8 +118,8 @@ impl Actionable for CreateOrAppendFile {
let uid = if let Some(user) = user {
Some(
User::from_name(user.as_str())
.map_err(|e| Self::Error::UserId(user.clone(), e))?
.ok_or(Self::Error::NoUser(user.clone()))?
.map_err(|e| CreateOrAppendFileError::UserId(user.clone(), e).boxed())?
.ok_or(CreateOrAppendFileError::NoUser(user.clone()).boxed())?
.uid,
)
} else {
@ -130,10 +129,13 @@ impl Actionable for CreateOrAppendFile {
if let Some(mode) = mode {
tokio::fs::set_permissions(&path, PermissionsExt::from_mode(*mode))
.await
.map_err(|e| Self::Error::SetPermissions(*mode, path.to_owned(), e))?;
.map_err(|e| {
CreateOrAppendFileError::SetPermissions(*mode, path.to_owned(), e).boxed()
})?;
}
chown(path, uid, gid).map_err(|e| Self::Error::Chown(path.clone(), e))?;
chown(path, uid, gid)
.map_err(|e| CreateOrAppendFileError::Chown(path.clone(), e).boxed())?;
tracing::trace!("Created or appended fragment to file");
*action_state = ActionState::Completed;
@ -168,7 +170,7 @@ impl Actionable for CreateOrAppendFile {
group = self.group,
mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))),
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
path,
user: _,
@ -189,12 +191,12 @@ impl Actionable for CreateOrAppendFile {
.read(true)
.open(&path)
.await
.map_err(|e| Self::Error::ReadFile(path.to_owned(), e))?;
.map_err(|e| CreateOrAppendFileError::ReadFile(path.to_owned(), e).boxed())?;
let mut file_contents = String::default();
file.read_to_string(&mut file_contents)
.await
.map_err(|e| Self::Error::SeekFile(path.to_owned(), e))?;
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
if let Some(start) = file_contents.rfind(buf.as_str()) {
let end = start + buf.len();
@ -204,16 +206,16 @@ impl Actionable for CreateOrAppendFile {
if buf.is_empty() {
remove_file(&path)
.await
.map_err(|e| Self::Error::RemoveFile(path.to_owned(), e))?;
.map_err(|e| CreateOrAppendFileError::RemoveFile(path.to_owned(), e).boxed())?;
tracing::trace!("Removed file (since all content was removed)");
} else {
file.seek(SeekFrom::Start(0))
.await
.map_err(|e| Self::Error::SeekFile(path.to_owned(), e))?;
.map_err(|e| CreateOrAppendFileError::SeekFile(path.to_owned(), e).boxed())?;
file.write_all(file_contents.as_bytes())
.await
.map_err(|e| Self::Error::WriteFile(path.to_owned(), e))?;
.map_err(|e| CreateOrAppendFileError::WriteFile(path.to_owned(), e).boxed())?;
tracing::trace!("Removed fragment from from file");
}
@ -222,12 +224,6 @@ impl Actionable for CreateOrAppendFile {
}
}
impl From<CreateOrAppendFile> for Action {
fn from(v: CreateOrAppendFile) -> Self {
Action::CreateOrAppendFile(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateOrAppendFileError {
#[error("Remove file `{0}`")]

View file

@ -3,7 +3,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateUser {
@ -28,9 +28,8 @@ impl CreateUser {
}
#[async_trait::async_trait]
#[typetag::serde(name = "create-user")]
impl Actionable for CreateUser {
type Error = CreateUserError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -58,7 +57,7 @@ impl Actionable for CreateUser {
groupname = self.groupname,
gid = self.gid,
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
name,
uid,
@ -86,7 +85,7 @@ impl Actionable for CreateUser {
&format!("/Users/{name}"),
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-create",
@ -95,7 +94,7 @@ impl Actionable for CreateUser {
&format!("{uid}"),
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-create",
@ -104,7 +103,7 @@ impl Actionable for CreateUser {
&format!("{gid}"),
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-create",
@ -113,7 +112,7 @@ impl Actionable for CreateUser {
"/var/empty",
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-create",
@ -122,7 +121,7 @@ impl Actionable for CreateUser {
"/sbin/nologin",
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
execute_command(
Command::new("/usr/bin/dscl")
.args([
@ -134,7 +133,7 @@ impl Actionable for CreateUser {
.arg(&name),
)
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
execute_command(Command::new("/usr/bin/dscl").args([
".",
"-create",
@ -143,7 +142,7 @@ impl Actionable for CreateUser {
"1",
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
execute_command(
Command::new("/usr/sbin/dseditgroup")
.args(["-o", "edit"])
@ -154,7 +153,7 @@ impl Actionable for CreateUser {
.arg(groupname),
)
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
},
_ => {
execute_command(Command::new("useradd").args([
@ -177,7 +176,7 @@ impl Actionable for CreateUser {
&name.to_string(),
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
},
}
@ -212,7 +211,7 @@ impl Actionable for CreateUser {
uid = self.uid,
gid = self.gid,
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
name,
uid: _,
@ -240,12 +239,12 @@ impl Actionable for CreateUser {
&format!("/Users/{name}"),
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
},
_ => {
execute_command(Command::new("userdel").args([&name.to_string()]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateUserError::Command(e).boxed())?;
},
};
@ -255,12 +254,6 @@ impl Actionable for CreateUser {
}
}
impl From<CreateUser> for Action {
fn from(v: CreateUser) -> Self {
Action::CreateUser(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateUserError {
#[error("Failed to execute command")]

View file

@ -5,7 +5,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct BootstrapVolume {
@ -15,7 +15,9 @@ pub struct BootstrapVolume {
impl BootstrapVolume {
#[tracing::instrument(skip_all)]
pub async fn plan(path: impl AsRef<Path>) -> Result<Self, BootstrapVolumeError> {
pub async fn plan(
path: impl AsRef<Path>,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
Ok(Self {
path: path.as_ref().to_path_buf(),
action_state: ActionState::Uncompleted,
@ -24,9 +26,8 @@ impl BootstrapVolume {
}
#[async_trait::async_trait]
#[typetag::serde(name = "bootstrap-volume")]
impl Actionable for BootstrapVolume {
type Error = BootstrapVolumeError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -41,7 +42,7 @@ impl Actionable for BootstrapVolume {
#[tracing::instrument(skip_all, fields(
path = %self.path.display(),
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { path, action_state } = self;
if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Bootstrapping volume");
@ -55,14 +56,14 @@ impl Actionable for BootstrapVolume {
.arg(path),
)
.await
.map_err(Self::Error::Command)?;
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
execute_command(Command::new("launchctl").args([
"kickstart",
"-k",
"system/org.nixos.darwin-store",
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
tracing::trace!("Bootstrapped volume");
*action_state = ActionState::Completed;
@ -83,7 +84,7 @@ impl Actionable for BootstrapVolume {
#[tracing::instrument(skip_all, fields(
path = %self.path.display(),
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { path, action_state } = self;
if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Stop volume");
@ -97,7 +98,7 @@ impl Actionable for BootstrapVolume {
.arg(path),
)
.await
.map_err(Self::Error::Command)?;
.map_err(|e| BootstrapVolumeError::Command(e).boxed())?;
tracing::trace!("Stopped volume");
*action_state = ActionState::Completed;
@ -105,12 +106,6 @@ impl Actionable for BootstrapVolume {
}
}
impl From<BootstrapVolume> for Action {
fn from(v: BootstrapVolume) -> Self {
Action::DarwinBootstrapVolume(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum BootstrapVolumeError {
#[error("Failed to execute command")]

View file

@ -3,7 +3,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateSyntheticObjects {
@ -12,7 +12,7 @@ pub struct CreateSyntheticObjects {
impl CreateSyntheticObjects {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<Self, CreateSyntheticObjectsError> {
pub async fn plan() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
Ok(Self {
action_state: ActionState::Uncompleted,
})
@ -20,9 +20,8 @@ impl CreateSyntheticObjects {
}
#[async_trait::async_trait]
#[typetag::serde(name = "create-synthetic-objects")]
impl Actionable for CreateSyntheticObjects {
type Error = CreateSyntheticObjectsError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -35,7 +34,7 @@ impl Actionable for CreateSyntheticObjects {
}
#[tracing::instrument(skip_all, fields())]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { action_state } = self;
if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Creating synthetic objects");
@ -74,7 +73,7 @@ impl Actionable for CreateSyntheticObjects {
}
#[tracing::instrument(skip_all, fields())]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { action_state } = self;
if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Refreshing synthetic objects");
@ -102,12 +101,6 @@ impl Actionable for CreateSyntheticObjects {
}
}
impl From<CreateSyntheticObjects> for Action {
fn from(v: CreateSyntheticObjects) -> Self {
Action::DarwinCreateSyntheticObjects(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateSyntheticObjectsError {
#[error("Failed to execute command")]

View file

@ -5,7 +5,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateVolume {
@ -21,7 +21,7 @@ impl CreateVolume {
disk: impl AsRef<Path>,
name: String,
case_sensitive: bool,
) -> Result<Self, CreateVolumeError> {
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
Ok(Self {
disk: disk.as_ref().to_path_buf(),
name,
@ -32,9 +32,8 @@ impl CreateVolume {
}
#[async_trait::async_trait]
#[typetag::serde(name = "create-volume")]
impl Actionable for CreateVolume {
type Error = CreateVolumeError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -55,7 +54,7 @@ impl Actionable for CreateVolume {
name = %self.name,
case_sensitive = %self.case_sensitive,
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
disk,
name,
@ -81,7 +80,7 @@ impl Actionable for CreateVolume {
"-nomount",
]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateVolumeError::Command(e).boxed())?;
tracing::trace!("Created volume");
*action_state = ActionState::Completed;
@ -108,7 +107,7 @@ impl Actionable for CreateVolume {
name = %self.name,
case_sensitive = %self.case_sensitive,
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
disk: _,
name,
@ -123,7 +122,7 @@ impl Actionable for CreateVolume {
execute_command(Command::new("/usr/sbin/diskutil").args(["apfs", "deleteVolume", name]))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateVolumeError::Command(e).boxed())?;
tracing::trace!("Deleted volume");
*action_state = ActionState::Completed;
@ -131,12 +130,6 @@ impl Actionable for CreateVolume {
}
}
impl From<CreateVolume> for Action {
fn from(v: CreateVolume) -> Self {
Action::DarwinCreateVolume(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateVolumeError {
#[error("Failed to execute command")]

View file

@ -6,7 +6,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
use crate::os::darwin::DiskUtilOutput;
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
@ -17,7 +17,9 @@ pub struct EnableOwnership {
impl EnableOwnership {
#[tracing::instrument(skip_all)]
pub async fn plan(path: impl AsRef<Path>) -> Result<Self, EnableOwnershipError> {
pub async fn plan(
path: impl AsRef<Path>,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
Ok(Self {
path: path.as_ref().to_path_buf(),
action_state: ActionState::Uncompleted,
@ -26,9 +28,8 @@ impl EnableOwnership {
}
#[async_trait::async_trait]
#[typetag::serde(name = "enable-ownership")]
impl Actionable for EnableOwnership {
type Error = EnableOwnershipError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -43,7 +44,7 @@ impl Actionable for EnableOwnership {
#[tracing::instrument(skip_all, fields(
path = %self.path.display(),
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { path, action_state } = self;
if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Enabling ownership");
@ -72,7 +73,7 @@ impl Actionable for EnableOwnership {
.arg(path),
)
.await
.map_err(Self::Error::Command)?;
.map_err(|e| EnableOwnershipError::Command(e).boxed())?;
}
tracing::trace!("Enabled ownership");
@ -91,7 +92,7 @@ impl Actionable for EnableOwnership {
#[tracing::instrument(skip_all, fields(
path = %self.path.display(),
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
path: _,
action_state,
@ -108,12 +109,6 @@ impl Actionable for EnableOwnership {
}
}
impl From<EnableOwnership> for Action {
fn from(v: EnableOwnership) -> Self {
Action::DarwinEnableOwnership(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum EnableOwnershipError {
#[error("Failed to execute command")]

View file

@ -1,7 +1,7 @@
use serde::Serialize;
use std::path::{Path, PathBuf};
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct EncryptVolume {
@ -15,7 +15,7 @@ impl EncryptVolume {
pub async fn plan(
disk: impl AsRef<Path>,
password: String,
) -> Result<Self, EncryptVolumeError> {
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
Ok(Self {
disk: disk.as_ref().to_path_buf(),
password,
@ -25,9 +25,8 @@ impl EncryptVolume {
}
#[async_trait::async_trait]
#[typetag::serde(name = "encrypt-volume")]
impl Actionable for EncryptVolume {
type Error = EncryptVolumeError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -42,7 +41,7 @@ impl Actionable for EncryptVolume {
#[tracing::instrument(skip_all, fields(
disk = %self.disk.display(),
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
disk: _,
password: _,
@ -72,7 +71,7 @@ impl Actionable for EncryptVolume {
#[tracing::instrument(skip_all, fields(
disk = %self.disk.display(),
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
disk: _,
password: _,
@ -90,12 +89,6 @@ impl Actionable for EncryptVolume {
}
}
impl From<EncryptVolume> for Action {
fn from(v: EncryptVolume) -> Self {
Action::DarwinEncryptVolume(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum EncryptVolumeError {
#[error("Failed to execute command")]

View file

@ -3,7 +3,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct KickstartLaunchctlService {
@ -13,7 +13,7 @@ pub struct KickstartLaunchctlService {
impl KickstartLaunchctlService {
#[tracing::instrument(skip_all)]
pub async fn plan(unit: String) -> Result<Self, KickstartLaunchctlServiceError> {
pub async fn plan(unit: String) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
Ok(Self {
unit,
action_state: ActionState::Uncompleted,
@ -22,9 +22,8 @@ impl KickstartLaunchctlService {
}
#[async_trait::async_trait]
#[typetag::serde(name = "kickstart-launchctl-service")]
impl Actionable for KickstartLaunchctlService {
type Error = KickstartLaunchctlServiceError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { unit, action_state } = self;
if *action_state == ActionState::Completed {
@ -42,7 +41,7 @@ impl Actionable for KickstartLaunchctlService {
#[tracing::instrument(skip_all, fields(
unit = %self.unit,
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { unit, action_state } = self;
if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Kickstarting launchctl unit");
@ -57,7 +56,7 @@ impl Actionable for KickstartLaunchctlService {
.arg(unit),
)
.await
.map_err(KickstartLaunchctlServiceError::Command)?;
.map_err(|e| KickstartLaunchctlServiceError::Command(e).boxed())?;
tracing::trace!("Kickstarted launchctl unit");
*action_state = ActionState::Completed;
@ -80,7 +79,7 @@ impl Actionable for KickstartLaunchctlService {
#[tracing::instrument(skip_all, fields(
unit = %self.unit,
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { unit, action_state } = self;
if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Stopping launchctl unit");
@ -90,7 +89,7 @@ impl Actionable for KickstartLaunchctlService {
execute_command(Command::new("launchctl").arg("stop").arg(unit))
.await
.map_err(KickstartLaunchctlServiceError::Command)?;
.map_err(|e| KickstartLaunchctlServiceError::Command(e).boxed())?;
tracing::trace!("Stopped launchctl unit");
*action_state = ActionState::Completed;
@ -98,12 +97,6 @@ impl Actionable for KickstartLaunchctlService {
}
}
impl From<KickstartLaunchctlService> for Action {
fn from(v: KickstartLaunchctlService) -> Self {
Action::DarwinKickStartLaunchctlService(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum KickstartLaunchctlServiceError {
#[error("Failed to execute command")]

View file

@ -5,7 +5,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct UnmountVolume {
@ -16,7 +16,10 @@ pub struct UnmountVolume {
impl UnmountVolume {
#[tracing::instrument(skip_all)]
pub async fn plan(disk: impl AsRef<Path>, name: String) -> Result<Self, UnmountVolumeError> {
pub async fn plan(
disk: impl AsRef<Path>,
name: String,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let disk = disk.as_ref().to_owned();
Ok(Self {
disk,
@ -27,9 +30,8 @@ impl UnmountVolume {
}
#[async_trait::async_trait]
#[typetag::serde(name = "unmount-volume")]
impl Actionable for UnmountVolume {
type Error = UnmountVolumeError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -47,7 +49,7 @@ impl Actionable for UnmountVolume {
disk = %self.disk.display(),
name = %self.name,
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
disk: _,
name,
@ -65,7 +67,7 @@ impl Actionable for UnmountVolume {
.arg(name),
)
.await
.map_err(Self::Error::Command)?;
.map_err(|e| UnmountVolumeError::Command(e).boxed())?;
tracing::trace!("Unmounted volume");
*action_state = ActionState::Completed;
@ -89,7 +91,7 @@ impl Actionable for UnmountVolume {
disk = %self.disk.display(),
name = %self.name,
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
disk: _,
name,
@ -107,7 +109,7 @@ impl Actionable for UnmountVolume {
.arg(name),
)
.await
.map_err(Self::Error::Command)?;
.map_err(|e| UnmountVolumeError::Command(e).boxed())?;
tracing::trace!("Stopped systemd unit");
*action_state = ActionState::Completed;
@ -115,12 +117,6 @@ impl Actionable for UnmountVolume {
}
}
impl From<UnmountVolume> for Action {
fn from(v: UnmountVolume) -> Self {
Action::DarwinUnmountVolume(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum UnmountVolumeError {
#[error("Failed to execute command")]

View file

@ -5,7 +5,7 @@ use reqwest::Url;
use serde::Serialize;
use tokio::task::JoinError;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct FetchNix {
@ -29,9 +29,8 @@ impl FetchNix {
}
#[async_trait::async_trait]
#[typetag::serde(name = "fetch-nix")]
impl Actionable for FetchNix {
type Error = FetchNixError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
url,
@ -52,7 +51,7 @@ impl Actionable for FetchNix {
url = %self.url,
dest = %self.dest.display(),
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
url,
dest,
@ -66,8 +65,11 @@ impl Actionable for FetchNix {
let res = reqwest::get(url.clone())
.await
.map_err(Self::Error::Reqwest)?;
let bytes = res.bytes().await.map_err(Self::Error::Reqwest)?;
.map_err(|e| FetchNixError::Reqwest(e).boxed())?;
let bytes = res
.bytes()
.await
.map_err(|e| FetchNixError::Reqwest(e).boxed())?;
// TODO(@Hoverbear): Pick directory
tracing::trace!("Unpacking tar.xz");
let dest_clone = dest.clone();
@ -76,7 +78,7 @@ impl Actionable for FetchNix {
let mut archive = tar::Archive::new(decoder);
archive
.unpack(&dest_clone)
.map_err(Self::Error::Unarchive)?;
.map_err(|e| FetchNixError::Unarchive(e).boxed())?;
tracing::trace!("Fetched Nix");
*action_state = ActionState::Completed;
@ -95,7 +97,7 @@ impl Actionable for FetchNix {
url = %self.url,
dest = %self.dest.display(),
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
url: _,
dest: _,
@ -112,12 +114,6 @@ impl Actionable for FetchNix {
}
}
impl From<FetchNix> for Action {
fn from(v: FetchNix) -> Self {
Action::FetchNix(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum FetchNixError {
#[error("Joining spawned async task")]

View file

@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};
use serde::Serialize;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
const DEST: &str = "/nix/store";
@ -24,9 +24,8 @@ impl MoveUnpackedNix {
}
#[async_trait::async_trait]
#[typetag::serde(name = "mount-unpacked-nix")]
impl Actionable for MoveUnpackedNix {
type Error = MoveUnpackedNixError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -45,7 +44,7 @@ impl Actionable for MoveUnpackedNix {
src = %self.src.display(),
dest = DEST,
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { src, action_state } = self;
if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Moving Nix");
@ -54,8 +53,10 @@ impl Actionable for MoveUnpackedNix {
tracing::debug!("Moving Nix");
// TODO(@Hoverbear): I would like to make this less awful
let found_nix_paths =
glob::glob(&format!("{}/nix-*", src.display()))?.collect::<Result<Vec<_>, _>>()?;
let found_nix_paths = glob::glob(&format!("{}/nix-*", src.display()))
.map_err(|e| e.boxed())?
.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.boxed())?;
assert_eq!(
found_nix_paths.len(),
1,
@ -67,11 +68,13 @@ impl Actionable for MoveUnpackedNix {
let dest = Path::new(DEST);
tokio::fs::rename(src_store.clone(), dest)
.await
.map_err(|e| MoveUnpackedNixError::Rename(src_store.clone(), dest.to_owned(), e))?;
.map_err(|e| {
MoveUnpackedNixError::Rename(src_store.clone(), dest.to_owned(), e).boxed()
})?;
tokio::fs::remove_dir_all(src)
.await
.map_err(|e| MoveUnpackedNixError::Rename(src_store, dest.to_owned(), e))?;
.map_err(|e| MoveUnpackedNixError::Rename(src_store, dest.to_owned(), e).boxed())?;
tracing::trace!("Moved Nix");
*action_state = ActionState::Completed;
Ok(())
@ -89,7 +92,7 @@ impl Actionable for MoveUnpackedNix {
src = %self.src.display(),
dest = DEST,
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
src: _,
action_state,
@ -104,12 +107,6 @@ impl Actionable for MoveUnpackedNix {
}
}
impl From<MoveUnpackedNix> for Action {
fn from(v: MoveUnpackedNix) -> Self {
Action::MoveUnpackedNix(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum MoveUnpackedNixError {
#[error("Glob pattern error")]

View file

@ -1,5 +1,5 @@
use crate::{
actions::{Action, ActionState},
actions::{ActionError, ActionState},
execute_command, set_env,
};
@ -26,9 +26,8 @@ impl SetupDefaultProfile {
}
#[async_trait::async_trait]
#[typetag::serde(name = "setup-default-profile")]
impl Actionable for SetupDefaultProfile {
type Error = SetupDefaultProfileError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -43,7 +42,7 @@ impl Actionable for SetupDefaultProfile {
#[tracing::instrument(skip_all, fields(
channels = %self.channels.join(","),
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
channels,
action_state,
@ -57,7 +56,9 @@ impl Actionable for SetupDefaultProfile {
// Find an `nix` package
let nix_pkg_glob = "/nix/store/*-nix-*";
let mut found_nix_pkg = None;
for entry in glob(nix_pkg_glob).map_err(Self::Error::GlobPatternError)? {
for entry in
glob(nix_pkg_glob).map_err(|e| SetupDefaultProfileError::GlobPatternError(e).boxed())?
{
match entry {
Ok(path) => {
// TODO(@Hoverbear): Should probably ensure is unique
@ -70,13 +71,15 @@ impl Actionable for SetupDefaultProfile {
let nix_pkg = if let Some(nix_pkg) = found_nix_pkg {
nix_pkg
} else {
return Err(Self::Error::NoNssCacert); // TODO(@hoverbear): Fix this error
return Err(Box::new(SetupDefaultProfileError::NoNssCacert)); // TODO(@hoverbear): Fix this error
};
// Find an `nss-cacert` package, add it too.
let nss_ca_cert_pkg_glob = "/nix/store/*-nss-cacert-*";
let mut found_nss_ca_cert_pkg = None;
for entry in glob(nss_ca_cert_pkg_glob).map_err(Self::Error::GlobPatternError)? {
for entry in glob(nss_ca_cert_pkg_glob)
.map_err(|e| SetupDefaultProfileError::GlobPatternError(e).boxed())?
{
match entry {
Ok(path) => {
// TODO(@Hoverbear): Should probably ensure is unique
@ -89,7 +92,7 @@ impl Actionable for SetupDefaultProfile {
let nss_ca_cert_pkg = if let Some(nss_ca_cert_pkg) = found_nss_ca_cert_pkg {
nss_ca_cert_pkg
} else {
return Err(Self::Error::NoNssCacert);
return Err(Box::new(SetupDefaultProfileError::NoNssCacert));
};
// Install `nix` itself into the store
@ -97,14 +100,17 @@ impl Actionable for SetupDefaultProfile {
Command::new(nix_pkg.join("bin/nix-env"))
.arg("-i")
.arg(&nix_pkg)
.env("HOME", dirs::home_dir().ok_or(Self::Error::NoRootHome)?)
.env(
"HOME",
dirs::home_dir().ok_or_else(|| SetupDefaultProfileError::NoRootHome.boxed())?,
)
.env(
"NIX_SSL_CERT_FILE",
nss_ca_cert_pkg.join("etc/ssl/certs/ca-bundle.crt"),
), /* This is apparently load bearing... */
)
.await
.map_err(SetupDefaultProfileError::Command)?;
.map_err(|e| SetupDefaultProfileError::Command(e).boxed())?;
// Install `nss-cacert` into the store
execute_command(
@ -117,7 +123,7 @@ impl Actionable for SetupDefaultProfile {
),
)
.await
.map_err(SetupDefaultProfileError::Command)?;
.map_err(|e| SetupDefaultProfileError::Command(e).boxed())?;
set_env(
"NIX_SSL_CERT_FILE",
@ -137,7 +143,7 @@ impl Actionable for SetupDefaultProfile {
execute_command(&mut command)
.await
.map_err(SetupDefaultProfileError::Command)?;
.map_err(|e| SetupDefaultProfileError::Command(e).boxed())?;
}
tracing::trace!("Set up default profile");
@ -159,7 +165,7 @@ impl Actionable for SetupDefaultProfile {
#[tracing::instrument(skip_all, fields(
channels = %self.channels.join(","),
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
channels: _,
action_state,
@ -178,12 +184,6 @@ impl Actionable for SetupDefaultProfile {
}
}
impl From<SetupDefaultProfile> for Action {
fn from(v: SetupDefaultProfile) -> Self {
Action::SetupDefaultProfile(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum SetupDefaultProfileError {
#[error("Glob pattern error")]

View file

@ -3,7 +3,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct StartSystemdUnit {
@ -13,7 +13,7 @@ pub struct StartSystemdUnit {
impl StartSystemdUnit {
#[tracing::instrument(skip_all)]
pub async fn plan(unit: String) -> Result<Self, StartSystemdUnitError> {
pub async fn plan(unit: String) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
Ok(Self {
unit,
action_state: ActionState::Uncompleted,
@ -22,9 +22,8 @@ impl StartSystemdUnit {
}
#[async_trait::async_trait]
#[typetag::serde(name = "start-systemd-unit")]
impl Actionable for StartSystemdUnit {
type Error = StartSystemdUnitError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -41,7 +40,7 @@ impl Actionable for StartSystemdUnit {
#[tracing::instrument(skip_all, fields(
unit = %self.unit,
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { unit, action_state } = self;
if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Starting systemd unit");
@ -57,7 +56,7 @@ impl Actionable for StartSystemdUnit {
.arg(format!("{unit}")),
)
.await
.map_err(StartSystemdUnitError::Command)?;
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
tracing::trace!("Started systemd unit");
*action_state = ActionState::Completed;
@ -80,7 +79,7 @@ impl Actionable for StartSystemdUnit {
#[tracing::instrument(skip_all, fields(
unit = %self.unit,
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self { unit, action_state } = self;
if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Stopping systemd unit");
@ -91,7 +90,7 @@ impl Actionable for StartSystemdUnit {
// TODO(@Hoverbear): Handle proxy vars
execute_command(Command::new("systemctl").arg("stop").arg(format!("{unit}")))
.await
.map_err(StartSystemdUnitError::Command)?;
.map_err(|e| StartSystemdUnitError::Command(e).boxed())?;
tracing::trace!("Stopped systemd unit");
*action_state = ActionState::Completed;
@ -99,12 +98,6 @@ impl Actionable for StartSystemdUnit {
}
}
impl From<StartSystemdUnit> for Action {
fn from(v: StartSystemdUnit) -> Self {
Action::StartSystemdUnit(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum StartSystemdUnitError {
#[error("Failed to execute command")]

View file

@ -5,7 +5,7 @@ use tokio::process::Command;
use crate::execute_command;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct SystemdSysextMerge {
@ -24,9 +24,8 @@ impl SystemdSysextMerge {
}
#[async_trait::async_trait]
#[typetag::serde(name = "systemd-sysext-merge")]
impl Actionable for SystemdSysextMerge {
type Error = SystemdSysextMergeError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
action_state,
@ -45,7 +44,7 @@ impl Actionable for SystemdSysextMerge {
#[tracing::instrument(skip_all, fields(
device = %self.device.display(),
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
device,
action_state,
@ -58,7 +57,7 @@ impl Actionable for SystemdSysextMerge {
execute_command(Command::new("systemd-sysext").arg("merge").arg(device))
.await
.map_err(Self::Error::Command)?;
.map_err(|e| SystemdSysextMergeError::Command(e).boxed())?;
tracing::trace!("Merged systemd-sysext");
*action_state = ActionState::Completed;
@ -81,7 +80,7 @@ impl Actionable for SystemdSysextMerge {
#[tracing::instrument(skip_all, fields(
device = %self.device.display(),
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
device,
action_state,
@ -95,7 +94,7 @@ impl Actionable for SystemdSysextMerge {
// TODO(@Hoverbear): Handle proxy vars
execute_command(Command::new("systemd-sysext").arg("unmerge").arg(device))
.await
.map_err(SystemdSysextMergeError::Command)?;
.map_err(|e| SystemdSysextMergeError::Command(e).boxed())?;
tracing::trace!("Unmerged systemd-sysext");
*action_state = ActionState::Completed;
@ -103,12 +102,6 @@ impl Actionable for SystemdSysextMerge {
}
}
impl From<SystemdSysextMerge> for Action {
fn from(v: SystemdSysextMerge) -> Self {
Action::SystemdSysextMerge(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum SystemdSysextMergeError {
#[error("Failed to execute command")]

View file

@ -12,12 +12,12 @@ use crate::{
ConfigureShellProfile, ConfigureShellProfileError, PlaceChannelConfiguration,
PlaceChannelConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError,
},
Action, ActionState,
ActionState,
},
cli::arg::ChannelValue,
};
use crate::actions::{ActionDescription, Actionable};
use crate::actions::{ActionDescription, ActionError, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct ConfigureNix {
@ -31,7 +31,9 @@ pub struct ConfigureNix {
impl ConfigureNix {
#[tracing::instrument(skip_all)]
pub async fn plan(settings: CommonSettings) -> Result<Self, ConfigureNixError> {
pub async fn plan(
settings: CommonSettings,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let channels: Vec<(String, Url)> = settings
.channels
.iter()
@ -39,7 +41,9 @@ impl ConfigureNix {
.collect();
let setup_default_profile =
SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect()).await?;
SetupDefaultProfile::plan(channels.iter().map(|(v, _k)| v.clone()).collect())
.await
.map_err(|e| e.boxed())?;
let configure_shell_profile = if settings.modify_profile {
Some(ConfigureShellProfile::plan().await?)
@ -68,8 +72,8 @@ impl ConfigureNix {
}
#[async_trait::async_trait]
#[typetag::serde(name = "configure-nix")]
impl Actionable for ConfigureNix {
type Error = ConfigureNixError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
setup_default_profile,
@ -95,7 +99,7 @@ impl Actionable for ConfigureNix {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
setup_default_profile,
configure_nix_daemon_service,
@ -113,51 +117,16 @@ impl Actionable for ConfigureNix {
if let Some(configure_shell_profile) = configure_shell_profile {
tokio::try_join!(
async move {
setup_default_profile
.execute()
.await
.map_err(|e| ConfigureNixError::from(e))
},
async move {
place_nix_configuration
.execute()
.await
.map_err(|e| ConfigureNixError::from(e))
},
async move {
place_channel_configuration
.execute()
.await
.map_err(|e| ConfigureNixError::from(e))
},
async move {
configure_shell_profile
.execute()
.await
.map_err(|e| ConfigureNixError::from(e))
},
async move { setup_default_profile.execute().await },
async move { place_nix_configuration.execute().await },
async move { place_channel_configuration.execute().await },
async move { configure_shell_profile.execute().await },
)?;
} else {
tokio::try_join!(
async move {
setup_default_profile
.execute()
.await
.map_err(|e| ConfigureNixError::from(e))
},
async move {
place_nix_configuration
.execute()
.await
.map_err(|e| ConfigureNixError::from(e))
},
async move {
place_channel_configuration
.execute()
.await
.map_err(|e| ConfigureNixError::from(e))
},
async move { setup_default_profile.execute().await },
async move { place_nix_configuration.execute().await },
async move { place_channel_configuration.execute().await },
)?;
};
configure_nix_daemon_service.execute().await?;
@ -194,7 +163,7 @@ impl Actionable for ConfigureNix {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
setup_default_profile,
configure_nix_daemon_service,
@ -223,43 +192,3 @@ impl Actionable for ConfigureNix {
Ok(())
}
}
impl From<ConfigureNix> for Action {
fn from(v: ConfigureNix) -> Self {
Action::ConfigureNix(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum ConfigureNixError {
#[error("Setting up default profile")]
SetupDefaultProfile(
#[source]
#[from]
SetupDefaultProfileError,
),
#[error("Placing Nix configuration")]
PlaceNixConfiguration(
#[source]
#[from]
PlaceNixConfigurationError,
),
#[error("Placing channel configuration")]
PlaceChannelConfiguration(
#[source]
#[from]
PlaceChannelConfigurationError,
),
#[error("Configuring Nix daemon")]
ConfigureNixDaemonService(
#[source]
#[from]
ConfigureNixDaemonServiceError,
),
#[error("Configuring shell profile")]
ConfigureShellProfile(
#[source]
#[from]
ConfigureShellProfileError,
),
}

View file

@ -4,7 +4,7 @@ use serde::Serialize;
use tokio::task::{JoinError, JoinSet};
use crate::actions::base::{CreateOrAppendFile, CreateOrAppendFileError};
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
const PROFILE_TARGETS: &[&str] = &[
"/etc/bashrc",
@ -24,7 +24,7 @@ pub struct ConfigureShellProfile {
impl ConfigureShellProfile {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<Self, ConfigureShellProfileError> {
pub async fn plan() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let mut create_or_append_files = Vec::default();
for profile_target in PROFILE_TARGETS {
let path = Path::new(profile_target);
@ -41,8 +41,11 @@ impl ConfigureShellProfile {
# End Nix\n
\n",
);
create_or_append_files
.push(CreateOrAppendFile::plan(path, None, None, 0o0644, buf).await?);
create_or_append_files.push(
CreateOrAppendFile::plan(path, None, None, 0o0644, buf)
.await
.map_err(|e| e.boxed())?,
);
}
Ok(Self {
@ -53,9 +56,8 @@ impl ConfigureShellProfile {
}
#[async_trait::async_trait]
#[typetag::serde(name = "configure-shell-profile")]
impl Actionable for ConfigureShellProfile {
type Error = ConfigureShellProfileError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -68,7 +70,7 @@ impl Actionable for ConfigureShellProfile {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
create_or_append_files,
action_state,
@ -87,7 +89,10 @@ impl Actionable for ConfigureShellProfile {
let mut create_or_append_file_clone = create_or_append_file.clone();
let _abort_handle = set.spawn(async move {
create_or_append_file_clone.execute().await?;
Result::<_, CreateOrAppendFileError>::Ok((idx, create_or_append_file_clone))
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((
idx,
create_or_append_file_clone,
))
});
}
@ -97,7 +102,7 @@ impl Actionable for ConfigureShellProfile {
create_or_append_files[idx] = create_or_append_file
},
Ok(Err(e)) => errors.push(e),
Err(e) => return Err(e.into()),
Err(e) => return Err(e.boxed()),
};
}
@ -105,9 +110,7 @@ impl Actionable for ConfigureShellProfile {
if errors.len() == 1 {
return Err(errors.into_iter().next().unwrap().into());
} else {
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(
errors,
));
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed());
}
}
@ -128,7 +131,7 @@ impl Actionable for ConfigureShellProfile {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
create_or_append_files,
action_state,
@ -147,7 +150,10 @@ impl Actionable for ConfigureShellProfile {
let mut create_or_append_file_clone = create_or_append_file.clone();
let _abort_handle = set.spawn(async move {
create_or_append_file_clone.revert().await?;
Result::<_, CreateOrAppendFileError>::Ok((idx, create_or_append_file_clone))
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((
idx,
create_or_append_file_clone,
))
});
}
@ -157,7 +163,7 @@ impl Actionable for ConfigureShellProfile {
create_or_append_files[idx] = create_or_append_file
},
Ok(Err(e)) => errors.push(e),
Err(e) => return Err(e.into()),
Err(e) => return Err(e.boxed()),
};
}
@ -165,9 +171,7 @@ impl Actionable for ConfigureShellProfile {
if errors.len() == 1 {
return Err(errors.into_iter().next().unwrap().into());
} else {
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(
errors,
));
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed());
}
}
@ -177,13 +181,7 @@ impl Actionable for ConfigureShellProfile {
}
}
impl From<ConfigureShellProfile> for Action {
fn from(v: ConfigureShellProfile) -> Self {
Action::ConfigureShellProfile(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
#[derive(Debug, thiserror::Error)]
pub enum ConfigureShellProfileError {
#[error("Creating or appending to file")]
CreateOrAppendFile(
@ -192,12 +190,11 @@ pub enum ConfigureShellProfileError {
CreateOrAppendFileError,
),
#[error("Multiple errors: {}", .0.iter().map(|v| format!("{v}")).collect::<Vec<_>>().join(" & "))]
MultipleCreateOrAppendFile(Vec<CreateOrAppendFileError>),
MultipleCreateOrAppendFile(Vec<Box<dyn std::error::Error + Send + Sync>>),
#[error("Joining spawned async task")]
Join(
#[source]
#[from]
#[serde(serialize_with = "crate::serialize_error_to_display")]
JoinError,
),
}

View file

@ -1,7 +1,7 @@
use serde::Serialize;
use crate::actions::base::{CreateDirectory, CreateDirectoryError};
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
const PATHS: &[&str] = &[
"/nix/var",
@ -27,7 +27,7 @@ pub struct CreateNixTree {
impl CreateNixTree {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<Self, CreateNixTreeError> {
pub async fn plan() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let mut create_directories = Vec::default();
for path in PATHS {
// We use `create_dir` over `create_dir_all` to ensure we always set permissions right
@ -42,9 +42,8 @@ impl CreateNixTree {
}
#[async_trait::async_trait]
#[typetag::serde(name = "creat-nix-tree")]
impl Actionable for CreateNixTree {
type Error = CreateNixTreeError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -69,7 +68,7 @@ impl Actionable for CreateNixTree {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
create_directories,
action_state,
@ -116,7 +115,7 @@ impl Actionable for CreateNixTree {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
create_directories,
action_state,
@ -139,12 +138,6 @@ impl Actionable for CreateNixTree {
}
}
impl From<CreateNixTree> for Action {
fn from(v: CreateNixTree) -> Self {
Action::CreateNixTree(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateNixTreeError {
#[error("Creating directory")]

View file

@ -2,7 +2,7 @@ use serde::Serialize;
use std::path::{Path, PathBuf};
use crate::actions::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
const PATHS: &[&str] = &[
"usr",
@ -23,7 +23,9 @@ pub struct CreateSystemdSysext {
impl CreateSystemdSysext {
#[tracing::instrument(skip_all)]
pub async fn plan(destination: impl AsRef<Path>) -> Result<Self, CreateSystemdSysextError> {
pub async fn plan(
destination: impl AsRef<Path>,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let destination = destination.as_ref();
let mut create_directories =
@ -43,8 +45,8 @@ impl CreateSystemdSysext {
false => None,
})
})
.map_err(CreateSystemdSysextError::ReadingOsRelease)?
.ok_or(CreateSystemdSysextError::NoVersionId)?;
.map_err(|e| CreateSystemdSysextError::ReadingOsRelease(e).boxed())?
.ok_or_else(|| CreateSystemdSysextError::NoVersionId.boxed())?;
let extension_release_buf =
format!("SYSEXT_LEVEL=1.0\nID=steamos\nVERSION_ID={version_id}");
let create_extension_release = CreateFile::plan(
@ -88,9 +90,8 @@ impl CreateSystemdSysext {
}
#[async_trait::async_trait]
#[typetag::serde(name = "create-systemd-sysext")]
impl Actionable for CreateSystemdSysext {
type Error = CreateSystemdSysextError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
action_state: _,
@ -112,7 +113,7 @@ impl Actionable for CreateSystemdSysext {
}
#[tracing::instrument(skip_all, fields(destination,))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
destination: _,
action_state,
@ -156,7 +157,7 @@ impl Actionable for CreateSystemdSysext {
}
#[tracing::instrument(skip_all, fields(destination,))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
destination: _,
action_state,
@ -184,12 +185,6 @@ impl Actionable for CreateSystemdSysext {
}
}
impl From<CreateSystemdSysext> for Action {
fn from(v: CreateSystemdSysext) -> Self {
Action::CreateSystemdSysext(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateSystemdSysextError {
#[error(transparent)]

View file

@ -4,7 +4,7 @@ use tokio::task::{JoinError, JoinSet};
use crate::CommonSettings;
use crate::actions::base::{CreateGroup, CreateGroupError, CreateUserError};
use crate::actions::{Action, ActionDescription, ActionState, Actionable, CreateUser};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable, CreateUser};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateUsersAndGroup {
@ -51,9 +51,8 @@ impl CreateUsersAndGroup {
}
#[async_trait::async_trait]
#[typetag::serde(name = "create-users-and-group")]
impl Actionable for CreateUsersAndGroup {
type Error = CreateUsersAndGroupError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
daemon_user_count,
@ -88,7 +87,7 @@ impl Actionable for CreateUsersAndGroup {
nix_build_user_prefix = self.nix_build_user_prefix,
nix_build_user_id_base = self.nix_build_user_id_base,
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
create_users,
create_group,
@ -173,7 +172,7 @@ impl Actionable for CreateUsersAndGroup {
nix_build_user_prefix = self.nix_build_user_prefix,
nix_build_user_id_base = self.nix_build_user_id_base,
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
create_users,
create_group,
@ -199,7 +198,7 @@ impl Actionable for CreateUsersAndGroup {
let mut create_user_clone = create_user.clone();
let _abort_handle = set.spawn(async move {
create_user_clone.revert().await?;
Result::<_, CreateUserError>::Ok((idx, create_user_clone))
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok((idx, create_user_clone))
});
}
@ -207,7 +206,7 @@ impl Actionable for CreateUsersAndGroup {
match result {
Ok(Ok((idx, success))) => create_users[idx] = success,
Ok(Err(e)) => errors.push(e),
Err(e) => return Err(e)?,
Err(e) => return Err(e.boxed())?,
};
}
@ -215,7 +214,7 @@ impl Actionable for CreateUsersAndGroup {
if errors.len() == 1 {
return Err(errors.into_iter().next().unwrap().into());
} else {
return Err(CreateUsersAndGroupError::CreateUsers(errors));
return Err(CreateUsersAndGroupError::CreateUsers(errors).boxed());
}
}
@ -228,13 +227,7 @@ impl Actionable for CreateUsersAndGroup {
}
}
impl From<CreateUsersAndGroup> for Action {
fn from(v: CreateUsersAndGroup) -> Self {
Action::CreateUsersAndGroup(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
#[derive(Debug, thiserror::Error)]
pub enum CreateUsersAndGroupError {
#[error("Creating user")]
CreateUser(
@ -243,7 +236,7 @@ pub enum CreateUsersAndGroupError {
CreateUserError,
),
#[error("Multiple errors: {}", .0.iter().map(|v| format!("{v}")).collect::<Vec<_>>().join(" & "))]
CreateUsers(Vec<CreateUserError>),
CreateUsers(Vec<Box<dyn std::error::Error + Send + Sync>>),
#[error("Creating group")]
CreateGroup(
#[source]
@ -254,7 +247,6 @@ pub enum CreateUsersAndGroupError {
Join(
#[source]
#[from]
#[serde(serialize_with = "crate::serialize_error_to_display")]
JoinError,
),
}

View file

@ -5,15 +5,19 @@ use std::{
};
use tokio::process::Command;
use crate::actions::base::{
darwin::{
BootstrapVolume, BootstrapVolumeError, CreateSyntheticObjects, CreateSyntheticObjectsError,
CreateVolume, CreateVolumeError, EnableOwnership, EnableOwnershipError, EncryptVolume,
EncryptVolumeError, UnmountVolume, UnmountVolumeError,
use crate::actions::{
base::{
darwin::{
BootstrapVolume, BootstrapVolumeError, CreateSyntheticObjects,
CreateSyntheticObjectsError, CreateVolume, CreateVolumeError, EnableOwnership,
EnableOwnershipError, EncryptVolume, EncryptVolumeError, UnmountVolume,
UnmountVolumeError,
},
CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError,
},
CreateFile, CreateFileError, CreateOrAppendFile, CreateOrAppendFileError,
ActionError,
};
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionState, Actionable};
const NIX_VOLUME_MOUNTD_DEST: &str = "/Library/LaunchDaemons/org.nixos.darwin-store.plist";
@ -42,7 +46,7 @@ impl CreateApfsVolume {
name: String,
case_sensitive: bool,
encrypt: Option<String>,
) -> Result<Self, CreateApfsVolumeError> {
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let disk = disk.as_ref();
let create_or_append_synthetic_conf = CreateOrAppendFile::plan(
"/etc/synthetic.conf",
@ -51,7 +55,8 @@ impl CreateApfsVolume {
0o0655,
"nix\n".into(), /* The newline is required otherwise it segfaults */
)
.await?;
.await
.map_err(|e| e.boxed())?;
let create_synthetic_objects = CreateSyntheticObjects::plan().await?;
@ -66,7 +71,8 @@ impl CreateApfsVolume {
0o0655,
format!("NAME=\"{name}\" /nix apfs rw,noauto,nobrowse,suid,owners"),
)
.await?;
.await
.map_err(|e| e.boxed())?;
let encrypt_volume = if let Some(password) = encrypt.as_ref() {
Some(EncryptVolume::plan(disk, password.to_string()).await?)
@ -140,9 +146,8 @@ impl CreateApfsVolume {
}
#[async_trait::async_trait]
#[typetag::serde(name = "create-apfs-volume")]
impl Actionable for CreateApfsVolume {
type Error = CreateApfsVolumeError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
disk,
@ -163,7 +168,7 @@ impl Actionable for CreateApfsVolume {
}
#[tracing::instrument(skip_all, fields(destination,))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
disk: _,
name: _,
@ -207,7 +212,7 @@ impl Actionable for CreateApfsVolume {
.stdout(std::process::Stdio::null())
.status()
.await
.map_err(Self::Error::Command)?;
.map_err(|e| CreateApfsVolumeError::Command(e).boxed())?;
if status.success() || retry_tokens == 0 {
break;
} else {
@ -243,7 +248,7 @@ impl Actionable for CreateApfsVolume {
}
#[tracing::instrument(skip_all, fields(disk, name))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
disk: _,
name: _,
@ -287,12 +292,6 @@ impl Actionable for CreateApfsVolume {
}
}
impl From<CreateApfsVolume> for Action {
fn from(v: CreateApfsVolume) -> Self {
Action::CreateApfsVolume(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateApfsVolumeError {
#[error(transparent)]

View file

@ -10,7 +10,7 @@ mod place_channel_configuration;
mod place_nix_configuration;
mod provision_nix;
pub use configure_nix::{ConfigureNix, ConfigureNixError};
pub use configure_nix::ConfigureNix;
pub use configure_shell_profile::{ConfigureShellProfile, ConfigureShellProfileError};
pub use create_nix_tree::{CreateNixTree, CreateNixTreeError};
pub use create_systemd_sysext::{CreateSystemdSysext, CreateSystemdSysextError};

View file

@ -1,7 +1,7 @@
use reqwest::Url;
use serde::Serialize;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
use crate::actions::base::{CreateFile, CreateFileError};
@ -17,7 +17,7 @@ impl PlaceChannelConfiguration {
pub async fn plan(
channels: Vec<(String, Url)>,
force: bool,
) -> Result<Self, PlaceChannelConfigurationError> {
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let buf = channels
.iter()
.map(|(name, url)| format!("{} {}", url, name))
@ -25,7 +25,7 @@ impl PlaceChannelConfiguration {
.join("\n");
let create_file = CreateFile::plan(
dirs::home_dir()
.ok_or(PlaceChannelConfigurationError::NoRootHome)?
.ok_or_else(|| PlaceChannelConfigurationError::NoRootHome.boxed())?
.join(".nix-channels"),
None,
None,
@ -43,9 +43,8 @@ impl PlaceChannelConfiguration {
}
#[async_trait::async_trait]
#[typetag::serde(name = "place-channel-configuration")]
impl Actionable for PlaceChannelConfiguration {
type Error = PlaceChannelConfigurationError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
channels: _,
@ -68,7 +67,7 @@ impl Actionable for PlaceChannelConfiguration {
#[tracing::instrument(skip_all, fields(
channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::<Vec<_>>().join(", "),
))]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
create_file,
channels: _,
@ -110,7 +109,7 @@ impl Actionable for PlaceChannelConfiguration {
#[tracing::instrument(skip_all, fields(
channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::<Vec<_>>().join(", "),
))]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
create_file,
channels: _,
@ -131,12 +130,6 @@ impl Actionable for PlaceChannelConfiguration {
}
}
impl From<PlaceChannelConfiguration> for Action {
fn from(v: PlaceChannelConfiguration) -> Self {
Action::PlaceChannelConfiguration(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum PlaceChannelConfigurationError {
#[error("Creating file")]

View file

@ -1,6 +1,6 @@
use serde::Serialize;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
use crate::actions::base::{CreateDirectory, CreateDirectoryError, CreateFile, CreateFileError};
@ -20,7 +20,7 @@ impl PlaceNixConfiguration {
nix_build_group_name: String,
extra_conf: Option<String>,
force: bool,
) -> Result<Self, PlaceNixConfigurationError> {
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let buf = format!(
"\
{extra_conf}\n\
@ -44,9 +44,8 @@ impl PlaceNixConfiguration {
}
#[async_trait::async_trait]
#[typetag::serde(name = "place-nix-configuration")]
impl Actionable for PlaceNixConfiguration {
type Error = PlaceNixConfigurationError;
fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed {
vec![]
@ -62,7 +61,7 @@ impl Actionable for PlaceNixConfiguration {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
create_file,
create_directory,
@ -98,7 +97,7 @@ impl Actionable for PlaceNixConfiguration {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
create_file,
create_directory,
@ -120,12 +119,6 @@ impl Actionable for PlaceNixConfiguration {
}
}
impl From<PlaceNixConfiguration> for Action {
fn from(v: PlaceNixConfiguration) -> Self {
Action::PlaceNixConfiguration(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum PlaceNixConfigurationError {
#[error("Creating file")]

View file

@ -8,7 +8,7 @@ use crate::actions::base::{
};
use crate::CommonSettings;
use crate::actions::{Action, ActionDescription, ActionState, Actionable};
use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
use super::{CreateNixTree, CreateNixTreeError, CreateUsersAndGroup, CreateUsersAndGroupError};
@ -23,16 +23,22 @@ pub struct ProvisionNix {
impl ProvisionNix {
#[tracing::instrument(skip_all)]
pub async fn plan(settings: CommonSettings) -> Result<Self, ProvisionNixError> {
pub async fn plan(
settings: CommonSettings,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let fetch_nix = FetchNix::plan(
settings.nix_package_url.clone(),
PathBuf::from("/nix/temp-install-dir"),
)
.await?;
let create_users_and_group = CreateUsersAndGroup::plan(settings.clone()).await?;
.await
.map_err(|e| e.boxed())?;
let create_users_and_group = CreateUsersAndGroup::plan(settings.clone())
.await
.map_err(|e| e.boxed())?;
let create_nix_tree = CreateNixTree::plan().await?;
let move_unpacked_nix =
MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir")).await?;
let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir"))
.await
.map_err(|e| e.boxed())?;
Ok(Self {
fetch_nix,
create_users_and_group,
@ -44,8 +50,8 @@ impl ProvisionNix {
}
#[async_trait::async_trait]
#[typetag::serde(name = "provision-nix")]
impl Actionable for ProvisionNix {
type Error = ProvisionNixError;
fn describe_execute(&self) -> Vec<ActionDescription> {
let Self {
fetch_nix,
@ -68,7 +74,7 @@ impl Actionable for ProvisionNix {
}
#[tracing::instrument(skip_all)]
async fn execute(&mut self) -> Result<(), Self::Error> {
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
fetch_nix,
create_nix_tree,
@ -87,16 +93,13 @@ impl Actionable for ProvisionNix {
let mut fetch_nix_clone = fetch_nix.clone();
let fetch_nix_handle = tokio::task::spawn(async {
fetch_nix_clone.execute().await?;
Result::<_, Self::Error>::Ok(fetch_nix_clone)
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok(fetch_nix_clone)
});
create_users_and_group.execute().await?;
create_nix_tree
.execute()
.await
.map_err(ProvisionNixError::from)?;
create_nix_tree.execute().await?;
*fetch_nix = fetch_nix_handle.await.map_err(ProvisionNixError::from)??;
*fetch_nix = fetch_nix_handle.await.map_err(|e| e.boxed())??;
move_unpacked_nix.execute().await?;
tracing::trace!("Provisioned Nix");
@ -125,7 +128,7 @@ impl Actionable for ProvisionNix {
}
#[tracing::instrument(skip_all)]
async fn revert(&mut self) -> Result<(), Self::Error> {
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let Self {
fetch_nix,
create_nix_tree,
@ -144,19 +147,19 @@ impl Actionable for ProvisionNix {
let mut fetch_nix_clone = fetch_nix.clone();
let fetch_nix_handle = tokio::task::spawn(async {
fetch_nix_clone.revert().await?;
Result::<_, Self::Error>::Ok(fetch_nix_clone)
Result::<_, Box<dyn std::error::Error + Send + Sync>>::Ok(fetch_nix_clone)
});
if let Err(err) = create_users_and_group.revert().await {
fetch_nix_handle.abort();
return Err(Self::Error::from(err));
return Err(err);
}
if let Err(err) = create_nix_tree.revert().await {
fetch_nix_handle.abort();
return Err(Self::Error::from(err));
return Err(err);
}
*fetch_nix = fetch_nix_handle.await.map_err(ProvisionNixError::from)??;
*fetch_nix = fetch_nix_handle.await.map_err(|e| e.boxed())??;
move_unpacked_nix.revert().await?;
tracing::trace!("Unprovisioned Nix");
@ -165,13 +168,7 @@ impl Actionable for ProvisionNix {
}
}
impl From<ProvisionNix> for Action {
fn from(v: ProvisionNix) -> Self {
Action::ProvisionNix(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
#[derive(Debug, thiserror::Error)]
pub enum ProvisionNixError {
#[error("Fetching Nix")]
FetchNix(
@ -183,7 +180,6 @@ pub enum ProvisionNixError {
Join(
#[source]
#[from]
#[serde(serialize_with = "crate::serialize_error_to_display")]
JoinError,
),
#[error("Creating directory")]

View file

@ -11,26 +11,37 @@ use base::{
};
use meta::{
darwin::{CreateApfsVolume, CreateApfsVolumeError},
ConfigureNix, ConfigureNixError, ConfigureShellProfile, ConfigureShellProfileError,
CreateNixTree, CreateNixTreeError, CreateSystemdSysext, CreateSystemdSysextError,
CreateUsersAndGroup, CreateUsersAndGroupError, PlaceChannelConfiguration,
PlaceChannelConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError,
ProvisionNix, ProvisionNixError,
ConfigureNix, ConfigureShellProfile, ConfigureShellProfileError, CreateNixTree,
CreateNixTreeError, CreateSystemdSysext, CreateSystemdSysextError, CreateUsersAndGroup,
CreateUsersAndGroupError, PlaceChannelConfiguration, PlaceChannelConfigurationError,
PlaceNixConfiguration, PlaceNixConfigurationError, ProvisionNix, ProvisionNixError,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[async_trait::async_trait]
pub trait Actionable: DeserializeOwned + Serialize + Into<Action> {
type Error: std::error::Error + std::fmt::Debug + Serialize + Into<ActionError>;
pub trait ActionError: std::error::Error + Send + Sync {
fn boxed(self) -> Box<dyn std::error::Error + Send + Sync>
where
Self: Sized + 'static,
{
Box::new(self)
}
}
impl<E> ActionError for E where E: std::error::Error + Send + Sized + Sync {}
#[async_trait::async_trait]
#[typetag::serde(tag = "action")]
pub trait Actionable: Send + Sync + std::fmt::Debug + dyn_clone::DynClone {
fn describe_execute(&self) -> Vec<ActionDescription>;
fn describe_revert(&self) -> Vec<ActionDescription>;
// They should also have an `async fn plan(args...) -> Result<ActionState<Self>, Self::Error>;`
async fn execute(&mut self) -> Result<(), Self::Error>;
async fn revert(&mut self) -> Result<(), Self::Error>;
async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
async fn revert(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
}
dyn_clone::clone_trait_object!(Actionable);
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub enum ActionState {
Completed,
@ -54,232 +65,3 @@ impl ActionDescription {
}
}
}
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub enum Action {
DarwinBootstrapVolume(base::darwin::BootstrapVolume),
DarwinCreateSyntheticObjects(base::darwin::CreateSyntheticObjects),
DarwinCreateVolume(base::darwin::CreateVolume),
DarwinEnableOwnership(base::darwin::EnableOwnership),
DarwinEncryptVolume(base::darwin::EncryptVolume),
DarwinKickStartLaunchctlService(base::darwin::KickstartLaunchctlService),
DarwinUnmountVolume(base::darwin::UnmountVolume),
ConfigureNix(ConfigureNix),
ConfigureNixDaemonService(ConfigureNixDaemonService),
ConfigureShellProfile(ConfigureShellProfile),
CreateApfsVolume(CreateApfsVolume),
CreateDirectory(CreateDirectory),
CreateFile(CreateFile),
CreateGroup(CreateGroup),
CreateNixTree(CreateNixTree),
CreateOrAppendFile(CreateOrAppendFile),
CreateSystemdSysext(CreateSystemdSysext),
CreateUser(CreateUser),
CreateUsersAndGroup(CreateUsersAndGroup),
FetchNix(FetchNix),
MoveUnpackedNix(MoveUnpackedNix),
PlaceChannelConfiguration(PlaceChannelConfiguration),
PlaceNixConfiguration(PlaceNixConfiguration),
ProvisionNix(ProvisionNix),
SetupDefaultProfile(SetupDefaultProfile),
StartSystemdUnit(StartSystemdUnit),
SystemdSysextMerge(SystemdSysextMerge),
}
#[derive(Debug, thiserror::Error, serde::Serialize)]
pub enum ActionError {
#[error(transparent)]
DarwinBootstrapVolume(#[from] base::darwin::BootstrapVolumeError),
#[error(transparent)]
DarwinCreateSyntheticObjects(#[from] base::darwin::CreateSyntheticObjectsError),
#[error(transparent)]
DarwinCreateVolume(#[from] base::darwin::CreateVolumeError),
#[error(transparent)]
DarwinEnableOwnership(#[from] base::darwin::EnableOwnershipError),
#[error(transparent)]
DarwinEncryptVolume(#[from] base::darwin::EncryptVolumeError),
#[error(transparent)]
DarwinKickStartLaunchctlService(#[from] base::darwin::KickstartLaunchctlServiceError),
#[error(transparent)]
DarwinUnmountVolume(#[from] base::darwin::UnmountVolumeError),
#[error("Attempted to revert an unexecuted action")]
NotExecuted(Action),
#[error("Attempted to execute an already executed action")]
AlreadyExecuted(Action),
#[error("Attempted to revert an already reverted action")]
AlreadyReverted(Action),
#[error(transparent)]
CreateApfsVolume(#[from] CreateApfsVolumeError),
#[error(transparent)]
ConfigureNixDaemonService(#[from] ConfigureNixDaemonServiceError),
#[error(transparent)]
ConfigureNix(#[from] ConfigureNixError),
#[error(transparent)]
ConfigureShellProfile(#[from] ConfigureShellProfileError),
#[error(transparent)]
CreateDirectory(#[from] CreateDirectoryError),
#[error(transparent)]
CreateSystemdSysext(#[from] CreateSystemdSysextError),
#[error(transparent)]
CreateFile(#[from] CreateFileError),
#[error(transparent)]
CreateGroup(#[from] CreateGroupError),
#[error(transparent)]
CreateOrAppendFile(#[from] CreateOrAppendFileError),
#[error(transparent)]
CreateNixTree(#[from] CreateNixTreeError),
#[error(transparent)]
CreateUser(#[from] CreateUserError),
#[error(transparent)]
CreateUsersAndGroup(#[from] CreateUsersAndGroupError),
#[error(transparent)]
FetchNix(#[from] FetchNixError),
#[error(transparent)]
MoveUnpackedNix(#[from] MoveUnpackedNixError),
#[error(transparent)]
PlaceChannelConfiguration(#[from] PlaceChannelConfigurationError),
#[error(transparent)]
PlaceNixConfiguration(#[from] PlaceNixConfigurationError),
#[error(transparent)]
SetupDefaultProfile(#[from] SetupDefaultProfileError),
#[error(transparent)]
StartSystemdUnit(#[from] StartSystemdUnitError),
#[error(transparent)]
SystemdSysExtMerge(#[from] SystemdSysextMergeError),
#[error(transparent)]
ProvisionNix(#[from] ProvisionNixError),
}
#[async_trait::async_trait]
impl Actionable for Action {
type Error = ActionError;
fn describe_execute(&self) -> Vec<ActionDescription> {
match self {
Action::DarwinBootstrapVolume(i) => i.describe_execute(),
Action::DarwinCreateSyntheticObjects(i) => i.describe_execute(),
Action::DarwinCreateVolume(i) => i.describe_execute(),
Action::DarwinEnableOwnership(i) => i.describe_execute(),
Action::DarwinEncryptVolume(i) => i.describe_execute(),
Action::DarwinUnmountVolume(i) => i.describe_execute(),
Action::ConfigureNix(i) => i.describe_execute(),
Action::ConfigureNixDaemonService(i) => i.describe_execute(),
Action::ConfigureShellProfile(i) => i.describe_execute(),
Action::CreateApfsVolume(i) => i.describe_execute(),
Action::CreateDirectory(i) => i.describe_execute(),
Action::CreateFile(i) => i.describe_execute(),
Action::CreateGroup(i) => i.describe_execute(),
Action::CreateNixTree(i) => i.describe_execute(),
Action::CreateOrAppendFile(i) => i.describe_execute(),
Action::CreateSystemdSysext(i) => i.describe_execute(),
Action::CreateUser(i) => i.describe_execute(),
Action::CreateUsersAndGroup(i) => i.describe_execute(),
Action::FetchNix(i) => i.describe_execute(),
Action::MoveUnpackedNix(i) => i.describe_execute(),
Action::PlaceChannelConfiguration(i) => i.describe_execute(),
Action::PlaceNixConfiguration(i) => i.describe_execute(),
Action::ProvisionNix(i) => i.describe_execute(),
Action::SetupDefaultProfile(i) => i.describe_execute(),
Action::DarwinKickStartLaunchctlService(i) => i.describe_execute(),
Action::StartSystemdUnit(i) => i.describe_execute(),
Action::SystemdSysextMerge(i) => i.describe_execute(),
}
}
async fn execute(&mut self) -> Result<(), Self::Error> {
match self {
Action::DarwinBootstrapVolume(i) => i.execute().await?,
Action::DarwinCreateSyntheticObjects(i) => i.execute().await?,
Action::DarwinCreateVolume(i) => i.execute().await?,
Action::DarwinEnableOwnership(i) => i.execute().await?,
Action::DarwinEncryptVolume(i) => i.execute().await?,
Action::DarwinUnmountVolume(i) => i.execute().await?,
Action::ConfigureNix(i) => i.execute().await?,
Action::ConfigureNixDaemonService(i) => i.execute().await?,
Action::ConfigureShellProfile(i) => i.execute().await?,
Action::CreateApfsVolume(i) => i.execute().await?,
Action::CreateDirectory(i) => i.execute().await?,
Action::CreateFile(i) => i.execute().await?,
Action::CreateGroup(i) => i.execute().await?,
Action::CreateNixTree(i) => i.execute().await?,
Action::CreateOrAppendFile(i) => i.execute().await?,
Action::CreateSystemdSysext(i) => i.execute().await?,
Action::CreateUser(i) => i.execute().await?,
Action::CreateUsersAndGroup(i) => i.execute().await?,
Action::FetchNix(i) => i.execute().await?,
Action::MoveUnpackedNix(i) => i.execute().await?,
Action::PlaceChannelConfiguration(i) => i.execute().await?,
Action::PlaceNixConfiguration(i) => i.execute().await?,
Action::ProvisionNix(i) => i.execute().await?,
Action::SetupDefaultProfile(i) => i.execute().await?,
Action::DarwinKickStartLaunchctlService(i) => i.execute().await?,
Action::StartSystemdUnit(i) => i.execute().await?,
Action::SystemdSysextMerge(i) => i.execute().await?,
};
Ok(())
}
fn describe_revert(&self) -> Vec<ActionDescription> {
match self {
Action::DarwinBootstrapVolume(i) => i.describe_revert(),
Action::DarwinCreateSyntheticObjects(i) => i.describe_revert(),
Action::DarwinCreateVolume(i) => i.describe_revert(),
Action::DarwinEnableOwnership(i) => i.describe_revert(),
Action::DarwinEncryptVolume(i) => i.describe_revert(),
Action::DarwinUnmountVolume(i) => i.describe_revert(),
Action::ConfigureNix(i) => i.describe_revert(),
Action::ConfigureNixDaemonService(i) => i.describe_revert(),
Action::ConfigureShellProfile(i) => i.describe_revert(),
Action::CreateApfsVolume(i) => i.describe_revert(),
Action::CreateDirectory(i) => i.describe_revert(),
Action::CreateFile(i) => i.describe_revert(),
Action::CreateGroup(i) => i.describe_revert(),
Action::CreateNixTree(i) => i.describe_revert(),
Action::CreateOrAppendFile(i) => i.describe_revert(),
Action::CreateSystemdSysext(i) => i.describe_revert(),
Action::CreateUser(i) => i.describe_revert(),
Action::CreateUsersAndGroup(i) => i.describe_revert(),
Action::FetchNix(i) => i.describe_revert(),
Action::MoveUnpackedNix(i) => i.describe_revert(),
Action::PlaceChannelConfiguration(i) => i.describe_revert(),
Action::PlaceNixConfiguration(i) => i.describe_revert(),
Action::ProvisionNix(i) => i.describe_revert(),
Action::SetupDefaultProfile(i) => i.describe_revert(),
Action::DarwinKickStartLaunchctlService(i) => i.describe_revert(),
Action::StartSystemdUnit(i) => i.describe_revert(),
Action::SystemdSysextMerge(i) => i.describe_revert(),
}
}
async fn revert(&mut self) -> Result<(), Self::Error> {
match self {
Action::DarwinBootstrapVolume(i) => i.revert().await?,
Action::DarwinCreateSyntheticObjects(i) => i.revert().await?,
Action::DarwinCreateVolume(i) => i.revert().await?,
Action::DarwinEnableOwnership(i) => i.revert().await?,
Action::DarwinEncryptVolume(i) => i.revert().await?,
Action::DarwinUnmountVolume(i) => i.revert().await?,
Action::ConfigureNix(i) => i.revert().await?,
Action::ConfigureNixDaemonService(i) => i.revert().await?,
Action::ConfigureShellProfile(i) => i.revert().await?,
Action::CreateApfsVolume(i) => i.revert().await?,
Action::CreateDirectory(i) => i.revert().await?,
Action::CreateFile(i) => i.revert().await?,
Action::CreateGroup(i) => i.revert().await?,
Action::CreateNixTree(i) => i.revert().await?,
Action::CreateOrAppendFile(i) => i.revert().await?,
Action::CreateSystemdSysext(i) => i.revert().await?,
Action::CreateUser(i) => i.revert().await?,
Action::CreateUsersAndGroup(i) => i.revert().await?,
Action::FetchNix(i) => i.revert().await?,
Action::MoveUnpackedNix(i) => i.revert().await?,
Action::PlaceChannelConfiguration(i) => i.revert().await?,
Action::PlaceNixConfiguration(i) => i.revert().await?,
Action::ProvisionNix(i) => i.revert().await?,
Action::SetupDefaultProfile(i) => i.revert().await?,
Action::DarwinKickStartLaunchctlService(i) => i.revert().await?,
Action::StartSystemdUnit(i) => i.revert().await?,
Action::SystemdSysextMerge(i) => i.revert().await?,
}
Ok(())
}
}

View file

@ -1,14 +1,12 @@
use std::path::PathBuf;
use crate::actions::ActionError;
#[derive(thiserror::Error, Debug)]
pub enum HarmonicError {
#[error("Error executing action")]
ActionError(
#[source]
#[from]
ActionError,
Box<dyn std::error::Error + Send + Sync>,
),
#[error("Recording install receipt")]
RecordingReceipt(PathBuf, #[source] std::io::Error),

View file

@ -1,13 +1,13 @@
use std::path::PathBuf;
use crate::{
actions::{Action, ActionDescription, ActionError, Actionable},
actions::{ActionDescription, Actionable},
BuiltinPlanner, HarmonicError,
};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct InstallPlan {
pub(crate) actions: Vec<Action>,
pub(crate) actions: Vec<Box<dyn Actionable>>,
pub(crate) planner: BuiltinPlanner,
}
@ -70,11 +70,12 @@ impl InstallPlan {
if let Err(err) = write_receipt(self.clone()).await {
tracing::error!("Error saving receipt: {:?}", err);
}
return Err(ActionError::from(err).into());
return Err(HarmonicError::ActionError(err));
}
}
write_receipt(self.clone()).await
write_receipt(self.clone()).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
@ -134,7 +135,7 @@ impl InstallPlan {
if let Err(err) = write_receipt(self.clone()).await {
tracing::error!("Error saving receipt: {:?}", err);
}
return Err(ActionError::from(err).into());
return Err(HarmonicError::ActionError(err));
}
}

View file

@ -7,11 +7,10 @@ use crate::{
actions::{
base::darwin::KickstartLaunchctlService,
meta::{darwin::CreateApfsVolume, ConfigureNix, ProvisionNix},
Action, ActionError,
},
execute_command,
os::darwin::DiskUtilOutput,
planner::{Plannable, PlannerError},
planner::{BuiltinPlannerError, Plannable},
BuiltinPlanner, CommonSettings, InstallPlan,
};
@ -32,7 +31,7 @@ pub struct DarwinMulti {
root_disk: Option<String>,
}
async fn default_root_disk() -> Result<String, PlannerError> {
async fn default_root_disk() -> Result<String, BuiltinPlannerError> {
let buf = execute_command(Command::new("/usr/sbin/diskutil").args(["info", "-plist", "/"]))
.await
.unwrap()
@ -46,8 +45,9 @@ async fn default_root_disk() -> Result<String, PlannerError> {
impl Plannable for DarwinMulti {
const DISPLAY_STRING: &'static str = "Darwin Multi-User";
const SLUG: &'static str = "darwin-multi";
type Error = BuiltinPlannerError;
async fn default() -> Result<Self, PlannerError> {
async fn default() -> Result<Self, Self::Error> {
Ok(Self {
settings: CommonSettings::default()?,
root_disk: Some(default_root_disk().await?),
@ -56,7 +56,7 @@ impl Plannable for DarwinMulti {
})
}
async fn plan(self) -> Result<crate::InstallPlan, crate::planner::PlannerError> {
async fn plan(self) -> Result<crate::InstallPlan, Self::Error> {
let root_disk = {
let buf =
execute_command(Command::new("/usr/sbin/diskutil").args(["info", "-plist", "/"]))
@ -77,22 +77,12 @@ impl Plannable for DarwinMulti {
//
// setup_Synthetic -> create_synthetic_objects
// Unmount -> create_volume -> Setup_fstab -> maybe encrypt_volume -> launchctl bootstrap -> launchctl kickstart -> await_volume -> maybe enableOwnership
CreateApfsVolume::plan(root_disk, volume_label, false, None)
.await
.map(Action::from)
.map_err(ActionError::from)?,
ProvisionNix::plan(self.settings.clone())
.await
.map(Action::from)
.map_err(ActionError::from)?,
ConfigureNix::plan(self.settings)
.await
.map(Action::from)
.map_err(ActionError::from)?,
KickstartLaunchctlService::plan("system/org.nixos.nix-daemon".into())
.await
.map(Action::from)
.map_err(ActionError::from)?,
Box::new(CreateApfsVolume::plan(root_disk, volume_label, false, None).await?),
Box::new(ProvisionNix::plan(self.settings.clone()).await?),
Box::new(ConfigureNix::plan(self.settings).await?),
Box::new(
KickstartLaunchctlService::plan("system/org.nixos.nix-daemon".into()).await?,
),
],
})
}

View file

@ -2,9 +2,8 @@ use crate::{
actions::{
base::{CreateDirectory, StartSystemdUnit},
meta::{ConfigureNix, ProvisionNix},
Action, ActionError,
},
planner::{Plannable, PlannerError},
planner::{BuiltinPlannerError, Plannable},
BuiltinPlanner, CommonSettings, InstallPlan,
};
@ -18,33 +17,22 @@ pub struct LinuxMulti {
impl Plannable for LinuxMulti {
const DISPLAY_STRING: &'static str = "Linux Multi-User";
const SLUG: &'static str = "linux-multi";
type Error = BuiltinPlannerError;
async fn default() -> Result<Self, PlannerError> {
async fn default() -> Result<Self, Self::Error> {
Ok(Self {
settings: CommonSettings::default()?,
})
}
async fn plan(self) -> Result<InstallPlan, PlannerError> {
async fn plan(self) -> Result<InstallPlan, Self::Error> {
Ok(InstallPlan {
planner: self.clone().into(),
actions: vec![
CreateDirectory::plan("/nix", None, None, 0o0755, true)
.await
.map(Action::from)
.map_err(ActionError::from)?,
ProvisionNix::plan(self.settings.clone())
.await
.map(Action::from)
.map_err(ActionError::from)?,
ConfigureNix::plan(self.settings)
.await
.map(Action::from)
.map_err(ActionError::from)?,
StartSystemdUnit::plan("nix-daemon.socket".into())
.await
.map(Action::from)
.map_err(ActionError::from)?,
Box::new(CreateDirectory::plan("/nix", None, None, 0o0755, true).await?),
Box::new(ProvisionNix::plan(self.settings.clone()).await?),
Box::new(ConfigureNix::plan(self.settings).await?),
Box::new(StartSystemdUnit::plan("nix-daemon.socket".into()).await?),
],
})
}

View file

@ -12,7 +12,7 @@ pub enum BuiltinPlanner {
}
impl BuiltinPlanner {
pub async fn default() -> Result<Self, PlannerError> {
pub async fn default() -> Result<Self, BuiltinPlannerError> {
use target_lexicon::{Architecture, OperatingSystem};
match (Architecture::host(), OperatingSystem::host()) {
(Architecture::X86_64, OperatingSystem::Linux) => {
@ -29,11 +29,13 @@ impl BuiltinPlanner {
| (Architecture::Aarch64(_), OperatingSystem::Darwin) => {
Ok(Self::DarwinMulti(darwin::DarwinMulti::default().await?))
},
_ => Err(PlannerError::UnsupportedArchitecture(target_lexicon::HOST)),
_ => Err(BuiltinPlannerError::UnsupportedArchitecture(
target_lexicon::HOST,
)),
}
}
pub async fn plan(self) -> Result<InstallPlan, PlannerError> {
pub async fn plan(self) -> Result<InstallPlan, BuiltinPlannerError> {
match self {
BuiltinPlanner::LinuxMulti(planner) => planner.plan().await,
BuiltinPlanner::DarwinMulti(planner) => planner.plan().await,
@ -43,26 +45,27 @@ impl BuiltinPlanner {
}
#[async_trait::async_trait]
trait Plannable: Into<BuiltinPlanner>
trait Plannable
where
Self: Sized,
{
const DISPLAY_STRING: &'static str;
const SLUG: &'static str;
type Error: std::error::Error;
async fn default() -> Result<Self, PlannerError>;
async fn plan(self) -> Result<InstallPlan, PlannerError>;
async fn default() -> Result<Self, Self::Error>;
async fn plan(self) -> Result<InstallPlan, Self::Error>;
}
#[derive(thiserror::Error, Debug)]
pub enum PlannerError {
pub enum BuiltinPlannerError {
#[error("Harmonic does not have a default planner for the `{0}` architecture right now, pass a specific archetype")]
UnsupportedArchitecture(target_lexicon::Triple),
#[error("Error executing action")]
ActionError(
#[source]
#[from]
ActionError,
Box<dyn std::error::Error + Send + Sync>,
),
#[error(transparent)]
InstallSettings(#[from] InstallSettingsError),

View file

@ -2,9 +2,8 @@ use crate::{
actions::{
base::{CreateDirectory, StartSystemdUnit},
meta::{CreateSystemdSysext, ProvisionNix},
Action, ActionError,
},
planner::{Plannable, PlannerError},
planner::{BuiltinPlannerError, Plannable},
BuiltinPlanner, CommonSettings, InstallPlan,
};
@ -18,33 +17,22 @@ pub struct SteamDeck {
impl Plannable for SteamDeck {
const DISPLAY_STRING: &'static str = "Steam Deck (x86_64 Linux Multi-User)";
const SLUG: &'static str = "steam-deck";
type Error = BuiltinPlannerError;
async fn default() -> Result<Self, PlannerError> {
async fn default() -> Result<Self, Self::Error> {
Ok(Self {
settings: CommonSettings::default()?,
})
}
async fn plan(self) -> Result<crate::InstallPlan, PlannerError> {
async fn plan(self) -> Result<crate::InstallPlan, Self::Error> {
Ok(InstallPlan {
planner: self.clone().into(),
actions: vec![
CreateSystemdSysext::plan("/var/lib/extensions")
.await
.map(Action::from)
.map_err(ActionError::from)?,
CreateDirectory::plan("/nix", None, None, 0o0755, true)
.await
.map(Action::from)
.map_err(ActionError::from)?,
ProvisionNix::plan(self.settings.clone())
.await
.map(Action::from)
.map_err(ActionError::from)?,
StartSystemdUnit::plan("nix-daemon.socket".into())
.await
.map(Action::from)
.map_err(ActionError::from)?,
Box::new(CreateSystemdSysext::plan("/var/lib/extensions").await?),
Box::new(CreateDirectory::plan("/nix", None, None, 0o0755, true).await?),
Box::new(ProvisionNix::plan(self.settings.clone()).await?),
Box::new(StartSystemdUnit::plan("nix-daemon.socket".into()).await?),
],
})
}