Files
process-compose-wrapper/services/postgres.nix
2025-03-17 17:40:13 +04:00

153 lines
4.1 KiB
Nix

{
pkgs,
}:
let
inherit (pkgs) lib;
in
{
name,
default_config ? true,
config ? { },
extra_config ? '''',
package ? pkgs.postgresql,
port ? 5432,
bind ? "localhost",
initialDatabases ? [ ],
...
}:
let
merged_config =
(
if default_config then
{
listen_addresses = "localhost";
port = 5432;
shared_buffers = "128MB";
work_mem = "4MB";
logging_collector = false;
}
else
{ }
)
// config
// {
unix_socket_directories = ".";
listen_addresses = bind;
inherit port;
};
toStr =
value:
if true == value then
"yes"
else if false == value then
"no"
else if lib.isString value then
"'${lib.replaceStrings [ "'" ] [ "''" ] value}'"
else
builtins.toString value;
configFile = pkgs.writeTextFile {
name = "postgresql.conf";
text =
lib.concatStringsSep "\n" (
lib.mapAttrsToList (n: v: "${n} = ${toStr v}") (
lib.filterAttrs (lib.const (x: x != null)) merged_config
)
)
+ extra_config;
};
setupInitialDatabases =
if initialDatabases != [ ] then
(lib.concatMapStrings (db: ''
echo "Checking presence of database ${db.name}"
dbAlreadyExists="$(echo "SELECT 1 AS result FROM pg_database WHERE datname='${db.name}';" | psql --dbname postgres | ${pkgs.gnugrep}/bin/grep -c 'exists = "1"' || true)"
echo "$dbAlreadyExists"
if [ 1 -ne "$dbAlreadyExists" ]; then
echo "Creating database ${db.name}"
echo 'CREATE DATABASE "${db.name}";' | psql --dbname postgres
${lib.optionalString (db.user != null && db.password != null) ''
echo "Creating user ${db.user}"
psql --dbname postgres <<'EOF'
DO $$
BEGIN
CREATE ROLE "${db.user}" WITH LOGIN PASSWORD '${db.password}';
EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
GRANT ALL PRIVILEGES ON DATABASE "${db.name}" TO "${db.user}";
\c ${db.name}
GRANT ALL PRIVILEGES ON SCHEMA public TO "${db.user}";
EOF
''}
else
echo "Database ${db.name} already exists"
fi
'') initialDatabases)
else
"";
setupScript = pkgs.writeShellApplication {
name = "setup-postgres";
runtimeInputs = [
package
pkgs.coreutils
];
text = ''
POSTGRES_RUN_INITIAL_SCRIPT="false"
if [[ ! -d "$PGDATA" ]]; then
initdb --no-instructions
POSTGRES_RUN_INITIAL_SCRIPT="true"
echo
echo "PostgreSQL initdb process completed"
echo
fi
cp ${configFile} "$PGDATA"/postgresql.conf
if [[ "$POSTGRES_RUN_INITIAL_SCRIPT" == "true" ]]; then
echo
echo "PostgreSQL is setting up the initial database"
echo
pg_ctl -w start -o "-c listen_addresses= -p ${builtins.toString port}"
${setupInitialDatabases}
pg_ctl -m fast -w stop
else
echo
echo "Database directory exists. Skipping initialization"
echo
fi
unset POSTGRES_RUN_INITIAL_SCRIPT
'';
};
script = pkgs.writeShellApplication {
name = "run-postgres";
text = ''
set -euo pipefail
PGDATA=$RUNTIME_PATH/${name}
PGHOST=$RUNTIME_PATH/${name}
PGPORT=${builtins.toString port}
echo "Starting postgres with PGDATA=''${PGDATA} PGHOST=''${PGHOST} PGPORT=''${PGPORT}"
export PGDATA PGHOST PGPORT
${setupScript}/bin/setup-postgres
${package}/bin/postgres
'';
};
in
# configFileCheck = pkgs.runCommand {} ''
# ${package}/bin/postgres -D${configFile} -C config_file
# touch $out
# '';
{
processes."${name}" = {
command = "${script}/bin/run-postgres";
shutdown.signal = 2;
readiness_probe = {
exec.command = "${package}/bin/pg_isready";
initial_delay_seconds = 2;
period_seconds = 2;
timeout_seconds = 2;
success_threshold = 1;
failure_threshold = 5;
};
availability.restart = "on_failure";
};
}