139 lines
6.3 KiB
Shell
139 lines
6.3 KiB
Shell
# Cases for the closure pull (hive-c0re/src/cache_pull.rs): `nix-store --realise
|
|
# <top> --max-jobs 0` against a store whose configuration is what the daemon
|
|
# has on a hive host, the host's own substituter plus the swarm cache from the
|
|
# nix.conf fragment.
|
|
#
|
|
# The swarm cache holds the top path and NOT its dependency, as attic does for
|
|
# a path cache.nixos.org serves; a second cache, standing for cache.nixos.org,
|
|
# holds the dependency. The stores are chroot stores, so the realise runs in
|
|
# this process where a host runs it in the daemon; the substitution logic is
|
|
# the same.
|
|
#
|
|
# The last cases are hive-priv's step before its `nix store verify`
|
|
# (hive-priv/src/closure.rs): a path the host built itself is valid in its store
|
|
# without the swarm signature, the pull leaves it so, and `nix store copy-sigs`
|
|
# imports the signature from the cache.
|
|
set -uo pipefail
|
|
|
|
failures=0
|
|
count=0
|
|
case="setup"
|
|
|
|
fail() {
|
|
echo "FAILED: $case: $*" >&2
|
|
failures=$((failures + 1))
|
|
}
|
|
|
|
expect() { # <description> <got> <want>
|
|
count=$((count + 1))
|
|
if [ "$2" != "$3" ]; then fail "$1: got '$2', wanted '$3'"; fi
|
|
}
|
|
|
|
T=$(mktemp -d)
|
|
export HOME=$T/home XDG_CONFIG_HOME=$T/home NIX_USER_CONF_FILES=
|
|
mkdir -p "$HOME" "$T/empty"
|
|
export NIX_CONF_DIR=$T/empty
|
|
printf 'build-users-group =\nsandbox = false\nexperimental-features = nix-command\n' >"$T/empty/nix.conf"
|
|
|
|
src="local?root=$T/src"
|
|
dep=$(nix eval --raw --store "$src" --expr 'builtins.toFile "dep" "dependency"')
|
|
top=$(nix eval --raw --store "$src" --expr 'let dep = builtins.toFile "dep" "dependency"; in builtins.toFile "top" "needs ${dep}"')
|
|
nix-store --generate-binary-cache-key swarm-test-1 "$T/swarm.sec" "$T/swarm.pub" >/dev/null
|
|
nix-store --generate-binary-cache-key upstream-test-1 "$T/up.sec" "$T/up.pub" >/dev/null
|
|
attic=file://$T/attic
|
|
upstream=file://$T/upstream
|
|
nix copy --from "$src" --to "$attic?secret-key=$T/swarm.sec" "$top"
|
|
nix copy --from "$src" --to "$upstream?secret-key=$T/up.sec" "$dep"
|
|
# What attic leaves out because the upstream serves it.
|
|
rm -f "$T/attic/$(basename "$dep" | cut -c1-32).narinfo"
|
|
|
|
# The daemon's nix.conf: its own substituter and key, then the include.
|
|
# conf <name> <fragment text>: a NIX_CONF_DIR with that fragment.
|
|
conf() {
|
|
mkdir -p "$T/$1"
|
|
printf 'build-users-group =\nsandbox = false\nsubstituters = %s\ntrusted-public-keys = %s\n!include %s/cache.conf\n' \
|
|
"$upstream" "$(cat "$T/up.pub")" "$T/$1" >"$T/$1/nix.conf"
|
|
printf '%s\n' "$2" >"$T/$1/cache.conf"
|
|
}
|
|
fragment="extra-substituters = $attic
|
|
extra-trusted-public-keys = $(cat "$T/swarm.pub")"
|
|
# The paths are content-addressed (`builtins.toFile`), which `require-sigs` does
|
|
# not apply to, so this test cannot show the signature gate; hive-priv's
|
|
# `nix store verify` step and the fragment test carry the key.
|
|
|
|
# pull <conf dir> <dest name>: the exact pull of cache_pull.rs.
|
|
pull() {
|
|
NIX_CONF_DIR=$T/$1 nix-store --store "local?root=$T/$2" --realise "$top" --max-jobs 0 >/dev/null 2>"$T/err"
|
|
}
|
|
valid() { # <dest name> <path>
|
|
nix-store --store "local?root=$T/$1" --check-validity "$2" 2>/dev/null && echo yes || echo no
|
|
}
|
|
|
|
narinfo() { # <cache dir> <store path>: whether the cache has its narinfo
|
|
if [ -e "$T/$1/$(basename "$2" | cut -c1-32).narinfo" ]; then echo yes; else echo no; fi
|
|
}
|
|
|
|
case="control: the swarm cache has the top path and lacks the dependency"
|
|
expect "attic has the top path" "$(narinfo attic "$top")" yes
|
|
expect "attic lacks the dependency" "$(narinfo attic "$dep")" no
|
|
expect "upstream has the dependency" "$(narinfo upstream "$dep")" yes
|
|
|
|
case="the client-side copy that failed on a host fails here too"
|
|
if nix copy --from "$attic" --to "local?root=$T/d0" "$top" 2>"$T/err"; then
|
|
fail "nix copy --from the swarm cache alone succeeded"
|
|
fi
|
|
expect "no top path" "$(valid d0 "$top")" no
|
|
|
|
case="the daemon with both substituters realises the top path and its dependency"
|
|
conf both "$fragment"
|
|
pull both d1
|
|
expect "exit" $? 0
|
|
expect "top path valid" "$(valid d1 "$top")" yes
|
|
expect "dependency valid" "$(valid d1 "$dep")" yes
|
|
|
|
case="without the fragment the daemon has only the upstream and cannot realise the top path"
|
|
conf plain ""
|
|
if pull plain d2; then fail "realised without the swarm cache"; fi
|
|
expect "no top path" "$(valid d2 "$top")" no
|
|
|
|
# A path built on the host is input-addressed and so, unlike the toFile paths
|
|
# above, has no content address that lets `nix store verify` pass it unsigned.
|
|
# The stores here share one store directory (`store=`), as cache and host do.
|
|
case="setup: a path built in the host store, and the swarm cache holding it signed"
|
|
: "${HIVE_TEST_BASH:=$(command -v bash)}"
|
|
host="local?store=$T/hs&state=$T/hs-state&log=$T/hs-log"
|
|
built=$(nix build --store "$host" --no-link --print-out-paths --impure --expr "derivation { name = \"agent-closure\"; system = builtins.currentSystem; builder = \"$HIVE_TEST_BASH\"; args = [ \"-c\" \"echo agent > \$out\" ]; }")
|
|
nix copy --from "$host" --to "file://$T/signed?store=$T/hs&secret-key=$T/swarm.sec" "$built"
|
|
swarm_key=$(cat "$T/swarm.pub")
|
|
other_key=$(cat "$T/up.pub")
|
|
|
|
# verify <key>: hive-priv's check, against the host store.
|
|
verify() {
|
|
nix store verify --store "$host" --no-contents --sigs-needed 1 --option trusted-public-keys "$1" "$built" >/dev/null 2>&1
|
|
echo $?
|
|
}
|
|
# copy_sigs <cache url>: the exact arguments of copy_sigs_args, bar the store.
|
|
copy_sigs() {
|
|
nix store copy-sigs --store "$host" --substituter "$1" --option netrc-file "$T/netrc" "$built" >/dev/null 2>"$T/err"
|
|
}
|
|
|
|
case="control: the path is valid in the host store and the cache serves it signed"
|
|
expect "valid" "$(nix-store --store "$host" --check-validity "$built" && echo yes || echo no)" yes
|
|
expect "the cache has its narinfo" "$(if [ -e "$T/signed/$(basename "$built" | cut -c1-32).narinfo" ]; then echo yes; else echo no; fi)" yes
|
|
|
|
case="a path valid in the host store without the signature fails the verify"
|
|
expect "verify exit" "$(verify "$swarm_key")" 2
|
|
|
|
case="a cache without the path imports nothing and the verify still refuses"
|
|
copy_sigs "file://$T/empty-cache?store=$T/hs"
|
|
expect "copy-sigs exit" $? 0
|
|
expect "verify exit" "$(verify "$swarm_key")" 2
|
|
|
|
case="the cache serving the path imports the signature and the verify passes"
|
|
copy_sigs "file://$T/signed?store=$T/hs"
|
|
expect "copy-sigs exit" $? 0
|
|
expect "verify exit" "$(verify "$swarm_key")" 0
|
|
expect "another key still fails" "$(verify "$other_key")" 2
|
|
|
|
echo "$count checks, $failures failed"
|
|
[ "$failures" = 0 ]
|