about summary refs log tree commit diff
path: root/scripts
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2014-07-11T14·02+0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2014-07-11T14·22+0200
commita5c6347ff06ba09530fdf0e01828aaec89f6ceb6 (patch)
treec7996e8e0ed2b8293424b2c75002c5ae0ee69490 /scripts
parentb8f24f253527e1cb071785c3b2d677ed2f734ab1 (diff)
build-remote.pl: Use ‘nix-store --serve’ on the remote side
This makes things more efficient (we don't need to use an SSH master
connection, and we only start a single remote process) and gets rid of
locking issues (the remote nix-store process will keep inputs and
outputs locked as long as they're needed).

It also makes it more or less secure to connect directly to the root
account on the build machine, using a forced command
(e.g. ‘command="nix-store --serve --write"’). This bypasses the Nix
daemon and is therefore more efficient.

Also, don't call nix-store to import the output paths.
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/build-remote.pl.in68
1 files changed, 22 insertions, 46 deletions
diff --git a/scripts/build-remote.pl.in b/scripts/build-remote.pl.in
index 6dfa16d5cbda..687b0e131066 100755
--- a/scripts/build-remote.pl.in
+++ b/scripts/build-remote.pl.in
@@ -4,7 +4,7 @@ use Fcntl qw(:DEFAULT :flock);
 use English '-no_match_vars';
 use IO::Handle;
 use Nix::Config;
-use Nix::SSH qw/sshOpts openSSHConnection/;
+use Nix::SSH;
 use Nix::CopyClosure;
 use Nix::Store;
 no warnings('once');
@@ -90,6 +90,7 @@ if (defined $conf && -e $conf) {
 
 # Wait for the calling process to ask us whether we can build some derivation.
 my ($drvPath, $hostName, $slotLock);
+my ($from, $to);
 
 REQ: while (1) {
     $_ = <STDIN> || exit 0;
@@ -195,13 +196,15 @@ REQ: while (1) {
         # Connect to the selected machine.
         @sshOpts = ("-i", $machine->{sshKeys}, "-x");
         $hostName = $machine->{hostName};
-        if (openSSHConnection($hostName)) {
-            last REQ if system("ssh $hostName @sshOpts nix-builds-inhibited < /dev/null > /dev/null 2>&1") != 0;
-            warn "machine `$hostName' is refusing builds, trying other available machines...\n";
-            closeSSHConnection;
-        } else {
-            warn "unable to open SSH connection to `$hostName', trying other available machines...\n";
-        }
+        eval {
+            ($from, $to) = connectToRemoteNix($hostName, \@sshOpts);
+            # FIXME: check if builds are inhibited.
+        };
+        last REQ unless $@;
+        print STDERR "$@";
+        warn "unable to open SSH connection to `$hostName', trying other available machines...\n";
+        $from = undef;
+        $to = undef;
         $machine->{enabled} = 0;
     }
 }
@@ -220,18 +223,6 @@ my $maybeSign = "";
 $maybeSign = "--sign" if -e "$Nix::Config::confDir/signing-key.sec";
 
 
-# Register the derivation as a temporary GC root.  Note that $PPID is
-# the PID of the remote SSH process, which, due to the use of a
-# persistant SSH connection, should be the same across all remote
-# command invocations for this session.
-my $rootsDir = "@localstatedir@/nix/gcroots/tmp";
-system("ssh $hostName @sshOpts 'mkdir -m 1777 -p $rootsDir; ln -sfn $drvPath $rootsDir/\$PPID.drv'");
-
-sub removeRoots {
-    system("ssh $hostName @sshOpts 'rm -f $rootsDir/\$PPID.drv $rootsDir/\$PPID.out'");
-}
-
-
 # Copy the derivation and its dependencies to the build machine.  This
 # is guarded by an exclusive lock per machine to prevent multiple
 # build-remote instances from copying to a machine simultaneously.
@@ -255,48 +246,33 @@ if ($@) {
     print STDERR "somebody is hogging $uploadLock, continuing...\n";
     unlink $uploadLock;
 }
-Nix::CopyClosure::copyTo($hostName, [ @sshOpts ], [ $drvPath, @inputs ], "", "", 0, 0, $maybeSign ne "", "");
+Nix::CopyClosure::copyToOpen($from, $to, $hostName, [ $drvPath, @inputs ], "", "", 0, 0, $maybeSign ne "", "");
 close UPLOADLOCK;
 
 
 # Perform the build.
-my $buildFlags =
-    "--max-silent-time $maxSilentTime --option build-timeout $buildTimeout"
-    . " --fallback --add-root $rootsDir/\$PPID.out --quiet"
-    . " --option build-keep-log false --option build-use-substitutes false";
-
-# We let the remote side kill its process group when the connection is
-# closed unexpectedly.  This is necessary to ensure that no processes
-# are left running on the remote system if the local Nix process is
-# killed.  (SSH itself doesn't kill child processes if the connection
-# is interrupted unless the `-tt' flag is used to force a pseudo-tty,
-# in which case every child receives SIGHUP; however, `-tt' doesn't
-# work on some platforms when connection sharing is used.)
 print STDERR "building `$drvPath' on `$hostName'\n";
-pipe STDIN, DUMMY; # make sure we have a readable STDIN
-if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & exec nix-store -r $drvPath $buildFlags > /dev/null' 2>&4") != 0) {
+writeInt(6, $to) or die; # == cmdBuildPaths
+writeStrings([$drvPath], $to);
+writeInt($maxSilentTime, $to);
+writeInt($buildTimeout, $to);
+my $res = readInt($from);
+if ($res != 0) {
     # Note that if we get exit code 100 from `nix-store -r', it
     # denotes a permanent build failure (as opposed to an SSH problem
     # or a temporary Nix problem).  We propagate this to the caller to
     # allow it to distinguish between transient and permanent
     # failures.
-    my $res = $? >> 8;
     print STDERR "build of `$drvPath' on `$hostName' failed with exit code $res\n";
-    removeRoots;
     exit $res;
 }
 
-#print "build of `$drvPath' on `$hostName' succeeded\n";
-
 
 # Copy the output from the build machine.
 my @outputs2 = grep { !isValidPath($_) } @outputs;
 if (scalar @outputs2 > 0) {
-    system("exec ssh $hostName @sshOpts 'nix-store --export @outputs2'" .
-           "| NIX_HELD_LOCKS='@outputs2' @bindir@/nix-store --import > /dev/null") == 0
-        or die("cannot copy paths " . join(", ", @outputs) . " from `$hostName': $?");
+    writeInt(5, $to) or die; # == cmdExportPaths
+    writeStrings(\@outputs2, $to);
+    $ENV{'NIX_HELD_LOCKS'} = "@outputs2"; # FIXME: ugly
+    importPaths(fileno($from));
 }
-
-
-# Get rid of the temporary GC roots.
-removeRoots;