From c909c5671bb9d70b1e0aa91db8c2f79a8b8657e8 Mon Sep 17 00:00:00 2001
From: Jean-Paul Calderone <exarkun@twistedmatrix.com>
Date: Mon, 22 Jun 2020 13:36:14 -0400
Subject: [PATCH] Tests for and improvements to the Tahoe-LAFS systemd
 definition

This removes the code that might delete a Tahoe-LAFS nodedir and replaces it
with code that will try to save an unexpected copy of it out of the way
instead.
---
 nixos/modules/tahoe.nix                 | 26 ++++++---
 nixos/modules/tests/private-storage.nix |  9 ++--
 nixos/modules/tests/tahoe.nix           | 72 +++++++++++++++++++++++++
 nixos/system-tests.nix                  | 10 ++--
 4 files changed, 102 insertions(+), 15 deletions(-)
 create mode 100644 nixos/modules/tests/tahoe.nix

diff --git a/nixos/modules/tahoe.nix b/nixos/modules/tahoe.nix
index 49881093..cb0abf08 100644
--- a/nixos/modules/tahoe.nix
+++ b/nixos/modules/tahoe.nix
@@ -241,22 +241,36 @@ in
               created = "${nodedir}.created";
               atomic = "${nodedir}.atomic";
             in ''
+              set -eo pipefail
               if [ ! -e ${created} ]; then
                 mkdir -p /var/db/tahoe-lafs/
 
                 # Get rid of any prior partial efforts.  It might not exist.
                 # Don't let this tank us.
-                rm -rv ${atomic} && [ ! -e ${atomic} ]
+                rm -rv ${atomic} || [ ! -e ${atomic} ]
 
                 # Really create the node.
                 tahoe create-node --hostname=localhost ${atomic}
 
-                # Move it to the real location.  We don't create it in-place
-                # because we might fail partway through and leave inconsistent
-                # state.  Also, systemd probably created logs/incidents/ already and
-                # `create-node` complains if it finds these exist already.
-                rm -rv ${nodedir} && [ ! -e ${nodedir} ]
+                # Get rid of any existing partially created node directory
+                # that might be in the way.
+                if [ -e ${nodedir} ]; then
+                  for backup in $(seq 1 100); do
+                    if [ ! -e ${nodedir}.$backup ]; then
+                      mv ${nodedir} ${nodedir}.$backup
+                      break
+                    fi
+                  done
+                fi
+
+                # Move the new thing to the real location.  We don't create it
+                # in-place because we might fail partway through and leave
+                # inconsistent state.  Also, systemd probably created
+                # logs/incidents/ already and `create-node` complains if it
+                # finds these exist already.
                 mv ${atomic} ${nodedir}
+
+                # Record our complete, consistent success.
                 touch ${created}
               fi
 
diff --git a/nixos/modules/tests/private-storage.nix b/nixos/modules/tests/private-storage.nix
index 4b01cdff..b51f85cf 100644
--- a/nixos/modules/tests/private-storage.nix
+++ b/nixos/modules/tests/private-storage.nix
@@ -1,5 +1,5 @@
+{ pkgs }:
 let
-  pkgs = import <nixpkgs> { };
   pspkgs = import ../pspkgs.nix { inherit pkgs; };
 
   sshPrivateKey = ./probeuser_ed25519;
@@ -89,10 +89,9 @@ let
       # succeed() is not success but 1 is.
       1;
       ";
-in
-# https://nixos.org/nixos/manual/index.html#sec-nixos-tests
-import <nixpkgs/nixos/tests/make-test.nix> {
-
+in {
+  # https://nixos.org/nixos/manual/index.html#sec-nixos-tests
+  # https://nixos.mayflower.consulting/blog/2019/07/11/leveraging-nixos-tests-in-your-project/
   nodes = rec {
     # Get a machine where we can run a Tahoe-LAFS client node.
     client =
diff --git a/nixos/modules/tests/tahoe.nix b/nixos/modules/tests/tahoe.nix
new file mode 100644
index 00000000..be3d51a0
--- /dev/null
+++ b/nixos/modules/tests/tahoe.nix
@@ -0,0 +1,72 @@
+{ ... }: {
+  nodes = {
+    storage = { config, pkgs, ... }: {
+      imports = [
+        ../tahoe.nix
+      ];
+
+      services.tahoe.nodes.storage = {
+        package = (pkgs.callPackage ../pspkgs.nix { }).privatestorage;
+        sections = {
+          node = {
+            nickname = "storage";
+            "web.port" = "tcp:4000:interface=127.0.0.1";
+            "tub.port" = "tcp:4001";
+            "tub.location" = "tcp:127.0.0.1:4001";
+          };
+          storage = {
+            enabled = true;
+          };
+        };
+      };
+    };
+  };
+  testScript = ''
+  startAll;
+
+  # After the service starts, destroy the "created" marker to force it to
+  # re-create its internal state.
+  $storage->waitForOpenPort(4001);
+  $storage->succeed("systemctl stop tahoe.storage");
+  $storage->succeed("rm /var/db/tahoe-lafs/storage.created");
+  $storage->succeed("systemctl start tahoe.storage");
+
+  # After it starts up again, verify it has consistent internal state and a
+  # backup of the prior state.
+  $storage->waitForOpenPort(4001);
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage ]");
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage.created ]");
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage.1 ]");
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage.1/private/node.privkey ]");
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage.1/private/node.pem ]");
+  $storage->succeed("[ ! -e /var/db/tahoe-lafs/storage.2 ]");
+
+  # Stop it again, once again destroy the "created" marker, and this time also
+  # jam some partial state in the way that will need cleanup.
+  $storage->succeed("systemctl stop tahoe.storage");
+  $storage->succeed("rm /var/db/tahoe-lafs/storage.created");
+  $storage->succeed("mkdir -p /var/db/tahoe-lafs/storage.atomic/partial");
+  eval {
+    $storage->succeed("systemctl start tahoe.storage");
+    1;
+  } or do {
+    my ($x, $y) = $storage->execute("journalctl -u tahoe.storage");
+    $storage->log($y);
+    die $@;
+  };
+
+  # After it starts up again, verify it has consistent internal state and
+  # backups of the prior two states.  It also has no copy of the inconsistent
+  # state because it could never have been used.
+  $storage->waitForOpenPort(4001);
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage ]");
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage.created ]");
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage.1 ]");
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage.2 ]");
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage.2/private/node.privkey ]");
+  $storage->succeed("[ -e /var/db/tahoe-lafs/storage.2/private/node.pem ]");
+  $storage->succeed("[ ! -e /var/db/tahoe-lafs/storage.atomic ]");
+  $storage->succeed("[ ! -e /var/db/tahoe-lafs/storage/partial ]");
+  $storage->succeed("[ ! -e /var/db/tahoe-lafs/storage.3 ]");
+  '';
+}
diff --git a/nixos/system-tests.nix b/nixos/system-tests.nix
index 5cc4088c..b2556d46 100644
--- a/nixos/system-tests.nix
+++ b/nixos/system-tests.nix
@@ -1,5 +1,7 @@
 # The overall system test suite for PrivateStorageio NixOS configuration.
-
-# There is only one system test so far so I don't have to do anything to
-# aggregate multiple tests...
-import ./modules/tests/private-storage.nix
+let
+  pkgs = import <nixpkgs> { };
+in {
+  private-storage = pkgs.nixosTest ./modules/tests/private-storage.nix;
+  tahoe = pkgs.nixosTest ./modules/tests/tahoe.nix;
+}
-- 
GitLab