Add --keep-result to create GC roots for profiles

This resembles the behavior of morph.

Reference: #18
This commit is contained in:
Zhaofeng Li 2021-03-17 14:59:05 -07:00
parent 81375e71b2
commit 610a725ba2
3 changed files with 65 additions and 5 deletions

View file

@ -57,6 +57,15 @@ Set to 0 to disable parallemism limit.
Err(_) => Err(String::from("The value must be a valid number")), Err(_) => Err(String::from("The value must be a valid number")),
} }
})) }))
.arg(Arg::with_name("keep-result")
.long("keep-result")
.help("Create GC roots for built profiles")
.long_help(r#"Create GC roots for built profiles.
The built system profiles will be added as GC roots so that they will not be removed by the garbage collector.
The links will be created under .gcroots in the directory the Hive configuration is located.
"#)
.takes_value(false))
.arg(Arg::with_name("verbose") .arg(Arg::with_name("verbose")
.short("v") .short("v")
.long("verbose") .long("verbose")
@ -101,6 +110,7 @@ pub fn subcommand() -> App<'static, 'static> {
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) { pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
let hive = util::hive_from_args(local_args).unwrap(); let hive = util::hive_from_args(local_args).unwrap();
let hive_base = hive.as_path().parent().unwrap().to_owned();
log::info!("Enumerating nodes..."); log::info!("Enumerating nodes...");
let all_nodes = hive.deployment_info().await.unwrap(); let all_nodes = hive.deployment_info().await.unwrap();
@ -176,6 +186,11 @@ pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
options.set_gzip(!local_args.is_present("no-gzip")); options.set_gzip(!local_args.is_present("no-gzip"));
options.set_progress_bar(!local_args.is_present("verbose")); options.set_progress_bar(!local_args.is_present("verbose"));
options.set_upload_keys(!local_args.is_present("no-keys")); options.set_upload_keys(!local_args.is_present("no-keys"));
if local_args.is_present("keep-result") {
options.set_gc_roots(hive_base.join(".gcroots"));
}
deployment.set_options(options); deployment.set_options(options);
if local_args.is_present("no-keys") && goal_arg == "keys" { if local_args.is_present("no-keys") && goal_arg == "keys" {

View file

@ -1,6 +1,8 @@
use std::cmp::max; use std::cmp::max;
use std::sync::Arc;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::AsRef;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use futures::future::join_all; use futures::future::join_all;
use tokio::sync::{Mutex, Semaphore}; use tokio::sync::{Mutex, Semaphore};
@ -367,8 +369,7 @@ impl Deployment {
); );
let goal = arc_self.goal; let goal = arc_self.goal;
let arc_self = arc_self.clone(); let profiles = arc_self.clone().build_profiles(&chunk, drv, bar.clone()).await;
let profiles = arc_self.build_profiles(&chunk, drv, bar.clone()).await;
let profiles = match profiles { let profiles = match profiles {
Some(profiles) => profiles, Some(profiles) => profiles,
@ -385,6 +386,14 @@ impl Deployment {
} }
} }
if let Some(base) = &arc_self.options.gc_roots {
// Create GC roots
if let Err(e) = profiles.create_gc_roots(base).await {
let bar = progress.create_task_progress(BATCH_OPERATION_LABEL.to_string());
bar.failure(&format!("Failed to create GC roots: {:?}", e));
}
}
drop(permit); drop(permit);
profiles profiles
}; };
@ -582,7 +591,7 @@ impl ParallelismLimit {
} }
} }
#[derive(Copy, Clone, Debug)] #[derive(Clone, Debug)]
pub struct DeploymentOptions { pub struct DeploymentOptions {
/// Whether to show condensed progress bars. /// Whether to show condensed progress bars.
/// ///
@ -597,6 +606,9 @@ pub struct DeploymentOptions {
/// Whether to upload keys when deploying. /// Whether to upload keys when deploying.
upload_keys: bool, upload_keys: bool,
/// Directory to create GC roots for node profiles in.
gc_roots: Option<PathBuf>,
} }
impl Default for DeploymentOptions { impl Default for DeploymentOptions {
@ -606,6 +618,7 @@ impl Default for DeploymentOptions {
substituters_push: true, substituters_push: true,
gzip: true, gzip: true,
upload_keys: true, upload_keys: true,
gc_roots: None,
} }
} }
} }
@ -627,6 +640,10 @@ impl DeploymentOptions {
self.upload_keys = enable; self.upload_keys = enable;
} }
pub fn set_gc_roots<P: AsRef<Path>>(&mut self, path: P) {
self.gc_roots = Some(path.as_ref().to_owned());
}
fn to_copy_options(&self) -> CopyOptions { fn to_copy_options(&self) -> CopyOptions {
let options = CopyOptions::default(); let options = CopyOptions::default();

View file

@ -1,8 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::ops::{Deref, DerefMut};
use std::fs; use std::fs;
use std::ops::{Deref, DerefMut};
use std::path::Path; use std::path::Path;
use std::process::Stdio;
use tokio::process::Command;
use super::{ use super::{
Goal, Goal,
@ -110,3 +113,28 @@ impl TryFrom<Vec<StorePath>> for ProfileMap {
} }
} }
} }
impl ProfileMap {
/// Create GC roots for all profiles in the map.
///
/// The created links will be located at `{base}/node-{node_name}`.
pub async fn create_gc_roots(&self, base: &Path) -> NixResult<()> {
// This will actually try to build all profiles, but since they
// already exist only the GC roots will be created.
for (node, profile) in self.0.iter() {
let path = base.join(format!("node-{}", node));
let mut command = Command::new("nix-store");
command.args(&["--no-build-output", "--indirect", "--add-root", path.to_str().unwrap()]);
command.args(&["--realise", profile.as_path().to_str().unwrap()]);
command.stdout(Stdio::null());
let status = command.status().await?;
if !status.success() {
return Err(NixError::NixFailure { exit_code: status.code().unwrap() });
}
}
Ok(())
}
}