From ed018806d8bfbe6da0b5b21419123e698fdc676b Mon Sep 17 00:00:00 2001 From: alexion Date: Sun, 23 Aug 2026 23:44:22 -0400 Subject: [PATCH] refactor(reverse-proxy): use route registry --- flake.nix | 77 ++++++++++++++++-------------------- guests/reverse-proxy.nix | 4 +- lib/default.nix | 15 +++---- lib/guests.nix | 9 +---- lib/registry.nix | 5 +++ lib/reverse-proxy-routes.nix | 42 ++------------------ modules/reverse-proxy.nix | 36 ++++++++++++++--- registry/route.nix | 7 ++++ 8 files changed, 89 insertions(+), 106 deletions(-) create mode 100644 registry/route.nix diff --git a/flake.nix b/flake.nix index b3ddcf1..40cf9e5 100644 --- a/flake.nix +++ b/flake.nix @@ -202,7 +202,7 @@ { modules.reverse-proxy = { enable = true; - routes."budget.alexion.dev" = { + routes.test."budget.alexion.dev" = { path = "/"; backend = "http://10.23.20.42:5006"; }; @@ -224,7 +224,7 @@ reverse-proxy-actualbudget-guest-route = let testConfig = self.nixosConfigurations.pikachu.config; - route = testConfig.modules.reverse-proxy.routes.actualbudget; + route = testConfig.registry.route.pikachu.actualbudget; vhost = testConfig.containers.reverse-proxy.config.services.nginx.virtualHosts."budget.alexion.dev"; in pkgs.runCommand "reverse-proxy-actualbudget-guest-route-check" { } '' @@ -233,7 +233,6 @@ !( route.host == "budget.alexion.dev" && route.backend == null - && route.endpoint == "pikachu-actualbudget" && route.port == 5006 && route.tls && route.aliases == [ ] @@ -361,25 +360,25 @@ system = "x86_64-linux"; specialArgs.my = self.lib; modules = [ - ./modules/reverse-proxy.nix + self.lib.registry.module { - modules.reverse-proxy.routes.app = { + registry.route.routeHost.app = { host = "worker.alexion.dev"; backend = "http://10.23.20.126:3923"; }; + system.stateVersion = "26.11"; } ]; }; - endpoints = self.lib.networkEndpoints.collectFleetEndpoints { - pikachu = self.nixosConfigurations.pikachu; + registry = self.lib.registry.collect { + hosts = { + inherit routeHost; + pikachu = self.nixosConfigurations.pikachu; + }; }; - fleetRoutes = self.lib.reverseProxyRoutes.collectFleetRoutes { - inherit routeHost; - pikachu = self.nixosConfigurations.pikachu; - } endpoints; testConfig = (self.nixosConfigurations.pikachu.extendModules { - specialArgs.reverseProxyFleetRoutes = fleetRoutes; + specialArgs.registry = registry; }).config; vhosts = testConfig.containers.reverse-proxy.config.services.nginx.virtualHosts; in @@ -387,8 +386,9 @@ ${ lib.optionalString ( !( - fleetRoutes ? routeHost-app - && fleetRoutes ? pikachu-actualbudget + registry.route ? routeHost + && registry.route.routeHost ? app + && registry.route.pikachu ? actualbudget && vhosts."worker.alexion.dev".locations."/".proxyPass == "http://10.23.20.126: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" @@ -398,9 +398,9 @@ touch $out ''; - reverse-proxy-endpoint-backends = + reverse-proxy-address-backends = let - endpointHost = + workerHost = lib.nixosSystem { system = "x86_64-linux"; specialArgs.my = self.lib; @@ -408,22 +408,11 @@ self.lib.registry.module { registry.address.worker.self = "10.23.50.146"; - system.stateVersion = "26.11"; - } - ]; - }; - routeHost = - lib.nixosSystem { - system = "x86_64-linux"; - specialArgs.my = self.lib; - modules = [ - ./modules/reverse-proxy.nix - { - modules.reverse-proxy.routes.app = { + registry.route.worker.self = { host = "worker.alexion.dev"; - endpoint = "worker"; port = 8080; }; + system.stateVersion = "26.11"; } ]; }; @@ -454,36 +443,40 @@ } ]; }; - endpoints = self.lib.networkEndpoints.collectFleetEndpoints { - worker = endpointHost; - pikachu = guestRouteHost; + registry = self.lib.registry.collect { + hosts = { + worker = workerHost; + pikachu = guestRouteHost; + }; }; - fleetRoutes = self.lib.reverseProxyRoutes.collectFleetRoutes { - inherit routeHost; - pikachu = guestRouteHost; - } endpoints; testConfig = (lib.nixosSystem { system = "x86_64-linux"; - specialArgs.my = self.lib; + specialArgs = { + my = self.lib; + inherit registry; + }; modules = [ ./modules/reverse-proxy.nix { modules.reverse-proxy = { enable = true; - routes = fleetRoutes; + routes = registry.route; }; } ]; }).config; vhosts = testConfig.services.nginx.virtualHosts; + workerFacts = self.lib.registry.at registry "worker" "self"; in - pkgs.runCommand "reverse-proxy-endpoint-backends-check" { } '' + pkgs.runCommand "reverse-proxy-address-backends-check" { } '' ${ lib.optionalString ( !( - vhosts."worker.alexion.dev".locations."/".proxyPass == "http://10.23.50.146:8080" - && fleetRoutes."pikachu-files".endpoint == "pikachu-files" + workerFacts.address == "10.23.50.146" + && workerFacts.route.host == "worker.alexion.dev" + && vhosts."worker.alexion.dev".locations."/".proxyPass == "http://10.23.50.146:8080" + && registry.route.pikachu.files.port == 3923 && vhosts."files.alexion.dev".locations."/".proxyPass == "http://10.23.20.126:3923" ) ) "exit 1" @@ -502,7 +495,7 @@ { modules.reverse-proxy = { enable = true; - routes.raichu-files = { + routes.raichu.files = { host = "files.alexion.dev"; path = "/raichu"; backend = "http://10.23.20.10:3923"; diff --git a/guests/reverse-proxy.nix b/guests/reverse-proxy.nix index 03d7cca..2e0adda 100644 --- a/guests/reverse-proxy.nix +++ b/guests/reverse-proxy.nix @@ -2,7 +2,7 @@ config, lib, my, - reverseProxyFleetRoutes ? config.modules.reverse-proxy.routes, + registry, ... }: let @@ -35,7 +35,7 @@ in interior = { modules.reverse-proxy = { enable = true; - routes = reverseProxyFleetRoutes // legacyRoutes; + routes = registry.route // { legacy = legacyRoutes; }; }; networking.firewall.allowedTCPPorts = [ 2022 ]; diff --git a/lib/default.nix b/lib/default.nix index ba3a9f5..cde9ddc 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -35,16 +35,11 @@ let ) (builtins.readDir dir) ); - specialArgs = - let - networkFleetEndpoints = self.lib.networkEndpoints.collectFleetEndpoints self.nixosConfigurations; - in - { - inherit inputs networkFleetEndpoints self; - my = self.lib; - registry = self.lib.registry.collect { hosts = self.nixosConfigurations; }; - reverseProxyFleetRoutes = self.lib.reverseProxyRoutes.collectFleetRoutes self.nixosConfigurations networkFleetEndpoints; - }; + specialArgs = { + inherit inputs self; + my = self.lib; + registry = self.lib.registry.collect { hosts = self.nixosConfigurations; }; + }; bridgeName = id: "br-vlan${toString id}"; diff --git a/lib/guests.nix b/lib/guests.nix index 1750756..7e3b054 100644 --- a/lib/guests.nix +++ b/lib/guests.nix @@ -279,13 +279,8 @@ let registry.address.${config.networking.hostName}.${machineName} = lib.mkIf (cfg.address != null) cfg.address; - modules.reverse-proxy.routes.${machineName} = lib.mkIf cfg.reverseProxy.enable ( - reverseProxyRoutes.routeFields ( - cfg.reverseProxy - // lib.optionalAttrs (cfg.reverseProxy.backend == null && cfg.reverseProxy.endpoint == null) { - endpoint = "${config.networking.hostName}-${machineName}"; - } - ) + registry.route.${config.networking.hostName}.${machineName} = lib.mkIf cfg.reverseProxy.enable ( + reverseProxyRoutes.routeFields cfg.reverseProxy ); assertions = [ diff --git a/lib/registry.nix b/lib/registry.nix index d43d6f4..d9b60ac 100644 --- a/lib/registry.nix +++ b/lib/registry.nix @@ -32,6 +32,11 @@ in { inherit definitions; + at = registry: hostName: entryName: + filterAttrs (_name: value: value != null) ( + mapAttrs (_name: hostEntriesByName: (hostEntriesByName.${hostName} or { }).${entryName} or null) registry + ); + module = { options.registry = mapAttrs (_name: registry: registryOption registry) definitions; }; diff --git a/lib/reverse-proxy-routes.nix b/lib/reverse-proxy-routes.nix index 4a40a7d..c81c6f2 100644 --- a/lib/reverse-proxy-routes.nix +++ b/lib/reverse-proxy-routes.nix @@ -42,23 +42,17 @@ let description = "Full HTTP backend URL that receives proxied traffic."; }; - endpoint = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - description = "Fleet network endpoint key used to derive the backend URL."; - }; - port = lib.mkOption { type = lib.types.nullOr (lib.types.ints.between 1 65535); default = null; example = 3923; - description = "Backend TCP port used with the fleet network endpoint."; + description = "Backend TCP port used with the matching address registry entry."; }; scheme = lib.mkOption { type = lib.types.str; default = "http"; - description = "Backend URL scheme used with the fleet network endpoint."; + description = "Backend URL scheme used with the matching address registry entry."; }; tls = lib.mkOption { @@ -80,45 +74,15 @@ let aliases path backend - endpoint port scheme tls websockets ; }; - - endpointBackend = endpoints: route: - let - endpoint = endpoints.${route.endpoint} or ( - throw "Unknown reverse-proxy endpoint `${route.endpoint}`. Known endpoints: ${lib.concatStringsSep ", " (lib.attrNames endpoints)}" - ); - in - "${route.scheme}://${endpoint.address}:${toString route.port}"; - - resolvedRouteFields = endpoints: route: - let - routeWithBackend = - if route.backend != null then - route - else if route.endpoint != null && route.port != null then - route // { backend = endpointBackend endpoints route; } - else - throw "Reverse-proxy route `${route.host}${route.path}` needs either backend or endpoint plus port."; - in - routeFields routeWithBackend; - - collectFleetRoutes = - hosts: endpoints: - lib.concatMapAttrs ( - hostName: host: - lib.mapAttrs' ( - routeName: route: lib.nameValuePair "${hostName}-${routeName}" (resolvedRouteFields endpoints route) - ) host.config.modules.reverse-proxy.routes - ) hosts; in { - inherit collectFleetRoutes mkRouteOptions resolvedRouteFields routeFields; + inherit mkRouteOptions routeFields; routeType = lib.types.submodule ( { name, ... }: diff --git a/modules/reverse-proxy.nix b/modules/reverse-proxy.nix index aaae1d9..62bccb2 100644 --- a/modules/reverse-proxy.nix +++ b/modules/reverse-proxy.nix @@ -1,10 +1,34 @@ -{ config, lib, my, ... }: +{ + config, + lib, + my, + registry ? config.registry, + ... +}: let cfg = config.modules.reverse-proxy; inherit (my) reverseProxyRoutes; - enabledRoutes = lib.filterAttrs (_: route: route.enable) cfg.routes; - routesByHost = lib.groupBy (route: route.host) (lib.attrValues enabledRoutes); + routeWithBackend = hostName: routeName: route: + let + facts = my.registry.at registry hostName routeName; + in + if route.backend != null then + route + else if facts ? address && route.port != null then + route // { backend = "${route.scheme}://${facts.address}:${toString route.port}"; } + else + route; + + allRoutes = lib.concatLists ( + lib.mapAttrsToList ( + hostName: routes: + lib.mapAttrsToList (routeName: route: routeWithBackend hostName routeName route) routes + ) cfg.routes + ); + + enabledRoutes = builtins.filter (route: route.enable) allRoutes; + routesByHost = lib.groupBy (route: route.host) enabledRoutes; rootRoutesByHost = lib.mapAttrs ( host: routes: @@ -47,9 +71,9 @@ in enable = lib.mkEnableOption "the Nginx reverse proxy route realizer"; routes = lib.mkOption { - type = lib.types.attrsOf reverseProxyRoutes.routeType; + type = lib.types.attrsOf (lib.types.attrsOf reverseProxyRoutes.routeType); default = { }; - description = "Reverse-proxy route declarations keyed by stable route name."; + description = "Reverse-proxy route declarations keyed by source host and stable route name."; }; }; @@ -66,7 +90,7 @@ in ++ map (route: { assertion = !hasUnresolvedBackend route; message = "modules.reverse-proxy.routes for ${route.host}${route.path} must resolve to a backend URL before actualization."; - }) (builtins.filter hasUnresolvedBackend (lib.attrValues enabledRoutes)); + }) (builtins.filter hasUnresolvedBackend enabledRoutes); networking.firewall.allowedTCPPorts = [ 80 diff --git a/registry/route.nix b/registry/route.nix new file mode 100644 index 0000000..222cfc2 --- /dev/null +++ b/registry/route.nix @@ -0,0 +1,7 @@ +{ lib }: +let + reverseProxyRoutes = import ../lib/reverse-proxy-routes.nix { inherit lib; }; +in +{ + type = reverseProxyRoutes.routeType; +}