From dffb629b8a93ac0ee850fefe75c487b2679c51e2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 2 Jul 2015 01:01:44 +0200 Subject: [PATCH] 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. --- hydra-module.nix | 124 +++++++++++++------ src/hydra-queue-runner/hydra-queue-runner.cc | 4 +- src/lib/Hydra.pm | 2 +- 3 files changed, 90 insertions(+), 40 deletions(-) diff --git a/hydra-module.nix b/hydra-module.nix index 6c395e76..d3f9a660 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -3,6 +3,7 @@ with lib; let + cfg = config.services.hydra; baseDir = "/var/lib/hydra"; @@ -11,19 +12,25 @@ let hydraEnv = { HYDRA_DBI = cfg.dbi; - HYDRA_CONFIG = "${baseDir}/data/hydra.conf"; - HYDRA_DATA = "${baseDir}/data"; + HYDRA_CONFIG = "${baseDir}/hydra.conf"; + HYDRA_DATA = "${baseDir}"; }; env = { NIX_REMOTE = "daemon"; - OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt"; - GIT_SSL_CAINFO = "/etc/ssl/certs/ca-bundle.crt"; + SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt"; + PGPASSFILE = "${baseDir}/pgpass"; } // hydraEnv // cfg.extraEnv; serverEnv = env // { HYDRA_TRACKER = cfg.tracker; + COLUMNS = "80"; } // (optionalAttrs cfg.debugServer { DBIC_TRACE = 1; }); + + localDB = "dbi:Pg:dbname=hydra;user=hydra;"; + + haveLocalDB = cfg.dbi == localDB; + in { @@ -42,7 +49,7 @@ in dbi = mkOption { type = types.str; - default = "dbi:Pg:dbname=hydra;user=hydra;"; + default = localDB; example = "dbi:Pg:dbname=hydra;host=postgres.example.org;user=foo;"; description = '' The DBI string for Hydra database connection. @@ -82,7 +89,7 @@ in minimumDiskFree = mkOption { type = types.int; - default = 5; + default = 0; description = '' Threshold of minimum disk space (GiB) to determine if queue runner should run or not. ''; @@ -90,7 +97,7 @@ in minimumDiskFreeEvaluator = mkOption { type = types.int; - default = 2; + default = 0; description = '' Threshold of minimum disk space (GiB) to determine if evaluator should run or not. ''; @@ -115,25 +122,25 @@ in type = types.nullOr types.path; default = null; 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 { type = types.bool; default = false; - description = "Whether to run the server in debug mode"; + description = "Whether to run the server in debug mode."; }; extraConfig = mkOption { type = types.lines; - description = "Extra lines for the hydra config"; + description = "Extra lines for the Hydra configuration."; }; extraEnv = mkOption { type = types.attrsOf types.str; default = {}; - description = "Extra environment variables for Hydra"; + description = "Extra environment variables for Hydra."; }; }; @@ -144,6 +151,30 @@ in 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.extraConfig = @@ -161,13 +192,6 @@ in environment.variables = hydraEnv; - users.extraUsers.hydra = - { description = "Hydra"; - home = baseDir; - createHome = true; - useDefaultShell = true; - }; - nix.extraOptions = '' gc-keep-outputs = true gc-keep-derivations = true @@ -177,18 +201,26 @@ in gc-check-reachability = false ''; - nix.trustedUsers = [ "hydra" ]; - systemd.services.hydra-init = { wantedBy = [ "multi-user.target" ]; requires = [ "postgresql.service" ]; after = [ "postgresql.service" ]; environment = env; preStart = '' - mkdir -m 0700 -p ${baseDir}/data - chown hydra ${baseDir}/data - ln -sf ${hydraConf} ${baseDir}/data/hydra.conf - ${optionalString (cfg.dbi == "dbi:Pg:dbname=hydra;user=hydra;") '' + mkdir -p ${baseDir} + chown hydra.hydra ${baseDir} + chmod 0750 ${baseDir} + + 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 ${config.services.postgresql.package}/bin/createuser hydra ${config.services.postgresql.package}/bin/createdb -O hydra hydra @@ -207,13 +239,14 @@ in { wantedBy = [ "multi-user.target" ]; requires = [ "hydra-init.service" ]; after = [ "hydra-init.service" ]; - environment = serverEnv // { COLUMNS = "80"; }; + 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"; + User = "hydra-www"; + PermissionsStartOnly = true; Restart = "always"; }; }; @@ -225,9 +258,10 @@ in path = [ pkgs.nettools ]; environment = env; 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"; - User = "hydra"; + User = "hydra-queue-runner"; Restart = "always"; }; }; @@ -253,7 +287,7 @@ in { ExecStart = "@${cfg.package}/bin/hydra-update-gc-roots hydra-update-gc-roots"; User = "hydra"; }; - startAt = "02:15"; + startAt = "2,14:15"; }; systemd.services.hydra-send-stats = @@ -266,13 +300,11 @@ in }; }; - 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" + # If there is less than a certain amount of free disk space, stop + # the queue/evaluator to prevent builds from failing or aborting. + systemd.services.hydra-check-space = + { script = '' - #! ${pkgs.stdenv.shell} 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..." systemctl stop hydra-queue-runner @@ -282,8 +314,24 @@ in systemctl stop hydra-evaluator fi ''; - in - [ "*/5 * * * * root ${checkSpace} &> ${baseDir}/data/checkspace.log" - ]; + startAt = "*:0/5"; + }; + + 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 + ''; + }; + } diff --git a/src/hydra-queue-runner/hydra-queue-runner.cc b/src/hydra-queue-runner/hydra-queue-runner.cc index d1f748c4..4d9f59a6 100644 --- a/src/hydra-queue-runner/hydra-queue-runner.cc +++ b/src/hydra-queue-runner/hydra-queue-runner.cc @@ -1568,7 +1568,9 @@ void State::notificationSender() std::shared_ptr State::acquireGlobalLock() { - Path lockPath = hydraData + "/queue-runner"; + Path lockPath = hydraData + "/queue-runner/lock"; + + createDirs(dirOf(lockPath)); auto lock = std::make_shared(); if (!lock->lockPaths(PathSet({lockPath}), "", false)) return 0; diff --git a/src/lib/Hydra.pm b/src/lib/Hydra.pm index 70999ebe..f4583eed 100644 --- a/src/lib/Hydra.pm +++ b/src/lib/Hydra.pm @@ -55,7 +55,7 @@ __PACKAGE__->config( }, 'Plugin::Session' => { 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 }, 'Plugin::AccessLog' => {