Use a vec for the plan instead of specifics

This commit is contained in:
Ana Hobden 2022-10-13 10:34:19 -07:00
parent cdec8549ff
commit 2bd6cf1e65
7 changed files with 140 additions and 120 deletions

55
Cargo.lock generated
View file

@ -390,6 +390,15 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "erased-serde"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54558e0ba96fbe24280072642eceb9d7d442e32c7ec0ea9e7ecd7b4ea2cf4e11"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "event-listener" name = "event-listener"
version = "2.5.3" version = "2.5.3"
@ -558,6 +567,17 @@ dependencies = [
"slab", "slab",
] ]
[[package]]
name = "ghost"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb19fe8de3ea0920d282f7b77dd4227aea6b8b999b42cdf0ca41b2472b14443a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "gimli" name = "gimli"
version = "0.26.2" version = "0.26.2"
@ -629,6 +649,7 @@ dependencies = [
"tracing", "tracing",
"tracing-error", "tracing-error",
"tracing-subscriber", "tracing-subscriber",
"typetag",
"url", "url",
"valuable", "valuable",
"walkdir", "walkdir",
@ -762,6 +783,16 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "inventory"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e21e0a36a4dc4b469422ee17f715e8313f4a637675656d6a13637954278c6f55"
dependencies = [
"ctor",
"ghost",
]
[[package]] [[package]]
name = "iovec" name = "iovec"
version = "0.1.4" version = "0.1.4"
@ -1652,6 +1683,30 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "typetag"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23c8909f61359cac318f10dc9a3f9a7ee2155b6511f2a0c973460222ae5a00bb"
dependencies = [
"erased-serde",
"inventory",
"once_cell",
"serde",
"typetag-impl",
]
[[package]]
name = "typetag-impl"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd4d8ff674190866d3437d2f725355d49e99e3b1a513bf7ee43dcfe7effc36f4"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.8" version = "0.3.8"

View file

@ -34,3 +34,4 @@ walkdir = "2.3.2"
serde = { version = "1.0.144", features = ["derive"] } serde = { version = "1.0.144", features = ["derive"] }
url = { version = "2.3.1", features = ["serde"] } url = { version = "2.3.1", features = ["serde"] }
serde_json = "1.0.85" serde_json = "1.0.85"
typetag = "0.2.3"

View file

@ -8,6 +8,7 @@ use base::{
FetchNixError, MoveUnpackedNix, MoveUnpackedNixError, SetupDefaultProfile, FetchNixError, MoveUnpackedNix, MoveUnpackedNixError, SetupDefaultProfile,
SetupDefaultProfileError, SetupDefaultProfileError,
}; };
use futures::Future;
use meta::{ use meta::{
ConfigureNix, ConfigureNixError, ConfigureShellProfile, ConfigureShellProfileError, ConfigureNix, ConfigureNixError, ConfigureShellProfile, ConfigureShellProfileError,
CreateNixTree, CreateNixTreeError, CreateUsersAndGroup, CreateUsersAndGroupError, CreateNixTree, CreateNixTreeError, CreateUsersAndGroup, CreateUsersAndGroupError,

View file

@ -22,7 +22,7 @@ pub(crate) struct Execute {
default_value = "false", default_value = "false",
global = true global = true
)] )]
pub(crate) explain: bool, explain: bool,
#[clap(default_value = "/dev/stdin")] #[clap(default_value = "/dev/stdin")]
plan: PathBuf, plan: PathBuf,
} }

View file

