about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorian Klink <flokli@flokli.de>2024-03-18T09·41+0200
committerflokli <flokli@flokli.de>2024-03-18T16·10+0000
commit997b59e4526d2313580c9c2de71285c77dc28e77 (patch)
tree7787662720cb578a3d9ae3a809d83232fc667d63
parent8fb1d0ad4bb4f1fef019d2562ecdd70e409afbe5 (diff)
feat(tvix/boot): support seeding closures via nar-bridge r/7727
This updates the parameters mkBootTest can be called with. It now
accepts a `path`, and then either importPathName needs to be set, or
isClosure needs to be set to true.

The former activates the existing functionality, tvix-store import is
used to import contents  as a NAR-addressed store path.
The latter uploads the path as a closure (so including its references,
and keeping the store paths intact) to tvix-store.
We use nar-bridge, and the HTTP interface it provides to do this.
As `nix copy` can't be used inside a Nix build, we use
`pkgs.mkBinaryCache` to come up with the .narinfo and .nar files that
would be in a binary cache, and then use a bit of GNU Parallel and bash
to upload store paths ourselves.

Change-Id: Icfa5c0af0c22ab5418686947aa2c060f5987b873
Reviewed-on: https://cl.tvl.fyi/c/depot/+/11188
Autosubmit: flokli <flokli@flokli.de>
Reviewed-by: Connor Brewster <cbrewster@hey.com>
Tested-by: BuildkiteCI
-rw-r--r--tvix/boot/tests/default.nix141
1 files changed, 99 insertions, 42 deletions
diff --git a/tvix/boot/tests/default.nix b/tvix/boot/tests/default.nix
index 378efaccc982..4204f5de9f79 100644
--- a/tvix/boot/tests/default.nix
+++ b/tvix/boot/tests/default.nix
@@ -8,56 +8,113 @@ let
     { blobServiceAddr ? "memory://"
     , directoryServiceAddr ? "memory://"
     , pathInfoServiceAddr ? "memory://"
-      # The path to import (via tvix-store import).
-    , importPath ? ../../docs
-    , importPathName ? "docs"
+
+
+      # The path to import.
+    , path
+
+      # Whether the path should be imported as a closure.
+      # If false, importPathName must be specified.
+    , isClosure ? false
+    , importPathName ? null
+
     }:
