diff --git a/nix/host-modules/swarm-grafana.nix b/nix/host-modules/swarm-grafana.nix index c85b8f9c..4b5258a9 100644 --- a/nix/host-modules/swarm-grafana.nix +++ b/nix/host-modules/swarm-grafana.nix @@ -22,6 +22,7 @@ let tlsCfg = hyperhiveCfg.tls; autheliaCfg = hyperhiveCfg.swarm.authelia; vmCfg = hyperhiveCfg.swarm.victoriametrics; + vlCfg = hyperhiveCfg.swarm.victorialogs; swarmDomain = hyperhiveCfg.swarm.domain; caTrust = import ./lib/hive-ca-trust.nix { inherit lib tlsCfg gatewayCfg; }; @@ -32,6 +33,11 @@ let # than an error. datasourceUid = "swarm-victoriametrics"; + # The logs store's own uid. Separate from the metrics one and spelled once for + # the same reason: a dashboard panel names it, and a second literal would be + # free to drift into a panel that renders empty rather than erroring. + logsDatasourceUid = "swarm-victorialogs"; + # The shipped dashboards carry `@datasourceUid@` where a real deployment needs # the uid above. They are substituted here rather than committed with the # literal so the single binding stays single. @@ -174,6 +180,21 @@ in ''; }; + logsDatasourceUrl = lib.mkOption { + type = lib.types.str; + default = "http://127.0.0.1:${toString vlCfg.port}"; + defaultText = lib.literalExpression ''"http://127.0.0.1:''${toString services.hyperhive.swarm.victorialogs.port}"''; + description = '' + Where the provisioned logs datasource points. Same reasoning as + {option}`services.hyperhive.swarm.grafana.datasourceUrl`: the store + binds loopback, so a Grafana elsewhere could not reach it anyway. + + Provisioned unconditionally, like the metrics datasource — the store + being off is a deployment choice rather than a reason to withhold the + connection, and an operator whose logs live elsewhere sets this. + ''; + }; + oidc = { clientId = lib.mkOption { type = lib.types.str; @@ -211,8 +232,24 @@ in plugins = lib.mkOption { type = lib.types.listOf lib.types.package; - default = [ pkgs.grafanaPlugins.grafana-metricsdrilldown-app ]; - defaultText = lib.literalExpression "[ pkgs.grafanaPlugins.grafana-metricsdrilldown-app ]"; + default = [ + pkgs.grafanaPlugins.grafana-metricsdrilldown-app + # Reading the swarm's logs needs both halves: the datasource that can + # speak LogsQL, and the browser that makes an unfamiliar log stream + # explorable without writing a query first. + # + # ⚠️ The app's plugin id is `grafana-lokiexplore-app` and its name is + # "Grafana Logs Drilldown" — Grafana renamed the product and kept the + # id. Searching nixpkgs for "logsdrilldown" finds nothing. + pkgs.grafanaPlugins.victoriametrics-logs-datasource + pkgs.grafanaPlugins.grafana-lokiexplore-app + ]; + defaultText = lib.literalExpression '' + [ + pkgs.grafanaPlugins.grafana-metricsdrilldown-app + pkgs.grafanaPlugins.victoriametrics-logs-datasource + pkgs.grafanaPlugins.grafana-lokiexplore-app + ]''; example = lib.literalExpression "[ pkgs.grafanaPlugins.grafana-piechart-panel ]"; description = '' Grafana plugins to install, as packages. Declarative rather than @@ -608,6 +645,21 @@ in access = "proxy"; isDefault = true; } + { + name = "VictoriaLogs"; + # The plugin's own id, read from its `plugin.json` rather + # than guessed. A `type` Grafana does not recognise is + # provisioned without complaint and every query against it + # fails at use, far from anything naming this file. + type = "victoriametrics-logs-datasource"; + uid = logsDatasourceUid; + url = cfg.logsDatasourceUrl; + access = "proxy"; + # Exactly one datasource may claim this, and metrics has it: + # two defaults is a coin toss over which one an untyped panel + # gets. + isDefault = false; + } ]; };