forked from lix-project/hydra
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:
parent
3e0f5f664a
commit
dffb629b8a
3 changed files with 90 additions and 40 deletions
124
hydra-module.nix
124
hydra-module.nix
|
@ -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
|
||||||
|
'';
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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' => {
|
||||||
|
|
Loading…
Reference in a new issue