-    pkgs.stdenv.mkDerivation {
-      name = "run-vm";
-      nativeBuildInputs = [
-        depot.tvix.store
-        depot.tvix.boot.runVM
-      ];
-      buildCommand = ''
-        touch $out
-
-        # Start the tvix daemon, listening on a unix socket.
-        BLOB_SERVICE_ADDR=${blobServiceAddr} \
-          DIRECTORY_SERVICE_ADDR=${directoryServiceAddr} \
-          PATH_INFO_SERVICE_ADDR=${pathInfoServiceAddr} \
-          tvix-store daemon -l $PWD/tvix-store.socket &
-
-        # Wait for the socket to be created.
-        while [ ! -e $PWD/tvix-store.socket ]; do sleep 1; done
-
-        # Export env vars so that subsequent tvix-store commands will talk to
-        # our tvix-store daemon over the unix socket.
-        export BLOB_SERVICE_ADDR=grpc+unix://$PWD/tvix-store.socket
-        export DIRECTORY_SERVICE_ADDR=grpc+unix://$PWD/tvix-store.socket
-        export PATH_INFO_SERVICE_ADDR=grpc+unix://$PWD/tvix-store.socket
-      '' + lib.optionalString (importPath != null) ''
-        echo "Importing ${importPath} into tvix-store with name ${importPathName}…"
-        cp -R ${importPath} ${importPathName}
-        outpath=$(tvix-store import ${importPathName})
-
-        echo "imported to $outpath"
-
-        # Invoke a VM using tvix as the backing store, ensure the outpath appears in its listing.
-
-        CH_CMDLINE="tvix.find" run-tvix-vm 2>&1 | tee output.txt
-        grep $outpath output.txt
-      '';
-      requiredSystemFeatures = [ "kvm" ];
-    };
+
+      assert isClosure -> importPathName == null;
+      assert (!isClosure) -> importPathName != null;
+
+      pkgs.stdenv.mkDerivation {
+        name = "run-vm";
+
+        nativeBuildInputs = [
+          depot.tvix.store
+          depot.tvix.boot.runVM
+        ] ++ lib.optionals isClosure [
+          depot.tvix.nar-bridge
+          pkgs.curl
+          pkgs.parallel
+          pkgs.xz.bin
+        ];
+        buildCommand = ''
+          touch $out
+
+          # Start the tvix daemon, listening on a unix socket.
+          BLOB_SERVICE_ADDR=${blobServiceAddr} \
+            DIRECTORY_SERVICE_ADDR=${directoryServiceAddr} \
+            PATH_INFO_SERVICE_ADDR=${pathInfoServiceAddr} \
+            tvix-store daemon -l $PWD/tvix-store.sock &
+
+          # Wait for the socket to be created.
+          while [ ! -e $PWD/tvix-store.sock ]; do sleep 1; done
+
+          # Export env vars so that subsequent tvix-store commands will talk to
+          # our tvix-store daemon over the unix socket.
+          export BLOB_SERVICE_ADDR=grpc+unix://$PWD/tvix-store.sock
+          export DIRECTORY_SERVICE_ADDR=grpc+unix://$PWD/tvix-store.sock
+          export PATH_INFO_SERVICE_ADDR=grpc+unix://$PWD/tvix-store.sock
+        '' + lib.optionalString (!isClosure) ''
+          echo "Importing ${path} into tvix-store with name ${importPathName}…"
+          cp -R ${path} ${importPathName}
+          outpath=$(tvix-store import ${importPathName})
+
+          echo "imported to $outpath"
+        '' + lib.optionalString (isClosure) ''
+          echo "Starting nar-bridge…"
+          nar-bridge-http --store-addr=unix://$PWD/tvix-store.sock --listen-addr=$PWD/nar-bridge.sock &
+
+          # Wait for the socket to be created.
+          while [ ! -e $PWD/nar-bridge.sock ]; do sleep 1; done
+
+          # Upload. We can't use nix copy --to http://…, as it wants access to the nix db.
+          # However, we can use mkBinaryCache to assemble .narinfo and .nar.xz to upload,
+          # and then drive a HTTP client ourselves.
+          to_upload=${pkgs.mkBinaryCache { rootPaths = [path];}}
+
+          # Upload all NAR files (with some parallelism).
+          # As mkBinaryCache produces them xz-compressed, unpack them on the fly.
+          # nar-bridge doesn't care about the path we upload *to*, but a
+          # subsequent .narinfo upload need to refer to its contents (by narhash).
+          echo -e "Uploading NARs… "
+          ls -d $to_upload/nar/*.nar.xz | parallel 'xz -d < {} | curl -s -T - --unix-socket $PWD/nar-bridge.sock http://localhost:9000/nar/$(basename {} | cut -d "." -f 1).nar'
+          echo "Done."
+
+          # Upload all NARInfo files.
+          # FUTUREWORK: This doesn't upload them in order, and currently relies
+          # on PathInfoService not doing any checking.
+          # In the future, we might want to make this behaviour configurable,
+          # and disable checking here, to keep the logic simple.
+          ls -d $to_upload/*.narinfo | parallel 'curl -s -T - --unix-socket $PWD/nar-bridge.sock http://localhost:9000/$(basename {}) < {}'
+        '' + ''
+          # Invoke a VM using tvix as the backing store, ensure the outpath appears in its listing.
+
+          CH_CMDLINE="tvix.find" run-tvix-vm 2>&1 | tee output.txt
+          grep ${path} output.txt
+        '';
+        requiredSystemFeatures = [ "kvm" ];
+      };
 in
-depot.nix.readTree.drvTargets {
-  docs-memory = (mkBootTest { });
+depot.nix.readTree.drvTargets
+{
+  docs-memory = (mkBootTest {
+    path = ../../docs;
+    importPathName = "docs";
+  });
   docs-sled = (mkBootTest {
     blobServiceAddr = "sled://$PWD/blobs.sled";
     directoryServiceAddr = "sled://$PWD/directories.sled";
     pathInfoServiceAddr = "sled://$PWD/pathinfo.sled";
+    path = ../../docs;
+    importPathName = "docs";
   });
   docs-objectstore-local = (mkBootTest {
     blobServiceAddr = "objectstore+file://$PWD/blobs";
+    path = ../../docs;
+    importPathName = "docs";
+  });
+
+  closure-tvix = (mkBootTest {
+    blobServiceAddr = "objectstore+file://$PWD/blobs";
+    path = depot.tvix.store;
+    isClosure = true;
   });
 }