From d16a13654c7ab4a84c21c57a7a7dd3dccc02c746 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Wed, 17 Feb 2021 21:08:31 -0800 Subject: [PATCH] Merge `nixpkgs.config` and `nixpkgs.overlays` This replaces #12, and allows for Nixpkgs overlays and config to be overridden in machine configs. With #12, overlays set in machine configurations (`nixpkgs.overlays`) get silently ignored. --- src/nix/eval.nix | 20 +++++- src/nix/tests.rs | 163 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) diff --git a/src/nix/eval.nix b/src/nix/eval.nix index 3b57f8f..9435f37 100644 --- a/src/nix/eval.nix +++ b/src/nix/eval.nix @@ -243,9 +243,28 @@ let in "Exactly one of `${prefix}.text`, `${prefix}.keyCommand` and `${prefix}.keyFile` must be set."; }) config.deployment.keys; }; + + # Here we need to merge the configurations in meta.nixpkgs + # and in machine config. + nixpkgsModule = { config, lib, ... }: { + nixpkgs.overlays = lib.mkBefore npkgs.overlays; + nixpkgs.config = lib.mkOptionDefault npkgs.config; + + # The merging of nixpkgs.config seems to be broken. + # Let's warn the user if not all config attributes set in + # meta.nixpkgs are overridden. + warnings = let + metaKeys = attrNames npkgs.config; + nodeKeys = attrNames config.nixpkgs.config; + remainingKeys = filter (k: ! elem k nodeKeys) metaKeys; + in + lib.optional (length remainingKeys != 0) + "The following Nixpkgs configuration keys set in meta.nixpkgs will be ignored: ${toString remainingKeys}"; + }; in evalConfig { modules = [ assertionModule + nixpkgsModule deploymentOptions hive.defaults config @@ -253,7 +272,6 @@ let specialArgs = { inherit name nodes; modulesPath = npkgs.path + "/nixos/modules"; - pkgs = npkgs; }; }; diff --git a/src/nix/tests.rs b/src/nix/tests.rs index 6750e5a..c1046c6 100644 --- a/src/nix/tests.rs +++ b/src/nix/tests.rs @@ -1,6 +1,7 @@ //! Integration-ish tests use super::*; +use crate::progress::TaskProgress; use std::collections::HashSet; use std::hash::Hash; @@ -56,6 +57,22 @@ impl TempHive { let hive = Self::new(text); assert!(block_on(hive.deployment_info()).is_err()); } + + /// Asserts that the specified nodes can be fully evaluated. + pub fn eval_success(text: &str, nodes: Vec) { + let hive = Self::new(text); + let progress = TaskProgress::new("tests".to_string(), 5); + let (profiles, _) = block_on(hive.eval_selected(&nodes, progress)); + assert!(profiles.is_ok()); + } + + /// Asserts that the specified nodes will fail to evaluate. + pub fn eval_failure(text: &str, nodes: Vec) { + let hive = Self::new(text); + let progress = TaskProgress::new("tests".to_string(), 5); + let (profiles, _) = block_on(hive.eval_selected(&nodes, progress)); + assert!(profiles.is_err()); + } } impl Deref for TempHive { @@ -210,3 +227,149 @@ fn test_parse_key_file() { } "#); } + +#[test] +fn test_eval_non_existent_pkg() { + // Sanity check + TempHive::eval_failure(r#" + { + test = { pkgs, ... }: { + boot.isContainer = true; + environment.systemPackages = with pkgs; [ thisPackageDoesNotExist ]; + }; + } + "#, vec![ "test".to_string() ]); +} + +// Nixpkgs config tests + +#[test] +fn test_nixpkgs_overlay_meta_nixpkgs() { + // Only set overlays in meta.nixpkgs + TempHive::eval_success(r#" + { + meta = { + nixpkgs = import { + overlays = [ + (self: super: { my-coreutils = super.coreutils; }) + ]; + }; + }; + test = { pkgs, ... }: { + boot.isContainer = true; + environment.systemPackages = with pkgs; [ my-coreutils ]; + }; + } + "#, vec![ "test".to_string() ]); +} + +#[test] +fn test_nixpkgs_overlay_node_config() { + // Only set overlays in node config + TempHive::eval_success(r#" + { + test = { pkgs, ... }: { + boot.isContainer = true; + nixpkgs.overlays = [ + (self: super: { my-coreutils = super.coreutils; }) + ]; + environment.systemPackages = with pkgs; [ my-coreutils ]; + }; + } + "#, vec![ "test".to_string() ]); +} + +#[test] +fn test_nixpkgs_overlay_both() { + // Set overlays both in meta.nixpkgs and in node config + TempHive::eval_success(r#" + { + meta = { + nixpkgs = import { + overlays = [ + (self: super: { meta-coreutils = super.coreutils; }) + ]; + }; + }; + test = { pkgs, ... }: { + boot.isContainer = true; + nixpkgs.overlays = [ + (self: super: { node-busybox = super.busybox; }) + ]; + environment.systemPackages = with pkgs; [ meta-coreutils node-busybox ]; + }; + } + "#, vec![ "test".to_string() ]); +} + +#[test] +fn test_nixpkgs_config_meta_nixpkgs() { + // Set config in meta.nixpkgs + TempHive::eval_success(r#" + { + meta = { + nixpkgs = import { + config = { + allowUnfree = true; + }; + }; + }; + test = { pkgs, ... }: { + boot.isContainer = assert pkgs.config.allowUnfree; true; + }; + } + "#, vec![ "test".to_string() ]); +} + +#[test] +fn test_nixpkgs_config_node_config() { + // Set config in node config + TempHive::eval_success(r#" + { + test = { pkgs, ... }: { + nixpkgs.config = { + allowUnfree = true; + }; + boot.isContainer = assert pkgs.config.allowUnfree; true; + }; + } + "#, vec![ "test".to_string() ]); +} + +#[test] +fn test_nixpkgs_config_override() { + // Set same config both in meta.nixpkgs and in node config + let template = r#" + { + meta = { + nixpkgs = import { + config = { + allowUnfree = META_VAL; + }; + }; + }; + test = { pkgs, ... }: { + nixpkgs.config = { + allowUnfree = NODE_VAL; + }; + boot.isContainer = assert pkgs.config.allowUnfree == EXPECTED_VAL; true; + }; + } + "#; + + TempHive::eval_success( + &template + .replace("META_VAL", "true") + .replace("NODE_VAL", "false") + .replace("EXPECTED_VAL", "false"), + vec![ "test".to_string() ] + ); + + TempHive::eval_success( + &template + .replace("META_VAL", "false") + .replace("NODE_VAL", "true") + .replace("EXPECTED_VAL", "true"), + vec![ "test".to_string() ] + ); +}