diff --git a/.circleci/config.yml b/.circleci/config.yml index 963f0928eb7e7dc3ffd2fc7b99bed4ac4ee4cddc..3256c18de0ec272c5182b93fb0d8b423cdaa2fc2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,6 +15,23 @@ version: 2 jobs: + test: + docker: + - image: "nixos/nix:2.2.1" + steps: + - run: + name: "Install Git" + command: | + # Required for the checkout step + nix-env -i git openssh + + - "checkout" + + - run: + name: "Run Tests" + command: | + nix-build nixos/unit-tests.nix && cat result + build: docker: - image: "nixos/nix:2.2.1" @@ -24,8 +41,8 @@ jobs: command: | # Required for cache and artifact interactions. Though we use a # nix image, it's actually an alpine base... The CircleCI cache - # management and artifact uploader doesn't know how to use the nix - # ca bundle we could install. + # management (which we stopped using) and artifact uploader don't + # know how to use the nix ca bundle we could install. apk update apk add ca-certificates @@ -35,21 +52,12 @@ jobs: # Required for the checkout step nix-env -i git openssh - - restore_cache: - keys: - - "v1-nix-store" - - "checkout" - run: name: "Nix Build" command: | - nix-build - - - save_cache: - key: "v1-nix-store" - paths: - - "/nix/store" + nix-build docs.nix - store_artifacts: path: "result/docs" @@ -59,4 +67,5 @@ workflows: version: 2 everything: jobs: + - "test" - "build" diff --git a/README.rst b/README.rst index 5fb1dfc668cf9de6c6c4207684836bbe881366fa..675c9a7fbf9f17f63f025ca843d8e985735b366b 100644 --- a/README.rst +++ b/README.rst @@ -2,3 +2,36 @@ PrivateStorageio ================ The backend for a private, secure, and end-to-end encrypted storage solution + +Building +-------- + +The build system uses `Nix`_ which must be installed before anything can be built. + +Documentation +~~~~~~~~~~~~~ + +The documentation can be built using this command:: + + $ nix-build docs.nix + +The documentation is also built on and published by CI. + +Testing +------- + +The test system uses `Nix`_ which must be installed before any tests can be run. + +Unit tests are run using this command:: + + $ nix-build nixos/unit-tests.nix + +Unit tests are also run on CI. + +The system tests are run using this command:: + + $ nix-build nixos/system-tests.nix + +The system tests boot QEMU VMs which prevents them from running on CI at this time. + +.. _Nix: https://nixos.org/nix diff --git a/default.nix b/docs.nix similarity index 100% rename from default.nix rename to docs.nix diff --git a/docs/source/README.rst b/docs/source/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..068656ccfbf7aabee907d6ffb3ac28c37415f7b7 --- /dev/null +++ b/docs/source/README.rst @@ -0,0 +1,2 @@ +.. include:: + ../../README.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index d06ae0d59b4ca91cbd00cd4a440da275f26f609e..e712e0e44c7d5e4b83f94c8798b2d089cf12edda 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -10,8 +10,11 @@ Welcome to PrivateStorageio's documentation! :maxdepth: 2 :caption: Contents: + README architecture-overview + + Indices and tables ================== diff --git a/nixos/lib/ini.nix b/nixos/lib/ini.nix new file mode 100644 index 0000000000000000000000000000000000000000..43f211b121fddcf0bfddbfa3a4e620667d2147b4 --- /dev/null +++ b/nixos/lib/ini.nix @@ -0,0 +1,53 @@ +# Functionality related to writing out ini syntax files (like Tahoe-LAFS' +# tahoe.cfg). +{ pkgs ? import <nixpkgs> { } }: +let lib = pkgs.lib; +in rec { + # Get the .ini-file-appropriate string representation of a simple value. + # + # > toINIString "hello" + # "hello" + # > toINIString true + # "true" + toINIString = v: + if builtins.isBool v then builtins.toJSON v + else builtins.toString v; + + # Map a function over an attrset and concatenate the string results. + # + # > concatMapAttrsToList (n: v: "${n} = ${v}\n") { a = "b"; c = "d"; } + # "a = b\nc = d\n" + concatMapAttrsToList = f: a: + lib.strings.concatStrings (lib.attrsets.mapAttrsToList f a); + + # Generate one line of configuration defining one item in one section. + # + # > oneConfigItemText "foo" "bar" + # "foo = bar\n" + oneConfigItemText = name: value: + "${name} = ${toINIString value}\n"; + + # Generate all lines of configuration defining all items in one section. + # + # > allConfigItemsText { foo = "bar"; baz = "quux"; } + # "foo = bar\nbaz = quux" + allConfigItemsText = items: + concatMapAttrsToList oneConfigItemText items; + + # Generate all lines of configuration for one section, header + # and items included. + # + # > oneConfigSectionText "foo" { bar = "baz"; } + # "[foo]\nbar = baz\n" + oneConfigSectionText = name: value: '' + [${name}] + ${allConfigItemsText value}''; + + # Generate all lines of configuration for all sections, headers + # and items included. + # + # > allConfigSectionsText { foo = { bar = "baz"; }; } + # "[foo]\nbar = baz\n" + allConfigSectionsText = sections: + concatMapAttrsToList oneConfigSectionText sections; +} diff --git a/nixos/lib/tests/test_ini.nix b/nixos/lib/tests/test_ini.nix new file mode 100644 index 0000000000000000000000000000000000000000..c9fb90b7a4d4059ce403f230e3a6731bd22595a5 --- /dev/null +++ b/nixos/lib/tests/test_ini.nix @@ -0,0 +1,64 @@ +ini: +{ test_empty = + { expected = ""; + expr = ini.allConfigSectionsText { }; + }; + + test_one_empty_section = + { expected = '' + [foo] + ''; + expr = ini.allConfigSectionsText { foo = { }; }; + }; + + test_one_section_one_item = + { expected = '' + [foo] + bar = baz + ''; + expr = ini.allConfigSectionsText { foo = { bar = "baz"; }; }; + }; + + test_one_section_two_items = + { expected = '' + [foo] + bar = baz + foobar = quux + ''; + expr = ini.allConfigSectionsText { foo = { bar = "baz"; foobar = "quux"; }; }; + }; + + test_two_sections = + { expected = '' + [alpha] + beta = gamma + [foo] + bar = baz + foobar = quux + ''; + expr = ini.allConfigSectionsText + { foo = { bar = "baz"; foobar = "quux"; }; + alpha = { beta = "gamma"; }; + }; + }; + + test_true = + { expected = "x = true\n"; + expr = ini.oneConfigItemText "x" true; + }; + + test_false = + { expected = "x = false\n"; + expr = ini.oneConfigItemText "x" false; + }; + + test_integer = + { expected = "x = 12345\n"; + expr = ini.oneConfigItemText "x" 12345; + }; + + test_dotted_key = + { expected = "x.y = z\n"; + expr = ini.oneConfigItemText "x.y" "z"; + }; +} diff --git a/nixos/modules/overlays.nix b/nixos/modules/overlays.nix new file mode 100644 index 0000000000000000000000000000000000000000..7e22c2f1d06f56e474e63492d3232ce9ef545cd7 --- /dev/null +++ b/nixos/modules/overlays.nix @@ -0,0 +1,43 @@ +self: super: { + python27 = super.python27.override { + packageOverrides = python-self: python-super: { + # Get the newest Tahoe-LAFS as a module instead of an application. + tahoe-lafs = python-super.toPythonModule (python-super.callPackage ../pkgs/tahoe-lafs.nix { }); + + # Get our ZKAP authorizer plugin package. + zkapauthorizer = python-self.callPackage ../pkgs/zkapauthorizer.nix { }; + + # new tahoe-lafs has a new dependency on eliot. + eliot = python-super.callPackage ../pkgs/eliot.nix { }; + + # new tahoe-lafs depends on a very recent autobahn for better websocket + # testing features. + autobahn = python-super.callPackage ../pkgs/autobahn.nix { }; + + # new autobahn requires a newer cryptography + cryptography = python-super.callPackage ../pkgs/cryptography.nix { }; + + # new cryptography requires a newer cryptography_vectors + cryptography_vectors = python-super.callPackage ../pkgs/cryptography_vectors.nix { }; + + # upstream twisted package is missing a recently added dependency. + twisted = python-super.twisted.overrideAttrs (old: + { propagatedBuildInputs = old.propagatedBuildInputs ++ [ python-super.appdirs ]; + checkPhase = '' + ${self.python.interpreter} -m twisted.trial twisted + ''; + }); + + }; + }; + + privatestorage = self.python27.buildEnv.override + { extraLibs = + [ self.python27Packages.tahoe-lafs + self.python27Packages.zkapauthorizer + ]; + # Twisted's dropin.cache always collides between different + # plugin-providing packages. + ignoreCollisions = true; + }; +} diff --git a/nixos/modules/private-storage.nix b/nixos/modules/private-storage.nix new file mode 100644 index 0000000000000000000000000000000000000000..00065052805eee80cd6302295ade199c08ac4d57 --- /dev/null +++ b/nixos/modules/private-storage.nix @@ -0,0 +1,56 @@ +# A NixOS module which can instantiate a Tahoe-LAFS storage server in the +# preferred configuration for the Private Storage grid. +{ pkgs, lib, config, ... }: +let + pspkgs = import pkgs.path + { overlays = [ (import ./overlays.nix) ]; + }; + cfg = config.services.private-storage; +in +{ + + # Upstream tahoe-lafs module conflicts with ours (since ours is a + # copy/paste/edit of upstream's...). Disable + # it. + # + # https://nixos.org/nixos/manual/#sec-replace-modules + disabledModules = + [ "services/network-filesystems/tahoe.nix" + ]; + + # Load our tahoe-lafs module. + imports = + [ ./tahoe.nix + ]; + + options = + { services.private-storage.enable = lib.mkEnableOption "private storage service"; + services.private-storage.tahoe.package = lib.mkOption + { default = pspkgs.privatestorage; + type = lib.types.package; + example = lib.literalExample "pkgs.tahoelafs"; + description = '' + The package to use for the Tahoe-LAFS daemon. + ''; + }; + }; + config = lib.mkIf cfg.enable + { services.tahoe.nodes."storage" = + { package = config.services.private-storage.tahoe.package; + sections = + { node = + # XXX Should try to name that is unique across the grid. + { nickname = "storage"; + "web.port" = "tcp:3456:interface=127.0.0.1"; + }; + storage = + { enabled = true; + plugins = "privatestorageio-zkapauthz-v1"; + }; + "storageserver.plugins.privatestorageio-zkapauthz-v1" = + { + }; + }; + }; + }; +} diff --git a/nixos/modules/tahoe.nix b/nixos/modules/tahoe.nix new file mode 100644 index 0000000000000000000000000000000000000000..05e68d4fd8c8bb83be85afefc6f1de66439cad8e --- /dev/null +++ b/nixos/modules/tahoe.nix @@ -0,0 +1,217 @@ +# Copy/pasted from nixos/modules/services/network-filesystems/tahoe.nix :/ We +# require control over additional configuration options compared to upstream +# and it's not clear how to do this without duplicating everything. +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.tahoe; + ini = pkgs.callPackage ../lib/ini.nix { }; +in + { + options.services.tahoe = { + introducers = mkOption { + default = {}; + type = with types; attrsOf (submodule { + options = { + nickname = mkOption { + type = types.str; + description = '' + The nickname of this Tahoe introducer. + ''; + }; + tub.port = mkOption { + default = 3458; + type = types.int; + description = '' + The port on which the introducer will listen. + ''; + }; + tub.location = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The external location that the introducer should listen on. + + If specified, the port should be included. + ''; + }; + package = mkOption { + default = pkgs.tahoelafs; + defaultText = "pkgs.tahoelafs"; + type = types.package; + example = literalExample "pkgs.tahoelafs"; + description = '' + The package to use for the Tahoe LAFS daemon. + ''; + }; + }; + }); + description = '' + The Tahoe introducers. + ''; + }; + nodes = mkOption { + default = {}; + type = with types; attrsOf (submodule { + options = { + sections = mkOption { + type = types.attrs; + description = '' + Top-level configuration sections. + ''; + default = { + "node" = { }; + "client" = { }; + "storage" = { }; + }; + }; + package = mkOption { + default = pkgs.tahoelafs; + defaultText = "pkgs.tahoelafs"; + type = types.package; + example = literalExample "pkgs.tahoelafs"; + description = '' + The package to use for the Tahoe LAFS daemon. + ''; + }; + }; + }); + description = '' + The Tahoe nodes. + ''; + }; + }; + config = mkMerge [ + (mkIf (cfg.introducers != {}) { + environment = { + etc = flip mapAttrs' cfg.introducers (node: settings: + nameValuePair "tahoe-lafs/introducer-${node}.cfg" { + mode = "0444"; + text = '' + # This configuration is generated by Nix. Edit at your own + # peril; here be dragons. + + [node] + nickname = ${settings.nickname} + tub.port = ${toString settings.tub.port} + ${optionalString (settings.tub.location != null) + "tub.location = ${settings.tub.location}"} + ''; + }); + # Actually require Tahoe, so that we will have it installed. + systemPackages = flip mapAttrsToList cfg.introducers (node: settings: + settings.package + ); + }; + # Open up the firewall. + # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.introducers + # (node: settings: settings.tub.port); + systemd.services = flip mapAttrs' cfg.introducers (node: settings: + let + pidfile = "/run/tahoe.introducer-${node}.pid"; + # This is a directory, but it has no trailing slash. Tahoe commands + # get antsy when there's a trailing slash. + nodedir = "/var/db/tahoe-lafs/introducer-${node}"; + in nameValuePair "tahoe.introducer-${node}" { + description = "Tahoe LAFS node ${node}"; + wantedBy = [ "multi-user.target" ]; + path = [ settings.package ]; + restartTriggers = [ + config.environment.etc."tahoe-lafs/introducer-${node}.cfg".source ]; + serviceConfig = { + Type = "simple"; + PIDFile = pidfile; + # Believe it or not, Tahoe is very brittle about the order of + # arguments to $(tahoe run). The node directory must come first, + # and arguments which alter Twisted's behavior come afterwards. + ExecStart = '' + ${settings.package}/bin/tahoe run ${lib.escapeShellArg nodedir} -n -l- --pidfile=${lib.escapeShellArg pidfile} + ''; + }; + preStart = '' + if [ ! -d ${lib.escapeShellArg nodedir} ]; then + mkdir -p /var/db/tahoe-lafs + tahoe create-introducer ${lib.escapeShellArg nodedir} + fi + + # Tahoe has created a predefined tahoe.cfg which we must now + # scribble over. + # XXX I thought that a symlink would work here, but it doesn't, so + # we must do this on every prestart. Fixes welcome. + # rm ${nodedir}/tahoe.cfg + # ln -s /etc/tahoe-lafs/introducer-${node}.cfg ${nodedir}/tahoe.cfg + cp /etc/tahoe-lafs/introducer-"${node}".cfg ${lib.escapeShellArg nodedir}/tahoe.cfg + ''; + }); + users.users = flip mapAttrs' cfg.introducers (node: _: + nameValuePair "tahoe.introducer-${node}" { + description = "Tahoe node user for introducer ${node}"; + isSystemUser = true; + }); + }) + (mkIf (cfg.nodes != {}) { + environment = { + etc = flip mapAttrs' cfg.nodes (node: settings: + nameValuePair "tahoe-lafs/${node}.cfg" { + mode = "0444"; + text = '' + # This configuration is generated by Nix. Edit at your own + # peril; here be dragons. + + ${ini.allConfigSectionsText settings.sections} + ''; + }); + # Actually require Tahoe, so that we will have it installed. + systemPackages = flip mapAttrsToList cfg.nodes (node: settings: + settings.package + ); + }; + # Open up the firewall. + # networking.firewall.allowedTCPPorts = flip mapAttrsToList cfg.nodes + # (node: settings: settings.tub.port); + systemd.services = flip mapAttrs' cfg.nodes (node: settings: + let + pidfile = "/run/tahoe.${node}.pid"; + # This is a directory, but it has no trailing slash. Tahoe commands + # get antsy when there's a trailing slash. + nodedir = "/var/db/tahoe-lafs/${node}"; + in nameValuePair "tahoe.${node}" { + description = "Tahoe LAFS node ${node}"; + wantedBy = [ "multi-user.target" ]; + path = [ settings.package ]; + restartTriggers = [ + config.environment.etc."tahoe-lafs/${node}.cfg".source ]; + serviceConfig = { + Type = "simple"; + PIDFile = pidfile; + # Believe it or not, Tahoe is very brittle about the order of + # arguments to $(tahoe run). The node directory must come first, + # and arguments which alter Twisted's behavior come afterwards. + ExecStart = '' + ${settings.package}/bin/tahoe run ${lib.escapeShellArg nodedir} -n -l- --pidfile=${lib.escapeShellArg pidfile} + ''; + }; + preStart = '' + if [ ! -d ${lib.escapeShellArg nodedir} ]; then + mkdir -p /var/db/tahoe-lafs + tahoe create-node --hostname=localhost ${lib.escapeShellArg nodedir} + fi + + # Tahoe has created a predefined tahoe.cfg which we must now + # scribble over. + # XXX I thought that a symlink would work here, but it doesn't, so + # we must do this on every prestart. Fixes welcome. + # rm ${nodedir}/tahoe.cfg + # ln -s /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${nodedir}/tahoe.cfg + cp /etc/tahoe-lafs/${lib.escapeShellArg node}.cfg ${lib.escapeShellArg nodedir}/tahoe.cfg + ''; + }); + users.users = flip mapAttrs' cfg.nodes (node: _: + nameValuePair "tahoe.${node}" { + description = "Tahoe node user for node ${node}"; + isSystemUser = true; + }); + }) + ]; + } diff --git a/nixos/modules/tests/private-storage.nix b/nixos/modules/tests/private-storage.nix new file mode 100644 index 0000000000000000000000000000000000000000..0838de10b1e794824a3cfea63e1c75ac81776286 --- /dev/null +++ b/nixos/modules/tests/private-storage.nix @@ -0,0 +1,31 @@ +# https://nixos.org/nixos/manual/index.html#sec-nixos-tests +import <nixpkgs/nixos/tests/make-test.nix> { + + # Configure a single machine as a PrivateStorage storage node. + machine = + { config, pkgs, ... }: + { imports = + [ ../private-storage.nix + ]; + services.private-storage.enable = true; + }; + + # Test the machine with a Perl program (sobbing). + testScript = + '' + # Boot the VM. + $machine->start; + + # The systemd unit should reach the running state. + $machine->waitForUnit("tahoe.storage.service"); + + # Some while after that the Tahoe-LAFS node should listen on the web API + # port. The port number here has to agree with the port number set in + # the private-storage.nix module. + $machine->waitForOpenPort(3456); + + # Once the web API is listening it should be possible to scrape some + # status from the node if it is really working. + $machine->succeed("tahoe -d /var/db/tahoe-lafs/storage status"); + ''; +} diff --git a/nixos/pkgs/autobahn.nix b/nixos/pkgs/autobahn.nix new file mode 100644 index 0000000000000000000000000000000000000000..3cc1df2138e783f7bb212b50ba09435773233b88 --- /dev/null +++ b/nixos/pkgs/autobahn.nix @@ -0,0 +1,35 @@ +{ lib, buildPythonPackage, fetchFromGitHub, isPy3k, + six, txaio, twisted, zope_interface, cffi, trollius, futures, cryptography, + mock, pytest +}: +buildPythonPackage rec { + pname = "autobahn"; + version = "19.7.1"; + + src = fetchFromGitHub { + owner = "crossbario"; + repo = "autobahn-python"; + rev = "v${version}"; + sha256 = "1gl2m18s77hlpiglh44plv3k6b965n66ylnxbzgvzcdl9jf3l3q3"; + }; + + propagatedBuildInputs = [ six txaio twisted zope_interface cffi cryptography ] ++ + (lib.optionals (!isPy3k) [ trollius futures ]); + + checkInputs = [ mock pytest ]; + checkPhase = '' + runHook preCheck + USE_TWISTED=true py.test $out + runHook postCheck + ''; + + # XXX Fails for some reason I don't understand. + doCheck = false; + + meta = with lib; { + description = "WebSocket and WAMP in Python for Twisted and asyncio."; + homepage = "https://crossbar.io/autobahn"; + license = licenses.mit; + maintainers = with maintainers; [ nand0p ]; + }; +} diff --git a/nixos/pkgs/cryptography.nix b/nixos/pkgs/cryptography.nix new file mode 100644 index 0000000000000000000000000000000000000000..bfa6d30208387b334af63b072b3e409b6d39a063 --- /dev/null +++ b/nixos/pkgs/cryptography.nix @@ -0,0 +1,75 @@ +{ stdenv +, buildPythonPackage +, fetchFromGitHub +, openssl +, cryptography_vectors +, darwin +, asn1crypto +, packaging +, six +, pythonOlder +, enum34 +, ipaddress +, isPyPy +, cffi +, pytest +, pretend +, iso8601 +, pytz +, hypothesis +}: + +buildPythonPackage rec { + pname = "cryptography"; + version = "2.7"; # Also update the hash in vectors.nix + + src = fetchFromGitHub { + owner = "pyca"; + repo = "cryptography"; + rev = "2.7"; + sha256 = "145byri5c3b8m6dbhwb6yxrv9jrr652l3z1w16mz205z8dz38qja"; + }; + + outputs = [ "out" "dev" ]; + + buildInputs = [ openssl ] + ++ stdenv.lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security; + propagatedBuildInputs = [ + asn1crypto + packaging + six + ] ++ stdenv.lib.optional (pythonOlder "3.4") enum34 + ++ stdenv.lib.optional (pythonOlder "3.3") ipaddress + ++ stdenv.lib.optional (!isPyPy) cffi; + + checkInputs = [ + cryptography_vectors + hypothesis + iso8601 + pretend + pytest + pytz + ]; + + checkPhase = '' + py.test --disable-pytest-warnings tests + ''; + + # IOKit's dependencies are inconsistent between OSX versions, so this is the best we + # can do until nix 1.11's release + __impureHostDeps = [ "/usr/lib" ]; + + meta = with stdenv.lib; { + description = "A package which provides cryptographic recipes and primitives"; + longDescription = '' + Cryptography includes both high level recipes and low level interfaces to + common cryptographic algorithms such as symmetric ciphers, message + digests, and key derivation functions. + Our goal is for it to be your "cryptographic standard library". It + supports Python 2.7, Python 3.4+, and PyPy 5.3+. + ''; + homepage = https://github.com/pyca/cryptography; + license = with licenses; [ asl20 bsd3 psfl ]; + maintainers = with maintainers; [ primeos ]; + }; +} diff --git a/nixos/pkgs/cryptography_vectors.nix b/nixos/pkgs/cryptography_vectors.nix new file mode 100644 index 0000000000000000000000000000000000000000..ea24ed908016b8ad2a534923709072b171b00b9a --- /dev/null +++ b/nixos/pkgs/cryptography_vectors.nix @@ -0,0 +1,23 @@ +{ buildPythonPackage, fetchPypi, lib, cryptography }: + +buildPythonPackage rec { + pname = "cryptography_vectors"; + # The test vectors must have the same version as the cryptography package: + version = cryptography.version; + + src = fetchPypi { + inherit pname version; + sha256 = "1g38zw90510azyfrj6mxbslx2gp9yrnv5dac0w2819k9ssdznbgi"; + }; + + # No tests included + doCheck = false; + + meta = with lib; { + description = "Test vectors for the cryptography package"; + homepage = https://cryptography.io/en/latest/development/test-vectors/; + # Source: https://github.com/pyca/cryptography/tree/master/vectors; + license = with licenses; [ asl20 bsd3 ]; + maintainers = with maintainers; [ primeos ]; + }; +} diff --git a/nixos/pkgs/eliot.nix b/nixos/pkgs/eliot.nix new file mode 100644 index 0000000000000000000000000000000000000000..f6d6b3061b1ea635bac0e694be407ca8d1b6befb --- /dev/null +++ b/nixos/pkgs/eliot.nix @@ -0,0 +1,27 @@ +{ lib, buildPythonPackage, fetchPypi, zope_interface, pyrsistent, boltons +, hypothesis, testtools, pytest }: +buildPythonPackage rec { + pname = "eliot"; + version = "1.7.0"; + + src = fetchPypi { + inherit pname version; + sha256 = "0ylyycf717s5qsrx8b9n6m38vyj2k8328lfhn8y6r31824991wv8"; + }; + + postPatch = '' + substituteInPlace setup.py \ + --replace "boltons >= 19.0.1" boltons + # depends on eliot.prettyprint._main which we don't have here. + rm eliot/tests/test_prettyprint.py + ''; + + checkInputs = [ testtools pytest hypothesis ]; + propagatedBuildInputs = [ zope_interface pyrsistent boltons ]; + + meta = with lib; { + homepage = https://github.com/itamarst/eliot/; + description = "Logging library that tells you why it happened"; + license = licenses.asl20; + }; +} diff --git a/nixos/pkgs/tahoe-lafs.nix b/nixos/pkgs/tahoe-lafs.nix new file mode 100644 index 0000000000000000000000000000000000000000..7d8b7b8efd35e27a1968e5fd69ce0f223d57f153 --- /dev/null +++ b/nixos/pkgs/tahoe-lafs.nix @@ -0,0 +1,11 @@ +{ fetchFromGitHub, eliot, tahoelafs, plugins ? [ ] }: +tahoelafs.overrideAttrs (old: +{ src = fetchFromGitHub + { owner = "tahoe-lafs"; + repo = "tahoe-lafs"; + rev = "6c1a37c95188c1d9a877286ef726280a68d38a4b"; + sha256 = "1fd8b6j52wn04bnvnvysws4c713max6k1592lz4nzyjlhrcwawwh"; + }; + propagatedBuildInputs = old.propagatedBuildInputs ++ [ eliot ] ++ plugins; + doInstallCheck = false; +}) diff --git a/nixos/pkgs/zkapauthorizer.nix b/nixos/pkgs/zkapauthorizer.nix new file mode 100644 index 0000000000000000000000000000000000000000..21c0a55b5de5810f0c3757a6afb4a140fbc70523 --- /dev/null +++ b/nixos/pkgs/zkapauthorizer.nix @@ -0,0 +1,12 @@ +{ pkgs, fetchFromGitHub, tahoe-lafs }: +let + src = fetchFromGitHub + { owner = "PrivateStorageio"; + repo = "ZKAPAuthorizer"; + rev = "a14b38f39e48d1560ea10ec26fffad6ce50fd00a"; + sha256 = "1v81l0ylx8r8xflhi16m8hb1dm3rlzyfrldiknvggqkyi5psdja4"; + }; +in +pkgs.python27Packages.callPackage "${src}/zkapauthorizer.nix" +{ inherit tahoe-lafs; +} diff --git a/nixos/system-tests.nix b/nixos/system-tests.nix new file mode 100644 index 0000000000000000000000000000000000000000..5cc4088c49a27ab7745e7e9f2a4dc1ad8b01ec93 --- /dev/null +++ b/nixos/system-tests.nix @@ -0,0 +1,5 @@ +# 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 diff --git a/nixos/unit-tests.nix b/nixos/unit-tests.nix new file mode 100644 index 0000000000000000000000000000000000000000..75016a17d128fabe11f4ecaad65dba3471ed863d --- /dev/null +++ b/nixos/unit-tests.nix @@ -0,0 +1,44 @@ +# The overall unit test suite for PrivateStorageio NixOS configuration. +let + pkgs = import <nixpkgs> { }; + + # Total the numbers in a list. + sum = builtins.foldl' (a: b: a + b) 0; + + # A helper for loading tests. + loadTest = moduleUnderTest: testModule: + (import testModule (pkgs.callPackage moduleUnderTest { })); + + # A list of tests to run. Manually updated for now, alas. Only tests in + # this list will be run! + testModules = + [ (loadTest ./lib/ini.nix ./lib/tests/test_ini.nix) + ]; + + # Count up the tests we're going to run. + numTests = sum (map (s: builtins.length (builtins.attrNames s)) testModules); + + # Convert it into a string for interpolation into the shell script. + numTestsStr = builtins.toString numTests; + + # Run the tests and collect the failures. + failures = map pkgs.lib.runTests testModules; + + # Count the number of failures in each module. + numFailures = sum (map builtins.length failures); + + # Convert the total into a string for easy interpolation into the shell script. + numFailuresStr = builtins.toString (numFailures); + + # Convert the failure information to a string for reporting. + failuresStr = builtins.toJSON failures; +in +pkgs.runCommand "test-results" {} '' +if [ ${numFailuresStr} -gt 0 ]; then + echo "Failed ${numFailuresStr} tests" + echo '${failuresStr}' + exit 1 +else + echo '${numTestsStr} tests OK' > $out +fi +''