From a39ef182d4942d7700c6ac3439fbdecaf5af823d Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 4 Jul 2024 17:03:39 +0200 Subject: [PATCH] cluster/services/ways: support multiple backends via consul services --- cluster/services/ways/host.nix | 89 +++++++++++++++++++-------- cluster/services/ways/options/way.nix | 25 +++++++- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/cluster/services/ways/host.nix b/cluster/services/ways/host.nix index 884fcf1..84bf0f4 100644 --- a/cluster/services/ways/host.nix +++ b/cluster/services/ways/host.nix @@ -1,37 +1,47 @@ -{ cluster, depot, lib, ... }: +{ cluster, config, depot, lib, pkgs, ... }: let inherit (depot.lib.meta) domain; externalWays = lib.filterAttrs (_: cfg: !cfg.internal) cluster.config.ways; + + consulServiceWays = lib.filterAttrs (_: cfg: cfg.useConsul) cluster.config.ways; + + consulHttpAddr = "${config.services.consul.extraConfig.addresses.http or "127.0.0.1"}:${toString (config.services.consul.extraConfig.ports.http or 8500)}"; in { - services.nginx.virtualHosts = lib.mapAttrs' (name: cfg: { - name = if cfg.internal then "${name}.internal.${domain}" else "${name}.${domain}"; - value = { ... }: { - imports = [ - cfg.extras - { - forceSSL = true; - enableACME = !cfg.internal; - useACMEHost = lib.mkIf cfg.internal "internal.${domain}"; - locations = lib.mkMerge [ - { - "/".proxyPass = cfg.target; - "${cfg.healthCheckPath}".extraConfig = "access_log off;"; - } - { - "/.well-known/ways/internal-health-check" = { - return = ''200 "INTERNAL_OK\n"''; - extraConfig = "access_log off;"; - }; - } - ]; - } - ]; - }; - }) cluster.config.ways; + services.nginx = { + virtualHosts = lib.mapAttrs' (name: cfg: { + name = if cfg.internal then "${name}.internal.${domain}" else "${name}.${domain}"; + value = { ... }: { + imports = [ + cfg.extras + { + forceSSL = true; + enableACME = !cfg.internal; + useACMEHost = lib.mkIf cfg.internal "internal.${domain}"; + locations = lib.mkMerge [ + { + "/".proxyPass = cfg.target; + "${cfg.healthCheckPath}".extraConfig = "access_log off;"; + } + { + "/.well-known/ways/internal-health-check" = { + return = ''200 "INTERNAL_OK\n"''; + extraConfig = "access_log off;"; + }; + } + ]; + } + ]; + }; + }) cluster.config.ways; + + appendHttpConfig = lib.mkIf (consulServiceWays != {}) '' + include /run/consul-template/nginx-ways-*.conf; + ''; + }; security.acme.certs = lib.mapAttrs' (name: cfg: { name = "${name}.${domain}"; @@ -41,6 +51,33 @@ in }; }) externalWays; + services.consul-template.instances.ways = lib.mkIf (consulServiceWays != {}) { + user = "nginx"; + group = "nginx"; + settings = { + consul.address = "http://${consulHttpAddr}"; + template = [ + { + source = let + upstreams = lib.mapAttrsToList (_: cfg: '' + upstream ${cfg.nginxUpstreamName} { + {{ range $i, $e := service "${cfg.consulService}~_agent" -}} + server {{ .Address }}:{{ .Port }}{{ if ne $i 0 }} backup{{ end }}; + {{end}} + } + '') consulServiceWays; + in pkgs.writeText "ways-upstreams.ctmpl" (lib.concatStringsSep "\n" upstreams); + destination = "/run/consul-template/nginx-ways-upstreams.conf"; + exec.command = [ + "${config.services.nginx.package}/bin/nginx" + "-s" "reload" + "-g" "pid /run/nginx/nginx.pid;" + ]; + } + ]; + }; + }; + consul.services.ways-proxy = { unit = "nginx"; mode = "external"; diff --git a/cluster/services/ways/options/way.nix b/cluster/services/ways/options/way.nix index 8561902..6bd3428 100644 --- a/cluster/services/ways/options/way.nix +++ b/cluster/services/ways/options/way.nix @@ -1,4 +1,4 @@ -{ lib, name, ... }: +{ lib, name, options, ... }: with lib; @@ -20,15 +20,38 @@ with lib; type = types.str; }; + consulService = mkOption { + type = types.str; + }; + healthCheckPath = mkOption { type = types.path; default = "/.well-known/ways/internal-health-check"; }; + useConsul = mkOption { + type = types.bool; + internal = true; + default = false; + }; + + nginxUpstreamName = mkOption { + type = types.str; + internal = true; + }; + extras = mkOption { description = "Extra configuration to pass to the nginx virtual host submodule."; type = types.deferredModule; default = {}; }; }; + + config = lib.mkMerge [ + (lib.mkIf options.consulService.isDefined { + useConsul = true; + nginxUpstreamName = "ways_upstream_${builtins.hashString "md5" options.consulService.value}"; + target = "http://${options.nginxUpstreamName.value}"; + }) + ]; }