git.delta.rocks / jrsonnet / refs/commits / 33f3601e24db

difftreelog

fix shared generator condition

ywyowvrkYaroslav Bolyukin2026-01-22parent: #45c49ea.patch.diff
in: trunk

10 files changed

modifiedcmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth
after · cmds/fleet/src/cmds/build_systems.rs
1use 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")118						.await119					{120						Ok(path) => path,121						Err(e) => {122							error!("failed to build host system closure: {:?}", e);123							return;124						}125					};126127					let deploy_kind = match host.deploy_kind().await {128						Ok(v) => v,129						Err(e) => {130							error!("failed to query target deploy kind: {e}");131							return;132						}133					};134135					// TODO: Make disable_rollback a host attribute instead136					let mut disable_rollback = self.disable_rollback;137					if !disable_rollback && deploy_kind != DeployKind::Fleet {138						warn!("disabling rollback, as not supported by non-fleet deployment kinds");139						disable_rollback = true;140					}141142					let remote_path =143						match upload_task(&config, &host, GenerationStorage::Deployer, built).await144						{145							Ok(v) => v,146							Err(e) => {147								error!("upload failed: {e}");148								return;149							}150						};151152					if let Err(e) = deploy_task(153						self.action,154						&host,155						remote_path,156						match opts.action_attr(&host, "specialisation").await {157							Ok(v) => v,158							_ => {159								error!("unreachable? failed to get specialization");160								return;161							}162						},163						disable_rollback,164					)165					.await166					{167						error!("activation failed: {e}");168					}169				})170				.instrument(span),171			);172		}173		set.await;174		Ok(())175	}176}
modifiedcmds/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())
 			}
modifiedcrates/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,
 }
 
modifiedcrates/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{}{}",
modifiedcrates/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"
modifiedlib/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,
modifiedlib/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
               (
modifiedmodules/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 = [
             {
modifiedmodules/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" ];
modifiedmodules/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;