From 48d9d3280f6b9ed22b2b3fcb5bff60e6b8e9663b Mon Sep 17 00:00:00 2001 From: alexion Date: Tue, 25 Aug 2026 22:29:56 -0400 Subject: [PATCH] feat(registry): validate unique addresses and routes --- flake.nix | 127 +++++++++++++++++++++++----------------- lib/registry.nix | 41 +++++++++---- registry/address.nix | 7 +++ registry/route.nix | 7 +++ registry/stable-uid.nix | 4 +- 5 files changed, 117 insertions(+), 69 deletions(-) diff --git a/flake.nix b/flake.nix index 4f02d49..49f56e3 100644 --- a/flake.nix +++ b/flake.nix @@ -403,6 +403,38 @@ touch $out ''; + address-registry-duplicate-value-fails = + let + alpha = lib.nixosSystem { + system = "x86_64-linux"; + specialArgs.my = self.lib; + modules = [ + self.lib.registry.module + { + registry.address.alpha.self = "10.23.50.146"; + system.stateVersion = "26.11"; + } + ]; + }; + beta = lib.nixosSystem { + system = "x86_64-linux"; + specialArgs.my = self.lib; + modules = [ + self.lib.registry.module + { + registry.address.beta.self = "10.23.50.146"; + system.stateVersion = "26.11"; + } + ]; + }; + registry = self.lib.registry.collect { hosts = { inherit alpha beta; }; }; + result = builtins.tryEval (builtins.deepSeq registry.address true); + in + pkgs.runCommand "address-registry-duplicate-value-fails-check" { } '' + ${lib.optionalString result.success "exit 1"} + touch $out + ''; + ssh-targets-use-address-registry = let neogaiaConfig = self.nixosConfigurations.neogaia.config; @@ -434,7 +466,7 @@ modules = [ self.lib.registry.module { - registry.address.routeHost.app = "10.23.20.126"; + registry.address.routeHost.app = "10.23.20.127"; registry.route.routeHost.app = { domain = "worker.alexion.dev"; port = 3923; @@ -472,7 +504,7 @@ registry.route ? routeHost && registry.route.routeHost ? app && registry.route.pikachu ? actualbudget - && vhosts."worker.alexion.dev".locations."/".proxyPass == "http://10.23.20.126:3923" + && vhosts."worker.alexion.dev".locations."/".proxyPass == "http://10.23.20.127:3923" && vhosts."budget.alexion.dev".locations."/".proxyPass == "http://10.23.20.42:5006" && vhosts."files.alexion.dev".locations."/".proxyPass == "http://10.23.20.126:3923" ) @@ -481,6 +513,44 @@ touch $out ''; + route-registry-duplicate-domain-fails = + let + alpha = lib.nixosSystem { + system = "x86_64-linux"; + specialArgs.my = self.lib; + modules = [ + self.lib.registry.module + { + registry.route.alpha.app = { + domain = "files.alexion.dev"; + port = 3923; + }; + system.stateVersion = "26.11"; + } + ]; + }; + beta = lib.nixosSystem { + system = "x86_64-linux"; + specialArgs.my = self.lib; + modules = [ + self.lib.registry.module + { + registry.route.beta.app = { + domain = "files.alexion.dev"; + port = 3923; + }; + system.stateVersion = "26.11"; + } + ]; + }; + registry = self.lib.registry.collect { hosts = { inherit alpha beta; }; }; + result = builtins.tryEval (builtins.deepSeq registry.route true); + in + pkgs.runCommand "route-registry-duplicate-domain-fails-check" { } '' + ${lib.optionalString result.success "exit 1"} + touch $out + ''; + reverse-proxy-address-backends = let workerHost = @@ -603,59 +673,6 @@ touch $out ''; - reverse-proxy-duplicate-domain-fails = - let - pikachu = lib.nixosSystem { - system = "x86_64-linux"; - specialArgs.my = self.lib; - modules = [ - self.lib.registry.module - { - registry.address.pikachu.copyparty = "10.23.20.126"; - registry.route.pikachu.copyparty = { - domain = "files.alexion.dev"; - port = 3923; - }; - system.stateVersion = "26.11"; - } - ]; - }; - raichu = lib.nixosSystem { - system = "x86_64-linux"; - specialArgs.my = self.lib; - modules = [ - self.lib.registry.module - { - registry.address.raichu.files = "10.23.20.10"; - registry.route.raichu.files = { - domain = "files.alexion.dev"; - port = 3923; - }; - system.stateVersion = "26.11"; - } - ]; - }; - registry = self.lib.registry.collect { hosts = { inherit pikachu raichu; }; }; - testConfig = lib.nixosSystem { - system = "x86_64-linux"; - specialArgs = { - my = self.lib; - inherit registry; - }; - modules = [ - ./modules/reverse-proxy.nix - { - modules.reverse-proxy.enable = true; - system.stateVersion = "26.11"; - } - ]; - }; - result = builtins.tryEval (builtins.deepSeq testConfig.config.services.nginx.virtualHosts true); - in - pkgs.runCommand "reverse-proxy-duplicate-domain-fails-check" { } '' - ${lib.optionalString result.success "exit 1"} - touch $out - ''; }; }; } diff --git a/lib/registry.nix b/lib/registry.nix index 2c00390..ee2cf30 100644 --- a/lib/registry.nix +++ b/lib/registry.nix @@ -16,26 +16,37 @@ let nameValuePair removeSuffix types + unique ; - assertionType = types.submodule { + checkType = types.submodule { options = { assertion = mkOption { - type = types.bool; + type = types.functionTo types.bool; description = '' - Whether this registry validation assertion passed. + Predicate run against this registry's flattened collected entries. ''; }; message = mkOption { type = types.str; description = '' - Error message shown when this registry validation assertion fails. + Error message shown when this registry validation check fails. ''; }; }; }; + predicates = { + uniqueValue = value: entries: builtins.length (unique (map value entries)) == builtins.length entries; + }; + + registryLib = lib // { + registry = (lib.registry or { }) // { + inherit predicates; + }; + }; + registryDefinitionModule = { options = { type = mkOption { @@ -53,11 +64,11 @@ let ''; }; - validate = mkOption { - type = types.functionTo (types.listOf assertionType); - default = _entries: [ ]; + checks = mkOption { + type = types.listOf checkType; + default = [ ]; description = '' - Validate this registry's flattened collected entries. + Validation checks for this registry's flattened collected entries. ''; }; }; @@ -76,7 +87,7 @@ let mkRegistry = path: let evaluated = lib.evalModules { - specialArgs = { inherit lib; }; + specialArgs.lib = registryLib; modules = [ registryDefinitionModule path @@ -84,7 +95,7 @@ let }; in { - inherit (evaluated.config) type replicate validate; + inherit (evaluated.config) type replicate checks; }; definitions = mapAttrs' ( @@ -149,7 +160,13 @@ let concatLists ( mapAttrsToList ( registryName: registry: - registry.validate (flattenRegistryEntries registryName data.${registryName}) + let + entries = flattenRegistryEntries registryName data.${registryName}; + in + map (check: { + assertion = check.assertion entries; + inherit (check) message; + }) registry.checks ) definitions ); @@ -189,7 +206,7 @@ let }; in { - inherit definitions; + inherit definitions predicates; module = { options.registry = mapAttrs (_name: registry: registryOption registry) definitions; diff --git a/registry/address.nix b/registry/address.nix index 8a6fd91..3f4eb65 100644 --- a/registry/address.nix +++ b/registry/address.nix @@ -1,4 +1,11 @@ { lib, ... }: { type = lib.types.str; + + checks = [ + { + assertion = lib.registry.predicates.uniqueValue (entry: entry.value); + message = "Address registry values must be unique."; + } + ]; } diff --git a/registry/route.nix b/registry/route.nix index a07f9dc..bee18a9 100644 --- a/registry/route.nix +++ b/registry/route.nix @@ -1,5 +1,12 @@ { lib, ... }: { + checks = [ + { + assertion = lib.registry.predicates.uniqueValue (entry: entry.value.domain); + message = "Route domains must be unique."; + } + ]; + type = lib.types.submodule { options = { domain = lib.mkOption { diff --git a/registry/stable-uid.nix b/registry/stable-uid.nix index bb29bd5..584c84a 100644 --- a/registry/stable-uid.nix +++ b/registry/stable-uid.nix @@ -22,9 +22,9 @@ in type = lib.types.ints.positive; - validate = entries: [ + checks = [ { - assertion = duplicateUids entries == { }; + assertion = entries: duplicateUids entries == { }; message = "Stable UID allocations must be unique."; } ];