Unify Hydra's NixOS module with the one used for hydra.nixos.org

In particular, the queue runner and web server now run under different
UIDs.
This commit is contained in:
Eelco Dolstra 2015-07-02 01:01:44 +02:00
parent 3e0f5f664a
commit dffb629b8a
3 changed files with 90 additions and 40 deletions

View file

@ -3,6 +3,7 @@
with lib; with lib;
let let
cfg = config.services.hydra; cfg = config.services.hydra;
baseDir = "/var/lib/hydra"; baseDir = "/var/lib/hydra";
@ -11,19 +12,25 @@ let
hydraEnv = hydraEnv =
{ HYDRA_DBI = cfg.dbi; { HYDRA_DBI = cfg.dbi;
HYDRA_CONFIG = "${baseDir}/data/hydra.conf"; HYDRA_CONFIG = "${baseDir}/hydra.conf";
HYDRA_DATA = "${baseDir}/data"; HYDRA_DATA = "${baseDir}";
}; };
env = env =
{ NIX_REMOTE = "daemon"; { NIX_REMOTE = "daemon";
OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt"; SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
GIT_SSL_CAINFO = "/etc/ssl/certs/ca-bundle.crt"; PGPASSFILE = "${baseDir}/pgpass";
} // hydraEnv // cfg.extraEnv; } // hydraEnv // cfg.extraEnv;
serverEnv = env // serverEnv = env //
{ HYDRA_TRACKER = cfg.tracker; { HYDRA_TRACKER = cfg.tracker;
COLUMNS = "80";
} // (optionalAttrs cfg.debugServer { DBIC_TRACE = 1; }); } // (optionalAttrs cfg.debugServer { DBIC_TRACE = 1; });
localDB = "dbi:Pg:dbname=hydra;user=hydra;";
haveLocalDB = cfg.dbi == localDB;
in in
{ {
@ -42,7 +49,7 @@ in
dbi = mkOption { dbi = mkOption {
type = types.str; type = types.str;
default = "dbi:Pg:dbname=hydra;user=hydra;"; default = localDB;
example = "dbi:Pg:dbname=hydra;host=postgres.example.org;user=foo;"; example = "dbi:Pg:dbname=hydra;host=postgres.example.org;user=foo;";
description = '' description = ''
The DBI string for Hydra database connection. The DBI string for Hydra database connection.
@ -82,7 +89,7 @@ in
minimumDiskFree = mkOption { minimumDiskFree = mkOption {
type = types.int; type = types.int;
default = 5; default = 0;
description = '' description = ''
Threshold of minimum disk space (GiB) to determine if queue runner should run or not. Threshold of minimum disk space (GiB) to determine if queue runner should run or not.
''; '';
@ -90,7 +97,7 @@ in
minimumDiskFreeEvaluator = mkOption { minimumDiskFreeEvaluator = mkOption {
type = types.int; type = types.int;
default = 2; default = 0;
description = '' description = ''
Threshold of minimum disk space (GiB) to determine if evaluator should run or not. Threshold of minimum disk space (GiB) to determine if evaluator should run or not.
''; '';
@ -115,25 +122,25 @@ in
type = types.nullOr types.path; type = types.nullOr types.path;
default = null; default = null;
description = '' description = ''
File name of an alternate logo to be displayed on the web pages. Path to a file containing the logo of your Hydra instance.
''; '';
}; };
debugServer = mkOption { debugServer = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
description = "Whether to run the server in debug mode"; description = "Whether to run the server in debug mode.";
}; };
extraConfig = mkOption { extraConfig = mkOption {
type = types.lines; type = types.lines;
description = "Extra lines for the hydra config"; description = "Extra lines for the Hydra configuration.";
}; };
extraEnv = mkOption { extraEnv = mkOption {
type = types.attrsOf types.str; type = types.attrsOf types.str;
default = {}; default = {};
description = "Extra environment variables for Hydra"; description = "Extra environment variables for Hydra.";
}; };
}; };
@ -144,6 +151,30 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
users.extraGroups.hydra = { };
users.extraUsers.hydra =
{ description = "Hydra";
group = "hydra";
createHome = true;
home = baseDir;
useDefaultShell = true;
};
users.extraUsers.hydra-queue-runner =
{ description = "Hydra queue runner";
group = "hydra";
useDefaultShell = true;
};
users.extraUsers.hydra-www =
{ description = "Hydra web server";
group = "hydra";
useDefaultShell = true;
};
nix.trustedUsers = [ "hydra-queue-runner" ];
services.hydra.package = mkDefault ((import ./release.nix {}).build.x86_64-linux); services.hydra.package = mkDefault ((import ./release.nix {}).build.x86_64-linux);
services.hydra.extraConfig = services.hydra.extraConfig =
@ -161,13 +192,6 @@ in
environment.variables = hydraEnv; environment.variables = hydraEnv;
users.extraUsers.hydra =
{ description = "Hydra";
home = baseDir;
createHome = true;
useDefaultShell = true;
};
nix.extraOptions = '' nix.extraOptions = ''
gc-keep-outputs = true gc-keep-outputs = true
gc-keep-derivations = true gc-keep-derivations = true
@ -177,18 +201,26 @@ in
gc-check-reachability = false gc-check-reachability = false
''; '';
nix.trustedUsers = [ "hydra" ];
systemd.services.hydra-init = systemd.services.hydra-init =
{ wantedBy = [ "multi-user.target" ]; { wantedBy = [ "multi-user.target" ];
requires = [ "postgresql.service" ]; requires = [ "postgresql.service" ];
after = [ "postgresql.service" ]; after = [ "postgresql.service" ];
environment = env; environment = env;
preStart = '' preStart = ''
mkdir -m 0700 -p ${baseDir}/data mkdir -p ${baseDir}
chown hydra ${baseDir}/data chown hydra.hydra ${baseDir}
ln -sf ${hydraConf} ${baseDir}/data/hydra.conf chmod 0750 ${baseDir}
${optionalString (cfg.dbi == "dbi:Pg:dbname=hydra;user=hydra;") ''
ln -sf ${hydraConf} ${baseDir}/hydra.conf
mkdir -m 0700 -p /var/lib/hydra/www
chown hydra-www.hydra /var/lib/hydra/www
mkdir -m 0700 -p /var/lib/hydra/queue-runner
mkdir -m 0750 -p /var/lib/hydra/build-logs
chown hydra-queue-runner.hydra /var/lib/hydra/queue-runner /var/lib/hydra/build-logs
${optionalString haveLocalDB ''
if ! [ -e ${baseDir}/.db-created ]; then if ! [ -e ${baseDir}/.db-created ]; then
${config.services.postgresql.package}/bin/createuser hydra ${config.services.postgresql.package}/bin/createuser hydra
${config.services.postgresql.package}/bin/createdb -O hydra hydra ${config.services.postgresql.package}/bin/createdb -O hydra hydra
@ -207,13 +239,14 @@ in
{ wantedBy = [ "multi-user.target" ]; { wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ]; requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" ]; after = [ "hydra-init.service" ];
environment = serverEnv // { COLUMNS = "80"; }; environment = serverEnv;
serviceConfig = serviceConfig =
{ ExecStart = { ExecStart =
"@${cfg.package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' " "@${cfg.package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' "
+ "-p ${toString cfg.port} --max_spare_servers 5 --max_servers 25 " + "-p ${toString cfg.port} --max_spare_servers 5 --max_servers 25 "
+ "--max_requests 100 ${optionalString cfg.debugServer "-d"}"; + "--max_requests 100 ${optionalString cfg.debugServer "-d"}";
User = "hydra"; User = "hydra-www";
PermissionsStartOnly = true;
Restart = "always"; Restart = "always";
}; };
}; };
@ -225,9 +258,10 @@ in
path = [ pkgs.nettools ]; path = [ pkgs.nettools ];
environment = env; environment = env;
serviceConfig = serviceConfig =
{ ExecStart = "@${cfg.package}/bin/hydra-queue-runner hydra-queue-runner"; { ExecStartPre = "${cfg.package}/bin/hydra-queue-runner --unlock";
ExecStart = "@${cfg.package}/bin/hydra-queue-runner hydra-queue-runner";
ExecStopPost = "${cfg.package}/bin/hydra-queue-runner --unlock"; ExecStopPost = "${cfg.package}/bin/hydra-queue-runner --unlock";
User = "hydra"; User = "hydra-queue-runner";
Restart = "always"; Restart = "always";
}; };
}; };
@ -253,7 +287,7 @@ in
{ ExecStart = "@${cfg.package}/bin/hydra-update-gc-roots hydra-update-gc-roots"; { ExecStart = "@${cfg.package}/bin/hydra-update-gc-roots hydra-update-gc-roots";
User = "hydra"; User = "hydra";
}; };
startAt = "02:15"; startAt = "2,14:15";
}; };
systemd.services.hydra-send-stats = systemd.services.hydra-send-stats =
@ -266,13 +300,11 @@ in
}; };
}; };
services.cron.systemCronJobs = # If there is less than a certain amount of free disk space, stop
let # the queue/evaluator to prevent builds from failing or aborting.
# If there is less than ... GiB of free disk space, stop the queue systemd.services.hydra-check-space =
# to prevent builds from failing or aborting. { script =
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 if [ $(($(stat -f -c '%a' /nix/store) * $(stat -f -c '%S' /nix/store))) -lt $((${toString cfg.minimumDiskFree} * 1024**3)) ]; then
echo "stopping Hydra queue runner due to lack of free space..." echo "stopping Hydra queue runner due to lack of free space..."
systemctl stop hydra-queue-runner systemctl stop hydra-queue-runner
@ -282,8 +314,24 @@ in
systemctl stop hydra-evaluator systemctl stop hydra-evaluator
fi fi
''; '';
in startAt = "*:0/5";
[ "*/5 * * * * root ${checkSpace} &> ${baseDir}/data/checkspace.log"
];
}; };
services.postgresql.enable = mkIf haveLocalDB true;
services.postgresql.identMap = optionalString haveLocalDB
''
hydra-users hydra hydra
hydra-users hydra-queue-runner hydra
hydra-users hydra-www hydra
hydra-users root hydra
'';
services.postgresql.authentication = optionalString haveLocalDB
''
local hydra all ident map=hydra-users
'';
};
} }

View file

@ -1568,7 +1568,9 @@ void State::notificationSender()
std::shared_ptr<PathLocks> State::acquireGlobalLock() std::shared_ptr<PathLocks> State::acquireGlobalLock()
{ {
Path lockPath = hydraData + "/queue-runner"; Path lockPath = hydraData + "/queue-runner/lock";
createDirs(dirOf(lockPath));
auto lock = std::make_shared<PathLocks>(); auto lock = std::make_shared<PathLocks>();
if (!lock->lockPaths(PathSet({lockPath}), "", false)) return 0; if (!lock->lockPaths(PathSet({lockPath}), "", false)) return 0;

View file

@ -55,7 +55,7 @@ __PACKAGE__->config(
}, },
'Plugin::Session' => { 'Plugin::Session' => {
expires => 3600 * 24 * 7, expires => 3600 * 24 * 7,
storage => ($ENV{'HYDRA_SERVER_DATA'} // Hydra::Model::DB::getHydraPath) . "/session_data", storage => Hydra::Model::DB::getHydraPath . "/www/session_data",
unlink_on_exit => 0 unlink_on_exit => 0
}, },
'Plugin::AccessLog' => { 'Plugin::AccessLog' => {