WIP: Generic activation program #9

Draft
ecoppens wants to merge 11 commits from ecoppens/colmena:main into main
7 changed files with 58 additions and 9 deletions
Showing only changes of commit 596a40020b - Show all commits

View file

@ -28,7 +28,7 @@ use super::{
host::Local as LocalHost,
key::{Key, UploadAt as UploadKeyAt},
ColmenaError, ColmenaResult, CopyDirection, CopyOptions, Hive, Host, NodeConfig, NodeName,
Profile, ProfileDerivation, RebootOptions,
Profile, ProfileDerivation, RebootOptions, StorePath,
};
/// A deployment.
@ -637,7 +637,18 @@ impl Deployment {
}
}
host.activate(&profile_r, arc_self.goal).await?;
let activation_program: &StorePath =
&arc_self.hive
.get_registry_config()
.await?
.systems
.get(target.config.system_type.as_ref().unwrap_or(&"nixos".to_owned()))
.expect(&format!("System type {} does not exists",
target.config.system_type.as_ref().unwrap_or(&"nixos".to_owned())
))
.activation_program;
host.activate(&profile_r, arc_self.goal, activation_program).await?;
job.success_with_message(arc_self.goal.success_str().to_string())?;

View file

@ -258,6 +258,12 @@ with builtins; rec {
type = types.functionTo types.unspecified;
default = _: {};
};
activationProgram = lib.mkOption {
description = mdDoc ''
Program to execute at activation time.
'';
type = types.path;
};
};
};
registryOptions = { lib, ... }: let

View file

@ -78,7 +78,12 @@ impl Host for Local {
Ok(())
}
async fn activate(&mut self, profile: &Profile, goal: Goal) -> ColmenaResult<()> {
async fn activate(
&mut self,
profile: &Profile,
goal: Goal,
activation_program: &StorePath,
) -> ColmenaResult<()> {
if !goal.requires_activation() {
return Err(ColmenaError::Unsupported);
}
@ -91,7 +96,9 @@ impl Host for Local {
}
let command = {
let activation_command = profile.activation_command(goal).unwrap();
let activation_command = profile
.activation_command(goal, activation_program)
.unwrap();
self.make_privileged_command(&activation_command)
};

View file

@ -128,6 +128,7 @@ pub trait Host: Send + Sync + std::fmt::Debug {
profile: &Profile,
goal: Goal,
copy_options: CopyOptions,
activation_program: Option<&StorePath>,
) -> ColmenaResult<()> {
self.copy_closure(
profile.as_store_path(),
@ -137,7 +138,12 @@ pub trait Host: Send + Sync + std::fmt::Debug {
.await?;
if goal.requires_activation() {
self.activate(profile, goal).await?;
self.activate(
profile,
goal,
activation_program.expect("Unknown activation program"),
)
.await?;
}
Ok(())
@ -171,7 +177,12 @@ pub trait Host: Send + Sync + std::fmt::Debug {
///
/// The profile must already exist on the host. You should probably use deploy instead.
#[allow(unused_variables)]
async fn activate(&mut self, profile: &Profile, goal: Goal) -> ColmenaResult<()> {
async fn activate(
&mut self,
profile: &Profile,
goal: Goal,
activation_program: &StorePath,
) -> ColmenaResult<()> {
Err(ColmenaError::Unsupported)
}

View file

@ -90,7 +90,12 @@ impl Host for Ssh {
Ok(())
}
async fn activate(&mut self, profile: &Profile, goal: Goal) -> ColmenaResult<()> {
async fn activate(
&mut self,
profile: &Profile,
goal: Goal,
activation_program: &StorePath,
) -> ColmenaResult<()> {
if !goal.requires_activation() {
return Err(ColmenaError::Unsupported);
}
@ -101,7 +106,9 @@ impl Host for Ssh {
self.run_command(set_profile).await?;
}
let activation_command = profile.activation_command(goal).unwrap();
let activation_command = profile
.activation_command(goal, activation_program)
.unwrap();
let v: Vec<&str> = activation_command.iter().map(|s| &**s).collect();
let command = self.ssh(&v);
self.run_command(command).await

View file

@ -101,6 +101,9 @@ pub struct MetaConfig {
pub struct SystemTypeConfig {
#[serde(rename = "supportsDeployment")]
pub supports_deployment: bool,
#[serde(rename = "activationProgram")]
pub activation_program: StorePath,
}
#[derive(Debug, Clone, Validate, Deserialize)]

View file

@ -26,7 +26,11 @@ impl Profile {
}
/// Returns the command to activate this profile.
pub fn activation_command(&self, goal: Goal) -> Option<Vec<String>> {
pub fn activation_command(
&self,
goal: Goal,
activation_program: &StorePath,
) -> Option<Vec<String>> {
if let Some(goal) = goal.as_str() {
let path = self.as_path().join("bin/switch-to-configuration");
let switch_to_configuration = path