aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flake.nix70
-rw-r--r--src/bin/activate.rs99
-rw-r--r--src/bin/deploy.rs8
-rw-r--r--src/deploy.rs29
-rw-r--r--src/lib.rs1
5 files changed, 128 insertions, 79 deletions
diff --git a/flake.nix b/flake.nix
index d87702f..d718e83 100644
--- a/flake.nix
+++ b/flake.nix
@@ -59,34 +59,48 @@
activate.custom;
activate = rec {
- custom = base: activate: pkgs.buildEnv {
- name = ("activatable-" + base.name);
- paths = [
- base
- (pkgs.writeTextFile {
- name = base.name + "-activate-path";
- text = ''
- #!${pkgs.runtimeShell}
- set -euo pipefail
-
- ${activate}
- '';
- executable = true;
- destination = "/deploy-rs-activate";
- })
- (pkgs.writeTextFile {
- name = base.name + "-activate-rs";
- text = ''
- #!${pkgs.runtimeShell}
- exec ${self.defaultPackage."${system}"}/bin/activate "$@"
- '';
- executable = true;
- destination = "/activate-rs";
- })
- ];
- };
-
- nixos = base: custom base.config.system.build.toplevel ''
+ custom =
+ {
+ __functor = customSelf: base: activate:
+ pkgs.buildEnv {
+ name = ("activatable-" + base.name);
+ paths =
+ [
+ base
+ (pkgs.writeTextFile {
+ name = base.name + "-activate-path";
+ text = ''
+ #!${pkgs.runtimeShell}
+ set -euo pipefail
+
+ if [[ $DRY_ACTIVATE == "1" ]]
+ then
+ ${if builtins.hasAttr "dryActivate" customSelf
+ then
+ customSelf.dryActivate
+ else
+ "echo ${pkgs.writeScript "activate" activate}"}
+ else
+ ${activate}
+ fi
+ '';
+ executable = true;
+ destination = "/deploy-rs-activate";
+ })
+ (pkgs.writeTextFile {
+ name = base.name + "-activate-rs";
+ text = ''
+ #!${pkgs.runtimeShell}
+ exec ${self.defaultPackage."${system}"}/bin/activate "$@"
+ '';
+ executable = true;
+ destination = "/activate-rs";
+ })
+ ];
+ };
+ };
+
+ nixos = base: (custom // { dryActivate = "$PROFILE/bin/switch-to-configuration dry-activate"; }) base.config.system.build.toplevel ''
# work around https://github.com/NixOS/nixpkgs/issues/73404
cd /tmp
diff --git a/src/bin/activate.rs b/src/bin/activate.rs
index 947e883..d17f3a8 100644
--- a/src/bin/activate.rs
+++ b/src/bin/activate.rs
@@ -66,6 +66,10 @@ struct ActivateOpts {
/// Auto rollback if failure
#[clap(long)]
auto_rollback: bool,
+
+ /// Show what will be activated on the machines
+ #[clap(long)]
+ dry_activate: bool,
}
/// Activate a profile
@@ -348,70 +352,78 @@ pub async fn activate(
temp_path: String,
confirm_timeout: u16,
magic_rollback: bool,
+ dry_activate: bool,
) -> Result<(), ActivateError> {
- info!("Activating profile");
-
- let nix_env_set_exit_status = Command::new("nix-env")
- .arg("-p")
- .arg(&profile_path)
- .arg("--set")
- .arg(&closure)
- .status()
- .await
- .map_err(ActivateError::SetProfileError)?;
-
- match nix_env_set_exit_status.code() {
- Some(0) => (),
- a => {
- if auto_rollback {
- deactivate(&profile_path).await?;
+ if !dry_activate {
+ info!("Activating profile");
+ let nix_env_set_exit_status = Command::new("nix-env")
+ .arg("-p")
+ .arg(&profile_path)
+ .arg("--set")
+ .arg(&closure)
+ .status()
+ .await
+ .map_err(ActivateError::SetProfileError)?;
+ match nix_env_set_exit_status.code() {
+ Some(0) => (),
+ a => {
+ if auto_rollback && !dry_activate {
+ deactivate(&profile_path).await?;
+ }
+ return Err(ActivateError::SetProfileExitError(a));
}
- return Err(ActivateError::SetProfileExitError(a));
- }
- };
+ };
+ }
debug!("Running activation script");
- let activate_status = match Command::new(format!("{}/deploy-rs-activate", profile_path))
- .env("PROFILE", &profile_path)
- .current_dir(&profile_path)
+ let activation_location = if dry_activate { &closure } else { &profile_path };
+
+ let activate_status = match Command::new(format!("{}/deploy-rs-activate", activation_location))
+ .env("PROFILE", activation_location)
+ .env("DRY_ACTIVATE", if dry_activate { "1" } else { "0" })
+ .current_dir(activation_location)
.status()
.await
.map_err(ActivateError::RunActivateError)
{
Ok(x) => x,
Err(e) => {
- if auto_rollback {
+ if auto_rollback && !dry_activate {
deactivate(&profile_path).await?;
}
return Err(e);
}
};
- match activate_status.code() {
- Some(0) => (),
- a => {
- if auto_rollback {
- deactivate(&profile_path).await?;
+ if !dry_activate {
+ match activate_status.code() {
+ Some(0) => (),
+ a => {
+ if auto_rollback {
+ deactivate(&profile_path).await?;
+ }
+ return Err(ActivateError::RunActivateExitError(a));
}
- return Err(ActivateError::RunActivateExitError(a));
- }
- };
+ };
- info!("Activation succeeded!");
+ if !dry_activate {
+ info!("Activation succeeded!");
+ }
- if magic_rollback {
- info!("Magic rollback is enabled, setting up confirmation hook...");
+ if magic_rollback {
+ info!("Magic rollback is enabled, setting up confirmation hook...");
- match activation_confirmation(profile_path.clone(), temp_path, confirm_timeout, closure)
- .await
- {
- Ok(()) => {}
- Err(err) => {
- deactivate(&profile_path).await?;
- return Err(ActivateError::ActivationConfirmationError(err));
- }
- };
+ match activation_confirmation(profile_path.clone(), temp_path, confirm_timeout, closure)
+ .await
+ {
+ Ok(()) => {}
+ Err(err) => {
+ deactivate(&profile_path).await?;
+ return Err(ActivateError::ActivationConfirmationError(err));
+ }
+ };
+ }
}
Ok(())
@@ -446,6 +458,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
opts.temp_path,
activate_opts.confirm_timeout,
activate_opts.magic_rollback,
+ activate_opts.dry_activate,
)
.await
.map_err(|x| Box::new(x) as Box<dyn std::error::Error>),
diff --git a/src/bin/deploy.rs b/src/bin/deploy.rs
index 9c41d25..10e0552 100644
--- a/src/bin/deploy.rs
+++ b/src/bin/deploy.rs
@@ -74,6 +74,9 @@ struct Opts {
/// Where to store temporary files (only used by magic-rollback)
#[clap(long)]
temp_path: Option<String>,
+ /// Show what will be activated on the machines
+ #[clap(long)]
+ dry_activate: bool,
}
/// Returns if the available Nix installation supports flakes
@@ -379,6 +382,7 @@ async fn run_deploy(
extra_build_args: &[String],
debug_logs: bool,
log_dir: Option<String>,
+ dry_activate: bool,
) -> Result<(), RunDeployError> {
let to_deploy: ToDeploy = match (&deploy_flake.node, &deploy_flake.profile) {
(Some(node_name), Some(profile_name)) => {
@@ -499,7 +503,7 @@ async fn run_deploy(
}
for (deploy_data, deploy_defs) in &parts {
- deploy::deploy::deploy_profile(&deploy_data, &deploy_defs).await?;
+ deploy::deploy::deploy_profile(&deploy_data, &deploy_defs, dry_activate).await?;
}
Ok(())
@@ -546,6 +550,7 @@ async fn run() -> Result<(), RunError> {
magic_rollback: opts.magic_rollback,
temp_path: opts.temp_path,
confirm_timeout: opts.confirm_timeout,
+ dry_activate: opts.dry_activate,
};
let supports_flakes = test_flake_support().await.map_err(RunError::FlakeTest)?;
@@ -574,6 +579,7 @@ async fn run() -> Result<(), RunError> {
&opts.extra_build_args,
opts.debug_logs,
opts.log_dir,
+ opts.dry_activate,
)
.await?;
diff --git a/src/deploy.rs b/src/deploy.rs
index f6871d2..285bbbd 100644
--- a/src/deploy.rs
+++ b/src/deploy.rs
@@ -18,6 +18,7 @@ struct ActivateCommandData<'a> {
magic_rollback: bool,
debug_logs: bool,
log_dir: Option<&'a str>,
+ dry_activate: bool,
}
fn build_activate_command(data: ActivateCommandData) -> String {
@@ -49,6 +50,10 @@ fn build_activate_command(data: ActivateCommandData) -> String {
self_activate_command = format!("{} --auto-rollback", self_activate_command);
}
+ if data.dry_activate {
+ self_activate_command = format!("{} --dry-activate", self_activate_command);
+ }
+
if let Some(sudo_cmd) = &data.sudo {
self_activate_command = format!("{} {}", sudo_cmd, self_activate_command);
}
@@ -62,6 +67,7 @@ fn test_activation_command_builder() {
let profile_path = "/blah/profiles/test";
let closure = "/nix/store/blah/etc";
let auto_rollback = true;
+ let dry_activate = false;
let temp_path = "/tmp";
let confirm_timeout = 30;
let magic_rollback = true;
@@ -78,7 +84,8 @@ fn test_activation_command_builder() {
confirm_timeout,
magic_rollback,
debug_logs,
- log_dir
+ log_dir,
+ dry_activate
}),
"sudo -u test /nix/store/blah/etc/activate-rs --debug-logs --log-dir /tmp/something.txt --temp-path '/tmp' activate '/nix/store/blah/etc' '/blah/profiles/test' --confirm-timeout 30 --magic-rollback --auto-rollback"
.to_string(),
@@ -210,11 +217,14 @@ pub enum DeployProfileError {
pub async fn deploy_profile(
deploy_data: &super::DeployData<'_>,
deploy_defs: &super::DeployDefs,
+ dry_activate: bool,
) -> Result<(), DeployProfileError> {
- info!(
- "Activating profile `{}` for node `{}`",
- deploy_data.profile_name, deploy_data.node_name
- );
+ if !dry_activate {
+ info!(
+ "Activating profile `{}` for node `{}`",
+ deploy_data.profile_name, deploy_data.node_name
+ );
+ }
let temp_path: Cow<str> = match &deploy_data.merged_settings.temp_path {
Some(x) => x.into(),
@@ -237,6 +247,7 @@ pub async fn deploy_profile(
magic_rollback,
debug_logs: deploy_data.debug_logs,
log_dir: deploy_data.log_dir,
+ dry_activate,
});
debug!("Constructed activation command: {}", self_activate_command);
@@ -255,7 +266,7 @@ pub async fn deploy_profile(
ssh_activate_command.arg(&ssh_opt);
}
- if !magic_rollback {
+ if !magic_rollback || dry_activate {
let ssh_activate_exit_status = ssh_activate_command
.arg(self_activate_command)
.status()
@@ -267,7 +278,11 @@ pub async fn deploy_profile(
a => return Err(DeployProfileError::SSHActivateExitError(a)),
};
- info!("Success activating, done!");
+ if dry_activate {
+ info!("Completed dry-activate!");
+ } else {
+ info!("Success activating, done!");
+ }
} else {
let self_wait_command = build_wait_command(WaitCommandData {
sudo: &deploy_defs.sudo,
diff --git a/src/lib.rs b/src/lib.rs
index b93b9ae..a6b57aa 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -140,6 +140,7 @@ pub struct CmdOverrides {
pub magic_rollback: Option<bool>,
pub temp_path: Option<String>,
pub confirm_timeout: Option<u16>,
+ pub dry_activate: bool,
}
#[derive(PartialEq, Debug)]