Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions lib/contracts/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{ lib, ... }:
let
inherit (lib) mkOption modules types;
inherit (types)
attrsOf
listOf
option
submodule
str
;
contractModule = mkOption {
type = submodule {
options = {
meta = mkOption {
description = ''
Useful information about the contract and its maintenance.
'';
type = submodule {
options = {
description = mkOption {
description = ''
Description of the contract.
'';
type = str;
};
maintainers = mkOption {
description = ''
Maintainers of the contract.
'';
type = listOf str;
};
};
};
};
input = mkOption {
description = ''
Input type of a contract.
'';
type = attrsOf option;
apply = modules.mkContract;
};
output = mkOption {
description = ''
Output type of a contract.
'';
type = attrsOf option;
apply = modules.mkContract;
};
behaviorTest = mkOption {
# The type should be more precise of course.
# There should actually be a NixOSTest type.
# And we can probably do something fancy with the `input` and `output` modules.
type = types.functionTo types.attrs;
};
};
};
};
in
# yields: attrsOf contractModule
lib.mapAttrs (_: path: modules.evalOption contractModule (import path { inherit lib; })) {
fileSecrets = ./file-secrets.nix;
}
134 changes: 134 additions & 0 deletions lib/contracts/file-secrets.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
{
lib,
...
}:
let
inherit (lib) mkOption types;
inherit (types) str;
in
{
meta = {
description = ''
Contract for secrets handling where a consumer requests a secret
and a provider provides it at runtime at a given file path.
'';
maintainers = with lib.maintainers; [
ibizaman
kiara
];
};

input = {
mode = mkOption {
description = ''
Mode the secret file must have.
'';
type = str;
default = "0400";
};

owner = mkOption {
description = ''
Linux user that must own the secret file.
'';
type = str;
};

group = mkOption {
description = ''
Linux group that must own the secret file.
'';
type = str;
};
};
output = {
path = mkOption {
type = str;
description = ''
Path to the file containing the secret generated out of band.

This path will exist after deploying to a target host,
it is not available through the nix store.
'';
};
};
behaviorTest =
{
name,
providerRoot,
extraModules ? [ ],
}:
{
name = "contracts_filesecrets_${name}";
nodes.machine =
{ config, ... }:
{
imports = extraModules;

options.test = {
owner = mkOption {
type = str;
default = "root";
};

group = mkOption {
type = str;
default = "root";
};

mode = mkOption {
type = str;
default = "0400";
};

content = mkOption {
type = str;
default = "a super secret secret!";
};
};

config = lib.mkMerge [
(lib.setAttrByPath providerRoot {
input = {
inherit (config.test) owner group mode;
};
})
(lib.mkIf (config.test.owner != "root") {
users.users.${config.test.owner}.isNormalUser = true;
})
(lib.mkIf (config.test.group != "root") {
users.groups.${config.test.group} = { };
})
];
};

testScript =
{ nodes, ... }:
let
cfg = nodes.machine;
inherit (lib.getAttrFromPath providerRoot nodes.machine) output;
in
''
owner = machine.succeed("stat -c '%U' ${output.path}").strip()
print(f"Got owner {owner}")
if owner != "${cfg.test.owner}":
raise Exception(f"Owner should be '${cfg.test.owner}' but got '{owner}'")

group = machine.succeed("stat -c '%G' ${output.path}").strip()
print(f"Got group {group}")
if group != "${cfg.test.group}":
raise Exception(f"Group should be '${cfg.test.group}' but got '{group}'")

mode = str(int(machine.succeed("stat -c '%a' ${output.path}").strip()))
print(f"Got mode {mode}")
wantedMode = str(int("${cfg.test.mode}"))
if mode != wantedMode:
raise Exception(f"Mode should be '{wantedMode}' but got '{mode}'")

content = machine.succeed("cat ${output.path}").strip()
print(f"Got content {content}")
if content != "${cfg.test.content}":
raise Exception(f"Content should be '${cfg.test.content}' but got '{content}'")
'';
};
}
3 changes: 3 additions & 0 deletions lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ let
options = callLibs ./options.nix;
types = callLibs ./types.nix;

# contracts
contracts = callLibs ./contracts;

# constants
licenses = callLibs ./licenses.nix;
sourceTypes = callLibs ./source-types.nix;
Expand Down
Loading
Loading