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
156 changes: 155 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,167 @@ jobs:
- name: Test on PostgreSQL ${{ matrix.pg }}
run: pg-build-test

# Proves extension_drop can be deployed with NO filesystem footprint at
# all, via pg_tle (AWS's Trusted Language Extensions -- a database-backed
# catalog for installing an extension with no .control file on disk;
# relevant for RDS/Aurora-style managed deployments). This is a distinct
# dimension from the `test` job above: not fresh vs. updated vs. upgraded,
# but filesystem-installed vs. registered purely through pg_tle's catalog.
#
# extension_drop REQUIRES cat_tools (see the Makefile's own `cat_tools`
# target), so this job has a complication a leaf extension's pg_tle test
# doesn't: cat_tools must ALSO be registered as a pg_tle extension, before
# extension_drop, so `CREATE EXTENSION extension_drop CASCADE` resolves
# its dependency through pg_tle too instead of failing (or, worse, silently
# resolving from a stray filesystem install of cat_tools). Every step that
# could plausibly write an extension file to disk is bracketed by
# bin/assert_fs_clean checks -- see its header for why that has to be an
# active, repeated assertion, not a one-time formality.
#
# A dedicated cluster, never shared with the `test` job above: pg_tle
# requires shared_preload_libraries, and mixing pg_tle/non-pg_tle
# extension installs on one cluster can misbehave. Also deliberately its
# own job rather than folded into any existing matrix/loop -- the entire
# point of this job is proving isolation from the filesystem, and any
# sibling loop iteration doing a real `make install` would contaminate
# that proof.
#
# Explicitly OUT OF SCOPE for this job (left as follow-up work):
# - The update path via pg_tle (ALTER EXTENSION UPDATE against a pg_tle-
# registered install). extension_drop has no prior released version to
# update FROM yet (see TEST_UPDATE_FROM's comment in the Makefile), so
# there's nothing real to exercise.
# - Binary pg_upgrade of a pg_tle-deployed extension. Heavier, separate
# concern from a fresh-install smoke test.
pg-tle-test:
# Gated behind the cheap `test` job: this job compiles pg_tle from
# source and does a chained pg_tle registration, expensive enough not to
# run against a baseline that's already broken by a failing fresh-install
# test. success() is required explicitly once a job's `if:` references
# anything -- GitHub only assumes success() as a default when no `if:` is
# written at all.
needs: [test]
if: success()
strategy:
matrix:
# Intersection of two independently-moving ranges, checked directly
# rather than assumed: extension_drop's own tested range (9.3-17,
# see the `test` job's matrix above) and pg_tle 1.5.2's supported
# PostgreSQL range (12-18, dropped PG11 -- see
# pgxntool/pgtle_versions.md). 18 isn't in the `test` job's own
# matrix yet, so it's left out here too rather than testing a PG
# major extension_drop's own baseline job doesn't cover.
pg: [17, 16, 15, 14, 13, 12]
name: 🧩 pg_tle ${{ matrix.pg }}
runs-on: ubuntu-latest
container: pgxn/pgxn-tools
env:
PG_TLE_RELEASE: "1.5.2"
steps:
- name: Start PostgreSQL ${{ matrix.pg }}
run: pg-start ${{ matrix.pg }}
- name: Check out the repo
uses: actions/checkout@v5
- name: Install pgtap (test harness dependency)
# pgTAP is a filesystem-installed dependency of the TEST HARNESS
# itself, not part of what this job proves is pg_tle-only -- it's
# never deployed via pg_tle. Installed explicitly here, before the
# baseline snapshot below, so it's part of the accepted starting
# state (like contrib) instead of tripping the contamination check
# if something installed it lazily later.
run: make pgtap
- name: Snapshot filesystem extension control files (pre-pg_tle baseline)
run: bin/assert_fs_clean snapshot ${{ matrix.pg }} /tmp/control_baseline.txt
- name: Build and install pg_tle ${{ env.PG_TLE_RELEASE }}
# flex/bison/libkrb5-dev aren't in the pgxn-tools image; pg_tle's
# build needs them (guc-file.l, and clientauth.c includes gssapi.h).
run: |
apt-get install -y flex bison libkrb5-dev
git clone --branch v${{ env.PG_TLE_RELEASE }} --depth 1 https://github.com/aws/pg_tle.git /tmp/pg_tle
make -C /tmp/pg_tle install
- name: Enable pg_tle and restart PostgreSQL ${{ matrix.pg }}
run: |
echo "shared_preload_libraries = 'pg_tle'" >> /etc/postgresql/${{ matrix.pg }}/test/postgresql.conf
pg_ctlcluster ${{ matrix.pg }} test restart
pg_isready -t 30
- name: Register pg_tle, then cat_tools, then extension_drop -- all against template1
# template1, not the ambient default db: pg_tle's registration
# catalog is per-database, and createdb only inherits it because it
# copies template1 by default. Every database used below is created
# AFTER this step so it inherits all three registrations.
#
# cat_tools must be registered BEFORE extension_drop: extension_drop
# requires cat_tools, so the CASCADE install below needs cat_tools
# already resolvable through pg_tle's own catalog by the time it
# runs. cat_tools is cloned fresh at the SAME git ref the Makefile's
# own filesystem `cat_tools` target pins to (CAT_TOOLS_GIT_REF),
# read via `make print-CAT_TOOLS_GIT_REF` so the two never drift
# apart. cat_tools vendors its own pgxntool copy directly (confirmed:
# its only git submodule is an unrelated linter, so a plain `git
# clone` alone gives a working `make run-pgtle`), so registering it
# needs nothing beyond the exact same target this repo uses on
# itself right after.
run: |
psql -d template1 -c "CREATE EXTENSION pg_tle"
CAT_TOOLS_GIT_REF=$(make -s print-CAT_TOOLS_GIT_REF 2>/dev/null | sed -n 's/.*set to "\(.*\)"$/\1/p')
git clone https://github.com/Postgres-Extensions/cat_tools.git /tmp/cat_tools_tle
# -C on both git and make, rather than `cd`: this is one continuous
# shell script (a multi-line `run:` block), so a bare `cd` here
# would still be in effect for the `make run-pgtle` below that's
# meant to run against OUR OWN checkout, not the cat_tools clone.
git -C /tmp/cat_tools_tle checkout "$CAT_TOOLS_GIT_REF"
PGDATABASE=template1 make -C /tmp/cat_tools_tle run-pgtle
PGDATABASE=template1 make run-pgtle
- name: Verify no stray extension control files landed on the filesystem
# CRITICAL: a filesystem control file silently wins over a pg_tle-
# registered extension of the same name, which would make this whole
# job a false pass without ever raising an error. Run again after
# every step below that could plausibly write extension files to
# disk -- never trust a single check to catch everything.
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt
- name: Install extension_drop purely via pg_tle (fresh install, no filesystem trace)
# Neither cat_tools nor extension_drop is ever `make install`ed in
# this job, so a successful CASCADE install here can only be
# resolving both through pg_tle's registration, not a control file
# on disk. Checked explicitly here too (not just via the
# comprehensive check above) as a guard specifically for the two
# extensions under test, in case that check's exclude logic has a
# bug.
run: |
test ! -e /usr/share/postgresql/${{ matrix.pg }}/extension/cat_tools.control
test ! -e /usr/share/postgresql/${{ matrix.pg }}/extension/extension_drop.control
createdb extension_drop_smoke
psql -d extension_drop_smoke -c "CREATE EXTENSION extension_drop CASCADE"
- name: Verify extension_drop works when deployed via pg_tle
run: |
INSTALLED=$(psql -d extension_drop_smoke -tAc "SELECT extversion FROM pg_extension WHERE extname = 'extension_drop'")
EXPECTED=$(make -s print-PGXNVERSION 2>/dev/null | sed -n 's/.*set to "\(.*\)"$/\1/p')
echo "installed=$INSTALLED expected=$EXPECTED"
if [ -z "$INSTALLED" ] || [ -z "$EXPECTED" ] || [ "$INSTALLED" != "$EXPECTED" ]; then
echo "FAIL: installed='$INSTALLED' expected='$EXPECTED'"; exit 1
fi
# A real function call, not just a successful install: exercises
# add/get/remove against an extension (pg_tle itself) that's
# genuinely present in this database, proving the pg_tle-deployed
# functions actually execute correctly, not merely that CREATE
# EXTENSION didn't error.
psql -d extension_drop_smoke -v ON_ERROR_STOP=1 -c "SELECT extension_drop__add('pg_tle', 'SELECT 1')"
psql -d extension_drop_smoke -v ON_ERROR_STOP=1 -c "SELECT * FROM extension_drop__get('pg_tle')" > /dev/null
psql -d extension_drop_smoke -v ON_ERROR_STOP=1 -c "SELECT extension_drop__remove('pg_tle')"
- name: Verify no stray extension control files after the fresh-install smoke test
# Runs AFTER the complete flow, not before: the whole point of
# pg_tle-mode testing is proving NOTHING touched the filesystem
# THROUGHOUT the flow, not merely that the environment started
# clean.
run: bin/assert_fs_clean verify ${{ matrix.pg }} /tmp/control_baseline.txt

# A single stable check name for use as a required status check in branch
# protection. Matrix jobs produce names like "🐘 PostgreSQL 14" that change
# with the matrix; this aggregates them into one. It passes if every needed
# job succeeded or was skipped (e.g. a docs-only push with paths-ignore) and
# fails if any failed or were cancelled.
all-checks-passed:
needs: [test]
needs: [test, pg-tle-test]
if: always()
runs-on: ubuntu-latest
steps:
Expand Down
85 changes: 85 additions & 0 deletions bin/assert_fs_clean
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
#
# assert_fs_clean - Verify no stray PostgreSQL extension control files exist
# on disk, to prove an extension (and its dependencies) were deployed purely
# via pg_tle (AWS's Trusted Language Extensions), not a filesystem install.
#
# A pre-existing filesystem *.control file silently wins over a pg_tle-
# registered extension of the same name -- PostgreSQL never reports an
# error, it just quietly resolves CREATE EXTENSION from disk instead of
# pg_tle's catalog. That makes "prove pg_tle-only" a real, load-bearing
# assertion, not a formality: it must run AFTER whatever step it's guarding,
# not just before, since the whole point is confirming nothing wrote to disk
# THROUGHOUT the guarded flow, not merely that the environment started
# clean.
#
# Deliberately generic over extension name: it diffs the FULL set of
# *.control files against a baseline, so it catches a stray install of ANY
# extension (extension_drop, cat_tools, or something unrelated), not just a
# hardcoded name list -- a chained dependency doesn't need its own entry
# here.
#
# Modeled on cat_tools's bin/assert_fs_clean.
#
# USAGE: bin/assert_fs_clean <subcommand> [args]
#
# snapshot PG_MAJOR BASELINE_FILE
# Record the current *.control files in PG_MAJOR's extension directory
# to BASELINE_FILE. Run this BEFORE installing pg_tle (or anything
# else that could write to disk), so whatever ships by default (e.g.
# contrib, pgTAP installed as the test harness's own dependency) is
# excluded automatically -- no hardcoded exclude list to keep in sync.
#
# verify PG_MAJOR BASELINE_FILE
# Fail if any *.control file exists now that wasn't in BASELINE_FILE,
# other than pg_tle.control itself (the one legitimate filesystem
# install in this flow). Run this after EVERY step that could
# plausibly have written extension files to disk.
set -euo pipefail

extdir_of() { echo "/usr/share/postgresql/$1/extension"; }

snapshot() {
local pg_major=$1 baseline=$2
find "$(extdir_of "$pg_major")" -maxdepth 1 -name '*.control' | sort > "$baseline"
}

verify() {
local pg_major=$1 baseline=$2
local after
after=$(mktemp)
# Bake the actual path into the trap string now (double-quoted expansion),
# rather than deferring expansion to whenever the trap fires -- a RETURN
# trap wouldn't fire at all under `set -e` (this script's own errexit), and
# a single-quoted EXIT trap referencing $after by name would break once
# this function returns and $after (declared local) goes out of scope.
trap "rm -f '$after'" EXIT
local new
find "$(extdir_of "$pg_major")" -maxdepth 1 -name '*.control' | sort > "$after"
new=$(comm -13 "$baseline" "$after" | grep -vx '.*/pg_tle\.control' || true)
if [ -n "$new" ]; then
echo "FAIL: unexpected extension control file(s) on disk (everything but pg_tle must be registered via pg_tle, not filesystem-installed):" >&2
echo "$new" >&2
exit 1
fi
echo "OK: no stray extension control files on disk (PG $pg_major)"
}

usage() {
echo "usage: bin/assert_fs_clean <subcommand> [args]" >&2
echo " snapshot PG_MAJOR BASELINE_FILE" >&2
echo " verify PG_MAJOR BASELINE_FILE" >&2
exit 2
}

main() {
local cmd=${1:-}
shift || true
case "$cmd" in
snapshot) snapshot "$@" ;;
verify) verify "$@" ;;
*) usage ;;
esac
}

main "$@"