aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2017-04-28 20:28:16 +0300
committerIgor Pashev <pashev.igor@gmail.com>2017-04-29 11:05:10 +0300
commitc2afdc99216156f87f436c52c82e286dd5f7e655 (patch)
treea325ca59b5ec19923952ddb5a1a3b776e50de9ae
parent10c8ff2047ad7ae45cd2cf60d77456dc205659fa (diff)
downloadnixsap-c2afdc99216156f87f436c52c82e286dd5f7e655.tar.gz
Added nix-serve app and package
-rw-r--r--modules/apps/nix-serve.nix126
-rw-r--r--modules/pkgs/nix-serve/default.nix31
-rw-r--r--modules/pkgs/nix-serve/nix-serve.psgi61
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"]];
+ }
+}