diff --git a/test/common.nix b/test/common.nix new file mode 100644 index 0000000..a6209fa --- /dev/null +++ b/test/common.nix @@ -0,0 +1,55 @@ +{ + lib, +}: +{ + accessScript = { + fqdn + , hasSSL + , waitForServices ? s: [] + , waitForPorts ? p: [] + , waitForUnixSocket ? u: [] + , extraScript ? {...}: "" + }: { nodes, ... }: + let + proto_fqdn = if hasSSL args then "https://${fqdn}" else "http://${fqdn}"; + + args = { + node.name = "server"; + node.config = nodes.server; + inherit proto_fqdn; + }; + in + '' + import json + import os + import pathlib + + start_all() + '' + + lib.strings.concatMapStrings (s: ''server.wait_for_unit("${s}")'' + "\n") (waitForServices args) + + lib.strings.concatMapStrings (p: ''server.wait_for_open_port(${toString p})'' + "\n") (waitForPorts args) + + lib.strings.concatMapStrings (u: ''server.wait_for_open_unix_socket("${u}")'' + "\n") (waitForUnixSocket args) + + '' + if ${if hasSSL args then "True" else "False"}: + server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt") + client.succeed("rm -r /etc/ssl/certs") + client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt") + + def curl(target, format, endpoint, succeed=True): + return json.loads(target.succeed( + "curl --fail-with-body --silent --show-error --output /dev/null --location" + + " --cookie-jar /tmp/cookies" + + " --connect-to ${fqdn}:443:server:443" + + " --connect-to ${fqdn}:80:server:80" + + f" --write-out '{format}'" + + " " + endpoint + )) + + with subtest("access"): + response = curl(client, """{"code":%{response_code}}""", "${proto_fqdn}") + + if response['code'] != 200: + raise Exception(f"Code is {response['code']}") + '' + + extraScript args; +} diff --git a/test/vm/arr.nix b/test/vm/arr.nix index e4e8be4..b0f843b 100644 --- a/test/vm/arr.nix +++ b/test/vm/arr.nix @@ -1,62 +1,51 @@ { pkgs, lib, ... }: let pkgs' = pkgs; + + domain = "example.com"; + healthUrl = "/health"; + loginUrl = "/UI/Login"; + + testLib = pkgs.callPackage ../common.nix {}; + # TODO: Test login - commonTestScript = appname: cfgPathFn: { nodes, ... }: + commonTestScript = appname: cfgPathFn: let - shbapp = nodes.server.shb.arr.${appname}; - cfgPath = cfgPathFn shbapp; - apiKey = if (shbapp.settings ? ApiKey) then "01234567890123456789" else null; - hasSSL = !(isNull shbapp.ssl); - fqdn = if hasSSL then "https://${appname}.example.com" else "http://${appname}.example.com"; - healthUrl = "/health"; - loginUrl = "/UI/Login"; - in - '' - import json - import os - import pathlib + fqdn = "${appname}.${domain}"; + in testLib.accessScript { + inherit fqdn; + hasSSL = { node, ... }: !(isNull node.config.shb.arr.${appname}.ssl); + waitForServices = { ... }: [ + "${appname}.service" + "nginx.service" + ]; + waitForPorts = { node, ... }: [ + node.config.shb.arr.${appname}.settings.Port + ]; + extraScript = { node, proto_fqdn, ... }: let + shbapp = node.config.shb.arr.${appname}; + cfgPath = cfgPathFn shbapp; + apiKey = if (shbapp.settings ? ApiKey) then "01234567890123456789" else null; + in '' + with subtest("health"): + response = curl(client, """{"code":%{response_code}}""", "${fqdn}${healthUrl}") - start_all() - server.wait_for_unit("${appname}.service") - server.wait_for_unit("nginx.service") - server.wait_for_open_port(${builtins.toString shbapp.settings.Port}) + if response['code'] != 200: + raise Exception(f"Code is {response['code']}") - if ${if hasSSL then "True" else "False"}: - server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt") - client.succeed("rm -r /etc/ssl/certs") - client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt") + with subtest("login"): + response = curl(client, """{"code":%{response_code}}""", "${fqdn}${loginUrl}") - def curl(target, format, endpoint, succeed=True): - return json.loads(target.succeed( - "curl -X GET --fail-with-body --silent --show-error --output /dev/null --location" - + " --connect-to ${appname}.example.com:443:server:443" - + " --connect-to ${appname}.example.com:80:server:80" - + " --cookie-jar /tmp/cookies" - # Uncomment for debugging: - # + " -v" - + f" --write-out '{format}'" - + " " + endpoint - )) + if response['code'] != 200: + raise Exception(f"Code is {response['code']}") + '' + lib.optionalString (apiKey != null) '' - with subtest("health"): - response = curl(client, """{"code":%{response_code}}""", "${fqdn}${healthUrl}") - - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") - - with subtest("login"): - response = curl(client, """{"code":%{response_code}}""", "${fqdn}${loginUrl}") - - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") - '' + lib.optionalString (apiKey != null) '' - - with subtest("apikey"): - config = server.succeed("cat ${cfgPath}") - if "${apiKey}" not in config: - raise Exception(f"Unexpected API Key. Want '${apiKey}', got '{config}'") - ''; + with subtest("apikey"): + config = server.succeed("cat ${cfgPath}") + if "${apiKey}" not in config: + raise Exception(f"Unexpected API Key. Want '${apiKey}', got '{config}'") + ''; + }; basic = appname: cfgPathFn: pkgs.testers.runNixOSTest { name = "arr-${appname}-basic"; @@ -78,7 +67,7 @@ let shb.arr.${appname} = { enable = true; - domain = "example.com"; + inherit domain; subdomain = appname; settings.ApiKey.source = pkgs.writeText "APIKey" "01234567890123456789"; # Needs to be >=20 characters. diff --git a/test/vm/audiobookshelf.nix b/test/vm/audiobookshelf.nix index 538ef13..9b15941 100644 --- a/test/vm/audiobookshelf.nix +++ b/test/vm/audiobookshelf.nix @@ -2,46 +2,26 @@ let pkgs' = pkgs; + testLib = pkgs.callPackage ../common.nix {}; + subdomain = "a"; domain = "example.com"; fqdn = "${subdomain}.${domain}"; - # TODO: Test login - commonTestScript = { nodes, ... }: - let - hasSSL = !(isNull nodes.server.shb.audiobookshelf.ssl); - proto_fqdn = if hasSSL then "https://${fqdn}" else "http://${fqdn}"; - in - '' - import json - import os - import pathlib - - start_all() - server.wait_for_unit("audiobookshelf.service") - server.wait_for_unit("nginx.service") - server.wait_for_open_port(${builtins.toString nodes.server.shb.audiobookshelf.webPort}) - - if ${if hasSSL then "True" else "False"}: - server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt") - client.succeed("rm -r /etc/ssl/certs") - client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt") - - def curl(target, format, endpoint, succeed=True): - return json.loads(target.succeed( - "curl --fail-with-body --silent --show-error --output /dev/null --location" - + " --connect-to ${fqdn}:443:server:443" - + " --connect-to ${fqdn}:80:server:80" - + f" --write-out '{format}'" - + " " + endpoint - )) - - with subtest("access"): - response = curl(client, """{"code":%{response_code}}""", "${proto_fqdn}") - - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") - ''; + commonTestScript = testLib.accessScript { + inherit fqdn; + hasSSL = { node, ... }: !(isNull node.config.shb.audiobookshelf.ssl); + waitForServices = { ... }: [ + "audiobookshelf.service" + "nginx.service" + ]; + waitForPorts = { node, ... }: [ + node.config.shb.audiobookshelf.webPort + ]; + # TODO: Test login + # extraScript = { ... }: '' + # ''; + }; base = { imports = [ diff --git a/test/vm/deluge.nix b/test/vm/deluge.nix index f419cca..f5ebc01 100644 --- a/test/vm/deluge.nix +++ b/test/vm/deluge.nix @@ -6,47 +6,26 @@ let domain = "example.com"; fqdn = "${subdomain}.${domain}"; - commonTestScript = { nodes, ... }: - let - hasSSL = !(isNull nodes.server.shb.deluge.ssl); - proto_fqdn = if hasSSL then "https://${fqdn}" else "http://${fqdn}"; - in - '' - import json - import os - import pathlib + testLib = pkgs.callPackage ../common.nix {}; - start_all() - server.wait_for_unit("nginx.service") - server.wait_for_unit("deluged.service") - server.wait_for_unit("delugeweb.service") - server.wait_for_open_port(${toString nodes.server.shb.deluge.daemonPort}) - server.wait_for_open_port(${toString nodes.server.shb.deluge.webPort}) - - if ${if hasSSL then "True" else "False"}: - server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt") - client.succeed("rm -r /etc/ssl/certs") - client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt") - - def curl(target, format, endpoint, succeed=True): - return json.loads(target.succeed( - "curl --fail-with-body --silent --show-error --output /dev/null --location" - + " --connect-to ${fqdn}:443:server:443" - + " --connect-to ${fqdn}:80:server:80" - + f" --write-out '{format}'" - + " " + endpoint - )) - - print(server.succeed('journalctl -n100 -u deluged')) - print(server.succeed('systemctl status deluged')) - print(server.succeed('systemctl status delugeweb')) - - with subtest("access"): - response = curl(client, """{"code":%{response_code}}""", "${proto_fqdn}") - - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") + commonTestScript = testLib.accessScript { + inherit fqdn; + hasSSL = { node, ... }: !(isNull node.config.shb.deluge.ssl); + waitForServices = { ... }: [ + "nginx.service" + "deluged.service" + "delugeweb.service" + ]; + waitForPorts = { node, ... }: [ + node.config.shb.deluge.daemonPort + node.config.shb.deluge.webPort + ]; + extraScript = { node, ... }: '' + print(${node.name}.succeed('journalctl -n100 -u deluged')) + print(${node.name}.succeed('systemctl status deluged')) + print(${node.name}.succeed('systemctl status delugeweb')) ''; + }; # TODO: Test login directly to deluge daemon to exercise extraUsers authTestScript = { nodes, ... }: diff --git a/test/vm/grocy.nix b/test/vm/grocy.nix index be67378..d01c6f5 100644 --- a/test/vm/grocy.nix +++ b/test/vm/grocy.nix @@ -2,46 +2,26 @@ let pkgs' = pkgs; + testLib = pkgs.callPackage ../common.nix {}; + subdomain = "g"; domain = "example.com"; fqdn = "${subdomain}.${domain}"; - # TODO: Test login - commonTestScript = { nodes, ... }: - let - hasSSL = !(isNull nodes.server.shb.grocy.ssl); - proto_fqdn = if hasSSL then "https://${fqdn}" else "http://${fqdn}"; - in - '' - import json - import os - import pathlib - - start_all() - server.wait_for_unit("phpfpm-grocy.service") - server.wait_for_unit("nginx.service") - server.wait_for_open_unix_socket("${nodes.server.services.phpfpm.pools.grocy.socket}") - - if ${if hasSSL then "True" else "False"}: - server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt") - client.succeed("rm -r /etc/ssl/certs") - client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt") - - def curl(target, format, endpoint, succeed=True): - return json.loads(target.succeed( - "curl --fail-with-body --silent --show-error --output /dev/null --location" - + " --connect-to ${fqdn}:443:server:443" - + " --connect-to ${fqdn}:80:server:80" - + f" --write-out '{format}'" - + " " + endpoint - )) - - with subtest("access"): - response = curl(client, """{"code":%{response_code}}""", "${proto_fqdn}") - - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") - ''; + commonTestScript = testLib.accessScript { + inherit fqdn; + hasSSL = { node, ... }: !(isNull node.config.shb.grocy.ssl); + waitForServices = { ... }: [ + "phpfpm-grocy.service" + "nginx.service" + ]; + waitForUnixSocket = { node, ... }: [ + node.config.services.phpfpm.pools.grocy.socket + ]; + # TODO: Test login + # extraScript = { ... }: '' + # ''; + }; base = { imports = [ diff --git a/test/vm/home-assistant.nix b/test/vm/home-assistant.nix index 1d265c3..bdaf909 100644 --- a/test/vm/home-assistant.nix +++ b/test/vm/home-assistant.nix @@ -2,46 +2,23 @@ let pkgs' = pkgs; + testLib = pkgs.callPackage ../common.nix {}; + subdomain = "ha"; domain = "example.com"; fqdn = "${subdomain}.${domain}"; - # TODO: Test login - commonTestScript = { nodes, ... }: - let - hasSSL = !(isNull nodes.server.shb.home-assistant.ssl); - proto_fqdn = if hasSSL then "https://${fqdn}" else "http://${fqdn}"; - in - '' - import json - import os - import pathlib - - start_all() - server.wait_for_unit("home-assistant.service") - server.wait_for_unit("nginx.service") - server.wait_for_open_port(8123) - - if ${if hasSSL then "True" else "False"}: - server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt") - client.succeed("rm -r /etc/ssl/certs") - client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt") - - def curl(target, format, endpoint, succeed=True): - return json.loads(target.succeed( - "curl --fail-with-body --silent --show-error --output /dev/null --location" - + " --connect-to ${fqdn}:443:server:443" - + " --connect-to ${fqdn}:80:server:80" - + f" --write-out '{format}'" - + " " + endpoint - )) - - with subtest("access"): - response = curl(client, """{"code":%{response_code}}""", "${proto_fqdn}") - - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") - ''; + commonTestScript = testLib.accessScript { + inherit fqdn; + hasSSL = { node, ... }: !(isNull node.config.shb.home-assistant.ssl); + waitForServices = { ... }: [ + "home-assistant.service" + "nginx.service" + ]; + waitForPorts = { node, ... }: [ + 8123 + ]; + }; base = { config, ... }: { imports = [ diff --git a/test/vm/jellyfin.nix b/test/vm/jellyfin.nix index 253424e..aadca81 100644 --- a/test/vm/jellyfin.nix +++ b/test/vm/jellyfin.nix @@ -2,42 +2,23 @@ let pkgs' = pkgs; - # TODO: Test login - commonTestScript = { nodes, ... }: - let - hasSSL = !(isNull nodes.server.shb.jellyfin.ssl); - fqdn = if hasSSL then "https://j.example.com" else "http://j.example.com"; - in - '' - import json - import os - import pathlib + testLib = pkgs.callPackage ../common.nix {}; - start_all() - server.wait_for_unit("jellyfin.service") - server.wait_for_unit("nginx.service") - server.wait_for_open_port(8096) + subdomain = "j"; + domain = "example.com"; + fqdn = "${subdomain}.${domain}"; - if ${if hasSSL then "True" else "False"}: - server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt") - client.succeed("rm -r /etc/ssl/certs") - client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt") - - def curl(target, format, endpoint, succeed=True): - return json.loads(target.succeed( - "curl --fail-with-body --silent --show-error --output /dev/null --location" - + " --connect-to j.example.com:443:server:443" - + " --connect-to j.example.com:80:server:80" - + f" --write-out '{format}'" - + " " + endpoint - )) - - with subtest("access"): - response = curl(client, """{"code":%{response_code}}""", "${fqdn}") - - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") - ''; + commonTestScript = testLib.accessScript { + inherit fqdn; + hasSSL = { node, ... }: !(isNull node.config.shb.jellyfin.ssl); + waitForServices = { ... }: [ + "jellyfin.service" + "nginx.service" + ]; + waitForPorts = { node, ... }: [ + 8096 + ]; + }; in { basic = pkgs.testers.runNixOSTest { diff --git a/test/vm/monitoring.nix b/test/vm/monitoring.nix index cc31db4..eda1946 100644 --- a/test/vm/monitoring.nix +++ b/test/vm/monitoring.nix @@ -2,65 +2,24 @@ let pkgs' = pkgs; + testLib = pkgs.callPackage ../common.nix {}; + subdomain = "grafana"; domain = "example.com"; fqdn = "${subdomain}.${domain}"; password = "securepw"; - commonTestScript = { nodes, ... }: - let - hasSSL = !(isNull nodes.server.shb.monitoring.ssl); - proto_fqdn = if hasSSL then "https://${fqdn}" else "http://${fqdn}"; - in - '' - import base64 - import json - import os - import pathlib - - start_all() - server.wait_for_unit("nginx.service") - server.wait_for_open_port(${toString nodes.server.shb.monitoring.grafanaPort}) - - if ${if hasSSL then "True" else "False"}: - server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt") - client.succeed("rm -r /etc/ssl/certs") - client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt") - - def find_in_logs(unit, text): - return server.systemctl("status {}".format(unit))[1].find(text) != -1 - - def curl(target, format, endpoint, user = None): - errcode, r = target.execute( - "curl --fail-with-body --silent --show-error --location" - + " --connect-to ${fqdn}:443:server:443" - + " --connect-to ${fqdn}:80:server:80" - + (f" --header \"Authorization: Basic {base64.b64encode(user).decode('utf-8')}\"" if user is not None else "") - + (" --output /dev/null" if format != "" else "") - + (f" --write-out '{format}'" if format != "" else "") - + " " + endpoint - ) - if format == "": - return r - return json.loads(r) - - with subtest("access"): - response = curl(client, """{"code":%{response_code}}""", "${proto_fqdn}") - - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") - - with subtest("api succeed"): - response = curl(client, """{"code":%{response_code}}""", "${proto_fqdn}/api/org", user=b"admin:${password}") - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") - - with subtest("api wrong code"): - response = curl(client, """{"code":%{response_code}}""", "${proto_fqdn}/api/org", user=b"admin:wrong") - if response['code'] != 401: - raise Exception(f"Code is {response['code']}") - ''; + commonTestScript = testLib.accessScript { + inherit fqdn; + hasSSL = { node, ... }: !(isNull node.config.shb.monitoring.ssl); + waitForServices = { ... }: [ + "nginx.service" + ]; + waitForPorts = { node, ... }: [ + node.config.shb.monitoring.grafanaPort + ]; + }; base = { imports = [ diff --git a/test/vm/nextcloud.nix b/test/vm/nextcloud.nix index d2d96a4..9a0c3f9 100644 --- a/test/vm/nextcloud.nix +++ b/test/vm/nextcloud.nix @@ -8,45 +8,24 @@ let domain = "example.com"; fqdn = "${subdomain}.${domain}"; - commonTestScript = { nodes, ... }: - let - hasSSL = !(isNull nodes.server.shb.nextcloud.ssl); - proto_fqdn = if hasSSL then "https://${fqdn}" else "http://${fqdn}"; - in - '' - import json - import os - import pathlib + testLib = pkgs.callPackage ../common.nix {}; + + commonTestScript = testLib.accessScript { + inherit fqdn; + hasSSL = { node, ... }: !(isNull node.config.shb.nextcloud.ssl); + waitForServices = { ... }: [ + "phpfpm-nextcloud.service" + "nginx.service" + ]; + waitForUnixSocket = { node, ... }: [ + node.config.services.phpfpm.pools.nextcloud.socket + ]; + extraScript = { node, proto_fqdn, ... }: '' import time - start_all() - server.wait_for_unit("phpfpm-nextcloud.service") - server.wait_for_unit("nginx.service") - server.wait_for_open_unix_socket("${nodes.server.services.phpfpm.pools.nextcloud.socket}") - - if ${if hasSSL then "True" else "False"}: - server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt") - client.succeed("rm -r /etc/ssl/certs") - client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt") - def find_in_logs(unit, text): return server.systemctl("status {}".format(unit))[1].find(text) != -1 - def curl(target, format, endpoint, succeed=True): - return json.loads(target.succeed( - "curl --fail-with-body --silent --show-error --output /dev/null --location" - + " --connect-to ${fqdn}:443:server:443" - + " --connect-to ${fqdn}:80:server:80" - + f" --write-out '{format}'" - + " " + endpoint - )) - - with subtest("access"): - response = curl(client, """{"code":%{response_code}}""", "${proto_fqdn}") - - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") - with subtest("cron job succeeds"): # This calls blocks until the service is done. server.systemctl("start nextcloud-cron.service") @@ -133,6 +112,7 @@ let if content != "hello\n": raise Exception("Got incorrect content for file, expected 'hello\n' but got:\n{}".format(content)) ''; + }; base = { imports = [ diff --git a/test/vm/vaultwarden.nix b/test/vm/vaultwarden.nix index bbc9f87..b294382 100644 --- a/test/vm/vaultwarden.nix +++ b/test/vm/vaultwarden.nix @@ -2,47 +2,24 @@ let pkgs' = pkgs; + testLib = pkgs.callPackage ../common.nix {}; + subdomain = "v"; domain = "example.com"; fqdn = "${subdomain}.${domain}"; - # TODO: Test login - commonTestScript = { nodes, ... }: - let - hasSSL = !(isNull nodes.server.shb.vaultwarden.ssl); - proto_fqdn = if hasSSL then "https://${fqdn}" else "http://${fqdn}"; - in - '' - import json - import os - import pathlib - - start_all() - server.wait_for_unit("vaultwarden.service") - server.wait_for_unit("nginx.service") - server.wait_for_open_port(8222) - server.wait_for_open_port(5432) - - if ${if hasSSL then "True" else "False"}: - server.copy_from_vm("/etc/ssl/certs/ca-certificates.crt") - client.succeed("rm -r /etc/ssl/certs") - client.copy_from_host(str(pathlib.Path(os.environ.get("out", os.getcwd())) / "ca-certificates.crt"), "/etc/ssl/certs/ca-certificates.crt") - - def curl(target, format, endpoint, succeed=True): - return json.loads(target.succeed( - "curl --fail-with-body --silent --show-error --output /dev/null --location" - + " --connect-to ${fqdn}:443:server:443" - + " --connect-to ${fqdn}:80:server:80" - + f" --write-out '{format}'" - + " " + endpoint - )) - - with subtest("access"): - response = curl(client, """{"code":%{response_code}}""", "${proto_fqdn}") - - if response['code'] != 200: - raise Exception(f"Code is {response['code']}") - ''; + commonTestScript = testLib.accessScript { + inherit fqdn; + hasSSL = { node, ... }: !(isNull node.config.shb.vaultwarden.ssl); + waitForServices = { ... }: [ + "vaultwarden.service" + "nginx.service" + ]; + waitForPorts = { node, ... }: [ + 8222 + 5432 + ]; + }; base = { config, ... }: { imports = [