From cc57b1ced7dba2557984c7671f1e13124c0725f1 Mon Sep 17 00:00:00 2001 From: ibizaman Date: Sat, 4 Nov 2023 20:30:17 -0700 Subject: [PATCH] add postgresql module with tests --- README.md | 2 +- flake.lock | 50 +++++++++++++++++++ flake.nix | 60 +++++++++++++--------- modules/postgresql.nix | 88 +++++++++++++++++++++++++++++++++ test/default.nix | 2 + test/modules/postgresql.nix | 99 +++++++++++++++++++++++++++++++++++++ 6 files changed, 278 insertions(+), 23 deletions(-) create mode 100644 modules/postgresql.nix create mode 100644 test/default.nix create mode 100644 test/modules/postgresql.nix diff --git a/README.md b/README.md index e3db8bb..ad85985 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ First, some common configuration: ```nix imports = [ - selfhostblocks.nixosModules.default + selfhostblocks.nixosModules.x86_64-linux.default sops-nix.nixosModules.default ] diff --git a/flake.lock b/flake.lock index ce3a191..982099d 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,38 @@ { "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nix-flake-tests": { + "locked": { + "lastModified": 1677844186, + "narHash": "sha256-ErJZ/Gs1rxh561CJeWP5bohA2IcTq1rDneu1WT6CVII=", + "owner": "antifuchs", + "repo": "nix-flake-tests", + "rev": "bbd9216bd0f6495bb961a8eb8392b7ef55c67afb", + "type": "github" + }, + "original": { + "owner": "antifuchs", + "repo": "nix-flake-tests", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1687412861, @@ -50,6 +83,8 @@ }, "root": { "inputs": { + "flake-utils": "flake-utils", + "nix-flake-tests": "nix-flake-tests", "nixpkgs": "nixpkgs", "sops-nix": "sops-nix" } @@ -72,6 +107,21 @@ "repo": "sops-nix", "type": "github" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 15a2b5f..881b795 100644 --- a/flake.nix +++ b/flake.nix @@ -4,29 +4,45 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; sops-nix.url = "github:Mic92/sops-nix"; + nix-flake-tests.url = "github:antifuchs/nix-flake-tests"; + flake-utils.url = "github:numtide/flake-utils"; }; - outputs = inputs@{ self, nixpkgs, sops-nix, ... }: { - nixosModules.default = { config, ... }: { - imports = [ - modules/arr.nix - modules/authelia.nix - modules/backup.nix - modules/deluge.nix - modules/davfs.nix - modules/hledger.nix - modules/home-assistant.nix - modules/jellyfin.nix - modules/ldap.nix - modules/monitoring.nix - modules/nextcloud-server.nix - modules/nginx.nix - modules/ssl.nix - modules/tinyproxy.nix - modules/vpn.nix - ]; - }; + outputs = inputs@{ self, nixpkgs, sops-nix, nix-flake-tests, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + nixosModules.default = { config, ... }: { + imports = [ + modules/arr.nix + modules/authelia.nix + modules/backup.nix + modules/deluge.nix + modules/davfs.nix + modules/hledger.nix + modules/home-assistant.nix + modules/jellyfin.nix + modules/ldap.nix + modules/monitoring.nix + modules/nextcloud-server.nix + modules/nginx.nix + modules/postgresql.nix + modules/ssl.nix + modules/tinyproxy.nix + modules/vpn.nix + ]; + }; - # templates.default = {}; Would be nice to have a template - }; + checks = { + tests = nix-flake-tests.lib.check { + inherit pkgs; + tests = import ./test/default.nix { + inherit (pkgs) lib; + }; + }; + }; + # templates.default = {}; Would be nice to have a template + } + ); } diff --git a/modules/postgresql.nix b/modules/postgresql.nix new file mode 100644 index 0000000..edc0710 --- /dev/null +++ b/modules/postgresql.nix @@ -0,0 +1,88 @@ +{ config, lib, ... }: +let + cfg = config.shb.postgresql; +in +{ + options.shb.postgresql = { + tcpIPPort = lib.mkOption { + type = lib.types.nullOr lib.types.port; + description = "Enable TCP/IP connection on given port."; + default = null; + }; + + passwords = lib.mkOption { + type = lib.types.listOf (lib.types.submodule { + options = { + username = lib.mkOption { + type = lib.types.str; + description = "Postgres user name."; + }; + + database = lib.mkOption { + type = lib.types.str; + description = "Postgres database."; + }; + + passwordFile = lib.mkOption { + type = lib.types.str; + description = "Password file for the postgres user."; + }; + }; + }); + default = []; + }; + }; + + config = + let + tcpConfig = port: { + services.postgresql.enableTCPIP = true; + services.postgresql.port = port; + services.postgresql.authentication = lib.mkOverride 10 '' + #type database DBuser origin-address auth-method + # ipv4 + host all all 127.0.0.1/32 trust + # ipv6 + host all all ::1/128 trust + ''; + }; + + dbConfig = passwordCfgs: { + services.postgresql.enable = (builtins.length passwordCfgs) > 0; + services.postgresql.ensureDatabases = map ({ database, ... }: database) passwordCfgs; + services.postgresql.Users = map ({ username, database, ... }: { + name = username; + ensurePermissions = { + "DATABASE ${database}" = "ALL PRIVILEGES"; + }; + ensureClauses = { + "login" = true; + }; + }) passwordCfgs; + }; + + pwdConfig = passwordCfgs: { + systemd.services.postgresql.postStart = + let + script = { username, passwordFile, ... }: '' + $PSQL -tA <<'EOF' + DO $$ + DECLARE password TEXT; + BEGIN + password := trim(both from replace(pg_read_file('${passwordFile}'), E'\n', ''')); + EXECUTE format('ALTER ROLE ${username} WITH PASSWORD '''%s''';', password); + END $$; + EOF + ''; + in + lib.concatStringsSep "\n" (map script passwordCfgs); + }; + in + lib.mkMerge ( + [ + (dbConfig cfg.passwords) + (pwdConfig cfg.passwords) + (lib.mkIf (!(isNull cfg.tcpIPPort)) (tcpConfig cfg.tcpIPPort)) + ] + ); +} diff --git a/test/default.nix b/test/default.nix new file mode 100644 index 0000000..9110865 --- /dev/null +++ b/test/default.nix @@ -0,0 +1,2 @@ +{ lib }: +import ./modules/postgresql.nix { inherit lib; } diff --git a/test/modules/postgresql.nix b/test/modules/postgresql.nix new file mode 100644 index 0000000..ae28808 --- /dev/null +++ b/test/modules/postgresql.nix @@ -0,0 +1,99 @@ +{ lib }: +let + anyOpt = default: lib.mkOption { + type = lib.types.anything; + inherit default; + }; + + testConfig = m: + let + cfg = (lib.evalModules { + modules = [ + { + options = { + services = anyOpt {}; + systemd = anyOpt {}; + }; + } + ../../modules/postgresql.nix + m + ]; + }).config; + in { + inherit (cfg) systemd services; + }; +in +{ + testPostgresNoOptions = { + expected = { + services.postgresql = { + enable = false; + Users = []; + ensureDatabases = []; + }; + systemd.services.postgresql.postStart = ""; + }; + expr = testConfig {}; + }; + + testPostgresOnePassword = { + expected = { + services.postgresql = { + enable = true; + Users = [{ + name = "myuser"; + ensurePermissions = { + "DATABASE mydatabase" = "ALL PRIVILEGES"; + }; + ensureClauses = { + "login" = true; + }; + }]; + ensureDatabases = ["mydatabase"]; + }; + systemd.services.postgresql.postStart = '' + $PSQL -tA <<'EOF' + DO $$ + DECLARE password TEXT; + BEGIN + password := trim(both from replace(pg_read_file('/my/file'), E'\n', ''')); + EXECUTE format('ALTER ROLE myuser WITH PASSWORD '''%s''';', password); + END $$; + EOF + ''; + }; + expr = testConfig { + shb.postgresql.passwords = [ + { + username = "myuser"; + database = "mydatabase"; + passwordFile = "/my/file"; + } + ]; + }; + }; + + testPostgresTCPIP = { + expected = { + services.postgresql = { + enable = false; + Users = []; + ensureDatabases = []; + + enableTCPIP = true; + port = 1234; + authentication = '' + #type database DBuser origin-address auth-method + # ipv4 + host all all 127.0.0.1/32 trust + # ipv6 + host all all ::1/128 trust + ''; + }; + systemd.services.postgresql.postStart = ""; + }; + expr = testConfig { + shb.postgresql.tcpIPPort = 1234; + }; + }; +}