From f042b0170eab5381a13437d3c283f0782246be73 Mon Sep 17 00:00:00 2001 From: matthewcroughan Date: Thu, 31 Mar 2022 17:36:45 +0100 Subject: [PATCH 1/4] Add .gitignore --- .gitignore | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..49c10b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Prevents Nix results from `nix build`, etc, from being checked in +# accidentally. +*result* + +# Github Workflows are not what we use to perform CI, we use Hercules-CI +# instead. +.github/workflows + +# Dockerfiles, or docker-compose files are not how we build or deploy software. +# Only Nix expressions are allowed. +*Dockerfile* +*docker-compose* -- 2.45.3 From fcae9a11d062a6e67701ce3fed63fd27f04426dd Mon Sep 17 00:00:00 2001 From: matthewcroughan Date: Thu, 31 Mar 2022 17:40:00 +0100 Subject: [PATCH 2/4] Provide pkgs in forAllSystems This is stolen from DavHau's dream2nix flake which reworks forAllSystems to provide two arguments, the pkgs and the system. --- flake.nix | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index e54f503..779c589 100644 --- a/flake.nix +++ b/flake.nix @@ -7,8 +7,11 @@ let version = builtins.substring 0 8 self.lastModifiedDate; supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; - forAllSystems = nixpkgs.lib.genAttrs supportedSystems; - nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; overlays = [ self.overlay ]; }); + forSystems = systems: f: + nixpkgs.lib.genAttrs systems + (system: f system nixpkgs.legacyPackages.${system}); + forAllSystems = forSystems supportedSystems; + nixpkgsFor = forAllSystems (system: pkgs: import nixpkgs { inherit system; overlays = [ self.overlay ]; }); in rec { overlay = final: prev: { @@ -69,6 +72,6 @@ ); }; }; - nixinate = forAllSystems (system: nixpkgsFor.${system}.generateApps); + nixinate = forAllSystems (system: pkgs: nixpkgsFor.${system}.generateApps); }; } -- 2.45.3 From 971bfe5d1f8d2e91ff93f8c602a22decd05a136a Mon Sep 17 00:00:00 2001 From: matthewcroughan Date: Thu, 31 Mar 2022 20:36:59 +0100 Subject: [PATCH 3/4] Add vmTest for Nixinate This initializes ./test/default.nix which is referred to by the flake.nix under the `checks` attribute. This default.nix should point to all future tests, where they can be looked up and ran like: nix build .#checks.x86_64-linux.vmTest The test included is a simple NixOS VM Test. It uses Nixinate to deploy a machine with `services.nginx.enable = true` set, and tests whether nginx.service is started and reachable after deployment. --- examples/flake.lock | 124 --------------------------- flake.nix | 15 +++- tests/default.nix | 4 + tests/vmTest/default.nix | 104 ++++++++++++++++++++++ tests/vmTest/nixinateeAdditional.nix | 8 ++ tests/vmTest/nixinateeBase.nix | 32 +++++++ 6 files changed, 162 insertions(+), 125 deletions(-) delete mode 100644 examples/flake.lock create mode 100644 tests/default.nix create mode 100644 tests/vmTest/default.nix create mode 100644 tests/vmTest/nixinateeAdditional.nix create mode 100644 tests/vmTest/nixinateeBase.nix diff --git a/examples/flake.lock b/examples/flake.lock deleted file mode 100644 index f89872e..0000000 --- a/examples/flake.lock +++ /dev/null @@ -1,124 +0,0 @@ -{ - "nodes": { - "examples": { - "inputs": { - "nixinate": "nixinate_2", - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "narHash": "sha256-1iruH96Aame+4NAAwSwVZAbHzfnKxhMLjgoVvJar6ls=", - "path": "./examples", - "type": "path" - }, - "original": { - "path": "./examples", - "type": "path" - } - }, - "nixinate": { - "inputs": { - "examples": "examples", - "nixpkgs": "nixpkgs_3" - }, - "locked": { - "lastModified": 1646587087, - "narHash": "sha256-SwOHL/tte1H8VvftnxtWCr5FIlZaGvNy57P9sMSrZ5Q=", - "owner": "matthewcroughan", - "repo": "nixinate", - "rev": "886c6a2b3bef14cacf6c3021df0a75bb57f9fbc7", - "type": "github" - }, - "original": { - "owner": "matthewcroughan", - "repo": "nixinate", - "type": "github" - } - }, - "nixinate_2": { - "inputs": { - "nixpkgs": "nixpkgs" - }, - "locked": { - "narHash": "sha256-lk8eIWYxtHqDT4ZmSFuXMlG067RdPqLCQocnN+hNE7U=", - "path": "/etc/nixos/nixinate", - "type": "path" - }, - "original": { - "path": "/etc/nixos/nixinate", - "type": "path" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1640887906, - "narHash": "sha256-Eupk1UlNicCD2UNZuEKt6yhE6kFWAxXM/HyziOjG9CA=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "8a053bc2255659c5ca52706b9e12e76a8f50dbdd", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-21.11", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1641147223, - "narHash": "sha256-eJnmISYGR7LeqEev4bsI/qcU0SgeFKHs3jnL4vMGL+k=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "08370e1e271f6fe00d302bebbe510fe0e2c611ca", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-21.11", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { - "locked": { - "lastModified": 1647893727, - "narHash": "sha256-pOi7VdCb+s5Cwh5CS7YEZVRgH9uCmE87J5W7iXv29Ck=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "1ec61dd4167f04be8d05c45780818826132eea0d", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_4": { - "locked": { - "lastModified": 1641147223, - "narHash": "sha256-eJnmISYGR7LeqEev4bsI/qcU0SgeFKHs3jnL4vMGL+k=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "08370e1e271f6fe00d302bebbe510fe0e2c611ca", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-21.11", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixinate": "nixinate", - "nixpkgs": "nixpkgs_4" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix index 779c589..c589bbc 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; }; - outputs = { self, nixpkgs, ... }: + outputs = { self, nixpkgs, ... }@inputs: let version = builtins.substring 0 8 self.lastModifiedDate; supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; @@ -73,5 +73,18 @@ }; }; nixinate = forAllSystems (system: pkgs: nixpkgsFor.${system}.generateApps); + checks = forAllSystems (system: pkgs: + let + vmTests = import ./tests { + makeTest = (import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system; }).makeTest; + inherit pkgs inputs; + }; + in + pkgs.lib.optionalAttrs pkgs.stdenv.isLinux vmTests # vmTests can only be ran on Linux, so append them only if on Linux. + // + { + # Other checks here... + } + ); }; } diff --git a/tests/default.nix b/tests/default.nix new file mode 100644 index 0000000..93de21b --- /dev/null +++ b/tests/default.nix @@ -0,0 +1,4 @@ +{ pkgs, makeTest, inputs }: +{ + vmTest = import ./vmTest { inherit pkgs makeTest inputs; }; +} diff --git a/tests/vmTest/default.nix b/tests/vmTest/default.nix new file mode 100644 index 0000000..5c86485 --- /dev/null +++ b/tests/vmTest/default.nix @@ -0,0 +1,104 @@ +{ pkgs, makeTest, inputs }: +let + # Return a store path with a closure containing everything including + # derivations and all build dependency outputs, all the way down. + allDrvOutputs = pkg: + let name = "allDrvOutputs-${pkg.pname or pkg.name or "unknown"}"; + in + pkgs.runCommand name { refs = pkgs.writeReferencesToFile pkg.drvPath; } '' + touch $out + while read ref; do + case $ref in + *.drv) + cat $ref >>$out + ;; + esac + done <$refs + ''; + # Imports a flake with inputs passed in by hand, rather than + # builtins.getFlake, which cannot be used in this way. + callLocklessFlake = path: inputs: let + r = {outPath = path;} // + ((import (path + "/flake.nix")).outputs (inputs // {self = r;})); + in + r; + exampleFlake = pkgs.writeTextFile { + name = "nixinate-example-flake"; + destination = "/flake.nix"; + text = '' + { + outputs = { self, nixpkgs }: + let + makeTest = (import (nixpkgs + "/nixos/lib/testing-python.nix") { system = "${pkgs.hostPlatform.system}"; }).makeTest; + baseConfig = ((makeTest { nodes.baseConfig = { ... }: {}; testScript = "";}).nodes {}).baseConfig.extendModules { + modules = [ + ${builtins.readFile ./nixinateeBase.nix} + ${builtins.readFile ./nixinateeAdditional.nix} + { + _module.args.nixinate = { + host = "nixinatee"; + sshUser = "nixinator"; + buildOn = "local"; # valid args are "local" or "remote" + }; + } + ]; + }; + in + { + nixosConfigurations = { + nixinatee = baseConfig; + }; + }; + } + ''; + }; + deployScript = inputs.self.nixinate.${pkgs.hostPlatform.system} (callLocklessFlake "${exampleFlake}" { nixpkgs = inputs.nixpkgs; }); + exampleSystem = (callLocklessFlake "${exampleFlake}" { nixpkgs = inputs.nixpkgs; }).nixosConfigurations.nixinatee.config.system.build.toplevel; +in +makeTest { + nodes = { + nixinatee = { ... }: { + imports = [ + ./nixinateeBase.nix + ]; + virtualisation = { + writableStore = true; + }; + }; + nixinator = { ... }: { + virtualisation = { + additionalPaths = [ + (allDrvOutputs exampleSystem) + ]; + }; + nix = { + extraOptions = + let empty_registry = builtins.toFile "empty-flake-registry.json" ''{"flakes":[],"version":2}''; in + '' + experimental-features = nix-command flakes + flake-registry = ${empty_registry} + ''; + registry.nixpkgs.flake = inputs.nixpkgs; + }; + }; + }; + testScript = + '' + start_all() + nixinatee.wait_for_unit("sshd.service") + nixinator.wait_for_unit("multi-user.target") + nixinator.succeed("mkdir ~/.ssh/") + nixinator.succeed("ssh-keyscan -H nixinatee >> ~/.ssh/known_hosts") + nixinator.succeed("exec ${deployScript.nixinate.nixinatee.program} >&2") + nixinatee.wait_for_unit("nginx.service") + nixinatee.wait_for_open_port("80") + with subtest("Check that Nginx webserver can be reached by deployer after deployment"): + assert "Welcome to nginx!" in nixinator.succeed( + "curl -sSf http:/nixinatee/ | grep title" + ) + with subtest("Check that Nginx webserver can be reached by deployee after deployment"): + assert "Welcome to nginx!" in nixinatee.succeed( + "curl -sSf http:/127.0.0.1/ | grep title" + ) + ''; +} diff --git a/tests/vmTest/nixinateeAdditional.nix b/tests/vmTest/nixinateeAdditional.nix new file mode 100644 index 0000000..d2fb098 --- /dev/null +++ b/tests/vmTest/nixinateeAdditional.nix @@ -0,0 +1,8 @@ +# Configuration that will be added to the nixinatee node. Nixinate will deploy +# the combination of nixinateBase.nix + nixinateAdditional.nix +{ + config = { + services.nginx.enable = true; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; +} diff --git a/tests/vmTest/nixinateeBase.nix b/tests/vmTest/nixinateeBase.nix new file mode 100644 index 0000000..3601657 --- /dev/null +++ b/tests/vmTest/nixinateeBase.nix @@ -0,0 +1,32 @@ +# Common configuration of nixinatee node in the vmTest. This is the base +# configuration which is required to perform the test. +{ + config = { + nix.trustedUsers = [ "nixinator" ]; + security.sudo.extraRules = [{ + users = [ "nixinator" ]; + commands = [{ + command = "ALL"; + options = [ "NOPASSWD" ]; + }]; + }]; + users = { + mutableUsers = false; + users = { + nixinator = { + extraGroups = [ + "wheel" + ]; + password = ""; + isNormalUser = true; + }; + }; + }; + services.openssh = { + enable = true; + extraConfig = "PermitEmptyPasswords yes"; + }; + documentation.enable = false; + boot.loader.grub.enable = false; + }; +} -- 2.45.3 From 719e06be2b97aded9023f40c505b8a291b034847 Mon Sep 17 00:00:00 2001 From: matthewcroughan Date: Thu, 21 Apr 2022 02:08:02 +0100 Subject: [PATCH 4/4] make vmTestLocal and vmTestRemote This creates and uses a function named mkNixinateTest which takes a buildOn argument. This expects to be either "local" or "remote". This means we can make a test for both use cases of Nixinate. One where we build on the remote machine, and one where we build locally and push to the remote machine. These tests are then added to the top level of the tests folder and are imported by the flake.nix --- tests/default.nix | 3 +- tests/vmTest/default.nix | 89 +++++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/tests/default.nix b/tests/default.nix index 93de21b..0826e0e 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -1,4 +1,5 @@ { pkgs, makeTest, inputs }: { - vmTest = import ./vmTest { inherit pkgs makeTest inputs; }; + vmTestLocal = (import ./vmTest { inherit pkgs makeTest inputs; }).local; + vmTestRemote = (import ./vmTest { inherit pkgs makeTest inputs; }).remote; } diff --git a/tests/vmTest/default.nix b/tests/vmTest/default.nix index 5c86485..cbbcedb 100644 --- a/tests/vmTest/default.nix +++ b/tests/vmTest/default.nix @@ -54,51 +54,56 @@ let }; deployScript = inputs.self.nixinate.${pkgs.hostPlatform.system} (callLocklessFlake "${exampleFlake}" { nixpkgs = inputs.nixpkgs; }); exampleSystem = (callLocklessFlake "${exampleFlake}" { nixpkgs = inputs.nixpkgs; }).nixosConfigurations.nixinatee.config.system.build.toplevel; -in -makeTest { - nodes = { - nixinatee = { ... }: { - imports = [ - ./nixinateeBase.nix - ]; - virtualisation = { - writableStore = true; - }; - }; - nixinator = { ... }: { - virtualisation = { - additionalPaths = [ - (allDrvOutputs exampleSystem) + mkNixinateTest = buildOn: makeTest { + nodes = { + nixinatee = { ... }: { + imports = [ + ./nixinateeBase.nix ]; + virtualisation = { + writableStore = true; + }; }; - nix = { - extraOptions = - let empty_registry = builtins.toFile "empty-flake-registry.json" ''{"flakes":[],"version":2}''; in - '' - experimental-features = nix-command flakes - flake-registry = ${empty_registry} - ''; - registry.nixpkgs.flake = inputs.nixpkgs; + nixinator = { ... }: { + virtualisation = { + additionalPaths = [ + (allDrvOutputs exampleSystem) + ] + ++ pkgs.lib.optional (buildOn == "remote") exampleFlake; + }; + nix = { + extraOptions = + let empty_registry = builtins.toFile "empty-flake-registry.json" ''{"flakes":[],"version":2}''; in + '' + experimental-features = nix-command flakes + flake-registry = ${empty_registry} + ''; + registry.nixpkgs.flake = inputs.nixpkgs; + }; }; }; + testScript = + '' + start_all() + nixinatee.wait_for_unit("sshd.service") + nixinator.wait_for_unit("multi-user.target") + nixinator.succeed("mkdir ~/.ssh/") + nixinator.succeed("ssh-keyscan -H nixinatee >> ~/.ssh/known_hosts") + nixinator.succeed("exec ${deployScript.nixinate.nixinatee.program} >&2") + nixinatee.wait_for_unit("nginx.service") + nixinatee.wait_for_open_port("80") + with subtest("Check that Nginx webserver can be reached by deployer after deployment"): + assert "Welcome to nginx!" in nixinator.succeed( + "curl -sSf http:/nixinatee/ | grep title" + ) + with subtest("Check that Nginx webserver can be reached by deployee after deployment"): + assert "Welcome to nginx!" in nixinatee.succeed( + "curl -sSf http:/127.0.0.1/ | grep title" + ) + ''; }; - testScript = - '' - start_all() - nixinatee.wait_for_unit("sshd.service") - nixinator.wait_for_unit("multi-user.target") - nixinator.succeed("mkdir ~/.ssh/") - nixinator.succeed("ssh-keyscan -H nixinatee >> ~/.ssh/known_hosts") - nixinator.succeed("exec ${deployScript.nixinate.nixinatee.program} >&2") - nixinatee.wait_for_unit("nginx.service") - nixinatee.wait_for_open_port("80") - with subtest("Check that Nginx webserver can be reached by deployer after deployment"): - assert "Welcome to nginx!" in nixinator.succeed( - "curl -sSf http:/nixinatee/ | grep title" - ) - with subtest("Check that Nginx webserver can be reached by deployee after deployment"): - assert "Welcome to nginx!" in nixinatee.succeed( - "curl -sSf http:/127.0.0.1/ | grep title" - ) - ''; +in +{ + local = (mkNixinateTest "local"); + remote = (mkNixinateTest "remote"); } -- 2.45.3