difftreelog
fix shared generator condition
in: trunk
10 files changed
cmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth1use std::{env::current_dir, os::unix::fs::symlink, path::PathBuf};23use anyhow::Result;4use clap::Parser;5use fleet_base::{6 deploy::{DeployAction, deploy_task, upload_task},7 host::{Config, DeployKind, GenerationStorage},8 opts::FleetOpts,9};10use nix_eval::nix_go;11use tokio::task::{LocalSet, spawn_blocking};12use tracing::{Instrument, error, field, info, info_span, warn};1314#[derive(Parser)]15pub struct Deploy {16 /// Disable automatic rollback17 #[clap(long)]18 disable_rollback: bool,19 /// Action to execute after system is built20 action: DeployAction,21}2223#[derive(Parser, Clone)]24pub struct BuildSystems {25 /// Attribute to build. Systems are deployed from "toplevel-fleet" attr, well-known used attributes26 /// are "sdImage"/"isoImage", and your configuration may include any other build attributes.27 #[clap(long, default_value = "toplevel-fleet")]28 build_attr: String,29}3031async fn build_task(config: Config, hostname: String, build_attr: &str) -> Result<PathBuf> {32 info!("building");33 let host = config.host(&hostname).await?;34 // let action = Action::from(self.subcommand.clone());35 let nixos = host.nixos_config().await?;36 let drv = nix_go!(nixos.system.build[{ build_attr }]);37 let out_output = spawn_blocking(move || drv.build("out"))38 .await39 .expect("system derivation build should not panic")?;4041 // We already have system profiles for backups.42 if !host.local {43 info!("adding gc root");44 let mut cmd = config.local_host().cmd("nix").await?;45 cmd.arg("build")46 .comparg(47 "--profile",48 format!(49 "/nix/var/nix/profiles/{}-{hostname}",50 config.data().gc_root_prefix51 ),52 )53 .arg(&out_output);54 cmd.sudo().run_nix().await?;55 }5657 Ok(out_output)58}5960impl BuildSystems {61 pub async fn run(self, config: &Config, opts: &FleetOpts) -> Result<()> {62 let hosts = opts.filter_skipped(config.list_hosts().await?).await?;63 let set = LocalSet::new();64 let build_attr = self.build_attr.clone();65 for host in hosts {66 let config = config.clone();67 let span = info_span!("build", host = field::display(&host.name));68 let hostname = host.name;69 let build_attr = build_attr.clone();70 set.spawn_local(71 (async move {72 let built = match build_task(config, hostname.clone(), &build_attr).await {73 Ok(path) => path,74 Err(e) => {75 error!("failed to deploy host: {}", e);76 return;77 }78 };79 // TODO: Handle error80 let mut out = current_dir().expect("cwd exists");81 out.push(format!("built-{hostname}"));8283 info!("linking iso image to {:?}", out);84 if let Err(e) = symlink(built, out) {85 error!("failed to symlink: {e}")86 }87 })88 .instrument(span),89 );90 }91 set.await;92 Ok(())93 }94}9596impl Deploy {97 pub async fn run(self, config: &Config, opts: &FleetOpts) -> Result<()> {98 let hosts = opts.filter_skipped(config.list_hosts().await?).await?;99 let set = LocalSet::new();100 for host in hosts.into_iter() {101 let config = config.clone();102 let span = info_span!("deploy", host = field::display(&host.name));103 let hostname = host.name.clone();104 let opts = opts.clone();105 if let Some(deploy_kind) = opts.action_attr::<DeployKind>(&host, "deploy_kind").await? {106 host.set_deploy_kind(deploy_kind);107 };108 if let Some(destination) = opts.action_attr::<String>(&host, "dest").await? {109 host.set_session_destination(destination);110 };111 if let Some(legacy) = opts.action_attr::<bool>(&host, "legacy_ssh_store").await? {112 host.set_legacy_ssh_store(legacy);113 };114115 set.spawn_local(116 (async move {117 let built = match build_task(config.clone(), hostname.clone(), "toplevel-fleet").await118 {119 Ok(path) => path,120 Err(e) => {121 error!("failed to build host system closure: {:?}", e);122 return;123 }124 };125126 let deploy_kind = match host.deploy_kind().await {127 Ok(v) => v,128 Err(e) => {129 error!("failed to query target deploy kind: {e}");130 return;131 }132 };133134 // TODO: Make disable_rollback a host attribute instead135 let mut disable_rollback = self.disable_rollback;136 if !disable_rollback && deploy_kind != DeployKind::Fleet {137 warn!("disabling rollback, as not supported by non-fleet deployment kinds");138 disable_rollback = true;139 }140141 let remote_path =142 match upload_task(&config, &host, GenerationStorage::Deployer, built).await143 {144 Ok(v) => v,145 Err(e) => {146 error!("upload failed: {e}");147 return;148 }149 };150151 if let Err(e) = deploy_task(152 self.action,153 &host,154 remote_path,155 match opts.action_attr(&host, "specialisation").await {156 Ok(v) => v,157 _ => {158 error!("unreachable? failed to get specialization");159 return;160 }161 },162 disable_rollback,163 )164 .await165 {166 error!("activation failed: {e}");167 }168 })169 .instrument(span),170 );171 }172 set.await;173 Ok(())174 }175}cmds/fleet/src/cmds/secrets/mod.rsdiffbeforeafterboth--- a/cmds/fleet/src/cmds/secrets/mod.rs
+++ b/cmds/fleet/src/cmds/secrets/mod.rs
@@ -632,23 +632,23 @@
let config = config.clone();
let data = config.shared_secret(&name).expect("exists");
/*
- let definition = config.shared_secret_definition(&name)?;
- let expectations = definition.expectations()?;
- let owners = data
- .owners()
- .map(|o| {
- if expectations.owners.contains(o) {
- o.green().to_string()
- } else {
- o.red().to_string()
- }
- })
- .collect::<Vec<_>>();
- table.push(SecretDisplay {
- owners: owners.join(", "),
- name,
- })
-*/
+ let definition = config.shared_secret_definition(&name)?;
+ let expectations = definition.expectations()?;
+ let owners = data
+ .owners()
+ .map(|o| {
+ if expectations.owners.contains(o) {
+ o.green().to_string()
+ } else {
+ o.red().to_string()
+ }
+ })
+ .collect::<Vec<_>>();
+ table.push(SecretDisplay {
+ owners: owners.join(", "),
+ name,
+ })
+ */
}
// info!("loaded\n{}", Table::new(table).to_string())
}
crates/fleet-base/src/fleetdata.rsdiffbeforeafterboth--- a/crates/fleet-base/src/fleetdata.rs
+++ b/crates/fleet-base/src/fleetdata.rs
@@ -153,7 +153,7 @@
#[serde(flatten)]
pub secret: FleetSecretData,
- #[serde(default, skip_serializing, alias="managed")]
+ #[serde(default, skip_serializing, alias = "managed")]
pub _deprecated_managed: bool,
}
crates/nix-eval/src/util.rsdiffbeforeafterboth--- a/crates/nix-eval/src/util.rs
+++ b/crates/nix-eval/src/util.rs
@@ -1,15 +1,23 @@
use std::time::Instant;
use anyhow::bail;
+use serde::Deserialize;
use tracing::{debug, warn};
use crate::{Value, nix_go_json};
+#[derive(Deserialize, Debug)]
+struct Assertion {
+ assertion: bool,
+ message: String,
+}
+
#[tracing::instrument(level = "info", skip(val))]
pub async fn assert_warn(action: &str, val: &Value) -> anyhow::Result<()> {
let before_errors = Instant::now();
let errors: Vec<String> = nix_go_json!(val.errors);
- debug!("errors evaluation took {:?}", before_errors.elapsed());
+ // let assertions: Vec<Assertion> = nix_go_json!(val.assertions);
+ debug!("errors evaluation took {:?} {errors:?} ", before_errors.elapsed());
if !errors.is_empty() {
bail!(
"failed with error{}{}",
crates/nixlike/Cargo.tomldiffbeforeafterboth--- a/crates/nixlike/Cargo.toml
+++ b/crates/nixlike/Cargo.toml
@@ -7,10 +7,10 @@
[dependencies]
thiserror.workspace = true
+itertools = "0.14.0"
linked-hash-map = "0.5.6"
peg = "0.8.5"
ron = "0.11.0"
serde = { version = "1.0.219", features = ["derive"] }
serde-transcode = "1.1.1"
serde_json = "1.0.140"
-itertools = "0.14.0"
lib/default.nixdiffbeforeafterboth--- a/lib/default.nix
+++ b/lib/default.nix
@@ -150,7 +150,10 @@
);
mkAskPass =
- { prompt ? "Secret value", part ? "secret" }:
+ {
+ prompt ? "Secret value",
+ part ? "secret",
+ }:
(
{
kdePackages,
lib/flakePart.nixdiffbeforeafterboth--- a/lib/flakePart.nix
+++ b/lib/flakePart.nix
@@ -34,7 +34,7 @@
# to do that, evaluate all the modules with only needed option declared.
bootstrapEval = lib.evalModules {
class = "fleet";
- prefix = ["fleetConfiguration"];
+ prefix = [ "fleetConfiguration" ];
modules = [
module
{
@@ -53,7 +53,7 @@
bootstrapNixpkgs = bootstrapEval.config.nixpkgs.buildUsing;
normalEval = bootstrapNixpkgs.lib.evalModules {
class = "fleet";
- prefix = ["fleetConfiguration"];
+ prefix = [ "fleetConfiguration" ];
modules = (import ../modules/module-list.nix) ++ [
module
(
modules/nixos.nixdiffbeforeafterboth--- a/modules/nixos.nix
+++ b/modules/nixos.nix
@@ -39,13 +39,23 @@
in
config.nixpkgs.buildUsing.lib.evalModules {
class = "nixos";
- prefix = ["fleetConfiguration" "hosts" hostArgs.config._module.args.name "nixos"];
+ prefix = [
+ "fleetConfiguration"
+ "hosts"
+ hostArgs.config._module.args.name
+ "nixos"
+ ];
modules = (import "${modulesPath}/module-list.nix") ++ [
(module // { key = "attr<host.nixos>"; })
(config.nixos // { key = "attr<fleet.nixos>"; })
];
specialArgs = {
- inherit fleetLib inputs self modulesPath;
+ inherit
+ fleetLib
+ inputs
+ self
+ modulesPath
+ ;
};
};
};
@@ -54,32 +64,34 @@
};
};
config = {
- nixos = let
- inherit (hostArgs.config) system;
- in {
- _module.args = {
- nixosHosts = mapAttrs (_: value: value.nixos_unchecked.config) config.hosts;
- hosts = config.hosts;
- host = hostArgs.config;
- fleetConfiguration = config;
+ nixos =
+ let
+ inherit (hostArgs.config) system;
+ in
+ {
+ _module.args = {
+ nixosHosts = mapAttrs (_: value: value.nixos_unchecked.config) config.hosts;
+ hosts = config.hosts;
+ host = hostArgs.config;
+ fleetConfiguration = config;
- inputs' = mapAttrs (
- inputName: input:
- builtins.addErrorContext
- "while retrieving system-dependent attributes for input ${escapeNixIdentifier inputName}"
- (
- if input._type or null == "flake" then
- _fleetFlakeRootConfig.perInput system input
- else
- "input is not a flake, perhaps flake = false was added to te input declaration?"
- )
- ) inputs;
- self' = builtins.addErrorContext "while retrieving system-dependent attributes for a flake's own outputs" (
- _fleetFlakeRootConfig.perInput system self
- );
+ inputs' = mapAttrs (
+ inputName: input:
+ builtins.addErrorContext
+ "while retrieving system-dependent attributes for input ${escapeNixIdentifier inputName}"
+ (
+ if input._type or null == "flake" then
+ _fleetFlakeRootConfig.perInput system input
+ else
+ "input is not a flake, perhaps flake = false was added to te input declaration?"
+ )
+ ) inputs;
+ self' = builtins.addErrorContext "while retrieving system-dependent attributes for a flake's own outputs" (
+ _fleetFlakeRootConfig.perInput system self
+ );
+ };
+ nixpkgs.hostPlatform = system;
};
- nixpkgs.hostPlatform = system;
- };
nixos_unchecked = hostArgs.config.nixos.extendModules {
modules = [
{
modules/nixos/secrets.nixdiffbeforeafterboth--- a/modules/nixos/secrets.nix
+++ b/modules/nixos/secrets.nix
@@ -77,7 +77,7 @@
}:
let
secretName = config._module.args.name;
- literal = l: enum [l];
+ literal = l: enum [ l ];
in
{
options = {
@@ -109,17 +109,16 @@
config = {
# C api is broken in regard to thunks
# https://github.com/NixOS/nix/issues/12800
- parts = let
- hostName = host._module.args.name;
- generator = config.generator;
- in builtins.deepSeq [
- hostName
- secretName
- generator
- ] (builtins.fleetEnsureHostSecret
- hostName
- secretName
- generator);
+ parts =
+ let
+ hostName = host._module.args.name;
+ generator = config.generator;
+ in
+ builtins.deepSeq [
+ hostName
+ secretName
+ generator
+ ] (builtins.fleetEnsureHostSecret hostName secretName generator);
};
}
);
@@ -136,14 +135,16 @@
secrets = mkOption {
type = attrsOf secretType;
default = { };
- apply = mapAttrs (_: secret: secret.parts // {definition = secret;});
+ apply = mapAttrs (_: secret: secret.parts // { definition = secret; });
description = "Host-local secrets";
};
system.secretsData = mkOption {
type = unspecified;
- default = mapAttrs (_: s:
- (removeAttrs s.definition ["generator"]) // {
- parts = mapAttrs (_: part: removeAttrs part ["data"]) s.definition.parts;
+ default = mapAttrs (
+ _: s:
+ (removeAttrs s.definition [ "generator" ])
+ // {
+ parts = mapAttrs (_: part: removeAttrs part [ "data" ]) s.definition.parts;
}
) config.secrets;
description = "secrets.json contents";
@@ -152,13 +153,25 @@
config = {
environment.systemPackages = [ pkgs.fleet-install-secrets ];
- assertions = mapAttrsToList (name: secret: let
- hasSharedDefinition = fleetConfiguration.secrets ? name;
- in {
- assertion = (secret.definition.generator == "shared") == hasSharedDefinition && hasSharedDefinition -> (elem host._module.args.name fleetConfiguration.secrets.${name}.expectedOwners);
- message = if hasSharedDefinition then"secret ${name} has host-specific secret generator, secrets with host-specific generators can not have shared generator in fleet configuration"
- else "secret ${name} is declared as shared, for shared secret fleet configuration should include shared secret generator, and expectedOwners should contain this host";
- }) config.secrets;
+ assertions = mapAttrsToList (
+ name: secret:
+ let
+ hasSharedDefinition = fleetConfiguration.secrets ? ${name};
+ in
+ {
+ assertion =
+ (secret.definition.generator == "shared") == hasSharedDefinition
+ && (
+ hasSharedDefinition
+ -> (elem host._module.args.name fleetConfiguration.secrets.${name}.expectedOwners)
+ );
+ message =
+ if hasSharedDefinition then
+ "secret ${name} has host-specific secret generator, secrets with host-specific generators can not have shared generator in fleet configuration"
+ else
+ "secret ${name} is declared as shared, for shared secret fleet configuration should include shared secret generator, and expectedOwners should contain this host";
+ }
+ ) config.secrets;
systemd.services.fleet-install-secrets = mkIf useSysusers {
wantedBy = [ "sysinit.target" ];
modules/nixos/top-level.nixdiffbeforeafterboth--- a/modules/nixos/top-level.nix
+++ b/modules/nixos/top-level.nix
@@ -2,6 +2,7 @@
pkgs,
config,
lib,
+ ...
}:
let
inherit (lib.strings) optionalString;