@ -18,7 +18,7 @@ pub(crate) struct Plan {
env = "HARMONIC_CHANNEL", env = "HARMONIC_CHANNEL",
default_value = "nixpkgs=https://nixos.org/channels/nixpkgs-unstable" default_value = "nixpkgs=https://nixos.org/channels/nixpkgs-unstable"
)] )]
pub(crate) channel: Vec<crate::cli::arg::ChannelValue>, channel: Vec<crate::cli::arg::ChannelValue>,
/// Don't modify the user profile to automatically load nix /// Don't modify the user profile to automatically load nix
#[clap( #[clap(
long, long,
@ -26,17 +26,17 @@ pub(crate) struct Plan {
default_value = "false", default_value = "false",
global = true global = true
)] )]
pub(crate) no_modify_profile: bool, no_modify_profile: bool,
/// Number of build users to create /// Number of build users to create
#[clap(long, default_value = "32", env = "HARMONIC_NIX_DAEMON_USER_COUNT")] #[clap(long, default_value = "32", env = "HARMONIC_NIX_DAEMON_USER_COUNT")]
pub(crate) daemon_user_count: usize, daemon_user_count: usize,
#[clap( #[clap(
long, long,
action(ArgAction::SetTrue), action(ArgAction::SetTrue),
default_value = "false", default_value = "false",
global = true global = true
)] )]
pub(crate) force: bool, force: bool,
#[clap(default_value = "/dev/stdout")] #[clap(default_value = "/dev/stdout")]
plan: PathBuf, plan: PathBuf,
} }

View file

@ -22,7 +22,7 @@ pub(crate) struct Uninstall {
default_value = "false", default_value = "false",
global = true global = true
)] )]
pub(crate) explain: bool, explain: bool,
#[clap(default_value = "/nix/receipt.json")] #[clap(default_value = "/nix/receipt.json")]
receipt: PathBuf, receipt: PathBuf,
} }

View file

