Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nix/checks.nix
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,7 @@
)
// pkgs.lib.optionalAttrs (pkgs.stdenv.isLinux) {
site = import ./tests/site.nix { inherit self pkgs; };
site-roundtrip = import ./tests/site-roundtrip.nix { inherit self pkgs; };
inherit (self'.packages)
postgresql_15_debug
postgresql_15_src
Expand Down
6 changes: 6 additions & 0 deletions nix/packages/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@
http-mock-server = pkgs.callPackage ./http-mock-server.nix { };
image-size-analyzer = pkgs.callPackage ./image-size-analyzer.nix { };
local-infra-bootstrap = pkgs.callPackage ./local-infra-bootstrap.nix { };
manifest-diff = pkgs.callPackage ./manifest-diff.nix { };
manifest-snapshot = pkgs.callPackage ./manifest-snapshot.nix { };
site-profile-sanity = pkgs.callPackage ./site-profile-sanity.nix { };
site-profile-roundtrip = pkgs.callPackage ./site-profile-test.nix {
inherit (self'.packages) manifest-snapshot site-profile-sanity;
};
mecab-naist-jdic = pkgs.callPackage ./mecab-naist-jdic.nix { };
migrate-tool = pkgs.callPackage ./migrate-tool.nix { psql_15 = self'.packages."psql_15/bin"; };
overlayfs-on-package = pkgs.callPackage ./overlayfs-on-package.nix { };
Expand Down
48 changes: 48 additions & 0 deletions nix/packages/manifest-diff.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
writeShellApplication,
diffutils,
gnused,
coreutils,
}:
writeShellApplication {
name = "manifest-diff";
runtimeInputs = [
diffutils
gnused
coreutils
];
text = ''
if [ "$#" -lt 4 ]; then
echo "Usage: manifest-diff <manifest_path> <current_manifest_path> <publish_hint> <diff_path>" >&2
exit 1
fi
MANIFEST_PATH="$1"
CURRENT_MANIFEST_PATH="$2"
PUBLISH_HINT="$3"
DIFF_PATH="$4"

if [ -s "$MANIFEST_PATH" ]; then
mv "$MANIFEST_PATH" /tmp/baseline-raw.txt
BASELINE_VERSION=$(sed -n 's/^# version: //p' /tmp/baseline-raw.txt | head -1)
tail -n +2 /tmp/baseline-raw.txt > /tmp/baseline.txt
else
touch /tmp/baseline.txt
BASELINE_VERSION=""
fi
mv "$CURRENT_MANIFEST_PATH" "$MANIFEST_PATH"
{
if [ -z "$BASELINE_VERSION" ]; then
echo "No baseline cached yet. $PUBLISH_HINT"
elif diff -q /tmp/baseline.txt "$MANIFEST_PATH" > /dev/null; then
echo "No changes vs baseline $BASELINE_VERSION."
else
echo "baseline: $BASELINE_VERSION"
diff \
--old-line-format='-%L' \
--new-line-format='+%L' \
--unchanged-group-format=$'... %dn unchanged\n' \
/tmp/baseline.txt "$MANIFEST_PATH" || true
fi
} | tee "$DIFF_PATH"
'';
}
13 changes: 13 additions & 0 deletions nix/packages/manifest-snapshot.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
writeShellApplication,
libarchive,
coreutils,
}:
writeShellApplication {
name = "manifest-snapshot";
runtimeInputs = [
libarchive
coreutils
];
text = builtins.readFile ../../testinfra/manifest-snapshot.sh;
}
9 changes: 9 additions & 0 deletions nix/packages/site-profile-sanity.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
writeShellApplication,
coreutils,
}:
writeShellApplication {
name = "site-profile-sanity";
runtimeInputs = [ coreutils ];
text = builtins.readFile ../../testinfra/site-profile-sanity.sh;
}
19 changes: 19 additions & 0 deletions nix/packages/site-profile-test.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
writeShellApplication,
coreutils,
diffutils,
gnugrep,
manifest-snapshot,
site-profile-sanity,
}:
writeShellApplication {
name = "site-profile-roundtrip";
runtimeInputs = [
coreutils
diffutils
gnugrep
manifest-snapshot
site-profile-sanity
];
text = builtins.readFile ../../testinfra/site-profile-roundtrip.sh;
}
58 changes: 58 additions & 0 deletions nix/tests/site-roundtrip.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{ self, pkgs }:
let
system = pkgs.pkgsLinux.stdenv.hostPlatform.system;
site-env-17 = self.packages.${system}."site-env-17";
roundtrip = self.packages.${system}.site-profile-roundtrip;
psql_17 = self.legacyPackages.${system}."psql_17".bin;
previousSiteEnv = pkgs.pkgsLinux.buildEnv {
name = "site-env-17-previous";
paths = [
(pkgs.pkgsLinux.writeTextDir "share/site-env-previous" "previous")
];
};
pgRestart = pkgs.pkgsLinux.writeShellScript "pg-restart" ''
exec su postgres -c '${psql_17}/bin/pg_ctl -D /tmp/pgdata -l /tmp/pg.log restart'
'';
psql = pkgs.pkgsLinux.writeShellScript "psql" ''
exec ${psql_17}/bin/psql -h /tmp -U postgres -X -At "$@"
'';
pgConf = pkgs.writeText "postgresql-test.conf" ''
dynamic_library_path = '/nix/var/nix/profiles/site/lib/postgresql:$libdir'
session_preload_libraries = 'supautils'
unix_socket_directories = '/tmp'
'';
in
pkgs.testers.runNixOSTest {
name = "site-roundtrip";
nodes.machine =
{ ... }:
{
environment.systemPackages = [
site-env-17
roundtrip
];
virtualisation.additionalPaths = [
psql_17
previousSiteEnv
];
users.users.postgres = {
isSystemUser = true;
group = "postgres";
shell = pkgs.bash;
};
users.groups.postgres = { };
};
testScript = ''
machine.succeed("install -d -o postgres -g postgres /tmp/pgdata")
machine.succeed("su postgres -c '${psql_17}/bin/initdb -D /tmp/pgdata'")
machine.succeed("install -o postgres -g postgres ${pgConf} /tmp/pgdata/postgresql.conf")
machine.succeed("su postgres -c '${psql_17}/bin/pg_ctl -D /tmp/pgdata -l /tmp/pg.log start'")

machine.succeed("nix-env --profile /nix/var/nix/profiles/site --set ${previousSiteEnv}")

machine.succeed(
"PG_RESTART=${pgRestart} PSQL='${psql} -c' "
"site-profile-roundtrip ${site-env-17} /tmp/manifests"
)
'';
}
40 changes: 40 additions & 0 deletions testinfra/manifest-snapshot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/sh
set -eu

