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 2 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};
60 changes: 37 additions & 23 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,17 +46,17 @@ 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)))?;
Expand All @@ -67,23 +67,37 @@ impl Action for MoveUnpackedNix {
);
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(())
}
}
34 changes: 27 additions & 7 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 @@ -17,13 +17,21 @@ Setup the default Nix profile with `nss-cacert` and `nix` itself.
*/
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct SetupDefaultProfile {
unpacked_path: PathBuf,
channels: Vec<ChannelValue>,
}

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

Expand Down Expand Up @@ -56,7 +64,10 @@ impl Action for SetupDefaultProfile {

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

// Find an `nix` package
let nix_pkg_glob = "/nix/store/*-nix-*";
Expand Down Expand Up @@ -104,9 +115,18 @@ impl Action for SetupDefaultProfile {
)));
};

{
let reginfo_path =
Path::new(crate::action::base::move_unpacked_nix::DEST).join(".reginfo");
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!(
Hoverbear marked this conversation as resolved.
Show resolved Hide resolved
found_nix_paths.len(),
1,
"Did not expect to find multiple nix paths, please report this"
Hoverbear marked this conversation as resolved.
Show resolved Hide resolved
);
let found_nix_path = found_nix_paths.into_iter().next().unwrap();
let reginfo_path = PathBuf::from(found_nix_path).join(".reginfo");
if reginfo_path.exists() {
Hoverbear marked this conversation as resolved.
Show resolved Hide resolved
let reginfo = tokio::fs::read(&reginfo_path)
.await
.map_err(|e| ActionError::Read(reginfo_path.to_path_buf(), e))?;
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
11 changes: 7 additions & 4 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, PlaceChannelConfiguration, PlaceNixConfiguration},
Action, ActionDescription, ActionError, ActionTag, StatefulAction,
},
settings::CommonSettings,
settings::{CommonSettings, SCRATCH_DIR},
};

use tracing::{span, Instrument, Span};
Expand All @@ -23,9 +25,10 @@ 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(settings.channels.clone())
.await
.map_err(|e| ActionError::Child(SetupDefaultProfile::action_tag(), Box::new(e)))?;
let setup_default_profile =
SetupDefaultProfile::plan(PathBuf::from(SCRATCH_DIR), settings.channels.clone())
.await
.map_err(|e| ActionError::Child(SetupDefaultProfile::action_tag(), Box::new(e)))?;

let configure_shell_profile = if settings.modify_profile {
Some(ConfigureShellProfile::plan().await.map_err(|e| {
Expand Down
12 changes: 5 additions & 7 deletions src/action/common/provision_nix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
base::{FetchAndUnpackNix, MoveUnpackedNix},
Action, ActionDescription, ActionError, ActionTag, StatefulAction,
},
settings::CommonSettings,
settings::{CommonSettings, SCRATCH_DIR},
};
use std::path::PathBuf;

Expand All @@ -24,18 +24,16 @@ pub struct ProvisionNix {
impl ProvisionNix {
#[tracing::instrument(level = "debug", skip_all)]
pub async fn plan(settings: &CommonSettings) -> Result<StatefulAction<Self>, ActionError> {
let fetch_nix = FetchAndUnpackNix::plan(
settings.nix_package_url.clone(),
PathBuf::from("/nix/temp-install-dir"),
)
.await?;
let fetch_nix =
FetchAndUnpackNix::plan(settings.nix_package_url.clone(), PathBuf::from(SCRATCH_DIR))
.await?;
let create_users_and_group = CreateUsersAndGroups::plan(settings.clone())
.await
.map_err(|e| ActionError::Child(CreateUsersAndGroups::action_tag(), Box::new(e)))?;
let create_nix_tree = CreateNixTree::plan()
.await
.map_err(|e| ActionError::Child(CreateNixTree::action_tag(), Box::new(e)))?;
let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from("/nix/temp-install-dir"))
let move_unpacked_nix = MoveUnpackedNix::plan(PathBuf::from(SCRATCH_DIR))
.await
.map_err(|e| ActionError::Child(MoveUnpackedNix::action_tag(), Box::new(e)))?;
Ok(Self {
Expand Down
6 changes: 5 additions & 1 deletion src/action/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,10 @@ pub enum ActionError {
existing_mode = .1 & 0o777,
planned_mode = .2 & 0o777)]
PathModeMismatch(std::path::PathBuf, u32, u32),
#[error("`{0}` was not a file")]
#[error("Path `{0}` exists, but is not a file, consider removing it with `rm {0}`")]
PathWasNotFile(std::path::PathBuf),
#[error("Path `{0}` exists, but is not a directory, consider removing it with `rm {0}`")]
PathWasNotDirectory(std::path::PathBuf),
#[error("Getting metadata for {0}`")]
GettingMetadata(std::path::PathBuf, #[source] std::io::Error),
#[error("Creating directory `{0}`")]
Expand Down Expand Up @@ -341,6 +343,8 @@ pub enum ActionError {
),
#[error("Read path `{0}`")]
Read(std::path::PathBuf, #[source] std::io::Error),
#[error("Reading directory `{0}`")]
ReadDir(std::path::PathBuf, #[source] std::io::Error),
#[error("Open path `{0}`")]
Open(std::path::PathBuf, #[source] std::io::Error),
#[error("Write path `{0}`")]
Expand Down
Loading