feat: introduce Buildbot multi-tenancy

This shares the same expression to deploy the Buildbot.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
This commit is contained in:
raito 2024-10-05 18:14:39 +02:00
parent 192ba49f7c
commit b56b8963a2
5 changed files with 167 additions and 29 deletions

View file

@ -24,6 +24,11 @@
# Lix # Lix
build01-aarch64-lix = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICC69NZD/zhIB/wUb5odg46bss5g8hH2fDl22bk4qeSW"; build01-aarch64-lix = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICC69NZD/zhIB/wUb5odg46bss5g8hH2fDl22bk4qeSW";
build02-aarch64-lix = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGdJE375pe58RJbhKwXRp3D//+SJ3ssiVZrLsM9CLHn0";
build01-aarch64-darwin-lix = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMVf1uO0lv5UBti/naW/+amqLxvWZg+StXk9aM+lJ7e4";
# Raito infrastructure
epyc-newtype-fr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOXT9Init1MhKt4rjBANLq0t0bPww/WQZ96uB4AEDrml";
}; };
users = { users = {

View file

@ -134,6 +134,7 @@
bagel.monitoring.grafana-agent.tenant = "floral"; bagel.monitoring.grafana-agent.tenant = "floral";
bagel.secrets.tenant = "floral"; bagel.secrets.tenant = "floral";
bagel.builders.extra-build-capacity.provider.tenant = "floral"; bagel.builders.extra-build-capacity.provider.tenant = "floral";
bagel.services.buildbot.tenant = "floral";
}) })
]; ];
@ -168,6 +169,7 @@
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDu4cEqZzAI/1vZjSQkTJ4ijIg9nuloOuSKUrnkJIOFn" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDu4cEqZzAI/1vZjSQkTJ4ijIg9nuloOuSKUrnkJIOFn"
]; ];
}; };
bagel.services.buildbot.tenant = "lix";
} }
]; ];

View file

