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};
|
||||
|
||||
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 {
|
||||
pub fn plan() -> Self {
|
||||
Self {}
|
||||
pub async fn plan() -> Result<Self, HarmonicError> {
|
||||
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> {
|
||||
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::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)]
|
||||
pub struct ConfigureShellProfile {}
|
||||
pub struct ConfigureShellProfile {
|
||||
create_or_append_files: Vec<CreateOrAppendFile>,
|
||||
}
|
||||
|
||||
impl ConfigureShellProfile {
|
||||
pub fn plan() -> Self {
|
||||
Self {}
|
||||
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);
|
||||
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> {
|
||||
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)]
|
||||
pub struct ConfigureShellProfileReceipt {}
|
||||
pub struct ConfigureShellProfileReceipt {
|
||||
create_or_append_files: Vec<CreateOrAppendFileReceipt>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> Revertable<'a> for ConfigureShellProfileReceipt {
|
||||
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()
|
||||
]
|
||||
),
|
||||
]
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
|
|
|
@ -3,6 +3,9 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use nix::unistd::{Group, User, Gid, Uid, chown};
|
||||
use tokio::fs::create_dir;
|
||||
|
||||
use crate::HarmonicError;
|
||||
|
||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
|
@ -16,14 +19,26 @@ pub struct CreateDirectory {
|
|||
}
|
||||
|
||||
impl CreateDirectory {
|
||||
pub fn plan(path: impl AsRef<Path>, user: String, group: String, mode: u32) -> Self {
|
||||
let path = path.as_ref().to_path_buf();
|
||||
Self {
|
||||
path,
|
||||
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()))))
|
||||
}
|
||||
// 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,
|
||||
group,
|
||||
mode,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,10 +46,11 @@ impl CreateDirectory {
|
|||
impl<'a> Actionable<'a> for CreateDirectory {
|
||||
type Receipt = CreateDirectoryReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
let Self { path, user, group, mode } = &self;
|
||||
vec![ActionDescription::new(
|
||||
format!("Create the directory `/nix`"),
|
||||
format!("Create the directory `{}`", path.display()),
|
||||
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,
|
||||
mode,
|
||||
} = 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 {
|
||||
path,
|
||||
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::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
|
@ -5,12 +7,14 @@ use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
|||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateGroup {
|
||||
name: String,
|
||||
uid: usize,
|
||||
gid: usize,
|
||||
}
|
||||
|
||||
impl CreateGroup {
|
||||
pub fn plan(name: String, uid: usize) -> Self {
|
||||
Self { name, uid }
|
||||
pub fn plan(name: String, gid: usize) -> Self {
|
||||
|
||||
|
||||
Self { name, gid }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,10 +22,9 @@ impl CreateGroup {
|
|||
impl<'a> Actionable<'a> for CreateGroup {
|
||||
type Receipt = CreateGroupReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
let name = &self.name;
|
||||
let uid = &self.uid;
|
||||
let Self { name, gid } = &self;
|
||||
vec![ActionDescription::new(
|
||||
format!("Create group {name} with UID {uid}"),
|
||||
format!("Create group {name} with GID {gid}"),
|
||||
vec![format!(
|
||||
"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> {
|
||||
let Self { name, uid } = self;
|
||||
Ok(CreateGroupReceipt { name, uid })
|
||||
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)),
|
||||
}
|
||||
|
||||
Ok(CreateGroupReceipt { name, gid })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateGroupReceipt {
|
||||
name: String,
|
||||
uid: usize,
|
||||
gid: usize,
|
||||
}
|
||||
|
||||
#[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::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
|
@ -6,11 +8,12 @@ use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
|||
pub struct CreateUser {
|
||||
name: String,
|
||||
uid: usize,
|
||||
gid: usize,
|
||||
}
|
||||
|
||||
impl CreateUser {
|
||||
pub fn plan(name: String, uid: usize) -> Self {
|
||||
Self { name, uid }
|
||||
pub fn plan(name: String, uid: usize, gid: usize) -> Self {
|
||||
Self { name, uid, gid }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,8 +32,41 @@ impl<'a> Actionable<'a> for CreateUser {
|
|||
}
|
||||
|
||||
async fn execute(self) -> Result<Self::Receipt, HarmonicError> {
|
||||
let Self { name, uid } = self;
|
||||
Ok(CreateUserReceipt { name, uid })
|
||||
let Self { name, uid, gid } = self;
|
||||
|
||||
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 {
|
||||
name: String,
|
||||
uid: usize,
|
||||
gid: usize,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use bytes::Buf;
|
||||
use reqwest::Url;
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
use crate::HarmonicError;
|
||||
|
||||
|
@ -13,8 +15,11 @@ pub struct FetchNix {
|
|||
}
|
||||
|
||||
impl FetchNix {
|
||||
pub fn plan(url: Url, destination: PathBuf) -> Self {
|
||||
Self { url, destination }
|
||||
pub async fn plan(url: Url, destination: PathBuf) -> Result<Self, HarmonicError> {
|
||||
// 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> {
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,21 +3,25 @@
|
|||
mod configure_nix_daemon_service;
|
||||
mod configure_shell_profile;
|
||||
mod create_directory;
|
||||
mod create_file;
|
||||
mod create_group;
|
||||
mod create_or_append_file;
|
||||
mod create_user;
|
||||
mod fetch_nix;
|
||||
mod move_unpacked_nix;
|
||||
mod place_channel_configuration;
|
||||
mod place_nix_configuration;
|
||||
mod setup_default_profile;
|
||||
mod start_systemd_service;
|
||||
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};
|
||||
pub use create_or_append_file::{CreateOrAppendFile, CreateOrAppendFileReceipt};
|
||||
pub use create_user::{CreateUser, CreateUserReceipt};
|
||||
pub use fetch_nix::{FetchNix, FetchNixReceipt};
|
||||
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 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;
|
||||
|
||||
|
@ -10,8 +10,9 @@ pub struct MoveUnpackedNix {
|
|||
}
|
||||
|
||||
impl MoveUnpackedNix {
|
||||
pub fn plan(source: PathBuf) -> Self {
|
||||
Self { source, }
|
||||
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, })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +31,23 @@ impl<'a> Actionable<'a> for MoveUnpackedNix {
|
|||
|
||||
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<_>, _>>()?;
|
||||
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 { })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,30 @@
|
|||
use std::path::Path;
|
||||
|
||||
use reqwest::Url;
|
||||
|
||||
use crate::HarmonicError;
|
||||
|
||||
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)]
|
||||
pub struct PlaceChannelConfiguration {}
|
||||
pub struct PlaceChannelConfiguration {
|
||||
channels: Vec<(String, Url)>,
|
||||
create_file: CreateFile,
|
||||
}
|
||||
|
||||
impl PlaceChannelConfiguration {
|
||||
pub fn plan() -> Self {
|
||||
Self {}
|
||||
pub async fn plan(channels: Vec<(String, Url)>) -> 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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,35 +32,34 @@ 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(
|
||||
"Start the systemd Nix daemon".to_string(),
|
||||
"Place a channel configuration".to_string(),
|
||||
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> {
|
||||
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)]
|
||||
pub struct PlaceChannelConfigurationReceipt {}
|
||||
pub struct PlaceChannelConfigurationReceipt {
|
||||
channels: Vec<(String, Url)>,
|
||||
create_file: CreateFileReceipt,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> Revertable<'a> for PlaceChannelConfigurationReceipt {
|
||||
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()
|
||||
]
|
||||
),
|
||||
]
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
|
|
|
@ -2,12 +2,26 @@ use crate::HarmonicError;
|
|||
|
||||
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)]
|
||||
pub struct PlaceNixConfiguration {}
|
||||
pub struct PlaceNixConfiguration {
|
||||
create_file: CreateFile,
|
||||
}
|
||||
|
||||
impl PlaceNixConfiguration {
|
||||
pub fn plan() -> Self {
|
||||
Self {}
|
||||
pub async fn plan(nix_build_group_name: String, extra_conf: Option<String>) -> Result<Self, HarmonicError> {
|
||||
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> {
|
||||
vec![
|
||||
ActionDescription::new(
|
||||
"Start the systemd Nix daemon".to_string(),
|
||||
"Place the nix configuration".to_string(),
|
||||
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> {
|
||||
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)]
|
||||
pub struct PlaceNixConfigurationReceipt {}
|
||||
pub struct PlaceNixConfigurationReceipt {
|
||||
create_file: CreateFileReceipt,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> Revertable<'a> for PlaceNixConfigurationReceipt {
|
||||
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()
|
||||
]
|
||||
),
|
||||
]
|
||||
todo!()
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct SetupDefaultProfile {}
|
||||
pub struct SetupDefaultProfile {
|
||||
channels: Vec<String>,
|
||||
}
|
||||
|
||||
impl SetupDefaultProfile {
|
||||
pub fn plan() -> Self {
|
||||
Self {}
|
||||
pub async fn plan(channels: Vec<String>) -> Result<Self, HarmonicError> {
|
||||
Ok(Self { channels })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +31,85 @@ impl<'a> Actionable<'a> for SetupDefaultProfile {
|
|||
}
|
||||
|
||||
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 crate::actions::base::{SetupDefaultProfile, ConfigureNixDaemonService, ConfigureShellProfile, SetupDefaultProfileReceipt, ConfigureNixDaemonServiceReceipt, 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};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct ConfigureNix {
|
||||
setup_default_profile: SetupDefaultProfile,
|
||||
configure_nix_daemon_service: ConfigureNixDaemonService,
|
||||
configure_shell_profile: Option<ConfigureShellProfile>,
|
||||
place_nix_configuration: PlaceNixConfiguration,
|
||||
configure_nix_daemon_service: ConfigureNixDaemonService,
|
||||
}
|
||||
|
||||
impl ConfigureNix {
|
||||
pub fn plan(settings: InstallSettings) -> Self {
|
||||
let setup_default_profile = SetupDefaultProfile::plan();
|
||||
let configure_nix_daemon_service = ConfigureNixDaemonService::plan();
|
||||
pub async fn plan(settings: InstallSettings) -> Result<Self, HarmonicError> {
|
||||
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 {
|
||||
Some(ConfigureShellProfile::plan())
|
||||
Some(ConfigureShellProfile::plan().await?)
|
||||
} else {
|
||||
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 {
|
||||
type Receipt = ConfigureNixReceipt;
|
||||
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();
|
||||
buf.append(&mut configure_nix_daemon_service.description());
|
||||
buf.append(&mut place_nix_configuration.description());
|
||||
if let Some(configure_shell_profile) = configure_shell_profile {
|
||||
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> {
|
||||
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!(
|
||||
setup_default_profile.execute(),
|
||||
configure_nix_daemon_service.execute(),
|
||||
configure_shell_profile.execute(),
|
||||
place_nix_configuration.execute(),
|
||||
)?;
|
||||
(a, b, Some(c))
|
||||
} else {
|
||||
let (a, b) = tokio::try_join!(
|
||||
setup_default_profile.execute(),
|
||||
configure_nix_daemon_service.execute(),
|
||||
)?;
|
||||
(a, b, None)
|
||||
(a, b, c, None)
|
||||
};
|
||||
|
||||
Ok(Self::Receipt {
|
||||
setup_default_profile,
|
||||
configure_nix_daemon_service,
|
||||
place_nix_configuration,
|
||||
configure_shell_profile,
|
||||
})
|
||||
|
||||
|
@ -73,8 +81,9 @@ impl<'a> Actionable<'a> for ConfigureNix {
|
|||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct ConfigureNixReceipt {
|
||||
setup_default_profile: SetupDefaultProfileReceipt,
|
||||
configure_nix_daemon_service: ConfigureNixDaemonServiceReceipt,
|
||||
configure_shell_profile: Option<ConfigureShellProfileReceipt>,
|
||||
place_nix_configuration: PlaceNixConfigurationReceipt,
|
||||
configure_nix_daemon_service: ConfigureNixDaemonServiceReceipt,
|
||||
}
|
||||
|
||||
#[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};
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateNixTree {
|
||||
settings: InstallSettings,
|
||||
create_directories: Vec<CreateDirectory>,
|
||||
}
|
||||
|
||||
impl CreateNixTree {
|
||||
pub fn plan(settings: InstallSettings) -> Self {
|
||||
Self { settings }
|
||||
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 {
|
||||
// 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> {
|
||||
let Self { settings: _ } = self;
|
||||
Ok(CreateNixTreeReceipt {})
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct CreateNixTreeReceipt {}
|
||||
pub struct CreateNixTreeReceipt {
|
||||
create_directories: Vec<CreateDirectoryReceipt>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
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)]
|
||||
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_users: Vec<CreateUser>,
|
||||
}
|
||||
|
||||
impl CreateUsersAndGroup {
|
||||
pub fn plan(
|
||||
pub async fn plan(
|
||||
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);
|
||||
// TODO(@hoverbear): CHeck if they exist, error if so
|
||||
let create_users = (0..settings.daemon_user_count)
|
||||
.map(|count| {
|
||||
CreateUser::plan(
|
||||
format!("{}{count}", settings.nix_build_user_prefix),
|
||||
settings.nix_build_user_id_base + count,
|
||||
settings.nix_build_group_id,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
Self {
|
||||
settings,
|
||||
Ok(Self {
|
||||
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_users,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,14 +49,11 @@ impl<'a> Actionable<'a> for CreateUsersAndGroup {
|
|||
type Receipt = CreateUsersAndGroupReceipt;
|
||||
fn description(&self) -> Vec<ActionDescription> {
|
||||
let Self {
|
||||
settings: InstallSettings {
|
||||
daemon_user_count,
|
||||
nix_build_group_name,
|
||||
nix_build_group_id,
|
||||
nix_build_user_prefix,
|
||||
nix_build_user_id_base,
|
||||
..
|
||||
},
|
||||
daemon_user_count,
|
||||
nix_build_group_name,
|
||||
nix_build_group_id,
|
||||
nix_build_user_prefix,
|
||||
nix_build_user_id_base,
|
||||
..
|
||||
} = &self;
|
||||
|
||||
|
@ -62,7 +70,7 @@ impl<'a> Actionable<'a> for CreateUsersAndGroup {
|
|||
}
|
||||
|
||||
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
|
||||
let create_group = create_group.execute().await?;
|
||||
|
@ -87,19 +95,6 @@ impl<'a> Actionable<'a> for CreateUsersAndGroup {
|
|||
}
|
||||
|
||||
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 {
|
||||
return Err(errors.into_iter().next().unwrap());
|
||||
} else {
|
||||
|
@ -107,7 +102,7 @@ impl<'a> Actionable<'a> for CreateUsersAndGroup {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(CreateUsersAndGroupReceipt {
|
||||
Ok(Self::Receipt {
|
||||
create_group,
|
||||
create_users: successes,
|
||||
})
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
/*! Actions which only call other base plugins. */
|
||||
|
||||
mod create_nix_tree;
|
||||
mod create_nix_tree_dirs;
|
||||
mod create_users_and_group;
|
||||
mod configure_nix;
|
||||
mod start_nix_daemon;
|
||||
mod provision_nix;
|
||||
|
||||
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 configure_nix::{ConfigureNix, ConfigureNixReceipt};
|
||||
pub use start_nix_daemon::{StartNixDaemon, StartNixDaemonReceipt};
|
||||
|
|
|
@ -16,13 +16,13 @@ pub struct 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 fetch_nix = FetchNix::plan(settings.nix_package_url.clone(), tempdir.path().to_path_buf());
|
||||
let create_users_and_group = CreateUsersAndGroup::plan(settings.clone());
|
||||
let create_nix_tree = CreateNixTree::plan(settings.clone());
|
||||
let move_unpacked_nix = MoveUnpackedNix::plan(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()).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 })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
use crate::actions::base::{ConfigureNixDaemonServiceReceipt, ConfigureNixDaemonService, StartSystemdUnit, StartSystemdUnitReceipt};
|
||||
use crate::{HarmonicError, InstallSettings};
|
||||
|
||||
use crate::actions::{ActionDescription, ActionReceipt, Actionable, Revertable};
|
||||
|
||||
/// This is mostly indirection for supporting non-systemd
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct StartNixDaemon {}
|
||||
pub struct StartNixDaemon {
|
||||
start_systemd_socket: StartSystemdUnit,
|
||||
start_systemd_service: StartSystemdUnit,
|
||||
}
|
||||
|
||||
impl StartNixDaemon {
|
||||
pub fn plan(settings: InstallSettings) -> Self {
|
||||
Self {}
|
||||
pub async fn plan(settings: InstallSettings) -> 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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,35 +22,28 @@ impl StartNixDaemon {
|
|||
impl<'a> Actionable<'a> for StartNixDaemon {
|
||||
type Receipt = StartNixDaemonReceipt;
|
||||
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()
|
||||
]
|
||||
),
|
||||
]
|
||||
let Self { start_systemd_socket, start_systemd_service } = &self;
|
||||
start_systemd_service.description()
|
||||
}
|
||||
|
||||
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)]
|
||||
pub struct StartNixDaemonReceipt {}
|
||||
pub struct StartNixDaemonReceipt {
|
||||
start_systemd_socket: StartSystemdUnitReceipt,
|
||||
start_systemd_service: StartSystemdUnitReceipt,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<'a> Revertable<'a> for StartNixDaemonReceipt {
|
||||
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()
|
||||
]
|
||||
),
|
||||
]
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn revert(self) -> Result<(), HarmonicError> {
|
||||
|
|
|
@ -5,19 +5,19 @@ use base::{
|
|||
ConfigureNixDaemonService, ConfigureNixDaemonServiceReceipt,
|
||||
ConfigureShellProfile, ConfigureShellProfileReceipt,
|
||||
CreateDirectory, CreateDirectoryReceipt,
|
||||
CreateFile, CreateFileReceipt,
|
||||
CreateGroup, CreateGroupReceipt,
|
||||
CreateOrAppendFile, CreateOrAppendFileReceipt,
|
||||
CreateUser, CreateUserReceipt,
|
||||
FetchNix, FetchNixReceipt,
|
||||
MoveUnpackedNix, MoveUnpackedNixReceipt,
|
||||
PlaceChannelConfiguration, PlaceChannelConfigurationReceipt,
|
||||
PlaceNixConfiguration, PlaceNixConfigurationReceipt,
|
||||
SetupDefaultProfile, SetupDefaultProfileReceipt,
|
||||
StartSystemdService, StartSystemdServiceReceipt,
|
||||
};
|
||||
use meta::{
|
||||
ConfigureNix, ConfigureNixReceipt,
|
||||
CreateNixTree, CreateNixTreeReceipt,
|
||||
CreateNixTreeDirs, CreateNixTreeDirsReceipt,
|
||||
CreateUsersAndGroup, CreateUsersAndGroupReceipt,
|
||||
StartNixDaemon, StartNixDaemonReceipt,
|
||||
};
|
||||
|
@ -62,8 +62,9 @@ pub enum Action {
|
|||
ConfigureNix(ConfigureNix),
|
||||
ConfigureShellProfile(ConfigureShellProfile),
|
||||
CreateDirectory(CreateDirectory),
|
||||
CreateFile(CreateFile),
|
||||
CreateGroup(CreateGroup),
|
||||
CreateNixTreeDirs(CreateNixTreeDirs),
|
||||
CreateOrAppendFile(CreateOrAppendFile),
|
||||
CreateNixTree(CreateNixTree),
|
||||
CreateUser(CreateUser),
|
||||
CreateUsersAndGroup(CreateUsersAndGroup),
|
||||
|
@ -73,7 +74,6 @@ pub enum Action {
|
|||
PlaceNixConfiguration(PlaceNixConfiguration),
|
||||
SetupDefaultProfile(SetupDefaultProfile),
|
||||
StartNixDaemon(StartNixDaemon),
|
||||
StartSystemdService(StartSystemdService),
|
||||
ProvisionNix(ProvisionNix),
|
||||
}
|
||||
|
||||
|
@ -83,8 +83,9 @@ pub enum ActionReceipt {
|
|||
ConfigureNix(ConfigureNixReceipt),
|
||||
ConfigureShellProfile(ConfigureShellProfileReceipt),
|
||||
CreateDirectory(CreateDirectoryReceipt),
|
||||
CreateFile(CreateFileReceipt),
|
||||
CreateGroup(CreateGroupReceipt),
|
||||
CreateNixTreeDirs(CreateNixTreeDirsReceipt),
|
||||
CreateOrAppendFile(CreateOrAppendFileReceipt),
|
||||
CreateNixTree(CreateNixTreeReceipt),
|
||||
CreateUser(CreateUserReceipt),
|
||||
CreateUsersAndGroup(CreateUsersAndGroupReceipt),
|
||||
|
@ -94,7 +95,6 @@ pub enum ActionReceipt {
|
|||
PlaceNixConfiguration(PlaceNixConfigurationReceipt),
|
||||
SetupDefaultProfile(SetupDefaultProfileReceipt),
|
||||
StartNixDaemon(StartNixDaemonReceipt),
|
||||
StartSystemdService(StartSystemdServiceReceipt),
|
||||
ProvisionNix(ProvisionNixReceipt),
|
||||
}
|
||||
|
||||
|
@ -107,8 +107,9 @@ impl<'a> Actionable<'a> for Action {
|
|||
Action::ConfigureNix(i) => i.description(),
|
||||
Action::ConfigureShellProfile(i) => i.description(),
|
||||
Action::CreateDirectory(i) => i.description(),
|
||||
Action::CreateFile(i) => i.description(),
|
||||
Action::CreateGroup(i) => i.description(),
|
||||
Action::CreateNixTreeDirs(i) => i.description(),
|
||||
Action::CreateOrAppendFile(i) => i.description(),
|
||||
Action::CreateNixTree(i) => i.description(),
|
||||
Action::CreateUser(i) => i.description(),
|
||||
Action::CreateUsersAndGroup(i) => i.description(),
|
||||
|
@ -118,7 +119,6 @@ impl<'a> Actionable<'a> for Action {
|
|||
Action::PlaceNixConfiguration(i) => i.description(),
|
||||
Action::SetupDefaultProfile(i) => i.description(),
|
||||
Action::StartNixDaemon(i) => i.description(),
|
||||
Action::StartSystemdService(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::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::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::CreateUser(i) => i.execute().await.map(ActionReceipt::CreateUser),
|
||||
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::SetupDefaultProfile(i) => i.execute().await.map(ActionReceipt::SetupDefaultProfile),
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -154,8 +154,9 @@ impl<'a> Revertable<'a> for ActionReceipt {
|
|||
ActionReceipt::ConfigureNix(i) => i.description(),
|
||||
ActionReceipt::ConfigureShellProfile(i) => i.description(),
|
||||
ActionReceipt::CreateDirectory(i) => i.description(),
|
||||
ActionReceipt::CreateFile(i) => i.description(),
|
||||
ActionReceipt::CreateGroup(i) => i.description(),
|
||||
ActionReceipt::CreateNixTreeDirs(i) => i.description(),
|
||||
ActionReceipt::CreateOrAppendFile(i) => i.description(),
|
||||
ActionReceipt::CreateNixTree(i) => i.description(),
|
||||
ActionReceipt::CreateUser(i) => i.description(),
|
||||
ActionReceipt::CreateUsersAndGroup(i) => i.description(),
|
||||
|
@ -165,7 +166,6 @@ impl<'a> Revertable<'a> for ActionReceipt {
|
|||
ActionReceipt::PlaceNixConfiguration(i) => i.description(),
|
||||
ActionReceipt::SetupDefaultProfile(i) => i.description(),
|
||||
ActionReceipt::StartNixDaemon(i) => i.description(),
|
||||
ActionReceipt::StartSystemdService(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::ConfigureShellProfile(i) => i.revert().await,
|
||||
ActionReceipt::CreateDirectory(i) => i.revert().await,
|
||||
ActionReceipt::CreateFile(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::CreateUser(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::SetupDefaultProfile(i) => i.revert().await,
|
||||
ActionReceipt::StartNixDaemon(i) => i.revert().await,
|
||||
ActionReceipt::StartSystemdService(i) => i.revert().await,
|
||||
ActionReceipt::ProvisionNix(i) => i.revert().await,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,13 @@ pub(crate) struct HarmonicCli {
|
|||
global = true
|
||||
)]
|
||||
pub(crate) explain: bool,
|
||||
#[clap(
|
||||
long,
|
||||
action(ArgAction::SetTrue),
|
||||
default_value = "false",
|
||||
global = true
|
||||
)]
|
||||
pub(crate) force: bool,
|
||||
#[clap(subcommand)]
|
||||
subcommand: Option<HarmonicSubcommand>,
|
||||
}
|
||||
|
@ -66,6 +73,7 @@ impl CommandExecute for HarmonicCli {
|
|||
no_modify_profile,
|
||||
explain,
|
||||
subcommand,
|
||||
force,
|
||||
} = self;
|
||||
|
||||
match subcommand {
|
||||
|
@ -74,6 +82,7 @@ impl CommandExecute for HarmonicCli {
|
|||
None => {
|
||||
let mut settings = InstallSettings::default();
|
||||
|
||||
settings.force(force);
|
||||
settings.explain(explain);
|
||||
settings.daemon_user_count(daemon_user_count);
|
||||
settings.channels(
|
||||
|
|
|
@ -29,6 +29,20 @@ pub(crate) struct Plan {
|
|||
/// Number of build users to create
|
||||
#[clap(long, default_value = "32", env = "HARMONIC_NIX_DAEMON_USER_COUNT")]
|
||||
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]
|
||||
|
@ -43,10 +57,14 @@ impl CommandExecute for Plan {
|
|||
channel,
|
||||
no_modify_profile,
|
||||
daemon_user_count,
|
||||
explain,
|
||||
force,
|
||||
} = self;
|
||||
|
||||
let mut settings = InstallSettings::default();
|
||||
|
||||
settings.force(force);
|
||||
settings.explain(explain);
|
||||
settings.daemon_user_count(daemon_user_count);
|
||||
settings.channels(
|
||||
channel
|
||||
|
|
|
@ -20,7 +20,7 @@ pub enum HarmonicError {
|
|||
NoNssCacert,
|
||||
#[error("No supported init system found")]
|
||||
InitNotSupported,
|
||||
#[error("Creating directory `{0}`")]
|
||||
#[error("Creating directory `{0}`: {1}")]
|
||||
CreateDirectory(std::path::PathBuf, std::io::Error),
|
||||
#[error("Walking directory `{0}`")]
|
||||
WalkDirectory(std::path::PathBuf, walkdir::Error),
|
||||
|
|
|
@ -83,9 +83,9 @@ impl InstallPlan {
|
|||
}
|
||||
pub async fn new(settings: InstallSettings) -> Result<Self, HarmonicError> {
|
||||
let actions = vec![
|
||||
Action::ProvisionNix(ProvisionNix::plan(settings.clone())?),
|
||||
Action::ConfigureNix(ConfigureNix::plan(settings.clone())),
|
||||
Action::StartNixDaemon(StartNixDaemon::plan(settings.clone())),
|
||||
Action::ProvisionNix(ProvisionNix::plan(settings.clone()).await?),
|
||||
Action::ConfigureNix(ConfigureNix::plan(settings.clone()).await?),
|
||||
Action::StartNixDaemon(StartNixDaemon::plan(settings.clone()).await?),
|
||||
];
|
||||
Ok(Self { settings, actions })
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ pub struct InstallSettings {
|
|||
pub(crate) nix_build_user_prefix: String,
|
||||
pub(crate) nix_build_user_id_base: usize,
|
||||
pub(crate) nix_package_url: Url,
|
||||
pub(crate) extra_conf: Option<String>,
|
||||
pub(crate) force: bool,
|
||||
}
|
||||
|
||||
impl Default for InstallSettings {
|
||||
|
@ -24,7 +26,9 @@ 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,4 +77,12 @@ impl InstallSettings {
|
|||
self.nix_package_url = url;
|
||||
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