From e348a491b0c2de961b7d33e60dad970f0706a7d7 Mon Sep 17 00:00:00 2001 From: Inex Code Date: Sun, 22 Dec 2024 13:19:10 +0300 Subject: [PATCH] feat: PostgreSQL migration --- configuration.nix | 1 + postgresql/postgresql.nix | 67 ++++++++++++++++++ postgresql/upgrade12to16.nix | 130 ++++++++++++++++++++++++++++++++++ selfprivacy-module.nix | 10 +++ sp-modules/pleroma/module.nix | 14 ---- 5 files changed, 208 insertions(+), 14 deletions(-) create mode 100644 postgresql/postgresql.nix create mode 100644 postgresql/upgrade12to16.nix diff --git a/configuration.nix b/configuration.nix index 4ee7959..9737c64 100644 --- a/configuration.nix +++ b/configuration.nix @@ -38,6 +38,7 @@ in ./letsencrypt/resolve.nix ./webserver/nginx.nix ./webserver/memcached.nix + ./postgresql/postgresql.nix # ./resources/limits.nix ]; diff --git a/postgresql/postgresql.nix b/postgresql/postgresql.nix new file mode 100644 index 0000000..e281d7f --- /dev/null +++ b/postgresql/postgresql.nix @@ -0,0 +1,67 @@ +{ config, lib, pkgs, ... }: +let + sp = config.selfprivacy; + pleroma_location = + if lib.attrsets.hasAttr "pleroma" sp.modules && lib.attrsets.hasAttr "location" sp.modules.pleroma + then sp.modules.pleroma.location + else null; + postgres_location = + if lib.attrsets.hasAttr "postgresql" sp && lib.attrsets.hasAttr "location" sp.postgresql + then sp.postgresql.location + else null; + # Priority: postgresql > pleroma + location = if postgres_location != null then postgres_location else pleroma_location; + # Active if there is a location + enable = location != null; + pleroma_enabled = + if lib.attrsets.hasAttr "pleroma" sp.modules && lib.attrsets.hasAttr "enable" sp.modules.pleroma + then sp.modules.pleroma.enable + else false; +in +{ + imports = [ + ./upgrade12to16.nix + ]; + fileSystems = lib.mkIf enable { + "/var/lib/postgresql" = { + device = "/volumes/${location}/postgresql"; + options = [ + "bind" + "x-systemd.required-by=postgresql.service" + "x-systemd.required-by=postgresql-upgrade12to16.service" + "x-systemd.before=postgresql.service" + "x-systemd.before=postgresql-upgrade12to16.service" + ]; + }; + "/var/lib/postgresql-dumps" = { + device = "/volumes/${location}/postgresql-dumps"; + options = [ "bind" ]; + }; + }; + services = { + postgresql = { + enable = enable; + package = pkgs.postgresql_16; + # Change to selfpirvacy-api user when API migrates to rootless daemon + authentication = '' + local all postgres peer map=selfprivacy-api + ''; + identMap = '' + selfprivacy-api root postgres + selfprivacy-api postgres postgres + ''; + }; + postgresqlUpgrade12to16 = { + enable = enable; + pleromaEnabled = pleroma_enabled; + }; + }; + systemd = { + services.postgresql = { + serviceConfig.Slice = "postgresql.slice"; + }; + slices.postgresql = { + description = "PostgreSQL slice"; + }; + }; +} diff --git a/postgresql/upgrade12to16.nix b/postgresql/upgrade12to16.nix new file mode 100644 index 0000000..2685559 --- /dev/null +++ b/postgresql/upgrade12to16.nix @@ -0,0 +1,130 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.postgresqlUpgrade12to16; + mkIf = lib.mkIf; + mkOption = lib.mkOption; + types = lib.types; + optional = lib.optional; +in + +{ + options.services.postgresqlUpgrade12to16 = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enables an automatic one-shot upgrade from PostgreSQL 12 to 16 if a + version 12 data directory is found. This will run pg_upgrade and may + take time depending on database size. Use with caution. + ''; + }; + dataDir12 = mkOption { + type = types.str; + default = "/var/lib/postgresql/12"; + description = "Location of the old PostgreSQL 12 data directory."; + }; + dataDir16 = mkOption { + type = types.str; + default = "/var/lib/postgresql/16"; + description = "Location of the new PostgreSQL 16 data directory."; + }; + pleromaEnabled = mkOption { + type = types.bool; + default = false; + description = '' + Whether Pleroma service is present and needs to be stopped/started + during the PostgreSQL upgrade process. + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = config.services.postgresql.package == pkgs.postgresql_16; + message = "PostgreSQL package must be set to postgresql_16 for the upgrade to work correctly."; + } + { + assertion = config.services.postgresql.dataDir == cfg.dataDir16; + message = "PostgreSQL data directory must be set to ${cfg.dataDir16} for the upgrade to work correctly. The current value is ${config.services.postgresql.dataDir}."; + } + ]; + + systemd.services."postgresql-upgrade12to16" = { + description = "Upgrade PostgreSQL 12 database to PostgreSQL 16"; + wantedBy = [ "multi-user.target" ]; + before = [ "postgresql.service" ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + serviceConfig = { + Type = "oneshot"; + + ExecStartPre = + # Stop Pleroma only if pleromaEnabled is true + optional cfg.pleromaEnabled "${pkgs.writeShellScript "postgresql-upgrade12to16-pre.sh" '' + if [ -d "${cfg.dataDir12}" ] && [ ! -d "${cfg.dataDir16}" ]; then + ${pkgs.systemd}/bin/systemctl stop pleroma.service + fi + '' + }"; + + ExecStart = "${pkgs.writeShellScript "postgresql-upgrade12to16.sh" '' + set -e + + oldDataDir="${cfg.dataDir12}" + newDataDir="${cfg.dataDir16}" + + # Only upgrade if old data directory exists, and the new one doesn't yet + if [ -d "$oldDataDir" ] && [ ! -d "$newDataDir" ]; then + echo "Detected PostgreSQL 12 data directory at $oldDataDir" + echo "Upgrading to PostgreSQL 16 data directory at $newDataDir" + + # Stop the old PostgreSQL if it's running + if systemctl is-active --quiet postgresql.service; then + systemctl stop postgresql.service + fi + + # Create the new data directory (if not already present) + mkdir -p "$newDataDir" + chown -R postgres:postgres "$(dirname "$newDataDir")" + + # Create a temporary working directory + tempDir=$(mktemp -d) + chown -R postgres:postgres "$tempDir" + trap 'rm -rf "$tempDir"' EXIT + + # Change to the temporary working directory + cd "$tempDir" + + # Initialize the new PostgreSQL 16 data directory + ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql_16.out}/bin/initdb -D "$newDataDir" -U postgres + + # Run pg_upgrade as the postgres user + ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql_16.out}/bin/pg_upgrade \ + --old-datadir "$oldDataDir" \ + --new-datadir "$newDataDir" \ + --old-bindir ${pkgs.postgresql_12.out}/bin \ + --new-bindir ${pkgs.postgresql_16.out}/bin \ + --jobs "$(nproc)" \ + --link \ + --verbose + + echo "PostgreSQL upgrade from 12 to 16 completed." + else + echo "No PostgreSQL 12 data directory detected or already upgraded. Skipping." + fi + ''}"; + + # Start Pleroma only if pleromaEnabled is true + ExecStartPost = + optional cfg.pleromaEnabled "${pkgs.writeShellScript "postgresql-upgrade12to16-pre.sh" '' + if [ -d "${cfg.dataDir12}" ] && [ ! -d "${cfg.dataDir16}" ]; then + ${pkgs.systemd}/bin/systemctl start --no-block pleroma.service + fi + '' + }"; + }; + }; + }; +} diff --git a/selfprivacy-module.nix b/selfprivacy-module.nix index b313a6d..21fd25f 100644 --- a/selfprivacy-module.nix +++ b/selfprivacy-module.nix @@ -129,5 +129,15 @@ with lib; default = false; description = "Whether to bind-mount vmail and sieve folders"; }; + ################ + # PostgreSQL # + ################ + postgresql = { + location = mkOption { + description = "Volume name where to store Postgres data."; + type = types.nullOr types.str; + default = null; + }; + }; }; } diff --git a/sp-modules/pleroma/module.nix b/sp-modules/pleroma/module.nix index 14c10a5..3621dff 100644 --- a/sp-modules/pleroma/module.nix +++ b/sp-modules/pleroma/module.nix @@ -48,16 +48,6 @@ in "x-systemd.before=pleroma-secrets.service" ]; }; - "/var/lib/postgresql" = { - device = "/volumes/${cfg.location}/postgresql"; - options = [ - "bind" - "x-systemd.required-by=pleroma-secrets.service" - "x-systemd.required-by=pleroma.service" - "x-systemd.before=pleroma-secrets.service" - "x-systemd.before=pleroma.service" - ]; - }; }; services = { pleroma = { @@ -72,8 +62,6 @@ in ]; }; postgresql = { - enable = true; - package = pkgs.postgresql_12; initialScript = "/etc/setup.psql"; ensureDatabases = [ "pleroma" @@ -88,8 +76,6 @@ in }; environment.etc."setup.psql".text = '' - CREATE USER pleroma; - CREATE DATABASE pleroma OWNER pleroma; \c pleroma; --Extensions made by ecto.migrate that need superuser access CREATE EXTENSION IF NOT EXISTS citext;