nix: move the reader-after-policy edges into their own colocation glue
The four readers' ordering after their policy units only applies where the store and that reader share a host, so it is colocation glue and does not belong in the reader modules (two of which are main modules). glue-bao-readers-policy-order.nix now sets the after+wants edges, gated on deploy.bao.enable AND the reader's own gate, so a store host without a reader gains no stub unit.
This commit is contained in:
parent
cde3fec956
commit
0bfe354b6d
7 changed files with 127 additions and 42 deletions
|
|
@ -23,6 +23,7 @@
|
||||||
./hive-priv.nix
|
./hive-priv.nix
|
||||||
./hive-tls.nix
|
./hive-tls.nix
|
||||||
./otel.nix
|
./otel.nix
|
||||||
|
./glue-bao-readers-policy-order.nix
|
||||||
./glue-bao-tls.nix
|
./glue-bao-tls.nix
|
||||||
./glue-controller-bao-identity.nix
|
./glue-controller-bao-identity.nix
|
||||||
./glue-grafana-oidc-client.nix
|
./glue-grafana-oidc-client.nix
|
||||||
|
|
|
||||||
60
nix/host-modules/glue-bao-readers-policy-order.nix
Normal file
60
nix/host-modules/glue-bao-readers-policy-order.nix
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Glue: where the store and one of its readers share a host, the reader waits
|
||||||
|
# for the unit that writes the cert-auth role it logs in with.
|
||||||
|
#
|
||||||
|
# ONE PAIRING PER FILE — the store's four reader policy units ← the readers
|
||||||
|
# they grant, and nothing else. Deleting this leaves every reader as it is on a
|
||||||
|
# host whose store is remote: it may log in before its role exists, and its own
|
||||||
|
# retries are what carry it past that.
|
||||||
|
#
|
||||||
|
# ⚠️ Gated on BOTH the store and that reader being here. Off the store's host
|
||||||
|
# there is no local policy unit to order against. On the store's host without
|
||||||
|
# the reader, setting `systemd.services.<reader>.after` would define a unit
|
||||||
|
# with no ExecStart, so each gate below restates the one the reader's own
|
||||||
|
# module puts on it. A reader whose gate changes must change here too.
|
||||||
|
#
|
||||||
|
# Ordering, never a requirement: a policy unit skips once the bootstrap token
|
||||||
|
# is gone, and a skipped unit counts as done. `wants` as well as `after`, so a
|
||||||
|
# reader started on its own pulls its policy unit into the same transaction.
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
hyperhiveCfg = config.services.hyperhive;
|
||||||
|
deployCfg = hyperhiveCfg.deploy;
|
||||||
|
baoDeploy = deployCfg.bao;
|
||||||
|
|
||||||
|
havePair = cert: key: cert != null && key != null;
|
||||||
|
|
||||||
|
# Reader unit → the gate its own module defines it under.
|
||||||
|
readersHere = {
|
||||||
|
# ./glue-matrix-bao-token.nix
|
||||||
|
swarm-bao-matrix-token =
|
||||||
|
hyperhiveCfg.enable
|
||||||
|
&& havePair baoDeploy.matrixTokenClientCertFile baoDeploy.matrixTokenClientKeyFile
|
||||||
|
&& deployCfg.matrix.enable;
|
||||||
|
# ./glue-queue-agent-credential.nix
|
||||||
|
swarm-bao-queue-agent =
|
||||||
|
hyperhiveCfg.enable
|
||||||
|
&& havePair baoDeploy.queueAgentClientCertFile baoDeploy.queueAgentClientKeyFile;
|
||||||
|
# ./swarm-grafana.nix
|
||||||
|
swarm-bao-grafana-oidc = hyperhiveCfg.enable && deployCfg.grafana.enable;
|
||||||
|
# ./swarm-otel.nix
|
||||||
|
swarm-bao-otel-oidc =
|
||||||
|
deployCfg.swarm-otel.enable
|
||||||
|
&& havePair baoDeploy.otelOidcClientCertFile baoDeploy.otelOidcClientKeyFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
orderAfterPolicy =
|
||||||
|
reader: here:
|
||||||
|
lib.mkIf (baoDeploy.enable && here) {
|
||||||
|
systemd.services.${reader} = {
|
||||||
|
after = [ "${reader}-policy.service" ];
|
||||||
|
wants = [ "${reader}-policy.service" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkMerge (lib.mapAttrsToList orderAfterPolicy readersHere);
|
||||||
|
}
|
||||||
|
|
@ -136,19 +136,11 @@ in
|
||||||
# `Requires=` on an absent unit fails the job outright, so the ordering is
|
# `Requires=` on an absent unit fails the job outright, so the ordering is
|
||||||
# conditional even though the read is not: off-host there is nothing local
|
# conditional even though the read is not: off-host there is nothing local
|
||||||
# to wait for, and the timeout below is what bounds the attempt instead.
|
# to wait for, and the timeout below is what bounds the attempt instead.
|
||||||
#
|
|
||||||
# The policy unit writes the role this reader logs in with. Ordering
|
|
||||||
# only: it skips once the bootstrap token is gone, and a skipped unit
|
|
||||||
# counts as done.
|
|
||||||
after = lib.optionals baoDeploy.enable [
|
after = lib.optionals baoDeploy.enable [
|
||||||
"swarm-bao-pki.service"
|
"swarm-bao-pki.service"
|
||||||
"container@${baoCfg.machine}.service"
|
"container@${baoCfg.machine}.service"
|
||||||
"swarm-bao-matrix-token-policy.service"
|
|
||||||
];
|
|
||||||
wants = lib.optionals baoDeploy.enable [
|
|
||||||
"container@${baoCfg.machine}.service"
|
|
||||||
"swarm-bao-matrix-token-policy.service"
|
|
||||||
];
|
];
|
||||||
|
wants = lib.optionals baoDeploy.enable [ "container@${baoCfg.machine}.service" ];
|
||||||
requires = lib.optionals baoDeploy.enable [ "swarm-bao-pki.service" ];
|
requires = lib.optionals baoDeploy.enable [ "swarm-bao-pki.service" ];
|
||||||
before = [ "container@${matrixMachine}.service" ];
|
before = [ "container@${matrixMachine}.service" ];
|
||||||
wantedBy = [ "container@${matrixMachine}.service" ];
|
wantedBy = [ "container@${matrixMachine}.service" ];
|
||||||
|
|
|
||||||
|
|
@ -161,19 +161,11 @@ in
|
||||||
# `Requires=` on an absent unit fails the job outright, so the ordering is
|
# `Requires=` on an absent unit fails the job outright, so the ordering is
|
||||||
# conditional even though the read is not: off-host there is nothing local
|
# conditional even though the read is not: off-host there is nothing local
|
||||||
# to wait for, and the timeout below is what bounds the attempt instead.
|
# to wait for, and the timeout below is what bounds the attempt instead.
|
||||||
#
|
|
||||||
# The policy unit writes the role this reader logs in with. Ordering
|
|
||||||
# only: it skips once the bootstrap token is gone, and a skipped unit
|
|
||||||
# counts as done.
|
|
||||||
after = lib.optionals baoDeploy.enable [
|
after = lib.optionals baoDeploy.enable [
|
||||||
"swarm-bao-pki.service"
|
"swarm-bao-pki.service"
|
||||||
"container@${baoCfg.machine}.service"
|
"container@${baoCfg.machine}.service"
|
||||||
"swarm-bao-queue-agent-policy.service"
|
|
||||||
];
|
|
||||||
wants = lib.optionals baoDeploy.enable [
|
|
||||||
"container@${baoCfg.machine}.service"
|
|
||||||
"swarm-bao-queue-agent-policy.service"
|
|
||||||
];
|
];
|
||||||
|
wants = lib.optionals baoDeploy.enable [ "container@${baoCfg.machine}.service" ];
|
||||||
requires = lib.optionals baoDeploy.enable [ "swarm-bao-pki.service" ];
|
requires = lib.optionals baoDeploy.enable [ "swarm-bao-pki.service" ];
|
||||||
# Ordered before hive-c0re, so no agent container renders ahead of an
|
# Ordered before hive-c0re, so no agent container renders ahead of an
|
||||||
# attempt at its credential. `Wants=`, not `Requires=`: a store this
|
# attempt at its credential. `Wants=`, not `Requires=`: a store this
|
||||||
|
|
|
||||||
|
|
@ -583,19 +583,11 @@ in
|
||||||
# `Requires=` on an absent unit fails the job outright, so the ordering
|
# `Requires=` on an absent unit fails the job outright, so the ordering
|
||||||
# is conditional even though the read is not: off-host there is nothing
|
# is conditional even though the read is not: off-host there is nothing
|
||||||
# local to wait for, and the timeout below bounds the attempt instead.
|
# local to wait for, and the timeout below bounds the attempt instead.
|
||||||
#
|
|
||||||
# The policy unit writes the role this reader logs in with. Ordering
|
|
||||||
# only: it skips once the bootstrap token is gone, and a skipped unit
|
|
||||||
# counts as done.
|
|
||||||
after = lib.optionals baoDeploy.enable [
|
after = lib.optionals baoDeploy.enable [
|
||||||
"swarm-bao-pki.service"
|
"swarm-bao-pki.service"
|
||||||
"container@${baoCfg.machine}.service"
|
"container@${baoCfg.machine}.service"
|
||||||
"swarm-bao-grafana-oidc-policy.service"
|
|
||||||
];
|
|
||||||
wants = lib.optionals baoDeploy.enable [
|
|
||||||
"container@${baoCfg.machine}.service"
|
|
||||||
"swarm-bao-grafana-oidc-policy.service"
|
|
||||||
];
|
];
|
||||||
|
wants = lib.optionals baoDeploy.enable [ "container@${baoCfg.machine}.service" ];
|
||||||
requires = lib.optionals baoDeploy.enable [ "swarm-bao-pki.service" ];
|
requires = lib.optionals baoDeploy.enable [ "swarm-bao-pki.service" ];
|
||||||
before = [ "container@${cfg.machine}.service" ];
|
before = [ "container@${cfg.machine}.service" ];
|
||||||
wantedBy = [
|
wantedBy = [
|
||||||
|
|
|
||||||
|
|
@ -766,19 +766,11 @@ in
|
||||||
# `Requires=` on an absent unit fails the job outright, so the ordering
|
# `Requires=` on an absent unit fails the job outright, so the ordering
|
||||||
# is conditional even though the read is not: off-host there is nothing
|
# is conditional even though the read is not: off-host there is nothing
|
||||||
# local to wait for, and the timeout below bounds the attempt instead.
|
# local to wait for, and the timeout below bounds the attempt instead.
|
||||||
#
|
|
||||||
# The policy unit writes the role this reader logs in with. Ordering
|
|
||||||
# only: it skips once the bootstrap token is gone, and a skipped unit
|
|
||||||
# counts as done.
|
|
||||||
after = lib.optionals baoDeploy.enable [
|
after = lib.optionals baoDeploy.enable [
|
||||||
"swarm-bao-pki.service"
|
"swarm-bao-pki.service"
|
||||||
"container@${baoCfg.machine}.service"
|
"container@${baoCfg.machine}.service"
|
||||||
"swarm-bao-otel-oidc-policy.service"
|
|
||||||
];
|
|
||||||
wants = lib.optionals baoDeploy.enable [
|
|
||||||
"container@${baoCfg.machine}.service"
|
|
||||||
"swarm-bao-otel-oidc-policy.service"
|
|
||||||
];
|
];
|
||||||
|
wants = lib.optionals baoDeploy.enable [ "container@${baoCfg.machine}.service" ];
|
||||||
requires = lib.optionals baoDeploy.enable [ "swarm-bao-pki.service" ];
|
requires = lib.optionals baoDeploy.enable [ "swarm-bao-pki.service" ];
|
||||||
before = [ "container@${cfg.machine}.service" ];
|
before = [ "container@${cfg.machine}.service" ];
|
||||||
wantedBy = [
|
wantedBy = [
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,43 @@ let
|
||||||
deploy.swarm-otel.enable = true;
|
deploy.swarm-otel.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# The store with none of the four readers beside it. Grafana, the collector and
|
||||||
|
# the homeserver are simply off; the queue reader renders on any host holding
|
||||||
|
# its leaf, which ./glue-bao-tls.nix mints here, so that leaf is taken away.
|
||||||
|
baoGrantNoReaders = hive {
|
||||||
|
deploy.bao.enable = true;
|
||||||
|
deploy.bao.bootstrapTokenFile = "/run/secrets/bao-bootstrap.token";
|
||||||
|
deploy.bao.queueAgentClientCertFile = lib.mkForce null;
|
||||||
|
deploy.bao.queueAgentClientKeyFile = lib.mkForce null;
|
||||||
|
};
|
||||||
|
|
||||||
|
# All four readers against a store they do not run, each with a leaf placed
|
||||||
|
# by hand. The deployment in which there is no local policy unit to wait for.
|
||||||
|
baoRemoteReaders = hive {
|
||||||
|
deploy.matrix.enable = true;
|
||||||
|
deploy.grafana.enable = true;
|
||||||
|
deploy.swarm-otel.enable = true;
|
||||||
|
deploy.bao.clientCertFile = "/etc/pki/bao-client.pem";
|
||||||
|
deploy.bao.clientKeyFile = "/etc/pki/bao-client-key.pem";
|
||||||
|
deploy.bao.matrixTokenClientCertFile = "/etc/pki/bao-matrix-token.pem";
|
||||||
|
deploy.bao.matrixTokenClientKeyFile = "/etc/pki/bao-matrix-token-key.pem";
|
||||||
|
deploy.bao.queueAgentClientCertFile = "/etc/pki/bao-queue-agent.pem";
|
||||||
|
deploy.bao.queueAgentClientKeyFile = "/etc/pki/bao-queue-agent-key.pem";
|
||||||
|
deploy.bao.grafanaOidcClientCertFile = "/etc/pki/bao-grafana-oidc.pem";
|
||||||
|
deploy.bao.grafanaOidcClientKeyFile = "/etc/pki/bao-grafana-oidc-key.pem";
|
||||||
|
deploy.bao.otelOidcClientCertFile = "/etc/pki/bao-otel-oidc.pem";
|
||||||
|
deploy.bao.otelOidcClientKeyFile = "/etc/pki/bao-otel-oidc-key.pem";
|
||||||
|
};
|
||||||
|
|
||||||
|
# The four readers ./glue-bao-readers-policy-order.nix orders after their
|
||||||
|
# policy units.
|
||||||
|
policyReaders = [
|
||||||
|
"swarm-bao-matrix-token"
|
||||||
|
"swarm-bao-queue-agent"
|
||||||
|
"swarm-bao-grafana-oidc"
|
||||||
|
"swarm-bao-otel-oidc"
|
||||||
|
];
|
||||||
|
|
||||||
cases = [
|
cases = [
|
||||||
{
|
{
|
||||||
# Reads the rendered unit on the HOST, which is where the write happens:
|
# Reads the rendered unit on the HOST, which is where the write happens:
|
||||||
|
|
@ -455,12 +492,31 @@ let
|
||||||
&& lib.elem policy s.${reader}.wants
|
&& lib.elem policy s.${reader}.wants
|
||||||
&& !(lib.elem policy s.${reader}.requires);
|
&& !(lib.elem policy s.${reader}.requires);
|
||||||
in
|
in
|
||||||
lib.all waitsFor [
|
lib.all waitsFor policyReaders;
|
||||||
"swarm-bao-matrix-token"
|
}
|
||||||
"swarm-bao-queue-agent"
|
{
|
||||||
"swarm-bao-grafana-oidc"
|
# The ordering is set apart from each reader's own definition, so it can
|
||||||
"swarm-bao-otel-oidc"
|
# define a reader by itself: `after` on a unit nothing else declares is a
|
||||||
];
|
# unit with no ExecStart. On the store's host without the readers, none
|
||||||
|
# of the four may exist.
|
||||||
|
name = "a store host without the readers gains no reader unit from their ordering";
|
||||||
|
ok = lib.all (reader: !(baoGrantNoReaders.systemd.services ? ${reader})) policyReaders;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# Where the store is remote there is no policy unit here to wait for, so
|
||||||
|
# the readers render as they did before the ordering existed.
|
||||||
|
name = "a reader whose store is remote is not ordered after a policy unit";
|
||||||
|
ok =
|
||||||
|
let
|
||||||
|
s = baoRemoteReaders.systemd.services;
|
||||||
|
unordered =
|
||||||
|
reader:
|
||||||
|
let
|
||||||
|
policy = "${reader}-policy.service";
|
||||||
|
in
|
||||||
|
s ? ${reader} && !(lib.elem policy s.${reader}.after) && !(lib.elem policy s.${reader}.wants);
|
||||||
|
in
|
||||||
|
lib.all unordered policyReaders;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# A store host that has not placed a bootstrap token can write no grant at
|
# A store host that has not placed a bootstrap token can write no grant at
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue