From 6b68ba832a672b43bbb5c5281ab09cf58542eee8 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Sat, 6 Dec 2025 00:41:47 -0500 Subject: [PATCH] feat: nix images --- flake.lock | 9 +- flake.nix | 1 + nix/checks.nix | 11 ++ nix/docker/base.nix | 15 ++ nix/docker/configs.nix | 196 +++++++++++++++++++++++ nix/docker/default.nix | 297 +++++++++++++++++++++++++++++++++++ nix/docker/entrypoint.nix | 18 +++ nix/docker/gosu.nix | 26 +++ nix/docker/tests.nix | 56 +++++++ nix/docker/usrbin-compat.nix | 54 +++++++ 10 files changed, 677 insertions(+), 6 deletions(-) create mode 100644 nix/docker/base.nix create mode 100644 nix/docker/configs.nix create mode 100644 nix/docker/default.nix create mode 100644 nix/docker/entrypoint.nix create mode 100644 nix/docker/gosu.nix create mode 100644 nix/docker/tests.nix create mode 100644 nix/docker/usrbin-compat.nix diff --git a/flake.lock b/flake.lock index b2d4ff6e3..b323d504c 100644 --- a/flake.lock +++ b/flake.lock @@ -146,19 +146,16 @@ }, "nix2container": { "inputs": { - "flake-utils": [ - "flake-utils" - ], "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1708764364, - "narHash": "sha256-+pOtDvmuVTg0Gi58hKDUyrNla5NbyUvt3Xs3gLR0Fws=", + "lastModified": 1749074755, + "narHash": "sha256-b1RwMnZoe+YrFBDkQaLrnhsSXBHx13rynnWLzDPrr0E=", "owner": "nlewo", "repo": "nix2container", - "rev": "c891f90d2e3c48a6b33466c96e4851e0fc0cf455", + "rev": "b7467d7cf0f05f9f467ab6b719008e1d9c2602c8", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 8f83a0a25..4804cb635 100644 --- a/flake.nix +++ b/flake.nix @@ -44,6 +44,7 @@ nix/checks.nix nix/config.nix nix/devShells.nix + nix/docker nix/fmt.nix nix/hooks.nix nix/nixpkgs.nix diff --git a/nix/checks.nix b/nix/checks.nix index 0bc5261b7..2e9c13eb5 100644 --- a/nix/checks.nix +++ b/nix/checks.nix @@ -414,6 +414,17 @@ inherit self; inherit pkgs; }) + ) + // pkgs.lib.optionalAttrs (system == "aarch64-linux") ( + import ./docker/tests.nix { + inherit + self + pkgs + lib + self' + system + ; + } ); }; } diff --git a/nix/docker/base.nix b/nix/docker/base.nix new file mode 100644 index 000000000..017dd4788 --- /dev/null +++ b/nix/docker/base.nix @@ -0,0 +1,15 @@ +{ nix2container, system }: +let + arch = if system == "x86_64-linux" then "amd64" else "arm64"; + hashes = { + amd64 = "sha256-gbZeiC4j9tbKcBY8PDYZxSG9IVhselwEWDuMG9DH650="; + arm64 = "sha256-HP+/whN55n2/hKs0ROHFVSNxNqUEpC3Y7GN84/YvKt4="; + }; +in +nix2container.pullImage { + imageName = "docker.io/library/ubuntu"; + # Ubuntu Noble (24.04) base image + imageDigest = "sha256:c35e29c9450151419d9448b0fd75374fec4fff364a27f176fb458d472dfc9e54"; + sha256 = hashes.${arch}; + inherit arch; +} diff --git a/nix/docker/configs.nix b/nix/docker/configs.nix new file mode 100644 index 000000000..8271de294 --- /dev/null +++ b/nix/docker/configs.nix @@ -0,0 +1,196 @@ +{ + lib, + pkgs, + variant, + sourceRoot, +}: +let + isOrioledb = variant == "orioledb-17"; + isPg17 = variant == "17" || isOrioledb; +in +pkgs.runCommand "postgres-docker-configs-${variant}" { nativeBuildInputs = [ pkgs.gnused ]; } '' + mkdir -p $out/etc/postgresql + mkdir -p $out/etc/postgresql-custom + mkdir -p $out/etc/postgresql-custom/extension-custom-scripts + mkdir -p $out/usr/lib/postgresql/bin + mkdir -p $out/home/postgres + mkdir -p $out/root + mkdir -p $out/docker-entrypoint-initdb.d/init-scripts + mkdir -p $out/docker-entrypoint-initdb.d/migrations + mkdir -p $out/var/lib/postgresql + mkdir -p $out/var/run/postgresql + + # Create passwd and group entries for postgres user + # These will be merged with the base image's files by the entrypoint + # Using standard postgres UID/GID (999) matching the official postgres image + cat > $out/etc/passwd <<'PASSWD' + root:x:0:0:root:/root:/bin/bash + daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin + bin:x:2:2:bin:/bin:/usr/sbin/nologin + sys:x:3:3:sys:/dev:/usr/sbin/nologin + sync:x:4:65534:sync:/bin:/bin/sync + games:x:5:60:games:/usr/games:/usr/sbin/nologin + man:x:6:12:man:/var/cache/man:/usr/sbin/nologin + lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin + mail:x:8:8:mail:/var/mail:/usr/sbin/nologin + news:x:9:9:news:/var/spool/news:/usr/sbin/nologin + uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin + proxy:x:13:13:proxy:/bin:/usr/sbin/nologin + www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin + backup:x:34:34:backup:/var/backups:/usr/sbin/nologin + list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin + irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin + _apt:x:42:65534::/nonexistent:/usr/sbin/nologin + nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin + postgres:x:999:999:PostgreSQL administrator:/var/lib/postgresql:/bin/bash + wal-g:x:998:998::/nonexistent:/bin/bash + PASSWD + + cat > $out/etc/group <<'GROUP' + root:x:0: + daemon:x:1: + bin:x:2: + sys:x:3: + adm:x:4: + tty:x:5: + disk:x:6: + lp:x:7: + mail:x:8: + news:x:9: + uucp:x:10: + man:x:12: + proxy:x:13: + kmem:x:15: + dialout:x:20: + fax:x:21: + voice:x:22: + cdrom:x:24: + floppy:x:25: + tape:x:26: + sudo:x:27: + audio:x:29: + dip:x:30: + www-data:x:33: + backup:x:34: + operator:x:37: + list:x:38: + irc:x:39: + src:x:40: + shadow:x:42: + utmp:x:43: + video:x:44: + sasl:x:45: + plugdev:x:46: + staff:x:50: + games:x:60: + users:x:100: + nogroup:x:65534: + postgres:x:999: + wal-g:x:998: + GROUP + + cat > $out/etc/shadow <<'SHADOW' + root:*:19000:0:99999:7::: + daemon:*:19000:0:99999:7::: + bin:*:19000:0:99999:7::: + sys:*:19000:0:99999:7::: + sync:*:19000:0:99999:7::: + games:*:19000:0:99999:7::: + man:*:19000:0:99999:7::: + lp:*:19000:0:99999:7::: + mail:*:19000:0:99999:7::: + news:*:19000:0:99999:7::: + uucp:*:19000:0:99999:7::: + proxy:*:19000:0:99999:7::: + www-data:*:19000:0:99999:7::: + backup:*:19000:0:99999:7::: + list:*:19000:0:99999:7::: + irc:*:19000:0:99999:7::: + _apt:*:19000:0:99999:7::: + nobody:*:19000:0:99999:7::: + postgres:*:19000:0:99999:7::: + wal-g:*:19000:0:99999:7::: + SHADOW + chmod 640 $out/etc/shadow + + # Copy base configs (and make writable for sed transformations) + cp ${sourceRoot}/ansible/files/postgresql_config/postgresql.conf.j2 $out/etc/postgresql/postgresql.conf + cp ${sourceRoot}/ansible/files/postgresql_config/pg_hba.conf.j2 $out/etc/postgresql/pg_hba.conf + cp ${sourceRoot}/ansible/files/postgresql_config/pg_ident.conf.j2 $out/etc/postgresql/pg_ident.conf + cp ${sourceRoot}/ansible/files/postgresql_config/postgresql-stdout-log.conf $out/etc/postgresql/logging.conf + cp ${sourceRoot}/ansible/files/postgresql_config/supautils.conf.j2 $out/etc/postgresql-custom/supautils.conf + cp ${sourceRoot}/ansible/files/postgresql_config/custom_read_replica.conf.j2 $out/etc/postgresql-custom/read-replica.conf + cp ${sourceRoot}/ansible/files/postgresql_config/custom_walg.conf.j2 $out/etc/postgresql-custom/wal-g.conf + mkdir -p $out/etc/postgresql-custom/conf.d + cp -r ${sourceRoot}/ansible/files/postgresql_config/conf.d/* $out/etc/postgresql-custom/conf.d/ + cp ${sourceRoot}/ansible/files/postgresql_extension_custom_scripts/before-create.sql $out/etc/postgresql-custom/extension-custom-scripts/ + + # Make config files writable for sed transformations + chmod -R u+w $out/etc/postgresql $out/etc/postgresql-custom + + # Create pgsodium getkey script with writable key location + # The original script writes to /etc/postgresql-custom which is read-only in nix + # Use PGDATA directory which is owned by postgres + cat > $out/usr/lib/postgresql/bin/pgsodium_getkey.sh <<'GETKEY' + #!/bin/bash + set -euo pipefail + + # Use PGDATA for the key file - this directory is created and owned by postgres + KEY_FILE="''${PGDATA:-/var/lib/postgresql/data}/pgsodium_root.key" + + if [[ ! -f "''${KEY_FILE}" ]]; then + head -c 32 /dev/urandom | od -A n -t x1 | tr -d ' \n' > "''${KEY_FILE}" + chmod 600 "''${KEY_FILE}" + fi + cat $KEY_FILE + GETKEY + chmod +x $out/usr/lib/postgresql/bin/pgsodium_getkey.sh + + # Copy wal-g helper scripts + cp ${sourceRoot}/ansible/files/walg_helper_scripts/wal_fetch.sh $out/home/postgres/wal_fetch.sh + cp ${sourceRoot}/ansible/files/walg_helper_scripts/wal_change_ownership.sh $out/root/wal_change_ownership.sh + + # Copy migrations + cp -r ${sourceRoot}/migrations/db/* $out/docker-entrypoint-initdb.d/ + cp ${sourceRoot}/ansible/files/pgbouncer_config/pgbouncer_auth_schema.sql $out/docker-entrypoint-initdb.d/init-scripts/00-schema.sql + cp ${sourceRoot}/ansible/files/stat_extension.sql $out/docker-entrypoint-initdb.d/migrations/00-extension.sql + + # Make init scripts writable for OrioleDB additions + chmod -R u+w $out/docker-entrypoint-initdb.d + + # Apply sed transformations (same as Dockerfile) + sed -i \ + -e "s|#unix_socket_directories = '/tmp'|unix_socket_directories = '/var/run/postgresql'|g" \ + -e "s|#session_preload_libraries = '''|session_preload_libraries = 'supautils'|g" \ + -e "s|#include = '/etc/postgresql-custom/supautils.conf'|include = '/etc/postgresql-custom/supautils.conf'|g" \ + -e "s|#include = '/etc/postgresql-custom/wal-g.conf'|include = '/etc/postgresql-custom/wal-g.conf'|g" \ + $out/etc/postgresql/postgresql.conf + + echo "pgsodium.getkey_script= '/usr/lib/postgresql/bin/pgsodium_getkey.sh'" >> $out/etc/postgresql/postgresql.conf + echo "vault.getkey_script= '/usr/lib/postgresql/bin/pgsodium_getkey.sh'" >> $out/etc/postgresql/postgresql.conf + + ${lib.optionalString isPg17 '' + # PG17 specific: remove timescaledb from shared_preload_libraries + sed -i 's/ timescaledb,//g' $out/etc/postgresql/postgresql.conf + # PG 16.4+ deprecation + sed -i 's/db_user_namespace = off/#db_user_namespace = off/g' $out/etc/postgresql/postgresql.conf + sed -i 's/ timescaledb,//g; s/ plv8,//g' $out/etc/postgresql-custom/supautils.conf + ''} + + ${lib.optionalString isOrioledb '' + # OrioleDB specific transforms + sed -i 's/ timescaledb,//g; s/ plv8,//g; s/ postgis,//g; s/ pgrouting,//g' $out/etc/postgresql-custom/supautils.conf + sed -i "s/\(shared_preload_libraries.*\)'\(.*\)$/\1, orioledb'\2/" $out/etc/postgresql/postgresql.conf + echo "default_table_access_method = 'orioledb'" >> $out/etc/postgresql/postgresql.conf + + # OrioleDB rewind configuration + echo "orioledb.enable_rewind = true" >> $out/etc/postgresql/postgresql.conf + echo "orioledb.rewind_max_time = 1200" >> $out/etc/postgresql/postgresql.conf + echo "orioledb.rewind_max_transactions = 100000" >> $out/etc/postgresql/postgresql.conf + echo "orioledb.rewind_buffers = 1280" >> $out/etc/postgresql/postgresql.conf + + # Enable orioledb extension first - use exact same format as initial-schema but with name that sorts earlier + # Must match the 14-zero format because en_US.UTF-8 locale collation ignores punctuation initially + echo "CREATE EXTENSION orioledb;" > $out/docker-entrypoint-initdb.d/init-scripts/00000000000000-a-orioledb.sql + ''} +'' diff --git a/nix/docker/default.nix b/nix/docker/default.nix new file mode 100644 index 000000000..d2d4ad600 --- /dev/null +++ b/nix/docker/default.nix @@ -0,0 +1,297 @@ +{ self, inputs, ... }: +{ + perSystem = + { + inputs', + lib, + pkgs, + self', + system, + ... + }: + let + nix2container = inputs'.nix2container.packages.nix2container; + + # Build skopeo with nix: transport support (works on darwin and linux) + # This is the same approach nix2container uses internally + nix2container-bin = pkgs.buildGoModule { + pname = "nix2container"; + version = "1.0.0"; + src = inputs.nix2container; + sourceRoot = "source"; + vendorHash = "sha256-/j4ZHOwU5Xi8CE/fHha+2iZhsLd/y2ovzVhvg8HDV78="; + ldflags = lib.optional pkgs.stdenv.isDarwin "-X github.com/nlewo/nix2container/nix.useNixCaseHack=true"; + }; + + skopeo-nix = pkgs.skopeo.overrideAttrs (old: { + EXTRA_LDFLAGS = lib.optionalString pkgs.stdenv.isDarwin "-X github.com/nlewo/nix2container/nix.useNixCaseHack=true"; + nativeBuildInputs = old.nativeBuildInputs ++ [ pkgs.patchutils ]; + preBuild = + let + fetchgitpatch = + args: + pkgs.fetchpatch2 ( + args + // { + postFetch = + (args.postFetch or "") + + '' + sed -i \ + -e '/^index /d' \ + -e '/^similarity index /d' \ + -e '/^dissimilarity index /d' \ + $out + ''; + } + ); + patch = fetchgitpatch { + url = "https://github.com/nlewo/image/commit/c2254c998433cf02af60bf0292042bd80b96a77e.patch"; + sha256 = "sha256-6CUjz46xD3ORgwrHwdIlSu6JUj7WLS6BOSyRGNnALHY="; + }; + in + '' + mkdir -p vendor/github.com/nlewo/nix2container/ + cp -r ${nix2container-bin.src}/* vendor/github.com/nlewo/nix2container/ + cd vendor/github.com/containers/image/v5 + mkdir -p nix/ + touch nix/transport.go + filterdiff -x '*/alltransports.go' ${patch} | patch -p1 + sed -i '\#_ "github.com/containers/image/v5/tarball"#a _ "github.com/containers/image/v5/nix"' transports/alltransports/alltransports.go + cd - + + echo '# github.com/nlewo/nix2container v1.0.0' >> vendor/modules.txt + echo '## explicit; go 1.13' >> vendor/modules.txt + echo github.com/nlewo/nix2container/nix >> vendor/modules.txt + echo github.com/nlewo/nix2container/types >> vendor/modules.txt + echo github.com/containers/image/v5/nix >> vendor/modules.txt + echo 'require (' >> go.mod + echo ' github.com/nlewo/nix2container v1.0.0' >> go.mod + echo ')' >> go.mod + ''; + }); + + # Import helper modules (only for Linux where images are built) + ubuntuBase = import ./base.nix { inherit nix2container system; }; + gosu = import ./gosu.nix { inherit pkgs system; }; + entrypoint = import ./entrypoint.nix { inherit pkgs; }; + usrbinCompat = import ./usrbin-compat.nix { inherit pkgs; }; + + makePostgresConfigs = + variant: + import ./configs.nix { + inherit lib pkgs variant; + sourceRoot = self; + }; + + # Version tags from ansible/vars.yml + versionTags = { + "15" = "15.14.1.060"; + "17" = "17.6.1.060"; + "orioledb-17" = "17.6.0.017-orioledb"; + }; + + makePostgresImage = + { variant }: + let + pgPackage = self'.packages."psql_${variant}/bin"; + configs = makePostgresConfigs variant; + tag = versionTags.${variant}; + isOrioledb = variant == "orioledb-17"; + isPg17 = variant == "17" || isOrioledb; + in + nix2container.buildImage { + name = "supabase/postgres"; + inherit tag; + + fromImage = ubuntuBase; + + copyToRoot = pkgs.buildEnv { + name = "postgres-root-${variant}"; + paths = [ + pgPackage + self'.packages.supabase-groonga + gosu + entrypoint + configs + usrbinCompat + # System utilities needed at runtime + pkgs.coreutils + pkgs.bash + pkgs.gnused + pkgs.gawk + pkgs.findutils + pkgs.gnugrep + # Locale support - provides en_US.UTF-8 + pkgs.glibcLocales + ]; + pathsToLink = [ + "/bin" + "/lib" + "/share" + "/etc" + "/usr" + "/docker-entrypoint-initdb.d" + "/home" + "/root" + ]; + }; + + # Separate layer for PostgreSQL to optimize caching + layers = [ (nix2container.buildLayer { deps = [ pgPackage ]; }) ]; + + config = { + Entrypoint = [ "/usr/local/bin/docker-entrypoint.sh" ]; + Cmd = [ + "postgres" + "-D" + "/etc/postgresql" + ]; + ExposedPorts = { + "5432/tcp" = { }; + }; + Env = + [ + "PGDATA=/var/lib/postgresql/data" + "POSTGRES_HOST=/var/run/postgresql" + "POSTGRES_USER=supabase_admin" + "POSTGRES_DB=postgres" + "LANG=en_US.UTF-8" + "LANGUAGE=en_US:en" + "LC_ALL=en_US.UTF-8" + # Point to nix glibc locales + "LOCALE_ARCHIVE=${pkgs.glibcLocales}/lib/locale/locale-archive" + "GRN_PLUGINS_DIR=/usr/lib/groonga/plugins" + # Add PATH so postgres binaries are found + "PATH=/bin:/usr/bin:/usr/local/bin" + ] + ++ lib.optionals isPg17 [ + "POSTGRES_INITDB_ARGS=--allow-group-access --locale-provider=icu --encoding=UTF-8 --icu-locale=en_US.UTF-8" + ]; + # Start as root - entrypoint uses gosu to switch to postgres + # This allows creating directories like /var/lib/postgresql at runtime + WorkingDir = "/"; + StopSignal = "SIGINT"; + }; + }; + # Test script that loads and tests a Docker image (runs outside sandbox) + # For darwin, we need to build the image for aarch64-linux but run the test script on darwin + # Uses skopeo with nix: transport to load the image - works cross-platform + makeDockerTestScript = + { variant }: + let + tag = versionTags.${variant}; + containerName = "nix-test-postgres-${variant}"; + port = + if variant == "15" then + "15432" + else if variant == "17" then + "15433" + else + "15434"; + isOrioledb = variant == "orioledb-17"; + # Determine target architecture based on current system + targetSystem = if system == "aarch64-darwin" then "aarch64-linux" else "x86_64-linux"; + in + pkgs.writeShellApplication { + name = "test-docker-postgres-${variant}"; + runtimeInputs = [ + pkgs.docker + skopeo-nix + pkgs.jq + ]; + text = '' + set -euo pipefail + + echo "=== Testing Docker image: supabase/postgres:${tag} ===" + + # Clean up any existing container from previous runs + docker rm -f ${containerName} 2>/dev/null || true + + # Build the image JSON (this is cross-platform - just builds the manifest) + echo "Building image manifest for ${targetSystem}..." + IMAGE_JSON=$(nix build .#packages.${targetSystem}.\"docker/postgres-${variant}\" --print-out-paths --no-link) + echo "Image manifest: $IMAGE_JSON" + + # Load the image into Docker using skopeo with nix: transport + echo "Loading image into Docker daemon..." + skopeo --insecure-policy copy "nix:$IMAGE_JSON" "docker-daemon:supabase/postgres:${tag}" + + echo "Starting container ${containerName}..." + docker run -d \ + --name ${containerName} \ + -p ${port}:5432 \ + -e POSTGRES_PASSWORD=testpassword \ + supabase/postgres:${tag} + + cleanup() { + echo "Cleaning up container..." + docker stop ${containerName} || true + docker rm ${containerName} || true + } + trap cleanup EXIT + + echo "Waiting for PostgreSQL to be ready..." + for i in $(seq 1 60); do + if docker exec ${containerName} pg_isready -U supabase_admin -h localhost -q 2>/dev/null; then + echo "PostgreSQL is ready!" + break + fi + if [ "$i" -eq 60 ]; then + echo "PostgreSQL failed to start within 60 seconds" + docker logs ${containerName} + exit 1 + fi + sleep 1 + done + + echo "Testing basic connectivity..." + docker exec ${containerName} psql -h localhost -U supabase_admin -d postgres -c "SELECT version();" + + echo "Testing extension loading..." + docker exec ${containerName} psql -h localhost -U supabase_admin -d postgres -c "CREATE EXTENSION IF NOT EXISTS vector;" + docker exec ${containerName} psql -h localhost -U supabase_admin -d postgres -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;" + + ${ + if isOrioledb then + '' + echo "Testing OrioleDB extension..." + docker exec ${containerName} psql -h localhost -U supabase_admin -d postgres -c "CREATE EXTENSION IF NOT EXISTS orioledb;" + '' + else + '' + echo "Testing PostGIS extension..." + docker exec ${containerName} psql -h localhost -U supabase_admin -d postgres -c "CREATE EXTENSION IF NOT EXISTS postgis;" + '' + } + + echo "=== All tests passed for postgres-${variant}! ===" + ''; + }; + in + { + packages = lib.optionalAttrs (system == "aarch64-linux" || system == "x86_64-linux") { + "docker/postgres-15" = makePostgresImage { variant = "15"; }; + "docker/postgres-17" = makePostgresImage { variant = "17"; }; + "docker/postgres-orioledb-17" = makePostgresImage { variant = "orioledb-17"; }; + }; + + # Apps for running Docker image tests (outside of nix flake check) + # Available on all systems - builds Linux image and loads into Docker + apps = { + "test-docker-postgres-15" = { + type = "app"; + program = "${makeDockerTestScript { variant = "15"; }}/bin/test-docker-postgres-15"; + }; + "test-docker-postgres-17" = { + type = "app"; + program = "${makeDockerTestScript { variant = "17"; }}/bin/test-docker-postgres-17"; + }; + "test-docker-postgres-orioledb-17" = { + type = "app"; + program = "${ + makeDockerTestScript { variant = "orioledb-17"; } + }/bin/test-docker-postgres-orioledb-17"; + }; + }; + }; +} diff --git a/nix/docker/entrypoint.nix b/nix/docker/entrypoint.nix new file mode 100644 index 000000000..a9a3c8622 --- /dev/null +++ b/nix/docker/entrypoint.nix @@ -0,0 +1,18 @@ +{ pkgs }: +pkgs.stdenv.mkDerivation { + pname = "docker-entrypoint"; + version = "17-bullseye"; + + src = pkgs.fetchurl { + url = "https://github.com/docker-library/postgres/raw/889f9447cd2dfe21cccfbe9bb7945e3b037e02d8/17/bullseye/docker-entrypoint.sh"; + sha256 = "19b51vlqbhj1njk8knf9i53bqhaggz2fdhnjn1ln5102zyq15s8y"; + }; + + dontUnpack = true; + + installPhase = '' + mkdir -p $out/usr/local/bin + cp $src $out/usr/local/bin/docker-entrypoint.sh + chmod +x $out/usr/local/bin/docker-entrypoint.sh + ''; +} diff --git a/nix/docker/gosu.nix b/nix/docker/gosu.nix new file mode 100644 index 000000000..539a23423 --- /dev/null +++ b/nix/docker/gosu.nix @@ -0,0 +1,26 @@ +{ pkgs, system }: +let + version = "1.16"; + arch = if system == "x86_64-linux" then "amd64" else "arm64"; + hashes = { + amd64 = "04ygchf66azbl54cz11ff18a5x3bwbyhpgnbn3bpv7hg8g3iykis"; + arm64 = "19mns8ihyv7i5xrpx7sm3zz4gfkzyn1zhfyyazid4ijjgn84kyi3"; + }; +in +pkgs.stdenv.mkDerivation { + pname = "gosu"; + inherit version; + + src = pkgs.fetchurl { + url = "https://github.com/tianon/gosu/releases/download/${version}/gosu-${arch}"; + sha256 = hashes.${arch}; + }; + + dontUnpack = true; + + installPhase = '' + mkdir -p $out/usr/local/bin + cp $src $out/usr/local/bin/gosu + chmod +x $out/usr/local/bin/gosu + ''; +} diff --git a/nix/docker/tests.nix b/nix/docker/tests.nix new file mode 100644 index 000000000..42474646d --- /dev/null +++ b/nix/docker/tests.nix @@ -0,0 +1,56 @@ +# Docker image build verification tests (sandboxed, no Docker daemon required) +# For runtime tests, use: nix run .#test-docker-postgres-{15,17,orioledb-17} +{ + pkgs, + lib, + self', + system, +}: +let + # Build verification - just ensures the image builds successfully + makeBuildCheck = + { variant }: + let + image = self'.packages."docker/postgres-${variant}"; + in + pkgs.runCommand "docker-build-check-postgres-${variant}" { } '' + echo "Verifying Docker image build for postgres-${variant}..." + + # Check that the image JSON exists and is valid + if [ ! -f "${image}" ]; then + echo "ERROR: Image manifest not found at ${image}" + exit 1 + fi + + # Verify it's valid JSON with expected fields + ${pkgs.jq}/bin/jq -e '.version' "${image}" > /dev/null || { + echo "ERROR: Invalid image manifest - missing version field" + exit 1 + } + + ${pkgs.jq}/bin/jq -e '."image-config".Entrypoint' "${image}" > /dev/null || { + echo "ERROR: Invalid image manifest - missing Entrypoint" + exit 1 + } + + ${pkgs.jq}/bin/jq -e '."image-config".Env' "${image}" > /dev/null || { + echo "ERROR: Invalid image manifest - missing Env" + exit 1 + } + + echo "Image manifest validation passed!" + echo "Image: ${image}" + echo "" + echo "Image config:" + ${pkgs.jq}/bin/jq '."image-config"' "${image}" + + mkdir -p $out + echo "Build check passed for postgres-${variant}" > $out/result.txt + cp "${image}" $out/image-manifest.json + ''; +in +lib.optionalAttrs (system == "aarch64-linux" || system == "x86_64-linux") { + "docker-build-postgres-15" = makeBuildCheck { variant = "15"; }; + "docker-build-postgres-17" = makeBuildCheck { variant = "17"; }; + "docker-build-postgres-orioledb-17" = makeBuildCheck { variant = "orioledb-17"; }; +} diff --git a/nix/docker/usrbin-compat.nix b/nix/docker/usrbin-compat.nix new file mode 100644 index 000000000..50eb53e83 --- /dev/null +++ b/nix/docker/usrbin-compat.nix @@ -0,0 +1,54 @@ +# Create /usr/bin symlinks for compatibility with standard entrypoint scripts +# The entrypoint script expects tools like `id`, `find`, etc. at /usr/bin/ +{ pkgs }: +pkgs.runCommand "usrbin-compat" { } '' + mkdir -p $out/usr/bin + + # Core utilities needed by docker-entrypoint.sh + ln -s /bin/id $out/usr/bin/id + ln -s /bin/find $out/usr/bin/find + ln -s /bin/install $out/usr/bin/install + ln -s /bin/chown $out/usr/bin/chown + ln -s /bin/chmod $out/usr/bin/chmod + ln -s /bin/mkdir $out/usr/bin/mkdir + ln -s /bin/rm $out/usr/bin/rm + ln -s /bin/cat $out/usr/bin/cat + ln -s /bin/ls $out/usr/bin/ls + ln -s /bin/cp $out/usr/bin/cp + ln -s /bin/mv $out/usr/bin/mv + ln -s /bin/env $out/usr/bin/env + ln -s /bin/basename $out/usr/bin/basename + ln -s /bin/dirname $out/usr/bin/dirname + ln -s /bin/head $out/usr/bin/head + ln -s /bin/tail $out/usr/bin/tail + ln -s /bin/sort $out/usr/bin/sort + ln -s /bin/wc $out/usr/bin/wc + ln -s /bin/tr $out/usr/bin/tr + ln -s /bin/cut $out/usr/bin/cut + ln -s /bin/tee $out/usr/bin/tee + ln -s /bin/touch $out/usr/bin/touch + ln -s /bin/stat $out/usr/bin/stat + ln -s /bin/readlink $out/usr/bin/readlink + ln -s /bin/realpath $out/usr/bin/realpath + ln -s /bin/sleep $out/usr/bin/sleep + ln -s /bin/date $out/usr/bin/date + ln -s /bin/uname $out/usr/bin/uname + ln -s /bin/whoami $out/usr/bin/whoami + ln -s /bin/groups $out/usr/bin/groups + ln -s /bin/xargs $out/usr/bin/xargs + ln -s /bin/expr $out/usr/bin/expr + ln -s /bin/test $out/usr/bin/test + ln -s /bin/[ $out/usr/bin/[ + ln -s /bin/true $out/usr/bin/true + ln -s /bin/false $out/usr/bin/false + ln -s /bin/seq $out/usr/bin/seq + ln -s /bin/printf $out/usr/bin/printf + ln -s /bin/echo $out/usr/bin/echo + + # sed, awk, grep + ln -s /bin/sed $out/usr/bin/sed + ln -s /bin/awk $out/usr/bin/awk + ln -s /bin/grep $out/usr/bin/grep + ln -s /bin/egrep $out/usr/bin/egrep + ln -s /bin/fgrep $out/usr/bin/fgrep +''