vars: init #1668

Merged
clan-bot merged 1 commits from DavHau/clan-core:DavHau-dave into main 2024-06-27 14:13:39 +00:00
7 changed files with 206 additions and 2 deletions

View File

@ -26,6 +26,7 @@
devShells.default = pkgs.mkShell {
packages = [
select-shell
pkgs.nix-unit
pkgs.tea
# Better error messages than nix 2.18
pkgs.nixVersions.latest

View File

@ -51,6 +51,7 @@
./formatter.nix
./lib/flake-module.nix
./nixosModules/flake-module.nix
./nixosModules/clanCore/vars/flake-module.nix
./pkgs/flake-module.nix
./templates/flake-module.nix
];

View File

@ -30,7 +30,7 @@ in
};
# Run: nix-unit --extra-experimental-features flakes --flake .#legacyPackages.x86_64-linux.evalTests
legacyPackages.evalTests = import ./tests {
legacyPackages.evalTests-inventory = import ./tests {
inherit buildInventory;
clan-core = self;
};
@ -42,7 +42,7 @@ in
nix-unit --eval-store "$HOME" \
--extra-experimental-features flakes \
${inputOverrides} \
--flake ${self}#legacyPackages.${system}.evalTests
--flake ${self}#legacyPackages.${system}.evalTests-inventory
touch $out
'';

View File

@ -0,0 +1,16 @@
{ lib, ... }:
{
options.clan.core.vars = lib.mkOption {
internal = true;
description = ''
Generated Variables
Define generators that prompt for or generate variables like facts and secrets to store, deploy, and rotate them easily.
For example, generators can be used to:
- prompt the user for input, like passwords or host names
- generate secrets like private keys automatically when they are needed
- output multiple values like private and public keys simultaneously
'';
type = lib.types.submoduleWith { modules = [ ./interface.nix ]; };
};
}

View File

@ -0,0 +1,50 @@
{ lib, ... }:
let
eval =
module:
(lib.evalModules {
modules = [
../default.nix
module
];
}).config;
in
{
single_file_single_prompt =
let
config = eval {
clan.core.vars.generators.my_secret = {
files.password = { };
files.username.secret = false;
prompts.prompt1 = { };
script = ''
cp $prompts/prompt1 $files/password
'';
};
};
in
{
test_file_secret_by_default = {
expr = config.clan.core.vars.generators.my_secret.files.password.secret;
expected = true;
};
test_secret_value_access_raises_error = {
expr = config.clan.core.vars.generators.my_secret.files.password.value;
expectedError.type = "ThrownError";
expectedError.msg = "Cannot access value of secret file";
};
test_public_value_access = {
expr = config.clan.core.vars.generators.my_secret.files.username ? value;
expected = true;
};
# both secret and public values must provide a path
test_secret_has_path = {
expr = config.clan.core.vars.generators.my_secret.files.password ? path;
expected = true;
};
test_public_var_has_path = {
expr = config.clan.core.vars.generators.my_secret.files.username ? path;
expected = true;
};
};
}

View File

@ -0,0 +1,31 @@
{
self,
inputs,
lib,
...
}:
let
inputOverrides = builtins.concatStringsSep " " (
builtins.map (input: " --override-input ${input} ${inputs.${input}}") (builtins.attrNames inputs)
);
in
{
perSystem =
{ system, pkgs, ... }:
{
legacyPackages.evalTests-module-clan-vars = import ./eval-tests {
inherit lib;
clan-core = self;
};
checks.module-clan-vars-eval = pkgs.runCommand "tests" { nativeBuildInputs = [ pkgs.nix-unit ]; } ''
export HOME="$(realpath .)"
nix-unit --eval-store "$HOME" \
--extra-experimental-features flakes \
${inputOverrides} \
--flake ${self}#legacyPackages.${system}.evalTests-module-clan-vars
touch $out
'';
};
}

View File

@ -0,0 +1,105 @@
{ lib, ... }:
let
inherit (lib) mkOption;
inherit (lib.types)
attrsOf
bool
enum
listOf
str
submodule
;
options = lib.mapAttrs (_: mkOption);
subOptions = opts: submodule { options = options opts; };
in
{
options = options {
generators = {
type = attrsOf (subOptions {
dependencies = {
description = ''
A list of other generators that this generator depends on.
The output values of these generators will be available to the generator script as files.
For example, the file 'file1' of a dependency named 'dep1' will be available via $dependencies/dep1/file1.
'';
type = listOf str;
default = [ ];
};
files = {
description = ''
A set of files to generate.
The generator 'script' is expected to produce exactly these files under $out.
'';
type = attrsOf (subOptions {
secret = {
description = ''
Whether the file should be treated as a secret.
'';
type = bool;
default = true;
};
path = {
description = ''
The path to the file containing the content of the generated value.
This will be set automatically
'';
type = str;
readOnly = true;
};
value = {
description = ''
The content of the generated value.
Only available if the file is not secret.
'';
type = str;
default = throw "Cannot access value of secret file";
defaultText = "Throws error because the value of a secret file is not accessible";
};
});
};
prompts = {
description = ''
A set of prompts to ask the user for values.
Prompts are available to the generator script as files.
For example, a prompt named 'prompt1' will be available via $prompts/prompt1
'';
type = attrsOf (subOptions {
description = {
description = ''
The description of the prompted value
'';
type = str;
example = "SSH private key";
};
type = {
description = ''
The input type of the prompt.
The following types are available:
- hidden: A hidden text (e.g. password)
- line: A single line of text
- multiline: A multiline text
'';
type = enum [
"hidden"
"line"
"multiline"
];
default = "line";
};
});
};
script = {
description = ''
The script to run to generate the files.
The script will be run with the following environment variables:
- $dependencies: The directory containing the output values of all declared dependencies
- $out: The output directory to put the generated files
- $prompts: The directory containing the prompted values as files
The script should produce the files specified in the 'files' attribute under $out.
'';
type = str;
};
});
};
};
}