diff --git a/nix/default.nix b/nix/default.nix new file mode 100644 index 000000000..07f5a27 --- /dev/null +++ b/nix/default.nix @@ -0,0 +1,17 @@ +{ pkgs ? import {} }: + +let + result = pkgs.lib.evalModules { + args.pkgs = pkgs; + modules = [ + ./options.nix + ./legacy.nix + ]; + }; +in +builtins.foldl' (config: warnings: + if warnings == [] + then config + else + builtins.trace "WARNING: ${builtins.head warnings}" config +) result.config result.config.warnings diff --git a/nix/options.nix b/nix/options.nix new file mode 100644 index 000000000..ac92106 --- /dev/null +++ b/nix/options.nix @@ -0,0 +1,59 @@ +{ config, lib, ... }: + +with lib; +let + netOpts = { name, ... }: { + options = { + vlan = mkOption { + description = "VLAN tag number"; + type = types.int; + }; + subnet4 = mkOption { + description = "v.w.x.y/z"; + type = with types; nullOr string; + default = null; + }; + subnets6 = mkOption { + description = "IPv6 subnets w/o prefixlen (always 64)"; + type = with types; attrsOf string; + default = {}; + }; + }; + }; +in +{ + options.warnings = mkOption { + type = types.listOf types.str; + default = []; + internal = true; + }; + + options.site = { + net = mkOption { + default = {}; + type = with types; attrsOf (submodule netOpts); + }; + }; + + config.warnings = + let + findCollisions = getter: xs: + (builtins.foldl' ({ known, dup }: k: + let + ks = builtins.toString k; + in + if known ? ${ks} + then { inherit known; dup = dup ++ [ks]; } + else { known = known // { ${ks} = true; }; inherit dup; } + ) { + known = {}; dup = []; + } ( + concatMap getter (builtins.attrValues xs) + )).dup; + reportCollisions = name: getter: xs: + map (k: "Duplicate ${name}: ${k}") (findCollisions getter xs); + in + (reportCollisions "VLAN tag" (x: [x.vlan]) config.site.net) ++ + (reportCollisions "IPv4 subnet" (x: if x.subnet4 == null then [] else [x.subnet4]) config.site.net) ++ + (reportCollisions "IPv6 subnet" (x: builtins.attrValues x.subnets6) config.site.net); +} diff --git a/nix/salt-support/load-yaml.nix b/nix/salt-support/load-yaml.nix new file mode 100644 index 000000000..027b930 --- /dev/null +++ b/nix/salt-support/load-yaml.nix @@ -0,0 +1,23 @@ +{ pkgs ? import {} +, gpgKeyFile ? ../../salt-gpg.asc +}: + +path: +let + json = pkgs.runCommand "json-from-j2yaml" { + nativeBuildInputs = with pkgs; [ + gnupg + pythonPackages.j2cli ruby yaml2json + ]; + } '' + export GNUPGHOME=$(mktemp -d) + gpg --import ${gpgKeyFile} + + j2 ${path} > expanded.yaml + ruby ${./yaml-gpg.rb} expanded.yaml > decrypted.yaml + yaml2json < decrypted.yaml > $out + ''; +in +builtins.fromJSON ( + builtins.readFile json +) diff --git a/nix/salt-support/salt-pillar.nix b/nix/salt-support/salt-pillar.nix new file mode 100644 index 000000000..2ffa5dc --- /dev/null +++ b/nix/salt-support/salt-pillar.nix @@ -0,0 +1,9 @@ +{ pkgs ? import {} }: + +with pkgs.lib; +let + loadYaml = import ./load-yaml.nix { inherit pkgs; }; +in +builtins.foldl' (result: filename: + recursiveUpdate result (loadYaml filename) +) {} (filesystem.listFilesRecursive ../../salt-pillar) diff --git a/nix/salt-support/yaml-gpg.rb b/nix/salt-support/yaml-gpg.rb new file mode 100644 index 000000000..b141398 --- /dev/null +++ b/nix/salt-support/yaml-gpg.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby + +require 'yaml' + +def expand_gpg yaml + if yaml.is_a? Hash + yaml.transform_values { |value| expand_gpg value } + elsif yaml.is_a? Array + yaml.map { |value| expand_gpg value } + elsif yaml.is_a? String + if yaml =~ /^-----BEGIN PGP MESSAGE-----.+-----END PGP MESSAGE-----$/m + IO::popen("gpg --decrypt", "r+") do |gpg| + gpg.puts yaml + gpg.close_write + gpg.readlines.join "\n" + end + else + yaml + end + else + yaml + end +end + +ARGV.each do |filename| + yaml = YAML::load File::read(filename) + yaml = expand_gpg yaml + puts YAML::dump(yaml) +end