Add hydra package and its NixOS module.

This was originally removed in d4d0e449d7b96704eeaa2570255fb41c85c49613.
The intent was not to maintain hydra expression at two places.

Nowadays we have enough devs to maintain this despite copy/pasta.

This should encourage more people to use Hydra, which is a really
great piece of software together with Nix.

Tested a deploy using https://github.com/peti/hydra-tutorial

+575
+1
nixos/modules/module-list.nix
··· 128 ./services/continuous-integration/jenkins/default.nix 129 ./services/continuous-integration/jenkins/slave.nix 130 ./services/continuous-integration/jenkins/job-builder.nix 131 ./services/databases/4store-endpoint.nix 132 ./services/databases/4store.nix 133 ./services/databases/couchdb.nix
··· 128 ./services/continuous-integration/jenkins/default.nix 129 ./services/continuous-integration/jenkins/slave.nix 130 ./services/continuous-integration/jenkins/job-builder.nix 131 + ./services/continuous-integration/hydra/default.nix 132 ./services/databases/4store-endpoint.nix 133 ./services/databases/4store.nix 134 ./services/databases/couchdb.nix
+418
nixos/modules/services/continuous-integration/hydra/default.nix
···
··· 1 + { config, pkgs, lib, ... }: 2 + 3 + with lib; 4 + 5 + let 6 + 7 + cfg = config.services.hydra; 8 + 9 + baseDir = "/var/lib/hydra"; 10 + 11 + hydraConf = pkgs.writeScript "hydra.conf" cfg.extraConfig; 12 + 13 + hydraEnv = 14 + { HYDRA_DBI = cfg.dbi; 15 + HYDRA_CONFIG = "${baseDir}/hydra.conf"; 16 + HYDRA_DATA = "${baseDir}"; 17 + }; 18 + 19 + env = 20 + { NIX_REMOTE = "daemon"; 21 + SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt"; # Remove in 16.03 22 + PGPASSFILE = "${baseDir}/pgpass"; 23 + NIX_REMOTE_SYSTEMS = concatStringsSep ":" cfg.buildMachinesFiles; 24 + } // optionalAttrs (cfg.smtpHost != null) { 25 + EMAIL_SENDER_TRANSPORT = "SMTP"; 26 + EMAIL_SENDER_TRANSPORT_host = cfg.smtpHost; 27 + } // hydraEnv // cfg.extraEnv; 28 + 29 + serverEnv = env // 30 + { HYDRA_TRACKER = cfg.tracker; 31 + COLUMNS = "80"; 32 + PGPASSFILE = "${baseDir}/pgpass-www"; # grrr 33 + } // (optionalAttrs cfg.debugServer { DBIC_TRACE = "1"; }); 34 + 35 + localDB = "dbi:Pg:dbname=hydra;user=hydra;"; 36 + 37 + haveLocalDB = cfg.dbi == localDB; 38 + 39 + in 40 + 41 + { 42 + ###### interface 43 + options = { 44 + 45 + services.hydra = rec { 46 + 47 + enable = mkOption { 48 + type = types.bool; 49 + default = false; 50 + description = '' 51 + Whether to run Hydra services. 52 + ''; 53 + }; 54 + 55 + dbi = mkOption { 56 + type = types.str; 57 + default = localDB; 58 + example = "dbi:Pg:dbname=hydra;host=postgres.example.org;user=foo;"; 59 + description = '' 60 + The DBI string for Hydra database connection. 61 + ''; 62 + }; 63 + 64 + package = mkOption { 65 + type = types.path; 66 + default = pkgs.hydra; 67 + defaultText = "pkgs.hydra"; 68 + description = "The Hydra package."; 69 + }; 70 + 71 + hydraURL = mkOption { 72 + type = types.str; 73 + description = '' 74 + The base URL for the Hydra webserver instance. Used for links in emails. 75 + ''; 76 + }; 77 + 78 + listenHost = mkOption { 79 + type = types.str; 80 + default = "*"; 81 + example = "localhost"; 82 + description = '' 83 + The hostname or address to listen on or <literal>*</literal> to listen 84 + on all interfaces. 85 + ''; 86 + }; 87 + 88 + port = mkOption { 89 + type = types.int; 90 + default = 3000; 91 + description = '' 92 + TCP port the web server should listen to. 93 + ''; 94 + }; 95 + 96 + minimumDiskFree = mkOption { 97 + type = types.int; 98 + default = 0; 99 + description = '' 100 + Threshold of minimum disk space (GiB) to determine if the queue runner should run or not. 101 + ''; 102 + }; 103 + 104 + minimumDiskFreeEvaluator = mkOption { 105 + type = types.int; 106 + default = 0; 107 + description = '' 108 + Threshold of minimum disk space (GiB) to determine if the evaluator should run or not. 109 + ''; 110 + }; 111 + 112 + notificationSender = mkOption { 113 + type = types.str; 114 + description = '' 115 + Sender email address used for email notifications. 116 + ''; 117 + }; 118 + 119 + smtpHost = mkOption { 120 + type = types.nullOr types.str; 121 + default = null; 122 + example = ["localhost"]; 123 + description = '' 124 + Hostname of the SMTP server to use to send email. 125 + ''; 126 + }; 127 + 128 + tracker = mkOption { 129 + type = types.str; 130 + default = ""; 131 + description = '' 132 + Piece of HTML that is included on all pages. 133 + ''; 134 + }; 135 + 136 + logo = mkOption { 137 + type = types.nullOr types.path; 138 + default = null; 139 + description = '' 140 + Path to a file containing the logo of your Hydra instance. 141 + ''; 142 + }; 143 + 144 + debugServer = mkOption { 145 + type = types.bool; 146 + default = false; 147 + description = "Whether to run the server in debug mode."; 148 + }; 149 + 150 + extraConfig = mkOption { 151 + type = types.lines; 152 + description = "Extra lines for the Hydra configuration."; 153 + }; 154 + 155 + extraEnv = mkOption { 156 + type = types.attrsOf types.str; 157 + default = {}; 158 + description = "Extra environment variables for Hydra."; 159 + }; 160 + 161 + gcRootsDir = mkOption { 162 + type = types.path; 163 + default = "/nix/var/nix/gcroots/hydra"; 164 + description = "Directory that holds Hydra garbage collector roots."; 165 + }; 166 + 167 + buildMachinesFiles = mkOption { 168 + type = types.listOf types.path; 169 + default = []; 170 + example = [ "/etc/nix/machines" "/var/lib/hydra/provisioner/machines" ]; 171 + description = "List of files containing build machines."; 172 + }; 173 + 174 + useSubstitutes = mkOption { 175 + type = types.bool; 176 + default = false; 177 + description = '' 178 + Whether to use binary caches for downloading store paths. Note that 179 + binary substitutions trigger (a potentially large number of) additional 180 + HTTP requests that slow down the queue monitor thread significantly. 181 + Also, this Hydra instance will serve those downloaded store paths to 182 + its users with its own signature attached as if it had built them 183 + itself, so don't enable this feature unless your active binary caches 184 + are absolute trustworthy. 185 + ''; 186 + }; 187 + }; 188 + 189 + }; 190 + 191 + 192 + ###### implementation 193 + 194 + config = mkIf cfg.enable { 195 + 196 + users.extraGroups.hydra = { }; 197 + 198 + users.extraUsers.hydra = 199 + { description = "Hydra"; 200 + group = "hydra"; 201 + createHome = true; 202 + home = baseDir; 203 + useDefaultShell = true; 204 + }; 205 + 206 + users.extraUsers.hydra-queue-runner = 207 + { description = "Hydra queue runner"; 208 + group = "hydra"; 209 + useDefaultShell = true; 210 + home = "${baseDir}/queue-runner"; # really only to keep SSH happy 211 + }; 212 + 213 + users.extraUsers.hydra-www = 214 + { description = "Hydra web server"; 215 + group = "hydra"; 216 + useDefaultShell = true; 217 + }; 218 + 219 + nix.trustedUsers = [ "hydra-queue-runner" ]; 220 + 221 + services.hydra.extraConfig = 222 + '' 223 + using_frontend_proxy 1 224 + base_uri ${cfg.hydraURL} 225 + notification_sender ${cfg.notificationSender} 226 + max_servers 25 227 + ${optionalString (cfg.logo != null) '' 228 + hydra_logo ${cfg.logo} 229 + ''} 230 + gc_roots_dir ${cfg.gcRootsDir} 231 + ''; 232 + 233 + environment.systemPackages = [ cfg.package ]; 234 + 235 + environment.variables = hydraEnv; 236 + 237 + nix.extraOptions = '' 238 + gc-keep-outputs = true 239 + gc-keep-derivations = true 240 + 241 + # The default (`true') slows Nix down a lot since the build farm 242 + # has so many GC roots. 243 + gc-check-reachability = false 244 + ''; 245 + 246 + systemd.services.hydra-init = 247 + { wantedBy = [ "multi-user.target" ]; 248 + requires = optional haveLocalDB "postgresql.service"; 249 + after = optional haveLocalDB "postgresql.service"; 250 + environment = env; 251 + preStart = '' 252 + mkdir -p ${baseDir} 253 + chown hydra.hydra ${baseDir} 254 + chmod 0750 ${baseDir} 255 + 256 + ln -sf ${hydraConf} ${baseDir}/hydra.conf 257 + 258 + mkdir -m 0700 -p ${baseDir}/www 259 + chown hydra-www.hydra ${baseDir}/www 260 + 261 + mkdir -m 0700 -p ${baseDir}/queue-runner 262 + mkdir -m 0750 -p ${baseDir}/build-logs 263 + chown hydra-queue-runner.hydra ${baseDir}/queue-runner ${baseDir}/build-logs 264 + 265 + ${optionalString haveLocalDB '' 266 + if ! [ -e ${baseDir}/.db-created ]; then 267 + ${config.services.postgresql.package}/bin/createuser hydra 268 + ${config.services.postgresql.package}/bin/createdb -O hydra hydra 269 + touch ${baseDir}/.db-created 270 + fi 271 + ''} 272 + 273 + if [ ! -e ${cfg.gcRootsDir} ]; then 274 + 275 + # Move legacy roots directory. 276 + if [ -e /nix/var/nix/gcroots/per-user/hydra/hydra-roots ]; then 277 + mv /nix/var/nix/gcroots/per-user/hydra/hydra-roots ${cfg.gcRootsDir} 278 + fi 279 + 280 + mkdir -p ${cfg.gcRootsDir} 281 + fi 282 + 283 + # Move legacy hydra-www roots. 284 + if [ -e /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots ]; then 285 + find /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots/ -type f \ 286 + | xargs -r mv -f -t ${cfg.gcRootsDir}/ 287 + rmdir /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots 288 + fi 289 + 290 + chown hydra.hydra ${cfg.gcRootsDir} 291 + chmod 2775 ${cfg.gcRootsDir} 292 + ''; 293 + serviceConfig.ExecStart = "${cfg.package}/bin/hydra-init"; 294 + serviceConfig.PermissionsStartOnly = true; 295 + serviceConfig.User = "hydra"; 296 + serviceConfig.Type = "oneshot"; 297 + serviceConfig.RemainAfterExit = true; 298 + }; 299 + 300 + systemd.services.hydra-server = 301 + { wantedBy = [ "multi-user.target" ]; 302 + requires = [ "hydra-init.service" ]; 303 + after = [ "hydra-init.service" ]; 304 + environment = serverEnv; 305 + serviceConfig = 306 + { ExecStart = 307 + "@${cfg.package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' " 308 + + "-p ${toString cfg.port} --max_spare_servers 5 --max_servers 25 " 309 + + "--max_requests 100 ${optionalString cfg.debugServer "-d"}"; 310 + User = "hydra-www"; 311 + PermissionsStartOnly = true; 312 + Restart = "always"; 313 + }; 314 + }; 315 + 316 + systemd.services.hydra-queue-runner = 317 + { wantedBy = [ "multi-user.target" ]; 318 + requires = [ "hydra-init.service" ]; 319 + after = [ "hydra-init.service" "network.target" ]; 320 + path = [ cfg.package pkgs.nettools pkgs.openssh pkgs.bzip2 config.nix.package ]; 321 + environment = env // { 322 + PGPASSFILE = "${baseDir}/pgpass-queue-runner"; # grrr 323 + IN_SYSTEMD = "1"; # to get log severity levels 324 + }; 325 + serviceConfig = 326 + { ExecStart = "@${cfg.package}/bin/hydra-queue-runner hydra-queue-runner -v --option build-use-substitutes ${if cfg.useSubstitutes then "true" else "false"}"; 327 + ExecStopPost = "${cfg.package}/bin/hydra-queue-runner --unlock"; 328 + User = "hydra-queue-runner"; 329 + Restart = "always"; 330 + 331 + # Ensure we can get core dumps. 332 + LimitCORE = "infinity"; 333 + WorkingDirectory = "${baseDir}/queue-runner"; 334 + }; 335 + }; 336 + 337 + systemd.services.hydra-evaluator = 338 + { wantedBy = [ "multi-user.target" ]; 339 + requires = [ "hydra-init.service" ]; 340 + after = [ "hydra-init.service" "network.target" ]; 341 + path = [ pkgs.nettools ]; 342 + environment = env; 343 + serviceConfig = 344 + { ExecStart = "@${cfg.package}/bin/hydra-evaluator hydra-evaluator"; 345 + User = "hydra"; 346 + Restart = "always"; 347 + WorkingDirectory = baseDir; 348 + }; 349 + }; 350 + 351 + systemd.services.hydra-update-gc-roots = 352 + { requires = [ "hydra-init.service" ]; 353 + after = [ "hydra-init.service" ]; 354 + environment = env; 355 + serviceConfig = 356 + { ExecStart = "@${cfg.package}/bin/hydra-update-gc-roots hydra-update-gc-roots"; 357 + User = "hydra"; 358 + }; 359 + startAt = "2,14:15"; 360 + }; 361 + 362 + systemd.services.hydra-send-stats = 363 + { wantedBy = [ "multi-user.target" ]; 364 + after = [ "hydra-init.service" ]; 365 + environment = env; 366 + serviceConfig = 367 + { ExecStart = "@${cfg.package}/bin/hydra-send-stats hydra-send-stats"; 368 + User = "hydra"; 369 + }; 370 + }; 371 + 372 + # If there is less than a certain amount of free disk space, stop 373 + # the queue/evaluator to prevent builds from failing or aborting. 374 + systemd.services.hydra-check-space = 375 + { script = 376 + '' 377 + if [ $(($(stat -f -c '%a' /nix/store) * $(stat -f -c '%S' /nix/store))) -lt $((${toString cfg.minimumDiskFree} * 1024**3)) ]; then 378 + echo "stopping Hydra queue runner due to lack of free space..." 379 + systemctl stop hydra-queue-runner 380 + fi 381 + if [ $(($(stat -f -c '%a' /nix/store) * $(stat -f -c '%S' /nix/store))) -lt $((${toString cfg.minimumDiskFreeEvaluator} * 1024**3)) ]; then 382 + echo "stopping Hydra evaluator due to lack of free space..." 383 + systemctl stop hydra-evaluator 384 + fi 385 + ''; 386 + startAt = "*:0/5"; 387 + }; 388 + 389 + # Periodically compress build logs. The queue runner compresses 390 + # logs automatically after a step finishes, but this doesn't work 391 + # if the queue runner is stopped prematurely. 392 + systemd.services.hydra-compress-logs = 393 + { path = [ pkgs.bzip2 ]; 394 + script = 395 + '' 396 + find /var/lib/hydra/build-logs -type f -name "*.drv" -mtime +3 -size +0c | xargs -r bzip2 -v -f 397 + ''; 398 + startAt = "Sun 01:45"; 399 + }; 400 + 401 + services.postgresql.enable = mkIf haveLocalDB true; 402 + 403 + services.postgresql.identMap = optionalString haveLocalDB 404 + '' 405 + hydra-users hydra hydra 406 + hydra-users hydra-queue-runner hydra 407 + hydra-users hydra-www hydra 408 + hydra-users root hydra 409 + ''; 410 + 411 + services.postgresql.authentication = optionalString haveLocalDB 412 + '' 413 + local hydra all ident map=hydra-users 414 + ''; 415 + 416 + }; 417 + 418 + }
+142
pkgs/development/tools/misc/hydra/default.nix
···
··· 1 + { stdenv, nixUnstable, perlPackages, buildEnv, releaseTools, fetchFromGitHub 2 + , makeWrapper, autoconf, automake, libtool, unzip, pkgconfig, sqlite, libpqxx 3 + , gitAndTools, mercurial, darcs, subversion, bazaar, openssl, bzip2, libxslt 4 + , guile, perl, postgresql92, aws-sdk-cpp, nukeReferences, git, boehmgc 5 + , docbook_xsl, openssh, gnused, coreutils, findutils, gzip, lzma, gnutar 6 + , rpm, dpkg, cdrkit }: 7 + 8 + with stdenv; 9 + 10 + let 11 + perlDeps = buildEnv { 12 + name = "hydra-perl-deps"; 13 + paths = with perlPackages; 14 + [ ModulePluggable 15 + CatalystActionREST 16 + CatalystAuthenticationStoreDBIxClass 17 + CatalystDevel 18 + CatalystDispatchTypeRegex 19 + CatalystPluginAccessLog 20 + CatalystPluginAuthorizationRoles 21 + CatalystPluginCaptcha 22 + CatalystPluginSessionStateCookie 23 + CatalystPluginSessionStoreFastMmap 24 + CatalystPluginStackTrace 25 + CatalystPluginUnicodeEncoding 26 + CatalystTraitForRequestProxyBase 27 + CatalystViewDownload 28 + CatalystViewJSON 29 + CatalystViewTT 30 + CatalystXScriptServerStarman 31 + CryptRandPasswd 32 + DBDPg 33 + DBDSQLite 34 + DataDump 35 + DateTime 36 + DigestSHA1 37 + EmailMIME 38 + EmailSender 39 + FileSlurp 40 + IOCompress 41 + IPCRun 42 + JSONXS 43 + LWP 44 + LWPProtocolHttps 45 + NetAmazonS3 46 + NetStatsd 47 + PadWalker 48 + Readonly 49 + SQLSplitStatement 50 + SetScalar 51 + Starman 52 + SysHostnameLong 53 + TestMore 54 + TextDiff 55 + TextTable 56 + XMLSimple 57 + nixUnstable 58 + git 59 + boehmgc 60 + ]; 61 + }; 62 + in releaseTools.nixBuild rec { 63 + name = "hydra-${version}"; 64 + version = "2016-04-15"; 65 + 66 + src = fetchFromGitHub { 67 + owner = "NixOS"; 68 + repo = "hydra"; 69 + rev = "177bf25d648092826a75369191503a3f2bb763a4"; 70 + sha256 = "0ngipzm2i2vz5ygfd70hh82d027snpl85r8ncn1rxlkak0g8fxsl"; 71 + }; 72 + 73 + buildInputs = 74 + [ makeWrapper autoconf automake libtool unzip nukeReferences pkgconfig sqlite libpqxx 75 + gitAndTools.topGit mercurial darcs subversion bazaar openssl bzip2 libxslt 76 + guile # optional, for Guile + Guix support 77 + perlDeps perl nixUnstable 78 + postgresql92 # for running the tests 79 + (lib.overrideDerivation (aws-sdk-cpp.override { 80 + apis = ["s3"]; 81 + customMemoryManagement = false; 82 + }) (attrs: { 83 + src = fetchFromGitHub { 84 + owner = "edolstra"; 85 + repo = "aws-sdk-cpp"; 86 + rev = "local"; 87 + sha256 = "1vhgsxkhpai9a7dk38q4r239l6dsz2jvl8hii24c194lsga3g84h"; 88 + }; 89 + })) 90 + ]; 91 + 92 + hydraPath = lib.makeBinPath ( 93 + [ libxslt sqlite subversion openssh nixUnstable coreutils findutils 94 + gzip bzip2 lzma gnutar unzip git gitAndTools.topGit mercurial darcs gnused bazaar 95 + ] ++ lib.optionals stdenv.isLinux [ rpm dpkg cdrkit ] ); 96 + 97 + postUnpack = '' 98 + # Clean up when building from a working tree. 99 + (cd $sourceRoot && (git ls-files -o --directory | xargs -r rm -rfv)) || true 100 + ''; 101 + 102 + configureFlags = [ "--with-docbook-xsl=${docbook_xsl}/xml/xsl/docbook" ]; 103 + 104 + preHook = '' 105 + PATH=$(pwd)/src/script:$(pwd)/src/hydra-eval-jobs:$(pwd)/src/hydra-queue-runner:$PATH 106 + PERL5LIB=$(pwd)/src/lib:$PERL5LIB; 107 + ''; 108 + 109 + preConfigure = "autoreconf -vfi"; 110 + 111 + enableParallelBuilding = true; 112 + 113 + preCheck = '' 114 + patchShebangs . 115 + export LOGNAME=${LOGNAME:-foo} 116 + ''; 117 + 118 + postInstall = '' 119 + mkdir -p $out/nix-support 120 + for i in $out/bin/*; do 121 + read -n 4 chars < $i 122 + if [[ $chars =~ ELF ]]; then continue; fi 123 + wrapProgram $i \ 124 + --prefix PERL5LIB ':' $out/libexec/hydra/lib:$PERL5LIB \ 125 + --prefix PATH ':' $out/bin:$hydraPath \ 126 + --set HYDRA_RELEASE ${version} \ 127 + --set HYDRA_HOME $out/libexec/hydra \ 128 + --set NIX_RELEASE ${nixUnstable.name or "unknown"} 129 + done 130 + ''; # */ 131 + 132 + dontStrip = true; 133 + 134 + passthru.perlDeps = perlDeps; 135 + 136 + meta = with stdenv.lib; { 137 + description = "Nix-based continuous build system"; 138 + license = licenses.gpl3; 139 + platforms = platforms.linux; 140 + maintainers = with maintainers; [ domenkozar ]; 141 + }; 142 + }
+2
pkgs/top-level/all-packages.nix
··· 7373 7374 hwloc = callPackage ../development/libraries/hwloc {}; 7375 7376 hydraAntLogger = callPackage ../development/libraries/java/hydra-ant-logger { }; 7377 7378 hyena = callPackage ../development/libraries/hyena { };
··· 7373 7374 hwloc = callPackage ../development/libraries/hwloc {}; 7375 7376 + hydra = callPackage ../development/tools/misc/hydra {}; 7377 + 7378 hydraAntLogger = callPackage ../development/libraries/java/hydra-ant-logger { }; 7379 7380 hyena = callPackage ../development/libraries/hyena { };
+12
pkgs/top-level/perl-packages.nix
··· 9444 }; 9445 }; 9446 9447 NetTwitterLite = buildPerlPackage { 9448 name = "Net-Twitter-Lite-0.11002"; 9449 src = fetchurl {
··· 9444 }; 9445 }; 9446 9447 + NetStatsd = buildPerlPackage { 9448 + name = "Net-Statsd-0.11"; 9449 + src = fetchurl { 9450 + url = mirror://cpan/authors/id/C/CO/COSIMO/Net-Statsd-0.11.tar.gz; 9451 + sha256 = "0f56c95846c7e65e6d32cec13ab9df65716429141f106d2dc587f1de1e09e163"; 9452 + }; 9453 + meta = { 9454 + description = "Sends statistics to the stats daemon over UDP"; 9455 + license = "perl"; 9456 + }; 9457 + }; 9458 + 9459 NetTwitterLite = buildPerlPackage { 9460 name = "Net-Twitter-Lite-0.11002"; 9461 src = fetchurl {