testing: migrate build-remote-trustless-*

Change-Id: I3edc3fe5babc93833bfe1e7c4bdeb02dc6ac17cd
This commit is contained in:
eldritch horrors
2026-01-02 15:37:09 +01:00
parent cc5c62ad72
commit 7dff4efc4a
12 changed files with 277 additions and 97 deletions
@@ -1,2 +0,0 @@
outPath=$(readlink -f $TEST_ROOT/result)
grep 'FOO BAR BAZ' ${remoteDir}/${outPath}
@@ -1,29 +0,0 @@
source common.sh
enableFeatures "daemon-trust-override"
restartDaemon
requireSandboxSupport
[[ $busybox =~ busybox ]] || skipTest "no busybox"
unset NIX_STORE_DIR
# We first build a dependency of the derivation we eventually want to
# build.
nix-build build-hook.nix -A passthru.input2 \
-o "$TEST_ROOT/input2" \
--arg busybox "$busybox" \
--store "$TEST_ROOT/local" \
--option system-features bar
# Now when we go to build that downstream derivation, Lix will try to
# copy our already-build `input2` to the remote store. That store object
# is input-addressed, so this will fail.
file=build-hook.nix
prog=$(readlink -e ./nix-daemon-untrusting.sh)
proto=ssh-ng
expectStderr 1 source build-remote-trustless.sh \
| grepQuiet "cannot add path '[^ ]*' because it lacks a signature by a trusted key"
@@ -1,9 +0,0 @@
source common.sh
# Remote trusts us
file=build-hook.nix
prog=nix-store
proto=ssh
source build-remote-trustless.sh
source build-remote-trustless-after.sh
@@ -1,9 +0,0 @@
source common.sh
# Remote trusts us
file=build-hook.nix
prog=nix-daemon
proto=ssh-ng
source build-remote-trustless.sh
source build-remote-trustless-after.sh
@@ -1,13 +0,0 @@
source common.sh
enableFeatures "daemon-trust-override"
restartDaemon
# Remote doesn't trust us
file=build-hook.nix
prog=$(readlink -e ./nix-daemon-untrusting.sh)
proto=ssh-ng
source build-remote-trustless.sh
source build-remote-trustless-after.sh
@@ -1,14 +0,0 @@
source common.sh
enableFeatures "daemon-trust-override"
restartDaemon
# Remote doesn't trusts us, but this is fine because we are only
# building (fixed) CA derivations.
file=build-hook-ca-fixed.nix
prog=$(readlink -e ./nix-daemon-untrusting.sh)
proto=ssh-ng
source build-remote-trustless.sh
source build-remote-trustless-after.sh
@@ -1,13 +0,0 @@
requireSandboxSupport
[[ $busybox =~ busybox ]] || skipTest "no busybox"
unset NIX_STORE_DIR
remoteDir=$TEST_ROOT/remote
# Note: ssh{-ng}://localhost bypasses ssh. See tests/functional/build-remote.sh for
# more details.
nix-build $file -o $TEST_ROOT/result --max-jobs 0 \
--arg busybox $busybox \
--store $TEST_ROOT/local \
--builders "$proto://localhost?remote-program=$prog&remote-store=${remoteDir}%3Fsystem-features=foo%20bar%20baz - - 1 1 foo,bar,baz"
-5
View File
@@ -98,11 +98,6 @@ functional_tests_scripts = [
'dependencies.sh',
'check-reqs.sh',
'build-remote-content-addressed-fixed.sh',
'build-remote-trustless-should-pass-0.sh',
'build-remote-trustless-should-pass-1.sh',
'build-remote-trustless-should-pass-2.sh',
'build-remote-trustless-should-pass-3.sh',
'build-remote-trustless-should-fail-0.sh',
'build-jobless.sh',
'nar-access.sh',
'impure-eval.sh',
@@ -1,3 +0,0 @@
#!/usr/bin/env bash
exec nix-daemon --force-untrusted "$@"
+152
View File
@@ -0,0 +1,152 @@
import pytest
import re
import textwrap
from urllib.parse import urlencode, quote
import sys
from functional2.testlib.fixtures.nix import Nix
from functional2.testlib.fixtures.file_helper import with_files
from functional2.testlib.utils import get_global_asset
from functional2.testlib.fixtures.env import ManagedEnv
@pytest.fixture
def busybox_args(env: ManagedEnv) -> list[str]:
return ["--arg", "busybox", env.path.which("busybox")]
@pytest.fixture(autouse=True)
def _setup_for_remote_builds(nix: Nix, env: ManagedEnv):
# always add bash, otherwise lix can't execute the build hook
env.path.add_program("bash")
# we don't always use this feature, but it also doesn't hurt
nix.settings.feature("daemon-trust-override")
def _builders(proto: str, flags: list[str], env: ManagedEnv) -> str:
prog = "nix-store" if proto == "ssh" else "nix-daemon"
script = f"""\
#!{sys.executable}
import os, sys
os.execvp("{env.dirs.nix_bin_dir}/nix", [*{[prog, *flags]!s}, *sys.argv[1:]])
"""
path = env.dirs.test_root / "remote-builder" / "launch.py"
path.parent.mkdir()
path.write_text(textwrap.dedent(script))
path.chmod(0o755)
remote_store = "local?" + urlencode(
{"system-features": "foo bar baz", "root": str(env.dirs.home / "remote")}, quote_via=quote
)
uri_args = urlencode(
{"remote-program": str(path), "remote-store": remote_store}, quote_via=quote
)
return textwrap.dedent(f"""
version = 1
[machines.remote]
uri = "{proto}://localhost?{uri_args}"
jobs = 8
speed-factor = 1
supported-features = [ "foo", "bar", "baz" ]
""")
@pytest.mark.full_sandbox
@with_files(
{
"build-hook.nix": get_global_asset("build-hook.nix"),
"config.nix": get_global_asset("config.nix"),
}
)
def test_remote_trustless_unsigned(nix: Nix, env: ManagedEnv, busybox_args: list[str]):
# We first build a dependency of the derivation we eventually want to build.
nix.nix_build(
[
"build-hook.nix",
"-A",
"passthru.input2",
*busybox_args,
"--option",
"system-features",
"bar",
]
).run().ok()
# Now when we go to build that downstream derivation, Lix will try to
# copy our already-build `input2` to the remote store. That store object
# is input-addressed, so this will fail.
result = nix.nix_build(
[
"build-hook.nix",
"--max-jobs",
"0",
*busybox_args,
"--builders",
_builders("ssh-ng", ["--force-untrusted"], env),
]
).run()
result.expect(1)
assert re.findall(
r"cannot add path '[^ ]*' because it lacks a signature by a trusted key",
result.stderr_plain,
)
@pytest.mark.full_sandbox
@pytest.mark.parametrize(
("protocol", "flags"), [("ssh", []), ("ssh-ng", []), ("ssh-ng", ["--force-untrusted"])]
)
@with_files(
{
"build-hook.nix": get_global_asset("build-hook.nix"),
"config.nix": get_global_asset("config.nix"),
}
)
def test_remote_trustless_ia(
nix: Nix, env: ManagedEnv, busybox_args: list[str], protocol: str, flags: list[str]
):
result = nix.nix_build(
[
"build-hook.nix",
"--max-jobs",
"0",
*busybox_args,
"--builders",
_builders(protocol, flags, env),
]
).run()
result.ok()
out_path = (env.dirs.home / "result").readlink()
assert nix.physical_store_path_for(out_path).read_text() == "FOO BAR BAZ\n"
@pytest.mark.full_sandbox
@pytest.mark.parametrize(("protocol", "flags"), [("ssh", []), ("ssh-ng", ["--force-untrusted"])])
@with_files(
{
"build-hook-ca-fixed.nix": get_global_asset("build-hook-ca-fixed.nix"),
"config.nix": get_global_asset("config.nix"),
}
)
def test_remote_trustless_ca(
nix: Nix, env: ManagedEnv, busybox_args: list[str], protocol: str, flags: list[str]
):
# Remote doesn't trusts us, but this is fine because we are only
# building (fixed) CA derivations.
result = nix.nix_build(
[
"build-hook-ca-fixed.nix",
"--max-jobs",
"0",
*busybox_args,
"--builders",
_builders(protocol, flags, env),
]
).run()
result.ok()
out_path = (env.dirs.home / "result").readlink()
assert nix.physical_store_path_for(out_path).read_text() == "FOO BAR BAZ\n"
@@ -0,0 +1,62 @@
{ busybox }:
with import ./config.nix;
let
mkDerivation = args:
derivation ({
inherit system;
builder = busybox;
args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" ''
if [ -e "$NIX_ATTRS_SH_FILE" ]; then source $NIX_ATTRS_SH_FILE; fi;
eval "$buildCommand"
'')];
outputHashMode = "recursive";
outputHashAlgo = "sha256";
} // removeAttrs args ["builder" "meta" "passthru"])
// { meta = args.meta or {}; passthru = args.passthru or {}; };
input1 = mkDerivation {
shell = busybox;
name = "build-remote-input-1";
buildCommand = "echo hi-input1; echo FOO > $out";
requiredSystemFeatures = ["foo"];
outputHash = "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=";
};
input2 = mkDerivation {
shell = busybox;
name = "build-remote-input-2";
buildCommand = "echo hi; echo BAR > $out";
requiredSystemFeatures = ["bar"];
outputHash = "sha256-XArauVH91AVwP9hBBQNlkX9ccuPpSYx9o0zeIHb6e+Q=";
};
input3 = mkDerivation {
shell = busybox;
name = "build-remote-input-3";
# `echo -n` tests handling of logs without trailing newlines
buildCommand = ''
echo -n hi-input3
read x < ${input2}
echo $x BAZ > $out
'';
requiredSystemFeatures = ["baz"];
outputHash = "sha256-daKAcPp/+BYMQsVi/YYMlCKoNAxCNDsaivwSHgQqD2s=";
};
in
mkDerivation {
shell = busybox;
name = "build-remote";
passthru = { inherit input1 input2 input3; };
buildCommand =
''
read x < ${input1}
read y < ${input3}
echo "$x $y" > $out
'';
outputHash = "sha256-5SxbkUw6xe2l9TE1uwCvTtTDysD1vhRor38OtDF0LqQ=";
}
@@ -0,0 +1,63 @@
{ busybox, contentAddressed ? false }:
with import ./config.nix;
let
caArgs = if contentAddressed then {
outputHashMode = "recursive";
outputHashAlgo = "sha256";
__contentAddressed = true;
} else {};
mkDerivation = args:
derivation ({
inherit system;
builder = busybox;
args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" ''
if [ -e "$NIX_ATTRS_SH_FILE" ]; then source $NIX_ATTRS_SH_FILE; fi;
eval "$buildCommand"
'')];
} // removeAttrs args ["builder" "meta" "passthru"]
// caArgs)
// { meta = args.meta or {}; passthru = args.passthru or {}; };
input1 = mkDerivation {
shell = busybox;
name = "build-remote-input-1";
buildCommand = "echo hi-input1; echo FOO > $out";
requiredSystemFeatures = ["foo"];
};
input2 = mkDerivation {
shell = busybox;
name = "build-remote-input-2";
buildCommand = "echo hi; echo BAR > $out";
requiredSystemFeatures = ["bar"];
};
input3 = mkDerivation {
shell = busybox;
name = "build-remote-input-3";
# `echo -n` tests handling of logs without trailing newlines
buildCommand = ''
echo -n hi-input3
read x < ${input2}
echo $x BAZ > $out
'';
requiredSystemFeatures = ["baz"];
};
in
mkDerivation {
shell = busybox;
name = "build-remote";
passthru = { inherit input1 input2 input3; };
buildCommand =
''
read x < ${input1}
read y < ${input3}
echo "$x $y" > $out
'';
}