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::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 SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service";
const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket"; const SOCKET_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket";
@ -21,7 +21,7 @@ pub struct ConfigureNixDaemonService {
impl ConfigureNixDaemonService { impl ConfigureNixDaemonService {
#[tracing::instrument(skip_all)] #[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() { match OperatingSystem::host() {
OperatingSystem::MacOSX { OperatingSystem::MacOSX {
major: _, major: _,
@ -31,7 +31,7 @@ impl ConfigureNixDaemonService {
| OperatingSystem::Darwin => (), | OperatingSystem::Darwin => (),
_ => { _ => {
if !Path::new("/run/systemd/system").exists() { 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] #[async_trait::async_trait]
#[typetag::serde(name = "configure-nix-daemon")]
impl Actionable for ConfigureNixDaemonService { impl Actionable for ConfigureNixDaemonService {
type Error = ConfigureNixDaemonServiceError;
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {
vec![] vec![]
@ -63,7 +62,7 @@ impl Actionable for ConfigureNixDaemonService {
} }
#[tracing::instrument(skip_all)] #[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; let Self { action_state } = self;
if *action_state == ActionState::Completed { if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Configuring nix daemon service"); 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) tokio::fs::copy(src.clone(), DARWIN_NIX_DAEMON_DEST)
.await .await
.map_err(|e| { .map_err(|e| {
Self::Error::Copy( ConfigureNixDaemonServiceError::Copy(
src.to_path_buf(), src.to_path_buf(),
PathBuf::from(DARWIN_NIX_DAEMON_DEST), PathBuf::from(DARWIN_NIX_DAEMON_DEST),
e, e,
) )
.boxed()
})?; })?;
execute_command( execute_command(
@ -98,18 +98,19 @@ impl Actionable for ConfigureNixDaemonService {
.arg(DARWIN_NIX_DAEMON_DEST), .arg(DARWIN_NIX_DAEMON_DEST),
) )
.await .await
.map_err(Self::Error::CommandFailed)?; .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
}, },
_ => { _ => {
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking"); tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST) tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
.await .await
.map_err(|e| { .map_err(|e| {
Self::Error::Symlink( ConfigureNixDaemonServiceError::Symlink(
PathBuf::from(TMPFILES_SRC), PathBuf::from(TMPFILES_SRC),
PathBuf::from(TMPFILES_DEST), PathBuf::from(TMPFILES_DEST),
e, e,
) )
.boxed()
})?; })?;
execute_command( execute_command(
@ -118,19 +119,19 @@ impl Actionable for ConfigureNixDaemonService {
.arg("--prefix=/nix/var/nix"), .arg("--prefix=/nix/var/nix"),
) )
.await .await
.map_err(Self::Error::CommandFailed)?; .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
execute_command(Command::new("systemctl").arg("link").arg(SERVICE_SRC)) execute_command(Command::new("systemctl").arg("link").arg(SERVICE_SRC))
.await .await
.map_err(Self::Error::CommandFailed)?; .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
execute_command(Command::new("systemctl").arg("link").arg(SOCKET_SRC)) execute_command(Command::new("systemctl").arg("link").arg(SOCKET_SRC))
.await .await
.map_err(Self::Error::CommandFailed)?; .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
execute_command(Command::new("systemctl").arg("daemon-reload")) execute_command(Command::new("systemctl").arg("daemon-reload"))
.await .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)] #[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; let Self { action_state } = self;
if *action_state == ActionState::Uncompleted { if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Unconfiguring nix daemon service"); 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...) // 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])) execute_command(Command::new("systemctl").args(["disable", SOCKET_SRC]))
.await .await
.map_err(Self::Error::CommandFailed)?; .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
execute_command(Command::new("systemctl").args(["disable", SERVICE_SRC])) execute_command(Command::new("systemctl").args(["disable", SERVICE_SRC]))
.await .await
.map_err(Self::Error::CommandFailed)?; .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
execute_command( execute_command(
Command::new("systemd-tmpfiles") Command::new("systemd-tmpfiles")
@ -179,15 +180,15 @@ impl Actionable for ConfigureNixDaemonService {
.arg("--prefix=/nix/var/nix"), .arg("--prefix=/nix/var/nix"),
) )
.await .await
.map_err(Self::Error::CommandFailed)?; .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
remove_file(TMPFILES_DEST) remove_file(TMPFILES_DEST).await.map_err(|e| {
.await ConfigureNixDaemonServiceError::RemoveFile(PathBuf::from(TMPFILES_DEST), e).boxed()
.map_err(|e| Self::Error::RemoveFile(PathBuf::from(TMPFILES_DEST), e))?; })?;
execute_command(Command::new("systemctl").arg("daemon-reload")) execute_command(Command::new("systemctl").arg("daemon-reload"))
.await .await
.map_err(Self::Error::CommandFailed)?; .map_err(|e| ConfigureNixDaemonServiceError::CommandFailed(e).boxed())?;
tracing::trace!("Unconfigured nix daemon service"); tracing::trace!("Unconfigured nix daemon service");
*action_state = ActionState::Uncompleted; *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)] #[derive(Debug, thiserror::Error, Serialize)]
pub enum ConfigureNixDaemonServiceError { pub enum ConfigureNixDaemonServiceError {
#[error("Symlinking from `{0}` to `{1}`")] #[error("Symlinking from `{0}` to `{1}`")]

View file

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

View file

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

View file

@ -3,7 +3,7 @@ use tokio::process::Command;
use crate::execute_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)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateGroup { pub struct CreateGroup {
@ -24,9 +24,8 @@ impl CreateGroup {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-group")]
impl Actionable for CreateGroup { impl Actionable for CreateGroup {
type Error = CreateGroupError;
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {
name, name,
@ -49,7 +48,7 @@ impl Actionable for CreateGroup {
user = self.name, user = self.name,
gid = self.gid, 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 { let Self {
name, name,
gid, gid,
@ -79,7 +78,7 @@ impl Actionable for CreateGroup {
name.as_str(), name.as_str(),
])) ]))
.await .await
.map_err(Self::Error::Command)?; .map_err(|e| CreateGroupError::Command(e).boxed())?;
}, },
_ => { _ => {
execute_command(Command::new("groupadd").args([ execute_command(Command::new("groupadd").args([
@ -89,7 +88,7 @@ impl Actionable for CreateGroup {
&name, &name,
])) ]))
.await .await
.map_err(CreateGroupError::Command)?; .map_err(|e| CreateGroupError::Command(e).boxed())?;
}, },
}; };
@ -120,7 +119,7 @@ impl Actionable for CreateGroup {
user = self.name, user = self.name,
gid = self.gid, 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 { let Self {
name, name,
gid: _, gid: _,
@ -142,12 +141,12 @@ impl Actionable for CreateGroup {
| OperatingSystem::Darwin => { | OperatingSystem::Darwin => {
execute_command(Command::new("groupdel").arg(&name)) execute_command(Command::new("groupdel").arg(&name))
.await .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 .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)] #[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateGroupError { pub enum CreateGroupError {
#[error("Failed to execute command")] #[error("Failed to execute command")]

View file

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

View file

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

View file

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

View file

@ -3,7 +3,7 @@ use tokio::process::Command;
use crate::execute_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)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateSyntheticObjects { pub struct CreateSyntheticObjects {
@ -12,7 +12,7 @@ pub struct CreateSyntheticObjects {
impl CreateSyntheticObjects { impl CreateSyntheticObjects {
#[tracing::instrument(skip_all)] #[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 { Ok(Self {
action_state: ActionState::Uncompleted, action_state: ActionState::Uncompleted,
}) })
@ -20,9 +20,8 @@ impl CreateSyntheticObjects {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "create-synthetic-objects")]
impl Actionable for CreateSyntheticObjects { impl Actionable for CreateSyntheticObjects {
type Error = CreateSyntheticObjectsError;
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {
vec![] vec![]
@ -35,7 +34,7 @@ impl Actionable for CreateSyntheticObjects {
} }
#[tracing::instrument(skip_all, fields())] #[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; let Self { action_state } = self;
if *action_state == ActionState::Completed { if *action_state == ActionState::Completed {
tracing::trace!("Already completed: Creating synthetic objects"); tracing::trace!("Already completed: Creating synthetic objects");
@ -74,7 +73,7 @@ impl Actionable for CreateSyntheticObjects {
} }
#[tracing::instrument(skip_all, fields())] #[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; let Self { action_state } = self;
if *action_state == ActionState::Uncompleted { if *action_state == ActionState::Uncompleted {
tracing::trace!("Already reverted: Refreshing synthetic objects"); 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)] #[derive(Debug, thiserror::Error, Serialize)]
pub enum CreateSyntheticObjectsError { pub enum CreateSyntheticObjectsError {
#[error("Failed to execute command")] #[error("Failed to execute command")]

View file

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

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@ use tokio::process::Command;
use crate::execute_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)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct UnmountVolume { pub struct UnmountVolume {
@ -16,7 +16,10 @@ pub struct UnmountVolume {
impl UnmountVolume { impl UnmountVolume {
#[tracing::instrument(skip_all)] #[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(); let disk = disk.as_ref().to_owned();
Ok(Self { Ok(Self {
disk, disk,
@ -27,9 +30,8 @@ impl UnmountVolume {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "unmount-volume")]
impl Actionable for UnmountVolume { impl Actionable for UnmountVolume {
type Error = UnmountVolumeError;
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {
vec![] vec![]
@ -47,7 +49,7 @@ impl Actionable for UnmountVolume {
disk = %self.disk.display(), disk = %self.disk.display(),
name = %self.name, 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 { let Self {
disk: _, disk: _,
name, name,
@ -65,7 +67,7 @@ impl Actionable for UnmountVolume {
.arg(name), .arg(name),
) )
.await .await
.map_err(Self::Error::Command)?; .map_err(|e| UnmountVolumeError::Command(e).boxed())?;
tracing::trace!("Unmounted volume"); tracing::trace!("Unmounted volume");
*action_state = ActionState::Completed; *action_state = ActionState::Completed;
@ -89,7 +91,7 @@ impl Actionable for UnmountVolume {
disk = %self.disk.display(), disk = %self.disk.display(),
name = %self.name, 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 { let Self {
disk: _, disk: _,
name, name,
@ -107,7 +109,7 @@ impl Actionable for UnmountVolume {
.arg(name), .arg(name),
) )
.await .await
.map_err(Self::Error::Command)?; .map_err(|e| UnmountVolumeError::Command(e).boxed())?;
tracing::trace!("Stopped systemd unit"); tracing::trace!("Stopped systemd unit");
*action_state = ActionState::Completed; *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)] #[derive(Debug, thiserror::Error, Serialize)]
pub enum UnmountVolumeError { pub enum UnmountVolumeError {
#[error("Failed to execute command")] #[error("Failed to execute command")]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -12,12 +12,12 @@ use crate::{
ConfigureShellProfile, ConfigureShellProfileError, PlaceChannelConfiguration, ConfigureShellProfile, ConfigureShellProfileError, PlaceChannelConfiguration,
PlaceChannelConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError, PlaceChannelConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError,
}, },
Action, ActionState, ActionState,
}, },
cli::arg::ChannelValue, cli::arg::ChannelValue,
}; };
use crate::actions::{ActionDescription, Actionable}; use crate::actions::{ActionDescription, ActionError, Actionable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct ConfigureNix { pub struct ConfigureNix {
@ -31,7 +31,9 @@ pub struct ConfigureNix {
impl ConfigureNix { impl ConfigureNix {
#[tracing::instrument(skip_all)] #[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 let channels: Vec<(String, Url)> = settings
.channels .channels
.iter() .iter()
@ -39,7 +41,9 @@ impl ConfigureNix {
.collect(); .collect();
let setup_default_profile = 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 { let configure_shell_profile = if settings.modify_profile {
Some(ConfigureShellProfile::plan().await?) Some(ConfigureShellProfile::plan().await?)
@ -68,8 +72,8 @@ impl ConfigureNix {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "configure-nix")]
impl Actionable for ConfigureNix { impl Actionable for ConfigureNix {
type Error = ConfigureNixError;
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
let Self { let Self {
setup_default_profile, setup_default_profile,
@ -95,7 +99,7 @@ impl Actionable for ConfigureNix {
} }
#[tracing::instrument(skip_all)] #[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 { let Self {
setup_default_profile, setup_default_profile,
configure_nix_daemon_service, configure_nix_daemon_service,
@ -113,51 +117,16 @@ impl Actionable for ConfigureNix {
if let Some(configure_shell_profile) = configure_shell_profile { if let Some(configure_shell_profile) = configure_shell_profile {
tokio::try_join!( tokio::try_join!(
async move { async move { setup_default_profile.execute().await },
setup_default_profile async move { place_nix_configuration.execute().await },
.execute() async move { place_channel_configuration.execute().await },
.await async move { configure_shell_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))
},
)?; )?;
} else { } else {
tokio::try_join!( tokio::try_join!(
async move { async move { setup_default_profile.execute().await },
setup_default_profile async move { place_nix_configuration.execute().await },
.execute() async move { place_channel_configuration.execute().await },
.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))
},
)?; )?;
}; };
configure_nix_daemon_service.execute().await?; configure_nix_daemon_service.execute().await?;
@ -194,7 +163,7 @@ impl Actionable for ConfigureNix {
} }
#[tracing::instrument(skip_all)] #[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 { let Self {
setup_default_profile, setup_default_profile,
configure_nix_daemon_service, configure_nix_daemon_service,
@ -223,43 +192,3 @@ impl Actionable for ConfigureNix {
Ok(()) 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 tokio::task::{JoinError, JoinSet};
use crate::actions::base::{CreateOrAppendFile, CreateOrAppendFileError}; use crate::actions::base::{CreateOrAppendFile, CreateOrAppendFileError};
use crate::actions::{Action, ActionDescription, ActionState, Actionable}; use crate::actions::{ActionDescription, ActionError, ActionState, Actionable};
const PROFILE_TARGETS: &[&str] = &[ const PROFILE_TARGETS: &[&str] = &[
"/etc/bashrc", "/etc/bashrc",
@ -24,7 +24,7 @@ pub struct ConfigureShellProfile {
impl ConfigureShellProfile { impl ConfigureShellProfile {
#[tracing::instrument(skip_all)] #[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(); let mut create_or_append_files = Vec::default();
for profile_target in PROFILE_TARGETS { for profile_target in PROFILE_TARGETS {
let path = Path::new(profile_target); let path = Path::new(profile_target);
@ -41,8 +41,11 @@ impl ConfigureShellProfile {
# End Nix\n # End Nix\n
\n", \n",
); );
create_or_append_files create_or_append_files.push(
.push(CreateOrAppendFile::plan(path, None, None, 0o0644, buf).await?); CreateOrAppendFile::plan(path, None, None, 0o0644, buf)
.await
.map_err(|e| e.boxed())?,
);
} }
Ok(Self { Ok(Self {
@ -53,9 +56,8 @@ impl ConfigureShellProfile {
} }
#[async_trait::async_trait] #[async_trait::async_trait]
#[typetag::serde(name = "configure-shell-profile")]
impl Actionable for ConfigureShellProfile { impl Actionable for ConfigureShellProfile {
type Error = ConfigureShellProfileError;
fn describe_execute(&self) -> Vec<ActionDescription> { fn describe_execute(&self) -> Vec<ActionDescription> {
if self.action_state == ActionState::Completed { if self.action_state == ActionState::Completed {
vec![] vec![]
@ -68,7 +70,7 @@ impl Actionable for ConfigureShellProfile {
} }
#[tracing::instrument(skip_all)] #[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 { let Self {
create_or_append_files, create_or_append_files,
action_state, action_state,
@ -87,7 +89,10 @@ impl Actionable for ConfigureShellProfile {
let mut create_or_append_file_clone = create_or_append_file.clone(); let mut create_or_append_file_clone = create_or_append_file.clone();
let _abort_handle = set.spawn(async move { let _abort_handle = set.spawn(async move {
create_or_append_file_clone.execute().await?; 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 create_or_append_files[idx] = create_or_append_file
}, },
Ok(Err(e)) => errors.push(e), 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 { if errors.len() == 1 {
return Err(errors.into_iter().next().unwrap().into()); return Err(errors.into_iter().next().unwrap().into());
} else { } else {
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile( return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed());
errors,
));
} }
} }
@ -128,7 +131,7 @@ impl Actionable for ConfigureShellProfile {
} }
#[tracing::instrument(skip_all)] #[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 { let Self {
create_or_append_files, create_or_append_files,
action_state, action_state,
@ -147,7 +150,10 @@ impl Actionable for ConfigureShellProfile {
let mut create_or_append_file_clone = create_or_append_file.clone(); let mut create_or_append_file_clone = create_or_append_file.clone();
let _abort_handle = set.spawn(async move { let _abort_handle = set.spawn(async move {
create_or_append_file_clone.revert().await?; 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 create_or_append_files[idx] = create_or_append_file
}, },
Ok(Err(e)) => errors.push(e), 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 { if errors.len() == 1 {
return Err(errors.into_iter().next().unwrap().into()); return Err(errors.into_iter().next().unwrap().into());
} else { } else {
return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile( return Err(ConfigureShellProfileError::MultipleCreateOrAppendFile(errors).boxed());
errors,
));
} }
} }
@ -177,13 +181,7 @@ impl Actionable for ConfigureShellProfile {
} }
} }
impl From<ConfigureShellProfile> for Action { #[derive(Debug, thiserror::Error)]
fn from(v: ConfigureShellProfile) -> Self {
Action::ConfigureShellProfile(v)
}
}
#[derive(Debug, thiserror::Error, Serialize)]
pub enum ConfigureShellProfileError { pub enum ConfigureShellProfileError {
#[error("Creating or appending to file")] #[error("Creating or appending to file")]
CreateOrAppendFile( CreateOrAppendFile(
@ -192,12 +190,11 @@ pub enum ConfigureShellProfileError {
CreateOrAppendFileError, CreateOrAppendFileError,
), ),
#[error("Multiple errors: {}", .0.iter().map(|v| format!("{v}")).collect::<Vec<_>>().join(" & "))] #[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")] #[error("Joining spawned async task")]
Join( Join(
#[source] #[source]
#[from] #[from]
#[serde(serialize_with = "crate::serialize_error_to_display")]
JoinError, JoinError,
), ),
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -11,26 +11,37 @@ use base::{
}; };
use meta::{ use meta::{
darwin::{CreateApfsVolume, CreateApfsVolumeError}, darwin::{CreateApfsVolume, CreateApfsVolumeError},
ConfigureNix, ConfigureNixError, ConfigureShellProfile, ConfigureShellProfileError, ConfigureNix, ConfigureShellProfile, ConfigureShellProfileError, CreateNixTree,
CreateNixTree, CreateNixTreeError, CreateSystemdSysext, CreateSystemdSysextError, CreateNixTreeError, CreateSystemdSysext, CreateSystemdSysextError, CreateUsersAndGroup,
CreateUsersAndGroup, CreateUsersAndGroupError, PlaceChannelConfiguration, CreateUsersAndGroupError, PlaceChannelConfiguration, PlaceChannelConfigurationError,
PlaceChannelConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError, PlaceNixConfiguration, PlaceNixConfigurationError, ProvisionNix, ProvisionNixError,
ProvisionNix, ProvisionNixError,
}; };
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[async_trait::async_trait] pub trait ActionError: std::error::Error + Send + Sync {
pub trait Actionable: DeserializeOwned + Serialize + Into<Action> { fn boxed(self) -> Box<dyn std::error::Error + Send + Sync>
type Error: std::error::Error + std::fmt::Debug + Serialize + Into<ActionError>; 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_execute(&self) -> Vec<ActionDescription>;
fn describe_revert(&self) -> Vec<ActionDescription>; fn describe_revert(&self) -> Vec<ActionDescription>;
// They should also have an `async fn plan(args...) -> Result<ActionState<Self>, Self::Error>;` // They should also have an `async fn plan(args...) -> Result<ActionState<Self>, Self::Error>;`
async fn execute(&mut self) -> Result<(), Self::Error>; async fn execute(&mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
async fn revert(&mut self) -> Result<(), Self::Error>; 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)] #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub enum ActionState { pub enum ActionState {
Completed, 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 std::path::PathBuf;
use crate::actions::ActionError;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum HarmonicError { pub enum HarmonicError {
#[error("Error executing action")] #[error("Error executing action")]
ActionError( ActionError(
#[source] #[source]
#[from] #[from]
ActionError, Box<dyn std::error::Error + Send + Sync>,
), ),
#[error("Recording install receipt")] #[error("Recording install receipt")]
RecordingReceipt(PathBuf, #[source] std::io::Error), RecordingReceipt(PathBuf, #[source] std::io::Error),

View file

@ -1,13 +1,13 @@
use std::path::PathBuf; use std::path::PathBuf;
use crate::{ use crate::{
actions::{Action, ActionDescription, ActionError, Actionable}, actions::{ActionDescription, Actionable},
BuiltinPlanner, HarmonicError, BuiltinPlanner, HarmonicError,
}; };
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct InstallPlan { pub struct InstallPlan {
pub(crate) actions: Vec<Action>, pub(crate) actions: Vec<Box<dyn Actionable>>,
pub(crate) planner: BuiltinPlanner, pub(crate) planner: BuiltinPlanner,
} }
@ -70,11 +70,12 @@ impl InstallPlan {
if let Err(err) = write_receipt(self.clone()).await { if let Err(err) = write_receipt(self.clone()).await {
tracing::error!("Error saving receipt: {:?}", err); 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)] #[tracing::instrument(skip_all)]
@ -134,7 +135,7 @@ impl InstallPlan {
if let Err(err) = write_receipt(self.clone()).await { if let Err(err) = write_receipt(self.clone()).await {
tracing::error!("Error saving receipt: {:?}", err); 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::{ actions::{
base::darwin::KickstartLaunchctlService, base::darwin::KickstartLaunchctlService,
meta::{darwin::CreateApfsVolume, ConfigureNix, ProvisionNix}, meta::{darwin::CreateApfsVolume, ConfigureNix, ProvisionNix},
Action, ActionError,
}, },
execute_command, execute_command,
os::darwin::DiskUtilOutput, os::darwin::DiskUtilOutput,
planner::{Plannable, PlannerError}, planner::{BuiltinPlannerError, Plannable},
BuiltinPlanner, CommonSettings, InstallPlan, BuiltinPlanner, CommonSettings, InstallPlan,
}; };
@ -32,7 +31,7 @@ pub struct DarwinMulti {
root_disk: Option<String>, 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", "/"])) let buf = execute_command(Command::new("/usr/sbin/diskutil").args(["info", "-plist", "/"]))
.await .await
.unwrap() .unwrap()
@ -46,8 +45,9 @@ async fn default_root_disk() -> Result<String, PlannerError> {
impl Plannable for DarwinMulti { impl Plannable for DarwinMulti {
const DISPLAY_STRING: &'static str = "Darwin Multi-User"; const DISPLAY_STRING: &'static str = "Darwin Multi-User";
const SLUG: &'static str = "darwin-multi"; const SLUG: &'static str = "darwin-multi";
type Error = BuiltinPlannerError;
async fn default() -> Result<Self, PlannerError> { async fn default() -> Result<Self, Self::Error> {
Ok(Self { Ok(Self {
settings: CommonSettings::default()?, settings: CommonSettings::default()?,
root_disk: Some(default_root_disk().await?), 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 root_disk = {
let buf = let buf =
execute_command(Command::new("/usr/sbin/diskutil").args(["info", "-plist", "/"])) execute_command(Command::new("/usr/sbin/diskutil").args(["info", "-plist", "/"]))
@ -77,22 +77,12 @@ impl Plannable for DarwinMulti {
// //
// setup_Synthetic -> create_synthetic_objects // setup_Synthetic -> create_synthetic_objects
// Unmount -> create_volume -> Setup_fstab -> maybe encrypt_volume -> launchctl bootstrap -> launchctl kickstart -> await_volume -> maybe enableOwnership // Unmount -> create_volume -> Setup_fstab -> maybe encrypt_volume -> launchctl bootstrap -> launchctl kickstart -> await_volume -> maybe enableOwnership
CreateApfsVolume::plan(root_disk, volume_label, false, None) Box::new(CreateApfsVolume::plan(root_disk, volume_label, false, None).await?),
.await Box::new(ProvisionNix::plan(self.settings.clone()).await?),
.map(Action::from) Box::new(ConfigureNix::plan(self.settings).await?),
.map_err(ActionError::from)?, Box::new(
ProvisionNix::plan(self.settings.clone()) KickstartLaunchctlService::plan("system/org.nixos.nix-daemon".into()).await?,
.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)?,
], ],
}) })
} }

View file

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

View file

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

View file

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