diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
index 0760a781ea33..a6bd453b35d9 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
@@ -122,6 +122,13 @@
services.powerdns-admin.
+
+
+ pgadmin4,
+ an admin interface for the PostgreSQL database. Available at
+ services.pgadmin.
+
+
input-remapper,
@@ -623,6 +630,13 @@
otelcorecol and enjoy a 7x smaller binary.
+
+
+ pkgs.pgadmin now refers to
+ pkgs.pgadmin4. If you still need pgadmin3,
+ use pkgs.pgadmin3.
+
+
pkgs.noto-fonts-cjk is now deprecated in
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 2a931cd7fd6f..8f5b4789a42c 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -39,6 +39,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- [PowerDNS-Admin](https://github.com/ngoduykhanh/PowerDNS-Admin), a web interface for the PowerDNS server. Available at [services.powerdns-admin](options.html#opt-services.powerdns-admin.enable).
+- [pgadmin4](https://github.com/postgres/pgadmin4), an admin interface for the PostgreSQL database. Available at [services.pgadmin](options.html#opt-services.pgadmin.enable).
+
- [input-remapper](https://github.com/sezanzeb/input-remapper), an easy to use tool to change the mapping of your input device buttons. Available at [services.input-remapper](options.html#opt-services.input-remapper.enable).
- [InvoicePlane](https://invoiceplane.com), web application for managing and creating invoices. Available at [services.invoiceplane](options.html#opt-services.invoiceplane.enable).
@@ -198,6 +200,8 @@ In addition to numerous new and upgraded packages, this release has the followin
you should change the package you refer to. If you don't need them update your
commands from `otelcontribcol` to `otelcorecol` and enjoy a 7x smaller binary.
+- `pkgs.pgadmin` now refers to `pkgs.pgadmin4`.
+ If you still need pgadmin3, use `pkgs.pgadmin3`.
- `pkgs.noto-fonts-cjk` is now deprecated in favor of `pkgs.noto-fonts-cjk-sans`
and `pkgs.noto-fonts-cjk-serif` because they each have different release
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 2d9942a85b36..29fcc920f421 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -254,6 +254,7 @@
./security/tpm2.nix
./services/admin/meshcentral.nix
./services/admin/oxidized.nix
+ ./services/admin/pgadmin.nix
./services/admin/salt/master.nix
./services/admin/salt/minion.nix
./services/amqp/activemq/default.nix
diff --git a/nixos/modules/services/admin/pgadmin.nix b/nixos/modules/services/admin/pgadmin.nix
new file mode 100644
index 000000000000..80b681454104
--- /dev/null
+++ b/nixos/modules/services/admin/pgadmin.nix
@@ -0,0 +1,127 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ pkg = pkgs.pgadmin4;
+ cfg = config.services.pgadmin;
+
+ _base = with types; [ int bool str ];
+ base = with types; oneOf ([ (listOf (oneOf _base)) (attrsOf (oneOf _base)) ] ++ _base);
+
+ formatAttrset = attr:
+ "{${concatStringsSep "\n" (mapAttrsToList (key: value: "${builtins.toJSON key}: ${formatPyValue value},") attr)}}";
+
+ formatPyValue = value:
+ if builtins.isString value then builtins.toJSON value
+ else if value ? _expr then value._expr
+ else if builtins.isInt value then toString value
+ else if builtins.isBool value then (if value then "True" else "False")
+ else if builtins.isAttrs value then (formatAttrset value)
+ else if builtins.isList value then "[${concatStringsSep "\n" (map (v: "${formatPyValue v},") value)}]"
+ else throw "Unrecognized type";
+
+ formatPy = attrs:
+ concatStringsSep "\n" (mapAttrsToList (key: value: "${key} = ${formatPyValue value}") attrs);
+
+ pyType = with types; attrsOf (oneOf [ (attrsOf base) (listOf base) base ]);
+in
+{
+ options.services.pgadmin = {
+ enable = mkEnableOption "PostgreSQL Admin 4";
+
+ port = mkOption {
+ description = "Port for pgadmin4 to run on";
+ type = types.port;
+ default = 5050;
+ };
+
+ initialEmail = mkOption {
+ description = "Initial email for the pgAdmin account.";
+ type = types.str;
+ };
+
+ initialPasswordFile = mkOption {
+ description = ''
+ Initial password file for the pgAdmin account.
+ NOTE: Should be string not a store path, to prevent the password from being world readable.
+ '';
+ type = types.path;
+ };
+
+ openFirewall = mkEnableOption "firewall passthrough for pgadmin4";
+
+ settings = mkOption {
+ description = ''
+ Settings for pgadmin4.
+ Documentation.
+ '';
+ type = pyType;
+ default= {};
+ };
+ };
+
+ config = mkIf (cfg.enable) {
+ networking.firewall.allowedTCPPorts = mkIf (cfg.openFirewall) [ cfg.port ];
+
+ services.pgadmin.settings = {
+ DEFAULT_SERVER_PORT = cfg.port;
+ SERVER_MODE = true;
+ } // (optionalAttrs cfg.openFirewall {
+ DEFAULT_SERVER = mkDefault "::";
+ });
+
+ systemd.services.pgadmin = {
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ requires = [ "network.target" ];
+ # we're adding this optionally so just in case there's any race it'll be caught
+ # in case postgres doesn't start, pgadmin will just start normally
+ wants = [ "postgresql.service" ];
+
+ path = [ config.services.postgresql.package pkgs.coreutils pkgs.bash ];
+
+ preStart = ''
+ # NOTE: this is idempotent (aka running it twice has no effect)
+ (
+ # Email address:
+ echo ${escapeShellArg cfg.initialEmail}
+
+ # file might not contain newline. echo hack fixes that.
+ PW=$(cat ${escapeShellArg cfg.initialPasswordFile})
+
+ # Password:
+ echo "$PW"
+ # Retype password:
+ echo "$PW"
+ ) | ${pkg}/bin/pgadmin4-setup
+ '';
+
+ restartTriggers = [
+ "/etc/pgadmin/config_system.py"
+ ];
+
+ serviceConfig = {
+ User = "pgadmin";
+ DynamicUser = true;
+ LogsDirectory = "pgadmin";
+ StateDirectory = "pgadmin";
+ ExecStart = "${pkg}/bin/pgadmin4";
+ };
+ };
+
+ users.users.pgadmin = {
+ isSystemUser = true;
+ group = "pgadmin";
+ };
+
+ users.groups.pgadmin = {};
+
+ environment.etc."pgadmin/config_system.py" = {
+ text = formatPy cfg.settings;
+ mode = "0600";
+ user = "pgadmin";
+ group = "pgadmin";
+ };
+ };
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 85f0a001e666..da94fc6d042d 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -394,6 +394,7 @@ in
peerflix = handleTest ./peerflix.nix {};
peertube = handleTestOn ["x86_64-linux"] ./web-apps/peertube.nix {};
pgadmin4 = handleTest ./pgadmin4.nix {};
+ pgadmin4-standalone = handleTest ./pgadmin4-standalone.nix {};
pgjwt = handleTest ./pgjwt.nix {};
pgmanage = handleTest ./pgmanage.nix {};
php = handleTest ./php {};
diff --git a/nixos/tests/pgadmin4-standalone.nix b/nixos/tests/pgadmin4-standalone.nix
new file mode 100644
index 000000000000..442570c5306b
--- /dev/null
+++ b/nixos/tests/pgadmin4-standalone.nix
@@ -0,0 +1,43 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }:
+ # This is seperate from pgadmin4 since we don't want both running at once
+
+ {
+ name = "pgadmin4-standalone";
+ meta.maintainers = with lib.maintainers; [ mkg20001 ];
+
+ nodes.machine = { pkgs, ... }: {
+ environment.systemPackages = with pkgs; [
+ curl
+ ];
+
+ services.postgresql = {
+ enable = true;
+
+ authentication = ''
+ host all all localhost trust
+ '';
+
+ ensureUsers = [
+ {
+ name = "postgres";
+ ensurePermissions = {
+ "DATABASE \"postgres\"" = "ALL PRIVILEGES";
+ };
+ }
+ ];
+ };
+
+ services.pgadmin = {
+ enable = true;
+ initialEmail = "bruh@localhost.de";
+ initialPasswordFile = pkgs.writeText "pw" "bruh2012!";
+ };
+ };
+
+ testScript = ''
+ machine.wait_for_unit("postgresql")
+ machine.wait_for_unit("pgadmin")
+
+ machine.wait_until_succeeds("curl -s localhost:5050")
+ '';
+ })
diff --git a/pkgs/tools/admin/pgadmin/default.nix b/pkgs/tools/admin/pgadmin/default.nix
index 994d0de6b263..86c68aa97834 100644
--- a/pkgs/tools/admin/pgadmin/default.nix
+++ b/pkgs/tools/admin/pgadmin/default.nix
@@ -35,6 +35,11 @@ python3.pkgs.buildPythonApplication rec {
format = "setuptools";
+ patches = [
+ # Expose setup.py for later use
+ ./expose-setup.py.patch
+ ];
+
postPatch = ''
# patching Makefile, so it doesn't try to build sphinx documentation here
# (will do so later)
@@ -146,7 +151,7 @@ python3.pkgs.buildPythonApplication rec {
];
passthru = {
- tests = { inherit (nixosTests) pgadmin4; };
+ tests = { inherit (nixosTests) pgadmin4 pgadmin4-standalone; };
};
meta = with lib; {
diff --git a/pkgs/tools/admin/pgadmin/expose-setup.py.patch b/pkgs/tools/admin/pgadmin/expose-setup.py.patch
new file mode 100644
index 000000000000..0b1cb8f95e9f
--- /dev/null
+++ b/pkgs/tools/admin/pgadmin/expose-setup.py.patch
@@ -0,0 +1,65 @@
+From 391433d020da52fba28ad08beb2cc85ffd852044 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Maciej=20Kr=C3=BCger?=
+Date: Wed, 23 Feb 2022 14:40:11 +0100
+Subject: [PATCH] Expose setup.py as pgadmin4-setup
+
+---
+ pkg/pip/setup_pip.py | 5 ++++-
+ web/setup.py | 14 ++++++++------
+ 2 files changed, 12 insertions(+), 7 deletions(-)
+
+diff --git a/pkg/pip/setup_pip.py b/pkg/pip/setup_pip.py
+index 5592d1b04..131eaa1ed 100644
+--- a/pkg/pip/setup_pip.py
++++ b/pkg/pip/setup_pip.py
+@@ -95,7 +95,10 @@ setup(
+ },
+
+ entry_points={
+- 'console_scripts': ['pgadmin4=pgadmin4.pgAdmin4:main'],
++ 'console_scripts': [
++ 'pgadmin4=pgadmin4.pgAdmin4:main',
++ 'pgadmin4-setup=pgadmin4.setup:main'
++ ],
+ },
+
+ )
+diff --git a/web/setup.py b/web/setup.py
+index 5f4257e86..df970f049 100644
+--- a/web/setup.py
++++ b/web/setup.py
+@@ -32,6 +32,10 @@ from pgadmin import create_app
+ from pgadmin.utils import clear_database_servers, dump_database_servers,\
+ load_database_servers
+
++# Configuration settings
++import config
++from pgadmin.model import SCHEMA_VERSION
++from pgadmin.setup import db_upgrade, create_app_data_directory
+
+ def dump_servers(args):
+ """Dump the server groups and servers.
+@@ -139,12 +143,7 @@ def clear_servers():
+ clear_database_servers(load_user, True)
+
+
+-if __name__ == '__main__':
+- # Configuration settings
+- import config
+- from pgadmin.model import SCHEMA_VERSION
+- from pgadmin.setup import db_upgrade, create_app_data_directory
+-
++def main():
+ parser = argparse.ArgumentParser(description='Setup the pgAdmin config DB')
+
+ exp_group = parser.add_argument_group('Dump server config')
+@@ -194,3 +193,6 @@ if __name__ == '__main__':
+ print(str(e))
+ else:
+ setup_db()
++
++if __name__ == '__main__':
++ main()
+--
+2.35.1
+