swarm-ui: build authelia/grafana/metrics/logs links from swarm domains
The swarm-controller module builds the Authelia, Grafana, Metrics and Logs quick links from services.hyperhive.swarm.<service>.domain, on the controller's host, instead of each service module adding its entry only on the host that runs it. A controller whose swarm runs those services on other hosts lists them in its /api/links popover. Forge, matrix and bao links are not moved yet: forge waits on #4891, matrix and bao on whether their GUI gate becomes swarm-level. Refs #4885
This commit is contained in:
parent
f63ab954c9
commit
8628e0ecdd
7 changed files with 108 additions and 57 deletions
|
|
@ -700,18 +700,6 @@ in
|
|||
authelia = "127.0.0.1:${toString cfg.metricsPort}";
|
||||
};
|
||||
|
||||
# This swarm-ui quick-links entry, same guard as the vhost/DNS name
|
||||
# above (only the host actually running the container claims it —
|
||||
# see `services.hyperhive.swarm.controller.links`'s description for
|
||||
# the contribute-your-own-entry idiom).
|
||||
services.hyperhive.swarm.controller.links = [
|
||||
{
|
||||
label = "Authelia";
|
||||
icon = "🔑";
|
||||
url = "https://${cfg.domain}/";
|
||||
}
|
||||
];
|
||||
|
||||
# `server_name = authelia.domain`, all of `/` → authelia.
|
||||
#
|
||||
# ⚠️ The server name must be exactly `cfg.domain`, not a near-miss:
|
||||
|
|
|
|||
|
|
@ -18,6 +18,40 @@ let
|
|||
deployCfg = config.services.hyperhive.deploy;
|
||||
autheliaCfg = config.services.hyperhive.swarm.authelia;
|
||||
natsCfg = config.services.hyperhive.swarm.nats;
|
||||
swarmCfg = config.services.hyperhive.swarm;
|
||||
|
||||
# Quick links to swarm services, built from `swarm.*` options alone: every
|
||||
# host evaluates the same entries, whichever host runs each service. One
|
||||
# entry per service; reading a `deploy.*` option here would tie a link to
|
||||
# where its service runs.
|
||||
swarmServiceLinks =
|
||||
map
|
||||
(s: {
|
||||
inherit (s) label icon;
|
||||
url = "https://${s.domain}/";
|
||||
})
|
||||
[
|
||||
{
|
||||
label = "Authelia";
|
||||
icon = "🔑";
|
||||
inherit (swarmCfg.authelia) domain;
|
||||
}
|
||||
{
|
||||
label = "Grafana";
|
||||
icon = "📊";
|
||||
inherit (swarmCfg.grafana) domain;
|
||||
}
|
||||
{
|
||||
label = "Metrics";
|
||||
icon = "📈";
|
||||
inherit (swarmCfg.victoriametrics) domain;
|
||||
}
|
||||
{
|
||||
label = "Logs";
|
||||
icon = "📜";
|
||||
inherit (swarmCfg.victorialogs) domain;
|
||||
}
|
||||
];
|
||||
|
||||
# Where the secret store is, and whether this host holds the controller's
|
||||
# own leaf for it. ⚠️ The controller's pair, NOT `deploy.bao.clientCertFile`
|
||||
|
|
@ -411,21 +445,18 @@ in
|
|||
links menu (`GET /api/links`). Same shape and same
|
||||
zero-code-change-to-extend idea as `hyperhive.dashboardLinks`
|
||||
(`nix/agent-modules/dashboard-links.nix`), one level up: rather
|
||||
than one central hardcoded list, each service's own module
|
||||
contributes its own entry when it is actually enabled on this
|
||||
host — `swarm-authelia.nix`, `hive-matrix.nix` and
|
||||
`hive-forge/default.nix` all do — the same list-merge idiom
|
||||
`services.hyperhive.gateway.localNames` already uses. A future
|
||||
service module can push its own entry the same way, and an
|
||||
operator can add arbitrary extra entries here directly; neither
|
||||
needs a swarm-controller or swarm-ui change.
|
||||
than one fixed list, definitions merge with the list-merge idiom
|
||||
`services.hyperhive.gateway.localNames` uses, so an operator can
|
||||
add arbitrary extra entries here directly with no swarm-controller
|
||||
or swarm-ui change.
|
||||
|
||||
Only meaningful on the host that actually runs the controller —
|
||||
entries contributed on any other host are computed but never
|
||||
read. In a swarm that splits `swarm-authelia`/`hive-matrix`/
|
||||
`hive-forge` across hosts other than the controller's, this list
|
||||
only reflects what is enabled locally; see each contributing
|
||||
module's own activation condition.
|
||||
The Authelia, Grafana, Metrics and Logs entries come from
|
||||
`services.hyperhive.swarm.<service>.domain`, so they are present
|
||||
whichever host runs each service. `hive-matrix.nix`,
|
||||
`hive-forge/default.nix` and `swarm-ui.nix` contribute their own
|
||||
entries, and only where they are enabled on this host.
|
||||
|
||||
Read only on the host that runs the controller.
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
@ -643,6 +674,8 @@ in
|
|||
};
|
||||
|
||||
config = lib.mkIf deployCfg.swarm-controller.enable {
|
||||
services.hyperhive.swarm.controller.links = swarmServiceLinks;
|
||||
|
||||
users.users.swarm-controller = {
|
||||
isSystemUser = true;
|
||||
group = "swarm-controller";
|
||||
|
|
|
|||
|
|
@ -399,14 +399,6 @@ in
|
|||
services.hyperhive.gateway.enable = lib.mkDefault true;
|
||||
services.hyperhive.gateway.dns.enable = lib.mkDefault true;
|
||||
|
||||
services.hyperhive.swarm.controller.links = [
|
||||
{
|
||||
label = "Grafana";
|
||||
icon = "📊";
|
||||
url = "https://${cfg.domain}/";
|
||||
}
|
||||
];
|
||||
|
||||
# Registering the client is NOT here any more: it has to happen on the
|
||||
# host that runs authelia, and this whole block is gated on the host that
|
||||
# runs Grafana. ./glue-grafana-oidc-client.nix is where it moved to.
|
||||
|
|
|
|||
|
|
@ -112,14 +112,6 @@ in
|
|||
services.hyperhive.gateway.enable = lib.mkDefault true;
|
||||
services.hyperhive.gateway.dns.enable = lib.mkDefault true;
|
||||
|
||||
services.hyperhive.swarm.controller.links = [
|
||||
{
|
||||
label = "Logs";
|
||||
icon = "📜";
|
||||
url = "https://${cfg.domain}/";
|
||||
}
|
||||
];
|
||||
|
||||
# Authenticated front door onto the loopback-only store — see the
|
||||
# file-top comment for why this is safe to add without touching the
|
||||
# store's own (still unauthenticated, still loopback) listener at all.
|
||||
|
|
|
|||
|
|
@ -71,14 +71,6 @@ in
|
|||
services.hyperhive.gateway.enable = lib.mkDefault true;
|
||||
services.hyperhive.gateway.dns.enable = lib.mkDefault true;
|
||||
|
||||
services.hyperhive.swarm.controller.links = [
|
||||
{
|
||||
label = "Metrics";
|
||||
icon = "📈";
|
||||
url = "https://${cfg.domain}/";
|
||||
}
|
||||
];
|
||||
|
||||
# This store publishes its own health as prometheus metrics on the same
|
||||
# listener it serves queries on, so the swarm's collector can scrape it
|
||||
# with no exporter and no extra port.
|
||||
|
|
|
|||
|
|
@ -95,6 +95,34 @@ let
|
|||
deploy.forgejo.mirrors = [ aMirror ];
|
||||
};
|
||||
|
||||
# The controller with every shared service on another host, and the same
|
||||
# controller hosting them all: the swarm UI's links must not tell the two
|
||||
# apart.
|
||||
controllerAlone = hive { deploy.swarm-controller.enable = true; };
|
||||
controllerWithServices = hive {
|
||||
deploy.swarm-controller.enable = true;
|
||||
deploy.allSwarmServices = true;
|
||||
};
|
||||
|
||||
# Each shared service with a web UI, and the link it must have: the label
|
||||
# and the swarm-level domain the URL is built from.
|
||||
swarmServiceLinkDomains =
|
||||
let
|
||||
s = controllerAlone.services.hyperhive.swarm;
|
||||
in
|
||||
{
|
||||
Authelia = s.authelia.domain;
|
||||
Grafana = s.grafana.domain;
|
||||
Metrics = s.victoriametrics.domain;
|
||||
Logs = s.victorialogs.domain;
|
||||
# forge: added once hive-forge/default.nix drops its per-host link
|
||||
# matrix: added once its GUI gate is settled
|
||||
# bao: added once its UI gate is settled
|
||||
};
|
||||
swarmServiceLinksOf =
|
||||
cfg:
|
||||
lib.filter (l: swarmServiceLinkDomains ? ${l.label}) cfg.services.hyperhive.swarm.controller.links;
|
||||
|
||||
# Every service container at once: the services-only host plus the CI
|
||||
# runner, the one container on a netns of its own.
|
||||
serviceContainersWithCi = hive {
|
||||
|
|
@ -194,6 +222,23 @@ let
|
|||
&& !(swarmServicesOnly.systemd.sockets ? hive-priv)
|
||||
&& !(s ? swarm-bao-queue-agent);
|
||||
}
|
||||
{
|
||||
# On a controller that runs none of them, so the link cannot come from
|
||||
# the service's own module.
|
||||
name = "the swarm UI links every shared service with a web UI at its swarm domain";
|
||||
ok =
|
||||
lib.listToAttrs (map (l: lib.nameValuePair l.label l.url) (swarmServiceLinksOf controllerAlone))
|
||||
== lib.mapAttrs (_: domain: "https://${domain}/") swarmServiceLinkDomains;
|
||||
}
|
||||
{
|
||||
# Exact list equality also catches an entry rendered twice where the
|
||||
# service runs on the controller's own host.
|
||||
name = "the swarm UI's service links are the same whichever host runs the services";
|
||||
ok =
|
||||
lib.length (swarmServiceLinksOf controllerAlone)
|
||||
== lib.length (lib.attrNames swarmServiceLinkDomains)
|
||||
&& swarmServiceLinksOf controllerWithServices == swarmServiceLinksOf controllerAlone;
|
||||
}
|
||||
{
|
||||
# The in-container option drives the container's firewall, and host
|
||||
# `false` with container `true` would let the container's
|
||||
|
|
|
|||
Loading…
Reference in a new issue