@ -3,7 +3,7 @@ use std::path::PathBuf;
use crate::{ use crate::{
actions::{ actions::{
meta::{ConfigureNix, ProvisionNix, StartNixDaemon}, meta::{ConfigureNix, ProvisionNix, StartNixDaemon},
ActionDescription, ActionError, Actionable, Action, ActionDescription, ActionError, Actionable,
}, },
settings::InstallSettings, settings::InstallSettings,
HarmonicError, HarmonicError,
@ -13,52 +13,33 @@ use crate::{
pub struct InstallPlan { pub struct InstallPlan {
settings: InstallSettings, settings: InstallSettings,
/** Bootstrap the install actions: Vec<Action>,
* There are roughly three phases:
* "Create nix tree"":
* download_nix --------------------------------------> move_downloaded_nix
* create_group -> create_users -> create_directories -> move_downloaded_nix
* place_channel_configuration
* place_nix_configuration
* ---
* "Configure Nix":
* setup_default_profile
* configure_nix_daemon_service
* configure_shell_profile
* ---
* "Start Nix"
* start_nix_daemon_service
*/
provision_nix: ProvisionNix,
configure_nix: ConfigureNix,
start_nix_daemon: StartNixDaemon,
} }
impl InstallPlan { impl InstallPlan {
pub async fn new(settings: InstallSettings) -> Result<Self, HarmonicError> { pub async fn new(settings: InstallSettings) -> Result<Self, HarmonicError> {
Ok(Self { Ok(Self {
settings: settings.clone(), settings: settings.clone(),
provision_nix: ProvisionNix::plan(settings.clone()) actions: vec![
.await ProvisionNix::plan(settings.clone())
.map_err(|e| ActionError::from(e))?, .await
configure_nix: ConfigureNix::plan(settings) .map(Action::from)
.await .map_err(ActionError::from)?,
.map_err(|e| ActionError::from(e))?, ConfigureNix::plan(settings)
start_nix_daemon: StartNixDaemon::plan() .await
.await .map(Action::from)
.map_err(|e| ActionError::from(e))?, .map_err(ActionError::from)?,
StartNixDaemon::plan()
.await
.map(Action::from)
.map_err(ActionError::from)?,
],
}) })
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub fn describe_execute(&self, explain: bool) -> String { pub fn describe_execute(&self, explain: bool) -> String {
let Self { let Self { settings, actions } = self;
settings,
provision_nix,
configure_nix,
start_nix_daemon,
} = self;
format!( format!(
"\ "\
This Nix install is for:\n\ This Nix install is for:\n\
@ -77,66 +58,53 @@ impl InstallPlan {
.map(|(name, url)| format!("{name}={url}")) .map(|(name, url)| format!("{name}={url}"))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(","), .join(","),
actions = { actions = actions
let mut buf = provision_nix.describe_execute(); .iter()
buf.append(&mut configure_nix.describe_execute()); .map(|v| v.describe_execute())
buf.append(&mut start_nix_daemon.describe_execute()); .flatten()
buf.iter() .map(|desc| {
.map(|desc| { let ActionDescription {
let ActionDescription { description,
description, explanation,
explanation, } = desc;
} = desc;
let mut buf = String::default(); let mut buf = String::default();
buf.push_str(&format!("* {description}\n")); buf.push_str(&format!("* {description}\n"));
if explain { if explain {
for line in explanation { for line in explanation {
buf.push_str(&format!(" {line}\n")); buf.push_str(&format!(" {line}\n"));
}
} }
buf }
}) buf
.collect::<Vec<_>>() })
.join("\n") .collect::<Vec<_>>()
}, .join("\n"),
) )
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn install(&mut self) -> Result<(), HarmonicError> { pub async fn install(&mut self) -> Result<(), HarmonicError> {
let Self {
actions,
settings: _,
} = self;
// This is **deliberately sequential**. // This is **deliberately sequential**.
// Actions which are parallelizable are represented by "group actions" like CreateUsers // Actions which are parallelizable are represented by "group actions" like CreateUsers
// The plan itself represents the concept of the sequence of stages. // The plan itself represents the concept of the sequence of stages.
for action in actions {
if let Err(err) = self.provision_nix.execute().await { if let Err(err) = action.execute().await {
write_receipt(self.clone()).await?; write_receipt(self.clone()).await?;
return Err(ActionError::from(err).into()); return Err(ActionError::from(err).into());
}
} }
if let Err(err) = self.configure_nix.execute().await { write_receipt(self.clone()).await
write_receipt(self.clone()).await?;
return Err(ActionError::from(err).into());
}
if let Err(err) = self.start_nix_daemon.execute().await {
write_receipt(self.clone()).await?;
return Err(ActionError::from(err).into());
}
write_receipt(self.clone()).await?;
Ok(())
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub fn describe_revert(&self, explain: bool) -> String { pub fn describe_revert(&self, explain: bool) -> String {
let Self { let Self { settings, actions } = self;
settings,
provision_nix,
configure_nix,
start_nix_daemon,
} = self;
format!( format!(
"\ "\
This Nix uninstall is for:\n\ This Nix uninstall is for:\n\
@ -155,50 +123,45 @@ impl InstallPlan {
.map(|(name, url)| format!("{name}={url}")) .map(|(name, url)| format!("{name}={url}"))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(","), .join(","),
actions = { actions = actions
let mut buf = provision_nix.describe_revert(); .iter()
buf.append(&mut configure_nix.describe_revert()); .map(|v| v.describe_revert())
buf.append(&mut start_nix_daemon.describe_revert()); .flatten()
buf.iter() .map(|desc| {
.map(|desc| { let ActionDescription {
let ActionDescription { description,
description, explanation,
explanation, } = desc;
} = desc;
let mut buf = String::default(); let mut buf = String::default();
buf.push_str(&format!("* {description}\n")); buf.push_str(&format!("* {description}\n"));
if explain { if explain {
for line in explanation { for line in explanation {
buf.push_str(&format!(" {line}\n")); buf.push_str(&format!(" {line}\n"));
}
} }
buf }
}) buf
.collect::<Vec<_>>() })
.join("\n") .collect::<Vec<_>>()
}, .join("\n"),
) )
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn revert(&mut self) -> Result<(), HarmonicError> { pub async fn revert(&mut self) -> Result<(), HarmonicError> {
let Self {
actions,
settings: _,
} = self;
// This is **deliberately sequential**. // This is **deliberately sequential**.
// Actions which are parallelizable are represented by "group actions" like CreateUsers // Actions which are parallelizable are represented by "group actions" like CreateUsers
// The plan itself represents the concept of the sequence of stages. // The plan itself represents the concept of the sequence of stages.
if let Err(err) = self.start_nix_daemon.revert().await { for action in actions {
write_receipt(self.clone()).await?; if let Err(err) = action.revert().await {
return Err(ActionError::from(err).into()); write_receipt(self.clone()).await?;
} return Err(ActionError::from(err).into());
}
if let Err(err) = self.configure_nix.revert().await {
write_receipt(self.clone()).await?;
return Err(ActionError::from(err).into());
}
if let Err(err) = self.provision_nix.revert().await {
write_receipt(self.clone()).await?;
return Err(ActionError::from(err).into());
} }
Ok(()) Ok(())