@ -2,6 +2,7 @@
config, config,
lib, lib,
pkgs, pkgs,
nodes,
... ...
}: }:
{ {
@ -26,6 +27,21 @@
bagel.services.buildbot = { bagel.services.buildbot = {
enable = true; enable = true;
domain = "buildbot.forkos.org"; domain = "buildbot.forkos.org";
gerrit =
let
cfgGerrit = nodes.gerrit01.config.bagel.services.gerrit;
in
{
domain = cfgGerrit.canonicalDomain;
port = cfgGerrit.port;
username = "buildbot";
};
cors.allowedOrigin = "*.forkos.org";
projects = [
"buildbot-test"
"nixpkgs"
"infra"
];
builders = [ "builder-10" ]; builders = [ "builder-10" ];
}; };

View file

@ -7,15 +7,69 @@
}: }:
let let
cfg = config.bagel.services.buildbot; cfg = config.bagel.services.buildbot;
cfgGerrit = nodes.gerrit01.config.bagel.services.gerrit;
ssh-keys = import ../../common/ssh-keys.nix; ssh-keys = import ../../common/ssh-keys.nix;
freeGbDiskSpace = 20;
extraTenantSpecificBuilders = {
lix = import ./lix.nix {
inherit config nodes;
};
floral = [ ];
}.${cfg.tenant or (throw "${cfg.tenant} is not a known tenant")};
clientId = {
lix = "buildbot";
floral = "forkos-buildbot";
}.${cfg.tenant or (throw "${cfg.tenant} is not a known tenant")};
inherit (lib) mkEnableOption mkOption mkIf types; inherit (lib) mkEnableOption mkOption mkIf types;
in in
{ {
options.bagel.services.buildbot = { options.bagel.services.buildbot = {
enable = mkEnableOption "Buildbot"; enable = mkEnableOption "Buildbot";
tenant = mkOption {
type = types.enum [ "lix" "floral" ];
description = "Which buildbot tenant to enable";
};
domain = mkOption { domain = mkOption {
type = types.str; type = types.str;
description = "Domain name for this Buildbot";
};
gerrit = {
domain = mkOption {
type = types.str;
description = "Canonical domain of the Gerrit associated to this Buildbot";
example = [ "cl.forkos.org" ];
};
port = mkOption {
type = types.port;
description = "Gerrit SSH port for this Buildbot";
};
username = mkOption {
type = types.str;
description = "Gerrit service username for this Buildbot";
};
};
cors.allowedOrigin = mkOption {
type = types.str;
example = "*.forkos.org";
description = "Allowed origin for Buildbot and NGINX for CORS without the protocol";
};
buildSystems = mkOption {
type = types.listOf (types.enum [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]);
default = [ "x86_64-linux" ];
example = [ "x86_64-linux" "aarch64-linux" ];
description = "Supported build systems for this buildbot instance.";
};
projects = mkOption {
type = types.listOf types.str;
example = [ "nixpkgs" ];
description = "Static list of projects enabled for Buildbot CI";
}; };
builders = mkOption { builders = mkOption {
@ -27,28 +81,38 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
networking.firewall.allowedTCPPorts = [ 80 443 ]; networking.firewall.allowedTCPPorts = [ 80 443 ];
age.secrets.buildbot-worker-password.file = ../../secrets/buildbot-worker-password.age; bagel.secrets.files = [
age.secrets.buildbot-oauth-secret.file = ../../secrets/buildbot-oauth-secret.age; "buildbot-worker-password"
age.secrets.buildbot-workers.file = ../../secrets/buildbot-workers.age; "buildbot-oauth-secret"
age.secrets.buildbot-service-key.file = ../../secrets/buildbot-service-key.age; "buildbot-workers"
"buildbot-service-key"
"buildbot-signing-key"
"buildbot-remote-builder-key"
];
age.secrets.buildbot-signing-key = { age.secrets.buildbot-signing-key = {
file = ../../secrets/buildbot-signing-key.age;
owner = "buildbot-worker"; owner = "buildbot-worker";
group = "buildbot-worker"; group = "buildbot-worker";
}; };
age.secrets.buildbot-remote-builder-key = { age.secrets.buildbot-remote-builder-key = {
file = ../../secrets/buildbot-remote-builder-key.age; file = ../../secrets/${cfg.tenant}/buildbot-remote-builder-key.age;
owner = "buildbot-worker"; owner = "buildbot-worker";
group = "buildbot-worker"; group = "buildbot-worker";
}; };
services.nginx.virtualHosts.${cfg.domain} = { services.nginx = {
forceSSL = true; appendHttpConfig = ''
enableACME = true; # Our session stuff is too big with the TWISTED_COOKIE in addition.
extraConfig = '' # Default is usually 4k or 8k.
add_header Access-Control-Allow-Credentials 'true' always; large_client_header_buffers 4 16k;
add_header Access-Control-Allow-Origin 'https://cl.forkos.org' always;
''; '';
virtualHosts.${cfg.domain} = {
forceSSL = true;
enableACME = true;
extraConfig = ''
add_header Access-Control-Allow-Credentials 'true' always;
add_header Access-Control-Allow-Origin 'https://${cfg.cors.allowedOrigin}' always;
'';
};
}; };
services.buildbot-nix.worker = { services.buildbot-nix.worker = {
@ -74,10 +138,12 @@ in
enable = true; enable = true;
inherit (cfg) domain; inherit (cfg) domain;
# TODO(raito): is that really necessary when we can just collect buildMachines' systems?
inherit (cfg) buildSystems;
oauth2 = { oauth2 = {
name = "Lix"; name = "Lix";
clientId = "forkos-buildbot"; inherit clientId;
clientSecretFile = config.age.secrets.buildbot-oauth-secret.path; clientSecretFile = config.age.secrets.buildbot-oauth-secret.path;
resourceEndpoint = "https://identity.lix.systems"; resourceEndpoint = "https://identity.lix.systems";
authUri = "https://identity.lix.systems/realms/lix-project/protocol/openid-connect/auth"; authUri = "https://identity.lix.systems/realms/lix-project/protocol/openid-connect/auth";
@ -90,12 +156,7 @@ in
workersFile = config.age.secrets.buildbot-workers.path; workersFile = config.age.secrets.buildbot-workers.path;
allowedOrigins = [ allowedOrigins = [
"*.forkos.org" cfg.cors.allowedOrigin
];
# TODO(raito): is that really necessary when we can just collect buildMachines' systems?
buildSystems = [
"x86_64-linux"
]; ];
buildMachines = map (n: { buildMachines = map (n: {
@ -110,20 +171,14 @@ in
# Contrary to how Nix works, here we can specify non-base64 public host keys. # Contrary to how Nix works, here we can specify non-base64 public host keys.
publicHostKey = ssh-keys.machines.${n}; publicHostKey = ssh-keys.machines.${n};
} }
) cfg.builders; ) cfg.builders ++ extraTenantSpecificBuilders;
gerrit = { gerrit = {
domain = cfgGerrit.canonicalDomain;
# Manually managed account… # Manually managed account…
# TODO: https://git.lix.systems/the-distro/infra/issues/69 # TODO: https://git.lix.systems/the-distro/infra/issues/69
username = "buildbot"; inherit (cfg.gerrit) domain port username;
port = cfgGerrit.port;
privateKeyFile = config.age.secrets.buildbot-service-key.path; privateKeyFile = config.age.secrets.buildbot-service-key.path;
projects = [ inherit (cfg) projects;
"buildbot-test"
"nixpkgs"
"infra"
];
}; };
evalWorkerCount = 6; evalWorkerCount = 6;
@ -132,10 +187,21 @@ in
signingKeyFile = config.age.secrets.buildbot-signing-key.path; signingKeyFile = config.age.secrets.buildbot-signing-key.path;
}; };
# Make PostgreSQL restart smoother.
systemd.services.postgresql.serviceConfig = {
Restart = "always";
RestartMaxDelaySec = "5m";
RestartSteps = 10;
};
nix.settings.keep-derivations = true; nix.settings.keep-derivations = true;
nix.gc = { nix.gc = {
automatic = true; automatic = true;
dates = "hourly"; dates = "hourly";
options = ''
--max-freed "$((${toString freeGbDiskSpace} * 1024**3 - 1024 * $(df -P -k /nix/store | tail -n 1 | ${pkgs.gawk}/bin/awk '{ print $4 }')))"
'';
}; };
}; };
} }

49
services/buildbot/lix.nix Normal file
View file

@ -0,0 +1,49 @@
{ config, nodes, ... }:
let
ssh-keys = import ../../common/ssh-keys.nix;
in
[
{
hostName = "build01.aarch64.lix.systems";
maxJobs = 2;
protocol = "ssh-ng";
sshKey = config.age.secrets.buildbot-remote-builder-key.path;
sshUser = "nix";
systems = [ "aarch64-linux" ];
publicHostKey = ssh-keys.machines.build01-aarch64-lix;
supportedFeatures = nodes.build01-aarch64-lix.config.nix.settings.system-features;
}
{
hostName = "build02.aarch64.lix.systems";
maxJobs = 4;
protocol = "ssh-ng";
sshKey = config.age.secrets.buildbot-remote-builder-key.path;
sshUser = "nix";
systems = [ "aarch64-linux" ];
publicHostKey = ssh-keys.machines.build02-aarch64-lix;
supportedFeatures = nodes.build02-aarch64-lix.config.nix.settings.system-features;
}
{
hostName = "build01.aarch64-darwin.lix.systems";
maxJobs = 2;
protocol = "ssh-ng";
sshKey = config.age.secrets.buildbot-remote-builder-key.path;
sshUser = "m1";
systems = [ "aarch64-darwin" "x86_64-darwin" ];
publicHostKey = ssh-keys.machines.build01-aarch64-darwin-lix;
supportedFeatures = [ "big-parallel" ];
}
# a.k.a. https://git.newtype.fr/newtype/newtype-org-configurations/src/branch/main/docs/epyc.md
{
hostName = "epyc.infra.newtype.fr";
# at 256G this could run 64 builds but the machine is shared
# (and historically we used no more than 16 concurrent jobs)
maxJobs = 16;
protocol = "ssh-ng";
sshKey = config.age.secrets.buildbot-remote-builder-key.path;
sshUser = "nix";
systems = [ "x86_64-linux" "i686-linux" ];
publicHostKey = ssh-keys.machines.epyc-newtype-fr;
supportedFeatures = [ "benchmark" "big-parallel" "nixos-test" "kvm" ];
}
]