diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2017-04-28 20:28:16 +0300 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2017-04-29 11:05:10 +0300 |
commit | c2afdc99216156f87f436c52c82e286dd5f7e655 (patch) | |
tree | a325ca59b5ec19923952ddb5a1a3b776e50de9ae | |
parent | 10c8ff2047ad7ae45cd2cf60d77456dc205659fa (diff) | |
download | nixsap-c2afdc99216156f87f436c52c82e286dd5f7e655.tar.gz |
Added nix-serve app and package
-rw-r--r-- | modules/apps/nix-serve.nix | 126 | ||||
-rw-r--r-- | modules/pkgs/nix-serve/default.nix | 31 | ||||
-rw-r--r-- | modules/pkgs/nix-serve/nix-serve.psgi | 61 |
3 files changed, 218 insertions, 0 deletions
diff --git a/modules/apps/nix-serve.nix b/modules/apps/nix-serve.nix new file mode 100644 index 0000000..20deab6 --- /dev/null +++ b/modules/apps/nix-serve.nix @@ -0,0 +1,126 @@ +{ config, pkgs, lib, ... }: + +let + + inherit (lib) + mkEnableOption mkIf mkOption optionalString ; + + inherit (lib.types) + int nullOr package path str ; + + cfg = config.nixsap.apps.nix-serve; + + start = + let + maybeTCP = optionalString (cfg.port != null) + "--listen '${cfg.address}:${toString cfg.port}'"; + in pkgs.writeBashScriptBin "nix-serve" '' + umask 0117 # for socket mode + + export NIX_REMOTE="daemon" + + ${optionalString (cfg.secretKeyFile != null) '' + export NIX_SECRET_KEY_FILE='${cfg.secretKeyFile}' + ''} + + exec "${cfg.package}/bin/nix-serve" \ + ${maybeTCP} \ + --listen '${cfg.socket}' \ + --workers ${toString cfg.workers} + ''; + +in +{ + options = { + nixsap.apps.nix-serve = { + enable = mkEnableOption "nix-serve, the standalone Nix binary cache server"; + + user = mkOption { + description = "User and group to run as"; + type = str; + default = "nix-serve"; + }; + + home = mkOption { + description = "Home directory (currently for Unix socket only)"; + type = path; + default = "/nix-serve"; + }; + + package = mkOption { + description = "nix-serve package"; + type = package; + default = pkgs.nix-serve; + }; + + workers = mkOption { + type = int; + default = 5; + description = "Specifies the number of worker pool"; + }; + + port = mkOption { + type = nullOr int; + default = null; + description = '' + Port number where nix-serve will listen on in addition to Unix + socket. By default nix-serve listens on Unix socket only. + ''; + }; + + address = mkOption { + type = str; + default = "127.0.0.1"; + description = '' + IP address where nix-serve will bind its TCP listening socket. + ''; + }; + + socket = mkOption { + description = '' + Unix socket to listen on. + ''; + readOnly = true; + type = path; + default = "${cfg.home}/socket"; + }; + + secretKeyFile = mkOption { + type = nullOr path; + default = null; + description = '' + The path to the file used for signing derivation data. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + nix.allowedUsers = [ cfg.user ]; + + nixsap.deployment.keyrings.${cfg.user} = [ cfg.secretKeyFile ]; + nixsap.system.users.daemons = [ cfg.user ]; + + systemd.services.nix-serve = { + description = "nix-serve binary cache server"; + wantedBy = [ "multi-user.target" ]; + wants = [ "keys.target" ]; + after = [ "keys.target" "network.target" "local-fs.target" ]; + + preStart = '' + mkdir -p -- '${cfg.home}' + rm -rf -- '${cfg.socket}' + chown -Rc '${cfg.user}:${cfg.user}' -- '${cfg.home}' + chmod -Rc u=rwX,g=rX,o= -- '${cfg.home}' + ''; + + serviceConfig = { + ExecStart = "${start}/bin/nix-serve"; + KillMode = "mixed"; + PermissionsStartOnly = true; + Restart = "always"; + User = cfg.user; + }; + }; + }; +} diff --git a/modules/pkgs/nix-serve/default.nix b/modules/pkgs/nix-serve/default.nix new file mode 100644 index 0000000..7dcd9df --- /dev/null +++ b/modules/pkgs/nix-serve/default.nix @@ -0,0 +1,31 @@ +{ stdenv, coreutils, pxz, nix, perl, perlPackages }: + +let + inherit (stdenv.lib) + makeBinPath + ; + +in stdenv.mkDerivation { + name = "nix-serve"; + + src = "${./nix-serve.psgi}"; + + buildInputs = [ pxz perl nix ] + ++ (with perlPackages; [ DBI DBDSQLite Plack Starman ]); + + phases = [ "installPhase" ]; + + installPhase = '' + mkdir -p $out/libexec/nix-serve + cat "$src" > "$out/libexec/nix-serve.psgi" + + mkdir -p $out/bin + cat > $out/bin/nix-serve <<EOF + #! ${stdenv.shell} + export PATH=${makeBinPath [ coreutils pxz nix ]}:\$PATH + export PERL5LIB=$PERL5LIB + exec ${perlPackages.Starman}/bin/starman "$out/libexec/nix-serve.psgi" "\$@" + EOF + chmod +x $out/bin/nix-serve + ''; +} diff --git a/modules/pkgs/nix-serve/nix-serve.psgi b/modules/pkgs/nix-serve/nix-serve.psgi new file mode 100644 index 0000000..ac4071b --- /dev/null +++ b/modules/pkgs/nix-serve/nix-serve.psgi @@ -0,0 +1,61 @@ +# This is nix-serve (https://github.com/edolstra/nix-serve) using pxz instead of bzip2 +use MIME::Base64; +use Nix::Config; +use Nix::Manifest; +use Nix::Store; +use Nix::Utils; +use strict; + +sub stripPath { + my ($x) = @_; + $x =~ s/.*\///; $x +} + +my $app = sub { + my $env = shift; + my $path = $env->{PATH_INFO}; + + if ($path eq "/nix-cache-info") { + return [200, ['Content-Type' => 'text/plain'], ["StoreDir: $Nix::Config::storeDir\nWantMassQuery: 1\nPriority: 30\n"]]; + } + + elsif ($path =~ "/([0-9a-z]+)\.narinfo") { + my $hashPart = $1; + my $storePath = queryPathFromHashPart($hashPart); + return [404, ['Content-Type' => 'text/plain'], ["No such path.\n"]] unless $storePath; + my ($deriver, $narHash, $time, $narSize, $refs) = queryPathInfo($storePath, 1) or die; + my $res = + "StorePath: $storePath\n" . + "URL: nar/$hashPart.nar.xz\n" . + "Compression: xz\n" . + "NarHash: $narHash\n" . + "NarSize: $narSize\n"; + $res .= "References: " . join(" ", map { stripPath($_) } @$refs) . "\n" + if scalar @$refs > 0; + $res .= "Deriver: " . stripPath($deriver) . "\n" if defined $deriver; + my $secretKeyFile = $ENV{'NIX_SECRET_KEY_FILE'}; + if (defined $secretKeyFile) { + my $s = readFile $secretKeyFile; + chomp $s; + my ($keyName, $secretKey) = split ":", $s; + die "invalid secret key file ‘$secretKeyFile’\n" unless defined $keyName && defined $secretKey; + my $fingerprint = fingerprintPath($storePath, $narHash, $narSize, $refs); + my $sig = encode_base64(signString(decode_base64($secretKey), $fingerprint), ""); + $res .= "Sig: $keyName:$sig\n"; + } + return [200, ['Content-Type' => 'text/x-nix-narinfo'], [$res]]; + } + + elsif ($path =~ "/nar/([0-9a-z]+)\.nar.xz") { + my $hashPart = $1; + my $storePath = queryPathFromHashPart($hashPart); + return [404, ['Content-Type' => 'text/plain'], ["No such path.\n"]] unless $storePath; + my $fh = new IO::Handle; + open $fh, "nix-store --dump '$storePath' | nice -n 19 pxz -0 |"; + return [200, ['Content-Type' => 'application/x-xz'], $fh]; + } + + else { + return [404, ['Content-Type' => 'text/plain'], ["File not found.\n"]]; + } +} |