INSTALLED=0
if ! command -v bsdtar >/dev/null 2>&1; then
if command -v apt-get >/dev/null 2>&1; then
DEBIAN_FRONTEND=noninteractive apt-get update -qq >/dev/null
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends libarchive-tools >/dev/null
elif command -v apk >/dev/null 2>&1; then
apk add --no-cache libarchive-tools >/dev/null
fi
INSTALLED=1
fi

cd /
# shellcheck disable=SC2086
bsdtar --format=mtree \
--options='!all,type,mode,uid,gid,sha256digest,link' \
--exclude=./dev --exclude=./proc --exclude=./run --exclude=./sys --exclude=./tmp --exclude=./var/log --exclude=./data \
${MANIFEST_EXCLUDES:-} \
-cf - . 2>/dev/null | sort

if [ "$INSTALLED" = 1 ]; then
if command -v apt-get >/dev/null 2>&1; then
DEBIAN_FRONTEND=noninteractive apt-get remove -y --purge libarchive-tools >/dev/null
elif command -v apk >/dev/null 2>&1; then
apk del libarchive-tools >/dev/null
fi
fi

echo '--- units ---'
systemctl list-unit-files --no-pager 2>/dev/null | sort
echo '--- users ---'
getent passwd | sort
echo '--- groups ---'
getent group | sort
echo '--- nft ---'
nft list ruleset 2>/dev/null || true
echo '--- sysctl ---'
sysctl -a 2>/dev/null | sort
51 changes: 51 additions & 0 deletions testinfra/site-profile-roundtrip.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
# Usage: site-profile-roundtrip <site_env_path> <out_dir>
# Run as root on a host booted from the latest AMI.
set -euo pipefail

SITE_ENV="${1:?Usage: $0 <site_env_path> <out_dir>}"
OUT="${2:?Usage: $0 <site_env_path> <out_dir>}"
PROFILE=/nix/var/nix/profiles/site
SANITY="${SITE_PROFILE_SANITY:-site-profile-sanity}"

mkdir -p "$OUT"
export MANIFEST_EXCLUDES="--exclude=./nix/store --exclude=./nix/.ro-store --exclude=./nix/.rw-store --exclude=./nix/var/nix/db"

# Store paths, the nix db, and generation links change on every update by design.
# Sysctl is dropped as it holds live counters. The store is skipped at capture, as hashing it dominates the runtime.
snapshot() {
manifest-snapshot | sed '/^--- sysctl ---$/,$d' | grep -v -e '^\./nix/store' -e '^\./nix/var/nix/db' -e '^\./nix/var/nix/profiles/.*-[0-9]*-link' -e '^\./nix/var/nix/gcroots/auto/' -e '^\./nix/var/nix/temproots' >"$OUT/$1.txt"
}

update() {
nix-env --profile "$PROFILE" --set "$SITE_ENV"
"$PROFILE/bin/activate"
}

expect_same() {
if ! diff -u "$OUT/$1.txt" "$OUT/$2.txt" >"$OUT/$1-vs-$2.diff"; then
echo "FAIL: $1 and $2 differ"
cat "$OUT/$1-vs-$2.diff"
exit 1
fi
echo "ok: $1 == $2"
}

snapshot m0

update
snapshot m1
diff -u "$OUT/m0.txt" "$OUT/m1.txt" >"$OUT/update.diff" || true
"$SANITY" updated

update
snapshot m2
expect_same m1 m2

if [[ -x "$PROFILE/bin/deactivate" ]]; then
"$PROFILE/bin/deactivate"
fi
nix-env --profile "$PROFILE" --rollback
snapshot m3
expect_same m0 m3
"$SANITY" rolled-back
19 changes: 19 additions & 0 deletions testinfra/site-profile-sanity.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
# Usage: site-profile-sanity <updated|rolled-back>
# PG_RESTART and PSQL override the AMI defaults.
set -euo pipefail

MODE="${1:?Usage: $0 <updated|rolled-back>}"
read -ra PG_RESTART <<<"${PG_RESTART:-systemctl restart postgresql}"
read -ra PSQL <<<"${PSQL:-sudo -u postgres psql -X -At -c}"

"${PG_RESTART[@]}"

[[ "$("${PSQL[@]}" 'select 1')" == 1 ]]

if [[ $MODE == updated ]]; then
"${PSQL[@]}" "load 'supautils'"
"${PSQL[@]}" 'show dynamic_library_path' | grep -q /nix/var/nix/profiles/site/lib/postgresql
fi

echo "ok: sanity $MODE"
Loading