Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Curing existing /nix #310

Merged
merged 7 commits into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/action/base/create_directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl CreateDirectory {
}

tracing::debug!("Creating directory `{}` already complete", path.display(),);
ActionState::Skipped
ActionState::Completed
} else {
ActionState::Uncompleted
};
Expand Down
2 changes: 2 additions & 0 deletions src/action/base/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub(crate) mod create_or_merge_nix_config;
pub(crate) mod create_user;
pub(crate) mod fetch_and_unpack_nix;
pub(crate) mod move_unpacked_nix;
pub(crate) mod remove_directory;
pub(crate) mod setup_default_profile;

pub use add_user_to_group::AddUserToGroup;
Expand All @@ -20,4 +21,5 @@ pub use create_or_merge_nix_config::CreateOrMergeNixConfig;
pub use create_user::CreateUser;
pub use fetch_and_unpack_nix::{FetchAndUnpackNix, FetchUrlError};
pub use move_unpacked_nix::{MoveUnpackedNix, MoveUnpackedNixError};
pub use remove_directory::RemoveDirectory;
pub use setup_default_profile::{SetupDefaultProfile, SetupDefaultProfileError};
68 changes: 40 additions & 28 deletions src/action/base/move_unpacked_nix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ Move an unpacked Nix at `src` to `/nix`
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct MoveUnpackedNix {
src: PathBuf,
unpacked_path: PathBuf,
}

impl MoveUnpackedNix {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(src: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
pub async fn plan(unpacked_path: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
// Note: Do NOT try to check for the src/dest since the installer creates those
Ok(Self { src }.into())
Ok(Self { unpacked_path }.into())
}
}

Expand All @@ -36,7 +36,7 @@ impl Action for MoveUnpackedNix {
span!(
tracing::Level::DEBUG,
"mount_unpacked_nix",
src = tracing::field::display(self.src.display()),
src = tracing::field::display(self.unpacked_path.display()),
dest = DEST,
)
}
Expand All @@ -46,44 +46,56 @@ impl Action for MoveUnpackedNix {
format!("Move the downloaded Nix into `/nix`"),
vec![format!(
"Nix is being downloaded to `{}` and should be in `/nix`",
self.src.display(),
self.unpacked_path.display(),
)],
)]
}

#[tracing::instrument(level = "debug", skip_all)]
async fn execute(&mut self) -> Result<(), ActionError> {
let Self { src } = self;
let Self { unpacked_path } = self;

// TODO(@Hoverbear): I would like to make this less awful
let found_nix_paths = glob::glob(&format!("{}/nix-*", src.display()))
// This is the `nix-$VERSION` folder which unpacks from the tarball, not a nix derivation
let found_nix_paths = glob::glob(&format!("{}/nix-*", unpacked_path.display()))
.map_err(|e| ActionError::Custom(Box::new(e)))?
.collect::<Result<Vec<_>, _>>()
.map_err(|e| ActionError::Custom(Box::new(e)))?;
assert_eq!(
found_nix_paths.len(),
1,
"Did not expect to find multiple nix paths, please report this"
);
if found_nix_paths.len() != 1 {
return Err(ActionError::MalformedBinaryTarball);
}
let found_nix_path = found_nix_paths.into_iter().next().unwrap();
let src_store = found_nix_path.join("store");
let dest = Path::new(DEST).join("store");
tracing::trace!(src = %src_store.display(), dest = %dest.display(), "Renaming");
tokio::fs::rename(&src_store, &dest)
let mut src_store_listing = tokio::fs::read_dir(src_store.clone())
.await
.map_err(|e| ActionError::Rename(src_store.clone(), dest.to_owned(), e))?;

let src_reginfo = found_nix_path.join(".reginfo");

// Move_unpacked_nix expects it here
let dest_reginfo = Path::new(DEST).join(".reginfo");
tokio::fs::rename(&src_reginfo, &dest_reginfo)
.await
.map_err(|e| ActionError::Rename(src_reginfo.clone(), dest_reginfo.to_owned(), e))?;

tokio::fs::remove_dir_all(&src)
.map_err(|e| ActionError::ReadDir(src_store.clone(), e))?;
let dest_store = Path::new(DEST).join("store");
if dest_store.exists() {
if !dest_store.is_dir() {
return Err(ActionError::PathWasNotDirectory(dest_store.clone()))?;
}
} else {
tokio::fs::create_dir(&dest_store)
.await
.map_err(|e| ActionError::CreateDirectory(dest_store.clone(), e))?;
}

while let Some(entry) = src_store_listing
.next_entry()
.await
.map_err(|e| ActionError::Remove(src.clone(), e))?;
.map_err(|e| ActionError::ReadDir(src_store.clone(), e))?
{
let entry_dest = dest_store.join(entry.file_name());
if entry_dest.exists() {
tracing::trace!(src = %entry.path().display(), dest = %entry_dest.display(), "Skipping, already exists");
} else {
tracing::trace!(src = %entry.path().display(), dest = %entry_dest.display(), "Renaming");
tokio::fs::rename(&entry.path(), &entry_dest)
.await
.map_err(|e| {
ActionError::Rename(entry.path().clone(), entry_dest.to_owned(), e)
})?;
}
}

Ok(())
}
Expand Down
76 changes: 76 additions & 0 deletions src/action/base/remove_directory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use std::path::{Path, PathBuf};

use tokio::fs::remove_dir_all;
use tracing::{span, Span};

use crate::action::{Action, ActionDescription, ActionState};
use crate::action::{ActionError, StatefulAction};

/** Remove a directory, does nothing on revert.
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct RemoveDirectory {
path: PathBuf,
}

impl RemoveDirectory {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(path: impl AsRef<Path>) -> Result<StatefulAction<Self>, ActionError> {
let path = path.as_ref().to_path_buf();

Ok(StatefulAction {
action: Self {
path: path.to_path_buf(),
},
state: ActionState::Uncompleted,
})
}
}

#[async_trait::async_trait]
#[typetag::serde(name = "remove_directory")]
impl Action for RemoveDirectory {
fn action_tag() -> crate::action::ActionTag {
crate::action::ActionTag("remove_directory")
}
fn tracing_synopsis(&self) -> String {
format!("Remove directory `{}`", self.path.display())
}

fn tracing_span(&self) -> Span {
span!(
tracing::Level::DEBUG,
"remove_directory",
path = tracing::field::display(self.path.display()),
)
}

fn execute_description(&self) -> Vec<ActionDescription> {
vec![ActionDescription::new(self.tracing_synopsis(), vec![])]
}

#[tracing::instrument(level = "debug", skip_all)]
async fn execute(&mut self) -> Result<(), ActionError> {
if self.path.exists() {
if !self.path.is_dir() {
return Err(ActionError::PathWasNotDirectory(self.path.clone()));
}
remove_dir_all(&self.path)
.await
.map_err(|e| ActionError::Remove(self.path.clone(), e))?;
} else {
tracing::debug!("Directory `{}` not present, skipping", self.path.display(),);
};

Ok(())
}

fn revert_description(&self) -> Vec<ActionDescription> {
vec![]
}

#[tracing::instrument(level = "debug", skip_all)]
async fn revert(&mut self) -> Result<(), ActionError> {
Ok(())
}
}
113 changes: 60 additions & 53 deletions src/action/base/setup_default_profile.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::{Path, PathBuf};
use std::path::PathBuf;

use crate::{
action::{ActionError, ActionTag, StatefulAction},
Expand All @@ -16,12 +16,14 @@ use crate::action::{Action, ActionDescription};
Setup the default Nix profile with `nss-cacert` and `nix` itself.
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct SetupDefaultProfile {}
pub struct SetupDefaultProfile {
unpacked_path: PathBuf,
}

impl SetupDefaultProfile {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan() -> Result<StatefulAction<Self>, ActionError> {
Ok(Self {}.into())
pub async fn plan(unpacked_path: PathBuf) -> Result<StatefulAction<Self>, ActionError> {
Ok(Self { unpacked_path }.into())
}
}

Expand Down Expand Up @@ -91,56 +93,61 @@ impl Action for SetupDefaultProfile {
)));
};

{
let reginfo_path =
Path::new(crate::action::base::move_unpacked_nix::DEST).join(".reginfo");
let reginfo = tokio::fs::read(&reginfo_path)
.await
.map_err(|e| ActionError::Read(reginfo_path.to_path_buf(), e))?;
let mut load_db_command = Command::new(nix_pkg.join("bin/nix-store"));
load_db_command.process_group(0);
load_db_command.arg("--load-db");
load_db_command.stdin(std::process::Stdio::piped());
load_db_command.stdout(std::process::Stdio::piped());
load_db_command.stderr(std::process::Stdio::piped());
load_db_command.env(
"HOME",
dirs::home_dir().ok_or_else(|| {
ActionError::Custom(Box::new(SetupDefaultProfileError::NoRootHome))
})?,
);
tracing::trace!(
"Executing `{:?}` with stdin from `{}`",
load_db_command.as_std(),
reginfo_path.display()
);
let mut handle = load_db_command
.spawn()
.map_err(|e| ActionError::command(&load_db_command, e))?;

let mut stdin = handle.stdin.take().unwrap();
stdin
.write_all(&reginfo)
.await
.map_err(|e| ActionError::Write(PathBuf::from("/dev/stdin"), e))?;
stdin
.flush()
.await
.map_err(|e| ActionError::Write(PathBuf::from("/dev/stdin"), e))?;
drop(stdin);
tracing::trace!(
"Wrote `{}` to stdin of `nix-store --load-db`",
reginfo_path.display()
);

let output = handle
.wait_with_output()
.await
.map_err(|e| ActionError::command(&load_db_command, e))?;
if !output.status.success() {
return Err(ActionError::command_output(&load_db_command, output));
};
let found_nix_paths = glob::glob(&format!("{}/nix-*", self.unpacked_path.display()))
.map_err(|e| ActionError::Custom(Box::new(e)))?
.collect::<Result<Vec<_>, _>>()
.map_err(|e| ActionError::Custom(Box::new(e)))?;
if found_nix_paths.len() != 1 {
return Err(ActionError::MalformedBinaryTarball);
}
let found_nix_path = found_nix_paths.into_iter().next().unwrap();
let reginfo_path = PathBuf::from(found_nix_path).join(".reginfo");
let reginfo = tokio::fs::read(&reginfo_path)
.await
.map_err(|e| ActionError::Read(reginfo_path.to_path_buf(), e))?;
let mut load_db_command = Command::new(nix_pkg.join("bin/nix-store"));
load_db_command.process_group(0);
load_db_command.arg("--load-db");
load_db_command.stdin(std::process::Stdio::piped());
load_db_command.stdout(std::process::Stdio::piped());
load_db_command.stderr(std::process::Stdio::piped());
load_db_command.env(
"HOME",
dirs::home_dir().ok_or_else(|| {
ActionError::Custom(Box::new(SetupDefaultProfileError::NoRootHome))
})?,
);
tracing::trace!(
"Executing `{:?}` with stdin from `{}`",
load_db_command.as_std(),
reginfo_path.display()
);
let mut handle = load_db_command
.spawn()
.map_err(|e| ActionError::command(&load_db_command, e))?;

let mut stdin = handle.stdin.take().unwrap();
stdin
.write_all(&reginfo)
.await
.map_err(|e| ActionError::Write(PathBuf::from("/dev/stdin"), e))?;
stdin
.flush()
.await
.map_err(|e| ActionError::Write(PathBuf::from("/dev/stdin"), e))?;
drop(stdin);
tracing::trace!(
"Wrote `{}` to stdin of `nix-store --load-db`",
reginfo_path.display()
);

let output = handle
.wait_with_output()
.await
.map_err(|e| ActionError::command(&load_db_command, e))?;
if !output.status.success() {
return Err(ActionError::command_output(&load_db_command, output));
};

// Install `nix` itself into the store
execute_command(
Expand Down
22 changes: 12 additions & 10 deletions src/action/common/configure_init_service.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::PathBuf;
use std::path::{Path, PathBuf};

use tokio::process::Command;
use tracing::{span, Span};
Expand Down Expand Up @@ -138,15 +138,17 @@ impl Action for ConfigureInitService {
#[cfg(target_os = "linux")]
InitSystem::Systemd => {
tracing::trace!(src = TMPFILES_SRC, dest = TMPFILES_DEST, "Symlinking");
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
.await
.map_err(|e| {
ActionError::Symlink(
PathBuf::from(TMPFILES_SRC),
PathBuf::from(TMPFILES_DEST),
e,
)
})?;
if !Path::new(TMPFILES_DEST).exists() {
tokio::fs::symlink(TMPFILES_SRC, TMPFILES_DEST)
.await
.map_err(|e| {
ActionError::Symlink(
PathBuf::from(TMPFILES_SRC),
PathBuf::from(TMPFILES_DEST),
e,
)
})?;
}

execute_command(
Command::new("systemd-tmpfiles")
Expand Down
6 changes: 4 additions & 2 deletions src/action/common/configure_nix.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::path::PathBuf;

use crate::{
action::{
base::SetupDefaultProfile,
common::{ConfigureShellProfile, PlaceNixConfiguration},
Action, ActionDescription, ActionError, ActionTag, StatefulAction,
},
settings::CommonSettings,
settings::{CommonSettings, SCRATCH_DIR},
};

use tracing::{span, Instrument, Span};
Expand All @@ -22,7 +24,7 @@ pub struct ConfigureNix {
impl ConfigureNix {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
let setup_default_profile = SetupDefaultProfile::plan()
let setup_default_profile = SetupDefaultProfile::plan(PathBuf::from(SCRATCH_DIR))
.await
.map_err(|e| ActionError::Child(SetupDefaultProfile::action_tag(), Box::new(e)))?;

Expand Down
Loading