2023-09-10 08:11:56 +00:00
|
|
|
{ config
|
2024-07-19 17:24:33 +00:00
|
|
|
, options
|
2023-09-10 08:11:56 +00:00
|
|
|
, pkgs
|
2023-09-10 08:53:04 +00:00
|
|
|
, lib
|
2023-09-10 08:11:56 +00:00
|
|
|
, ...
|
|
|
|
}:
|
|
|
|
let
|
2024-07-19 17:24:33 +00:00
|
|
|
inherit (lib) filterAttrs;
|
2024-03-02 15:10:47 +00:00
|
|
|
cfg = config.services.buildbot-nix.coordinator;
|
2024-07-22 12:36:23 +00:00
|
|
|
debuggingManhole = if cfg.debugging.enable then
|
|
|
|
"manhole.TelnetManhole(${toString cfg.debugging.port}, 'admin', 'admin')"
|
|
|
|
else "None";
|
2023-09-10 08:11:56 +00:00
|
|
|
in
|
|
|
|
{
|
2023-09-10 08:53:04 +00:00
|
|
|
options = {
|
2024-03-02 15:10:47 +00:00
|
|
|
services.buildbot-nix.coordinator = {
|
|
|
|
enable = lib.mkEnableOption "buildbot-coordinator";
|
2023-09-10 08:53:04 +00:00
|
|
|
dbUrl = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
default = "postgresql://@/buildbot";
|
|
|
|
description = "Postgresql database url";
|
|
|
|
};
|
2024-07-19 17:24:33 +00:00
|
|
|
|
2023-09-10 08:53:04 +00:00
|
|
|
workersFile = lib.mkOption {
|
|
|
|
type = lib.types.path;
|
|
|
|
description = "File containing a list of nix workers";
|
|
|
|
};
|
2024-07-19 17:24:33 +00:00
|
|
|
|
2024-07-19 17:24:33 +00:00
|
|
|
buildMachines = lib.mkOption {
|
|
|
|
type = options.nix.buildMachines.type;
|
|
|
|
description = "List of local remote builders machines associated to that Buildbot instance";
|
|
|
|
};
|
|
|
|
|
2024-07-22 12:36:23 +00:00
|
|
|
debugging = {
|
|
|
|
enable = lib.mkEnableOption "manhole's buildbot debugging on localhost using `admin:admin`";
|
|
|
|
port = lib.mkOption {
|
|
|
|
type = lib.types.port;
|
|
|
|
default = 15000;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2024-07-19 17:24:33 +00:00
|
|
|
oauth2 = {
|
|
|
|
name = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
description = "Name of the OAuth2 login method";
|
|
|
|
};
|
|
|
|
|
|
|
|
icon = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
description = "FontAwesome string for the icon associated to the OAuth2 login";
|
|
|
|
default = "fa-login";
|
|
|
|
example = "fa-login";
|
|
|
|
};
|
|
|
|
|
|
|
|
clientId = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
description = "Client ID for the OAuth2 authentication";
|
|
|
|
};
|
|
|
|
|
|
|
|
clientSecretFile = lib.mkOption {
|
|
|
|
type = lib.types.path;
|
|
|
|
description = "Path to a file containing an OAuth 2 client secret";
|
|
|
|
};
|
|
|
|
|
|
|
|
resourceEndpoint = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
description = "URL to the OAuth 2 resource";
|
|
|
|
example = "https://identity.lix.systems";
|
|
|
|
};
|
|
|
|
|
|
|
|
authUri = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
description = "Authentication URI";
|
|
|
|
example = "https://identity.lix.systems/realms/lix-project/protocol/openid-connect/auth";
|
|
|
|
};
|
|
|
|
|
|
|
|
tokenUri = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
description = "Token URI";
|
|
|
|
example = "https://identity.lix.systems/realms/lix-project/protocol/openid-connect/token";
|
|
|
|
};
|
|
|
|
userinfoUri = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
description = "User info URI";
|
|
|
|
example = "https://identity.lix.systems/realms/lix-project/protocol/openid-connect/token";
|
|
|
|
};
|
2024-03-02 17:47:15 +00:00
|
|
|
};
|
2023-09-10 08:53:04 +00:00
|
|
|
buildSystems = lib.mkOption {
|
|
|
|
type = lib.types.listOf lib.types.str;
|
2023-09-10 10:11:50 +00:00
|
|
|
default = [ pkgs.hostPlatform.system ];
|
2023-09-10 08:53:04 +00:00
|
|
|
description = "Systems that we will be build";
|
|
|
|
};
|
|
|
|
evalMaxMemorySize = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
2023-09-10 10:11:50 +00:00
|
|
|
default = "2048";
|
2023-09-10 08:53:04 +00:00
|
|
|
description = ''
|
|
|
|
Maximum memory size for nix-eval-jobs (in MiB) per
|
|
|
|
worker. After the limit is reached, the worker is
|
|
|
|
restarted.
|
|
|
|
'';
|
|
|
|
};
|
2023-11-12 06:07:30 +00:00
|
|
|
evalWorkerCount = lib.mkOption {
|
|
|
|
type = lib.types.nullOr lib.types.int;
|
|
|
|
default = null;
|
|
|
|
description = ''
|
|
|
|
Number of nix-eval-jobs worker processes. If null, the number of cores is used.
|
|
|
|
If you experience memory issues (buildbot-workers going out-of-memory), you can reduce this number.
|
|
|
|
'';
|
|
|
|
};
|
2023-09-10 11:29:56 +00:00
|
|
|
domain = lib.mkOption {
|
2023-09-10 08:53:04 +00:00
|
|
|
type = lib.types.str;
|
2023-09-10 11:29:56 +00:00
|
|
|
description = "Buildbot domain";
|
|
|
|
example = "buildbot.numtide.com";
|
2023-09-10 08:53:04 +00:00
|
|
|
};
|
2023-11-04 08:50:23 +00:00
|
|
|
|
2024-07-19 17:24:33 +00:00
|
|
|
allowedOrigins = lib.mkOption {
|
|
|
|
type = lib.types.listOf lib.types.str;
|
|
|
|
description = "Allowed origins for buildbot";
|
|
|
|
example = [ "*.mydomain.com" ];
|
|
|
|
};
|
|
|
|
|
2024-03-11 22:20:58 +00:00
|
|
|
signingKeyFile = lib.mkOption {
|
|
|
|
type = lib.types.nullOr lib.types.path;
|
|
|
|
description = "A path to a Nix signing key";
|
|
|
|
default = null;
|
|
|
|
example = "/run/agenix.d/signing-key";
|
|
|
|
};
|
|
|
|
|
2024-05-06 12:26:32 +00:00
|
|
|
prometheus = {
|
|
|
|
enable = lib.mkEnableOption " the export of metrics in Prometheus format";
|
|
|
|
address = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
default = "";
|
|
|
|
description = "The local IPv4 or IPv6 address to which to bind; defaults to '' represents all IPv4 addresses.";
|
|
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
|
|
type = lib.types.port;
|
|
|
|
default = 9100;
|
|
|
|
description = "A port on which the metrics endpoint will be available";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2024-05-06 16:40:38 +00:00
|
|
|
gerrit = {
|
|
|
|
domain = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
description = "Domain to the Gerrit server";
|
|
|
|
example = "gerrit.lix.systems";
|
|
|
|
};
|
|
|
|
|
|
|
|
username = lib.mkOption {
|
|
|
|
type = lib.types.str;
|
|
|
|
description = "Username to log in to the Gerrit API";
|
|
|
|
example = "buildbot";
|
|
|
|
};
|
|
|
|
|
|
|
|
port = lib.mkOption {
|
|
|
|
type = lib.types.port;
|
|
|
|
description = "Port to log in to the Gerrit API";
|
|
|
|
example = 2022;
|
|
|
|
};
|
|
|
|
|
|
|
|
privateKeyFile = lib.mkOption {
|
|
|
|
type = lib.types.path;
|
|
|
|
description = ''
|
|
|
|
Path to the SSH private key to authenticate against the Gerrit API
|
|
|
|
'';
|
|
|
|
example = "/var/lib/buildbot/master/id_gerrit";
|
|
|
|
};
|
2024-05-06 17:39:16 +00:00
|
|
|
|
|
|
|
projects = lib.mkOption {
|
|
|
|
type = lib.types.listOf lib.types.str;
|
|
|
|
description = ''
|
|
|
|
List of projects which are to check on Gerrit.
|
|
|
|
'';
|
|
|
|
example = [ "lix" ];
|
|
|
|
};
|
2024-05-06 17:39:16 +00:00
|
|
|
|
|
|
|
projects = lib.mkOption {
|
|
|
|
type = lib.types.listOf lib.types.str;
|
|
|
|
description = ''
|
|
|
|
List of projects which are to check on Gerrit.
|
|
|
|
'';
|
|
|
|
example = [ "lix" ];
|
|
|
|
};
|
2024-05-06 16:40:38 +00:00
|
|
|
};
|
|
|
|
|
2024-03-11 21:32:06 +00:00
|
|
|
binaryCache = {
|
|
|
|
enable = lib.mkEnableOption " binary cache upload to a S3 bucket";
|
|
|
|
profileCredentialsFile = lib.mkOption {
|
|
|
|
type = lib.types.nullOr lib.types.path;
|
|
|
|
description = "A path to the various AWS profile credentials related to the S3 bucket containing a profile named `default`";
|
|
|
|
default = null;
|
|
|
|
example = "/run/agenix.d/aws-profile";
|
|
|
|
};
|
|
|
|
bucket = lib.mkOption {
|
|
|
|
type = lib.types.nullOr lib.types.str;
|
|
|
|
description = "Bucket where to store the data";
|
|
|
|
default = null;
|
|
|
|
example = "lix-cache";
|
|
|
|
};
|
|
|
|
endpoint = lib.mkOption {
|
|
|
|
type = lib.types.nullOr lib.types.str;
|
|
|
|
description = "Endpoint for the S3 server";
|
|
|
|
default = null;
|
|
|
|
example = "s3.lix.systems";
|
|
|
|
};
|
|
|
|
region = lib.mkOption {
|
|
|
|
type = lib.types.nullOr lib.types.str;
|
|
|
|
description = "Region for the S3 bucket";
|
|
|
|
default = null;
|
|
|
|
example = "garage";
|
|
|
|
};
|
|
|
|
};
|
2023-09-10 08:53:04 +00:00
|
|
|
};
|
2023-09-10 08:11:56 +00:00
|
|
|
};
|
2023-09-10 09:00:42 +00:00
|
|
|
config = lib.mkIf cfg.enable {
|
2023-11-04 08:19:56 +00:00
|
|
|
# By default buildbot uses a normal user, which is not a good default, because
|
|
|
|
# we grant normal users potentially access to other resources. Also
|
|
|
|
# we don't to be able to ssh into buildbot.
|
|
|
|
|
|
|
|
users.users.buildbot = {
|
|
|
|
isNormalUser = lib.mkForce false;
|
|
|
|
isSystemUser = true;
|
|
|
|
};
|
|
|
|
|
2023-09-10 08:53:04 +00:00
|
|
|
services.buildbot-master = {
|
|
|
|
enable = true;
|
2023-11-04 10:25:33 +00:00
|
|
|
|
|
|
|
# disable example workers from nixpkgs
|
|
|
|
builders = [ ];
|
|
|
|
schedulers = [ ];
|
|
|
|
workers = [ ];
|
|
|
|
|
2023-11-04 08:19:56 +00:00
|
|
|
home = "/var/lib/buildbot";
|
2023-09-24 07:08:03 +00:00
|
|
|
extraImports = ''
|
|
|
|
from datetime import timedelta
|
2024-07-19 17:24:33 +00:00
|
|
|
from buildbot_nix import GerritNixConfigurator, read_secret_file, make_oauth2_method, OAuth2Config, assemble_secret_file_path
|
2024-07-22 12:36:23 +00:00
|
|
|
from buildbot import manhole
|
2024-07-19 17:24:33 +00:00
|
|
|
|
|
|
|
# TODO(raito): make me configurable from the NixOS module.
|
|
|
|
# how?
|
|
|
|
CustomOAuth2 = make_oauth2_method(OAuth2Config(
|
|
|
|
name=${builtins.toJSON cfg.oauth2.name},
|
|
|
|
faIcon=${builtins.toJSON cfg.oauth2.icon},
|
|
|
|
resourceEndpoint=${builtins.toJSON cfg.oauth2.resourceEndpoint},
|
|
|
|
authUri=${builtins.toJSON cfg.oauth2.authUri},
|
|
|
|
tokenUri=${builtins.toJSON cfg.oauth2.tokenUri},
|
|
|
|
userinfoUri=${builtins.toJSON cfg.oauth2.userinfoUri}
|
|
|
|
))
|
2023-09-24 07:08:03 +00:00
|
|
|
'';
|
|
|
|
configurators = [
|
|
|
|
''
|
|
|
|
util.JanitorConfigurator(logHorizon=timedelta(weeks=4), hour=12, dayOfWeek=6)
|
|
|
|
''
|
|
|
|
''
|
2024-03-02 17:43:14 +00:00
|
|
|
GerritNixConfigurator(
|
2024-05-06 16:40:38 +00:00
|
|
|
"${cfg.gerrit.domain}",
|
|
|
|
"${cfg.gerrit.username}",
|
|
|
|
"${toString cfg.gerrit.port}",
|
2024-07-19 17:24:33 +00:00
|
|
|
assemble_secret_file_path('buildbot-service-private-key'),
|
2024-05-06 17:39:16 +00:00
|
|
|
projects=${builtins.toJSON cfg.gerrit.projects},
|
2024-07-19 17:24:33 +00:00
|
|
|
allowed_origins=${builtins.toJSON cfg.allowedOrigins},
|
2024-03-02 17:39:34 +00:00
|
|
|
url=${builtins.toJSON config.services.buildbot-master.buildbotUrl},
|
2023-09-24 07:08:03 +00:00
|
|
|
nix_eval_max_memory_size=${builtins.toJSON cfg.evalMaxMemorySize},
|
2023-12-23 18:54:42 +00:00
|
|
|
nix_eval_worker_count=${if cfg.evalWorkerCount == null then "None" else builtins.toString cfg.evalWorkerCount},
|
2023-09-24 07:08:03 +00:00
|
|
|
nix_supported_systems=${builtins.toJSON cfg.buildSystems},
|
2024-05-06 12:26:32 +00:00
|
|
|
prometheus_config=${if (!cfg.prometheus.enable) then "None" else builtins.toJSON {
|
|
|
|
inherit (cfg.prometheus) address port;
|
2024-05-11 18:07:32 +00:00
|
|
|
}},
|
2024-07-19 17:24:33 +00:00
|
|
|
nix_builders=${builtins.toJSON (map (b: filterAttrs (n: _: n != "system") b) cfg.buildMachines)},
|
2024-03-11 22:20:58 +00:00
|
|
|
# Signing key file must be available on the workers and readable.
|
|
|
|
signing_keyfile=${if cfg.signingKeyFile == null then "None" else builtins.toJSON cfg.signingKeyFile},
|
2024-03-11 21:32:06 +00:00
|
|
|
binary_cache_config=${if (!cfg.binaryCache.enable) then "None" else builtins.toJSON {
|
|
|
|
inherit (cfg.binaryCache) bucket region endpoint;
|
|
|
|
profile = "default";
|
2024-05-06 17:52:55 +00:00
|
|
|
}},
|
2024-07-19 17:24:33 +00:00
|
|
|
auth_method=CustomOAuth2(${builtins.toJSON cfg.oauth2.clientId},
|
2024-05-06 17:52:55 +00:00
|
|
|
read_secret_file('buildbot-oauth2-secret'),
|
|
|
|
autologin=True
|
2024-07-22 12:36:23 +00:00
|
|
|
),
|
|
|
|
manhole=${debuggingManhole}
|
2023-09-24 07:08:03 +00:00
|
|
|
)
|
|
|
|
''
|
|
|
|
];
|
|
|
|
buildbotUrl =
|
|
|
|
let
|
|
|
|
host = config.services.nginx.virtualHosts.${cfg.domain};
|
|
|
|
hasSSL = host.forceSSL || host.addSSL;
|
|
|
|
in
|
|
|
|
"${if hasSSL then "https" else "http"}://${cfg.domain}/";
|
2024-03-02 17:39:34 +00:00
|
|
|
dbUrl = cfg.dbUrl;
|
2024-05-06 12:26:32 +00:00
|
|
|
pythonPackages = ps: ([
|
2023-09-10 08:53:04 +00:00
|
|
|
ps.requests
|
|
|
|
ps.treq
|
|
|
|
ps.psycopg2
|
|
|
|
(ps.toPythonModule pkgs.buildbot-worker)
|
2024-03-15 13:40:23 +00:00
|
|
|
pkgs.buildbot-plugins.www
|
2023-10-31 10:32:05 +00:00
|
|
|
(pkgs.python3.pkgs.callPackage ../default.nix { })
|
2024-05-06 12:26:32 +00:00
|
|
|
] ++ lib.optional cfg.prometheus.enable (pkgs.python3.pkgs.callPackage ./prometheus-plugin.nix { })
|
|
|
|
);
|
2023-09-10 08:53:04 +00:00
|
|
|
};
|
2023-09-10 08:11:56 +00:00
|
|
|
|
2024-03-11 21:32:06 +00:00
|
|
|
# TODO(raito): we assume worker runs on coordinator. please clean up this later.
|
|
|
|
systemd.services.buildbot-worker.serviceConfig.Environment =
|
2024-03-15 12:17:25 +00:00
|
|
|
lib.mkIf cfg.binaryCache.enable (
|
2024-03-11 21:32:06 +00:00
|
|
|
let
|
|
|
|
awsConfigFile = pkgs.writeText "config.ini" ''
|
|
|
|
[default]
|
|
|
|
region = ${cfg.binaryCache.region}
|
|
|
|
endpoint_url = ${cfg.binaryCache.endpoint}
|
|
|
|
'';
|
|
|
|
in
|
|
|
|
[
|
|
|
|
"AWS_CONFIG_FILE=${awsConfigFile}"
|
|
|
|
"AWS_SHARED_CREDENTIALS_FILE=${cfg.binaryCache.profileCredentialsFile}"
|
2024-03-15 12:17:25 +00:00
|
|
|
]
|
|
|
|
);
|
2024-03-11 21:32:06 +00:00
|
|
|
|
2023-09-10 08:53:04 +00:00
|
|
|
systemd.services.buildbot-master = {
|
2023-10-31 10:32:05 +00:00
|
|
|
after = [ "postgresql.service" ];
|
2024-07-19 17:24:33 +00:00
|
|
|
path = [ pkgs.openssh ];
|
2023-09-10 08:53:04 +00:00
|
|
|
serviceConfig = {
|
|
|
|
# in master.py we read secrets from $CREDENTIALS_DIRECTORY
|
|
|
|
LoadCredential = [
|
|
|
|
"buildbot-nix-workers:${cfg.workersFile}"
|
2024-07-19 17:24:33 +00:00
|
|
|
"buildbot-oauth2-secret:${cfg.oauth2.clientSecretFile}"
|
2024-07-19 17:24:33 +00:00
|
|
|
"buildbot-service-private-key:${cfg.gerrit.privateKeyFile}"
|
2024-03-11 04:45:33 +00:00
|
|
|
];
|
2023-09-10 08:53:04 +00:00
|
|
|
};
|
2023-09-10 08:11:56 +00:00
|
|
|
};
|
2023-09-10 08:53:04 +00:00
|
|
|
|
|
|
|
services.postgresql = {
|
2023-09-10 10:11:50 +00:00
|
|
|
enable = true;
|
2023-09-10 08:53:04 +00:00
|
|
|
ensureDatabases = [ "buildbot" ];
|
2023-11-18 07:32:11 +00:00
|
|
|
ensureUsers = [{
|
|
|
|
name = "buildbot";
|
|
|
|
ensureDBOwnership = true;
|
|
|
|
}];
|
2023-09-10 08:11:56 +00:00
|
|
|
};
|
|
|
|
|
2023-09-10 11:29:56 +00:00
|
|
|
services.nginx.enable = true;
|
2024-03-02 17:39:34 +00:00
|
|
|
services.nginx.virtualHosts.${cfg.domain} =
|
|
|
|
let
|
|
|
|
port = config.services.buildbot-master.port;
|
|
|
|
in
|
|
|
|
{
|
2023-11-04 08:50:23 +00:00
|
|
|
locations = {
|
2024-03-02 17:39:34 +00:00
|
|
|
"/".proxyPass = "http://127.0.0.1:${builtins.toString port}/";
|
2023-11-04 08:50:23 +00:00
|
|
|
"/sse" = {
|
2024-03-02 17:39:34 +00:00
|
|
|
proxyPass = "http://127.0.0.1:${builtins.toString port}/sse";
|
2023-11-04 08:50:23 +00:00
|
|
|
# proxy buffering will prevent sse to work
|
|
|
|
extraConfig = "proxy_buffering off;";
|
|
|
|
};
|
|
|
|
"/ws" = {
|
2024-03-02 17:39:34 +00:00
|
|
|
proxyPass = "http://127.0.0.1:${builtins.toString port}/ws";
|
2023-11-04 08:50:23 +00:00
|
|
|
proxyWebsockets = true;
|
|
|
|
# raise the proxy timeout for the websocket
|
|
|
|
extraConfig = "proxy_read_timeout 6000s;";
|
|
|
|
};
|
2023-09-10 08:53:04 +00:00
|
|
|
};
|
2023-09-10 08:11:56 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|