builder.nix's bare, servicesHere and natsHere were exact copies of shared.bareWithController, swarm-services-switch's swarmServicesHere and nats-authelia's natsOldPath. The last two move into shared.nix; each file rebinds them under its local name, so no case changes.
130 lines
5.2 KiB
Nix
130 lines
5.2 KiB
Nix
# `checks.module-eval-builder` — see ./lib.nix for the shared rationale (why
|
|
# this suite exists, naming convention, "evaluates not executes"). The swarm
|
|
# builder's container, its default and its queue and IdP registrations; its
|
|
# store grants are in ./bao-grants.nix beside every other principal's.
|
|
{
|
|
pkgs,
|
|
lib,
|
|
self,
|
|
nixosSystem,
|
|
shared,
|
|
}:
|
|
let
|
|
inherit
|
|
(import ./lib.nix {
|
|
inherit
|
|
pkgs
|
|
lib
|
|
self
|
|
nixosSystem
|
|
;
|
|
})
|
|
hive
|
|
runGroup
|
|
;
|
|
|
|
bare = shared.bareWithController;
|
|
|
|
# On the store's host, which mints the builder's store identity.
|
|
withBuilder = hive {
|
|
deploy.bao.enable = true;
|
|
deploy.builder.enable = true;
|
|
};
|
|
|
|
servicesHere = shared.swarmServicesWithController;
|
|
# Short-circuits on `assertion`, so only a failing one has its message read.
|
|
refusedWithoutIdentity =
|
|
m:
|
|
lib.any (a: !a.assertion && lib.hasInfix "deploy.builder.enable requires" a.message) m.assertions;
|
|
|
|
natsHere = shared.natsOldPath;
|
|
|
|
autheliaHere = hive {
|
|
deploy.bao.enable = true;
|
|
deploy.authelia.enable = true;
|
|
};
|
|
|
|
container = withBuilder.containers.swarm-builder;
|
|
inner = container.config;
|
|
svc = inner.systemd.services.swarm-builder;
|
|
env = svc.environment;
|
|
|
|
cases = [
|
|
{
|
|
name = "the swarm builder's container exists only where its host enables it";
|
|
ok = !(bare.containers ? swarm-builder) && withBuilder.containers ? swarm-builder;
|
|
}
|
|
{
|
|
# The store's host mints the builder's leaf, so the switch needs no
|
|
# manual identity step: the pair is set and the module's identity
|
|
# assertion, which nothing else reads in this fixture, passes. `nats` is
|
|
# the control that the switch is on.
|
|
name = "hosting the shared services turns the builder on, with its store identity";
|
|
ok =
|
|
servicesHere.services.hyperhive.deploy.builder.enable
|
|
&& servicesHere.containers ? swarm-builder
|
|
&& servicesHere.services.hyperhive.deploy.bao.builderClientCertFile != null
|
|
&& servicesHere.services.hyperhive.deploy.bao.builderClientKeyFile != null
|
|
&& !(refusedWithoutIdentity servicesHere)
|
|
&& servicesHere.services.hyperhive.deploy.bao.enable
|
|
&& servicesHere.services.hyperhive.deploy.nats.enable;
|
|
}
|
|
{
|
|
# A config PR is unreviewed input to an evaluation: the container keeps
|
|
# its own netns, and reaches the host's nix daemon through its socket
|
|
# alone, with no daemon of its own racing the bind.
|
|
name = "the builder's container has its own netns and builds only through the host's daemon socket";
|
|
ok =
|
|
container.privateNetwork
|
|
&& inner.services.hyperhive.swarmContainer.privateNetwork
|
|
&& container.bindMounts."/nix/var/nix/daemon-socket".hostPath == "/nix/var/nix/daemon-socket"
|
|
&& !inner.systemd.services.nix-daemon.enable
|
|
&& !inner.systemd.sockets.nix-daemon.enable
|
|
&& inner.systemd.globalEnvironment.NIX_REMOTE == "daemon";
|
|
}
|
|
{
|
|
# The daemon holds its store leaf, handed in by nspawn without a copy on
|
|
# disk; the forge token is read per build and reaches only `git`.
|
|
name = "the builder loads only its store leaf, runs as a dynamic user and carries no token in its unit";
|
|
ok =
|
|
lib.elem "builder-bao-key" svc.serviceConfig.LoadCredential
|
|
&& env.BAO_CLIENT_KEY == "/run/credentials/swarm-builder.service/builder-bao-key"
|
|
&& env.SWARM_BUILDER_OIDC_CLIENT_ID == "swarm-builder"
|
|
&& lib.elem "--load-credential=builder-bao-key:/var/lib/swarm-bao-pki/builder-key.pem" container.extraFlags
|
|
&& svc.serviceConfig.DynamicUser
|
|
&& !(lib.any (lib.hasSuffix "TOKEN") (lib.attrNames env));
|
|
}
|
|
{
|
|
# The check builds on the sources a hive on this host would render into
|
|
# its meta flake, so a green check means the same base.
|
|
name = "the builder builds on the hyperhive and nixpkgs this host's hives use";
|
|
ok =
|
|
env.SWARM_BUILDER_HYPERHIVE_FLAKE == withBuilder.services.hyperhive.c0re.hyperhiveFlake
|
|
&& env.SWARM_BUILDER_NIXPKGS_FLAKE == withBuilder.services.hyperhive.c0re.nixpkgsFlake
|
|
&& lib.hasSuffix "-check-flake" env.SWARM_BUILDER_CHECK_FLAKE
|
|
&& env.SWARM_BUILDER_CONFIG_ORG == "agent-configs"
|
|
&& env.SWARM_BUILDER_FORGE_URL == "https://${withBuilder.services.hyperhive.swarm.forge.domain}";
|
|
}
|
|
{
|
|
# The responder denies a client id it does not know, and a NATS denial
|
|
# arrives as a timeout.
|
|
name = "the queue's responder grants the builder's client its subject";
|
|
ok = lib.hasInfix "--builder-client swarm-builder" natsHere.containers.swarm-nats.config.systemd.services.swarm-nats-auth.serviceConfig.ExecStart;
|
|
}
|
|
{
|
|
name = "the builder's client is registered as a machine, and the publisher carries its secret";
|
|
ok =
|
|
let
|
|
c = lib.findFirst (
|
|
c: c.id == "swarm-builder"
|
|
) null autheliaHere.services.hyperhive.swarm.authelia.oidc.clients;
|
|
in
|
|
c != null
|
|
&& c.kind == "machine"
|
|
&& lib.hasInfix "secret/swarm/services/swarm-builder/oidc/client" (
|
|
autheliaHere.systemd.services.swarm-secret-publish.script
|
|
);
|
|
}
|
|
];
|
|
in
|
|
runGroup "builder" cases
|