Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

switch databasebackup contract to new contract style #372

Merged
merged 1 commit into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 17 additions & 24 deletions modules/blocks/postgresql.nix
Original file line number Diff line number Diff line change
Expand Up @@ -52,35 +52,23 @@ in

databasebackup = lib.mkOption {
description = ''
Backup configuration. This is an output option.

Use it to initialize a block implementing the "backup" contract.
For example, with the restic block:

```
shb.restic.instances."postgresql" = {
request = config.shb.postgresl.backup;
settings = {
enable = true;
};
};
```
Backup configuration.
'';

type = contracts.databasebackup.request;

default = {
user = "postgres";
type = lib.types.submodule {
options = contracts.databasebackup.mkRequester {
user = "postgres";

backupName = "postgres.sql";
backupName = "postgres.sql";

backupCmd = ''
${pkgs.postgresql}/bin/pg_dumpall | ${pkgs.gzip}/bin/gzip --rsyncable
'';
backupCmd = ''
${pkgs.postgresql}/bin/pg_dumpall | ${pkgs.gzip}/bin/gzip --rsyncable
'';

restoreCmd = ''
${pkgs.gzip}/bin/gunzip | ${pkgs.postgresql}/bin/psql postgres
'';
restoreCmd = ''
${pkgs.gzip}/bin/gunzip | ${pkgs.postgresql}/bin/psql postgres
'';
};
};
};

Expand Down Expand Up @@ -181,5 +169,10 @@ in
(upgrade-script 15 16)
];
}
{
# Seems superfluous but otherwise we get:
# The option `shb.postgresql.databasebackup' was accessed but has no value defined.
shb.postgresql.databasebackup = {};
}
]);
}
36 changes: 6 additions & 30 deletions modules/blocks/restic.nix
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,7 @@ in
description = "Databases to backup following the [database backup contract](./contracts-databasebackup.html).";
default = {};
type = attrsOf (submodule ({ name, config, ... }: {
options = {
request = mkOption {
description = ''
Request part of the backup contract.

Accepts values from a requester.
'';

type = contracts.databasebackup.request;
};

options = contracts.databasebackup.mkProvider {
settings = mkOption {
description = ''
Settings specific to the Restic provider.
Expand All @@ -158,26 +148,12 @@ in
};
};

result = mkOption {
description = ''
Result part of the backup contract.
resultCfg = {
restoreScript = fullName name config.settings.repository;
restoreScriptText = "${fullName "<name>" { path = "path/to/repository"; }}";

Contains the output of the Restic provider.
'';
default = {
restoreScript = fullName name config.settings.repository;
backupService = "${fullName name config.settings.repository}.service";
};
defaultText = {
restoreScriptText = "${fullName "<name>" { path = "path/to/repository"; }}";
backupServiceText = "${fullName "<name>" { path = "path/to/repository"; }}.service";
};
type = contracts.databasebackup.result {
restoreScript = fullName name config.settings.repository;
backupService = "${fullName name config.settings.repository}.service";
restoreScriptText = "${fullName "<name>" { path = "path/to/repository"; }}";
backupServiceText = "${fullName "<name>" { path = "path/to/repository"; }}.service";
};
backupService = "${fullName name config.settings.repository}.service";
backupServiceText = "${fullName "<name>" { path = "path/to/repository"; }}.service";
};
};
}));
Expand Down
209 changes: 141 additions & 68 deletions modules/contracts/databasebackup.nix
Original file line number Diff line number Diff line change
@@ -1,87 +1,160 @@
{ lib, ... }:
{ pkgs, lib, ... }:
let
inherit (lib) mkIf mkOption literalExpression;
inherit (lib.types) anything submodule str;
inherit (lib) mkOption literalExpression literalMD optionalAttrs optionalString;
inherit (lib.types) submodule str;

shblib = pkgs.callPackage ../../lib {};
inherit (shblib) anyNotNull;
in
{
request = submodule {
options = {
user = mkOption {
description = ''
Unix user doing the backups.

This should be an admin user having access to all databases.
'';
type = str;
example = "postgres";
};
mkRequest =
{ user ? "root",
userText ? null,
backupName ? "dump",
backupNameText ? null,
backupCmd ? "",
backupCmdText ? null,
restoreCmd ? "",
restoreCmdText ? null,
}: mkOption {
description = ''
Request part of the backup contract.

backupName = mkOption {
description = "Name of the backup in the repository.";
type = str;
default = "dump";
example = "postgresql.sql";
};
Options set by the requester module
enforcing how to backup files.
'';

backupCmd = mkOption {
description = "Command that produces the database dump on stdout.";
type = str;
example = literalExpression ''
''${pkgs.postgresql}/bin/pg_dumpall | ''${pkgs.gzip}/bin/gzip --rsyncable
'';
default = {
inherit user backupName backupCmd restoreCmd;
};

restoreCmd = mkOption {
description = "Command that reads the database dump on stdin and restores the database.";
type = str;
example = literalExpression ''
''${pkgs.gzip}/bin/gunzip | ''${pkgs.postgresql}/bin/psql postgres
'';
defaultText = optionalString (anyNotNull [
userText
backupNameText
backupCmdText
restoreCmdText
]) (literalMD ''
{
user = ${if userText != null then userText else user};
backupName = ${if backupNameText != null then backupNameText else backupName};
backupCmd = ${if backupCmdText != null then backupCmdText else backupCmd};
restoreCmd = ${if restoreCmdText != null then restoreCmdText else restoreCmd};
}
'');

type = submodule {
options = {
user = mkOption {
description = ''
Unix user doing the backups.

This should be an admin user having access to all databases.
'';
type = str;
example = "postgres";
default = user;
} // optionalAttrs (userText != null) {
defaultText = literalMD userText;
};

backupName = mkOption {
description = "Name of the backup in the repository.";
type = str;
example = "postgresql.sql";
default = backupName;
} // optionalAttrs (backupNameText != null) {
defaultText = literalMD backupNameText;
};

backupCmd = mkOption {
description = "Command that produces the database dump on stdout.";
type = str;
example = literalExpression ''
''${pkgs.postgresql}/bin/pg_dumpall | ''${pkgs.gzip}/bin/gzip --rsyncable
'';
default = backupCmd;
} // optionalAttrs (backupCmdText != null) {
defaultText = literalMD backupCmdText;
};

restoreCmd = mkOption {
description = "Command that reads the database dump on stdin and restores the database.";
type = str;
example = literalExpression ''
''${pkgs.gzip}/bin/gunzip | ''${pkgs.postgresql}/bin/psql postgres
'';
default = restoreCmd;
} // optionalAttrs (restoreCmdText != null) {
defaultText = literalMD restoreCmdText;
};
};
};
};
};


result = {
restoreScript,
mkResult = {
restoreScript ? "restore",
restoreScriptText ? null,
backupService,
backupService ? "backup.service",
backupServiceText ? null,
}: submodule {
options = {
restoreScript = mkOption {
description = ''
Name of script that can restore the database.
One can then list snapshots with:

```bash
$ ${if restoreScriptText != null then restoreScriptText else restoreScript} snapshots
```

And restore the database with:

```bash
$ ${if restoreScriptText != null then restoreScriptText else restoreScript} restore latest
```
'';
type = str;
default = restoreScript;
defaultText = restoreScriptText;
};
}: mkOption {
description = ''
Result part of the backup contract.

Options set by the provider module that indicates the name of the backup and restor scripts.
'';
default = {
inherit restoreScript backupService;
};

defaultText = optionalString (anyNotNull [
restoreScriptText
backupServiceText
]) (literalMD ''
{
restoreScript = ${if restoreScriptText != null then restoreScriptText else restoreScript};
backupService = ${if backupServiceText != null then backupServiceText else backupService};
}
'');

type = submodule {
options = {
restoreScript = mkOption {
description = ''
Name of script that can restore the database.
One can then list snapshots with:

```bash
$ ${if restoreScriptText != null then restoreScriptText else restoreScript} snapshots
```

And restore the database with:

```bash
$ ${if restoreScriptText != null then restoreScriptText else restoreScript} restore latest
```
'';
type = str;
default = restoreScript;
} // optionalAttrs (restoreScriptText != null) {
defaultText = literalMD restoreScriptText;
};

backupService = mkOption {
description = ''
Name of service backing up the database.
backupService = mkOption {
description = ''
Name of service backing up the database.

This script can be ran manually to backup the database:
This script can be ran manually to backup the database:

```bash
$ systemctl start ${if backupServiceText != null then backupServiceText else backupService}
```
'';
type = str;
default = backupService;
defaultText = backupServiceText;
```bash
$ systemctl start ${if backupServiceText != null then backupServiceText else backupService}
```
'';
type = str;
default = backupService;
} // optionalAttrs (backupServiceText != null) {
defaultText = literalMD backupServiceText;
};
};
};
};
Expand Down
21 changes: 2 additions & 19 deletions modules/contracts/databasebackup/dummyModule.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ let
contracts = pkgs.callPackage ../. {};

inherit (lib) mkOption;
inherit (lib.types) anything submodule;
inherit (lib.types) submodule;
in
{
options.shb.contracts.databasebackup = mkOption {
Expand All @@ -23,24 +23,7 @@ in
'';

type = submodule {
options = {
request = mkOption {
description = ''
Options set by a requester module of the database backup contract.
'';
type = contracts.databasebackup.request;
};

result = mkOption {
description = ''
Options set by a provider module of the database backup contract.
'';
type = contracts.databasebackup.result {
restoreScript = "my_restore_script";
backupService = "my_backup_service.service";
};
};
};
options = contracts.databasebackup.contract;
};
};
}
Loading
Loading