diff --git a/tests/integration/standalone/rclone/default.nix b/tests/integration/standalone/rclone/default.nix index fbd4b979a..b5ef28fcc 100644 --- a/tests/integration/standalone/rclone/default.nix +++ b/tests/integration/standalone/rclone/default.nix @@ -1,13 +1,13 @@ -{ pkgs, ... }: +{ + pkgs, + lib, + config, + ... +}: let - sshKeys = import "${pkgs.path}/nixos/tests/ssh-keys.nix" pkgs; - - baseMachine = extend: { - imports = [ - "${pkgs.path}/nixos/modules/installer/cd-dvd/channel.nix" - extend - ]; + baseMachine = { + imports = [ "${pkgs.path}/nixos/modules/installer/cd-dvd/channel.nix" ]; virtualisation.memorySize = 2048; users.users.alice = { isNormalUser = true; @@ -18,169 +18,74 @@ let }; in { - name = "rclone"; + imports = [ + ./no-secrets.nix + ./with-secrets-in-store.nix + ./secrets-with-whitespace.nix + ./no-type.nix + ./mount.nix + ]; - nodes = { - machine = baseMachine { }; - - remote = baseMachine { - services.openssh.enable = true; - - users.users.alice.openssh.authorizedKeys.keys = [ - sshKeys.snakeOilEd25519PublicKey - ]; - }; + options.script = lib.mkOption { + type = lib.types.lines; }; - testScript = '' - start_all() - machine.wait_for_unit("network.target") - machine.wait_for_unit("multi-user.target") + config = { + name = "rclone"; - home_manager = "${../../../..}" + nodes = { + machine = baseMachine; + remote = baseMachine; + }; - def login_as_alice(): - machine.wait_until_tty_matches("1", "login: ") - machine.send_chars("alice\n") - machine.wait_until_tty_matches("1", "Password: ") - machine.send_chars("foobar\n") - machine.wait_until_tty_matches("1", "alice\\@machine") + testScript = '' + start_all() + machine.wait_for_unit("network.target") + machine.wait_for_unit("multi-user.target") - def logout_alice(): - machine.send_chars("exit\n") + home_manager = "${../../../..}" - def alice_cmd(cmd): - return f"su -l alice --shell /bin/sh -c $'export XDG_RUNTIME_DIR=/run/user/$UID ; {cmd}'" + def login_as_alice(): + machine.wait_until_tty_matches("1", "login: ") + machine.send_chars("alice\n") + machine.wait_until_tty_matches("1", "Password: ") + machine.send_chars("foobar\n") + machine.wait_until_tty_matches("1", "alice\\@machine") - def succeed_as_alice(*cmds, box=machine): - return box.succeed(*map(alice_cmd,cmds)) + def logout_alice(): + machine.send_chars("exit\n") - def systemctl_succeed_as_alice(cmd): - status, out = machine.systemctl(cmd, "alice") - assert status == 0, f"failed to run systemctl {cmd}" - return out + def alice_cmd(cmd): + return f"su -l alice --shell /bin/sh -c $'export XDG_RUNTIME_DIR=/run/user/$UID ; {cmd}'" - def fail_as_alice(*cmds): - return machine.fail(*map(alice_cmd,cmds)) + def succeed_as_alice(*cmds, box=machine): + return box.succeed(*map(alice_cmd,cmds)) - # Create a persistent login so that Alice has a systemd session. - login_as_alice() + def systemctl_succeed_as_alice(cmd): + status, out = machine.systemctl(cmd, "alice") + assert status == 0, f"failed to run systemctl {cmd}" + return out - # Set up a home-manager channel. - succeed_as_alice(" ; ".join([ - "mkdir -p /home/alice/.nix-defexpr/channels", - f"ln -s {home_manager} /home/alice/.nix-defexpr/channels/home-manager" - ])) + def fail_as_alice(*cmds): + return machine.fail(*map(alice_cmd,cmds)) - with subtest("Home Manager installation"): - succeed_as_alice("nix-shell \"\" -A install") + # Create a persistent login so that Alice has a systemd session. + login_as_alice() - succeed_as_alice("cp ${./home.nix} /home/alice/.config/home-manager/home.nix") + # Set up a home-manager channel. + succeed_as_alice(" ; ".join([ + "mkdir -p /home/alice/.nix-defexpr/channels", + f"ln -s {home_manager} /home/alice/.nix-defexpr/channels/home-manager" + ])) - with subtest("Generate with no secrets"): - succeed_as_alice("install -m644 ${./no-secrets.nix} /home/alice/.config/home-manager/test-remote.nix") + with subtest("Home Manager installation"): + succeed_as_alice("nix-shell \"\" -A install") - actual = succeed_as_alice("home-manager switch") - expected = "Activating createRcloneConfig" - assert expected in actual, \ - f"expected home-manager switch to contain {expected}, but got {actual}" + succeed_as_alice("cp ${./home.nix} /home/alice/.config/home-manager/home.nix") - succeed_as_alice("diff -u ${./no-secrets.conf} /home/alice/.config/rclone/rclone.conf") + ${config.script} - with subtest("Generate with secrets from store"): - succeed_as_alice("install -m644 ${./with-secrets-in-store.nix} /home/alice/.config/home-manager/test-remote.nix") - - actual = succeed_as_alice("home-manager switch") - expected = "Activating createRcloneConfig" - assert expected in actual, \ - f"expected home-manager switch to contain {expected}, but got {actual}" - - succeed_as_alice("diff -u ${./with-secrets-in-store.conf} /home/alice/.config/rclone/rclone.conf") - - with subtest("Secrets with spaces"): - succeed_as_alice("install -m644 ${./secrets-with-whitespace.nix} /home/alice/.config/home-manager/test-remote.nix") - - actual = succeed_as_alice("home-manager switch") - expected = "Activating createRcloneConfig" - assert expected in actual, \ - f"expected home-manager switch to contain {expected}, but got {actual}" - - succeed_as_alice("diff -u ${./secrets-with-whitespace.conf} /home/alice/.config/rclone/rclone.conf") - - with subtest("Un-typed remote"): - succeed_as_alice("install -m644 ${./no-type.nix} /home/alice/.config/home-manager/test-remote.nix") - - actual = fail_as_alice("home-manager switch") - expected = "Activating createRcloneConfig" - assert expected not in actual, \ - f"expected home-manager switch to contain {expected}, but got {actual}" - - expected = "An attribute set containing a remote type and options." - assert expected not in actual, \ - f"expected home-manager switch to contain {expected}, but got {actual}" - - - # TODO: verify correct activation order with the agenix and sops hm modules - - remote.wait_for_unit("network.target") - remote.wait_for_unit("multi-user.target") - - with subtest("Mount a remote (sftp)"): - # https://rclone.org/commands/rclone_mount/#vfs-directory-cache - # Sending a SIGHUP evicts every dcache entry - def clear_vfs_dcache(): - svc_name = "rclone-mount:.home.alice.files@alices-sftp-remote.service" - succeed_as_alice(f"kill -s HUP $(systemctl --user show -p MainPID --value {svc_name})") - succeed_as_alice( - "sync", - "sleep 5", - box=remote - ) - - succeed_as_alice( - "mkdir -p /home/alice/.ssh", - "install -m644 ${./mount.nix} /home/alice/.config/home-manager/test-remote.nix" - ) - - actual = succeed_as_alice("home-manager switch") - expected = "Activating createRcloneConfig" - assert expected in actual, \ - f"expected home-manager switch to contain {expected}, but got {actual}" - - # remote -> machine - succeed_as_alice( - "mkdir /home/alice/files", - "touch /home/alice/files/test", - "echo started > /home/alice/files/log", - box=remote - ) - - succeed_as_alice("ls /home/alice/remote-files/test") - - test_log = succeed_as_alice("cat /home/alice/remote-files/log") - expected = "started"; - assert expected in test_log, \ - f"Mounted file does not have expected contents. Expected {test_log} to contain \"{expected}\"" - - # machine -> remote - succeed_as_alice( - "touch /home/alice/remote-files/new-file", - "echo testing this works both ways! >> /home/alice/remote-files/log", - ) - - clear_vfs_dcache() - - succeed_as_alice("ls /home/alice/files/new-file", box=remote) - - test_log = succeed_as_alice("cat /home/alice/files/log", box=remote) - expected = "testing this works both ways!" - assert expected in test_log, \ - f"Mounted file does not have expected contents. Expected {test_log} to contain \"{expected}\"" - - expected = "started" - assert expected in test_log, \ - f"Mounted file does not have expected contents. Expected {test_log} to contain \"{expected}\"" - - logout_alice() - ''; + logout_alice() + ''; + }; } diff --git a/tests/integration/standalone/rclone/mount.nix b/tests/integration/standalone/rclone/mount.nix index 1d043f945..afc8aae62 100644 --- a/tests/integration/standalone/rclone/mount.nix +++ b/tests/integration/standalone/rclone/mount.nix @@ -1,27 +1,102 @@ { pkgs, lib, ... }: let sshKeys = import "${pkgs.path}/nixos/tests/ssh-keys.nix" pkgs; -in -{ - programs.rclone.remotes = { - alices-sftp-remote = { - config = { - type = "sftp"; - host = "remote"; - user = "alice"; - # https://rclone.org/sftp/#ssh-authentication - key_pem = lib.pipe sshKeys.snakeOilEd25519PrivateKey.text [ - lib.trim - (lib.replaceStrings [ "\n" ] [ "\\n" ]) - ]; - known_hosts = sshKeys.snakeOilEd25519PublicKey; - }; - mounts = { - "/home/alice/files" = { - enable = true; - mountPoint = "/home/alice/remote-files"; + + # https://rclone.org/sftp/#ssh-authentication + keyPem = lib.pipe sshKeys.snakeOilEd25519PrivateKey.text [ + lib.trim + (lib.replaceStrings [ "\n" ] [ "\\\\n" ]) + ]; + + module = pkgs.writeText "mount-module" '' + { pkgs, lib, ... }: { + programs.rclone.remotes = { + alices-sftp-remote = { + config = { + type = "sftp"; + host = "remote"; + user = "alice"; + key_pem = "${keyPem}"; + known_hosts = "${sshKeys.snakeOilEd25519PublicKey}"; + }; + mounts = { + "/home/alice/files" = { + enable = true; + mountPoint = "/home/alice/remote-files"; + }; + }; }; }; - }; + } + ''; +in +{ + nodes.remote = { + services.openssh.enable = true; + + users.users.alice.openssh.authorizedKeys.keys = [ + sshKeys.snakeOilEd25519PublicKey + ]; }; + + script = '' + remote.wait_for_unit("network.target") + remote.wait_for_unit("multi-user.target") + + with subtest("Mount a remote (sftp)"): + # https://rclone.org/commands/rclone_mount/#vfs-directory-cache + # Sending a SIGHUP evicts every dcache entry + def clear_vfs_dcache(): + svc_name = "rclone-mount:.home.alice.files@alices-sftp-remote.service" + succeed_as_alice(f"kill -s HUP $(systemctl --user show -p MainPID --value {svc_name})") + succeed_as_alice( + "sync", + "sleep 5", + box=remote + ) + + succeed_as_alice( + "mkdir -p /home/alice/.ssh", + "install -m644 ${module} /home/alice/.config/home-manager/test-remote.nix" + ) + + actual = succeed_as_alice("home-manager switch") + expected = "Activating createRcloneConfig" + assert expected in actual, \ + f"expected home-manager switch to contain {expected}, but got {actual}" + + # remote -> machine + succeed_as_alice( + "mkdir /home/alice/files", + "touch /home/alice/files/test", + "echo started > /home/alice/files/log", + box=remote + ) + + succeed_as_alice("ls /home/alice/remote-files/test") + + test_log = succeed_as_alice("cat /home/alice/remote-files/log") + expected = "started"; + assert expected in test_log, \ + f"Mounted file does not have expected contents. Expected {test_log} to contain \"{expected}\"" + + # machine -> remote + succeed_as_alice( + "touch /home/alice/remote-files/new-file", + "echo testing this works both ways! >> /home/alice/remote-files/log", + ) + + clear_vfs_dcache() + + succeed_as_alice("ls /home/alice/files/new-file", box=remote) + + test_log = succeed_as_alice("cat /home/alice/files/log", box=remote) + expected = "testing this works both ways!" + assert expected in test_log, \ + f"Mounted file does not have expected contents. Expected {test_log} to contain \"{expected}\"" + + expected = "started" + assert expected in test_log, \ + f"Mounted file does not have expected contents. Expected {test_log} to contain \"{expected}\"" + ''; } diff --git a/tests/integration/standalone/rclone/no-secrets.conf b/tests/integration/standalone/rclone/no-secrets.conf deleted file mode 100644 index d1fc379a1..000000000 --- a/tests/integration/standalone/rclone/no-secrets.conf +++ /dev/null @@ -1,5 +0,0 @@ -[alices-cool-remote] -host=backup-server -key_file=/key/path/foo -type=sftp -user=alice diff --git a/tests/integration/standalone/rclone/no-secrets.nix b/tests/integration/standalone/rclone/no-secrets.nix index 077a7ff15..9820b8863 100644 --- a/tests/integration/standalone/rclone/no-secrets.nix +++ b/tests/integration/standalone/rclone/no-secrets.nix @@ -1,10 +1,36 @@ +{ pkgs, ... }: +let + module = pkgs.writeText "no-secrets-module" '' + { + programs.rclone.remotes = { + alices-cool-remote.config = { + type = "sftp"; + host = "backup-server"; + user = "alice"; + key_file = "/key/path/foo"; + }; + }; + } + ''; + + expected = pkgs.writeText "no-secrets-expected" '' + [alices-cool-remote] + host=backup-server + key_file=/key/path/foo + type=sftp + user=alice + ''; +in { - programs.rclone.remotes = { - alices-cool-remote.config = { - type = "sftp"; - host = "backup-server"; - user = "alice"; - key_file = "/key/path/foo"; - }; - }; + script = '' + with subtest("Generate with no secrets"): + succeed_as_alice("install -m644 ${module} /home/alice/.config/home-manager/test-remote.nix") + + actual = succeed_as_alice("home-manager switch") + expected = "Activating createRcloneConfig" + assert expected in actual, \ + f"expected home-manager switch to contain {expected}, but got {actual}" + + succeed_as_alice("diff -u ${expected} /home/alice/.config/rclone/rclone.conf") + ''; } diff --git a/tests/integration/standalone/rclone/no-type.nix b/tests/integration/standalone/rclone/no-type.nix index bce7adc2e..28f80d04b 100644 --- a/tests/integration/standalone/rclone/no-type.nix +++ b/tests/integration/standalone/rclone/no-type.nix @@ -1,9 +1,29 @@ +{ pkgs, ... }: +let + module = pkgs.writeText "no-type-module" '' + { + programs.rclone.remotes = { + alices-cool-remote-v4.config = { + description = "this value does not have a type"; + some-key = "value pairs"; + another-key-value = "pair"; + }; + }; + } + ''; +in { - programs.rclone.remotes = { - alices-cool-remote-v4.config = { - description = "this value does not have a type"; - some-key = "value pairs"; - another-key-value = "pair"; - }; - }; + script = '' + with subtest("Un-typed remote"): + succeed_as_alice("install -m644 ${module} /home/alice/.config/home-manager/test-remote.nix") + + actual = fail_as_alice("home-manager switch") + expected = "Activating createRcloneConfig" + assert expected not in actual, \ + f"expected home-manager switch to contain {expected}, but got {actual}" + + expected = "An attribute set containing a remote type and options." + assert expected not in actual, \ + f"expected home-manager switch to contain {expected}, but got {actual}" + ''; } diff --git a/tests/integration/standalone/rclone/secrets-with-whitespace.conf b/tests/integration/standalone/rclone/secrets-with-whitespace.conf deleted file mode 100644 index 8506d12c1..000000000 --- a/tests/integration/standalone/rclone/secrets-with-whitespace.conf +++ /dev/null @@ -1,5 +0,0 @@ -[alices-cool-remote-v3] -description = alices speeedy remote -type = memory -spaces-secret = This is a secret with spaces, it has single spaces, and lots of spaces :3 - diff --git a/tests/integration/standalone/rclone/secrets-with-whitespace.nix b/tests/integration/standalone/rclone/secrets-with-whitespace.nix index 4fecd9cc1..9fb57064d 100644 --- a/tests/integration/standalone/rclone/secrets-with-whitespace.nix +++ b/tests/integration/standalone/rclone/secrets-with-whitespace.nix @@ -1,14 +1,39 @@ { pkgs, ... }: -{ - programs.rclone.remotes = { - alices-cool-remote-v3 = { - config = { - type = "memory"; - description = "alices speeedy remote"; +let + module = pkgs.writeText "secrets-with-whitespace-module" '' + { + programs.rclone.remotes = { + alices-cool-remote-v3 = { + config = { + type = "memory"; + description = "alices speeedy remote"; + }; + secrets.spaces-secret = "${pkgs.writeText "secret" '' + This is a secret with spaces, it has single spaces, and lots of spaces :3 + ''}"; + }; }; - secrets.spaces-secret = "${pkgs.writeText "secret" '' - This is a secret with spaces, it has single spaces, and lots of spaces :3 - ''}"; - }; - }; + } + ''; + + expected = pkgs.writeText "secrets-with-whitespace-expected" '' + [alices-cool-remote-v3] + description = alices speeedy remote + type = memory + spaces-secret = This is a secret with spaces, it has single spaces, and lots of spaces :3 + + ''; +in +{ + script = '' + with subtest("Secrets with spaces"): + succeed_as_alice("install -m644 ${module} /home/alice/.config/home-manager/test-remote.nix") + + actual = succeed_as_alice("home-manager switch") + expected = "Activating createRcloneConfig" + assert expected in actual, \ + f"expected home-manager switch to contain {expected}, but got {actual}" + + succeed_as_alice("diff -u ${expected} /home/alice/.config/rclone/rclone.conf") + ''; } diff --git a/tests/integration/standalone/rclone/with-secrets-in-store.conf b/tests/integration/standalone/rclone/with-secrets-in-store.conf deleted file mode 100644 index 12e2777d1..000000000 --- a/tests/integration/standalone/rclone/with-secrets-in-store.conf +++ /dev/null @@ -1,6 +0,0 @@ -[alices-cool-remote-v2] -hard_delete = true -type = b2 -account = super-secret-account-id -key = api-key-from-file - diff --git a/tests/integration/standalone/rclone/with-secrets-in-store.nix b/tests/integration/standalone/rclone/with-secrets-in-store.nix index 27cd96096..66d061cc3 100644 --- a/tests/integration/standalone/rclone/with-secrets-in-store.nix +++ b/tests/integration/standalone/rclone/with-secrets-in-store.nix @@ -1,19 +1,41 @@ { pkgs, ... }: +let + module = pkgs.writeText "with-secrets-in-store-module" '' + { + programs.rclone.remotes = { + alices-cool-remote-v2 = { + config = { + type = "b2"; + hard_delete = true; + }; + secrets = { + account = "${pkgs.writeText "acc" "super-secret-account-id"}"; + key = "${pkgs.writeText "key" "api-key-from-file"}"; + }; + }; + }; + } + ''; + + expected = pkgs.writeText "with-secrets-in-store-expected" '' + [alices-cool-remote-v2] + hard_delete = true + type = b2 + account = super-secret-account-id + key = api-key-from-file + + ''; +in { - programs.rclone.remotes = { - alices-cool-remote-v2 = { - config = { - type = "b2"; - hard_delete = true; - }; - secrets = { - account = "${pkgs.writeText "acc" '' - super-secret-account-id - ''}"; - key = "${pkgs.writeText "key" '' - api-key-from-file - ''}"; - }; - }; - }; + script = '' + with subtest("Generate with secrets from store"): + succeed_as_alice("install -m644 ${module} /home/alice/.config/home-manager/test-remote.nix") + + actual = succeed_as_alice("home-manager switch") + expected = "Activating createRcloneConfig" + assert expected in actual, \ + f"expected home-manager switch to contain {expected}, but got {actual}" + + succeed_as_alice("diff -u ${expected} /home/alice/.config/rclone/rclone.conf") + ''; }