nix/tests/rwhod: init
This commit is contained in:
@@ -122,5 +122,11 @@
|
||||
# NOTE: the non-crane build runs tests during checkPhase
|
||||
inherit (self.packages.${system}) roowho2;
|
||||
});
|
||||
|
||||
tests = forAllSystems (system: pkgs: _: let
|
||||
pkgs' = pkgs.extend self.overlays.default;
|
||||
in
|
||||
import ./nix/tests { pkgs = pkgs'; }
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
# Backwards compatibility tests
|
||||
# bsd-fingerd = pkgs.testers.runNixOSTest ./bsd-fingerd.nix;
|
||||
# netkit-rwho = pkgs.testers.runNixOSTest ./netkit-rwho.nix;
|
||||
|
||||
# roowho2 integration tests
|
||||
rwhod = pkgs.testers.runNixOSTest ./rwhod.nix;
|
||||
# fingerd = pkgs.testers.runNixOSTest ./fingerd.nix;
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
{ ... }:
|
||||
{
|
||||
name = "rwhod";
|
||||
|
||||
nodes = {
|
||||
host1 = { pkgs, ... }: {
|
||||
imports = [ ../module.nix ];
|
||||
|
||||
boot.kernelParams = [ "audit=1" ];
|
||||
|
||||
environment.systemPackages = [ pkgs.screen ];
|
||||
|
||||
services.roowho2 = {
|
||||
enable = true;
|
||||
settings = {
|
||||
log_level = "debug";
|
||||
rwhod = {
|
||||
enable = true;
|
||||
# Effectively infinite interval means host1 will never announce itself
|
||||
# to the network in a periodic broadcast. We will force it to do so
|
||||
# via the realtime push mechanism when a user logs in or out.
|
||||
send_interval_seconds = 99999999;
|
||||
};
|
||||
fingerd.enable = false;
|
||||
};
|
||||
};
|
||||
|
||||
users.users.alice.isNormalUser = true;
|
||||
};
|
||||
|
||||
host2 = { pkgs, ... }: {
|
||||
imports = [ ../module.nix ];
|
||||
|
||||
environment.systemPackages = [ pkgs.screen ];
|
||||
|
||||
services.roowho2 = {
|
||||
enable = true;
|
||||
settings = {
|
||||
log_level = "debug";
|
||||
rwhod = {
|
||||
enable = true;
|
||||
send_interval_seconds = 5;
|
||||
};
|
||||
fingerd.enable = false;
|
||||
};
|
||||
};
|
||||
|
||||
users.users.bob.isNormalUser = true;
|
||||
|
||||
specialisation.ignore-bob.configuration = {
|
||||
services.roowho2.settings.rwhod.ignoreUsers = [ "bob" ];
|
||||
};
|
||||
};
|
||||
|
||||
host3 = { ... }: {
|
||||
imports = [ ../module.nix ];
|
||||
|
||||
services.roowho2 = {
|
||||
enable = true;
|
||||
settings = {
|
||||
log_level = "debug";
|
||||
rwhod = {
|
||||
enable = true;
|
||||
send_interval_seconds = 5;
|
||||
# Without any interfaces to broadcast on,
|
||||
# this host will never announce itself to the network.
|
||||
interfaces = [ ];
|
||||
};
|
||||
fingerd.enable = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
import json
|
||||
|
||||
def rwho_entries(machine):
|
||||
return json.loads(machine.succeed("rwho --all --json"))
|
||||
|
||||
def ruptime_entries(machine):
|
||||
return json.loads(machine.succeed("ruptime --all --json"))
|
||||
|
||||
def find_ruptime_entry(entries, hostname):
|
||||
return next((e for e in entries if e["hostname"] == hostname), None)
|
||||
|
||||
def find_rwho_user(entries, hostname, username):
|
||||
return next(
|
||||
(user for user in entries.get(hostname, []) if user["user_id"] == username),
|
||||
None,
|
||||
)
|
||||
|
||||
def wait_for_host_in_ruptime(machine, hostname, present=True, timeout=300):
|
||||
def check(_last_try):
|
||||
found = find_ruptime_entry(ruptime_entries(machine), hostname) is not None
|
||||
return found == present
|
||||
retry(check, timeout_seconds=timeout)
|
||||
|
||||
def wait_for_user_in_rwho(machine, hostname, username, present=True, timeout=300):
|
||||
def check(_last_try):
|
||||
found = find_rwho_user(rwho_entries(machine), hostname, username) is not None
|
||||
return found == present
|
||||
retry(check, timeout_seconds=timeout)
|
||||
|
||||
def start_session(machine, username, session_name):
|
||||
machine.succeed(f"screen -dmS {session_name} login -f {username}")
|
||||
|
||||
def stop_session(machine, session_name):
|
||||
machine.succeed(f"screen -S {session_name} -X quit")
|
||||
|
||||
start_all()
|
||||
for machine in [host1, host2, host3]:
|
||||
machine.wait_for_unit("roowho2-rwhod.socket")
|
||||
machine.wait_for_unit("roowho2-client.socket")
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("ruptime --all --json")
|
||||
|
||||
with subtest("host2 broadcasts its status periodically"):
|
||||
host2.wait_for_console_text(r"Sending rwhod packet to interface")
|
||||
wait_for_host_in_ruptime(host1, "host2", timeout=20)
|
||||
wait_for_host_in_ruptime(host3, "host2", timeout=20)
|
||||
|
||||
with subtest("Check host2 ruptime fields"):
|
||||
entry = find_ruptime_entry(ruptime_entries(host1), "host2")
|
||||
assert entry is not None, "Expected host2 to be present in host1's ruptime"
|
||||
assert entry["users"] == [], f"Expected no users logged in yet, got: {entry!r}"
|
||||
|
||||
with subtest("Starting a session on host1 triggers a realtime status push"):
|
||||
start_session(host1, "alice", "alice-session")
|
||||
wait_for_user_in_rwho(host2, "host1", "alice")
|
||||
wait_for_user_in_rwho(host3, "host1", "alice")
|
||||
|
||||
with subtest("Stopping a session on host1 triggers a realtime status push"):
|
||||
stop_session(host1, "alice-session")
|
||||
wait_for_user_in_rwho(host2, "host1", "alice", present=False)
|
||||
wait_for_user_in_rwho(host3, "host1", "alice", present=False)
|
||||
|
||||
with subtest("Starting a session on host2 eventually becomes visible"):
|
||||
start_session(host2, "bob", "bob-session")
|
||||
wait_for_user_in_rwho(host1, "host2", "bob")
|
||||
wait_for_user_in_rwho(host3, "host2", "bob")
|
||||
|
||||
with subtest("Switching host2 to ignore bob removes them from the next broadcast"):
|
||||
host2.succeed(
|
||||
"/run/current-system/specialisation/ignore-bob/bin/switch-to-configuration test"
|
||||
)
|
||||
host2.wait_for_unit("roowho2-client.socket")
|
||||
host2.succeed("who | grep bob") # Ensure bob is still logged in
|
||||
wait_for_user_in_rwho(host1, "host2", "bob", present=False)
|
||||
wait_for_user_in_rwho(host3, "host2", "bob", present=False)
|
||||
|
||||
with subtest("host3's interfaces allowlist keeps it from ever broadcasting"):
|
||||
assert find_ruptime_entry(ruptime_entries(host1), "host3") is None, (
|
||||
"host1 should not have learned about host3"
|
||||
)
|
||||
assert find_ruptime_entry(ruptime_entries(host2), "host3") is None, (
|
||||
"host2 should not have learned about host3"
|
||||
)
|
||||
wait_for_host_in_ruptime(host3, "host1")
|
||||
wait_for_host_in_ruptime(host3, "host2")
|
||||
'';
|
||||
}
|
||||
Reference in New Issue
Block a user