Skip to content

Commit c3ab436

Browse files
committed
feat: multiple versions for the pgaudit extension
Build multiple versions of the pgaudit extension on different PostgreSQL versions. Add test for the extensions and their upgrade on PostgreSQL 15 and 17.
1 parent 24430ee commit c3ab436

File tree

4 files changed

+308
-41
lines changed

4 files changed

+308
-41
lines changed

nix/checks.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@
309309
;
310310
}
311311
// pkgs.lib.optionalAttrs (system == "x86_64-linux") {
312+
pgaudit = import ./ext/tests/pgaudit.nix { inherit self; inherit pkgs; };
312313
wrappers = import ./ext/tests/wrappers.nix {
313314
inherit self;
314315
inherit pkgs;

nix/ext/pgaudit.nix

Lines changed: 112 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,132 @@
11
{
22
lib,
33
stdenv,
4+
buildEnv,
45
fetchFromGitHub,
56
libkrb5,
67
openssl,
78
postgresql,
89
}:
910
#adapted from https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/sql/postgresql/ext/pgaudit.nix
1011
let
11-
source =
12-
{
13-
"17" = {
14-
version = "17.0";
15-
hash = "sha256-3ksq09wiudQPuBQI3dhEQi8IkXKLVIsPFgBnwLiicro=";
16-
};
17-
"16" = {
18-
version = "16.0";
19-
hash = "sha256-8+tGOl1U5y9Zgu+9O5UDDE4bec4B0JC/BQ6GLhHzQzc=";
20-
};
21-
"15" = {
22-
version = "1.7.0";
23-
hash = "sha256-8pShPr4HJaJQPjW1iPJIpj3CutTx8Tgr+rOqoXtgCcw=";
24-
};
25-
}
26-
.${lib.versions.major postgresql.version}
27-
or (throw "Source for pgaudit is not available for ${postgresql.version}");
28-
in
29-
stdenv.mkDerivation {
3012
pname = "pgaudit";
31-
inherit (source) version;
13+
# Load version configuration from external file
14+
allVersions = (builtins.fromJSON (builtins.readFile ./versions.json)).${pname};
3215

33-
src = fetchFromGitHub {
34-
owner = "pgaudit";
35-
repo = "pgaudit";
36-
rev = source.version;
37-
hash = source.hash;
38-
};
16+
# Filter versions compatible with current PostgreSQL version
17+
supportedVersions = lib.filterAttrs (
18+
_: value: builtins.elem (lib.versions.major postgresql.version) value.postgresql
19+
) allVersions;
20+
21+
# Derived version information
22+
versions = lib.naturalSort (lib.attrNames supportedVersions);
23+
latestVersion = lib.last versions;
24+
numberOfVersions = builtins.length versions;
25+
packages = builtins.attrValues (
26+
lib.mapAttrs (name: value: build name value.hash) supportedVersions
27+
);
28+
29+
# Build function for individual pgaudit versions
30+
build =
31+
version: hash:
32+
stdenv.mkDerivation {
33+
inherit pname version;
34+
35+
src = fetchFromGitHub {
36+
owner = "pgaudit";
37+
repo = "pgaudit";
38+
rev = version;
39+
inherit hash;
40+
};
41+
42+
buildInputs = [
43+
libkrb5
44+
openssl
45+
postgresql
46+
];
47+
48+
makeFlags = [ "USE_PGXS=1" ];
3949

40-
buildInputs = [
41-
libkrb5
42-
openssl
43-
postgresql
50+
postBuild =
51+
lib.optionalString (version == "1.7.0") ''
52+
mv ${pname}--1.7.sql ${pname}--1.7.0.sql
53+
cp ${pname}--1.7.0.sql ${pname}--1.6.1--1.7.0.sql
54+
''
55+
+ lib.optionalString (version == "1.7.1") ''
56+
mv ${pname}--1.7--1.7.1.sql ${pname}--1.7.0--1.7.1.sql
57+
'';
58+
59+
installPhase = ''
60+
runHook preInstall
61+
62+
mkdir -p $out/{lib,share/postgresql/extension}
63+
64+
# Install shared library with version suffix
65+
mv ${pname}${postgresql.dlSuffix} $out/lib/${pname}-${version}${postgresql.dlSuffix}
66+
67+
# Install SQL files
68+
sed -i '1s/^/DROP EVENT TRIGGER IF EXISTS pgaudit_ddl_command_end; \n/' *.sql
69+
sed -i '1s/^/DROP EVENT TRIGGER IF EXISTS pgaudit_sql_drop; \n/' *.sql
70+
sed -i 's/CREATE FUNCTION/CREATE OR REPLACE FUNCTION/' *.sql
71+
cp *.sql $out/share/postgresql/extension
72+
73+
# Create version-specific control file
74+
sed -e "/^default_version =/d" \
75+
-e "s|^module_pathname = .*|module_pathname = '\$libdir/${pname}'|" \
76+
${pname}.control > $out/share/postgresql/extension/${pname}--${version}.control
77+
78+
# For the latest version, create default control file and symlink
79+
if [[ "${version}" == "${latestVersion}" ]]; then
80+
{
81+
echo "default_version = '${latestVersion}'"
82+
cat $out/share/postgresql/extension/${pname}--${latestVersion}.control
83+
} > $out/share/postgresql/extension/${pname}.control
84+
ln -sfn ${pname}-${latestVersion}${postgresql.dlSuffix} $out/lib/${pname}${postgresql.dlSuffix}
85+
fi
86+
87+
runHook postInstall
88+
'';
89+
90+
meta = with lib; {
91+
description = "Open Source PostgreSQL Audit Logging";
92+
homepage = "https://github.com/pgaudit/pgaudit";
93+
changelog = "https://github.com/pgaudit/pgaudit/releases/tag/${source.version}";
94+
license = licenses.postgresql;
95+
inherit (postgresql.meta) platforms;
96+
};
97+
};
98+
in
99+
buildEnv {
100+
name = pname;
101+
paths = packages;
102+
pathsToLink = [
103+
"/lib"
104+
"/share/postgresql/extension"
44105
];
106+
postBuild = ''
107+
# checks
108+
(set -x
109+
test "$(ls -A $out/lib/${pname}*${postgresql.dlSuffix} | wc -l)" = "${
110+
toString (numberOfVersions + 1)
111+
}"
112+
)
45113
46-
makeFlags = [ "USE_PGXS=1" ];
114+
# Verify all expected library files are present
115+
expectedFiles=${toString (numberOfVersions + 1)}
116+
actualFiles=$(ls -A $out/lib/${pname}*${postgresql.dlSuffix} | wc -l)
47117
48-
installPhase = ''
49-
install -D -t $out/lib pgaudit${postgresql.dlSuffix}
50-
install -D -t $out/share/postgresql/extension *.sql
51-
install -D -t $out/share/postgresql/extension *.control
118+
if [[ "$actualFiles" != "$expectedFiles" ]]; then
119+
echo "Error: Expected $expectedFiles library files, found $actualFiles"
120+
echo "Files found:"
121+
ls -la $out/lib/${pname}*${postgresql.dlSuffix} || true
122+
exit 1
123+
fi
52124
'';
53125

54-
meta = with lib; {
55-
description = "Open Source PostgreSQL Audit Logging";
56-
homepage = "https://github.com/pgaudit/pgaudit";
57-
changelog = "https://github.com/pgaudit/pgaudit/releases/tag/${source.version}";
58-
platforms = postgresql.meta.platforms;
59-
license = licenses.postgresql;
126+
passthru = {
127+
inherit versions numberOfVersions;
128+
pname = "${pname}-all";
129+
version =
130+
"multi-" + lib.concatStringsSep "-" (map (v: lib.replaceStrings [ "." ] [ "-" ] v) versions);
60131
};
61132
}

nix/ext/tests/pgaudit.nix

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
{ self, pkgs }:
2+
let
3+
pname = "pgaudit";
4+
inherit (pkgs) lib;
5+
installedExtension =
6+
postgresMajorVersion: self.packages.${pkgs.system}."psql_${postgresMajorVersion}/exts/${pname}-all";
7+
versions = postgresqlMajorVersion: (installedExtension postgresqlMajorVersion).versions;
8+
postgresqlWithExtension =
9+
postgresql:
10+
let
11+
majorVersion = lib.versions.major postgresql.version;
12+
pkg = pkgs.buildEnv {
13+
name = "postgresql-${majorVersion}-${pname}";
14+
paths = [
15+
postgresql
16+
postgresql.lib
17+
(installedExtension majorVersion)
18+
];
19+
passthru = {
20+
inherit (postgresql) version psqlSchema;
21+
lib = pkg;
22+
withPackages = _: pkg;
23+
};
24+
nativeBuildInputs = [ pkgs.makeWrapper ];
25+
pathsToLink = [
26+
"/"
27+
"/bin"
28+
"/lib"
29+
];
30+
postBuild = ''
31+
wrapProgram $out/bin/postgres --set NIX_PGLIBDIR $out/lib
32+
wrapProgram $out/bin/pg_ctl --set NIX_PGLIBDIR $out/lib
33+
wrapProgram $out/bin/pg_upgrade --set NIX_PGLIBDIR $out/lib
34+
'';
35+
};
36+
in
37+
pkg;
38+
in
39+
self.inputs.nixpkgs.lib.nixos.runTest {
40+
name = pname;
41+
hostPkgs = pkgs;
42+
nodes.server =
43+
{ config, ... }:
44+
{
45+
virtualisation = {
46+
forwardPorts = [
47+
{
48+
from = "host";
49+
host.port = 13022;
50+
guest.port = 22;
51+
}
52+
];
53+
};
54+
services.openssh = {
55+
enable = true;
56+
};
57+
users.users.root.openssh.authorizedKeys.keys = [
58+
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIo+ulCUfJjnCVgfM4946Ih5Nm8DeZZiayYeABHGPEl7 jfroche"
59+
];
60+
61+
services.postgresql = {
62+
enable = true;
63+
package = postgresqlWithExtension self.packages.${pkgs.system}.postgresql_15;
64+
settings = {
65+
"shared_preload_libraries" = pname;
66+
};
67+
};
68+
69+
specialisation.postgresql17.configuration = {
70+
services.postgresql = {
71+
package = lib.mkForce (postgresqlWithExtension self.packages.${pkgs.system}.postgresql_17);
72+
};
73+
74+
systemd.services.postgresql-migrate = {
75+
serviceConfig = {
76+
Type = "oneshot";
77+
RemainAfterExit = true;
78+
User = "postgres";
79+
Group = "postgres";
80+
StateDirectory = "postgresql";
81+
WorkingDirectory = "${builtins.dirOf config.services.postgresql.dataDir}";
82+
};
83+
script =
84+
let
85+
oldPostgresql = postgresqlWithExtension self.packages.${pkgs.system}.postgresql_15;
86+
newPostgresql = postgresqlWithExtension self.packages.${pkgs.system}.postgresql_17;
87+
oldDataDir = "${builtins.dirOf config.services.postgresql.dataDir}/${oldPostgresql.psqlSchema}";
88+
newDataDir = "${builtins.dirOf config.services.postgresql.dataDir}/${newPostgresql.psqlSchema}";
89+
in
90+
''
91+
if [[ ! -d ${newDataDir} ]]; then
92+
install -d -m 0700 -o postgres -g postgres "${newDataDir}"
93+
${newPostgresql}/bin/initdb -D "${newDataDir}"
94+
echo "shared_preload_libraries = '${pname}'" >> "${newDataDir}/postgresql.conf"
95+
${newPostgresql}/bin/pg_upgrade --old-datadir "${oldDataDir}" --new-datadir "${newDataDir}" \
96+
--old-bindir "${oldPostgresql}/bin" --new-bindir "${newPostgresql}/bin"
97+
else
98+
echo "${newDataDir} already exists"
99+
fi
100+
'';
101+
};
102+
103+
systemd.services.postgresql = {
104+
after = [ "postgresql-migrate.service" ];
105+
requires = [ "postgresql-migrate.service" ];
106+
};
107+
};
108+
109+
};
110+
testScript =
111+
{ nodes, ... }:
112+
let
113+
pg17-configuration = "${nodes.server.system.build.toplevel}/specialisation/postgresql17";
114+
in
115+
''
116+
versions = {
117+
"15": [${lib.concatStringsSep ", " (map (s: ''"${s}"'') (versions "15"))}],
118+
"17": [${lib.concatStringsSep ", " (map (s: ''"${s}"'') (versions "17"))}],
119+
}
120+
121+
def run_sql(query):
122+
return server.succeed(f"""sudo -u postgres psql -t -A -F\",\" -c \"{query}\" """).strip()
123+
124+
def check_upgrade_path(pg_version):
125+
with subtest("Check ${pname} upgrade path"):
126+
firstVersion = versions[pg_version][0]
127+
server.succeed("sudo -u postgres psql -c 'DROP EXTENSION IF EXISTS ${pname};'")
128+
run_sql(f"""CREATE EXTENSION ${pname} WITH VERSION '{firstVersion}';""")
129+
installed_version = run_sql(r"""SELECT extversion FROM pg_extension WHERE extname = '${pname}';""")
130+
assert installed_version == firstVersion, f"Expected ${pname} version {firstVersion}, but found {installed_version}"
131+
for version in versions[pg_version][1:]:
132+
run_sql(f"""ALTER EXTENSION ${pname} UPDATE TO '{version}';""")
133+
installed_version = run_sql(r"""SELECT extversion FROM pg_extension WHERE extname = '${pname}';""")
134+
assert installed_version == version, f"Expected ${pname} version {version}, but found {installed_version}"
135+
136+
start_all()
137+
138+
server.wait_for_unit("multi-user.target")
139+
server.wait_for_unit("postgresql.service")
140+
141+
check_upgrade_path("15")
142+
143+
with subtest("Check ${pname} latest extension version"):
144+
server.succeed("sudo -u postgres psql -c 'DROP EXTENSION ${pname};'")
145+
server.succeed("sudo -u postgres psql -c 'CREATE EXTENSION ${pname};'")
146+
installed_extensions=run_sql(r"""SELECT extname, extversion FROM pg_extension;""")
147+
latestVersion = versions["15"][-1]
148+
assert f"${pname},{latestVersion}" in installed_extensions
149+
150+
with subtest("switch to postgresql 17"):
151+
server.succeed(
152+
"${pg17-configuration}/bin/switch-to-configuration test >&2"
153+
)
154+
155+
# cannot upgrade pgaudit from 15 to 17
156+
# with subtest("Check ${pname} latest extension version"):
157+
# installed_extensions=run_sql(r"""SELECT extname, extversion FROM pg_extension;""")
158+
# latestVersion = versions["17"][-1]
159+
# assert f"${pname},{latestVersion}" in installed_extensions
160+
161+
check_upgrade_path("17")
162+
'';
163+
}

nix/ext/versions.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,37 @@
2020
],
2121
"hash": "sha256-G0eQk2bY5CNPMeokN/nb05g03CuiplRf902YXFVQFbs="
2222
}
23+
},
24+
"pgaudit": {
25+
"1.6.1": {
26+
"postgresql": [
27+
"15"
28+
],
29+
"hash": "sha256-vxUDVq7nWkq7Qugy7HJLOXk4B61MSBIYQkzcbU6wSG8="
30+
},
31+
"1.7.0": {
32+
"postgresql": [
33+
"15"
34+
],
35+
"hash": "sha256-8pShPr4HJaJQPjW1iPJIpj3CutTx8Tgr+rOqoXtgCcw="
36+
},
37+
"1.7.1": {
38+
"postgresql": [
39+
"15"
40+
],
41+
"hash": "sha256-emwoTowT7WKFX0RQDqJXjIblrzqaUIUkzqSqBCHVKQ8="
42+
},
43+
"17.0": {
44+
"postgresql": [
45+
"17"
46+
],
47+
"hash": "sha256-3ksq09wiudQPuBQI3dhEQi8IkXKLVIsPFgBnwLiicro="
48+
},
49+
"17.1": {
50+
"postgresql": [
51+
"17"
52+
],
53+
"hash": "sha256-9St/ESPiFq2NiPKqbwHLwkIyATKUkOGxFcUrWgT+Iqo="
54+
}
2355
}
2456
}

0 commit comments

Comments
 (0)