110 lines
3.6 KiB
Nix
110 lines
3.6 KiB
Nix
{ pkgs, lib }:
|
|
rec {
|
|
replaceSecrets = { userConfig, resultPath, generator }:
|
|
let
|
|
configWithTemplates = withReplacements userConfig;
|
|
|
|
nonSecretConfigFile = pkgs.writeText "${resultPath}.template" (generator configWithTemplates);
|
|
|
|
replacements = getReplacements userConfig;
|
|
in
|
|
replaceSecretsScript {
|
|
file = nonSecretConfigFile;
|
|
inherit resultPath replacements;
|
|
};
|
|
|
|
template = file: newPath: replacements: replaceSecretsScript { inherit file replacements; resultPath = newPath; };
|
|
replaceSecretsScript = { file, resultPath, replacements }:
|
|
let
|
|
templatePath = resultPath + ".template";
|
|
sedPatterns = lib.strings.concatStringsSep " " (lib.attrsets.mapAttrsToList (from: to: "-e \"s|${from}|${to}|\"") replacements);
|
|
in
|
|
''
|
|
set -euo pipefail
|
|
set -x
|
|
ln -fs ${file} ${templatePath}
|
|
rm -f ${resultPath}
|
|
${pkgs.gnused}/bin/sed ${sedPatterns} ${templatePath}
|
|
${pkgs.gnused}/bin/sed ${sedPatterns} ${templatePath} > ${resultPath}
|
|
'';
|
|
|
|
secretFileType = lib.types.submodule {
|
|
options = {
|
|
source = lib.mkOption {
|
|
type = lib.types.path;
|
|
description = "File containing the value.";
|
|
};
|
|
|
|
transform = lib.mkOption {
|
|
type = lib.types.raw;
|
|
description = "An optional function to transform the secret.";
|
|
default = null;
|
|
example = lib.literalExpression ''
|
|
v: "prefix-$${v}-suffix"
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
secretName = name:
|
|
"%SECRET${lib.strings.toUpper (lib.strings.concatMapStrings (s: "_" + s) name)}%";
|
|
|
|
withReplacements = attrs:
|
|
let
|
|
valueOrReplacement = name: value:
|
|
if !(builtins.isAttrs value && value ? "source")
|
|
then value
|
|
else secretName name;
|
|
in
|
|
mapAttrsRecursiveCond (v: ! v ? "source") valueOrReplacement attrs;
|
|
|
|
getReplacements = attrs:
|
|
let
|
|
addNameField = name: value:
|
|
if !(builtins.isAttrs value && value ? "source")
|
|
then value
|
|
else value // { name = name; };
|
|
|
|
secretsWithName = mapAttrsRecursiveCond (v: ! v ? "source") addNameField attrs;
|
|
|
|
allSecrets = collect (v: builtins.isAttrs v && v ? "source") secretsWithName;
|
|
|
|
t = { transform ? null, ... }: if isNull transform then x: x else transform;
|
|
|
|
genReplacement = secret:
|
|
lib.attrsets.nameValuePair (secretName secret.name) ((t secret) "$(cat ${toString secret.source})");
|
|
in
|
|
lib.attrsets.listToAttrs (map genReplacement allSecrets);
|
|
|
|
# Inspired lib.attrsets.mapAttrsRecursiveCond but also recurses on lists.
|
|
mapAttrsRecursiveCond =
|
|
# A function, given the attribute set the recursion is currently at, determine if to recurse deeper into that attribute set.
|
|
cond:
|
|
# A function, given a list of attribute names and a value, returns a new value.
|
|
f:
|
|
# Attribute set or list to recursively map over.
|
|
set:
|
|
let
|
|
recurse = path: val:
|
|
if builtins.isAttrs val && cond val
|
|
then lib.attrsets.mapAttrs (n: v: recurse (path ++ [n]) v) val
|
|
else if builtins.isList val && cond val
|
|
then lib.lists.imap0 (i: v: recurse (path ++ [(builtins.toString i)]) v) val
|
|
else f path val;
|
|
in recurse [] set;
|
|
|
|
# Like lib.attrsets.collect but also recurses on lists.
|
|
collect =
|
|
# Given an attribute's value, determine if recursion should stop.
|
|
pred:
|
|
# The attribute set to recursively collect.
|
|
attrs:
|
|
if pred attrs then
|
|
[ attrs ]
|
|
else if builtins.isAttrs attrs then
|
|
lib.lists.concatMap (collect pred) (lib.attrsets.attrValues attrs)
|
|
else if builtins.isList attrs then
|
|
lib.lists.concatMap (collect pred) attrs
|
|
else
|
|
[];
|
|
}
|