Skip to content

Commit

Permalink
add pgAdmin service (#115)
Browse files Browse the repository at this point in the history
  • Loading branch information
conscious-puppet authored Feb 29, 2024
1 parent bdd6dde commit 5e48c5a
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 0 deletions.
18 changes: 18 additions & 0 deletions doc/pgadmin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# pgAdmin

[pgAdmin] is a feature rich Open Source administration and development platform for PostgreSQL.

[pgAdmin]: https://www.pgadmin.org/

## Getting Started

```nix
# In `perSystem.process-compose.<name>`
{
services.pgadmin."pgad1" = {
enable = true;
initialEmail = "[email protected]";
initialPassword = "password";
};
}
```
1 change: 1 addition & 0 deletions doc/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ short-title: Services
- [ ] Zookeeper
- [x] [[grafana]]#
- [X] [[prometheus]]#
- [X] [[pgadmin]]#
- [ ] ...

[gh]: https://github.com/juspay/services-flake
1 change: 1 addition & 0 deletions nix/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ in
./zookeeper.nix
./grafana.nix
./prometheus.nix
./pgadmin.nix
];
}
171 changes: 171 additions & 0 deletions nix/pgadmin.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Based on https://github.com/NixOS/nixpkgs/blob/d53c2037394da6fe98decca417fc8fda64bf2443/nixos/modules/services/admin/pgadmin.nix
{ pkgs, lib, name, config, ... }:
let
inherit (lib) types;

_base = with types; [ int bool str ];
base = with types; oneOf ([ (listOf (oneOf _base)) (attrsOf (oneOf _base)) ] ++ _base);

formatAttrset = attr:
"{${lib.concatStringsSep "\n" (lib.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 "[${lib.concatStringsSep "\n" (map (v: "${formatPyValue v},") value)}]"
else throw "Unrecognized type";

formatPy = attrs:
lib.concatStringsSep "\n" (lib.mapAttrsToList (key: value: "${key} = ${formatPyValue value}") attrs);

pyType = with types; attrsOf (oneOf [ (attrsOf base) (listOf base) base ]);
in
{
options = {
enable = lib.mkEnableOption name;

package = lib.mkPackageOption pkgs "pgadmin4" { };

host = lib.mkOption {
description = lib.mdDoc "Host for pgadmin4 to run on";
type = types.str;
default = "localhost";
};

port = lib.mkOption {
description = lib.mdDoc "Port for pgadmin4 to run on";
type = types.port;
default = 5050;
};


initialEmail = lib.mkOption {
description = "Initial email for the pgAdmin account";
type = types.str;
};

initialPassword = lib.mkOption {
description = "Initial password for the pgAdmin account";
type = types.str;
};

minimumPasswordLength = lib.mkOption {
description = "Minimum length of the password";
type = types.int;
default = 6;
};

dataDir = lib.mkOption {
type = types.str;
default = "./data/${name}";
description = "The pgadmin4 data directory";
};

extraDefaultConfig = lib.mkOption {
type = pyType;
internal = true;
readOnly = true;
default = {
DATA_DIR = {
_expr = "os.getenv('PGADMIN_DATADIR')";
};
DEFAULT_SERVER = config.host;
DEFAULT_SERVER_PORT = config.port;
PASSWORD_LENGTH_MIN = config.minimumPasswordLength;
SERVER_MODE = true;
UPGRADE_CHECK_ENABLED = false;
};
};

extraConfig = lib.mkOption {
type = pyType;
default = { };
description = "Additional config for pgadmin4";
};

outputs.settings = lib.mkOption {
type = types.deferredModule;
internal = true;
readOnly = true;
default = {
processes =
let
pgadminConfig = pkgs.writeTextDir "config_local.py" (
"import os\n" + (formatPy
(lib.recursiveUpdate config.extraDefaultConfig config.extraConfig))
);
in
{
"${name}-init" =
let
setupScript = pkgs.writeShellApplication {
name = "setup-pgadmin";
runtimeInputs = [ config.package ];
text = ''
export PYTHONPATH="${pgadminConfig}"
PGADMIN_DATADIR="$(readlink -m ${config.dataDir})"
mkdir -p "$PGADMIN_DATADIR"
export PGADMIN_DATADIR
PGADMIN_SETUP_EMAIL=${lib.escapeShellArg config.initialEmail}
export PGADMIN_SETUP_EMAIL
PGADMIN_SETUP_PASSWORD=${lib.escapeShellArg config.initialPassword}
export PGADMIN_SETUP_PASSWORD
if [ -f ${config.package}/bin/.pgadmin4-setup-wrapped ]; then
# pgadmin-7.5 has .pgadmin4-setup-wrapped
${config.package}/bin/.pgadmin4-setup-wrapped setup
else
# pgadmin-8.2 has .pgadmin4-cli-wrapped
${config.package}/bin/.pgadmin4-cli-wrapped setup
fi
'';
};
in
{
command = setupScript;
namespace = name;
};

"${name}" =
let
startScript = pkgs.writeShellApplication {
name = "start-pgadmin";
runtimeInputs = [ config.package ];
text = ''
export PYTHONPATH="${pgadminConfig}"
PGADMIN_DATADIR="$(readlink -m ${config.dataDir})"
export PGADMIN_DATADIR
PGADMIN_SETUP_EMAIL=${lib.escapeShellArg config.initialEmail}
export PGADMIN_SETUP_EMAIL
PGADMIN_SETUP_PASSWORD=${lib.escapeShellArg config.initialPassword}
export PGADMIN_SETUP_PASSWORD
${config.package}/bin/.pgadmin4-wrapped
'';
};
in
{
command = startScript;
readiness_probe = {
http_get = {
host = config.host;
port = config.port;
path = "/misc/ping";
};
initial_delay_seconds = 2;
period_seconds = 10;
timeout_seconds = 4;
success_threshold = 1;
failure_threshold = 5;
};
namespace = name;
depends_on."${name}-init".condition = "process_completed_successfully";
# https://github.com/F1bonacc1/process-compose#-auto-restart-if-not-healthy
availability.restart = "on_failure";
};
};
};
};
};
}
23 changes: 23 additions & 0 deletions nix/pgadmin_test.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{ pkgs, config, ... }: {

services.pgadmin."pgad1" = {
enable = true;
initialEmail = "[email protected]";
initialPassword = "password";
};

settings.processes.test =
let
cfg = config.services.pgadmin."pgad1";
in
{
command = pkgs.writeShellApplication {
runtimeInputs = [ cfg.package pkgs.curl pkgs.gnugrep ];
text = ''
curl http://localhost:5050/misc/ping | grep "PING"
'';
name = "pgadmin-test";
};
depends_on."pgad1".condition = "process_healthy";
};
}
7 changes: 7 additions & 0 deletions test/flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
inherit system;
# Required for elastic search
config.allowUnfree = true;
overlays = [
(final: prev: {
# Because tests are failing on darwin: https://github.com/juspay/services-flake/pull/115#issuecomment-1970467684
pgadmin4 = prev.pgadmin4.overrideAttrs (_: { doInstallCheck = false; });
})
];
};
process-compose =
let
Expand Down Expand Up @@ -48,6 +54,7 @@
"${inputs.services-flake}/nix/zookeeper_test.nix"
"${inputs.services-flake}/nix/grafana_test.nix"
"${inputs.services-flake}/nix/prometheus_test.nix"
"${inputs.services-flake}/nix/pgadmin_test.nix"
]);
};
};
Expand Down

0 comments on commit 5e48c5a

Please sign in to comment.