hydra/hydra-module.nix

279 lines
8 KiB
Nix
Raw Normal View History

2011-05-05 06:27:38 +00:00
{ config, pkgs, ... }:
with pkgs.lib;
let
cfg = config.services.hydra;
baseDir = "/var/lib/hydra";
hydraConf = pkgs.writeScript "hydra.conf" cfg.extraConfig;
2013-01-22 13:41:02 +00:00
env =
{ NIX_REMOTE = "daemon";
HYDRA_DBI = cfg.dbi;
HYDRA_CONFIG = "${baseDir}/data/hydra.conf";
HYDRA_DATA = "${baseDir}/data";
OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt";
GIT_SSL_CAINFO = "/etc/ssl/certs/ca-bundle.crt";
};
serverEnv = env //
2013-11-06 16:27:36 +00:00
{ HYDRA_TRACKER = cfg.tracker;
} // (optionalAttrs cfg.debugServer { DBIC_TRACE = 1; });
2011-05-05 06:27:38 +00:00
in
{
###### interface
options = {
2013-11-06 16:27:36 +00:00
2011-05-05 06:27:38 +00:00
services.hydra = rec {
2013-01-22 13:41:02 +00:00
2011-05-05 06:27:38 +00:00
enable = mkOption {
type = types.bool;
2011-05-05 06:27:38 +00:00
default = false;
description = ''
Whether to run Hydra services.
'';
};
dbi = mkOption {
type = types.string;
default = "dbi:Pg:dbname=hydra;user=hydra;";
example = "dbi:Pg:dbname=hydra;host=postgres.example.org;user=foo;";
2011-05-05 06:27:38 +00:00
description = ''
2011-05-05 10:07:59 +00:00
The DBI string for Hydra database connection.
2011-05-05 06:27:38 +00:00
'';
};
2013-01-22 13:41:02 +00:00
package = mkOption {
type = types.path;
#default = pkgs.hydra;
description = "The Hydra package.";
2011-05-05 06:27:38 +00:00
};
2013-01-22 13:41:02 +00:00
2011-05-05 06:27:38 +00:00
hydraURL = mkOption {
type = types.str;
2011-05-05 06:27:38 +00:00
description = ''
2013-01-22 13:41:02 +00:00
The base URL for the Hydra webserver instance. Used for links in emails.
2011-05-05 06:27:38 +00:00
'';
};
2011-05-05 10:07:56 +00:00
listenHost = mkOption {
type = types.str;
default = "*";
example = "localhost";
description = ''
The hostname or address to listen on or <literal>*</literal> to listen
on all interfaces.
'';
};
2011-05-05 10:07:56 +00:00
port = mkOption {
type = types.int;
2011-05-05 10:07:56 +00:00
default = 3000;
description = ''
TCP port the web server should listen to.
'';
};
2011-05-05 06:27:38 +00:00
minimumDiskFree = mkOption {
type = types.int;
2011-05-05 06:27:38 +00:00
default = 5;
description = ''
Threshold of minimum disk space (GiB) to determine if queue runner should run or not.
2011-05-05 06:27:38 +00:00
'';
};
minimumDiskFreeEvaluator = mkOption {
type = types.int;
2011-05-05 06:27:38 +00:00
default = 2;
description = ''
Threshold of minimum disk space (GiB) to determine if evaluator should run or not.
2011-05-05 06:27:38 +00:00
'';
};
notificationSender = mkOption {
type = types.str;
2011-05-05 06:27:38 +00:00
description = ''
2013-01-22 13:41:02 +00:00
Sender email address used for email notifications.
2011-05-05 06:27:38 +00:00
'';
2013-01-22 13:41:02 +00:00
};
2011-05-05 06:27:38 +00:00
tracker = mkOption {
type = types.str;
2011-05-05 06:27:38 +00:00
default = "";
description = ''
Piece of HTML that is included on all pages.
'';
2011-05-05 10:07:59 +00:00
};
logo = mkOption {
2013-11-06 16:27:36 +00:00
type = types.nullOr types.path;
2011-05-05 10:07:59 +00:00
default = null;
description = ''
File name of an alternate logo to be displayed on the web pages.
'';
};
debugServer = mkOption {
type = types.bool;
default = false;
description = "Whether to run the server in debug mode";
};
extraConfig = mkOption {
type = types.lines;
description = "Extra lines for the hydra config";
};
2011-05-05 06:27:38 +00:00
};
};
2013-01-22 13:41:02 +00:00
2011-05-05 06:27:38 +00:00
###### implementation
config = mkIf cfg.enable {
2013-11-06 16:27:36 +00:00
services.hydra.extraConfig =
''
using_frontend_proxy 1
base_uri ${cfg.hydraURL}
notification_sender ${cfg.notificationSender}
max_servers 25
2013-11-06 16:27:36 +00:00
${optionalString (cfg.logo != null) ''
hydra_logo ${cfg.logo}
''}
'';
environment.systemPackages = [ cfg.package ];
2011-05-05 06:27:38 +00:00
users.extraUsers.hydra =
{ description = "Hydra";
home = baseDir;
2011-05-05 06:27:38 +00:00
createHome = true;
useDefaultShell = true;
};
2011-05-05 06:27:38 +00:00
nix.extraOptions = ''
gc-keep-outputs = true
gc-keep-derivations = true
# The default (`true') slows Nix down a lot since the build farm
# has so many GC roots.
gc-check-reachability = false
# Hydra needs caching of build failures.
build-cache-failure = true
build-poll-interval = 10
# Online log compression makes it impossible to get the tail of
# builds that are in progress.
build-compress-log = false
2011-05-05 06:27:38 +00:00
'';
systemd.services."hydra-init" =
{ wantedBy = [ "multi-user.target" ];
requires = [ "postgresql.service" ];
after = [ "postgresql.service" ];
environment = env;
script = ''
mkdir -p ${baseDir}/data
chown hydra ${baseDir}/data
ln -sf ${hydraConf} ${baseDir}/data/hydra.conf
${optionalString (cfg.dbi == "dbi:Pg:dbname=hydra;user=hydra;") ''
if ! [ -e ${baseDir}/.db-created ]; then
${config.services.postgresql.package}/bin/createuser hydra
${config.services.postgresql.package}/bin/createdb -O hydra hydra
touch ${baseDir}/.db-created
fi
''}
${pkgs.shadow}/bin/su hydra -c ${cfg.package}/bin/hydra-init
2011-05-05 06:27:38 +00:00
'';
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
};
systemd.services."hydra-server" =
{ wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" ];
environment = serverEnv;
serviceConfig =
{ ExecStart =
"@${cfg.package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' "
+ "-p ${toString cfg.port} --max_spare_servers 5 --max_servers 25 "
+ "--max_requests 100 ${optionalString cfg.debugServer "-d"}";
User = "hydra";
Restart = "always";
};
2011-05-05 06:27:38 +00:00
};
systemd.services."hydra-queue-runner" =
{ wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" "network.target" ];
path = [ pkgs.nettools ];
environment = env;
serviceConfig =
{ ExecStartPre = "${cfg.package}/bin/hydra-queue-runner --unlock";
ExecStart = "@${cfg.package}/bin/hydra-queue-runner hydra-queue-runner";
User = "hydra";
Restart = "always";
};
2011-05-05 06:27:38 +00:00
};
systemd.services."hydra-evaluator" =
{ wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" "network.target" ];
path = [ pkgs.nettools ];
environment = env;
serviceConfig =
{ ExecStart = "@${cfg.package}/bin/hydra-evaluator hydra-evaluator";
User = "hydra";
Restart = "always";
};
2011-05-05 06:27:38 +00:00
};
systemd.services."hydra-update-gc-roots" =
{ requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" ];
environment = env;
serviceConfig =
{ ExecStart = "@${cfg.package}/bin/hydra-update-gc-roots hydra-update-gc-roots";
User = "hydra";
};
2011-05-05 06:27:38 +00:00
};
services.cron.systemCronJobs =
let
# If there is less than ... GiB of free disk space, stop the queue
# to prevent builds from failing or aborting.
checkSpace = pkgs.writeScript "hydra-check-space"
''
#! ${pkgs.stdenv.shell}
if [ $(($(stat -f -c '%a' /nix/store) * $(stat -f -c '%S' /nix/store))) -lt $((${toString cfg.minimumDiskFree} * 1024**3)) ]; then
systemctl stop hydra-queue-runner
fi
if [ $(($(stat -f -c '%a' /nix/store) * $(stat -f -c '%S' /nix/store))) -lt $((${toString cfg.minimumDiskFreeEvaluator} * 1024**3)) ]; then
systemctl stop hydra-evaluator
fi
'';
compressLogs = pkgs.writeScript "compress-logs" ''
#! ${pkgs.stdenv.shell} -e
find /nix/var/log/nix/drvs \
-type f -a ! -newermt 'last month' \
-name '*.drv' -exec bzip2 -v {} +
'';
in
[ "*/5 * * * * root ${checkSpace} &> ${baseDir}/data/checkspace.log"
"15 5 * * * root ${compressLogs} &> ${baseDir}/data/compress.log"
"15 2 * * * root ${pkgs.systemd}/bin/systemctl start hydra-update-gc-roots.service"
];
};
2011-05-05 06:27:38 +00:00
}