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;
|
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" ''
|
activationScript = pkgs.writeShellScript "activate" ''
|
||||||
${system-manager}/bin/system-manager activate \
|
${system-manager}/bin/system-manager activate \
|
||||||
--store-path "$(realpath $(dirname ''${0}))" \
|
--store-path "$(dirname $(realpath $(dirname ''${0})))" \
|
||||||
"$@"
|
"$@"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
deactivationScript = pkgs.writeShellScript "deactivate" ''
|
deactivationScript = pkgs.writeShellScript "deactivate" ''
|
||||||
${system-manager}/bin/system-manager deactivate "$@"
|
${system-manager}/bin/system-manager deactivate "$@"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
linkFarmEntryFromDrv = drv: {
|
||||||
|
name = drv.name;
|
||||||
|
path = drv;
|
||||||
|
};
|
||||||
|
|
||||||
|
linkFarmBinEntryFromDrv = drv: {
|
||||||
|
name = "bin/${drv.name}";
|
||||||
|
path = drv;
|
||||||
|
};
|
||||||
in
|
in
|
||||||
returnIfNoAssertions (
|
returnIfNoAssertions (
|
||||||
pkgs.linkFarmFromDrvs "system-manager" [
|
pkgs.linkFarm "system-manager" [
|
||||||
servicesPath
|
(linkFarmEntryFromDrv servicesPath)
|
||||||
etcPath
|
(linkFarmEntryFromDrv etcPath)
|
||||||
activationScript
|
(linkFarmBinEntryFromDrv activationScript)
|
||||||
deactivationScript
|
(linkFarmBinEntryFromDrv deactivationScript)
|
||||||
|
(linkFarmBinEntryFromDrv registerProfileScript)
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,18 @@ use anyhow::Result;
|
||||||
|
|
||||||
use crate::StorePath;
|
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}");
|
log::info!("Activating system-manager profile: {store_path}");
|
||||||
if ephemeral {
|
if ephemeral {
|
||||||
log::info!("Running in ephemeral mode");
|
log::info!("Running in ephemeral mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
etc_files::activate(&store_path, ephemeral)?;
|
etc_files::activate(store_path, ephemeral)?;
|
||||||
services::activate(&store_path, ephemeral)?;
|
services::activate(store_path, ephemeral)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO should we also remove the GC root for the profile if it exists?
|
||||||
pub fn deactivate() -> Result<()> {
|
pub fn deactivate() -> Result<()> {
|
||||||
log::info!("Deactivating system-manager");
|
log::info!("Deactivating system-manager");
|
||||||
etc_files::deactivate()?;
|
etc_files::deactivate()?;
|
||||||
|
|
|
||||||
|
|
@ -14,28 +14,18 @@ struct NixBuildOutput {
|
||||||
outputs: HashMap<String, String>,
|
outputs: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate(flake_uri: &str) -> Result<StorePath> {
|
pub fn generate(store_path: &StorePath) -> Result<()> {
|
||||||
let store_path = build(flake_uri)?;
|
|
||||||
let profile_dir = Path::new(PROFILE_DIR);
|
let profile_dir = Path::new(PROFILE_DIR);
|
||||||
let profile_name = Path::new(PROFILE_NAME);
|
let profile_name = Path::new(PROFILE_NAME);
|
||||||
|
|
||||||
log::info!("Creating new generation from {store_path}");
|
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...");
|
log::info!("Registering GC root...");
|
||||||
create_gcroot(GCROOT_PATH, &profile_dir.join(profile_name))?;
|
create_gcroot(GCROOT_PATH, &profile_dir.join(profile_name))?;
|
||||||
|
|
||||||
log::info!("Done");
|
log::info!("Done");
|
||||||
Ok(store_path)
|
Ok(())
|
||||||
}
|
|
||||||
|
|
||||||
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 install_nix_profile(
|
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))
|
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> {
|
fn get_store_path(nix_build_result: process::Output) -> Result<StorePath> {
|
||||||
if nix_build_result.status.success() {
|
if nix_build_result.status.success() {
|
||||||
String::from_utf8(nix_build_result.stdout)
|
String::from_utf8(nix_build_result.stdout)
|
||||||
|
|
|
||||||
140
src/main.rs
140
src/main.rs
|
|
@ -1,4 +1,4 @@
|
||||||
use std::process::ExitCode;
|
use std::process::{self, ExitCode, ExitStatus};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
@ -10,6 +10,13 @@ use system_manager::StorePath;
|
||||||
struct Args {
|
struct Args {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
action: Action,
|
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)]
|
#[derive(clap::Args, Debug)]
|
||||||
|
|
@ -19,6 +26,17 @@ struct BuildArgs {
|
||||||
flake_uri: String,
|
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)]
|
#[derive(clap::Args, Debug)]
|
||||||
struct ActivationArgs {
|
struct ActivationArgs {
|
||||||
#[arg(long, action)]
|
#[arg(long, action)]
|
||||||
|
|
@ -45,7 +63,7 @@ enum Action {
|
||||||
/// Generate a new system-manager generation
|
/// Generate a new system-manager generation
|
||||||
Generate {
|
Generate {
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
build_args: BuildArgs,
|
generate_args: GenerateArgs,
|
||||||
},
|
},
|
||||||
/// Generate a new system-manager generation and activate it
|
/// Generate a new system-manager generation and activate it
|
||||||
Switch {
|
Switch {
|
||||||
|
|
@ -57,42 +75,69 @@ enum Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
let args = Args::parse();
|
|
||||||
|
|
||||||
// FIXME: set default level to info
|
// FIXME: set default level to info
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
|
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 {
|
match action {
|
||||||
Action::Activate {
|
Action::Activate {
|
||||||
store_path,
|
store_path,
|
||||||
activation_args: ActivationArgs { ephemeral },
|
activation_args: ActivationArgs { ephemeral },
|
||||||
} => {
|
} => {
|
||||||
check_root()?;
|
// FIXME handle target_host
|
||||||
activate(store_path, ephemeral)
|
copy_closure(&store_path, &target_host)?;
|
||||||
|
activate(&store_path, ephemeral, &target_host, use_remote_sudo)
|
||||||
}
|
}
|
||||||
Action::Build {
|
Action::Build {
|
||||||
build_args: BuildArgs { flake_uri },
|
build_args: BuildArgs { flake_uri },
|
||||||
} => build(flake_uri),
|
} => build(flake_uri),
|
||||||
Action::Deactivate => {
|
Action::Deactivate => {
|
||||||
check_root()?;
|
check_root()?;
|
||||||
|
// FIXME handle target_host
|
||||||
deactivate()
|
deactivate()
|
||||||
}
|
}
|
||||||
Action::Generate {
|
Action::Generate { generate_args } => {
|
||||||
build_args: BuildArgs { flake_uri },
|
generate(generate_args, &target_host, use_remote_sudo)
|
||||||
} => {
|
|
||||||
check_root()?;
|
|
||||||
generate(flake_uri).map(|_| ())
|
|
||||||
}
|
}
|
||||||
Action::Switch {
|
Action::Switch {
|
||||||
build_args: BuildArgs { flake_uri },
|
build_args: BuildArgs { flake_uri },
|
||||||
activation_args: ActivationArgs { ephemeral },
|
activation_args: ActivationArgs { ephemeral },
|
||||||
} => {
|
} => {
|
||||||
check_root()?;
|
let store_path = do_build(flake_uri)?;
|
||||||
let store_path = generate(flake_uri)?;
|
copy_closure(&store_path, &target_host)?;
|
||||||
activate(store_path, ephemeral)
|
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)
|
system_manager::generate::build(&flake_uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate(flake_uri: String) -> Result<StorePath> {
|
fn do_generate(
|
||||||
system_manager::generate::generate(&flake_uri)
|
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<()> {
|
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)
|
system_manager::activate::activate(store_path, ephemeral)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn deactivate() -> Result<()> {
|
fn deactivate() -> Result<()> {
|
||||||
system_manager::activate::deactivate()
|
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<()> {
|
fn check_root() -> Result<()> {
|
||||||
if !nix::unistd::Uid::is_root(nix::unistd::getuid()) {
|
if !nix::unistd::Uid::is_root(nix::unistd::getuid()) {
|
||||||
anyhow::bail!("We need root permissions.")
|
anyhow::bail!("We need root permissions.")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue