diff --git a/default.nix b/default.nix index 5122e5a..697681f 100644 --- a/default.nix +++ b/default.nix @@ -9,28 +9,18 @@ in import src { inherit pkgs; }, }: let inherit (qpkgs) lib; - - # Use LLD for faster link times. - defaultStdenv = pkgs.clangStdenv.override { - cc = pkgs.clangStdenv.cc.override { - bintools = pkgs.wrapBintoolsWith { inherit (pkgs.llvmPackages) bintools; }; - }; - }; - - dynix = (qpkgs.callPackage ./package.nix { clangStdenv = defaultStdenv; }) + dynix = (qpkgs.callPackage ./package.nix { }) .overrideAttrs (final: prev: { dynixCommand = qpkgs.stdlib.mkStdenvPretty prev.dynixCommand; dynixModules = qpkgs.stdlib.mkStdenvPretty prev.dynixModules; }) |> qpkgs.stdlib.mkStdenvPretty; - byStdenv = lib.mapAttrs (stdenvName: stdenv: let withStdenv = dynix.override { clangStdenv = stdenv; }; dynix' = withStdenv.overrideAttrs (prev: { pname = "${prev.pname}-${stdenvName}"; }); in dynix') qpkgs.validStdenvs; - in dynix.overrideAttrs (prev: lib.recursiveUpdate prev { passthru = { inherit byStdenv; }; }) diff --git a/package.nix b/package.nix index 08b6bbc..8f828c9 100644 --- a/package.nix +++ b/package.nix @@ -7,15 +7,23 @@ clangStdenv, callPackage, linkFarm, + llvmPackages, rustHooks, rustPackages, versionCheckHook, - writeScript, + wrapBintoolsWith, }: lib.callWith' rustPackages ({ rustPlatform, cargo, }: let - stdenv = clangStdenv; + # Use LLD for faster link times. + stdenv = clangStdenv.override { + cc = clangStdenv.cc.override { + bintools = wrapBintoolsWith { + bintools = llvmPackages.bintools; + }; + }; + }; cargoToml = lib.importTOML ./Cargo.toml; cargoPackage = cargoToml.package; in stdenv.mkDerivation (finalAttrs: let @@ -38,29 +46,12 @@ in { cp -r --reflink=auto "$dynixCommand/"* "$out/" mkdir -p "$modules" cp -r --reflink=auto "$dynixModules/"* "$modules/" - install -Dm a=rx "$dynixTestingClient" "$out/libexec/dynix-testing-client.py" ''; # # SUB-DERIVATONS # - dynixTestingClient = writeScript "dynix-testing-client.py" '' - #!/usr/bin/env python3 - import socket, sys, os, json - try: - sockpath = sys.argv[1] - except IndexError: - sockpath = f"{os.environ['XDG_RUNTIME_DIR']}/dynix.sock" - sock = socket.socket(family=socket.AF_UNIX) - sock.connect(sockpath) - sock.sendall(sys.stdin.buffer.read()) - sock.settimeout(20) - reply = json.loads(sock.recv(256).decode("utf-8")) - print(json.dumps(reply, indent=2)) - sys.exit(reply["status"]) - ''; - dynixCommand = stdenv.mkDerivation { pname = "${self.pname}-command"; inherit (self) version; @@ -82,12 +73,6 @@ in { lockFile = ./Cargo.lock; }; - postInstall = '' - cargo doc --document-private-items - mkdir -p "$doc" - cp -r ./target/doc/* "$doc/" - ''; - nativeBuildInputs = rustHooks.asList ++ [ cargo ]; diff --git a/src/daemon.rs b/src/daemon.rs index 5f1b88c..6fbbd86 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -64,14 +64,6 @@ pub static NIXOS_REBUILD: LazyLock<&'static Path> = LazyLock::new(|| { .unwrap_or(Path::new("/run/current-system/sw/bin/nixos-rebuild")) }); -pub static NIX: LazyLock<&'static Path> = LazyLock::new(|| { - which::which("nix") - .inspect_err(|e| error!("couldn't find `nix` in PATH: {e}")) - .map(PathBuf::into_boxed_path) - .map(|boxed| &*Box::leak(boxed)) - .unwrap_or(Path::new("/run/current-system/sw/bin/nix")) -}); - const TIMEOUT_NEVER: Option = None; static NEXT_TOKEN_NUMBER: AtomicUsize = AtomicUsize::new(1); @@ -195,7 +187,6 @@ impl Daemon { token } - #[expect(dead_code)] fn register_with_name(&mut self, fd: RawFd, kind: FdKind, name: Box) -> Token { let token = next_token(); @@ -369,31 +360,31 @@ const DAEMON: Token = Token(0); /// Private helpers. impl Daemon { - fn proxy_stdio(&mut self, fd: &BorrowedFd) -> Result<(), IoError> { - let info = self.fd_info.get(&fd.as_raw_fd()).unwrap(); - let label = match info.kind { - FdKind::ChildStdout => "stdout", - FdKind::ChildStderr => "stderr", - other => unreachable!("child stdio cannot have kind {other:?}"), - }; - // FIXME: don't use a new allocation every time. - let mut buffer: Vec = Vec::with_capacity(1024); - // FIXME: handle line buffering correctly. - loop { - let count = rustix::io::read(fd, spare_capacity(&mut buffer)) - .inspect_err(|e| error!("read() on child stdio fd {fd:?} failed: {e}"))?; - - if count == 0 { - break; - } - - for line in buffer.lines() { - info!("[child {label}]: {}", line.as_bstr()) - } - } - - Ok(()) - } + //fn proxy_stdio(&mut self, fd: &BorrowedFd) -> Result<(), IoError> { + // let info = self.fd_info.get(&fd.as_raw_fd()).unwrap(); + // let label = match info.kind { + // FdKind::ChildStdout => "stdout", + // FdKind::ChildStderr => "stderr", + // other => unreachable!("child stdio cannot have kind {other:?}"), + // }; + // // FIXME: don't use a new allocation every time. + // let mut buffer: Vec = Vec::with_capacity(1024); + // // FIXME: handle line buffering correctly. + // loop { + // let count = rustix::io::read(fd, spare_capacity(&mut buffer)) + // .inspect_err(|e| error!("read() on child stdio fd {fd:?} failed: {e}"))?; + // + // if count == 0 { + // break; + // } + // + // for line in buffer.lines() { + // debug!("[child {label}]: {}", line.as_bstr()) + // } + // } + // + // Ok(()) + //} fn read_cmd(&mut self, fd: &BorrowedFd) -> Result<(), IoError> { // FIXME: don't use a new allocation every time. @@ -442,44 +433,25 @@ impl Daemon { let new_pri = pri - 1; // Get next priority line. let opt_name = name.to_nix_decl(); - let new_line = crate::get_next_prio_line( - source_file.clone(), - &opt_name, - new_pri, - &value.to_nix_source(), - ) - .unwrap_or_else(|e| panic!("someone is holding a reference to source.lines(): {e}")); + let new_line = crate::get_next_prio_line(source_file.clone(), &opt_name, new_pri, &value) + .unwrap_or_else(|e| panic!("someone is holding a reference to source.lines(): {e}")); crate::write_next_prio(source_file, new_line).unwrap_or_else(|e| todo!("{e}")); // Rebuild and switch. // FIXME: allow passing additional args. - //let child = Command::new(*NIXOS_REBUILD) - // .arg("switch") - // .arg("--log-format") - // .arg("raw-with-logs") - // .arg("--no-reexec") - // .arg("-v") - // .stdout(Stdio::piped()) - // .stderr(Stdio::piped()) - // .spawn() - // .inspect_err(|e| { - // error!("failed to spawn `nixos-rebuild` command: {e}"); - // })?; - - let expr = "(import { }).config.dynamicism.applyDynamicConfiguration { }"; - let child = Command::new(*NIX) - .arg("run") - .arg("--show-trace") + let child = Command::new(*NIXOS_REBUILD) + .arg("switch") .arg("--log-format") .arg("raw-with-logs") - .arg("--impure") - .arg("-E") - .arg(expr) + .arg("--no-reexec") + .arg("-v") .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() - .inspect_err(|e| error!("failed to spawn `nix run` command: {e}"))?; + .inspect_err(|e| { + error!("failed to spawn `nixos-rebuild` command: {e}"); + })?; debug!("Spanwed child process {}", child.id()); @@ -650,19 +622,9 @@ impl Daemon { panic!("Received an event on an unregistered fd {fd}; IO-safety violation?"); }; - let either_available = event.is_readable() || event.is_writable(); - if !either_available { - info!( - "File descriptor {} r:{}, w:{}", - info.display(), - event.is_readable(), - event.is_writable(), - ); - // FIXME: code duplication - if event.is_read_closed() { - self.deregister(fd); - return Ok(()); - } + if event.is_read_closed() { + self.deregister(fd); + return Ok(()); } match info.kind { @@ -690,34 +652,17 @@ impl Daemon { // Close the pidfd. self.deregister(fd); - - let stream = self - .fd_info - .iter() - .find_map(|info| (info.kind == FdKind::SockStream).then_some(info)); - if let Some(stream) = stream { - // SAFETY: fixme. - let stream_fd = unsafe { BorrowedFd::borrow_raw(stream.fd) }; - let payload = format!("{{ \"status\": {exit_code} }}\n"); - if let Err(e) = rustix::io::write(stream_fd, payload.as_bytes()) { - error!("couldn't write reply to stream fd {stream_fd:?}: {e}"); - } - } - }, - FdKind::ChildStdout => { - warn!("got stdout"); - // SAFETY: oh boy. - let stdout = unsafe { BorrowedFd::borrow_raw(fd) }; - self.proxy_stdio(&stdout) - .unwrap_or_else(|e| error!("failed to proxy child stdout: {e}")); - }, - FdKind::ChildStderr => { - warn!("got stderr"); - // SAFETY: oh boy. - let stderr = unsafe { BorrowedFd::borrow_raw(fd) }; - self.proxy_stdio(&stderr) - .unwrap_or_else(|e| error!("failed to proxy child stderr: {e}")); }, + //FdKind::ChildStdout => { + // warn!("got stdout"); + // todo!(); + //}, + //FdKind::ChildStderr => { + // warn!("got stderr"); + // // SAFETY: oh boy. + // let stderr = unsafe { BorrowedFd::borrow_raw(fd) }; + // self.proxy_stdio(&stderr).unwrap(); + //}, FdKind::SockStream => { // SAFETY: oh boy. let stream_fd = unsafe { BorrowedFd::borrow_raw(fd) }; @@ -725,11 +670,6 @@ impl Daemon { }, kind => todo!("{kind:?}"), }; - - if event.is_read_closed() { - self.deregister(fd); - return Ok(()); - } }, } diff --git a/src/daemon/api.rs b/src/daemon/api.rs index 2ab805c..077ce0f 100644 --- a/src/daemon/api.rs +++ b/src/daemon/api.rs @@ -47,24 +47,6 @@ impl ConvenientAttrPath { } } -#[derive(Debug, Clone, PartialEq)] -#[derive(Deserialize, Serialize)] -#[serde(untagged)] -pub enum NixLiteral { - String(String), - Number(f64), - // FIXME: add the rest =P -} - -impl NixLiteral { - pub fn to_nix_source(&self) -> String { - match self { - NixLiteral::String(s) => format!("\"{s}\""), - NixLiteral::Number(n) => n.to_string(), - } - } -} - #[derive(Debug, Clone, PartialEq)] #[derive(serde::Deserialize, serde::Serialize)] #[serde(tag = "action", content = "args", rename_all = "snake_case")] @@ -72,6 +54,6 @@ impl NixLiteral { pub enum DaemonCmd { Append { name: ConvenientAttrPath, - value: Box, + value: Box, }, } diff --git a/src/lib.rs b/src/lib.rs index cfa921f..9475e70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -231,7 +231,6 @@ pub fn get_next_prio_line( let penultimate = penultimate.unwrap(); let new_generation = 0 - new_prio; - info!("setting '{option_name}' to '{new_value}' for generation '{new_generation}'"); let new_line = SourceLine { line: penultimate.line, @@ -257,8 +256,6 @@ pub fn write_next_prio(mut source: SourceFile, new_line: SourceLine) -> Result<( text: Arc::from(" }"), }; - debug!("writing new source line: {new_line}"); - source.insert_lines(&[new_mod_start, new_line, new_mod_end])?; Ok(()) diff --git a/tests/default.nix b/tests/default.nix index 0541f6f..57b1c94 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -50,11 +50,16 @@ requiredBy = [ "multi-user.target" ]; after = [ "default.target" ]; script = '' + if [[ -e /etc/nixos/hardware-configuration.nix ]]; then + echo "install-dynix: configuration already copied; nothing to do" + exit 0 + fi + nix profile install -vv "${dynix.drvPath}^*" # " mkdir -vp /etc/nixos nixos-generate-config - cp -rvf --dereference /run/current-system/sw/share/nixos/*.nix /etc/nixos/ + cp -rv --dereference /run/current-system/sw/share/nixos/*.nix /etc/nixos/ if ! [[ -e /etc/nixos/dynix-vm-configuration.nix ]]; then echo "FAILURE" echo "FAILURE" >&2 diff --git a/tests/dynix-vm-configuration.nix b/tests/dynix-vm-configuration.nix index 677ef70..2e299c3 100644 --- a/tests/dynix-vm-configuration.nix +++ b/tests/dynix-vm-configuration.nix @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: EUPL-1.1 -{ pkgs, lib, modulesPath, config, ... }: +{ pkgs, lib, modulesPath, ... }: let moduleList = import (modulesPath + "/module-list.nix"); @@ -44,40 +44,11 @@ in }; }; - # Setup XDG base directories for me. - security.pam.services.login = { - rules.session.xdg = { - enable = true; - control = "optional"; - modulePath = "${pkgs.pam_xdg}/lib/security/pam_xdg.so"; - args = [ ]; - order = 10500; - }; - }; - environment.pathsToLink = [ "/share" ]; environment.extraOutputsToInstall = [ "modules" ]; environment.variables = { "NIXOS_CONFIG" = "/etc/nixos/configuration.nix"; }; - environment.sessionVariables = { - "NIXOS_CONFIG" = "/etc/nixos/configuration.nix"; - }; - - systemd.services.dynix-daemon = { - enable = true; - path = [ config.nix.package ]; - serviceConfig = { - Environment = [ - "RUST_LOG=trace" - ]; - ExecSearchPath = [ "/run/current-system/sw/bin" ]; - SuccessExitStatus = [ "0" "2" ]; - # `bash -l` so XDG_RUNTIME_DIR is set correctly. lol. - ExecStart = "bash -l -c 'exec /root/.nix-profile/bin/dynix daemon --color=always'"; - SyslogIdentifier = "dynix-daemon"; - }; - }; environment.shellAliases = { ls = "eza --long --header --group --group-directories-first --classify --binary"; @@ -87,8 +58,5 @@ in eza fd ripgrep - netcat.nc - socat - python3 ]; } diff --git a/tests/gotosocial/test-script.py b/tests/gotosocial/test-script.py index 0f3131e..eca3044 100644 --- a/tests/gotosocial/test-script.py +++ b/tests/gotosocial/test-script.py @@ -5,7 +5,7 @@ from pathlib import Path import shlex import textwrap -from typing import Any, cast, TYPE_CHECKING +from typing import cast, TYPE_CHECKING from beartype import beartype @@ -18,13 +18,6 @@ if TYPE_CHECKING: assert machine.shell is not None ls = "eza -lah --color=always --group-directories-first" -testing_client = "/root/.nix-profile/libexec/dynix-testing-client.py" - -ANSI_RESET = "\x1b[0m" -ANSI_BOLD = "\x1b[1m" -ANSI_NOBOLD = "\x1b[22m" -ANSI_RED = "\x1b[31m" -ANSI_GREEN = "\x1b[32m" @beartype def run_log(machine: Machine, *commands: str, timeout: int | None = 60) -> str: @@ -61,108 +54,71 @@ def get_config_file() -> str: config_file_path.unlink() - machine.logger.info(f"{ANSI_GREEN}INFO{ANSI_RESET}: got config file:") - machine.logger.info(textwrap.indent(data, " ")) - return data @beartype -def dynix_append_cli(option: str, value: Any): - value = f'"{value}"' if isinstance(value, str) else value +def dynix_append(option: str, value: str): machine.succeed(f''' dynix append {shlex.quote(option)} {shlex.quote(value)} '''.strip()) +@beartype +def do_apply(): expr = textwrap.dedent(""" (import { }).config.dynamicism.applyDynamicConfiguration { } """).strip() - machine.succeed(textwrap.dedent(rf""" + + machine.succeed(rf""" nix run --show-trace --log-format raw-with-logs --impure -E {shlex.quote(expr)} - """).strip()) + """.strip()) -@beartype -def dynix_append_daemon(option: str, value: Any): - import json - payload = json.dumps(dict( - action="append", - args=dict( - name=option, - value=value, - ), - )) - - machine.succeed(f"echo '{payload}' | {testing_client} /run/user/0/dynix.sock") - -@beartype -def run_all_tests(machine: Machine, *, use_daemon: bool): - dynix_append = dynix_append_daemon if use_daemon else dynix_append_cli - - dynix_out = machine.succeed("dynix --version") - assert "dynix" in dynix_out, f"dynix not in {dynix_out=}" - - machine.succeed("systemctl start user@0.service") - machine.wait_for_unit("user@0.service") - machine.succeed("systemctl start dynix-daemon.service") - machine.wait_for_unit("dynix-daemon.service") - - machine.log("REBUILDING configuration inside VM") - machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs --no-reexec --fallback") - machine.wait_for_unit("gotosocial.service") - - # Make sure the config before any dynamic changes is what we expect. - config_text = get_config_file() - lines = config_text.splitlines() - try: - application_name = next(line for line in lines if line.startswith("application-name:")) - except StopIteration: - raise AssertionError(f"no 'application-name:' found in config file: {textwrap.indent(config_text, " ")}") - assert "gotosocial-for-machine" in application_name, f"'gotosocial-for-machine' should be in {application_name=}" - - try: - host = next(line for line in lines if line.startswith("host:")) - except StopIteration: - raise AssertionError(f"no 'host:' found in config file: {textwrap.indent(config_text, " ")}") - assert "gotosocial-machine" in host, f"'gotosocial-machine' should be in {host=}" - - new_app_name = "yay!" - dynix_append("services.gotosocial.settings.application-name", f'{new_app_name}') - - config_text = get_config_file() - lines = config_text.splitlines() - try: - application_name = next(line for line in lines if line.startswith("application-name:")) - except StopIteration: - raise AssertionError(f"no 'application-name:' found in config file: {textwrap.indent(config_text, " ")}") - assert new_app_name in application_name, f"'{new_app_name}' should be in {application_name=}" - - machine.log("REBUILDING configuration inside VM") - machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs --no-reexec --fallback") - - machine.wait_for_unit("gotosocial.service") - - config_text = get_config_file() - lines = config_text.splitlines() - try: - application_name = next(line for line in lines if line.startswith("application-name:")) - except StopIteration: - raise AssertionError(f"no 'application-name:' found in config file: {textwrap.indent(config_text, " ")}") - assert "gotosocial-for-machine" in application_name, f"'gotosocial-for-machine' should be in {application_name=}" - -machine.start(allow_reboot=True) machine.wait_for_unit("default.target") machine.wait_for_unit("install-dynix.service") -try: - run_all_tests(machine, use_daemon=False) -except Exception as e: - machine.logger.error(f"{ANSI_RED}ERROR{ANSI_RESET} during {ANSI_BOLD}CLI{ANSI_RESET} tests: {e}") - raise -machine.reboot() +dynix_out = machine.succeed("dynix --version") +assert "dynix" in dynix_out, f"dynix not in {dynix_out=}" -machine.wait_for_unit("install-dynix.service") +machine.log("REBUILDING configuration inside VM") +machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs --no-reexec --fallback") +machine.wait_for_unit("gotosocial.service") + +# Make sure the config before any dynamic changes is what we expect. +config_text = get_config_file() +lines = config_text.splitlines() try: - run_all_tests(machine, use_daemon=True) -except Exception as e: - machine.logger.error(f"{ANSI_RED}ERROR{ANSI_RESET} during {ANSI_BOLD}daemon{ANSI_RESET} tests: {e}") - raise + application_name = next(line for line in lines if line.startswith("application-name:")) +except StopIteration: + raise AssertionError(f"no 'application-name:' found in config file: {textwrap.indent(config_text, " ")}") +assert "gotosocial-for-machine" in application_name, f"'gotosocial-for-machine' should be in {application_name=}" + +try: + host = next(line for line in lines if line.startswith("host:")) +except StopIteration: + raise AssertionError(f"no 'host:' found in config file: {textwrap.indent(config_text, " ")}") +assert "gotosocial-machine" in host, f"'gotosocial-machine' should be in {host=}" + +new_app_name = "yay!" +dynix_append("services.gotosocial.settings.application-name", f'"{new_app_name}"') +do_apply() + +config_text = get_config_file() +lines = config_text.splitlines() +try: + application_name = next(line for line in lines if line.startswith("application-name:")) +except StopIteration: + raise AssertionError(f"no 'application-name:' found in config file: {textwrap.indent(config_text, " ")}") +assert new_app_name in application_name, f"'{new_app_name}' should be in {application_name=}" + +machine.log("REBUILDING configuration inside VM") +machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs --no-reexec --fallback") + +machine.wait_for_unit("gotosocial.service") + +config_text = get_config_file() +lines = config_text.splitlines() +try: + application_name = next(line for line in lines if line.startswith("application-name:")) +except StopIteration: + raise AssertionError(f"no 'application-name:' found in config file: {textwrap.indent(config_text, " ")}") +assert "gotosocial-for-machine" in application_name, f"'gotosocial-for-machine' should be in {application_name=}" diff --git a/tests/harmonia/test-script.py b/tests/harmonia/test-script.py index 83ca0db..39d0e5d 100644 --- a/tests/harmonia/test-script.py +++ b/tests/harmonia/test-script.py @@ -4,7 +4,6 @@ import functools from pathlib import Path -import json import shlex import textwrap import tomllib @@ -21,7 +20,6 @@ if TYPE_CHECKING: assert machine.shell is not None ls = "eza -lah --color=always --group-directories-first" -testing_client = "/root/.nix-profile/libexec/dynix-testing-client.py" indent = functools.partial(textwrap.indent, prefix=' ') @@ -57,114 +55,66 @@ def get_config_file() -> dict[str, Any]: with open(config_file_path, "rb") as f: config_data = tomllib.load(f) - try: - config_file_path.unlink() - except Exception as e: - machine.log(f"Couldn't unlike path {config_file_path}: {e}") - raise + config_file_path.unlink() return config_data @beartype -def dynix_append_daemon(option: str, value: Any): - payload = json.dumps(dict( - action="append", - args=dict( - name=option, - value=value, - ), - )) - - machine.succeed(f"echo '{payload}' | {testing_client} /run/user/0/dynix.sock") - -@beartype -def dynix_append_cli(option: str, value: Any): +def dynix_append(option: str, value: Any): machine.succeed(f''' dynix append {shlex.quote(option)} {shlex.quote(str(value))} '''.strip()) +@beartype +def do_apply(): expr = textwrap.dedent(""" (import { }).config.dynamicism.applyDynamicConfiguration { } """).strip() + machine.succeed(rf""" nix run --show-trace --log-format raw-with-logs --impure -E {shlex.quote(expr)} """.strip()) -@beartype -def run_all_tests(machine: Machine, *, use_daemon: bool): - dynix_append = dynix_append_daemon if use_daemon else dynix_append_cli +machine.wait_for_unit("default.target") +machine.wait_for_unit("install-dynix.service") - # - # Setup. - # - dynix_out = machine.succeed("dynix --version") - assert "dynix" in dynix_out, f"dynix not in {dynix_out=}" +dynix_out = machine.succeed("dynix --version") +assert "dynix" in dynix_out, f"dynix not in {dynix_out=}" - machine.succeed("systemctl start user@0.service") - machine.wait_for_unit("user@0.service") - run_log(machine, "systemctl start dynix-daemon.service") - machine.wait_for_unit("dynix-daemon.service") +# Config should have our initial values. +config_toml = get_config_file() +assert int(config_toml['workers']) == 4, f"{config_toml['workers']=} != 4" +assert int(config_toml['max_connection_rate']) == 256, f"{config_toml['max_connection_rate']=} != 256" - machine.log("Checking initial harmonia.service conditions") - - # Config should have our initial values. - config_toml = get_config_file() - assert int(config_toml['workers']) == 4, f"{config_toml['workers']=} != 4" - assert int(config_toml['max_connection_rate']) == 256, f"{config_toml['max_connection_rate']=} != 256" - - with machine.nested("must succeed: initial nixos-rebuild switch"): - machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs --no-reexec -v --fallback") - - # Config should not have changed. - config_toml = get_config_file() - assert int(config_toml['workers']) == 4, f"{config_toml['workers']=} != 4" - assert int(config_toml['max_connection_rate']) == 256, f"{config_toml['max_connection_rate']=} != 256" - - machine.log("Testing dynamic workers=20") - new_workers = 20 - dynix_append("services.harmonia.settings.workers", new_workers) - - machine.log("Testing that workers, but not max_connection_rate, changed") - # Workers, but not max connection rate, should have changed. - config_toml = get_config_file() - from pprint import pformat - machine.log(pformat(config_toml)) - assert int(config_toml['workers']) == new_workers, f"{config_toml['workers']=} != {new_workers}" - assert int(config_toml['max_connection_rate']) == 256, f"{config_toml['max_connection_rate']=} != 256" - - machine.log("Testing dynamic max_connection_rate=100") - new_max_connection_rate = 100 - dynix_append("services.harmonia.settings.max_connection_rate", new_max_connection_rate) - - # Max connection rate should have changed, and workers should be the same as before. - config_toml = get_config_file() - print(f"checking connection rate, {use_daemon=}") - assert int(config_toml['max_connection_rate']) == new_max_connection_rate, f"{config_toml['max_connection_rate']=} != {new_max_connection_rate}" - print(f"checking workers, {use_daemon=}") - assert int(config_toml['workers']) == new_workers, f"{config_toml['workers']=} != {new_workers}" - - machine.log("Done with tests; stopping dynix-daemon") - machine.succeed("systemctl stop dynix-daemon.service") - - # And this should set everything back. +with machine.nested("must succeed: initial nixos-rebuild switch"): machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs --no-reexec -v --fallback") - machine.wait_for_unit("harmonia.service") - config_toml = get_config_file() - assert int(config_toml['max_connection_rate']) == 256, f'{config_toml["max_connection_rate"]=} != 256' - assert int(config_toml['workers']) == 4, f'{config_toml["workers"]=} != 4' -machine.start(allow_reboot=True) -machine.wait_for_unit("install-dynix.service") -try: - run_all_tests(machine, use_daemon=False) -except Exception as e: - machine.log(f"ERROR running CLI tests: {e}") +# Config should not have changed. +config_toml = get_config_file() +assert int(config_toml['workers']) == 4, f"{config_toml['workers']=} != 4" +assert int(config_toml['max_connection_rate']) == 256, f"{config_toml['max_connection_rate']=} != 256" -machine.reboot() +new_workers = 20 +dynix_append("services.harmonia.settings.workers", new_workers) +do_apply() -machine.wait_for_unit("install-dynix.service") -try: - run_all_tests(machine, use_daemon=True) -except Exception as e: - machine.log(f"ERROR running DAEMON tests: {e}") - raise +# Workers, but not max connection rate, should have changed. +config_toml = get_config_file() +assert int(config_toml['workers']) == new_workers, f"{config_toml['workers']=} != {new_workers}" +assert int(config_toml['max_connection_rate']) == 256, f"{config_toml['max_connection_rate']=} != 256" + +new_max_connection_rate = 100 +dynix_append("services.harmonia.settings.max_connection_rate", new_max_connection_rate) +do_apply() + +# Max connection rate should have changed, and workers should be the same as before. +config_toml = get_config_file() +assert int(config_toml['max_connection_rate']) == new_max_connection_rate, f"{config_toml['max_connection_rate']=} != {new_max_connection_rate}" +assert int(config_toml['workers']) == new_workers, f"{config_toml['workers']=} != {new_workers}" + +# And this should set everything back. +machine.succeed("env PAGER= nixos-rebuild switch --log-format raw-with-logs --no-reexec -v --fallback") +machine.wait_for_unit("harmonia.service") +config_toml = get_config_file() +assert int(config_toml['max_connection_rate']) == 256, f'{config_toml["max_connection_rate"]=} != 256' +assert int(config_toml['workers']) == 4, f'{config_toml["workers"]=} != 4'