Implement --target-host
This commit is contained in:
parent
49f30480ea
commit
3280073a0f
4 changed files with 161 additions and 42 deletions
29
nix/lib.nix
29
nix/lib.nix
|
|
@ -88,22 +88,39 @@ in
|
|||
text = lib.generators.toJSON { } etcFiles;
|
||||
};
|
||||
|
||||
registerProfileScript = pkgs.writeShellScript "register-profile" ''
|
||||
${system-manager}/bin/system-manager generate \
|
||||
--store-path "$(dirname $(realpath $(dirname ''${0})))" \
|
||||
"$@"
|
||||
'';
|
||||
|
||||
activationScript = pkgs.writeShellScript "activate" ''
|
||||
${system-manager}/bin/system-manager activate \
|
||||
--store-path "$(realpath $(dirname ''${0}))" \
|
||||
--store-path "$(dirname $(realpath $(dirname ''${0})))" \
|
||||
"$@"
|
||||
'';
|
||||
|
||||
deactivationScript = pkgs.writeShellScript "deactivate" ''
|
||||
${system-manager}/bin/system-manager deactivate "$@"
|
||||
'';
|
||||
|
||||
linkFarmEntryFromDrv = drv: {
|
||||
name = drv.name;
|
||||
path = drv;
|
||||
};
|
||||
|
||||
linkFarmBinEntryFromDrv = drv: {
|
||||
name = "bin/${drv.name}";
|
||||
path = drv;
|
||||
};
|
||||
in
|
||||
returnIfNoAssertions (
|
||||
pkgs.linkFarmFromDrvs "system-manager" [
|
||||
servicesPath
|
||||
etcPath
|
||||
activationScript
|
||||
deactivationScript
|
||||
pkgs.linkFarm "system-manager" [
|
||||
(linkFarmEntryFromDrv servicesPath)
|
||||
(linkFarmEntryFromDrv etcPath)
|
||||
(linkFarmBinEntryFromDrv activationScript)
|
||||
(linkFarmBinEntryFromDrv deactivationScript)
|
||||
(linkFarmBinEntryFromDrv registerProfileScript)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,18 @@ use anyhow::Result;
|
|||
|
||||
use crate::StorePath;
|
||||
|
||||
pub fn activate(store_path: StorePath, ephemeral: bool) -> Result<()> {
|
||||
pub fn activate(store_path: &StorePath, ephemeral: bool) -> Result<()> {
|
||||
log::info!("Activating system-manager profile: {store_path}");
|
||||
if ephemeral {
|
||||
log::info!("Running in ephemeral mode");
|
||||
}
|
||||
|
||||
etc_files::activate(&store_path, ephemeral)?;
|
||||
services::activate(&store_path, ephemeral)?;
|
||||
etc_files::activate(store_path, ephemeral)?;
|
||||
services::activate(store_path, ephemeral)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO should we also remove the GC root for the profile if it exists?
|
||||
pub fn deactivate() -> Result<()> {
|
||||
log::info!("Deactivating system-manager");
|
||||
etc_files::deactivate()?;
|
||||
|
|
|
|||
|
|
@ -14,28 +14,18 @@ struct NixBuildOutput {
|
|||
outputs: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub fn generate(flake_uri: &str) -> Result<StorePath> {
|
||||
let store_path = build(flake_uri)?;
|
||||
pub fn generate(store_path: &StorePath) -> Result<()> {
|
||||
let profile_dir = Path::new(PROFILE_DIR);
|
||||
let profile_name = Path::new(PROFILE_NAME);
|
||||
|
||||
log::info!("Creating new generation from {store_path}");
|
||||
install_nix_profile(&store_path, profile_dir, profile_name)?;
|
||||
install_nix_profile(store_path, profile_dir, profile_name)?;
|
||||
|
||||
log::info!("Registering GC root...");
|
||||
create_gcroot(GCROOT_PATH, &profile_dir.join(profile_name))?;
|
||||
|
||||
log::info!("Done");
|
||||
Ok(store_path)
|
||||
}
|
||||
|
||||
pub fn build(flake_uri: &str) -> Result<StorePath> {
|
||||
// FIXME: we should not hard-code the system here
|
||||
let flake_attr = format!("{FLAKE_ATTR}.x86_64-linux");
|
||||
|
||||
log::info!("Building new system-manager generation...");
|
||||
log::info!("Running nix build...");
|
||||
run_nix_build(flake_uri, &flake_attr).and_then(get_store_path)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn install_nix_profile(
|
||||
|
|
@ -61,6 +51,15 @@ fn create_gcroot(gcroot_path: &str, profile_path: &Path) -> Result<()> {
|
|||
create_store_link(&store_path, Path::new(gcroot_path))
|
||||
}
|
||||
|
||||
pub fn build(flake_uri: &str) -> Result<StorePath> {
|
||||
// FIXME: we should not hard-code the system here
|
||||
let flake_attr = format!("{FLAKE_ATTR}.x86_64-linux");
|
||||
|
||||
log::info!("Building new system-manager generation...");
|
||||
log::info!("Running nix build...");
|
||||
run_nix_build(flake_uri, &flake_attr).and_then(get_store_path)
|
||||
}
|
||||
|
||||
fn get_store_path(nix_build_result: process::Output) -> Result<StorePath> {
|
||||
if nix_build_result.status.success() {
|
||||
String::from_utf8(nix_build_result.stdout)
|
||||
|
|
|
|||
142
src/main.rs
142
src/main.rs
|
|
@ -1,4 +1,4 @@
|
|||
use std::process::ExitCode;
|
||||
use std::process::{self, ExitCode, ExitStatus};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
|
|
@ -10,6 +10,13 @@ use system_manager::StorePath;
|
|||
struct Args {
|
||||
#[command(subcommand)]
|
||||
action: Action,
|
||||
|
||||
#[arg(long)]
|
||||
/// The host to deploy the system-manager profile to
|
||||
target_host: Option<String>,
|
||||
|
||||
#[arg(long, action)]
|
||||
use_remote_sudo: bool,
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Debug)]
|
||||
|
|
@ -19,6 +26,17 @@ struct BuildArgs {
|
|||
flake_uri: String,
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Debug)]
|
||||
struct GenerateArgs {
|
||||
#[arg(long)]
|
||||
/// The flake defining the system-manager profile
|
||||
flake_uri: Option<String>,
|
||||
|
||||
#[arg(long)]
|
||||
/// The store path containing the system-manager profile
|
||||
store_path: Option<StorePath>,
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Debug)]
|
||||
struct ActivationArgs {
|
||||
#[arg(long, action)]
|
||||
|
|
@ -45,7 +63,7 @@ enum Action {
|
|||
/// Generate a new system-manager generation
|
||||
Generate {
|
||||
#[command(flatten)]
|
||||
build_args: BuildArgs,
|
||||
generate_args: GenerateArgs,
|
||||
},
|
||||
/// Generate a new system-manager generation and activate it
|
||||
Switch {
|
||||
|
|
@ -57,42 +75,69 @@ enum Action {
|
|||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = Args::parse();
|
||||
|
||||
// FIXME: set default level to info
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
|
||||
handle_toplevel_error(go(args.action))
|
||||
handle_toplevel_error(go(Args::parse()))
|
||||
}
|
||||
|
||||
fn go(action: Action) -> Result<()> {
|
||||
fn go(args: Args) -> Result<()> {
|
||||
let Args {
|
||||
action,
|
||||
target_host,
|
||||
use_remote_sudo,
|
||||
} = args;
|
||||
|
||||
match action {
|
||||
Action::Activate {
|
||||
store_path,
|
||||
activation_args: ActivationArgs { ephemeral },
|
||||
} => {
|
||||
check_root()?;
|
||||
activate(store_path, ephemeral)
|
||||
// FIXME handle target_host
|
||||
copy_closure(&store_path, &target_host)?;
|
||||
activate(&store_path, ephemeral, &target_host, use_remote_sudo)
|
||||
}
|
||||
Action::Build {
|
||||
build_args: BuildArgs { flake_uri },
|
||||
} => build(flake_uri),
|
||||
Action::Deactivate => {
|
||||
check_root()?;
|
||||
// FIXME handle target_host
|
||||
deactivate()
|
||||
}
|
||||
Action::Generate {
|
||||
build_args: BuildArgs { flake_uri },
|
||||
} => {
|
||||
check_root()?;
|
||||
generate(flake_uri).map(|_| ())
|
||||
Action::Generate { generate_args } => {
|
||||
generate(generate_args, &target_host, use_remote_sudo)
|
||||
}
|
||||
Action::Switch {
|
||||
build_args: BuildArgs { flake_uri },
|
||||
activation_args: ActivationArgs { ephemeral },
|
||||
} => {
|
||||
check_root()?;
|
||||
let store_path = generate(flake_uri)?;
|
||||
activate(store_path, ephemeral)
|
||||
let store_path = do_build(flake_uri)?;
|
||||
copy_closure(&store_path, &target_host)?;
|
||||
do_generate(&store_path, &target_host, use_remote_sudo)?;
|
||||
activate(&store_path, ephemeral, &target_host, use_remote_sudo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate(args: GenerateArgs, target_host: &Option<String>, use_remote_sudo: bool) -> Result<()> {
|
||||
match args {
|
||||
GenerateArgs {
|
||||
flake_uri: Some(flake_uri),
|
||||
store_path: None,
|
||||
} => {
|
||||
let store_path = do_build(flake_uri)?;
|
||||
copy_closure(&store_path, target_host)?;
|
||||
do_generate(&store_path, target_host, use_remote_sudo)
|
||||
}
|
||||
GenerateArgs {
|
||||
flake_uri: None,
|
||||
store_path: Some(store_path),
|
||||
} => {
|
||||
copy_closure(&store_path, target_host)?;
|
||||
do_generate(&store_path, target_host, use_remote_sudo)
|
||||
}
|
||||
_ => {
|
||||
anyhow::bail!("Supply either a flake URI or a store path.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -109,18 +154,75 @@ fn do_build(flake_uri: String) -> Result<StorePath> {
|
|||
system_manager::generate::build(&flake_uri)
|
||||
}
|
||||
|
||||
fn generate(flake_uri: String) -> Result<StorePath> {
|
||||
system_manager::generate::generate(&flake_uri)
|
||||
fn do_generate(
|
||||
store_path: &StorePath,
|
||||
target_host: &Option<String>,
|
||||
use_remote_sudo: bool,
|
||||
) -> Result<()> {
|
||||
if let Some(target_host) = target_host {
|
||||
invoke_remote_script(store_path, "register-profile", target_host, use_remote_sudo)?;
|
||||
Ok(())
|
||||
} else {
|
||||
check_root()?;
|
||||
system_manager::generate::generate(store_path)
|
||||
}
|
||||
}
|
||||
|
||||
fn activate(store_path: StorePath, ephemeral: bool) -> Result<()> {
|
||||
system_manager::activate::activate(store_path, ephemeral)
|
||||
fn activate(
|
||||
store_path: &StorePath,
|
||||
ephemeral: bool,
|
||||
target_host: &Option<String>,
|
||||
use_remote_sudo: bool,
|
||||
) -> Result<()> {
|
||||
if let Some(target_host) = target_host {
|
||||
invoke_remote_script(store_path, "activate", target_host, use_remote_sudo)?;
|
||||
Ok(())
|
||||
} else {
|
||||
check_root()?;
|
||||
system_manager::activate::activate(store_path, ephemeral)
|
||||
}
|
||||
}
|
||||
|
||||
fn deactivate() -> Result<()> {
|
||||
system_manager::activate::deactivate()
|
||||
}
|
||||
|
||||
fn copy_closure(store_path: &StorePath, target_host: &Option<String>) -> Result<()> {
|
||||
target_host
|
||||
.as_ref()
|
||||
.map_or(Ok(()), |target| do_copy_closure(store_path, target))
|
||||
}
|
||||
|
||||
fn do_copy_closure(store_path: &StorePath, target_host: &str) -> Result<()> {
|
||||
process::Command::new("nix-copy-closure")
|
||||
.arg("--to")
|
||||
.arg(target_host)
|
||||
.arg("--use-substitutes")
|
||||
.arg(&store_path.store_path)
|
||||
.stdout(process::Stdio::inherit())
|
||||
.stderr(process::Stdio::inherit())
|
||||
.status()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn invoke_remote_script(
|
||||
store_path: &StorePath,
|
||||
script_name: &str,
|
||||
target_host: &str,
|
||||
use_remote_sudo: bool,
|
||||
) -> Result<ExitStatus> {
|
||||
let mut cmd = process::Command::new("ssh");
|
||||
cmd.arg(target_host).arg("--");
|
||||
if use_remote_sudo {
|
||||
cmd.arg("sudo");
|
||||
}
|
||||
cmd.arg(format!("{store_path}/bin/{script_name}"))
|
||||
.stdout(process::Stdio::inherit())
|
||||
.stderr(process::Stdio::inherit())
|
||||
.status()
|
||||
.map_err(anyhow::Error::from)
|
||||
}
|
||||
|
||||
fn check_root() -> Result<()> {
|
||||
if !nix::unistd::Uid::is_root(nix::unistd::getuid()) {
|
||||
anyhow::bail!("We need root permissions.")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue