forked from lix-project/lix-installer
A bunch of fleshing out
Signed-off-by: Ana Hobden <operator@hoverbear.org>
This commit is contained in:
parent
4fdba83039
commit
3c1a8fdcc8
|
@ -1,13 +1,31 @@
|
||||||
use crate::HarmonicError;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::{HarmonicError, execute_command};
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
use crate::actions::{ActionDescription, ActionReceipt, 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 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 {
|
||||||
pub fn plan() -> Self {
|
pub async fn plan() -> Result<Self, HarmonicError> {
|
||||||
Self {}
|
if !Path::new("/run/systemd/system").exists() {
|
||||||
|
return Err(HarmonicError::InitNotSupported);
|
||||||
|
}
|
||||||
|
Ok(Self {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +44,29 @@ impl<'a> Actionable<'a> for ConfigureNixDaemonService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||||
todo!()
|
tracing::info!("Configuring nix daemon service");
|
||||||
|
|
||||||
|
tracing::trace!("Symlinking");
|
||||||
|
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
|
||||||
|
.await
|
||||||
|
.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?;
|
||||||
|
|
||||||
|
execute_command(
|
||||||
|
Command::new("systemctl").arg("link").arg(SERVICE_SRC),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
execute_command(Command::new("systemctl").arg("daemon-reload"), false).await?;
|
||||||
|
|
||||||
|
Ok(Self::Receipt {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,48 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use tokio::task::JoinSet;
|
||||||
|
|
||||||
use crate::HarmonicError;
|
use crate::HarmonicError;
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
|
|
||||||
|
use super::{CreateOrAppendFile, CreateOrAppendFileReceipt};
|
||||||
|
|
||||||
|
const PROFILE_TARGETS: &[&str] = &[
|
||||||
|
"/etc/bashrc",
|
||||||
|
"/etc/profile.d/nix.sh",
|
||||||
|
"/etc/zshrc",
|
||||||
|
"/etc/bash.bashrc",
|
||||||
|
"/etc/zsh/zshrc",
|
||||||
|
// TODO(@hoverbear): FIsh
|
||||||
|
];
|
||||||
|
const PROFILE_NIX_FILE: &str = "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh";
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct ConfigureShellProfile {}
|
pub struct ConfigureShellProfile {
|
||||||
|
create_or_append_files: Vec<CreateOrAppendFile>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ConfigureShellProfile {
|
impl ConfigureShellProfile {
|
||||||
pub fn plan() -> Self {
|
pub async fn plan() -> Result<Self, HarmonicError> {
|
||||||
Self {}
|
let mut create_or_append_files = Vec::default();
|
||||||
|
for profile_target in PROFILE_TARGETS {
|
||||||
|
let path = Path::new(profile_target);
|
||||||
|
let buf = format!(
|
||||||
|
"\n\
|
||||||
|
# Nix\n\
|
||||||
|
if [ -e '{PROFILE_NIX_FILE}' ]; then\n\
|
||||||
|
. '{PROFILE_NIX_FILE}'\n\
|
||||||
|
fi\n\
|
||||||
|
# End Nix\n
|
||||||
|
\n",
|
||||||
|
);
|
||||||
|
create_or_append_files.push(CreateOrAppendFile::plan(path, "root".to_string(), "root".to_string(), 0o0644, buf).await?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
create_or_append_files
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,24 +61,49 @@ impl<'a> Actionable<'a> for ConfigureShellProfile {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||||
todo!()
|
let Self { create_or_append_files } = self;
|
||||||
|
tracing::info!("Configuring shell profile");
|
||||||
|
|
||||||
|
let mut set = JoinSet::new();
|
||||||
|
|
||||||
|
let mut successes = Vec::with_capacity(create_or_append_files.len());
|
||||||
|
let mut errors = Vec::default();
|
||||||
|
|
||||||
|
for create_or_append_file in create_or_append_files {
|
||||||
|
let _abort_handle = set.spawn(async move { create_or_append_file.execute().await });
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(result) = set.join_next().await {
|
||||||
|
match result {
|
||||||
|
Ok(Ok(success)) => successes.push(success),
|
||||||
|
Ok(Err(e)) => errors.push(e),
|
||||||
|
Err(e) => errors.push(e.into()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
if errors.len() == 1 {
|
||||||
|
return Err(errors.into_iter().next().unwrap());
|
||||||
|
} else {
|
||||||
|
return Err(HarmonicError::Multiple(errors));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self::Receipt {
|
||||||
|
create_or_append_files: successes,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct ConfigureShellProfileReceipt {}
|
pub struct ConfigureShellProfileReceipt {
|
||||||
|
create_or_append_files: Vec<CreateOrAppendFileReceipt>,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<'a> Revertable<'a> for ConfigureShellProfileReceipt {
|
impl<'a> Revertable<'a> for ConfigureShellProfileReceipt {
|
||||||
fn description(&self) -> Vec<ActionDescription> {
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
vec![
|
todo!()
|
||||||
ActionDescription::new(
|
|
||||||
"Stop the systemd Nix daemon".to_string(),
|
|
||||||
vec![
|
|
||||||
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn revert(self) -> Result<(), HarmonicError> {
|
async fn revert(self) -> Result<(), HarmonicError> {
|
||||||
|
|
|
@ -3,6 +3,9 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use nix::unistd::{Group, User, Gid, Uid, chown};
|
||||||
|
use tokio::fs::create_dir;
|
||||||
|
|
||||||
use crate::HarmonicError;
|
use crate::HarmonicError;
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
|
@ -16,14 +19,26 @@ pub struct CreateDirectory {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateDirectory {
|
impl CreateDirectory {
|
||||||
pub fn plan(path: impl AsRef<Path>, user: String, group: String, mode: u32) -> Self {
|
pub async fn plan(path: impl AsRef<Path>, user: String, group: String, mode: u32, force: bool) -> Result<Self, HarmonicError> {
|
||||||
let path = path.as_ref().to_path_buf();
|
let path = path.as_ref();
|
||||||
Self {
|
|
||||||
path,
|
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()))))
|
||||||
|
}
|
||||||
|
// 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,
|
user,
|
||||||
group,
|
group,
|
||||||
mode,
|
mode,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,10 +46,11 @@ 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;
|
||||||
vec![ActionDescription::new(
|
vec![ActionDescription::new(
|
||||||
format!("Create the directory `/nix`"),
|
format!("Create the directory `{}`", path.display()),
|
||||||
vec![format!(
|
vec![format!(
|
||||||
"Nix and the Nix daemon require a Nix Store, which will be stored at `/nix`"
|
"Creating directory `{}` owned by `{user}:{group}` with mode `{mode:#o}`", path.display()
|
||||||
)],
|
)],
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
|
@ -46,7 +62,22 @@ impl<'a> Actionable<'a> for CreateDirectory {
|
||||||
group,
|
group,
|
||||||
mode,
|
mode,
|
||||||
} = self;
|
} = self;
|
||||||
todo!();
|
|
||||||
|
let gid = Group::from_name(group.as_str())
|
||||||
|
.map_err(|e| HarmonicError::GroupId(group.clone(), e))?
|
||||||
|
.ok_or(HarmonicError::NoGroup(group.clone()))?
|
||||||
|
.gid;
|
||||||
|
let uid = User::from_name(user.as_str())
|
||||||
|
.map_err(|e| HarmonicError::UserId(user.clone(), e))?
|
||||||
|
.ok_or(HarmonicError::NoUser(user.clone()))?
|
||||||
|
.uid;
|
||||||
|
|
||||||
|
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))?;
|
||||||
|
|
||||||
Ok(CreateDirectoryReceipt {
|
Ok(CreateDirectoryReceipt {
|
||||||
path,
|
path,
|
||||||
user,
|
user,
|
||||||
|
|
100
src/actions/base/create_file.rs
Normal file
100
src/actions/base/create_file.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
use std::{
|
||||||
|
fs::Permissions,
|
||||||
|
path::{Path, PathBuf, self}, io::SeekFrom,
|
||||||
|
};
|
||||||
|
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};
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct CreateFile {
|
||||||
|
path: PathBuf,
|
||||||
|
user: String,
|
||||||
|
group: String,
|
||||||
|
mode: u32,
|
||||||
|
buf: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateFile {
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<'a> Actionable<'a> for CreateFile {
|
||||||
|
type Receipt = CreateFileReceipt;
|
||||||
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
|
let Self { path, user, group, mode, buf } = &self;
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
format!("Create or overwrite file `{}`", path.display()),
|
||||||
|
vec![format!(
|
||||||
|
"Create or overwrite `{}` owned by `{user}:{group}` with mode `{mode:#o}` with `{buf}`", path.display()
|
||||||
|
)],
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
|
||||||
|
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 mut file = OpenOptions::new()
|
||||||
|
.create_new(true)
|
||||||
|
.write(true)
|
||||||
|
.read(true)
|
||||||
|
.open(&path)
|
||||||
|
.await
|
||||||
|
.map_err(|e| HarmonicError::OpenFile(path.to_owned(), e))?;
|
||||||
|
|
||||||
|
file.write_all(buf.as_bytes())
|
||||||
|
.await
|
||||||
|
.map_err(|e| HarmonicError::WriteFile(path.to_owned(), e))?;
|
||||||
|
|
||||||
|
let gid = Group::from_name(group.as_str())
|
||||||
|
.map_err(|e| HarmonicError::GroupId(group.clone(), e))?
|
||||||
|
.ok_or(HarmonicError::NoGroup(group.clone()))?
|
||||||
|
.gid;
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct CreateFileReceipt {
|
||||||
|
path: PathBuf,
|
||||||
|
user: String,
|
||||||
|
group: String,
|
||||||
|
mode: u32,
|
||||||
|
buf: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<'a> Revertable<'a> for CreateFileReceipt {
|
||||||
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn revert(self) -> Result<(), HarmonicError> {
|
||||||
|
|
||||||
|
|
||||||
|
todo!();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::HarmonicError;
|
use crate::HarmonicError;
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
|
@ -5,12 +7,14 @@ use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateGroup {
|
pub struct CreateGroup {
|
||||||
name: String,
|
name: String,
|
||||||
uid: usize,
|
gid: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateGroup {
|
impl CreateGroup {
|
||||||
pub fn plan(name: String, uid: usize) -> Self {
|
pub fn plan(name: String, gid: usize) -> Self {
|
||||||
Self { name, uid }
|
|
||||||
|
|
||||||
|
Self { name, gid }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,10 +22,9 @@ impl CreateGroup {
|
||||||
impl<'a> Actionable<'a> for CreateGroup {
|
impl<'a> Actionable<'a> for CreateGroup {
|
||||||
type Receipt = CreateGroupReceipt;
|
type Receipt = CreateGroupReceipt;
|
||||||
fn description(&self) -> Vec<ActionDescription> {
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
let name = &self.name;
|
let Self { name, gid } = &self;
|
||||||
let uid = &self.uid;
|
|
||||||
vec![ActionDescription::new(
|
vec![ActionDescription::new(
|
||||||
format!("Create group {name} with UID {uid}"),
|
format!("Create group {name} with GID {gid}"),
|
||||||
vec![format!(
|
vec![format!(
|
||||||
"The nix daemon requires a system user group its system users can be part of"
|
"The nix daemon requires a system user group its system users can be part of"
|
||||||
)],
|
)],
|
||||||
|
@ -29,15 +32,36 @@ impl<'a> Actionable<'a> for CreateGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||||
let Self { name, uid } = self;
|
let Self { name, gid } = self;
|
||||||
Ok(CreateGroupReceipt { name, uid })
|
|
||||||
|
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)),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(CreateGroupReceipt { name, gid })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateGroupReceipt {
|
pub struct CreateGroupReceipt {
|
||||||
name: String,
|
name: String,
|
||||||
uid: usize,
|
gid: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|
108
src/actions/base/create_or_append_file.rs
Normal file
108
src/actions/base/create_or_append_file.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
use std::{
|
||||||
|
fs::Permissions,
|
||||||
|
path::{Path, PathBuf, self}, io::SeekFrom,
|
||||||
|
};
|
||||||
|
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};
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct CreateOrAppendFile {
|
||||||
|
path: PathBuf,
|
||||||
|
user: String,
|
||||||
|
group: String,
|
||||||
|
mode: u32,
|
||||||
|
buf: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreateOrAppendFile {
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<'a> Actionable<'a> for CreateOrAppendFile {
|
||||||
|
type Receipt = CreateOrAppendFileReceipt;
|
||||||
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
|
let Self { path, user, group, mode, buf } = &self;
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
format!("Create or append file `{}`", path.display()),
|
||||||
|
vec![format!(
|
||||||
|
"Create or append `{}` owned by `{user}:{group}` with mode `{mode:#o}` with `{buf}`", path.display()
|
||||||
|
)],
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute(self) -> Result<CreateOrAppendFileReceipt, 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 mut file = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.write(true)
|
||||||
|
.read(true)
|
||||||
|
.open(&path)
|
||||||
|
.await
|
||||||
|
.map_err(|e| HarmonicError::OpenFile(path.to_owned(), e))?;
|
||||||
|
|
||||||
|
file.seek(SeekFrom::End(0))
|
||||||
|
.await
|
||||||
|
.map_err(|e| HarmonicError::SeekFile(path.to_owned(), e))?;
|
||||||
|
file.write_all(buf.as_bytes())
|
||||||
|
.await
|
||||||
|
.map_err(|e| HarmonicError::WriteFile(path.to_owned(), e))?;
|
||||||
|
|
||||||
|
let gid = Group::from_name(group.as_str())
|
||||||
|
.map_err(|e| HarmonicError::GroupId(group.clone(), e))?
|
||||||
|
.ok_or(HarmonicError::NoGroup(group.clone()))?
|
||||||
|
.gid;
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct CreateOrAppendFileReceipt {
|
||||||
|
path: PathBuf,
|
||||||
|
user: String,
|
||||||
|
group: String,
|
||||||
|
mode: u32,
|
||||||
|
buf: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<'a> Revertable<'a> for CreateOrAppendFileReceipt {
|
||||||
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
|
vec![ActionDescription::new(
|
||||||
|
format!("Create the directory `/nix`"),
|
||||||
|
vec![format!(
|
||||||
|
"Nix and the Nix daemon require a Nix Store, which will be stored at `/nix`"
|
||||||
|
)],
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn revert(self) -> Result<(), HarmonicError> {
|
||||||
|
|
||||||
|
|
||||||
|
todo!();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::HarmonicError;
|
use crate::HarmonicError;
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
|
@ -6,11 +8,12 @@ use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
pub struct CreateUser {
|
pub struct CreateUser {
|
||||||
name: String,
|
name: String,
|
||||||
uid: usize,
|
uid: usize,
|
||||||
|
gid: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateUser {
|
impl CreateUser {
|
||||||
pub fn plan(name: String, uid: usize) -> Self {
|
pub fn plan(name: String, uid: usize, gid: usize) -> Self {
|
||||||
Self { name, uid }
|
Self { name, uid, gid }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +32,41 @@ impl<'a> Actionable<'a> for CreateUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||||
let Self { name, uid } = self;
|
let Self { name, uid, gid } = self;
|
||||||
Ok(CreateUserReceipt { name, uid })
|
|
||||||
|
let mut command = Command::new("useradd");
|
||||||
|
command.args([
|
||||||
|
"--home-dir",
|
||||||
|
"/var/empty",
|
||||||
|
"--comment",
|
||||||
|
&format!("\"Nix build user\""),
|
||||||
|
"--gid",
|
||||||
|
&gid.to_string(),
|
||||||
|
"--groups",
|
||||||
|
&gid.to_string(),
|
||||||
|
"--no-user-group",
|
||||||
|
"--system",
|
||||||
|
"--shell",
|
||||||
|
"/sbin/nologin",
|
||||||
|
"--uid",
|
||||||
|
&uid.to_string(),
|
||||||
|
"--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)),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(CreateUserReceipt { name, uid, gid })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +74,7 @@ impl<'a> Actionable<'a> for CreateUser {
|
||||||
pub struct CreateUserReceipt {
|
pub struct CreateUserReceipt {
|
||||||
name: String,
|
name: String,
|
||||||
uid: usize,
|
uid: usize,
|
||||||
|
gid: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use bytes::Buf;
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
use crate::HarmonicError;
|
use crate::HarmonicError;
|
||||||
|
|
||||||
|
@ -13,8 +15,11 @@ pub struct FetchNix {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FetchNix {
|
impl FetchNix {
|
||||||
pub fn plan(url: Url, destination: PathBuf) -> Self {
|
pub async fn plan(url: Url, destination: PathBuf) -> Result<Self, HarmonicError> {
|
||||||
Self { url, destination }
|
// TODO(@hoverbear): Check URL exists?
|
||||||
|
// TODO(@hoverbear): Check tempdir exists
|
||||||
|
|
||||||
|
Ok(Self { url, destination })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +40,24 @@ impl<'a> Actionable<'a> for FetchNix {
|
||||||
|
|
||||||
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");
|
||||||
|
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");
|
||||||
|
let destination_clone = destination.clone();
|
||||||
|
let handle: Result<(), HarmonicError> = spawn_blocking(move || {
|
||||||
|
let decoder = xz2::read::XzDecoder::new(bytes.reader());
|
||||||
|
let mut archive = tar::Archive::new(decoder);
|
||||||
|
archive.unpack(&destination_clone).map_err(HarmonicError::Unarchive)?;
|
||||||
|
tracing::debug!(destination = %destination_clone.display(), "Downloaded & extracted Nix");
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
handle?;
|
||||||
|
|
||||||
Ok(FetchNixReceipt { url, destination })
|
Ok(FetchNixReceipt { url, destination })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,21 +3,25 @@
|
||||||
mod configure_nix_daemon_service;
|
mod configure_nix_daemon_service;
|
||||||
mod configure_shell_profile;
|
mod configure_shell_profile;
|
||||||
mod create_directory;
|
mod create_directory;
|
||||||
|
mod create_file;
|
||||||
mod create_group;
|
mod create_group;
|
||||||
|
mod create_or_append_file;
|
||||||
mod create_user;
|
mod create_user;
|
||||||
mod fetch_nix;
|
mod fetch_nix;
|
||||||
mod move_unpacked_nix;
|
mod move_unpacked_nix;
|
||||||
mod place_channel_configuration;
|
mod place_channel_configuration;
|
||||||
mod place_nix_configuration;
|
mod place_nix_configuration;
|
||||||
mod setup_default_profile;
|
mod setup_default_profile;
|
||||||
mod start_systemd_service;
|
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 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_group::{CreateGroup, CreateGroupReceipt};
|
pub use create_group::{CreateGroup, CreateGroupReceipt};
|
||||||
|
pub use create_or_append_file::{CreateOrAppendFile, CreateOrAppendFileReceipt};
|
||||||
pub use create_user::{CreateUser, CreateUserReceipt};
|
pub use create_user::{CreateUser, CreateUserReceipt};
|
||||||
pub use fetch_nix::{FetchNix, FetchNixReceipt};
|
pub use fetch_nix::{FetchNix, FetchNixReceipt};
|
||||||
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixReceipt};
|
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixReceipt};
|
||||||
|
@ -26,4 +30,4 @@ pub use place_channel_configuration::{
|
||||||
};
|
};
|
||||||
pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationReceipt};
|
pub use place_nix_configuration::{PlaceNixConfiguration, PlaceNixConfigurationReceipt};
|
||||||
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileReceipt};
|
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileReceipt};
|
||||||
pub use start_systemd_service::{StartSystemdService, StartSystemdServiceReceipt};
|
pub use start_systemd_unit::{StartSystemdUnit, StartSystemdUnitReceipt};
|
|
@ -1,4 +1,4 @@
|
||||||
use std::path::PathBuf;
|
use std::path::{PathBuf, Path};
|
||||||
|
|
||||||
use crate::HarmonicError;
|
use crate::HarmonicError;
|
||||||
|
|
||||||
|
@ -10,8 +10,9 @@ pub struct MoveUnpackedNix {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MoveUnpackedNix {
|
impl MoveUnpackedNix {
|
||||||
pub fn plan(source: PathBuf) -> Self {
|
pub async fn plan(source: PathBuf) -> Result<Self, HarmonicError> {
|
||||||
Self { source, }
|
// Note: Do NOT try to check for the source/dest since the installer creates those
|
||||||
|
Ok(Self { source, })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +31,23 @@ impl<'a> Actionable<'a> for MoveUnpackedNix {
|
||||||
|
|
||||||
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
|
||||||
|
let found_nix_paths = glob::glob(&format!("{}/nix-*", source.display()))?
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
assert_eq!(
|
||||||
|
found_nix_paths.len(),
|
||||||
|
1,
|
||||||
|
"Did not expect to find multiple nix paths, please report this"
|
||||||
|
);
|
||||||
|
let found_nix_path = found_nix_paths.into_iter().next().unwrap();
|
||||||
|
tracing::trace!("Renaming");
|
||||||
|
let src = found_nix_path.join("store");
|
||||||
|
let dest = Path::new("/nix/store");
|
||||||
|
tokio::fs::rename(src.clone(), dest)
|
||||||
|
.await
|
||||||
|
.map_err(|e| HarmonicError::Rename(src, dest.to_owned(), e))?;
|
||||||
|
|
||||||
Ok(MoveUnpackedNixReceipt { })
|
Ok(MoveUnpackedNixReceipt { })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,30 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use reqwest::Url;
|
||||||
|
|
||||||
use crate::HarmonicError;
|
use crate::HarmonicError;
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
|
|
||||||
|
use super::{CreateOrAppendFile, CreateFile, CreateFileReceipt};
|
||||||
|
|
||||||
|
const NIX_CHANNELS_PATH: &str = "/root/.nix-channels";
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct PlaceChannelConfiguration {}
|
pub struct PlaceChannelConfiguration {
|
||||||
|
channels: Vec<(String, Url)>,
|
||||||
|
create_file: CreateFile,
|
||||||
|
}
|
||||||
|
|
||||||
impl PlaceChannelConfiguration {
|
impl PlaceChannelConfiguration {
|
||||||
pub fn plan() -> Self {
|
pub async fn plan(channels: Vec<(String, Url)>) -> Result<Self, HarmonicError> {
|
||||||
Self {}
|
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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,35 +32,34 @@ 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;
|
||||||
vec![
|
vec![
|
||||||
ActionDescription::new(
|
ActionDescription::new(
|
||||||
"Start the systemd Nix daemon".to_string(),
|
"Place a channel configuration".to_string(),
|
||||||
vec![
|
vec![
|
||||||
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
"Place a configuration at `{NIX_CHANNELS_PATH}` setting the channels".to_string()
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||||
todo!()
|
let Self { create_file, channels } = self;
|
||||||
|
let create_file = create_file.execute().await?;
|
||||||
|
Ok(Self::Receipt { create_file, channels })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct PlaceChannelConfigurationReceipt {}
|
pub struct PlaceChannelConfigurationReceipt {
|
||||||
|
channels: Vec<(String, Url)>,
|
||||||
|
create_file: CreateFileReceipt,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<'a> Revertable<'a> for PlaceChannelConfigurationReceipt {
|
impl<'a> Revertable<'a> for PlaceChannelConfigurationReceipt {
|
||||||
fn description(&self) -> Vec<ActionDescription> {
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
vec![
|
todo!()
|
||||||
ActionDescription::new(
|
|
||||||
"Stop the systemd Nix daemon".to_string(),
|
|
||||||
vec![
|
|
||||||
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn revert(self) -> Result<(), HarmonicError> {
|
async fn revert(self) -> Result<(), HarmonicError> {
|
||||||
|
|
|
@ -2,12 +2,26 @@ use crate::HarmonicError;
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
|
|
||||||
|
use super::{CreateFile, CreateFileReceipt};
|
||||||
|
|
||||||
|
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_file: CreateFile,
|
||||||
|
}
|
||||||
|
|
||||||
impl PlaceNixConfiguration {
|
impl PlaceNixConfiguration {
|
||||||
pub fn plan() -> Self {
|
pub async fn plan(nix_build_group_name: String, extra_conf: Option<String>) -> Result<Self, HarmonicError> {
|
||||||
Self {}
|
let buf = format!(
|
||||||
|
"\
|
||||||
|
{extra_conf}\n\
|
||||||
|
build-users-group = {nix_build_group_name}\n\
|
||||||
|
",
|
||||||
|
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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,33 +31,30 @@ impl<'a> Actionable<'a> for PlaceNixConfiguration {
|
||||||
fn description(&self) -> Vec<ActionDescription> {
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
vec![
|
vec![
|
||||||
ActionDescription::new(
|
ActionDescription::new(
|
||||||
"Start the systemd Nix daemon".to_string(),
|
"Place the nix configuration".to_string(),
|
||||||
vec![
|
vec![
|
||||||
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
"Boop".to_string()
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||||
todo!()
|
let Self { create_file } = self;
|
||||||
|
let create_file = create_file.execute().await?;
|
||||||
|
Ok(Self::Receipt { create_file })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct PlaceNixConfigurationReceipt {}
|
pub struct PlaceNixConfigurationReceipt {
|
||||||
|
create_file: CreateFileReceipt,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<'a> Revertable<'a> for PlaceNixConfigurationReceipt {
|
impl<'a> Revertable<'a> for PlaceNixConfigurationReceipt {
|
||||||
fn description(&self) -> Vec<ActionDescription> {
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
vec![
|
todo!()
|
||||||
ActionDescription::new(
|
|
||||||
"Stop the systemd Nix daemon".to_string(),
|
|
||||||
vec![
|
|
||||||
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn revert(self) -> Result<(), HarmonicError> {
|
async fn revert(self) -> Result<(), HarmonicError> {
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
use crate::HarmonicError;
|
use crate::{HarmonicError, execute_command};
|
||||||
|
|
||||||
|
use glob::glob;
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct SetupDefaultProfile {}
|
pub struct SetupDefaultProfile {
|
||||||
|
channels: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl SetupDefaultProfile {
|
impl SetupDefaultProfile {
|
||||||
pub fn plan() -> Self {
|
pub async fn plan(channels: Vec<String>) -> Result<Self, HarmonicError> {
|
||||||
Self {}
|
Ok(Self { channels })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +31,85 @@ impl<'a> Actionable<'a> for SetupDefaultProfile {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||||
todo!()
|
let Self { channels } = self;
|
||||||
|
tracing::info!("Setting up default profile");
|
||||||
|
|
||||||
|
// Find an `nix` package
|
||||||
|
let nix_pkg_glob = "/nix/store/*-nix-*";
|
||||||
|
let mut found_nix_pkg = None;
|
||||||
|
for entry in glob(nix_pkg_glob).map_err(HarmonicError::GlobPatternError)? {
|
||||||
|
match entry {
|
||||||
|
Ok(path) => {
|
||||||
|
// TODO(@Hoverbear): Should probably ensure is unique
|
||||||
|
found_nix_pkg = Some(path);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
Err(_) => continue, /* Ignore it */
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let nix_pkg = if let Some(nix_pkg) = found_nix_pkg {
|
||||||
|
nix_pkg
|
||||||
|
} else {
|
||||||
|
return Err(HarmonicError::NoNssCacert); // TODO(@hoverbear): Fix this error
|
||||||
|
};
|
||||||
|
|
||||||
|
// Install `nix` itself into the store
|
||||||
|
execute_command(
|
||||||
|
Command::new(nix_pkg.join("bin/nix-env"))
|
||||||
|
.arg("-i")
|
||||||
|
.arg(&nix_pkg),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Find an `nss-cacert` package, add it too.
|
||||||
|
let nss_ca_cert_pkg_glob = "/nix/store/*-nss-cacert-*";
|
||||||
|
let mut found_nss_ca_cert_pkg = None;
|
||||||
|
for entry in glob(nss_ca_cert_pkg_glob).map_err(HarmonicError::GlobPatternError)? {
|
||||||
|
match entry {
|
||||||
|
Ok(path) => {
|
||||||
|
// TODO(@Hoverbear): Should probably ensure is unique
|
||||||
|
found_nss_ca_cert_pkg = Some(path);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
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"))
|
||||||
|
.arg("-i")
|
||||||
|
.arg(&nss_ca_cert_pkg),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !channels.is_empty() {
|
||||||
|
let mut command = Command::new(nix_pkg.join("bin/nix-channel"));
|
||||||
|
command.arg("--update");
|
||||||
|
for channel in channels {
|
||||||
|
command.arg(channel);
|
||||||
|
}
|
||||||
|
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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self::Receipt {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
use crate::HarmonicError;
|
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
|
||||||
pub struct StartSystemdService {}
|
|
||||||
|
|
||||||
impl StartSystemdService {
|
|
||||||
pub fn plan() -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<'a> Actionable<'a> for StartSystemdService {
|
|
||||||
type Receipt = StartSystemdServiceReceipt;
|
|
||||||
fn description(&self) -> Vec<ActionDescription> {
|
|
||||||
vec![
|
|
||||||
ActionDescription::new(
|
|
||||||
"Start the systemd Nix daemon".to_string(),
|
|
||||||
vec![
|
|
||||||
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
|
||||||
pub struct StartSystemdServiceReceipt {}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<'a> Revertable<'a> for StartSystemdServiceReceipt {
|
|
||||||
fn description(&self) -> Vec<ActionDescription> {
|
|
||||||
vec![
|
|
||||||
ActionDescription::new(
|
|
||||||
"Stop the systemd Nix daemon".to_string(),
|
|
||||||
vec![
|
|
||||||
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn revert(self) -> Result<(), HarmonicError> {
|
|
||||||
todo!();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
71
src/actions/base/start_systemd_unit.rs
Normal file
71
src/actions/base/start_systemd_unit.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::{HarmonicError, execute_command};
|
||||||
|
|
||||||
|
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct StartSystemdUnit {
|
||||||
|
unit: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StartSystemdUnit {
|
||||||
|
pub async fn plan(unit: String) -> Result<Self, HarmonicError> {
|
||||||
|
Ok(Self { unit })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<'a> Actionable<'a> for StartSystemdUnit {
|
||||||
|
type Receipt = StartSystemdUnitReceipt;
|
||||||
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
|
vec![
|
||||||
|
ActionDescription::new(
|
||||||
|
"Start the systemd Nix service and socket".to_string(),
|
||||||
|
vec![
|
||||||
|
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
||||||
|
]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
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(format!("{unit}")),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Self::Receipt {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct StartSystemdUnitReceipt {}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl<'a> Revertable<'a> for StartSystemdUnitReceipt {
|
||||||
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn revert(self) -> Result<(), HarmonicError> {
|
||||||
|
todo!();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +1,34 @@
|
||||||
use tokio::task::JoinSet;
|
use tokio::task::JoinSet;
|
||||||
|
|
||||||
use crate::actions::base::{SetupDefaultProfile, ConfigureNixDaemonService, ConfigureShellProfile, SetupDefaultProfileReceipt, ConfigureNixDaemonServiceReceipt, ConfigureShellProfileReceipt};
|
use crate::actions::base::{SetupDefaultProfile, ConfigureNixDaemonService, ConfigureShellProfile, SetupDefaultProfileReceipt, ConfigureNixDaemonServiceReceipt, ConfigureShellProfileReceipt, PlaceNixConfigurationReceipt, PlaceNixConfiguration};
|
||||||
use crate::{HarmonicError, InstallSettings};
|
use crate::{HarmonicError, InstallSettings, Harmonic};
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
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_nix_daemon_service: ConfigureNixDaemonService,
|
|
||||||
configure_shell_profile: Option<ConfigureShellProfile>,
|
configure_shell_profile: Option<ConfigureShellProfile>,
|
||||||
|
place_nix_configuration: PlaceNixConfiguration,
|
||||||
|
configure_nix_daemon_service: ConfigureNixDaemonService,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigureNix {
|
impl ConfigureNix {
|
||||||
pub fn plan(settings: InstallSettings) -> Self {
|
pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> {
|
||||||
let setup_default_profile = SetupDefaultProfile::plan();
|
let channels = settings.channels.iter().map(|(channel, _)| channel.to_string()).collect();
|
||||||
let configure_nix_daemon_service = ConfigureNixDaemonService::plan();
|
|
||||||
|
let setup_default_profile = SetupDefaultProfile::plan(channels).await?;
|
||||||
|
|
||||||
let configure_shell_profile = if settings.modify_profile {
|
let configure_shell_profile = if settings.modify_profile {
|
||||||
Some(ConfigureShellProfile::plan())
|
Some(ConfigureShellProfile::plan().await?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
let place_nix_configuration = PlaceNixConfiguration::plan(settings.nix_build_group_name, settings.extra_conf).await?;
|
||||||
|
let configure_nix_daemon_service = ConfigureNixDaemonService::plan().await?;
|
||||||
|
|
||||||
|
|
||||||
Self { setup_default_profile, configure_nix_daemon_service, configure_shell_profile }
|
Ok(Self { place_nix_configuration, setup_default_profile, configure_nix_daemon_service, configure_shell_profile })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,10 +36,11 @@ 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, configure_shell_profile } = &self;
|
let Self { setup_default_profile, configure_nix_daemon_service, place_nix_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());
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
@ -44,26 +49,29 @@ impl<'a> Actionable<'a> for ConfigureNix {
|
||||||
}
|
}
|
||||||
|
|
||||||
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, configure_shell_profile } = self;
|
let Self { setup_default_profile, configure_nix_daemon_service, place_nix_configuration, configure_shell_profile } = self;
|
||||||
|
|
||||||
let (setup_default_profile, configure_nix_daemon_service, configure_shell_profile) = if let Some(configure_shell_profile) = configure_shell_profile {
|
let (setup_default_profile, configure_nix_daemon_service, place_nix_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(),
|
||||||
|
configure_shell_profile.execute(),
|
||||||
|
)?;
|
||||||
|
(a, b, c, Some(d))
|
||||||
|
} 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(),
|
configure_nix_daemon_service.execute(),
|
||||||
configure_shell_profile.execute(),
|
place_nix_configuration.execute(),
|
||||||
)?;
|
)?;
|
||||||
(a, b, Some(c))
|
(a, b, c, None)
|
||||||
} else {
|
|
||||||
let (a, b) = tokio::try_join!(
|
|
||||||
setup_default_profile.execute(),
|
|
||||||
configure_nix_daemon_service.execute(),
|
|
||||||
)?;
|
|
||||||
(a, b, None)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self::Receipt {
|
Ok(Self::Receipt {
|
||||||
setup_default_profile,
|
setup_default_profile,
|
||||||
configure_nix_daemon_service,
|
configure_nix_daemon_service,
|
||||||
|
place_nix_configuration,
|
||||||
configure_shell_profile,
|
configure_shell_profile,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -73,8 +81,9 @@ impl<'a> Actionable<'a> for ConfigureNix {
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct ConfigureNixReceipt {
|
pub struct ConfigureNixReceipt {
|
||||||
setup_default_profile: SetupDefaultProfileReceipt,
|
setup_default_profile: SetupDefaultProfileReceipt,
|
||||||
configure_nix_daemon_service: ConfigureNixDaemonServiceReceipt,
|
|
||||||
configure_shell_profile: Option<ConfigureShellProfileReceipt>,
|
configure_shell_profile: Option<ConfigureShellProfileReceipt>,
|
||||||
|
place_nix_configuration: PlaceNixConfigurationReceipt,
|
||||||
|
configure_nix_daemon_service: ConfigureNixDaemonServiceReceipt,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|
|
@ -1,15 +1,37 @@
|
||||||
use crate::{HarmonicError, InstallSettings};
|
use crate::HarmonicError;
|
||||||
|
|
||||||
|
use crate::actions::base::{CreateDirectory, CreateDirectoryReceipt};
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateNixTree {
|
pub struct CreateNixTree {
|
||||||
settings: InstallSettings,
|
create_directories: Vec<CreateDirectory>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateNixTree {
|
impl CreateNixTree {
|
||||||
pub fn plan(settings: InstallSettings) -> Self {
|
pub async fn plan(force: bool) -> Result<Self, HarmonicError> {
|
||||||
Self { settings }
|
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 {
|
||||||
|
// 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?)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { create_directories })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,13 +48,22 @@ impl<'a> Actionable<'a> for CreateNixTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||||
let Self { settings: _ } = self;
|
let Self { create_directories } = self;
|
||||||
Ok(CreateNixTreeReceipt {})
|
|
||||||
|
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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateNixTreeReceipt {}
|
pub struct CreateNixTreeReceipt {
|
||||||
|
create_directories: Vec<CreateDirectoryReceipt>,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<'a> Revertable<'a> for CreateNixTreeReceipt {
|
impl<'a> Revertable<'a> for CreateNixTreeReceipt {
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
use crate::HarmonicError;
|
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
|
||||||
pub struct CreateNixTreeDirs {}
|
|
||||||
|
|
||||||
impl CreateNixTreeDirs {
|
|
||||||
pub fn plan(name: String, uid: usize) -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<'a> Actionable<'a> for CreateNixTreeDirs {
|
|
||||||
type Receipt = CreateNixTreeDirsReceipt;
|
|
||||||
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`"
|
|
||||||
)],
|
|
||||||
)]
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
|
||||||
let Self {} = self;
|
|
||||||
Ok(CreateNixTreeDirsReceipt {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
|
||||||
pub struct CreateNixTreeDirsReceipt {}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl<'a> Revertable<'a> for CreateNixTreeDirsReceipt {
|
|
||||||
fn description(&self) -> Vec<ActionDescription> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn revert(self) -> Result<(), HarmonicError> {
|
|
||||||
todo!();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,29 +7,40 @@ use crate::actions::{ActionDescription, ActionReceipt, Actionable, CreateUser, R
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||||
pub struct CreateUsersAndGroup {
|
pub struct CreateUsersAndGroup {
|
||||||
settings: InstallSettings,
|
daemon_user_count: usize,
|
||||||
|
nix_build_group_name: String,
|
||||||
|
nix_build_group_id: usize,
|
||||||
|
nix_build_user_prefix: String,
|
||||||
|
nix_build_user_id_base: usize,
|
||||||
create_group: CreateGroup,
|
create_group: CreateGroup,
|
||||||
create_users: Vec<CreateUser>,
|
create_users: Vec<CreateUser>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateUsersAndGroup {
|
impl CreateUsersAndGroup {
|
||||||
pub fn plan(
|
pub async fn plan(
|
||||||
settings: InstallSettings
|
settings: InstallSettings
|
||||||
) -> Self {
|
) -> 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)
|
let create_users = (0..settings.daemon_user_count)
|
||||||
.map(|count| {
|
.map(|count| {
|
||||||
CreateUser::plan(
|
CreateUser::plan(
|
||||||
format!("{}{count}", settings.nix_build_user_prefix),
|
format!("{}{count}", settings.nix_build_user_prefix),
|
||||||
settings.nix_build_user_id_base + count,
|
settings.nix_build_user_id_base + count,
|
||||||
|
settings.nix_build_group_id,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Self {
|
Ok(Self {
|
||||||
settings,
|
daemon_user_count: settings.daemon_user_count,
|
||||||
|
nix_build_group_name: settings.nix_build_group_name,
|
||||||
|
nix_build_group_id: settings.nix_build_group_id,
|
||||||
|
nix_build_user_prefix: settings.nix_build_user_prefix,
|
||||||
|
nix_build_user_id_base: settings.nix_build_user_id_base,
|
||||||
create_group,
|
create_group,
|
||||||
create_users,
|
create_users,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,14 +49,11 @@ impl<'a> Actionable<'a> for CreateUsersAndGroup {
|
||||||
type Receipt = CreateUsersAndGroupReceipt;
|
type Receipt = CreateUsersAndGroupReceipt;
|
||||||
fn description(&self) -> Vec<ActionDescription> {
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
let Self {
|
let Self {
|
||||||
settings: InstallSettings {
|
daemon_user_count,
|
||||||
daemon_user_count,
|
nix_build_group_name,
|
||||||
nix_build_group_name,
|
nix_build_group_id,
|
||||||
nix_build_group_id,
|
nix_build_user_prefix,
|
||||||
nix_build_user_prefix,
|
nix_build_user_id_base,
|
||||||
nix_build_user_id_base,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
..
|
||||||
} = &self;
|
} = &self;
|
||||||
|
|
||||||
|
@ -62,7 +70,7 @@ impl<'a> Actionable<'a> for CreateUsersAndGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||||
let Self { create_users, create_group, settings: _ } = 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?;
|
||||||
|
@ -87,19 +95,6 @@ impl<'a> Actionable<'a> for CreateUsersAndGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
// If we got an error in a child, we need to revert the successful ones:
|
|
||||||
let mut failed_reverts = Vec::default();
|
|
||||||
for success in successes {
|
|
||||||
match success.revert().await {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(e) => failed_reverts.push(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !failed_reverts.is_empty() {
|
|
||||||
return Err(HarmonicError::FailedReverts(errors, failed_reverts));
|
|
||||||
}
|
|
||||||
|
|
||||||
if errors.len() == 1 {
|
if errors.len() == 1 {
|
||||||
return Err(errors.into_iter().next().unwrap());
|
return Err(errors.into_iter().next().unwrap());
|
||||||
} else {
|
} else {
|
||||||
|
@ -107,7 +102,7 @@ impl<'a> Actionable<'a> for CreateUsersAndGroup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CreateUsersAndGroupReceipt {
|
Ok(Self::Receipt {
|
||||||
create_group,
|
create_group,
|
||||||
create_users: successes,
|
create_users: successes,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
/*! Actions which only call other base plugins. */
|
/*! Actions which only call other base plugins. */
|
||||||
|
|
||||||
mod create_nix_tree;
|
mod create_nix_tree;
|
||||||
mod create_nix_tree_dirs;
|
|
||||||
mod create_users_and_group;
|
mod create_users_and_group;
|
||||||
mod configure_nix;
|
mod configure_nix;
|
||||||
mod start_nix_daemon;
|
mod start_nix_daemon;
|
||||||
mod provision_nix;
|
mod provision_nix;
|
||||||
|
|
||||||
pub use create_nix_tree::{CreateNixTree, CreateNixTreeReceipt};
|
pub use create_nix_tree::{CreateNixTree, CreateNixTreeReceipt};
|
||||||
pub use create_nix_tree_dirs::{CreateNixTreeDirs, CreateNixTreeDirsReceipt};
|
|
||||||
pub use create_users_and_group::{CreateUsersAndGroup, CreateUsersAndGroupReceipt};
|
pub use create_users_and_group::{CreateUsersAndGroup, CreateUsersAndGroupReceipt};
|
||||||
pub use configure_nix::{ConfigureNix, ConfigureNixReceipt};
|
pub use configure_nix::{ConfigureNix, ConfigureNixReceipt};
|
||||||
pub use start_nix_daemon::{StartNixDaemon, StartNixDaemonReceipt};
|
pub use start_nix_daemon::{StartNixDaemon, StartNixDaemonReceipt};
|
||||||
|
|
|
@ -16,13 +16,13 @@ pub struct ProvisionNix {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProvisionNix {
|
impl ProvisionNix {
|
||||||
pub 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());
|
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());
|
let create_users_and_group = CreateUsersAndGroup::plan(settings.clone()).await?;
|
||||||
let create_nix_tree = CreateNixTree::plan(settings.clone());
|
let create_nix_tree = CreateNixTree::plan(settings.force).await?;
|
||||||
let move_unpacked_nix = MoveUnpackedNix::plan(tempdir.path().to_path_buf());
|
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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
|
use crate::actions::base::{ConfigureNixDaemonServiceReceipt, ConfigureNixDaemonService, StartSystemdUnit, StartSystemdUnitReceipt};
|
||||||
use crate::{HarmonicError, InstallSettings};
|
use crate::{HarmonicError, InstallSettings};
|
||||||
|
|
||||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||||
|
|
||||||
|
/// 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_service: StartSystemdUnit,
|
||||||
|
}
|
||||||
|
|
||||||
impl StartNixDaemon {
|
impl StartNixDaemon {
|
||||||
pub fn plan(settings: InstallSettings) -> Self {
|
pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> {
|
||||||
Self {}
|
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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,35 +22,28 @@ 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> {
|
||||||
vec![
|
let Self { start_systemd_socket, start_systemd_service } = &self;
|
||||||
ActionDescription::new(
|
start_systemd_service.description()
|
||||||
"Start the systemd Nix daemon".to_string(),
|
|
||||||
vec![
|
|
||||||
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||||
todo!()
|
let Self { start_systemd_socket, start_systemd_service } = self;
|
||||||
|
let start_systemd_service = start_systemd_service.execute().await?;
|
||||||
|
let start_systemd_socket = start_systemd_socket.execute().await?;
|
||||||
|
Ok(Self::Receipt { start_systemd_socket, start_systemd_service })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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_service: StartSystemdUnitReceipt,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<'a> Revertable<'a> for StartNixDaemonReceipt {
|
impl<'a> Revertable<'a> for StartNixDaemonReceipt {
|
||||||
fn description(&self) -> Vec<ActionDescription> {
|
fn description(&self) -> Vec<ActionDescription> {
|
||||||
vec![
|
todo!()
|
||||||
ActionDescription::new(
|
|
||||||
"Stop the systemd Nix daemon".to_string(),
|
|
||||||
vec![
|
|
||||||
"The `nix` command line tool communicates with a running Nix daemon managed by your init system".to_string()
|
|
||||||
]
|
|
||||||
),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn revert(self) -> Result<(), HarmonicError> {
|
async fn revert(self) -> Result<(), HarmonicError> {
|
||||||
|
|
|
@ -5,19 +5,19 @@ use base::{
|
||||||
ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt,
|
ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt,
|
||||||
ConfigureShellProfile, ConfigureShellProfileReceipt,
|
ConfigureShellProfile, ConfigureShellProfileReceipt,
|
||||||
CreateDirectory, CreateDirectoryReceipt,
|
CreateDirectory, CreateDirectoryReceipt,
|
||||||
|
CreateFile, CreateFileReceipt,
|
||||||
CreateGroup, CreateGroupReceipt,
|
CreateGroup, CreateGroupReceipt,
|
||||||
|
CreateOrAppendFile, CreateOrAppendFileReceipt,
|
||||||
CreateUser, CreateUserReceipt,
|
CreateUser, CreateUserReceipt,
|
||||||
FetchNix, FetchNixReceipt,
|
FetchNix, FetchNixReceipt,
|
||||||
MoveUnpackedNix, MoveUnpackedNixReceipt,
|
MoveUnpackedNix, MoveUnpackedNixReceipt,
|
||||||
PlaceChannelConfiguration, PlaceChannelConfigurationReceipt,
|
PlaceChannelConfiguration, PlaceChannelConfigurationReceipt,
|
||||||
PlaceNixConfiguration, PlaceNixConfigurationReceipt,
|
PlaceNixConfiguration, PlaceNixConfigurationReceipt,
|
||||||
SetupDefaultProfile, SetupDefaultProfileReceipt,
|
SetupDefaultProfile, SetupDefaultProfileReceipt,
|
||||||
StartSystemdService, StartSystemdServiceReceipt,
|
|
||||||
};
|
};
|
||||||
use meta::{
|
use meta::{
|
||||||
ConfigureNix, ConfigureNixReceipt,
|
ConfigureNix, ConfigureNixReceipt,
|
||||||
CreateNixTree, CreateNixTreeReceipt,
|
CreateNixTree, CreateNixTreeReceipt,
|
||||||
CreateNixTreeDirs, CreateNixTreeDirsReceipt,
|
|
||||||
CreateUsersAndGroup, CreateUsersAndGroupReceipt,
|
CreateUsersAndGroup, CreateUsersAndGroupReceipt,
|
||||||
StartNixDaemon, StartNixDaemonReceipt,
|
StartNixDaemon, StartNixDaemonReceipt,
|
||||||
};
|
};
|
||||||
|
@ -62,8 +62,9 @@ pub enum Action {
|
||||||
ConfigureNix(ConfigureNix),
|
ConfigureNix(ConfigureNix),
|
||||||
ConfigureShellProfile(ConfigureShellProfile),
|
ConfigureShellProfile(ConfigureShellProfile),
|
||||||
CreateDirectory(CreateDirectory),
|
CreateDirectory(CreateDirectory),
|
||||||
|
CreateFile(CreateFile),
|
||||||
CreateGroup(CreateGroup),
|
CreateGroup(CreateGroup),
|
||||||
CreateNixTreeDirs(CreateNixTreeDirs),
|
CreateOrAppendFile(CreateOrAppendFile),
|
||||||
CreateNixTree(CreateNixTree),
|
CreateNixTree(CreateNixTree),
|
||||||
CreateUser(CreateUser),
|
CreateUser(CreateUser),
|
||||||
CreateUsersAndGroup(CreateUsersAndGroup),
|
CreateUsersAndGroup(CreateUsersAndGroup),
|
||||||
|
@ -73,7 +74,6 @@ pub enum Action {
|
||||||
PlaceNixConfiguration(PlaceNixConfiguration),
|
PlaceNixConfiguration(PlaceNixConfiguration),
|
||||||
SetupDefaultProfile(SetupDefaultProfile),
|
SetupDefaultProfile(SetupDefaultProfile),
|
||||||
StartNixDaemon(StartNixDaemon),
|
StartNixDaemon(StartNixDaemon),
|
||||||
StartSystemdService(StartSystemdService),
|
|
||||||
ProvisionNix(ProvisionNix),
|
ProvisionNix(ProvisionNix),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,8 +83,9 @@ pub enum ActionReceipt {
|
||||||
ConfigureNix(ConfigureNixReceipt),
|
ConfigureNix(ConfigureNixReceipt),
|
||||||
ConfigureShellProfile(ConfigureShellProfileReceipt),
|
ConfigureShellProfile(ConfigureShellProfileReceipt),
|
||||||
CreateDirectory(CreateDirectoryReceipt),
|
CreateDirectory(CreateDirectoryReceipt),
|
||||||
|
CreateFile(CreateFileReceipt),
|
||||||
CreateGroup(CreateGroupReceipt),
|
CreateGroup(CreateGroupReceipt),
|
||||||
CreateNixTreeDirs(CreateNixTreeDirsReceipt),
|
CreateOrAppendFile(CreateOrAppendFileReceipt),
|
||||||
CreateNixTree(CreateNixTreeReceipt),
|
CreateNixTree(CreateNixTreeReceipt),
|
||||||
CreateUser(CreateUserReceipt),
|
CreateUser(CreateUserReceipt),
|
||||||
CreateUsersAndGroup(CreateUsersAndGroupReceipt),
|
CreateUsersAndGroup(CreateUsersAndGroupReceipt),
|
||||||
|
@ -94,7 +95,6 @@ pub enum ActionReceipt {
|
||||||
PlaceNixConfiguration(PlaceNixConfigurationReceipt),
|
PlaceNixConfiguration(PlaceNixConfigurationReceipt),
|
||||||
SetupDefaultProfile(SetupDefaultProfileReceipt),
|
SetupDefaultProfile(SetupDefaultProfileReceipt),
|
||||||
StartNixDaemon(StartNixDaemonReceipt),
|
StartNixDaemon(StartNixDaemonReceipt),
|
||||||
StartSystemdService(StartSystemdServiceReceipt),
|
|
||||||
ProvisionNix(ProvisionNixReceipt),
|
ProvisionNix(ProvisionNixReceipt),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,8 +107,9 @@ impl<'a> Actionable<'a> for Action {
|
||||||
Action::ConfigureNix(i) => i.description(),
|
Action::ConfigureNix(i) => i.description(),
|
||||||
Action::ConfigureShellProfile(i) => i.description(),
|
Action::ConfigureShellProfile(i) => i.description(),
|
||||||
Action::CreateDirectory(i) => i.description(),
|
Action::CreateDirectory(i) => i.description(),
|
||||||
|
Action::CreateFile(i) => i.description(),
|
||||||
Action::CreateGroup(i) => i.description(),
|
Action::CreateGroup(i) => i.description(),
|
||||||
Action::CreateNixTreeDirs(i) => i.description(),
|
Action::CreateOrAppendFile(i) => i.description(),
|
||||||
Action::CreateNixTree(i) => i.description(),
|
Action::CreateNixTree(i) => i.description(),
|
||||||
Action::CreateUser(i) => i.description(),
|
Action::CreateUser(i) => i.description(),
|
||||||
Action::CreateUsersAndGroup(i) => i.description(),
|
Action::CreateUsersAndGroup(i) => i.description(),
|
||||||
|
@ -118,7 +119,6 @@ impl<'a> Actionable<'a> for Action {
|
||||||
Action::PlaceNixConfiguration(i) => i.description(),
|
Action::PlaceNixConfiguration(i) => i.description(),
|
||||||
Action::SetupDefaultProfile(i) => i.description(),
|
Action::SetupDefaultProfile(i) => i.description(),
|
||||||
Action::StartNixDaemon(i) => i.description(),
|
Action::StartNixDaemon(i) => i.description(),
|
||||||
Action::StartSystemdService(i) => i.description(),
|
|
||||||
Action::ProvisionNix(i) => i.description(),
|
Action::ProvisionNix(i) => i.description(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,8 +129,9 @@ impl<'a> Actionable<'a> for Action {
|
||||||
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::CreateGroup(i) => i.execute().await.map(ActionReceipt::CreateGroup),
|
Action::CreateGroup(i) => i.execute().await.map(ActionReceipt::CreateGroup),
|
||||||
Action::CreateNixTreeDirs(i) => i.execute().await.map(ActionReceipt::CreateNixTreeDirs),
|
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),
|
||||||
|
@ -140,7 +141,6 @@ impl<'a> Actionable<'a> for Action {
|
||||||
Action::PlaceNixConfiguration(i) => i.execute().await.map(ActionReceipt::PlaceNixConfiguration),
|
Action::PlaceNixConfiguration(i) => i.execute().await.map(ActionReceipt::PlaceNixConfiguration),
|
||||||
Action::SetupDefaultProfile(i) => i.execute().await.map(ActionReceipt::SetupDefaultProfile),
|
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::StartSystemdService(i) => i.execute().await.map(ActionReceipt::StartSystemdService),
|
|
||||||
Action::ProvisionNix(i) => i.execute().await.map(ActionReceipt::ProvisionNix),
|
Action::ProvisionNix(i) => i.execute().await.map(ActionReceipt::ProvisionNix),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,8 +154,9 @@ impl<'a> Revertable<'a> for ActionReceipt {
|
||||||
ActionReceipt::ConfigureNix(i) => i.description(),
|
ActionReceipt::ConfigureNix(i) => i.description(),
|
||||||
ActionReceipt::ConfigureShellProfile(i) => i.description(),
|
ActionReceipt::ConfigureShellProfile(i) => i.description(),
|
||||||
ActionReceipt::CreateDirectory(i) => i.description(),
|
ActionReceipt::CreateDirectory(i) => i.description(),
|
||||||
|
ActionReceipt::CreateFile(i) => i.description(),
|
||||||
ActionReceipt::CreateGroup(i) => i.description(),
|
ActionReceipt::CreateGroup(i) => i.description(),
|
||||||
ActionReceipt::CreateNixTreeDirs(i) => i.description(),
|
ActionReceipt::CreateOrAppendFile(i) => i.description(),
|
||||||
ActionReceipt::CreateNixTree(i) => i.description(),
|
ActionReceipt::CreateNixTree(i) => i.description(),
|
||||||
ActionReceipt::CreateUser(i) => i.description(),
|
ActionReceipt::CreateUser(i) => i.description(),
|
||||||
ActionReceipt::CreateUsersAndGroup(i) => i.description(),
|
ActionReceipt::CreateUsersAndGroup(i) => i.description(),
|
||||||
|
@ -165,7 +166,6 @@ impl<'a> Revertable<'a> for ActionReceipt {
|
||||||
ActionReceipt::PlaceNixConfiguration(i) => i.description(),
|
ActionReceipt::PlaceNixConfiguration(i) => i.description(),
|
||||||
ActionReceipt::SetupDefaultProfile(i) => i.description(),
|
ActionReceipt::SetupDefaultProfile(i) => i.description(),
|
||||||
ActionReceipt::StartNixDaemon(i) => i.description(),
|
ActionReceipt::StartNixDaemon(i) => i.description(),
|
||||||
ActionReceipt::StartSystemdService(i) => i.description(),
|
|
||||||
ActionReceipt::ProvisionNix(i) => i.description(),
|
ActionReceipt::ProvisionNix(i) => i.description(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,8 +176,9 @@ impl<'a> Revertable<'a> for ActionReceipt {
|
||||||
ActionReceipt::ConfigureNix(i) => i.revert().await,
|
ActionReceipt::ConfigureNix(i) => i.revert().await,
|
||||||
ActionReceipt::ConfigureShellProfile(i) => i.revert().await,
|
ActionReceipt::ConfigureShellProfile(i) => i.revert().await,
|
||||||
ActionReceipt::CreateDirectory(i) => i.revert().await,
|
ActionReceipt::CreateDirectory(i) => i.revert().await,
|
||||||
|
ActionReceipt::CreateFile(i) => i.revert().await,
|
||||||
ActionReceipt::CreateGroup(i) => i.revert().await,
|
ActionReceipt::CreateGroup(i) => i.revert().await,
|
||||||
ActionReceipt::CreateNixTreeDirs(i) => i.revert().await,
|
ActionReceipt::CreateOrAppendFile(i) => i.revert().await,
|
||||||
ActionReceipt::CreateNixTree(i) => i.revert().await,
|
ActionReceipt::CreateNixTree(i) => i.revert().await,
|
||||||
ActionReceipt::CreateUser(i) => i.revert().await,
|
ActionReceipt::CreateUser(i) => i.revert().await,
|
||||||
ActionReceipt::CreateUsersAndGroup(i) => i.revert().await,
|
ActionReceipt::CreateUsersAndGroup(i) => i.revert().await,
|
||||||
|
@ -187,7 +188,6 @@ impl<'a> Revertable<'a> for ActionReceipt {
|
||||||
ActionReceipt::PlaceNixConfiguration(i) => i.revert().await,
|
ActionReceipt::PlaceNixConfiguration(i) => i.revert().await,
|
||||||
ActionReceipt::SetupDefaultProfile(i) => i.revert().await,
|
ActionReceipt::SetupDefaultProfile(i) => i.revert().await,
|
||||||
ActionReceipt::StartNixDaemon(i) => i.revert().await,
|
ActionReceipt::StartNixDaemon(i) => i.revert().await,
|
||||||
ActionReceipt::StartSystemdService(i) => i.revert().await,
|
|
||||||
ActionReceipt::ProvisionNix(i) => i.revert().await,
|
ActionReceipt::ProvisionNix(i) => i.revert().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,13 @@ pub(crate) struct HarmonicCli {
|
||||||
global = true
|
global = true
|
||||||
)]
|
)]
|
||||||
pub(crate) explain: bool,
|
pub(crate) explain: bool,
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
action(ArgAction::SetTrue),
|
||||||
|
default_value = "false",
|
||||||
|
global = true
|
||||||
|
)]
|
||||||
|
pub(crate) force: bool,
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
subcommand: Option<HarmonicSubcommand>,
|
subcommand: Option<HarmonicSubcommand>,
|
||||||
}
|
}
|
||||||
|
@ -66,6 +73,7 @@ impl CommandExecute for HarmonicCli {
|
||||||
no_modify_profile,
|
no_modify_profile,
|
||||||
explain,
|
explain,
|
||||||
subcommand,
|
subcommand,
|
||||||
|
force,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
match subcommand {
|
match subcommand {
|
||||||
|
@ -74,6 +82,7 @@ impl CommandExecute for HarmonicCli {
|
||||||
None => {
|
None => {
|
||||||
let mut settings = InstallSettings::default();
|
let mut settings = InstallSettings::default();
|
||||||
|
|
||||||
|
settings.force(force);
|
||||||
settings.explain(explain);
|
settings.explain(explain);
|
||||||
settings.daemon_user_count(daemon_user_count);
|
settings.daemon_user_count(daemon_user_count);
|
||||||
settings.channels(
|
settings.channels(
|
||||||
|
|
|
@ -29,6 +29,20 @@ pub(crate) struct Plan {
|
||||||
/// Number of build users to create
|
/// Number of build users to create
|
||||||
#[clap(long, default_value = "32", env = "HARMONIC_NIX_DAEMON_USER_COUNT")]
|
#[clap(long, default_value = "32", env = "HARMONIC_NIX_DAEMON_USER_COUNT")]
|
||||||
pub(crate) daemon_user_count: usize,
|
pub(crate) daemon_user_count: usize,
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
action(ArgAction::SetTrue),
|
||||||
|
default_value = "false",
|
||||||
|
global = true
|
||||||
|
)]
|
||||||
|
pub(crate) explain: bool,
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
action(ArgAction::SetTrue),
|
||||||
|
default_value = "false",
|
||||||
|
global = true
|
||||||
|
)]
|
||||||
|
pub(crate) force: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
@ -43,10 +57,14 @@ impl CommandExecute for Plan {
|
||||||
channel,
|
channel,
|
||||||
no_modify_profile,
|
no_modify_profile,
|
||||||
daemon_user_count,
|
daemon_user_count,
|
||||||
|
explain,
|
||||||
|
force,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let mut settings = InstallSettings::default();
|
let mut settings = InstallSettings::default();
|
||||||
|
|
||||||
|
settings.force(force);
|
||||||
|
settings.explain(explain);
|
||||||
settings.daemon_user_count(daemon_user_count);
|
settings.daemon_user_count(daemon_user_count);
|
||||||
settings.channels(
|
settings.channels(
|
||||||
channel
|
channel
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub enum HarmonicError {
|
||||||
NoNssCacert,
|
NoNssCacert,
|
||||||
#[error("No supported init system found")]
|
#[error("No supported init system found")]
|
||||||
InitNotSupported,
|
InitNotSupported,
|
||||||
#[error("Creating directory `{0}`")]
|
#[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}`")]
|
||||||
WalkDirectory(std::path::PathBuf, walkdir::Error),
|
WalkDirectory(std::path::PathBuf, walkdir::Error),
|
||||||
|
|
|
@ -83,9 +83,9 @@ impl InstallPlan {
|
||||||
}
|
}
|
||||||
pub async fn new(settings: InstallSettings) -> Result<Self, HarmonicError> {
|
pub async fn new(settings: InstallSettings) -> Result<Self, HarmonicError> {
|
||||||
let actions = vec![
|
let actions = vec![
|
||||||
Action::ProvisionNix(ProvisionNix::plan(settings.clone())?),
|
Action::ProvisionNix(ProvisionNix::plan(settings.clone()).await?),
|
||||||
Action::ConfigureNix(ConfigureNix::plan(settings.clone())),
|
Action::ConfigureNix(ConfigureNix::plan(settings.clone()).await?),
|
||||||
Action::StartNixDaemon(StartNixDaemon::plan(settings.clone())),
|
Action::StartNixDaemon(StartNixDaemon::plan(settings.clone()).await?),
|
||||||
];
|
];
|
||||||
Ok(Self { settings, actions })
|
Ok(Self { settings, actions })
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ pub struct InstallSettings {
|
||||||
pub(crate) nix_build_user_prefix: String,
|
pub(crate) nix_build_user_prefix: String,
|
||||||
pub(crate) nix_build_user_id_base: usize,
|
pub(crate) nix_build_user_id_base: usize,
|
||||||
pub(crate) nix_package_url: Url,
|
pub(crate) nix_package_url: Url,
|
||||||
|
pub(crate) extra_conf: Option<String>,
|
||||||
|
pub(crate) force: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for InstallSettings {
|
impl Default for InstallSettings {
|
||||||
|
@ -24,7 +26,9 @@ 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(),
|
||||||
|
force: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,4 +77,12 @@ impl InstallSettings {
|
||||||
self.nix_package_url = url;
|
self.nix_package_url = url;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
pub fn extra_conf(&mut self, extra_conf: Option<String>) -> &mut Self {
|
||||||
|
self.extra_conf = extra_conf;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn force(&mut self, force: bool) -> &mut Self {
|
||||||
|
self.force = force;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue