It's working meme

Signed-off-by: Ana Hobden <operator@hoverbear.org>
This commit is contained in:
Ana Hobden 2022-09-21 15:20:31 -07:00
parent 3c1a8fdcc8
commit a44c9eb45f
25 changed files with 547 additions and 354 deletions

View file

@ -2,25 +2,20 @@ use std::path::{Path, PathBuf};
use tokio::process::Command; use tokio::process::Command;
use crate::{HarmonicError, execute_command}; use crate::{execute_command, HarmonicError};
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
const SERVICE_SRC: &str = const SERVICE_SRC: &str = "/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.service";
"/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 TMPFILES_SRC: &str = "/nix/var/nix/profiles/default//lib/tmpfiles.d/nix-daemon.conf";
const SOCKET_SRC: &str =
"/nix/var/nix/profiles/default/lib/systemd/system/nix-daemon.socket";
const TMPFILES_SRC: &str =
"/nix/var/nix/profiles/default//lib/tmpfiles.d/nix-daemon.conf";
const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf"; const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf";
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct ConfigureNixDaemonService {} pub struct ConfigureNixDaemonService {}
impl ConfigureNixDaemonService { impl ConfigureNixDaemonService {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<Self, HarmonicError> { pub async fn plan() -> Result<Self, HarmonicError> {
if !Path::new("/run/systemd/system").exists() { if !Path::new("/run/systemd/system").exists() {
return Err(HarmonicError::InitNotSupported); return Err(HarmonicError::InitNotSupported);
@ -33,30 +28,35 @@ impl ConfigureNixDaemonService {
impl<'a> Actionable<'a> for ConfigureNixDaemonService { impl<'a> Actionable<'a> for ConfigureNixDaemonService {
type Receipt = ConfigureNixDaemonServiceReceipt; type Receipt = ConfigureNixDaemonServiceReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
vec![ vec![ActionDescription::new(
ActionDescription::new( "Configure Nix daemon related settings with systemd".to_string(),
"Configure the Nix Daemon Service".to_string(), vec![
vec![ "Run `systemd-tempfiles --create --prefix=/nix/var/nix`".to_string(),
"Sets init system specific options".to_string() "Run `systemctl link {SERVICE_SRC}`".to_string(),
] "Run `systemctl link {SOCKET_SRC}`".to_string(),
), "Run `systemctl daemon-reload`".to_string(),
] ],
)]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
tracing::info!("Configuring nix daemon service"); tracing::info!("Configuring nix daemon service");
tracing::trace!("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| HarmonicError::Symlink(PathBuf::from(TMPFILES_SRC), PathBuf::from(TMPFILES_DEST), e))?; .map_err(|e| {
HarmonicError::Symlink(PathBuf::from(TMPFILES_SRC), PathBuf::from(TMPFILES_DEST), e)
})?;
execute_command( execute_command(
Command::new("systemd-tmpfiles") Command::new("systemd-tmpfiles")
.arg("--create") .arg("--create")
.arg("--prefix=/nix/var/nix"), .arg("--prefix=/nix/var/nix"),
false, false,
).await?; )
.await?;
execute_command( execute_command(
Command::new("systemctl").arg("link").arg(SERVICE_SRC), Command::new("systemctl").arg("link").arg(SERVICE_SRC),
@ -64,6 +64,8 @@ impl<'a> Actionable<'a> for ConfigureNixDaemonService {
) )
.await?; .await?;
execute_command(Command::new("systemctl").arg("link").arg(SOCKET_SRC), false).await?;
execute_command(Command::new("systemctl").arg("daemon-reload"), false).await?; execute_command(Command::new("systemctl").arg("daemon-reload"), false).await?;
Ok(Self::Receipt {}) Ok(Self::Receipt {})
@ -86,6 +88,7 @@ impl<'a> Revertable<'a> for ConfigureNixDaemonServiceReceipt {
] ]
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,14 +1,11 @@
use std::{ use std::path::{Path, PathBuf};
fs::Permissions,
path::{Path, PathBuf},
};
use nix::unistd::{Group, User, Gid, Uid, chown}; use nix::unistd::{chown, Group, User};
use tokio::fs::create_dir; use tokio::fs::create_dir;
use crate::HarmonicError; use crate::HarmonicError;
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateDirectory { pub struct CreateDirectory {
@ -19,19 +16,25 @@ pub struct CreateDirectory {
} }
impl CreateDirectory { impl CreateDirectory {
pub async fn plan(path: impl AsRef<Path>, user: String, group: String, mode: u32, force: bool) -> Result<Self, HarmonicError> { #[tracing::instrument(skip_all)]
pub async fn plan(
path: impl AsRef<Path>,
user: String,
group: String,
mode: u32,
force: bool,
) -> Result<Self, HarmonicError> {
let path = path.as_ref(); let path = path.as_ref();
if path.exists() && !force { if path.exists() && !force {
return Err(HarmonicError::CreateDirectory(path.to_path_buf(), std::io::Error::new(std::io::ErrorKind::AlreadyExists, format!("Directory `{}` already exists", path.display())))) return Err(HarmonicError::CreateDirectory(
path.to_path_buf(),
std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
format!("Directory `{}` already exists", path.display()),
),
));
} }
// Ensure the group/user exist, we don't store them since we really need to serialize them
let _has_gid = Group::from_name(group.as_str())
.map_err(|e| HarmonicError::GroupId(group.clone(), e))?
.ok_or(HarmonicError::NoGroup(group.clone()))?;
let _has_uid = User::from_name(user.as_str())
.map_err(|e| HarmonicError::UserId(user.clone(), e))?
.ok_or(HarmonicError::NoUser(user.clone()))?;
Ok(Self { Ok(Self {
path: path.to_path_buf(), path: path.to_path_buf(),
@ -46,15 +49,22 @@ impl CreateDirectory {
impl<'a> Actionable<'a> for CreateDirectory { impl<'a> Actionable<'a> for CreateDirectory {
type Receipt = CreateDirectoryReceipt; type Receipt = CreateDirectoryReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
let Self { path, user, group, mode } = &self; let Self {
path,
user,
group,
mode,
} = &self;
vec![ActionDescription::new( vec![ActionDescription::new(
format!("Create the directory `{}`", path.display()), format!("Create the directory `{}`", path.display()),
vec![format!( vec![format!(
"Creating directory `{}` owned by `{user}:{group}` with mode `{mode:#o}`", path.display() "Creating directory `{}` owned by `{user}:{group}` with mode `{mode:#o}`",
path.display()
)], )],
)] )]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<CreateDirectoryReceipt, HarmonicError> { async fn execute(self) -> Result<CreateDirectoryReceipt, HarmonicError> {
let Self { let Self {
path, path,
@ -72,11 +82,11 @@ impl<'a> Actionable<'a> for CreateDirectory {
.ok_or(HarmonicError::NoUser(user.clone()))? .ok_or(HarmonicError::NoUser(user.clone()))?
.uid; .uid;
tracing::trace!(path = %path.display(), "Creating directory");
create_dir(path.clone()) create_dir(path.clone())
.await .await
.map_err(|e| HarmonicError::CreateDirectory(path.clone(), e))?; .map_err(|e| HarmonicError::CreateDirectory(path.clone(), e))?;
chown(&path, Some(uid), Some(gid)) chown(&path, Some(uid), Some(gid)).map_err(|e| HarmonicError::Chown(path.clone(), e))?;
.map_err(|e| HarmonicError::Chown(path.clone(), e))?;
Ok(CreateDirectoryReceipt { Ok(CreateDirectoryReceipt {
path, path,
@ -106,6 +116,7 @@ impl<'a> Revertable<'a> for CreateDirectoryReceipt {
)] )]
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
let Self { let Self {
path, path,

View file

@ -1,13 +1,13 @@
use std::{ use nix::unistd::{chown, Group, User};
fs::Permissions, use std::path::{Path, PathBuf};
path::{Path, PathBuf, self}, io::SeekFrom, use tokio::{
fs::{create_dir_all, OpenOptions},
io::AsyncWriteExt,
}; };
use nix::unistd::{Group, User, Gid, Uid, chown};
use tokio::{fs::{create_dir, create_dir_all, OpenOptions}, io::{AsyncWriteExt, AsyncSeekExt}};
use crate::HarmonicError; use crate::HarmonicError;
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateFile { pub struct CreateFile {
@ -16,13 +16,39 @@ pub struct CreateFile {
group: String, group: String,
mode: u32, mode: u32,
buf: String, buf: String,
force: bool,
} }
impl CreateFile { impl CreateFile {
pub async fn plan(path: impl AsRef<Path>, user: String, group: String, mode: u32, buf: String) -> Result<Self, HarmonicError> { #[tracing::instrument(skip_all)]
pub async fn plan(
path: impl AsRef<Path>,
user: String,
group: String,
mode: u32,
buf: String,
force: bool,
) -> Result<Self, HarmonicError> {
let path = path.as_ref().to_path_buf(); let path = path.as_ref().to_path_buf();
Ok(Self { path, user, group, mode, buf }) if path.exists() && !force {
return Err(HarmonicError::CreateFile(
path.to_path_buf(),
std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
format!("Directory `{}` already exists", path.display()),
),
));
}
Ok(Self {
path,
user,
group,
mode,
buf,
force,
})
} }
} }
@ -30,7 +56,14 @@ impl CreateFile {
impl<'a> Actionable<'a> for CreateFile { impl<'a> Actionable<'a> for CreateFile {
type Receipt = CreateFileReceipt; type Receipt = CreateFileReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
let Self { path, user, group, mode, buf } = &self; let Self {
path,
user,
group,
mode,
buf,
force,
} = &self;
vec![ActionDescription::new( vec![ActionDescription::new(
format!("Create or overwrite file `{}`", path.display()), format!("Create or overwrite file `{}`", path.display()),
vec![format!( vec![format!(
@ -39,15 +72,17 @@ impl<'a> Actionable<'a> for CreateFile {
)] )]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<CreateFileReceipt, HarmonicError> { async fn execute(self) -> Result<CreateFileReceipt, HarmonicError> {
let Self { path, user, group, mode, buf } = self; let Self {
path,
tracing::trace!("Creating or appending"); user,
if let Some(parent) = path.parent() { group,
create_dir_all(parent) mode,
.await buf,
.map_err(|e| HarmonicError::CreateDirectory(parent.to_owned(), e))?; force: _,
} } = self;
tracing::trace!(path = %path.display(), "Creating file");
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.create_new(true) .create_new(true)
.write(true) .write(true)
@ -68,10 +103,17 @@ impl<'a> Actionable<'a> for CreateFile {
.map_err(|e| HarmonicError::UserId(user.clone(), e))? .map_err(|e| HarmonicError::UserId(user.clone(), e))?
.ok_or(HarmonicError::NoUser(user.clone()))? .ok_or(HarmonicError::NoUser(user.clone()))?
.uid; .uid;
chown(&path, Some(uid), Some(gid))
.map_err(|e| HarmonicError::Chown(path.clone(), e))?;
Ok(Self::Receipt { path, user, group, mode, buf }) tracing::trace!(path = %path.display(), "Chowning file");
chown(&path, Some(uid), Some(gid)).map_err(|e| HarmonicError::Chown(path.clone(), e))?;
Ok(Self::Receipt {
path,
user,
group,
mode,
buf,
})
} }
} }
@ -90,9 +132,8 @@ impl<'a> Revertable<'a> for CreateFileReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();
Ok(()) Ok(())

View file

@ -1,8 +1,8 @@
use tokio::process::Command; use tokio::process::Command;
use crate::HarmonicError; use crate::{HarmonicError, execute_command};
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateGroup { pub struct CreateGroup {
@ -11,9 +11,8 @@ pub struct CreateGroup {
} }
impl CreateGroup { impl CreateGroup {
#[tracing::instrument(skip_all)]
pub fn plan(name: String, gid: usize) -> Self { pub fn plan(name: String, gid: usize) -> Self {
Self { name, gid } Self { name, gid }
} }
} }
@ -31,28 +30,14 @@ impl<'a> Actionable<'a> for CreateGroup {
)] )]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { name, gid } = self; let Self { name, gid } = self;
let mut command = Command::new("groupadd"); execute_command(
Command::new("groupadd").args(["-g", &gid.to_string(), "--system", &name]),
command.args([ false,
"-g", ).await?;
&gid.to_string(),
"--system",
&name
]);
let command_str = format!("{:?}", command.as_std());
let status = command
.status()
.await
.map_err(|e| HarmonicError::CommandFailedExec(command_str.clone(), e))?;
match status.success() {
true => (),
false => return Err(HarmonicError::CommandFailedStatus(command_str)),
}
Ok(CreateGroupReceipt { name, gid }) Ok(CreateGroupReceipt { name, gid })
} }
@ -70,6 +55,7 @@ impl<'a> Revertable<'a> for CreateGroupReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,13 +1,16 @@
use nix::unistd::{chown, Group, User};
use std::{ use std::{
fs::Permissions, io::SeekFrom,
path::{Path, PathBuf, self}, io::SeekFrom, path::{Path, PathBuf},
};
use tokio::{
fs::{create_dir_all, OpenOptions},
io::{AsyncSeekExt, AsyncWriteExt},
}; };
use nix::unistd::{Group, User, Gid, Uid, chown};
use tokio::{fs::{create_dir, create_dir_all, OpenOptions}, io::{AsyncWriteExt, AsyncSeekExt}};
use crate::HarmonicError; use crate::HarmonicError;
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateOrAppendFile { pub struct CreateOrAppendFile {
@ -19,10 +22,23 @@ pub struct CreateOrAppendFile {
} }
impl CreateOrAppendFile { impl CreateOrAppendFile {
pub async fn plan(path: impl AsRef<Path>, user: String, group: String, mode: u32, buf: String) -> Result<Self, HarmonicError> { #[tracing::instrument(skip_all)]
pub async fn plan(
path: impl AsRef<Path>,
user: String,
group: String,
mode: u32,
buf: String,
) -> Result<Self, HarmonicError> {
let path = path.as_ref().to_path_buf(); let path = path.as_ref().to_path_buf();
Ok(Self { path, user, group, mode, buf }) Ok(Self {
path,
user,
group,
mode,
buf,
})
} }
} }
@ -30,7 +46,13 @@ impl CreateOrAppendFile {
impl<'a> Actionable<'a> for CreateOrAppendFile { impl<'a> Actionable<'a> for CreateOrAppendFile {
type Receipt = CreateOrAppendFileReceipt; type Receipt = CreateOrAppendFileReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
let Self { path, user, group, mode, buf } = &self; let Self {
path,
user,
group,
mode,
buf,
} = &self;
vec![ActionDescription::new( vec![ActionDescription::new(
format!("Create or append file `{}`", path.display()), format!("Create or append file `{}`", path.display()),
vec![format!( vec![format!(
@ -39,15 +61,17 @@ impl<'a> Actionable<'a> for CreateOrAppendFile {
)] )]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<CreateOrAppendFileReceipt, HarmonicError> { async fn execute(self) -> Result<CreateOrAppendFileReceipt, HarmonicError> {
let Self { path, user, group, mode, buf } = self; let Self {
path,
user,
group,
mode,
buf,
} = self;
tracing::trace!("Creating or appending"); tracing::trace!(path = %path.display(), "Creating or appending");
if let Some(parent) = path.parent() {
create_dir_all(parent)
.await
.map_err(|e| HarmonicError::CreateDirectory(parent.to_owned(), e))?;
}
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.create(true) .create(true)
.write(true) .write(true)
@ -71,10 +95,17 @@ impl<'a> Actionable<'a> for CreateOrAppendFile {
.map_err(|e| HarmonicError::UserId(user.clone(), e))? .map_err(|e| HarmonicError::UserId(user.clone(), e))?
.ok_or(HarmonicError::NoUser(user.clone()))? .ok_or(HarmonicError::NoUser(user.clone()))?
.uid; .uid;
chown(&path, Some(uid), Some(gid))
.map_err(|e| HarmonicError::Chown(path.clone(), e))?;
Ok(Self::Receipt { path, user, group, mode, buf }) tracing::trace!(path = %path.display(), "Chowning");
chown(&path, Some(uid), Some(gid)).map_err(|e| HarmonicError::Chown(path.clone(), e))?;
Ok(Self::Receipt {
path,
user,
group,
mode,
buf,
})
} }
} }
@ -98,9 +129,8 @@ impl<'a> Revertable<'a> for CreateOrAppendFileReceipt {
)] )]
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();
Ok(()) Ok(())

View file

@ -1,8 +1,8 @@
use tokio::process::Command; use tokio::process::Command;
use crate::HarmonicError; use crate::{HarmonicError, execute_command};
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateUser { pub struct CreateUser {
@ -12,6 +12,7 @@ pub struct CreateUser {
} }
impl CreateUser { impl CreateUser {
#[tracing::instrument(skip_all)]
pub fn plan(name: String, uid: usize, gid: usize) -> Self { pub fn plan(name: String, uid: usize, gid: usize) -> Self {
Self { name, uid, gid } Self { name, uid, gid }
} }
@ -31,11 +32,11 @@ impl<'a> Actionable<'a> for CreateUser {
)] )]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { name, uid, gid } = self; let Self { name, uid, gid } = self;
let mut command = Command::new("useradd"); execute_command(Command::new("useradd").args([
command.args([
"--home-dir", "--home-dir",
"/var/empty", "/var/empty",
"--comment", "--comment",
@ -53,18 +54,7 @@ impl<'a> Actionable<'a> for CreateUser {
"--password", "--password",
"\"!\"", "\"!\"",
&name.to_string(), &name.to_string(),
]); ]), false).await?;
let command_str = format!("{:?}", command.as_std());
let status = command
.status()
.await
.map_err(|e| HarmonicError::CommandFailedExec(command_str.clone(), e))?;
match status.success() {
true => (),
false => return Err(HarmonicError::CommandFailedStatus(command_str)),
}
Ok(CreateUserReceipt { name, uid, gid }) Ok(CreateUserReceipt { name, uid, gid })
} }
@ -83,6 +73,7 @@ impl<'a> Revertable<'a> for CreateUserReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,4 +1,4 @@
use std::path::{Path, PathBuf}; use std::path::{PathBuf};
use bytes::Buf; use bytes::Buf;
use reqwest::Url; use reqwest::Url;
@ -6,7 +6,7 @@ use tokio::task::spawn_blocking;
use crate::HarmonicError; use crate::HarmonicError;
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct FetchNix { pub struct FetchNix {
@ -15,6 +15,7 @@ pub struct FetchNix {
} }
impl FetchNix { impl FetchNix {
#[tracing::instrument(skip_all)]
pub async fn plan(url: Url, destination: PathBuf) -> Result<Self, HarmonicError> { pub async fn plan(url: Url, destination: PathBuf) -> Result<Self, HarmonicError> {
// TODO(@hoverbear): Check URL exists? // TODO(@hoverbear): Check URL exists?
// TODO(@hoverbear): Check tempdir exists // TODO(@hoverbear): Check tempdir exists
@ -27,22 +28,24 @@ impl FetchNix {
impl<'a> Actionable<'a> for FetchNix { impl<'a> Actionable<'a> for FetchNix {
type Receipt = FetchNixReceipt; type Receipt = FetchNixReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
let Self { let Self { url, destination } = &self;
url, destination
} = &self;
vec![ActionDescription::new( vec![ActionDescription::new(
format!("Fetch Nix from `{url}`"), format!("Fetch Nix from `{url}`"),
vec![format!( vec![format!(
"Unpack it to `{}`, so it can later be moved into the Nix store at /nix", destination.display() "Unpack it to `{}` (moved later)",
destination.display()
)], )],
)] )]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { url, destination } = self; let Self { url, destination } = self;
tracing::trace!("Fetching url"); tracing::trace!(%url, "Fetching url");
let res = reqwest::get(url.clone()).await.map_err(HarmonicError::Reqwest)?; let res = reqwest::get(url.clone())
.await
.map_err(HarmonicError::Reqwest)?;
let bytes = res.bytes().await.map_err(HarmonicError::Reqwest)?; let bytes = res.bytes().await.map_err(HarmonicError::Reqwest)?;
// TODO(@Hoverbear): Pick directory // TODO(@Hoverbear): Pick directory
tracing::trace!("Unpacking tar.xz"); tracing::trace!("Unpacking tar.xz");
@ -74,6 +77,7 @@ impl<'a> Revertable<'a> for FetchNixReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,7 +1,6 @@
/*! Actions which do not only call other base plugins. */ /*! Actions which do not only call other base plugins. */
mod configure_nix_daemon_service; mod configure_nix_daemon_service;
mod configure_shell_profile;
mod create_directory; mod create_directory;
mod create_file; mod create_file;
mod create_group; mod create_group;
@ -17,7 +16,6 @@ mod start_systemd_unit;
pub use configure_nix_daemon_service::{ pub use configure_nix_daemon_service::{
ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt, ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt,
}; };
pub use configure_shell_profile::{ConfigureShellProfile, ConfigureShellProfileReceipt};
pub use create_directory::{CreateDirectory, CreateDirectoryReceipt}; pub use create_directory::{CreateDirectory, CreateDirectoryReceipt};
pub use create_file::{CreateFile, CreateFileReceipt}; pub use create_file::{CreateFile, CreateFileReceipt};
pub use create_group::{CreateGroup, CreateGroupReceipt}; pub use create_group::{CreateGroup, CreateGroupReceipt};

View file

@ -1,8 +1,8 @@
use std::path::{PathBuf, Path}; use std::path::{Path, PathBuf};
use crate::HarmonicError; use crate::HarmonicError;
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct MoveUnpackedNix { pub struct MoveUnpackedNix {
@ -10,9 +10,10 @@ pub struct MoveUnpackedNix {
} }
impl MoveUnpackedNix { impl MoveUnpackedNix {
#[tracing::instrument(skip_all)]
pub async fn plan(source: PathBuf) -> Result<Self, HarmonicError> { pub async fn plan(source: PathBuf) -> Result<Self, HarmonicError> {
// Note: Do NOT try to check for the source/dest since the installer creates those // Note: Do NOT try to check for the source/dest since the installer creates those
Ok(Self { source, }) Ok(Self { source })
} }
} }
@ -24,17 +25,19 @@ impl<'a> Actionable<'a> for MoveUnpackedNix {
vec![ActionDescription::new( vec![ActionDescription::new(
format!("Move the downloaded Nix into `/nix`"), format!("Move the downloaded Nix into `/nix`"),
vec![format!( vec![format!(
"Nix is downloaded to `{}` and should be in `nix`", source.display(), "Nix is being downloaded to `{}` and should be in `nix`",
source.display(),
)], )],
)] )]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { source } = self; let Self { source } = self;
// TODO(@Hoverbear): I would like to make this less awful // TODO(@Hoverbear): I would like to make this less awful
let found_nix_paths = glob::glob(&format!("{}/nix-*", source.display()))? let found_nix_paths =
.collect::<Result<Vec<_>, _>>()?; glob::glob(&format!("{}/nix-*", source.display()))?.collect::<Result<Vec<_>, _>>()?;
assert_eq!( assert_eq!(
found_nix_paths.len(), found_nix_paths.len(),
1, 1,
@ -48,14 +51,12 @@ impl<'a> Actionable<'a> for MoveUnpackedNix {
.await .await
.map_err(|e| HarmonicError::Rename(src, dest.to_owned(), e))?; .map_err(|e| HarmonicError::Rename(src, dest.to_owned(), e))?;
Ok(MoveUnpackedNixReceipt { }) Ok(MoveUnpackedNixReceipt {})
} }
} }
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct MoveUnpackedNixReceipt { pub struct MoveUnpackedNixReceipt {}
}
#[async_trait::async_trait] #[async_trait::async_trait]
impl<'a> Revertable<'a> for MoveUnpackedNixReceipt { impl<'a> Revertable<'a> for MoveUnpackedNixReceipt {
@ -63,6 +64,7 @@ impl<'a> Revertable<'a> for MoveUnpackedNixReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,12 +1,10 @@
use std::path::Path;
use reqwest::Url; use reqwest::Url;
use crate::HarmonicError; use crate::HarmonicError;
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
use super::{CreateOrAppendFile, CreateFile, CreateFileReceipt}; use super::{CreateFile, CreateFileReceipt};
const NIX_CHANNELS_PATH: &str = "/root/.nix-channels"; const NIX_CHANNELS_PATH: &str = "/root/.nix-channels";
@ -17,14 +15,19 @@ pub struct PlaceChannelConfiguration {
} }
impl PlaceChannelConfiguration { impl PlaceChannelConfiguration {
pub async fn plan(channels: Vec<(String, Url)>) -> Result<Self, HarmonicError> { #[tracing::instrument(skip_all)]
pub async fn plan(channels: Vec<(String, Url)>, force: bool) -> Result<Self, HarmonicError> {
let buf = channels let buf = channels
.iter() .iter()
.map(|(name, url)| format!("{} {}", url, name)) .map(|(name, url)| format!("{} {}", url, name))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n"); .join("\n");
let create_file = CreateFile::plan(NIX_CHANNELS_PATH, "root".into(), "root".into(), 0o0664, buf).await?; let create_file =
Ok(Self { create_file, channels }) CreateFile::plan(NIX_CHANNELS_PATH, "root".into(), "root".into(), 0o0664, buf, force).await?;
Ok(Self {
create_file,
channels,
})
} }
} }
@ -32,21 +35,27 @@ impl PlaceChannelConfiguration {
impl<'a> Actionable<'a> for PlaceChannelConfiguration { impl<'a> Actionable<'a> for PlaceChannelConfiguration {
type Receipt = PlaceChannelConfigurationReceipt; type Receipt = PlaceChannelConfigurationReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
let Self { channels, create_file } = self; let Self {
vec![ channels,
ActionDescription::new( create_file,
"Place a channel configuration".to_string(), } = self;
vec![ vec![ActionDescription::new(
"Place a configuration at `{NIX_CHANNELS_PATH}` setting the channels".to_string() "Place a channel configuration".to_string(),
] vec!["Place a configuration at `{NIX_CHANNELS_PATH}` setting the channels".to_string()],
), )]
]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { create_file, channels } = self; let Self {
create_file,
channels,
} = self;
let create_file = create_file.execute().await?; let create_file = create_file.execute().await?;
Ok(Self::Receipt { create_file, channels }) Ok(Self::Receipt {
create_file,
channels,
})
} }
} }
@ -62,6 +71,7 @@ impl<'a> Revertable<'a> for PlaceChannelConfigurationReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,18 +1,25 @@
use crate::HarmonicError; use crate::HarmonicError;
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
use super::{CreateFile, CreateFileReceipt}; use super::{CreateFile, CreateFileReceipt, CreateDirectory, CreateDirectoryReceipt};
const NIX_CONF_FOLDER: &str = "/etc/nix";
const NIX_CONF: &str = "/etc/nix/nix.conf"; const NIX_CONF: &str = "/etc/nix/nix.conf";
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct PlaceNixConfiguration { pub struct PlaceNixConfiguration {
create_directory: CreateDirectory,
create_file: CreateFile, create_file: CreateFile,
} }
impl PlaceNixConfiguration { impl PlaceNixConfiguration {
pub async fn plan(nix_build_group_name: String, extra_conf: Option<String>) -> Result<Self, HarmonicError> { #[tracing::instrument(skip_all)]
pub async fn plan(
nix_build_group_name: String,
extra_conf: Option<String>,
force: bool,
) -> Result<Self, HarmonicError> {
let buf = format!( let buf = format!(
"\ "\
{extra_conf}\n\ {extra_conf}\n\
@ -20,8 +27,10 @@ impl PlaceNixConfiguration {
", ",
extra_conf = extra_conf.unwrap_or_else(|| "".into()), extra_conf = extra_conf.unwrap_or_else(|| "".into()),
); );
let create_file = CreateFile::plan(NIX_CONF, "root".into(), "root".into(), 0o0664, buf).await?; let create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, "root".into(), "root".into(), 0o0755, force).await?;
Ok(Self { create_file }) let create_file =
CreateFile::plan(NIX_CONF, "root".into(), "root".into(), 0o0664, buf, force).await?;
Ok(Self { create_directory, create_file })
} }
} }
@ -29,25 +38,24 @@ impl PlaceNixConfiguration {
impl<'a> Actionable<'a> for PlaceNixConfiguration { impl<'a> Actionable<'a> for PlaceNixConfiguration {
type Receipt = PlaceNixConfigurationReceipt; type Receipt = PlaceNixConfigurationReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
vec![ vec![ActionDescription::new(
ActionDescription::new( format!("Place the nix configuration in `{NIX_CONF}`"),
"Place the nix configuration".to_string(), vec!["This file is read by the Nix daemon to set its configuration options at runtime.".to_string()],
vec![ )]
"Boop".to_string()
]
),
]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { create_file } = self; let Self { create_file, create_directory } = self;
let create_directory = create_directory.execute().await?;
let create_file = create_file.execute().await?; let create_file = create_file.execute().await?;
Ok(Self::Receipt { create_file }) Ok(Self::Receipt { create_file, create_directory })
} }
} }
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct PlaceNixConfigurationReceipt { pub struct PlaceNixConfigurationReceipt {
create_directory: CreateDirectoryReceipt,
create_file: CreateFileReceipt, create_file: CreateFileReceipt,
} }
@ -57,6 +65,7 @@ impl<'a> Revertable<'a> for PlaceNixConfigurationReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,9 +1,9 @@
use crate::{HarmonicError, execute_command}; use crate::{execute_command, HarmonicError};
use glob::glob; use glob::glob;
use tokio::process::Command; use tokio::process::Command;
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct SetupDefaultProfile { pub struct SetupDefaultProfile {
@ -11,6 +11,7 @@ pub struct SetupDefaultProfile {
} }
impl SetupDefaultProfile { impl SetupDefaultProfile {
#[tracing::instrument(skip_all)]
pub async fn plan(channels: Vec<String>) -> Result<Self, HarmonicError> { pub async fn plan(channels: Vec<String>) -> Result<Self, HarmonicError> {
Ok(Self { channels }) Ok(Self { channels })
} }
@ -20,16 +21,13 @@ impl SetupDefaultProfile {
impl<'a> Actionable<'a> for SetupDefaultProfile { impl<'a> Actionable<'a> for SetupDefaultProfile {
type Receipt = SetupDefaultProfileReceipt; type Receipt = SetupDefaultProfileReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
vec![ vec![ActionDescription::new(
ActionDescription::new( "Setup the default Nix profile".to_string(),
"Setup the default Nix profile".to_string(), vec!["TODO".to_string()],
vec![ )]
"TODO".to_string()
]
),
]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { channels } = self; let Self { channels } = self;
tracing::info!("Setting up default profile"); tracing::info!("Setting up default profile");
@ -74,7 +72,7 @@ impl<'a> Actionable<'a> for SetupDefaultProfile {
}, },
Err(_) => continue, /* Ignore it */ Err(_) => continue, /* Ignore it */
}; };
}; }
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 {
@ -96,7 +94,10 @@ impl<'a> Actionable<'a> for SetupDefaultProfile {
for channel in channels { for channel in channels {
command.arg(channel); command.arg(channel);
} }
command.env("NIX_SSL_CERT_FILE", "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"); command.env(
"NIX_SSL_CERT_FILE",
"/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt",
);
let command_str = format!("{:?}", command.as_std()); let command_str = format!("{:?}", command.as_std());
let status = command let status = command
@ -119,16 +120,13 @@ pub struct SetupDefaultProfileReceipt {}
#[async_trait::async_trait] #[async_trait::async_trait]
impl<'a> Revertable<'a> for SetupDefaultProfileReceipt { impl<'a> Revertable<'a> for SetupDefaultProfileReceipt {
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
vec![ vec![ActionDescription::new(
ActionDescription::new( "Unset the default Nix profile".to_string(),
"Unset the default Nix profile".to_string(), vec!["TODO".to_string()],
vec![ )]
"TODO".to_string()
]
),
]
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,8 +1,8 @@
use tokio::process::Command; use tokio::process::Command;
use crate::{HarmonicError, execute_command}; use crate::{execute_command, HarmonicError};
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct StartSystemdUnit { pub struct StartSystemdUnit {
@ -10,6 +10,7 @@ pub struct StartSystemdUnit {
} }
impl StartSystemdUnit { impl StartSystemdUnit {
#[tracing::instrument(skip_all)]
pub async fn plan(unit: String) -> Result<Self, HarmonicError> { pub async fn plan(unit: String) -> Result<Self, HarmonicError> {
Ok(Self { unit }) Ok(Self { unit })
} }
@ -29,22 +30,15 @@ impl<'a> Actionable<'a> for StartSystemdUnit {
] ]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { unit } = self; let Self { unit } = self;
// TODO(@Hoverbear): Handle proxy vars // TODO(@Hoverbear): Handle proxy vars
execute_command( execute_command(
Command::new("systemctl") Command::new("systemctl")
.arg("enable") .arg("enable")
.arg(format!("{unit}")), .arg("--now")
false,
)
.await?;
execute_command(
Command::new("systemctl")
.arg("restart")
.arg(format!("{unit}")), .arg(format!("{unit}")),
false, false,
) )
@ -63,6 +57,7 @@ impl<'a> Revertable<'a> for StartSystemdUnitReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,21 +1,31 @@
use tokio::task::JoinSet; use crate::actions::{
base::{
ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt, PlaceNixConfiguration,
PlaceNixConfigurationReceipt, SetupDefaultProfile, SetupDefaultProfileReceipt, PlaceChannelConfiguration, PlaceChannelConfigurationReceipt,
},
meta::{ConfigureShellProfile, ConfigureShellProfileReceipt},
};
use crate::{HarmonicError, InstallSettings};
use crate::actions::base::{SetupDefaultProfile, ConfigureNixDaemonService, ConfigureShellProfile, SetupDefaultProfileReceipt, ConfigureNixDaemonServiceReceipt, ConfigureShellProfileReceipt, PlaceNixConfigurationReceipt, PlaceNixConfiguration}; use crate::actions::{ActionDescription, Actionable, Revertable};
use crate::{HarmonicError, InstallSettings, Harmonic};
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct ConfigureNix { pub struct ConfigureNix {
setup_default_profile: SetupDefaultProfile, setup_default_profile: SetupDefaultProfile,
configure_shell_profile: Option<ConfigureShellProfile>, configure_shell_profile: Option<ConfigureShellProfile>,
place_channel_configuration: PlaceChannelConfiguration,
place_nix_configuration: PlaceNixConfiguration, place_nix_configuration: PlaceNixConfiguration,
configure_nix_daemon_service: ConfigureNixDaemonService, configure_nix_daemon_service: ConfigureNixDaemonService,
} }
impl ConfigureNix { impl ConfigureNix {
#[tracing::instrument(skip_all)]
pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> { pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> {
let channels = settings.channels.iter().map(|(channel, _)| channel.to_string()).collect(); let channels = settings
.channels
.iter()
.map(|(channel, _)| channel.to_string())
.collect();
let setup_default_profile = SetupDefaultProfile::plan(channels).await?; let setup_default_profile = SetupDefaultProfile::plan(channels).await?;
@ -24,11 +34,19 @@ impl ConfigureNix {
} else { } else {
None None
}; };
let place_nix_configuration = PlaceNixConfiguration::plan(settings.nix_build_group_name, settings.extra_conf).await?; let place_channel_configuration =
PlaceChannelConfiguration::plan(settings.channels, settings.force).await?;
let place_nix_configuration =
PlaceNixConfiguration::plan(settings.nix_build_group_name, settings.extra_conf, settings.force).await?;
let configure_nix_daemon_service = ConfigureNixDaemonService::plan().await?; let configure_nix_daemon_service = ConfigureNixDaemonService::plan().await?;
Ok(Self {
Ok(Self { place_nix_configuration, setup_default_profile, configure_nix_daemon_service, configure_shell_profile }) place_channel_configuration,
place_nix_configuration,
setup_default_profile,
configure_nix_daemon_service,
configure_shell_profile,
})
} }
} }
@ -36,11 +54,18 @@ impl ConfigureNix {
impl<'a> Actionable<'a> for ConfigureNix { impl<'a> Actionable<'a> for ConfigureNix {
type Receipt = ConfigureNixReceipt; type Receipt = ConfigureNixReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
let Self { setup_default_profile, configure_nix_daemon_service, place_nix_configuration, configure_shell_profile } = &self; let Self {
setup_default_profile,
configure_nix_daemon_service,
place_nix_configuration,
place_channel_configuration,
configure_shell_profile,
} = &self;
let mut buf = setup_default_profile.description(); let mut buf = setup_default_profile.description();
buf.append(&mut configure_nix_daemon_service.description()); buf.append(&mut configure_nix_daemon_service.description());
buf.append(&mut place_nix_configuration.description()); buf.append(&mut place_nix_configuration.description());
buf.append(&mut place_channel_configuration.description());
if let Some(configure_shell_profile) = configure_shell_profile { if let Some(configure_shell_profile) = configure_shell_profile {
buf.append(&mut configure_shell_profile.description()); buf.append(&mut configure_shell_profile.description());
} }
@ -48,33 +73,46 @@ impl<'a> Actionable<'a> for ConfigureNix {
buf buf
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { setup_default_profile, configure_nix_daemon_service, place_nix_configuration, configure_shell_profile } = self; let Self {
setup_default_profile,
configure_nix_daemon_service,
place_nix_configuration,
place_channel_configuration,
configure_shell_profile,
} = self;
let (setup_default_profile, configure_nix_daemon_service, place_nix_configuration, configure_shell_profile) = if let Some(configure_shell_profile) = configure_shell_profile { let (
setup_default_profile,
place_nix_configuration,
place_channel_configuration,
configure_shell_profile,
) = if let Some(configure_shell_profile) = configure_shell_profile {
let (a, b, c, d) = tokio::try_join!( let (a, b, c, d) = tokio::try_join!(
setup_default_profile.execute(), setup_default_profile.execute(),
configure_nix_daemon_service.execute(),
place_nix_configuration.execute(), place_nix_configuration.execute(),
place_channel_configuration.execute(),
configure_shell_profile.execute(), configure_shell_profile.execute(),
)?; )?;
(a, b, c, Some(d)) (a, b, c, Some(d))
} else { } else {
let (a, b, c) = tokio::try_join!( let (a, b, c) = tokio::try_join!(
setup_default_profile.execute(), setup_default_profile.execute(),
configure_nix_daemon_service.execute(),
place_nix_configuration.execute(), place_nix_configuration.execute(),
place_channel_configuration.execute(),
)?; )?;
(a, b, c, None) (a, b, c, None)
}; };
let configure_nix_daemon_service = configure_nix_daemon_service.execute().await?;
Ok(Self::Receipt { Ok(Self::Receipt {
setup_default_profile, setup_default_profile,
configure_nix_daemon_service, configure_nix_daemon_service,
place_nix_configuration, place_nix_configuration,
place_channel_configuration,
configure_shell_profile, configure_shell_profile,
}) })
} }
} }
@ -83,6 +121,7 @@ pub struct ConfigureNixReceipt {
setup_default_profile: SetupDefaultProfileReceipt, setup_default_profile: SetupDefaultProfileReceipt,
configure_shell_profile: Option<ConfigureShellProfileReceipt>, configure_shell_profile: Option<ConfigureShellProfileReceipt>,
place_nix_configuration: PlaceNixConfigurationReceipt, place_nix_configuration: PlaceNixConfigurationReceipt,
place_channel_configuration: PlaceChannelConfigurationReceipt,
configure_nix_daemon_service: ConfigureNixDaemonServiceReceipt, configure_nix_daemon_service: ConfigureNixDaemonServiceReceipt,
} }
@ -99,6 +138,7 @@ impl<'a> Revertable<'a> for ConfigureNixReceipt {
] ]
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -4,9 +4,8 @@ use tokio::task::JoinSet;
use crate::HarmonicError; use crate::HarmonicError;
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::base::{CreateOrAppendFile, CreateOrAppendFileReceipt};
use crate::actions::{ActionDescription, Actionable, Revertable};
use super::{CreateOrAppendFile, CreateOrAppendFileReceipt};
const PROFILE_TARGETS: &[&str] = &[ const PROFILE_TARGETS: &[&str] = &[
"/etc/bashrc", "/etc/bashrc",
@ -24,10 +23,15 @@ pub struct ConfigureShellProfile {
} }
impl ConfigureShellProfile { impl ConfigureShellProfile {
#[tracing::instrument(skip_all)]
pub async fn plan() -> Result<Self, HarmonicError> { pub async fn plan() -> Result<Self, HarmonicError> {
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);
if !path.exists() {
tracing::trace!("Did not plan to edit `{profile_target}` as it does not exist.");
continue;
}
let buf = format!( let buf = format!(
"\n\ "\n\
# Nix\n\ # Nix\n\
@ -37,11 +41,14 @@ impl ConfigureShellProfile {
# End Nix\n # End Nix\n
\n", \n",
); );
create_or_append_files.push(CreateOrAppendFile::plan(path, "root".to_string(), "root".to_string(), 0o0644, buf).await?); create_or_append_files.push(
CreateOrAppendFile::plan(path, "root".to_string(), "root".to_string(), 0o0644, buf)
.await?,
);
} }
Ok(Self { Ok(Self {
create_or_append_files create_or_append_files,
}) })
} }
} }
@ -50,18 +57,17 @@ impl ConfigureShellProfile {
impl<'a> Actionable<'a> for ConfigureShellProfile { impl<'a> Actionable<'a> for ConfigureShellProfile {
type Receipt = ConfigureShellProfileReceipt; type Receipt = ConfigureShellProfileReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
vec![ vec![ActionDescription::new(
ActionDescription::new( "Configure the shell profiles".to_string(),
"Configure the shell profiles".to_string(), vec!["Update shell profiles to import Nix".to_string()],
vec![ )]
"Update shell profiles to import Nix".to_string()
]
),
]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { create_or_append_files } = self; let Self {
create_or_append_files,
} = self;
tracing::info!("Configuring shell profile"); tracing::info!("Configuring shell profile");
let mut set = JoinSet::new(); let mut set = JoinSet::new();
@ -106,6 +112,7 @@ impl<'a> Revertable<'a> for ConfigureShellProfileReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,7 +1,24 @@
use crate::HarmonicError; use crate::HarmonicError;
use crate::actions::base::{CreateDirectory, CreateDirectoryReceipt}; use crate::actions::base::{CreateDirectory, CreateDirectoryReceipt};
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
const PATHS: &[&str] = &[
"/nix",
"/nix/var",
"/nix/var/log",
"/nix/var/log/nix",
"/nix/var/log/nix/drvs",
"/nix/var/nix",
"/nix/var/nix/db",
"/nix/var/nix/gcroots",
"/nix/var/nix/gcroots/per-user",
"/nix/var/nix/profiles",
"/nix/var/nix/profiles/per-user",
"/nix/var/nix/temproots",
"/nix/var/nix/userpool",
"/nix/var/nix/daemon-socket",
];
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateNixTree { pub struct CreateNixTree {
@ -9,26 +26,14 @@ pub struct CreateNixTree {
} }
impl CreateNixTree { impl CreateNixTree {
#[tracing::instrument(skip_all)]
pub async fn plan(force: bool) -> Result<Self, HarmonicError> { pub async fn plan(force: bool) -> Result<Self, HarmonicError> {
let mut create_directories = Vec::default(); let mut create_directories = Vec::default();
let paths = [ for path in PATHS {
"/nix/var",
"/nix/var/log",
"/nix/var/log/nix",
"/nix/var/log/nix/drvs",
"/nix/var/nix",
"/nix/var/nix/db",
"/nix/var/nix/gcroots",
"/nix/var/nix/gcroots/per-user",
"/nix/var/nix/profiles",
"/nix/var/nix/profiles/per-user",
"/nix/var/nix/temproots",
"/nix/var/nix/userpool",
"/nix/var/nix/daemon-socket",
];
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
create_directories.push(CreateDirectory::plan(path, "root".into(), "root".into(), 0o0755, force).await?) create_directories.push(
CreateDirectory::plan(path, "root".into(), "root".into(), 0o0755, force).await?,
)
} }
Ok(Self { create_directories }) Ok(Self { create_directories })
@ -41,12 +46,14 @@ impl<'a> Actionable<'a> for CreateNixTree {
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
vec![ActionDescription::new( vec![ActionDescription::new(
format!("Create a directory tree in `/nix`"), format!("Create a directory tree in `/nix`"),
vec![format!( vec![
"Nix and the Nix daemon require a Nix Store, which will be stored at `/nix`" format!("Nix and the Nix daemon require a Nix Store, which will be stored at `/nix`"),
)], format!("Creates: {}", PATHS.iter().map(|v| format!("`{v}`")).collect::<Vec<_>>().join(", ")),
],
)] )]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { create_directories } = self; let Self { create_directories } = self;
@ -56,7 +63,9 @@ impl<'a> Actionable<'a> for CreateNixTree {
successes.push(create_directory.execute().await?) successes.push(create_directory.execute().await?)
} }
Ok(CreateNixTreeReceipt { create_directories: successes }) Ok(CreateNixTreeReceipt {
create_directories: successes,
})
} }
} }
@ -71,6 +80,7 @@ impl<'a> Revertable<'a> for CreateNixTreeReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -2,8 +2,8 @@ use tokio::task::JoinSet;
use crate::{HarmonicError, InstallSettings}; use crate::{HarmonicError, InstallSettings};
use crate::actions::base::{CreateGroup, CreateUserReceipt, CreateGroupReceipt}; use crate::actions::base::{CreateGroup, CreateGroupReceipt, CreateUserReceipt};
use crate::actions::{ActionDescription, ActionReceipt, Actionable, CreateUser, Revertable}; use crate::actions::{ActionDescription, Actionable, CreateUser, Revertable};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateUsersAndGroup { pub struct CreateUsersAndGroup {
@ -17,11 +17,13 @@ pub struct CreateUsersAndGroup {
} }
impl CreateUsersAndGroup { impl CreateUsersAndGroup {
pub async fn plan( #[tracing::instrument(skip_all)]
settings: InstallSettings pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> {
) -> Result<Self, HarmonicError> {
// TODO(@hoverbear): CHeck if it exist, error if so // TODO(@hoverbear): CHeck if it exist, error if so
let create_group = CreateGroup::plan(settings.nix_build_group_name.clone(), settings.nix_build_group_id); let create_group = CreateGroup::plan(
settings.nix_build_group_name.clone(),
settings.nix_build_group_id,
);
// TODO(@hoverbear): CHeck if they exist, error if so // TODO(@hoverbear): CHeck if they exist, error if so
let create_users = (0..settings.daemon_user_count) let create_users = (0..settings.daemon_user_count)
.map(|count| { .map(|count| {
@ -69,8 +71,13 @@ impl<'a> Actionable<'a> for CreateUsersAndGroup {
] ]
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { create_users, create_group, .. } = self; let Self {
create_users,
create_group,
..
} = self;
// Create group // Create group
let create_group = create_group.execute().await?; let create_group = create_group.execute().await?;
@ -121,6 +128,7 @@ impl<'a> Revertable<'a> for CreateUsersAndGroupReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,13 +1,15 @@
/*! Actions which only call other base plugins. */ /*! Actions which only call other base plugins. */
mod configure_nix;
mod configure_shell_profile;
mod create_nix_tree; mod create_nix_tree;
mod create_users_and_group; mod create_users_and_group;
mod configure_nix;
mod start_nix_daemon;
mod provision_nix; mod provision_nix;
mod start_nix_daemon;
pub use configure_nix::{ConfigureNix, ConfigureNixReceipt};
pub use configure_shell_profile::{ConfigureShellProfile, ConfigureShellProfileReceipt};
pub use create_nix_tree::{CreateNixTree, CreateNixTreeReceipt}; pub use create_nix_tree::{CreateNixTree, CreateNixTreeReceipt};
pub use create_users_and_group::{CreateUsersAndGroup, CreateUsersAndGroupReceipt}; pub use create_users_and_group::{CreateUsersAndGroup, CreateUsersAndGroupReceipt};
pub use configure_nix::{ConfigureNix, ConfigureNixReceipt};
pub use start_nix_daemon::{StartNixDaemon, StartNixDaemonReceipt};
pub use provision_nix::{ProvisionNix, ProvisionNixReceipt}; pub use provision_nix::{ProvisionNix, ProvisionNixReceipt};
pub use start_nix_daemon::{StartNixDaemon, StartNixDaemonReceipt};

View file

@ -1,11 +1,14 @@
use tempdir::TempDir; use tempdir::TempDir;
use crate::actions::base::{FetchNix, MoveUnpackedNix, FetchNixReceipt, MoveUnpackedNixReceipt}; use crate::actions::base::{FetchNix, FetchNixReceipt, MoveUnpackedNix, MoveUnpackedNixReceipt};
use crate::{HarmonicError, InstallSettings}; use crate::{HarmonicError, InstallSettings};
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
use super::{CreateUsersAndGroup, CreateNixTree, create_users_and_group, create_nix_tree, CreateUsersAndGroupReceipt, CreateNixTreeReceipt}; use super::{
CreateNixTree, CreateNixTreeReceipt,
CreateUsersAndGroup, CreateUsersAndGroupReceipt,
};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct ProvisionNix { pub struct ProvisionNix {
@ -16,14 +19,24 @@ pub struct ProvisionNix {
} }
impl ProvisionNix { impl ProvisionNix {
#[tracing::instrument(skip_all)]
pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> { pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> {
let tempdir = TempDir::new("nix").map_err(HarmonicError::TempDir)?; let tempdir = TempDir::new("nix").map_err(HarmonicError::TempDir)?;
let fetch_nix = FetchNix::plan(settings.nix_package_url.clone(), tempdir.path().to_path_buf()).await?; let fetch_nix = FetchNix::plan(
settings.nix_package_url.clone(),
tempdir.path().to_path_buf(),
)
.await?;
let create_users_and_group = CreateUsersAndGroup::plan(settings.clone()).await?; let create_users_and_group = CreateUsersAndGroup::plan(settings.clone()).await?;
let create_nix_tree = CreateNixTree::plan(settings.force).await?; let create_nix_tree = CreateNixTree::plan(settings.force).await?;
let move_unpacked_nix = MoveUnpackedNix::plan(tempdir.path().to_path_buf()).await?; let move_unpacked_nix = MoveUnpackedNix::plan(tempdir.path().to_path_buf()).await?;
Ok(Self { fetch_nix, create_users_and_group, create_nix_tree, move_unpacked_nix }) Ok(Self {
fetch_nix,
create_users_and_group,
create_nix_tree,
move_unpacked_nix,
})
} }
} }
@ -31,7 +44,12 @@ impl ProvisionNix {
impl<'a> Actionable<'a> for ProvisionNix { impl<'a> Actionable<'a> for ProvisionNix {
type Receipt = ProvisionNixReceipt; type Receipt = ProvisionNixReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
let Self { fetch_nix, create_users_and_group, create_nix_tree, move_unpacked_nix } = &self; let Self {
fetch_nix,
create_users_and_group,
create_nix_tree,
move_unpacked_nix,
} = &self;
let mut buf = fetch_nix.description(); let mut buf = fetch_nix.description();
buf.append(&mut create_users_and_group.description()); buf.append(&mut create_users_and_group.description());
@ -41,8 +59,14 @@ impl<'a> Actionable<'a> for ProvisionNix {
buf buf
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { fetch_nix, create_nix_tree, create_users_and_group, move_unpacked_nix } = self; let Self {
fetch_nix,
create_nix_tree,
create_users_and_group,
move_unpacked_nix,
} = self;
// We fetch nix while doing the rest, then move it over. // We fetch nix while doing the rest, then move it over.
let fetch_nix_handle = tokio::spawn(async move { fetch_nix.execute().await }); let fetch_nix_handle = tokio::spawn(async move { fetch_nix.execute().await });
@ -53,7 +77,12 @@ impl<'a> Actionable<'a> for ProvisionNix {
let fetch_nix = fetch_nix_handle.await??; let fetch_nix = fetch_nix_handle.await??;
let move_unpacked_nix = move_unpacked_nix.execute().await?; let move_unpacked_nix = move_unpacked_nix.execute().await?;
Ok(ProvisionNixReceipt { fetch_nix, create_users_and_group, create_nix_tree, move_unpacked_nix }) Ok(ProvisionNixReceipt {
fetch_nix,
create_users_and_group,
create_nix_tree,
move_unpacked_nix,
})
} }
} }
@ -71,6 +100,7 @@ impl<'a> Revertable<'a> for ProvisionNixReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -1,20 +1,22 @@
use crate::actions::base::{ConfigureNixDaemonServiceReceipt, ConfigureNixDaemonService, StartSystemdUnit, StartSystemdUnitReceipt}; use crate::actions::base::{StartSystemdUnit, StartSystemdUnitReceipt};
use crate::{HarmonicError, InstallSettings}; use crate::HarmonicError;
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable}; use crate::actions::{ActionDescription, Actionable, Revertable};
/// This is mostly indirection for supporting non-systemd /// This is mostly indirection for supporting non-systemd
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct StartNixDaemon { pub struct StartNixDaemon {
start_systemd_socket: StartSystemdUnit, start_systemd_socket: StartSystemdUnit,
start_systemd_service: StartSystemdUnit,
} }
impl StartNixDaemon { impl StartNixDaemon {
pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> { #[tracing::instrument(skip_all)]
pub async fn plan() -> Result<Self, HarmonicError> {
let start_systemd_socket = StartSystemdUnit::plan("nix-daemon.socket".into()).await?; let start_systemd_socket = StartSystemdUnit::plan("nix-daemon.socket".into()).await?;
let start_systemd_service = StartSystemdUnit::plan("nix-daemon.service".into()).await?; let start_systemd_service = StartSystemdUnit::plan("nix-daemon.service".into()).await?;
Ok(Self { start_systemd_socket, start_systemd_service }) Ok(Self {
start_systemd_socket,
})
} }
} }
@ -22,22 +24,27 @@ impl StartNixDaemon {
impl<'a> Actionable<'a> for StartNixDaemon { impl<'a> Actionable<'a> for StartNixDaemon {
type Receipt = StartNixDaemonReceipt; type Receipt = StartNixDaemonReceipt;
fn description(&self) -> Vec<ActionDescription> { fn description(&self) -> Vec<ActionDescription> {
let Self { start_systemd_socket, start_systemd_service } = &self; let Self {
start_systemd_service.description() start_systemd_socket,
} = &self;
start_systemd_socket.description()
} }
#[tracing::instrument(skip_all)]
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
let Self { start_systemd_socket, start_systemd_service } = self; let Self {
let start_systemd_service = start_systemd_service.execute().await?; start_systemd_socket,
} = self;
let start_systemd_socket = start_systemd_socket.execute().await?; let start_systemd_socket = start_systemd_socket.execute().await?;
Ok(Self::Receipt { start_systemd_socket, start_systemd_service }) Ok(Self::Receipt {
start_systemd_socket,
})
} }
} }
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct StartNixDaemonReceipt { pub struct StartNixDaemonReceipt {
start_systemd_socket: StartSystemdUnitReceipt, start_systemd_socket: StartSystemdUnitReceipt,
start_systemd_service: StartSystemdUnitReceipt,
} }
#[async_trait::async_trait] #[async_trait::async_trait]
@ -46,6 +53,7 @@ impl<'a> Revertable<'a> for StartNixDaemonReceipt {
todo!() todo!()
} }
#[tracing::instrument(skip_all)]
async fn revert(self) -> Result<(), HarmonicError> { async fn revert(self) -> Result<(), HarmonicError> {
todo!(); todo!();

View file

@ -2,31 +2,21 @@ pub mod base;
pub mod meta; pub mod meta;
use base::{ use base::{
ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt, ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt, CreateDirectory,
ConfigureShellProfile, ConfigureShellProfileReceipt, CreateDirectoryReceipt, CreateFile, CreateFileReceipt, CreateGroup, CreateGroupReceipt,
CreateDirectory, CreateDirectoryReceipt, CreateOrAppendFile, CreateOrAppendFileReceipt, CreateUser, CreateUserReceipt, FetchNix,
CreateFile, CreateFileReceipt, FetchNixReceipt, MoveUnpackedNix, MoveUnpackedNixReceipt, PlaceChannelConfiguration,
CreateGroup, CreateGroupReceipt, PlaceChannelConfigurationReceipt, PlaceNixConfiguration, PlaceNixConfigurationReceipt,
CreateOrAppendFile, CreateOrAppendFileReceipt,
CreateUser, CreateUserReceipt,
FetchNix, FetchNixReceipt,
MoveUnpackedNix, MoveUnpackedNixReceipt,
PlaceChannelConfiguration, PlaceChannelConfigurationReceipt,
PlaceNixConfiguration, PlaceNixConfigurationReceipt,
SetupDefaultProfile, SetupDefaultProfileReceipt, SetupDefaultProfile, SetupDefaultProfileReceipt,
}; };
use meta::{ use meta::{
ConfigureNix, ConfigureNixReceipt, ConfigureNix, ConfigureNixReceipt, ConfigureShellProfile, ConfigureShellProfileReceipt,
CreateNixTree, CreateNixTreeReceipt, CreateNixTree, CreateNixTreeReceipt, CreateUsersAndGroup, CreateUsersAndGroupReceipt,
CreateUsersAndGroup, CreateUsersAndGroupReceipt, ProvisionNix, ProvisionNixReceipt, StartNixDaemon, StartNixDaemonReceipt,
StartNixDaemon, StartNixDaemonReceipt,
}; };
use crate::HarmonicError; use crate::HarmonicError;
use self::meta::{ProvisionNix, ProvisionNixReceipt};
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait Actionable<'a>: serde::de::Deserialize<'a> + serde::Serialize { pub trait Actionable<'a>: serde::de::Deserialize<'a> + serde::Serialize {
type Receipt; type Receipt;
@ -125,21 +115,37 @@ impl<'a> Actionable<'a> for Action {
async fn execute(self) -> Result<Self::Receipt, HarmonicError> { async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
match self { match self {
Action::ConfigureNixDaemonService(i) => i.execute().await.map(ActionReceipt::ConfigureNixDaemonService), Action::ConfigureNixDaemonService(i) => i
.execute()
.await
.map(ActionReceipt::ConfigureNixDaemonService),
Action::ConfigureNix(i) => i.execute().await.map(ActionReceipt::ConfigureNix), Action::ConfigureNix(i) => i.execute().await.map(ActionReceipt::ConfigureNix),
Action::ConfigureShellProfile(i) => i.execute().await.map(ActionReceipt::ConfigureShellProfile), Action::ConfigureShellProfile(i) => {
i.execute().await.map(ActionReceipt::ConfigureShellProfile)
},
Action::CreateDirectory(i) => i.execute().await.map(ActionReceipt::CreateDirectory), Action::CreateDirectory(i) => i.execute().await.map(ActionReceipt::CreateDirectory),
Action::CreateFile(i) => i.execute().await.map(ActionReceipt::CreateFile), Action::CreateFile(i) => i.execute().await.map(ActionReceipt::CreateFile),
Action::CreateGroup(i) => i.execute().await.map(ActionReceipt::CreateGroup), Action::CreateGroup(i) => i.execute().await.map(ActionReceipt::CreateGroup),
Action::CreateOrAppendFile(i) => i.execute().await.map(ActionReceipt::CreateOrAppendFile), Action::CreateOrAppendFile(i) => {
i.execute().await.map(ActionReceipt::CreateOrAppendFile)
},
Action::CreateNixTree(i) => i.execute().await.map(ActionReceipt::CreateNixTree), Action::CreateNixTree(i) => i.execute().await.map(ActionReceipt::CreateNixTree),
Action::CreateUser(i) => i.execute().await.map(ActionReceipt::CreateUser), Action::CreateUser(i) => i.execute().await.map(ActionReceipt::CreateUser),
Action::CreateUsersAndGroup(i) => i.execute().await.map(ActionReceipt::CreateUsersAndGroup), Action::CreateUsersAndGroup(i) => {
i.execute().await.map(ActionReceipt::CreateUsersAndGroup)
},
Action::FetchNix(i) => i.execute().await.map(ActionReceipt::FetchNix), Action::FetchNix(i) => i.execute().await.map(ActionReceipt::FetchNix),
Action::MoveUnpackedNix(i) => i.execute().await.map(ActionReceipt::MoveUnpackedNix), Action::MoveUnpackedNix(i) => i.execute().await.map(ActionReceipt::MoveUnpackedNix),
Action::PlaceChannelConfiguration(i) => i.execute().await.map(ActionReceipt::PlaceChannelConfiguration), Action::PlaceChannelConfiguration(i) => i
Action::PlaceNixConfiguration(i) => i.execute().await.map(ActionReceipt::PlaceNixConfiguration), .execute()
Action::SetupDefaultProfile(i) => i.execute().await.map(ActionReceipt::SetupDefaultProfile), .await
.map(ActionReceipt::PlaceChannelConfiguration),
Action::PlaceNixConfiguration(i) => {
i.execute().await.map(ActionReceipt::PlaceNixConfiguration)
},
Action::SetupDefaultProfile(i) => {
i.execute().await.map(ActionReceipt::SetupDefaultProfile)
},
Action::StartNixDaemon(i) => i.execute().await.map(ActionReceipt::StartNixDaemon), Action::StartNixDaemon(i) => i.execute().await.map(ActionReceipt::StartNixDaemon),
Action::ProvisionNix(i) => i.execute().await.map(ActionReceipt::ProvisionNix), Action::ProvisionNix(i) => i.execute().await.map(ActionReceipt::ProvisionNix),
} }

View file

@ -1,11 +1,11 @@
use std::process::ExitCode; use std::process::ExitCode;
use clap::{ArgAction, Parser}; use clap::{ArgAction, Parser};
use harmonic::{InstallPlan, InstallSettings}; use harmonic::InstallPlan;
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::AsyncReadExt;
use crate::{ use crate::{
cli::{arg::ChannelValue, CommandExecute}, cli::CommandExecute,
interaction, interaction,
}; };

View file

@ -20,6 +20,8 @@ pub enum HarmonicError {
NoNssCacert, NoNssCacert,
#[error("No supported init system found")] #[error("No supported init system found")]
InitNotSupported, InitNotSupported,
#[error("Creating file `{0}`: {1}")]
CreateFile(std::path::PathBuf, std::io::Error),
#[error("Creating directory `{0}`: {1}")] #[error("Creating directory `{0}`: {1}")]
CreateDirectory(std::path::PathBuf, std::io::Error), CreateDirectory(std::path::PathBuf, std::io::Error),
#[error("Walking directory `{0}`")] #[error("Walking directory `{0}`")]

View file

@ -2,12 +2,8 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
actions::{ actions::{
meta::{ConfigureNix, ProvisionNix, StartNixDaemon},
Action, ActionDescription, ActionReceipt, Actionable, Revertable, Action, ActionDescription, ActionReceipt, Actionable, Revertable,
meta::{
ConfigureNix,
ProvisionNix,
StartNixDaemon,
},
}, },
settings::InstallSettings, settings::InstallSettings,
HarmonicError, HarmonicError,
@ -38,6 +34,7 @@ pub struct InstallPlan {
} }
impl InstallPlan { impl InstallPlan {
#[tracing::instrument(skip_all)]
pub fn description(&self) -> String { pub fn description(&self) -> String {
format!( format!(
"\ "\
@ -85,10 +82,12 @@ impl InstallPlan {
let actions = vec![ let actions = vec![
Action::ProvisionNix(ProvisionNix::plan(settings.clone()).await?), Action::ProvisionNix(ProvisionNix::plan(settings.clone()).await?),
Action::ConfigureNix(ConfigureNix::plan(settings.clone()).await?), Action::ConfigureNix(ConfigureNix::plan(settings.clone()).await?),
Action::StartNixDaemon(StartNixDaemon::plan(settings.clone()).await?), Action::StartNixDaemon(StartNixDaemon::plan().await?),
]; ];
Ok(Self { settings, actions }) Ok(Self { settings, actions })
} }
#[tracing::instrument(skip_all)]
pub async fn install(self) -> Result<Receipt, HarmonicError> { pub async fn install(self) -> Result<Receipt, HarmonicError> {
let mut receipt = Receipt::default(); let mut receipt = Receipt::default();
// This is **deliberately sequential**. // This is **deliberately sequential**.
@ -98,18 +97,18 @@ impl InstallPlan {
match action.execute().await { match action.execute().await {
Ok(action_receipt) => receipt.actions.push(action_receipt), Ok(action_receipt) => receipt.actions.push(action_receipt),
Err(err) => { Err(err) => {
let mut revert_errs = Vec::default();
for action_receipt in receipt.actions {
if let Err(err) = action_receipt.revert().await {
revert_errs.push(err);
}
}
if !revert_errs.is_empty() {
return Err(HarmonicError::FailedReverts(vec![err], revert_errs));
}
return Err(err); return Err(err);
// TODO
// let mut revert_errs = Vec::default();
// for action_receipt in receipt.actions {
// if let Err(err) = action_receipt.revert().await {
// revert_errs.push(err);
// }
// }
// if !revert_errs.is_empty() {
// return Err(HarmonicError::FailedReverts(vec![err], revert_errs));
// }
}, },
}; };
} }

View file

@ -26,7 +26,10 @@ impl Default for InstallSettings {
nix_build_group_id: 3000, nix_build_group_id: 3000,
nix_build_user_prefix: String::from("nixbld"), nix_build_user_prefix: String::from("nixbld"),
nix_build_user_id_base: 3001, nix_build_user_id_base: 3001,
nix_package_url: "https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-x86_64-linux.tar.xz".parse().expect("Could not parse default Nix archive url, please report this issue"), nix_package_url:
"https://releases.nixos.org/nix/nix-2.11.0/nix-2.11.0-x86_64-linux.tar.xz"
.parse()
.expect("Could not parse default Nix archive url, please report this issue"),
extra_conf: Default::default(), extra_conf: Default::default(),
force: false, force: false,
} }