|
36 | 36 |
|
37 | 37 | groupAccessAvailable = versionAtLeast postgresql.version "11.0"; |
38 | 38 |
|
| 39 | + upgradeScript = let |
| 40 | + args = { |
| 41 | + old-bindir = "${cfg.databaseDir}/current/nix-postgresql-bin"; |
| 42 | + old-datadir = "${cfg.databaseDir}/current"; |
| 43 | + new-datadir = cfg.dataDir; |
| 44 | + }; |
| 45 | + in pkgs.writeShellApplication { |
| 46 | + name = "upgrade.sh"; |
| 47 | + text = '' |
| 48 | + if ! [[ -e "${cfg.databaseDir}/current" ]]; then |
| 49 | + echo "No old data found, assuming fresh deployment" |
| 50 | + exit 0 |
| 51 | + fi |
| 52 | +
|
| 53 | + if [[ "$(tr --delete '[:space:]' <"${cfg.databaseDir}/current/PG_VERSION")" == "${postgresql.psqlSchema}" ]]; then |
| 54 | + echo "Previous major version matches the current one. No upgrade necessary" |
| 55 | + exit 0 |
| 56 | + fi |
| 57 | +
|
| 58 | + pushd "${cfg.dataDir}" |
| 59 | + ${postgresql}/bin/pg_upgrade ${lib.cli.toGNUCommandLineShell {} args} ${lib.escapeShellArgs cfg.upgrade.extraArgs} |
| 60 | + popd |
| 61 | + touch "${cfg.dataDir}/.post_upgrade" |
| 62 | + ''; |
| 63 | + }; |
| 64 | + |
39 | 65 | in |
40 | 66 |
|
41 | 67 | { |
|
75 | 101 | description = lib.mdDoc "Check the syntax of the configuration file at compile time"; |
76 | 102 | }; |
77 | 103 |
|
| 104 | + databaseDir = mkOption { |
| 105 | + type = types.path; |
| 106 | + description = '' |
| 107 | + Version-independent location of the database data directories. |
| 108 | + Unlike `dataDir`, this value must not contain the postgresql version. |
| 109 | + ''; |
| 110 | + default = "/var/lib/postgresql"; |
| 111 | + }; |
| 112 | + |
78 | 113 | dataDir = mkOption { |
79 | 114 | type = types.path; |
80 | | - defaultText = literalExpression ''"/var/lib/postgresql/''${config.services.postgresql.package.psqlSchema}"''; |
| 115 | + defaultText = literalExpression ''"''${config.services.postgresql.databaseDir}/''${config.services.postgresql.package.psqlSchema}"''; |
81 | 116 | example = "/var/lib/postgresql/11"; |
82 | 117 | description = lib.mdDoc '' |
83 | 118 | The data directory for PostgreSQL. If left as the default value |
|
438 | 473 | PostgreSQL superuser account to use for various operations. Internal since changing |
439 | 474 | this value would lead to breakage while setting up databases. |
440 | 475 | ''; |
| 476 | + }; |
| 477 | + |
| 478 | + upgrade = mkOption { |
| 479 | + type = types.submodule { |
| 480 | + options = { |
| 481 | + enable = mkEnableOption (lib.mdDoc "Major version automatic upgrades"); |
| 482 | + |
| 483 | + enablePreviousInstallationAutodetection = lib.mkOption { |
| 484 | + type = types.bool; |
| 485 | + default = true; # Can be disabled by default in newer stateVersion |
| 486 | + description = lib.mdDoc '' |
| 487 | + Adds an activation script that tries to detect dataDir and binDir from previous installation using systemctl Environment. |
| 488 | + The activation script has no effect if "''${cfg.databaseDir}/current" symlink already exists. |
| 489 | +
|
| 490 | + If this option is disabled, you must ensure that the "''${cfg.databaseDir}/current" symlink already exists (e.g. by starting the database at least once without upgrading), |
| 491 | + otherwise setting newer major release of postgresql will start with empty dataDir. |
| 492 | + Not setting the option is ok for fresh deployments. |
| 493 | + ''; |
| 494 | + }; |
| 495 | + |
| 496 | + runAnalyze = mkOption { |
| 497 | + type = types.bool; |
| 498 | + default = true; |
| 499 | + description = lib.mdDoc '' |
| 500 | + Whether to run `vacuumdb --all --analyze-in-stages` after an successful upgrade as pg_upgrade suggests. |
| 501 | + ''; |
| 502 | + }; |
| 503 | + |
| 504 | + extraArgs = mkOption { |
| 505 | + type = types.listOf types.str; |
| 506 | + description = lib.mdDoc '' |
| 507 | + Additional arguments passed to pg_upgrade. |
| 508 | + See [pg_upgrade docs]<https://www.postgresql.org/docs/current/pgupgrade.html> for available options. |
| 509 | + ''; |
| 510 | + default = []; |
| 511 | + example = [ "--link" "--jobs=16" ]; |
| 512 | + }; |
| 513 | + }; |
441 | 514 | }; |
| 515 | + default = {}; |
| 516 | + description = lib.mdDoc "Settings related to automatically upgrading major versions"; |
| 517 | + }; |
442 | 518 | }; |
443 | 519 |
|
444 | 520 | }; |
|
447 | 523 | ###### implementation |
448 | 524 |
|
449 | 525 | config = mkIf cfg.enable { |
| 526 | + assertions = [ |
| 527 | + { |
| 528 | + # Note: We don't really need to be versioned _under databaseDir_ can probably we could make this less strict. |
| 529 | + # However, the requirement for versioned dataDir is necessary. |
| 530 | + assertion = cfg.upgrade.enable -> cfg.dataDir == "${cfg.databaseDir}/${cfg.package.psqlSchema}"; |
| 531 | + message = '' |
| 532 | + Automatic postgresql upgrades require dataDir to be versioned under databaseDir. |
| 533 | +
|
| 534 | + Example: |
| 535 | + ``` |
| 536 | + databaseDir = "/var/lib/postgresql" |
| 537 | + dataDir = "/var/lib/postgresql/15" |
| 538 | + ``` |
| 539 | +
|
| 540 | + Current: |
| 541 | + ``` |
| 542 | + databaseDir = "${cfg.databaseDir}" |
| 543 | + dataDir = "${cfg.dataDir}" |
| 544 | + ``` |
| 545 | + ''; |
| 546 | + } |
| 547 | + ]; |
450 | 548 |
|
451 | 549 | services.postgresql.settings = |
452 | 550 | { |
|
473 | 571 | # systems! |
474 | 572 | mkDefault (if cfg.enableJIT then base.withJIT else base); |
475 | 573 |
|
476 | | - services.postgresql.dataDir = mkDefault "/var/lib/postgresql/${cfg.package.psqlSchema}"; |
| 574 | + services.postgresql.dataDir = mkDefault "${cfg.databaseDir}/${cfg.package.psqlSchema}"; |
477 | 575 |
|
478 | 576 | services.postgresql.authentication = mkAfter |
479 | 577 | '' |
|
522 | 620 | # Initialise the database. |
523 | 621 | initdb -U ${cfg.superUser} ${concatStringsSep " " cfg.initdbArgs} |
524 | 622 |
|
525 | | - # See postStart! |
526 | | - touch "${cfg.dataDir}/.first_startup" |
| 623 | + ${optionalString cfg.upgrade.enable (getExe upgradeScript)} |
| 624 | +
|
| 625 | + if ! test -e "${cfg.dataDir}/.post_upgrade"; then |
| 626 | + # See postStart! |
| 627 | + touch "${cfg.dataDir}/.first_startup" |
| 628 | + fi |
527 | 629 | fi |
528 | 630 |
|
529 | 631 | ln -sfn "${configFile}/postgresql.conf" "${cfg.dataDir}/postgresql.conf" |
530 | 632 | ${optionalString (cfg.recoveryConfig != null) '' |
531 | 633 | ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \ |
532 | 634 | "${cfg.dataDir}/recovery.conf" |
533 | 635 | ''} |
| 636 | +
|
| 637 | + ln -sfn "${postgresql}/bin" "${cfg.dataDir}/nix-postgresql-bin" |
| 638 | + if [[ -d "${cfg.databaseDir}" ]]; then |
| 639 | + ln -sfn "${cfg.dataDir}" "${cfg.databaseDir}/current" |
| 640 | + fi |
534 | 641 | ''; |
535 | 642 |
|
536 | 643 | # Wait for PostgreSQL to be ready to accept connections. |
|
549 | 656 | ''} |
550 | 657 | rm -f "${cfg.dataDir}/.first_startup" |
551 | 658 | fi |
| 659 | + '' + optionalString cfg.upgrade.enable '' |
| 660 | + if test -e "${cfg.dataDir}/.post_upgrade"; then |
| 661 | + ${optionalString cfg.upgrade.runAnalyze '' |
| 662 | + vacuumdb --port=${toString cfg.port} --all --analyze-in-stages |
| 663 | + ''} |
| 664 | + rm -f "${cfg.dataDir}/.post_upgrade" |
| 665 | + fi |
552 | 666 | '' + optionalString (cfg.ensureDatabases != []) '' |
553 | 667 | ${concatMapStrings (database: '' |
554 | 668 | $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${database}'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "${database}"' |
|
608 | 722 | unitConfig.RequiresMountsFor = "${cfg.dataDir}"; |
609 | 723 | }; |
610 | 724 |
|
| 725 | + system.activationScripts.detect-previous-postgresql-installation = lib.mkIf (cfg.upgrade.enable && cfg.upgrade.enablePreviousInstallationAutodetection) { |
| 726 | + deps = [ "etc" ]; |
| 727 | + text = '' |
| 728 | + previousPgDetect() { |
| 729 | + echo "Trying to detect prevoius PostgreSQL installation, because 'current' symlink does not exist in 'config.services.postgresql.databaseDir'." |
| 730 | +
|
| 731 | + if [[ ! -x /run/current-system/sw/bin/systemctl ]]; then |
| 732 | + echo "systemctl binary is missing or is not executable. This is ok on fresh deployments. Not running previous PostgreSQL installation detection." |
| 733 | + return |
| 734 | + fi |
| 735 | +
|
| 736 | + prev_postgresql_env="$(/run/current-system/sw/bin/systemctl show postgresql.service --property=Environment --value || true)" |
| 737 | + if [[ -z "$prev_postgresql_env" ]]; then |
| 738 | + echo "Cannot load old PostgreSQL Environment from systemctl." |
| 739 | + return |
| 740 | + fi |
| 741 | + old_data_dir="$(export $prev_postgresql_env; echo "''${PGDATA:-}")" |
| 742 | + if [[ -z "$old_data_dir" ]]; then |
| 743 | + echo "Cannot detect old PostgreSQL data dir!" |
| 744 | + return |
| 745 | + fi |
| 746 | + old_bin=$(export $prev_postgresql_env; command -v postgres) |
| 747 | + if [[ -z "$old_bin" ]]; then |
| 748 | + echo "Cannot detect old PostgreSQL binary!" |
| 749 | + return |
| 750 | + fi |
| 751 | + old_bin_dir=$(dirname "$old_bin") |
| 752 | +
|
| 753 | + echo "Detected old PostgreSQL installation!" |
| 754 | + echo "Setting old dataDir to '$old_data_dir'" |
| 755 | + echo "Setting old binDir to '$old_bin_dir'" |
| 756 | +
|
| 757 | + ln -sn "$old_data_dir" "${cfg.databaseDir}/current" |
| 758 | + ln -sn "$old_bin_dir" "${cfg.databaseDir}/current/nix-postgresql-bin" |
| 759 | + } |
| 760 | +
|
| 761 | + if [[ ! -e "${cfg.databaseDir}/current" ]]; then |
| 762 | + previousPgDetect |
| 763 | + fi |
| 764 | + ''; |
| 765 | + }; |
| 766 | + |
611 | 767 | }; |
612 | 768 |
|
613 | 769 | meta.doc = ./postgresql.md; |
|
0 commit comments