It's working meme
Signed-off-by: Ana Hobden <operator@hoverbear.org>
This commit is contained in:
parent
3c1a8fdcc8
commit
a44c9eb45f
|
@ -2,25 +2,20 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
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 =
|
||||
"/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 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 TMPFILES_SRC: &str = "/nix/var/nix/profiles/default//lib/tmpfiles.d/nix-daemon.conf";
|
||||
const TMPFILES_DEST: &str = "/etc/tmpfiles.d/nix-daemon.conf";
|
||||
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct ConfigureNixDaemonService {}
|
||||
|
||||
impl ConfigureNixDaemonService {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan() -> Result<Self, HarmonicError> {
|
||||
if !Path::new("/run/systemd/system").exists() {
|
||||
return Err(HarmonicError::InitNotSupported);
|
||||
|
@ -33,30 +28,35 @@ impl ConfigureNixDaemonService {
|
|||
impl<'a> Actionable<'a> for ConfigureNixDaemonService {
|
||||
type Receipt = ConfigureNixDaemonServiceReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
vec![
|
||||
ActionDescription::new(
|
||||
"Configure the Nix Daemon Service".to_string(),
|
||||
vec![
|
||||
"Sets init system specific options".to_string()
|
||||
]
|
||||
),
|
||||
]
|
||||
vec![ActionDescription::new(
|
||||
"Configure Nix daemon related settings with systemd".to_string(),
|
||||
vec![
|
||||
"Run `systemd-tempfiles --create --prefix=/nix/var/nix`".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> {
|
||||
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)
|
||||
.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(
|
||||
Command::new("systemd-tmpfiles")
|
||||
.arg("--create")
|
||||
.arg("--prefix=/nix/var/nix"),
|
||||
false,
|
||||
).await?;
|
||||
)
|
||||
.await?;
|
||||
|
||||
execute_command(
|
||||
Command::new("systemctl").arg("link").arg(SERVICE_SRC),
|
||||
|
@ -64,6 +64,8 @@ impl<'a> Actionable<'a> for ConfigureNixDaemonService {
|
|||
)
|
||||
.await?;
|
||||
|
||||
execute_command(Command::new("systemctl").arg("link").arg(SOCKET_SRC), false).await?;
|
||||
|
||||
execute_command(Command::new("systemctl").arg("daemon-reload"), false).await?;
|
||||
|
||||
Ok(Self::Receipt {})
|
||||
|
@ -86,6 +88,7 @@ impl<'a> Revertable<'a> for ConfigureNixDaemonServiceReceipt {
|
|||
]
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
use std::{
|
||||
fs::Permissions,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use nix::unistd::{Group, User, Gid, Uid, chown};
|
||||
use nix::unistd::{chown, Group, User};
|
||||
use tokio::fs::create_dir;
|
||||
|
||||
use crate::HarmonicError;
|
||||
|
||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
use crate::actions::{ActionDescription, Actionable, Revertable};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateDirectory {
|
||||
|
@ -19,20 +16,26 @@ pub struct 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();
|
||||
|
||||
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 {
|
||||
path: path.to_path_buf(),
|
||||
user,
|
||||
|
@ -46,15 +49,22 @@ impl CreateDirectory {
|
|||
impl<'a> Actionable<'a> for CreateDirectory {
|
||||
type Receipt = CreateDirectoryReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
let Self { path, user, group, mode } = &self;
|
||||
let Self {
|
||||
path,
|
||||
user,
|
||||
group,
|
||||
mode,
|
||||
} = &self;
|
||||
vec![ActionDescription::new(
|
||||
format!("Create the directory `{}`", path.display()),
|
||||
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> {
|
||||
let Self {
|
||||
path,
|
||||
|
@ -62,7 +72,7 @@ impl<'a> Actionable<'a> for CreateDirectory {
|
|||
group,
|
||||
mode,
|
||||
} = self;
|
||||
|
||||
|
||||
let gid = Group::from_name(group.as_str())
|
||||
.map_err(|e| HarmonicError::GroupId(group.clone(), e))?
|
||||
.ok_or(HarmonicError::NoGroup(group.clone()))?
|
||||
|
@ -72,12 +82,12 @@ impl<'a> Actionable<'a> for CreateDirectory {
|
|||
.ok_or(HarmonicError::NoUser(user.clone()))?
|
||||
.uid;
|
||||
|
||||
tracing::trace!(path = %path.display(), "Creating directory");
|
||||
create_dir(path.clone())
|
||||
.await
|
||||
.map_err(|e| HarmonicError::CreateDirectory(path.clone(), e))?;
|
||||
chown(&path, Some(uid), Some(gid))
|
||||
.map_err(|e| HarmonicError::Chown(path.clone(), e))?;
|
||||
|
||||
chown(&path, Some(uid), Some(gid)).map_err(|e| HarmonicError::Chown(path.clone(), e))?;
|
||||
|
||||
Ok(CreateDirectoryReceipt {
|
||||
path,
|
||||
user,
|
||||
|
@ -106,6 +116,7 @@ impl<'a> Revertable<'a> for CreateDirectoryReceipt {
|
|||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
let Self {
|
||||
path,
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use std::{
|
||||
fs::Permissions,
|
||||
path::{Path, PathBuf, self}, io::SeekFrom,
|
||||
use nix::unistd::{chown, Group, User};
|
||||
use std::path::{Path, PathBuf};
|
||||
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::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
use crate::actions::{ActionDescription, Actionable, Revertable};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateFile {
|
||||
|
@ -16,13 +16,39 @@ pub struct CreateFile {
|
|||
group: String,
|
||||
mode: u32,
|
||||
buf: String,
|
||||
force: bool,
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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 {
|
||||
type Receipt = CreateFileReceipt;
|
||||
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(
|
||||
format!("Create or overwrite file `{}`", path.display()),
|
||||
vec![format!(
|
||||
|
@ -39,15 +72,17 @@ impl<'a> Actionable<'a> for CreateFile {
|
|||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn execute(self) -> Result<CreateFileReceipt, HarmonicError> {
|
||||
let Self { path, user, group, mode, buf } = self;
|
||||
|
||||
tracing::trace!("Creating or appending");
|
||||
if let Some(parent) = path.parent() {
|
||||
create_dir_all(parent)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::CreateDirectory(parent.to_owned(), e))?;
|
||||
}
|
||||
let Self {
|
||||
path,
|
||||
user,
|
||||
group,
|
||||
mode,
|
||||
buf,
|
||||
force: _,
|
||||
} = self;
|
||||
tracing::trace!(path = %path.display(), "Creating file");
|
||||
let mut file = OpenOptions::new()
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
|
@ -67,11 +102,18 @@ impl<'a> Actionable<'a> for CreateFile {
|
|||
let uid = User::from_name(user.as_str())
|
||||
.map_err(|e| HarmonicError::UserId(user.clone(), e))?
|
||||
.ok_or(HarmonicError::NoUser(user.clone()))?
|
||||
.uid;
|
||||
chown(&path, Some(uid), Some(gid))
|
||||
.map_err(|e| HarmonicError::Chown(path.clone(), e))?;
|
||||
.uid;
|
||||
|
||||
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!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
|
||||
|
||||
todo!();
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
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)]
|
||||
pub struct CreateGroup {
|
||||
|
@ -11,9 +11,8 @@ pub struct CreateGroup {
|
|||
}
|
||||
|
||||
impl CreateGroup {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn plan(name: String, gid: usize) -> Self {
|
||||
|
||||
|
||||
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> {
|
||||
let Self { name, gid } = self;
|
||||
|
||||
let mut command = Command::new("groupadd");
|
||||
|
||||
command.args([
|
||||
"-g",
|
||||
&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)),
|
||||
}
|
||||
execute_command(
|
||||
Command::new("groupadd").args(["-g", &gid.to_string(), "--system", &name]),
|
||||
false,
|
||||
).await?;
|
||||
|
||||
Ok(CreateGroupReceipt { name, gid })
|
||||
}
|
||||
|
@ -70,6 +55,7 @@ impl<'a> Revertable<'a> for CreateGroupReceipt {
|
|||
todo!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
use nix::unistd::{chown, Group, User};
|
||||
use std::{
|
||||
fs::Permissions,
|
||||
path::{Path, PathBuf, self}, io::SeekFrom,
|
||||
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::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
use crate::actions::{ActionDescription, Actionable, Revertable};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateOrAppendFile {
|
||||
|
@ -19,10 +22,23 @@ pub struct 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();
|
||||
|
||||
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 {
|
||||
type Receipt = CreateOrAppendFileReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
let Self { path, user, group, mode, buf } = &self;
|
||||
let Self {
|
||||
path,
|
||||
user,
|
||||
group,
|
||||
mode,
|
||||
buf,
|
||||
} = &self;
|
||||
vec![ActionDescription::new(
|
||||
format!("Create or append file `{}`", path.display()),
|
||||
vec![format!(
|
||||
|
@ -39,15 +61,17 @@ impl<'a> Actionable<'a> for CreateOrAppendFile {
|
|||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
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");
|
||||
if let Some(parent) = path.parent() {
|
||||
create_dir_all(parent)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::CreateDirectory(parent.to_owned(), e))?;
|
||||
}
|
||||
tracing::trace!(path = %path.display(), "Creating or appending");
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
|
@ -70,11 +94,18 @@ impl<'a> Actionable<'a> for CreateOrAppendFile {
|
|||
let uid = User::from_name(user.as_str())
|
||||
.map_err(|e| HarmonicError::UserId(user.clone(), e))?
|
||||
.ok_or(HarmonicError::NoUser(user.clone()))?
|
||||
.uid;
|
||||
chown(&path, Some(uid), Some(gid))
|
||||
.map_err(|e| HarmonicError::Chown(path.clone(), e))?;
|
||||
|
||||
Ok(Self::Receipt { path, user, group, mode, buf })
|
||||
.uid;
|
||||
|
||||
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> {
|
||||
|
||||
|
||||
todo!();
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
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)]
|
||||
pub struct CreateUser {
|
||||
|
@ -12,6 +12,7 @@ pub struct CreateUser {
|
|||
}
|
||||
|
||||
impl CreateUser {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn plan(name: String, uid: usize, gid: usize) -> Self {
|
||||
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> {
|
||||
let Self { name, uid, gid } = self;
|
||||
|
||||
let mut command = Command::new("useradd");
|
||||
command.args([
|
||||
execute_command(Command::new("useradd").args([
|
||||
"--home-dir",
|
||||
"/var/empty",
|
||||
"--comment",
|
||||
|
@ -53,18 +54,7 @@ impl<'a> Actionable<'a> for CreateUser {
|
|||
"--password",
|
||||
"\"!\"",
|
||||
&name.to_string(),
|
||||
]);
|
||||
|
||||
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)),
|
||||
}
|
||||
]), false).await?;
|
||||
|
||||
Ok(CreateUserReceipt { name, uid, gid })
|
||||
}
|
||||
|
@ -83,6 +73,7 @@ impl<'a> Revertable<'a> for CreateUserReceipt {
|
|||
todo!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::path::{PathBuf};
|
||||
|
||||
use bytes::Buf;
|
||||
use reqwest::Url;
|
||||
|
@ -6,7 +6,7 @@ use tokio::task::spawn_blocking;
|
|||
|
||||
use crate::HarmonicError;
|
||||
|
||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
use crate::actions::{ActionDescription, Actionable, Revertable};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct FetchNix {
|
||||
|
@ -15,6 +15,7 @@ pub struct FetchNix {
|
|||
}
|
||||
|
||||
impl FetchNix {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(url: Url, destination: PathBuf) -> Result<Self, HarmonicError> {
|
||||
// TODO(@hoverbear): Check URL exists?
|
||||
// TODO(@hoverbear): Check tempdir exists
|
||||
|
@ -27,22 +28,24 @@ impl FetchNix {
|
|||
impl<'a> Actionable<'a> for FetchNix {
|
||||
type Receipt = FetchNixReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
let Self {
|
||||
url, destination
|
||||
} = &self;
|
||||
let Self { url, destination } = &self;
|
||||
vec![ActionDescription::new(
|
||||
format!("Fetch Nix from `{url}`"),
|
||||
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> {
|
||||
let Self { url, destination } = self;
|
||||
|
||||
tracing::trace!("Fetching url");
|
||||
let res = reqwest::get(url.clone()).await.map_err(HarmonicError::Reqwest)?;
|
||||
tracing::trace!(%url, "Fetching url");
|
||||
let res = reqwest::get(url.clone())
|
||||
.await
|
||||
.map_err(HarmonicError::Reqwest)?;
|
||||
let bytes = res.bytes().await.map_err(HarmonicError::Reqwest)?;
|
||||
// TODO(@Hoverbear): Pick directory
|
||||
tracing::trace!("Unpacking tar.xz");
|
||||
|
@ -74,6 +77,7 @@ impl<'a> Revertable<'a> for FetchNixReceipt {
|
|||
todo!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/*! Actions which do not only call other base plugins. */
|
||||
|
||||
mod configure_nix_daemon_service;
|
||||
mod configure_shell_profile;
|
||||
mod create_directory;
|
||||
mod create_file;
|
||||
mod create_group;
|
||||
|
@ -17,7 +16,6 @@ mod start_systemd_unit;
|
|||
pub use configure_nix_daemon_service::{
|
||||
ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt,
|
||||
};
|
||||
pub use configure_shell_profile::{ConfigureShellProfile, ConfigureShellProfileReceipt};
|
||||
pub use create_directory::{CreateDirectory, CreateDirectoryReceipt};
|
||||
pub use create_file::{CreateFile, CreateFileReceipt};
|
||||
pub use create_group::{CreateGroup, CreateGroupReceipt};
|
||||
|
@ -30,4 +28,4 @@ pub use place_channel_configuration::{
|
|||
};
|
||||
pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationReceipt};
|
||||
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileReceipt};
|
||||
pub use start_systemd_unit::{StartSystemdUnit, StartSystemdUnitReceipt};
|
||||
pub use start_systemd_unit::{StartSystemdUnit, StartSystemdUnitReceipt};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::path::{PathBuf, Path};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::HarmonicError;
|
||||
|
||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
use crate::actions::{ActionDescription, Actionable, Revertable};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct MoveUnpackedNix {
|
||||
|
@ -10,9 +10,10 @@ pub struct MoveUnpackedNix {
|
|||
}
|
||||
|
||||
impl MoveUnpackedNix {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(source: PathBuf) -> Result<Self, HarmonicError> {
|
||||
// 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(
|
||||
format!("Move the downloaded Nix into `/nix`"),
|
||||
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> {
|
||||
let Self { source } = self;
|
||||
|
||||
// TODO(@Hoverbear): I would like to make this less awful
|
||||
let found_nix_paths = glob::glob(&format!("{}/nix-*", source.display()))?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let found_nix_paths =
|
||||
glob::glob(&format!("{}/nix-*", source.display()))?.collect::<Result<Vec<_>, _>>()?;
|
||||
assert_eq!(
|
||||
found_nix_paths.len(),
|
||||
1,
|
||||
|
@ -47,15 +50,13 @@ impl<'a> Actionable<'a> for MoveUnpackedNix {
|
|||
tokio::fs::rename(src.clone(), dest)
|
||||
.await
|
||||
.map_err(|e| HarmonicError::Rename(src, dest.to_owned(), e))?;
|
||||
|
||||
Ok(MoveUnpackedNixReceipt { })
|
||||
|
||||
Ok(MoveUnpackedNixReceipt {})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct MoveUnpackedNixReceipt {
|
||||
|
||||
}
|
||||
pub struct MoveUnpackedNixReceipt {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> Revertable<'a> for MoveUnpackedNixReceipt {
|
||||
|
@ -63,6 +64,7 @@ impl<'a> Revertable<'a> for MoveUnpackedNixReceipt {
|
|||
todo!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use std::path::Path;
|
||||
|
||||
use reqwest::Url;
|
||||
|
||||
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";
|
||||
|
||||
|
@ -17,14 +15,19 @@ pub struct 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
|
||||
.iter()
|
||||
.map(|(name, url)| format!("{} {}", url, name))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let create_file = CreateFile::plan(NIX_CHANNELS_PATH, "root".into(), "root".into(), 0o0664, buf).await?;
|
||||
Ok(Self { create_file, channels })
|
||||
let create_file =
|
||||
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 {
|
||||
type Receipt = PlaceChannelConfigurationReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
let Self { channels, create_file } = self;
|
||||
vec![
|
||||
ActionDescription::new(
|
||||
"Place a channel configuration".to_string(),
|
||||
vec![
|
||||
"Place a configuration at `{NIX_CHANNELS_PATH}` setting the channels".to_string()
|
||||
]
|
||||
),
|
||||
]
|
||||
let Self {
|
||||
channels,
|
||||
create_file,
|
||||
} = self;
|
||||
vec![ActionDescription::new(
|
||||
"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> {
|
||||
let Self { create_file, channels } = self;
|
||||
let Self {
|
||||
create_file,
|
||||
channels,
|
||||
} = self;
|
||||
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!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,18 +1,25 @@
|
|||
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";
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct PlaceNixConfiguration {
|
||||
create_directory: CreateDirectory,
|
||||
create_file: CreateFile,
|
||||
}
|
||||
|
||||
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!(
|
||||
"\
|
||||
{extra_conf}\n\
|
||||
|
@ -20,8 +27,10 @@ impl PlaceNixConfiguration {
|
|||
",
|
||||
extra_conf = extra_conf.unwrap_or_else(|| "".into()),
|
||||
);
|
||||
let create_file = CreateFile::plan(NIX_CONF, "root".into(), "root".into(), 0o0664, buf).await?;
|
||||
Ok(Self { create_file })
|
||||
let create_directory = CreateDirectory::plan(NIX_CONF_FOLDER, "root".into(), "root".into(), 0o0755, force).await?;
|
||||
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 {
|
||||
type Receipt = PlaceNixConfigurationReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
vec![
|
||||
ActionDescription::new(
|
||||
"Place the nix configuration".to_string(),
|
||||
vec![
|
||||
"Boop".to_string()
|
||||
]
|
||||
),
|
||||
]
|
||||
vec![ActionDescription::new(
|
||||
format!("Place the nix configuration in `{NIX_CONF}`"),
|
||||
vec!["This file is read by the Nix daemon to set its configuration options at runtime.".to_string()],
|
||||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
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?;
|
||||
Ok(Self::Receipt { create_file })
|
||||
Ok(Self::Receipt { create_file, create_directory })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct PlaceNixConfigurationReceipt {
|
||||
create_directory: CreateDirectoryReceipt,
|
||||
create_file: CreateFileReceipt,
|
||||
}
|
||||
|
||||
|
@ -57,6 +65,7 @@ impl<'a> Revertable<'a> for PlaceNixConfigurationReceipt {
|
|||
todo!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::{HarmonicError, execute_command};
|
||||
use crate::{execute_command, HarmonicError};
|
||||
|
||||
use glob::glob;
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
use crate::actions::{ActionDescription, Actionable, Revertable};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct SetupDefaultProfile {
|
||||
|
@ -11,6 +11,7 @@ pub struct SetupDefaultProfile {
|
|||
}
|
||||
|
||||
impl SetupDefaultProfile {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(channels: Vec<String>) -> Result<Self, HarmonicError> {
|
||||
Ok(Self { channels })
|
||||
}
|
||||
|
@ -20,16 +21,13 @@ impl SetupDefaultProfile {
|
|||
impl<'a> Actionable<'a> for SetupDefaultProfile {
|
||||
type Receipt = SetupDefaultProfileReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
vec![
|
||||
ActionDescription::new(
|
||||
"Setup the default Nix profile".to_string(),
|
||||
vec![
|
||||
"TODO".to_string()
|
||||
]
|
||||
),
|
||||
]
|
||||
vec![ActionDescription::new(
|
||||
"Setup the default Nix profile".to_string(),
|
||||
vec!["TODO".to_string()],
|
||||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||
let Self { channels } = self;
|
||||
tracing::info!("Setting up default profile");
|
||||
|
@ -74,13 +72,13 @@ impl<'a> Actionable<'a> for SetupDefaultProfile {
|
|||
},
|
||||
Err(_) => continue, /* Ignore it */
|
||||
};
|
||||
};
|
||||
}
|
||||
let nss_ca_cert_pkg = if let Some(nss_ca_cert_pkg) = found_nss_ca_cert_pkg {
|
||||
nss_ca_cert_pkg
|
||||
} else {
|
||||
return Err(HarmonicError::NoNssCacert);
|
||||
};
|
||||
|
||||
|
||||
// Install `nss-cacert` into the store
|
||||
execute_command(
|
||||
Command::new(nix_pkg.join("bin/nix-env"))
|
||||
|
@ -96,14 +94,17 @@ impl<'a> Actionable<'a> for SetupDefaultProfile {
|
|||
for channel in channels {
|
||||
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 status = command
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| HarmonicError::CommandFailedExec(command_str.clone(), e))?;
|
||||
|
||||
|
||||
match status.success() {
|
||||
true => (),
|
||||
false => return Err(HarmonicError::CommandFailedStatus(command_str)),
|
||||
|
@ -119,16 +120,13 @@ pub struct SetupDefaultProfileReceipt {}
|
|||
#[async_trait::async_trait]
|
||||
impl<'a> Revertable<'a> for SetupDefaultProfileReceipt {
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
vec![
|
||||
ActionDescription::new(
|
||||
"Unset the default Nix profile".to_string(),
|
||||
vec![
|
||||
"TODO".to_string()
|
||||
]
|
||||
),
|
||||
]
|
||||
vec![ActionDescription::new(
|
||||
"Unset the default Nix profile".to_string(),
|
||||
vec!["TODO".to_string()],
|
||||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
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)]
|
||||
pub struct StartSystemdUnit {
|
||||
|
@ -10,6 +10,7 @@ pub struct StartSystemdUnit {
|
|||
}
|
||||
|
||||
impl StartSystemdUnit {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(unit: String) -> Result<Self, HarmonicError> {
|
||||
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> {
|
||||
let Self { unit } = self;
|
||||
// TODO(@Hoverbear): Handle proxy vars
|
||||
|
||||
|
||||
execute_command(
|
||||
Command::new("systemctl")
|
||||
.arg("enable")
|
||||
.arg(format!("{unit}")),
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
execute_command(
|
||||
Command::new("systemctl")
|
||||
.arg("restart")
|
||||
.arg("--now")
|
||||
.arg(format!("{unit}")),
|
||||
false,
|
||||
)
|
||||
|
@ -63,6 +57,7 @@ impl<'a> Revertable<'a> for StartSystemdUnitReceipt {
|
|||
todo!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,22 +1,32 @@
|
|||
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::{HarmonicError, InstallSettings, Harmonic};
|
||||
|
||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
use crate::actions::{ActionDescription, Actionable, Revertable};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct ConfigureNix {
|
||||
setup_default_profile: SetupDefaultProfile,
|
||||
configure_shell_profile: Option<ConfigureShellProfile>,
|
||||
place_channel_configuration: PlaceChannelConfiguration,
|
||||
place_nix_configuration: PlaceNixConfiguration,
|
||||
configure_nix_daemon_service: ConfigureNixDaemonService,
|
||||
}
|
||||
|
||||
impl ConfigureNix {
|
||||
#[tracing::instrument(skip_all)]
|
||||
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 configure_shell_profile = if settings.modify_profile {
|
||||
|
@ -24,11 +34,19 @@ impl ConfigureNix {
|
|||
} else {
|
||||
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?;
|
||||
|
||||
|
||||
Ok(Self { place_nix_configuration, setup_default_profile, configure_nix_daemon_service, configure_shell_profile })
|
||||
Ok(Self {
|
||||
place_channel_configuration,
|
||||
place_nix_configuration,
|
||||
setup_default_profile,
|
||||
configure_nix_daemon_service,
|
||||
configure_shell_profile,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,45 +54,65 @@ impl ConfigureNix {
|
|||
impl<'a> Actionable<'a> for ConfigureNix {
|
||||
type Receipt = ConfigureNixReceipt;
|
||||
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();
|
||||
buf.append(&mut configure_nix_daemon_service.description());
|
||||
buf.append(&mut place_nix_configuration.description());
|
||||
buf.append(&mut place_channel_configuration.description());
|
||||
if let Some(configure_shell_profile) = configure_shell_profile {
|
||||
buf.append(&mut configure_shell_profile.description());
|
||||
}
|
||||
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
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!(
|
||||
setup_default_profile.execute(),
|
||||
configure_nix_daemon_service.execute(),
|
||||
place_nix_configuration.execute(),
|
||||
place_channel_configuration.execute(),
|
||||
configure_shell_profile.execute(),
|
||||
)?;
|
||||
(a, b, c, Some(d))
|
||||
} else {
|
||||
let (a, b, c) = tokio::try_join!(
|
||||
setup_default_profile.execute(),
|
||||
configure_nix_daemon_service.execute(),
|
||||
place_nix_configuration.execute(),
|
||||
place_channel_configuration.execute(),
|
||||
)?;
|
||||
(a, b, c, None)
|
||||
};
|
||||
let configure_nix_daemon_service = configure_nix_daemon_service.execute().await?;
|
||||
|
||||
Ok(Self::Receipt {
|
||||
setup_default_profile,
|
||||
configure_nix_daemon_service,
|
||||
place_nix_configuration,
|
||||
place_channel_configuration,
|
||||
configure_shell_profile,
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,6 +121,7 @@ pub struct ConfigureNixReceipt {
|
|||
setup_default_profile: SetupDefaultProfileReceipt,
|
||||
configure_shell_profile: Option<ConfigureShellProfileReceipt>,
|
||||
place_nix_configuration: PlaceNixConfigurationReceipt,
|
||||
place_channel_configuration: PlaceChannelConfigurationReceipt,
|
||||
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> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -4,9 +4,8 @@ use tokio::task::JoinSet;
|
|||
|
||||
use crate::HarmonicError;
|
||||
|
||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
|
||||
use super::{CreateOrAppendFile, CreateOrAppendFileReceipt};
|
||||
use crate::actions::base::{CreateOrAppendFile, CreateOrAppendFileReceipt};
|
||||
use crate::actions::{ActionDescription, Actionable, Revertable};
|
||||
|
||||
const PROFILE_TARGETS: &[&str] = &[
|
||||
"/etc/bashrc",
|
||||
|
@ -24,10 +23,15 @@ pub struct ConfigureShellProfile {
|
|||
}
|
||||
|
||||
impl ConfigureShellProfile {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan() -> Result<Self, HarmonicError> {
|
||||
let mut create_or_append_files = Vec::default();
|
||||
for profile_target in PROFILE_TARGETS {
|
||||
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!(
|
||||
"\n\
|
||||
# Nix\n\
|
||||
|
@ -37,11 +41,14 @@ impl ConfigureShellProfile {
|
|||
# End Nix\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 {
|
||||
create_or_append_files
|
||||
create_or_append_files,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -50,18 +57,17 @@ impl ConfigureShellProfile {
|
|||
impl<'a> Actionable<'a> for ConfigureShellProfile {
|
||||
type Receipt = ConfigureShellProfileReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
vec![
|
||||
ActionDescription::new(
|
||||
"Configure the shell profiles".to_string(),
|
||||
vec![
|
||||
"Update shell profiles to import Nix".to_string()
|
||||
]
|
||||
),
|
||||
]
|
||||
vec![ActionDescription::new(
|
||||
"Configure the shell profiles".to_string(),
|
||||
vec!["Update shell profiles to import Nix".to_string()],
|
||||
)]
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
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");
|
||||
|
||||
let mut set = JoinSet::new();
|
||||
|
@ -106,6 +112,7 @@ impl<'a> Revertable<'a> for ConfigureShellProfileReceipt {
|
|||
todo!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
|
@ -1,7 +1,24 @@
|
|||
use crate::HarmonicError;
|
||||
|
||||
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)]
|
||||
pub struct CreateNixTree {
|
||||
|
@ -9,26 +26,14 @@ pub struct CreateNixTree {
|
|||
}
|
||||
|
||||
impl CreateNixTree {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(force: bool) -> Result<Self, HarmonicError> {
|
||||
let mut create_directories = Vec::default();
|
||||
let 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 {
|
||||
for path in PATHS {
|
||||
// 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 })
|
||||
|
@ -41,22 +46,26 @@ impl<'a> Actionable<'a> for CreateNixTree {
|
|||
fn description(&self) -> Vec<ActionDescription> {
|
||||
vec![ActionDescription::new(
|
||||
format!("Create a directory tree in `/nix`"),
|
||||
vec![format!(
|
||||
"Nix and the Nix daemon require a Nix Store, which will be stored at `/nix`"
|
||||
)],
|
||||
vec![
|
||||
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> {
|
||||
let Self { create_directories } = self;
|
||||
|
||||
|
||||
let mut successes = Vec::with_capacity(create_directories.len());
|
||||
// Just do sequential since parallizing this will have little benefit
|
||||
for create_directory in create_directories {
|
||||
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!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ use tokio::task::JoinSet;
|
|||
|
||||
use crate::{HarmonicError, InstallSettings};
|
||||
|
||||
use crate::actions::base::{CreateGroup, CreateUserReceipt, CreateGroupReceipt};
|
||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, CreateUser, Revertable};
|
||||
use crate::actions::base::{CreateGroup, CreateGroupReceipt, CreateUserReceipt};
|
||||
use crate::actions::{ActionDescription, Actionable, CreateUser, Revertable};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateUsersAndGroup {
|
||||
|
@ -17,11 +17,13 @@ pub struct CreateUsersAndGroup {
|
|||
}
|
||||
|
||||
impl CreateUsersAndGroup {
|
||||
pub async fn plan(
|
||||
settings: InstallSettings
|
||||
) -> Result<Self, HarmonicError> {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> {
|
||||
// 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
|
||||
let create_users = (0..settings.daemon_user_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> {
|
||||
let Self { create_users, create_group, .. } = self;
|
||||
let Self {
|
||||
create_users,
|
||||
create_group,
|
||||
..
|
||||
} = self;
|
||||
|
||||
// Create group
|
||||
let create_group = create_group.execute().await?;
|
||||
|
@ -81,7 +88,7 @@ impl<'a> Actionable<'a> for CreateUsersAndGroup {
|
|||
|
||||
let mut successes = Vec::with_capacity(create_users.len());
|
||||
let mut errors = Vec::default();
|
||||
|
||||
|
||||
for create_user in create_users {
|
||||
let _abort_handle = set.spawn(async move { create_user.execute().await });
|
||||
}
|
||||
|
@ -121,6 +128,7 @@ impl<'a> Revertable<'a> for CreateUsersAndGroupReceipt {
|
|||
todo!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
/*! Actions which only call other base plugins. */
|
||||
|
||||
mod configure_nix;
|
||||
mod configure_shell_profile;
|
||||
mod create_nix_tree;
|
||||
mod create_users_and_group;
|
||||
mod configure_nix;
|
||||
mod start_nix_daemon;
|
||||
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_users_and_group::{CreateUsersAndGroup, CreateUsersAndGroupReceipt};
|
||||
pub use configure_nix::{ConfigureNix, ConfigureNixReceipt};
|
||||
pub use provision_nix::{ProvisionNix, ProvisionNixReceipt};
|
||||
pub use start_nix_daemon::{StartNixDaemon, StartNixDaemonReceipt};
|
||||
pub use provision_nix::{ProvisionNix, ProvisionNixReceipt};
|
|
@ -1,11 +1,14 @@
|
|||
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::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)]
|
||||
pub struct ProvisionNix {
|
||||
|
@ -16,14 +19,24 @@ pub struct ProvisionNix {
|
|||
}
|
||||
|
||||
impl ProvisionNix {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> {
|
||||
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_nix_tree = CreateNixTree::plan(settings.force).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,29 +44,45 @@ impl ProvisionNix {
|
|||
impl<'a> Actionable<'a> for ProvisionNix {
|
||||
type Receipt = ProvisionNixReceipt;
|
||||
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();
|
||||
buf.append(&mut create_users_and_group.description());
|
||||
buf.append(&mut create_nix_tree.description());
|
||||
buf.append(&mut move_unpacked_nix.description());
|
||||
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
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 create_users_and_group = create_users_and_group.execute().await?;
|
||||
let create_nix_tree = create_nix_tree.execute().await?;
|
||||
|
||||
let fetch_nix = fetch_nix_handle.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!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
use crate::actions::base::{ConfigureNixDaemonServiceReceipt, ConfigureNixDaemonService, StartSystemdUnit, StartSystemdUnitReceipt};
|
||||
use crate::{HarmonicError, InstallSettings};
|
||||
use crate::actions::base::{StartSystemdUnit, StartSystemdUnitReceipt};
|
||||
use crate::HarmonicError;
|
||||
|
||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
use crate::actions::{ActionDescription, Actionable, Revertable};
|
||||
|
||||
/// This is mostly indirection for supporting non-systemd
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct StartNixDaemon {
|
||||
start_systemd_socket: StartSystemdUnit,
|
||||
start_systemd_service: StartSystemdUnit,
|
||||
start_systemd_socket: StartSystemdUnit,
|
||||
}
|
||||
|
||||
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_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 {
|
||||
type Receipt = StartNixDaemonReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
let Self { start_systemd_socket, start_systemd_service } = &self;
|
||||
start_systemd_service.description()
|
||||
let Self {
|
||||
start_systemd_socket,
|
||||
} = &self;
|
||||
start_systemd_socket.description()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||
let Self { start_systemd_socket, start_systemd_service } = self;
|
||||
let start_systemd_service = start_systemd_service.execute().await?;
|
||||
let Self {
|
||||
start_systemd_socket,
|
||||
} = self;
|
||||
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)]
|
||||
pub struct StartNixDaemonReceipt {
|
||||
start_systemd_socket: StartSystemdUnitReceipt,
|
||||
start_systemd_service: StartSystemdUnitReceipt,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -46,6 +53,7 @@ impl<'a> Revertable<'a> for StartNixDaemonReceipt {
|
|||
todo!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
todo!();
|
||||
|
||||
|
|
|
@ -2,31 +2,21 @@ pub mod base;
|
|||
pub mod meta;
|
||||
|
||||
use base::{
|
||||
ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt,
|
||||
ConfigureShellProfile, ConfigureShellProfileReceipt,
|
||||
CreateDirectory, CreateDirectoryReceipt,
|
||||
CreateFile, CreateFileReceipt,
|
||||
CreateGroup, CreateGroupReceipt,
|
||||
CreateOrAppendFile, CreateOrAppendFileReceipt,
|
||||
CreateUser, CreateUserReceipt,
|
||||
FetchNix, FetchNixReceipt,
|
||||
MoveUnpackedNix, MoveUnpackedNixReceipt,
|
||||
PlaceChannelConfiguration, PlaceChannelConfigurationReceipt,
|
||||
PlaceNixConfiguration, PlaceNixConfigurationReceipt,
|
||||
ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt, CreateDirectory,
|
||||
CreateDirectoryReceipt, CreateFile, CreateFileReceipt, CreateGroup, CreateGroupReceipt,
|
||||
CreateOrAppendFile, CreateOrAppendFileReceipt, CreateUser, CreateUserReceipt, FetchNix,
|
||||
FetchNixReceipt, MoveUnpackedNix, MoveUnpackedNixReceipt, PlaceChannelConfiguration,
|
||||
PlaceChannelConfigurationReceipt, PlaceNixConfiguration, PlaceNixConfigurationReceipt,
|
||||
SetupDefaultProfile, SetupDefaultProfileReceipt,
|
||||
};
|
||||
use meta::{
|
||||
ConfigureNix, ConfigureNixReceipt,
|
||||
CreateNixTree, CreateNixTreeReceipt,
|
||||
CreateUsersAndGroup, CreateUsersAndGroupReceipt,
|
||||
StartNixDaemon, StartNixDaemonReceipt,
|
||||
ConfigureNix, ConfigureNixReceipt, ConfigureShellProfile, ConfigureShellProfileReceipt,
|
||||
CreateNixTree, CreateNixTreeReceipt, CreateUsersAndGroup, CreateUsersAndGroupReceipt,
|
||||
ProvisionNix, ProvisionNixReceipt, StartNixDaemon, StartNixDaemonReceipt,
|
||||
};
|
||||
|
||||
|
||||
use crate::HarmonicError;
|
||||
|
||||
use self::meta::{ProvisionNix, ProvisionNixReceipt};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait Actionable<'a>: serde::de::Deserialize<'a> + serde::Serialize {
|
||||
type Receipt;
|
||||
|
@ -125,21 +115,37 @@ impl<'a> Actionable<'a> for Action {
|
|||
|
||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||
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::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::CreateFile(i) => i.execute().await.map(ActionReceipt::CreateFile),
|
||||
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::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::MoveUnpackedNix(i) => i.execute().await.map(ActionReceipt::MoveUnpackedNix),
|
||||
Action::PlaceChannelConfiguration(i) => i.execute().await.map(ActionReceipt::PlaceChannelConfiguration),
|
||||
Action::PlaceNixConfiguration(i) => i.execute().await.map(ActionReceipt::PlaceNixConfiguration),
|
||||
Action::SetupDefaultProfile(i) => i.execute().await.map(ActionReceipt::SetupDefaultProfile),
|
||||
Action::PlaceChannelConfiguration(i) => i
|
||||
.execute()
|
||||
.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::ProvisionNix(i) => i.execute().await.map(ActionReceipt::ProvisionNix),
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::process::ExitCode;
|
||||
|
||||
use clap::{ArgAction, Parser};
|
||||
use harmonic::{InstallPlan, InstallSettings};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use harmonic::InstallPlan;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
use crate::{
|
||||
cli::{arg::ChannelValue, CommandExecute},
|
||||
cli::CommandExecute,
|
||||
interaction,
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ pub enum HarmonicError {
|
|||
NoNssCacert,
|
||||
#[error("No supported init system found")]
|
||||
InitNotSupported,
|
||||
#[error("Creating file `{0}`: {1}")]
|
||||
CreateFile(std::path::PathBuf, std::io::Error),
|
||||
#[error("Creating directory `{0}`: {1}")]
|
||||
CreateDirectory(std::path::PathBuf, std::io::Error),
|
||||
#[error("Walking directory `{0}`")]
|
||||
|
|
33
src/plan.rs
33
src/plan.rs
|
@ -2,12 +2,8 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::{
|
||||
actions::{
|
||||
meta::{ConfigureNix, ProvisionNix, StartNixDaemon},
|
||||
Action, ActionDescription, ActionReceipt, Actionable, Revertable,
|
||||
meta::{
|
||||
ConfigureNix,
|
||||
ProvisionNix,
|
||||
StartNixDaemon,
|
||||
},
|
||||
},
|
||||
settings::InstallSettings,
|
||||
HarmonicError,
|
||||
|
@ -38,6 +34,7 @@ pub struct InstallPlan {
|
|||
}
|
||||
|
||||
impl InstallPlan {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn description(&self) -> String {
|
||||
format!(
|
||||
"\
|
||||
|
@ -85,10 +82,12 @@ impl InstallPlan {
|
|||
let actions = vec![
|
||||
Action::ProvisionNix(ProvisionNix::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 })
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn install(self) -> Result<Receipt, HarmonicError> {
|
||||
let mut receipt = Receipt::default();
|
||||
// This is **deliberately sequential**.
|
||||
|
@ -98,18 +97,18 @@ impl InstallPlan {
|
|||
match action.execute().await {
|
||||
Ok(action_receipt) => receipt.actions.push(action_receipt),
|
||||
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);
|
||||
// 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));
|
||||
// }
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -26,7 +26,10 @@ impl Default for InstallSettings {
|
|||
nix_build_group_id: 3000,
|
||||
nix_build_user_prefix: String::from("nixbld"),
|
||||
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(),
|
||||
force: false,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue