update documentation
This commit is contained in:
parent
64d337e1a6
commit
07f6de7409
15 changed files with 419 additions and 138 deletions
|
|
@ -34,6 +34,10 @@ Not all blocks are yet documented. You can find all available blocks [in the rep
|
||||||
modules/blocks/ssl/docs/default.md
|
modules/blocks/ssl/docs/default.md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```{=include=} chapters html:into-file=//blocks-postgresql.html
|
||||||
|
modules/blocks/postgresql/docs/default.md
|
||||||
|
```
|
||||||
|
|
||||||
```{=include=} chapters html:into-file=//blocks-restic.html
|
```{=include=} chapters html:into-file=//blocks-restic.html
|
||||||
modules/blocks/restic/docs/default.md
|
modules/blocks/restic/docs/default.md
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,13 @@ as possible, reducing the quite thick layer that it is now.
|
||||||
Provided contracts are:
|
Provided contracts are:
|
||||||
|
|
||||||
- [SSL generator contract](contracts-ssl.html) to generate SSL certificates.
|
- [SSL generator contract](contracts-ssl.html) to generate SSL certificates.
|
||||||
Two implementations are provided: self-signed and Let's Encrypt.
|
Two providers are implemented: self-signed and Let's Encrypt.
|
||||||
- [Backup contract](contracts-backup.html) to backup directories.
|
- [Backup contract](contracts-backup.html) to backup directories.
|
||||||
This contract allows to backup multiple times the same directories for extra protection.
|
One provider is implemented: Restic.
|
||||||
|
- [Database Backup contract](contracts-databasebackup.html) to backup database dumps.
|
||||||
|
One provider is implemented: Restic.
|
||||||
- [Secret contract](contracts-secret.html) to provide secrets that are deployed outside of the Nix store.
|
- [Secret contract](contracts-secret.html) to provide secrets that are deployed outside of the Nix store.
|
||||||
|
One provider is implemented: SOPS.
|
||||||
|
|
||||||
```{=include=} chapters html:into-file=//contracts-ssl.html
|
```{=include=} chapters html:into-file=//contracts-ssl.html
|
||||||
modules/contracts/ssl/docs/default.md
|
modules/contracts/ssl/docs/default.md
|
||||||
|
|
@ -33,6 +36,10 @@ modules/contracts/ssl/docs/default.md
|
||||||
modules/contracts/backup/docs/default.md
|
modules/contracts/backup/docs/default.md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```{=include=} chapters html:into-file=//contracts-databasebackup.html
|
||||||
|
modules/contracts/databasebackup/docs/default.md
|
||||||
|
```
|
||||||
|
|
||||||
```{=include=} chapters html:into-file=//contracts-secret.html
|
```{=include=} chapters html:into-file=//contracts-secret.html
|
||||||
modules/contracts/secret/docs/default.md
|
modules/contracts/secret/docs/default.md
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -132,13 +132,15 @@ in stdenv.mkDerivation {
|
||||||
'@OPTIONS_JSON@' \
|
'@OPTIONS_JSON@' \
|
||||||
${individualModuleOptionsDocs [ ../modules/blocks/ssl.nix ]}/share/doc/nixos/options.json
|
${individualModuleOptionsDocs [ ../modules/blocks/ssl.nix ]}/share/doc/nixos/options.json
|
||||||
|
|
||||||
|
substituteInPlace ./modules/blocks/postgresql/docs/default.md \
|
||||||
|
--replace \
|
||||||
|
'@OPTIONS_JSON@' \
|
||||||
|
${individualModuleOptionsDocs [ ../modules/blocks/postgresql.nix ]}/share/doc/nixos/options.json
|
||||||
|
|
||||||
substituteInPlace ./modules/blocks/restic/docs/default.md \
|
substituteInPlace ./modules/blocks/restic/docs/default.md \
|
||||||
--replace \
|
--replace \
|
||||||
'@OPTIONS_JSON@' \
|
'@OPTIONS_JSON@' \
|
||||||
${individualModuleOptionsDocs [
|
${individualModuleOptionsDocs [ ../modules/blocks/restic.nix ]}/share/doc/nixos/options.json
|
||||||
../modules/blocks/restic.nix
|
|
||||||
../modules/blocks/restic/dummyModule.nix
|
|
||||||
]}/share/doc/nixos/options.json
|
|
||||||
|
|
||||||
substituteInPlace ./modules/services/nextcloud-server/docs/default.md \
|
substituteInPlace ./modules/services/nextcloud-server/docs/default.md \
|
||||||
--replace \
|
--replace \
|
||||||
|
|
@ -163,6 +165,11 @@ in stdenv.mkDerivation {
|
||||||
'@OPTIONS_JSON@' \
|
'@OPTIONS_JSON@' \
|
||||||
${individualModuleOptionsDocs [ ../modules/contracts/backup/dummyModule.nix ]}/share/doc/nixos/options.json
|
${individualModuleOptionsDocs [ ../modules/contracts/backup/dummyModule.nix ]}/share/doc/nixos/options.json
|
||||||
|
|
||||||
|
substituteInPlace ./modules/contracts/databasebackup/docs/default.md \
|
||||||
|
--replace \
|
||||||
|
'@OPTIONS_JSON@' \
|
||||||
|
${individualModuleOptionsDocs [ ../modules/contracts/databasebackup/dummyModule.nix ]}/share/doc/nixos/options.json
|
||||||
|
|
||||||
substituteInPlace ./modules/contracts/secret/docs/default.md \
|
substituteInPlace ./modules/contracts/secret/docs/default.md \
|
||||||
--replace \
|
--replace \
|
||||||
'@OPTIONS_JSON@' \
|
'@OPTIONS_JSON@' \
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,7 @@ in
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
backup = lib.mkOption {
|
databasebackup = lib.mkOption {
|
||||||
type = contracts.databasebackup.request;
|
|
||||||
description = ''
|
description = ''
|
||||||
Backup configuration. This is an output option.
|
Backup configuration. This is an output option.
|
||||||
|
|
||||||
|
|
@ -67,7 +66,9 @@ in
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
'';
|
'';
|
||||||
readOnly = true;
|
|
||||||
|
type = contracts.databasebackup.requestType;
|
||||||
|
|
||||||
default = {
|
default = {
|
||||||
user = "postgres";
|
user = "postgres";
|
||||||
|
|
||||||
|
|
|
||||||
39
modules/blocks/postgresql/docs/default.md
Normal file
39
modules/blocks/postgresql/docs/default.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# PostgreSQL Block {#blocks-postgresql}
|
||||||
|
|
||||||
|
Defined in [`/modules/blocks/postgresql.nix`](@REPO@/modules/blocks/postgresql.nix).
|
||||||
|
|
||||||
|
This block sets up a [PostgreSQL][] database.
|
||||||
|
|
||||||
|
[postgresql]: https://www.postgresql.org/
|
||||||
|
|
||||||
|
## Tests {#blocks-postgresql-tests}
|
||||||
|
|
||||||
|
Specific integration tests are defined in [`/test/blocks/postgresql.nix`](@REPO@/test/blocks/postgresql.nix).
|
||||||
|
|
||||||
|
## Database Backup Requester Contracts {#blocks-postgresql-contract-databasebackup}
|
||||||
|
|
||||||
|
This block can be backed up using the [database backup](contracts-databasebackup.html) contract.
|
||||||
|
|
||||||
|
Contract integration tests are defined in [`/test/contracts/databasebackup.nix`](@REPO@/test/contracts/databasebackup.nix).
|
||||||
|
|
||||||
|
### Backing up All Databases {#blocks-postgresql-contract-databasebackup-all}
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
my.backup.provider."postgresql" = {
|
||||||
|
request = config.shb.postgresql.databasebackup;
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
// Specific options for the backup provider.
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options Reference {#blocks-postgresql-options}
|
||||||
|
|
||||||
|
```{=include=} options
|
||||||
|
id-prefix: blocks-postgresql-options-
|
||||||
|
list-id: selfhostblocks-block-postgresql-options
|
||||||
|
source: @OPTIONS_JSON@
|
||||||
|
```
|
||||||
|
|
@ -10,9 +10,13 @@ let
|
||||||
inherit (lib) generators hasPrefix mkIf nameValuePair optionalAttrs removePrefix;
|
inherit (lib) generators hasPrefix mkIf nameValuePair optionalAttrs removePrefix;
|
||||||
inherit (lib.types) attrsOf enum int ints listOf oneOf nonEmptyListOf nonEmptyStr nullOr path str submodule;
|
inherit (lib.types) attrsOf enum int ints listOf oneOf nonEmptyListOf nonEmptyStr nullOr path str submodule;
|
||||||
|
|
||||||
commonOptions = submodule {
|
commonOptions = {
|
||||||
options = {
|
enable = mkEnableOption ''
|
||||||
enable = mkEnableOption "shb restic. A disabled instance will not backup data anymore but still provides the helper tool to introspect and rollback snapshots";
|
this backup intance.
|
||||||
|
|
||||||
|
A disabled instance will not backup data anymore
|
||||||
|
but still provides the helper tool to restore snapshots
|
||||||
|
'';
|
||||||
|
|
||||||
passphraseFile = mkOption {
|
passphraseFile = mkOption {
|
||||||
description = "Encryption key for the backups.";
|
description = "Encryption key for the backups.";
|
||||||
|
|
@ -40,8 +44,8 @@ let
|
||||||
'';
|
'';
|
||||||
example = literalExpression ''
|
example = literalExpression ''
|
||||||
{
|
{
|
||||||
AWS_ACCESS_KEY_ID = <path/to/secret>;
|
AWS_ACCESS_KEY_ID.source = <path/to/secret>;
|
||||||
AWS_SECRET_ACCESS_KEY = <path/to/secret>;
|
AWS_SECRET_ACCESS_KEY.source = <path/to/secret>;
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
@ -89,7 +93,6 @@ let
|
||||||
example = 8000;
|
example = 8000;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
repoSlugName = name: builtins.replaceStrings ["/" ":"] ["_" "_"] (removePrefix "/" name);
|
repoSlugName = name: builtins.replaceStrings ["/" ":"] ["_" "_"] (removePrefix "/" name);
|
||||||
backupName = name: repository: "${name}_${repoSlugName repository.path}";
|
backupName = name: repository: "${name}_${repoSlugName repository.path}";
|
||||||
|
|
@ -103,43 +106,89 @@ in
|
||||||
type = attrsOf (submodule ({ name, options, ... }: {
|
type = attrsOf (submodule ({ name, options, ... }: {
|
||||||
options = {
|
options = {
|
||||||
request = mkOption {
|
request = mkOption {
|
||||||
|
description = ''
|
||||||
|
Request part of the backup contract.
|
||||||
|
|
||||||
|
Accepts values from a requester.
|
||||||
|
'';
|
||||||
|
|
||||||
type = contracts.backup.request;
|
type = contracts.backup.request;
|
||||||
};
|
};
|
||||||
|
|
||||||
settings = mkOption {
|
settings = mkOption {
|
||||||
type = commonOptions;
|
description = ''
|
||||||
|
Settings specific to the Restic provider.
|
||||||
|
'';
|
||||||
|
|
||||||
|
type = submodule {
|
||||||
|
options = commonOptions;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
result = mkOption {
|
result = mkOption {
|
||||||
type = contracts.databasebackup.result;
|
description = ''
|
||||||
|
Result part of the backup contract.
|
||||||
|
|
||||||
|
Contains the output of the Restic provider.
|
||||||
|
'';
|
||||||
|
type = lib.types.anything; # contracts.databasebackup.result;
|
||||||
default = {
|
default = {
|
||||||
restoreScript = fullName name options.settings.value.repository;
|
restoreScript = fullName name options.settings.value.repository;
|
||||||
backupService = "${fullName name options.settings.value.repository}.service";
|
backupService = "${fullName name options.settings.value.repository}.service";
|
||||||
};
|
};
|
||||||
|
defaultText = literalExpression ''
|
||||||
|
{
|
||||||
|
restoreScript = "${fullName "<name>" { path = "path/to/repository"; }}";
|
||||||
|
backupService = "${fullName "<name>" { path = "path/to/repository"; }}.service";
|
||||||
|
}
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
databases = mkOption {
|
databases = mkOption {
|
||||||
description = "Each item is backing up some database to one repository.";
|
description = "Databases to backup following the database backup contract.";
|
||||||
default = {};
|
default = {};
|
||||||
type = attrsOf (submodule ({ name, options, ... }: {
|
type = attrsOf (submodule ({ name, options, ... }: {
|
||||||
options = {
|
options = {
|
||||||
request = mkOption {
|
request = mkOption {
|
||||||
type = contracts.databasebackup.request;
|
description = ''
|
||||||
|
Request part of the backup contract.
|
||||||
|
|
||||||
|
Accepts values from a requester.
|
||||||
|
'';
|
||||||
|
|
||||||
|
type = contracts.databasebackup.requestType;
|
||||||
};
|
};
|
||||||
|
|
||||||
settings = mkOption {
|
settings = mkOption {
|
||||||
type = commonOptions;
|
description = ''
|
||||||
|
Settings specific to the Restic provider.
|
||||||
|
'';
|
||||||
|
|
||||||
|
type = submodule {
|
||||||
|
options = commonOptions;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
result = mkOption {
|
result = mkOption {
|
||||||
type = contracts.databasebackup.result;
|
description = ''
|
||||||
|
Result part of the backup contract.
|
||||||
|
|
||||||
|
Contains the output of the Restic provider.
|
||||||
|
'';
|
||||||
|
type = contracts.databasebackup.resultType;
|
||||||
default = {
|
default = {
|
||||||
restoreScript = fullName name options.settings.value.repository;
|
restoreScript = fullName name options.settings.value.repository;
|
||||||
backupService = "${fullName name (lib.debug.traceValSeqN 2 (lib.debug.traceValSeqN 2 (lib.debug.traceValSeqN 2 options).settings).value).repository}.service";
|
backupService = "${fullName name options.settings.value.repository}.service";
|
||||||
};
|
};
|
||||||
|
defaultText = literalExpression ''
|
||||||
|
{
|
||||||
|
restoreScript = "${fullName "<name>" { path = "path/to/repository"; }}";
|
||||||
|
backupService = "${fullName "<name>" { path = "path/to/repository"; }}.service";
|
||||||
|
}
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,17 @@ This block sets up a backup job using [Restic][restic].
|
||||||
|
|
||||||
[restic]: https://restic.net/
|
[restic]: https://restic.net/
|
||||||
|
|
||||||
## Contract {#blocks-restic-features}
|
## Tests {#blocks-restic-tests}
|
||||||
|
|
||||||
This block implements the [backup](contracts-backup.html) contract.
|
Specific integration tests are defined in [`/test/blocks/restic.nix`](@REPO@/test/blocks/restic.nix).
|
||||||
|
|
||||||
Integration tests are defined in [`/test/blocks/restic.nix`](@REPO@/test/blocks/restic.nix).
|
## Provider Contracts {#blocks-restic-contract-provider}
|
||||||
|
|
||||||
## Usage {#blocks-backup-usage}
|
This block implements the [backup](contracts-backup.html) and [database backup](contracts-databasebackup.html) contracts.
|
||||||
|
|
||||||
### One folder backed up to mounted hard drives {#blocks-backup-usage-one}
|
Contract integration tests are defined in [`/test/contracts/backup.nix`](@REPO@/test/contracts/backup.nix).
|
||||||
|
|
||||||
|
### One folder backed up to mounted hard drives {#blocks-restic-contract-provider-one}
|
||||||
|
|
||||||
The following snippet shows how to configure
|
The following snippet shows how to configure
|
||||||
the backup of 1 folder to 1 repository.
|
the backup of 1 folder to 1 repository.
|
||||||
|
|
@ -54,7 +56,7 @@ shb.restic.instances.myservice = {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### One folder backed up to S3 {#blocks-restic-usage-remote}
|
### One folder backed up to S3 {#blocks-restic-contract-provider-remote}
|
||||||
|
|
||||||
Here we will only highlight the differences with the previous configuration.
|
Here we will only highlight the differences with the previous configuration.
|
||||||
|
|
||||||
|
|
@ -79,7 +81,7 @@ This assumes you have access to such a remote S3 store, for example by using [Ba
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Secrets {#blocks-restic-secrets}
|
## Secrets {#blocks-restic-secrets}
|
||||||
|
|
||||||
To be secure, the secrets should deployed out of band, otherwise they will be world-readable in the nix store.
|
To be secure, the secrets should deployed out of band, otherwise they will be world-readable in the nix store.
|
||||||
|
|
||||||
|
|
@ -119,7 +121,7 @@ sops.secrets."backup/b2/secret_access_key" = {
|
||||||
Pay attention that the owner must be the `myservice` user, the one owning the files to be backed up.
|
Pay attention that the owner must be the `myservice` user, the one owning the files to be backed up.
|
||||||
A `secrets` contract is in progress that will allow one to not care about such details.
|
A `secrets` contract is in progress that will allow one to not care about such details.
|
||||||
|
|
||||||
### Multiple directories to multiple destinations {#blocks-restic-usage-multiple}
|
## Multiple directories to multiple destinations {#blocks-restic-usage-multiple}
|
||||||
|
|
||||||
The following snippet shows how to configure backup of any number of folders to 3 repositories,
|
The following snippet shows how to configure backup of any number of folders to 3 repositories,
|
||||||
each happening at different times to avoid I/O contention.
|
each happening at different times to avoid I/O contention.
|
||||||
|
|
@ -230,7 +232,7 @@ restic-myfolder2_s3_s3.us-west-000.backblazeb2.com_backups
|
||||||
|
|
||||||
Discovering those is easy thanks to tab-completion.
|
Discovering those is easy thanks to tab-completion.
|
||||||
|
|
||||||
One can then restore a backup with:
|
One can then restore a backup from a given repository with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
restic-myfolder1_srv_pool1_backups restore latest
|
restic-myfolder1_srv_pool1_backups restore latest
|
||||||
|
|
@ -243,7 +245,7 @@ In case something bad happens with a backup, the [official documentation](https:
|
||||||
## Options Reference {#blocks-restic-options}
|
## Options Reference {#blocks-restic-options}
|
||||||
|
|
||||||
```{=include=} options
|
```{=include=} options
|
||||||
id-prefix: blocks-backup-options-
|
id-prefix: blocks-restic-options-
|
||||||
list-id: selfhostblocks-block-backup-options
|
list-id: selfhostblocks-block-restic-options
|
||||||
source: @OPTIONS_JSON@
|
source: @OPTIONS_JSON@
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
{
|
{
|
||||||
config = {
|
|
||||||
shb.restic.databases."<name>".settings = {
|
|
||||||
repository = "";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
request = submodule {
|
request = submodule {
|
||||||
freeformType = anything;
|
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
user = mkOption {
|
user = mkOption {
|
||||||
description = "Unix user doing the backups.";
|
description = "Unix user doing the backups.";
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
# Backup Contract {#backup-contract}
|
# Backup Contract {#contract-backup}
|
||||||
|
|
||||||
This NixOS contract represents a backup job
|
This NixOS contract represents a backup job
|
||||||
that will backup one or more files or directories
|
that will backup one or more files or directories
|
||||||
at a regular schedule.
|
on a regular schedule.
|
||||||
|
|
||||||
It is a contract between a service that has files to be backed up
|
It is a contract between a service that has files to be backed up
|
||||||
and a service that backs up files.
|
and a service that backs up files.
|
||||||
|
|
||||||
## Contract Reference {#backup-contract-options}
|
## Contract Reference {#contract-backup-options}
|
||||||
|
|
||||||
These are all the options that are expected to exist for this contract to be respected.
|
These are all the options that are expected to exist for this contract to be respected.
|
||||||
|
|
||||||
|
|
@ -17,9 +17,11 @@ list-id: selfhostblocks-options
|
||||||
source: @OPTIONS_JSON@
|
source: @OPTIONS_JSON@
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage {#backup-contract-usage}
|
## Usage {#contract-backup-usage}
|
||||||
|
|
||||||
A service that can be backed up will provide a `backup` option.
|
A service that can be backed up will provide a `backup` option.
|
||||||
|
Such a service is a `requester` providing a `request` for a module `provider` of this contract.
|
||||||
|
|
||||||
What this option defines is, from the user perspective - that is _you_ - an implementation detail
|
What this option defines is, from the user perspective - that is _you_ - an implementation detail
|
||||||
but it will at least define what directories to backup,
|
but it will at least define what directories to backup,
|
||||||
the user to backup with
|
the user to backup with
|
||||||
|
|
@ -30,9 +32,8 @@ Here is an example module defining such a `backup` option:
|
||||||
```nix
|
```nix
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
myservice.backup = lib.mkOption {
|
myservice.backup = mkOption {
|
||||||
type = contracts.backup.request;
|
type = contracts.backup.request;
|
||||||
readOnly = true;
|
|
||||||
default = {
|
default = {
|
||||||
user = "myservice";
|
user = "myservice";
|
||||||
sourceDirectories = [
|
sourceDirectories = [
|
||||||
|
|
@ -44,16 +45,18 @@ Here is an example module defining such a `backup` option:
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
As you can see, NixOS modules are a bit abused to make contracts work.
|
|
||||||
Default values are set as well as the `readOnly` attribute to ensure those values stay as defined.
|
|
||||||
|
|
||||||
Now, on the other side we have a service that uses this `backup` option and actually backs up files.
|
Now, on the other side we have a service that uses this `backup` option and actually backs up files.
|
||||||
|
This service is a `provider` of this contract and will provide a `result` option.
|
||||||
|
|
||||||
Let's assume such a module is available under the `backupservice` option
|
Let's assume such a module is available under the `backupservice` option
|
||||||
and that one can create multiple backup instances under `backupservice.instances`.
|
and that one can create multiple backup instances under `backupservice.instances`.
|
||||||
Then, to actually backup the `myservice` service, one would write:
|
Then, to actually backup the `myservice` service, one would write:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
backupservice.instances.myservice = myservice.backup // {
|
backupservice.instances.myservice = {
|
||||||
|
request = myservice.backup;
|
||||||
|
|
||||||
|
settings = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
repository = {
|
repository = {
|
||||||
|
|
@ -61,6 +64,7 @@ backupservice.instances.myservice = myservice.backup // {
|
||||||
};
|
};
|
||||||
|
|
||||||
# ... Other options specific to backupservice like scheduling.
|
# ... Other options specific to backupservice like scheduling.
|
||||||
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -68,28 +72,27 @@ It is advised to backup files to different location, to improve redundancy.
|
||||||
Thanks to using contracts, this can be made easily either with the same `backupservice`:
|
Thanks to using contracts, this can be made easily either with the same `backupservice`:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
backupservice.instances.myservice_2 = myservice.backup // {
|
backupservice.instances.myservice_2 = {
|
||||||
|
request = myservice.backup;
|
||||||
|
|
||||||
|
settings = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
repository = {
|
repository = {
|
||||||
path = "<remote path>";
|
path = "<remote path>";
|
||||||
};
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Or with another module `backupservice_2`!
|
Or with another module `backupservice_2`!
|
||||||
|
|
||||||
## Provided Implementations {#backup-contract-impl}
|
## Providers of the Backup Contract {#contract-backup-providers}
|
||||||
|
|
||||||
An implementation here is a service that understands the `backup` contract
|
|
||||||
and will backup the files accordingly.
|
|
||||||
|
|
||||||
One implementation is provided out of the box:
|
|
||||||
- [Restic block](blocks-restic.html).
|
- [Restic block](blocks-restic.html).
|
||||||
|
- [Borgbackup block](blocks-borgbackup.html) [WIP].
|
||||||
|
|
||||||
A second one based on `borgbackup` is in progress.
|
## Requester Blocks and Services {#contract-backup-requesters}
|
||||||
|
|
||||||
## Services Providing `backup` Option {#backup-contract-services}
|
|
||||||
|
|
||||||
- <!-- [ -->Audiobookshelf<!-- ](services-audiobookshelf.html). --> (no manual yet)
|
- <!-- [ -->Audiobookshelf<!-- ](services-audiobookshelf.html). --> (no manual yet)
|
||||||
- <!-- [ -->Deluge<!--](services-deluge.html). --> (no manual yet)
|
- <!-- [ -->Deluge<!--](services-deluge.html). --> (no manual yet)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
{ pkgs, lib, ... }:
|
{ pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
contracts = pkgs.callPackage ../. {};
|
contracts = pkgs.callPackage ../. {};
|
||||||
|
|
||||||
|
inherit (lib) mkOption;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.shb.contracts.backup = lib.mkOption {
|
options.shb.contracts.backup = mkOption {
|
||||||
description = "Contract for backups.";
|
description = "Contract for backups.";
|
||||||
type = contracts.backup.request;
|
type = contracts.backup.request;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,76 @@
|
||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) mkOption;
|
inherit (lib) mkIf mkOption literalExpression;
|
||||||
inherit (lib.types) anything submodule str;
|
inherit (lib.types) anything submodule str;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
request = submodule {
|
requestType = submodule {
|
||||||
freeformType = anything;
|
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
user = mkOption {
|
user = mkOption {
|
||||||
description = "Unix user doing the backups.";
|
description = "Unix user doing the backups.";
|
||||||
type = str;
|
type = str;
|
||||||
|
example = "postgres";
|
||||||
};
|
};
|
||||||
|
|
||||||
backupFile = mkOption {
|
backupFile = mkOption {
|
||||||
description = "Filename of the backup.";
|
description = "Filename of the backup.";
|
||||||
type = str;
|
type = str;
|
||||||
|
default = "dump";
|
||||||
|
example = "postgresql.sql";
|
||||||
};
|
};
|
||||||
|
|
||||||
backupCmd = mkOption {
|
backupCmd = mkOption {
|
||||||
description = "Command that produces the database dump on stdout.";
|
description = "Command that produces the database dump on stdout.";
|
||||||
type = str;
|
type = str;
|
||||||
|
example = literalExpression ''
|
||||||
|
''${pkgs.postgresql}/bin/pg_dumpall | ''${pkgs.gzip}/bin/gzip --rsyncable
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
restoreCmd = mkOption {
|
restoreCmd = mkOption {
|
||||||
description = "Command that reads the database dump on stdin and restores the database.";
|
description = "Command that reads the database dump on stdin and restores the database.";
|
||||||
type = str;
|
type = str;
|
||||||
|
example = literalExpression ''
|
||||||
|
''${pkgs.gzip}/bin/gunzip | ''${pkgs.postgresql}/bin/psql postgres
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
result = submodule {
|
|
||||||
|
resultType = submodule {
|
||||||
options = {
|
options = {
|
||||||
restoreScript = mkOption {
|
restoreScript = mkOption {
|
||||||
description = "Name of script that can restore the database.";
|
description = ''
|
||||||
|
Name of script that can restore the database.
|
||||||
|
One can then list snapshots with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ my_restore_script snapshots
|
||||||
|
```
|
||||||
|
|
||||||
|
And restore the database with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ my_restore_script restore latest
|
||||||
|
```
|
||||||
|
'';
|
||||||
type = str;
|
type = str;
|
||||||
|
example = "my_restore_script";
|
||||||
};
|
};
|
||||||
|
|
||||||
backupService = mkOption {
|
backupService = mkOption {
|
||||||
description = "Name of service backing up the database.";
|
description = ''
|
||||||
|
Name of service backing up the database.
|
||||||
|
|
||||||
|
This script can be ran manually to backup the database:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ systemctl start my_backup_service
|
||||||
|
```
|
||||||
|
'';
|
||||||
type = str;
|
type = str;
|
||||||
|
example = "my_backup_service.service";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
100
modules/contracts/databasebackup/docs/default.md
Normal file
100
modules/contracts/databasebackup/docs/default.md
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
# Database Backup Contract {#contract-databasebackup}
|
||||||
|
|
||||||
|
This NixOS contract represents a backup job
|
||||||
|
that will backup everything in one database
|
||||||
|
on a regular schedule.
|
||||||
|
|
||||||
|
It is a contract between a service that has database dumps to be backed up
|
||||||
|
and a service that backs up databases dumps.
|
||||||
|
|
||||||
|
## Contract Reference {#contract-databasebackup-options}
|
||||||
|
|
||||||
|
These are all the options that are expected to exist for this contract to be respected.
|
||||||
|
|
||||||
|
```{=include=} options
|
||||||
|
id-prefix: contracts-databasebackup-options-
|
||||||
|
list-id: selfhostblocks-options
|
||||||
|
source: @OPTIONS_JSON@
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage {#contract-databasebackup-usage}
|
||||||
|
|
||||||
|
A database that can be backed up will provide a `databasebackup` option.
|
||||||
|
Such a service is a `requester` providing a `request` for a module `provider` of this contract.
|
||||||
|
|
||||||
|
What this option defines is, from the user perspective - that is _you_ - an implementation detail
|
||||||
|
but it will at least define how to create a database dump,
|
||||||
|
the user to backup with
|
||||||
|
and how to restore from a database dump.
|
||||||
|
|
||||||
|
Here is an example module defining such a `databasebackup` option:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
myservice.databasebackup = mkOption {
|
||||||
|
type = contracts.databasebackup.request;
|
||||||
|
default = {
|
||||||
|
user = "myservice";
|
||||||
|
backupCmd = ''
|
||||||
|
${pkgs.postgresql}/bin/pg_dumpall | ${pkgs.gzip}/bin/gzip --rsyncable
|
||||||
|
'';
|
||||||
|
restoreCmd = ''
|
||||||
|
${pkgs.gzip}/bin/gunzip | ${pkgs.postgresql}/bin/psql postgres
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, on the other side we have a service that uses this `backup` option and actually backs up files.
|
||||||
|
This service is a `provider` of this contract and will provide a `result` option.
|
||||||
|
|
||||||
|
Let's assume such a module is available under the `databasebackupservice` option
|
||||||
|
and that one can create multiple backup instances under `databasebackupservice.instances`.
|
||||||
|
Then, to actually backup the `myservice` service, one would write:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
databasebackupservice.instances.myservice = {
|
||||||
|
request = myservice.databasebackup;
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
repository = {
|
||||||
|
path = "/srv/backup/myservice";
|
||||||
|
};
|
||||||
|
|
||||||
|
# ... Other options specific to backupservice like scheduling.
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
It is advised to backup files to different location, to improve redundancy.
|
||||||
|
Thanks to using contracts, this can be made easily either with the same `databasebackupservice`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
databasebackupservice.instances.myservice_2 = {
|
||||||
|
request = myservice.backup;
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
repository = {
|
||||||
|
path = "<remote path>";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with another module `databasebackupservice_2`!
|
||||||
|
|
||||||
|
## Providers of the Database Backup Contract {#contract-databasebackup-providers}
|
||||||
|
|
||||||
|
- [Restic block](blocks-restic.html).
|
||||||
|
- [Borgbackup block](blocks-borgbackup.html) [WIP].
|
||||||
|
|
||||||
|
## Requester Blocks and Services {#contract-databasebackup-requesters}
|
||||||
|
|
||||||
|
- [PostgreSQL](blocks-postgresql.html#blocks-postgresql-contract-databasebackup).
|
||||||
43
modules/contracts/databasebackup/dummyModule.nix
Normal file
43
modules/contracts/databasebackup/dummyModule.nix
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
contracts = pkgs.callPackage ../. {};
|
||||||
|
|
||||||
|
inherit (lib) mkOption;
|
||||||
|
inherit (lib.types) anything submodule;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.shb.contracts.databasebackup = mkOption {
|
||||||
|
description = ''
|
||||||
|
Contract for database backup between a requester module
|
||||||
|
and a provider module.
|
||||||
|
|
||||||
|
The requester communicates to the provider
|
||||||
|
how to backup the database
|
||||||
|
through the `request` options.
|
||||||
|
|
||||||
|
The provider reads from the `request` options
|
||||||
|
and backs up the database as requested.
|
||||||
|
It communicates to the requester what script is used
|
||||||
|
to backup and restore the database
|
||||||
|
through the `result` options.
|
||||||
|
'';
|
||||||
|
|
||||||
|
type = submodule {
|
||||||
|
options = {
|
||||||
|
request = mkOption {
|
||||||
|
description = ''
|
||||||
|
Options set by a requester module of the database contract.
|
||||||
|
'';
|
||||||
|
type = contracts.databasebackup.requestType;
|
||||||
|
};
|
||||||
|
|
||||||
|
result = mkOption {
|
||||||
|
description = ''
|
||||||
|
Options set by a provider module of the database contract.
|
||||||
|
'';
|
||||||
|
type = contracts.databasebackup.resultType;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ in
|
||||||
imports = ( testLib.baseImports pkgs' ) ++ modules;
|
imports = ( testLib.baseImports pkgs' ) ++ modules;
|
||||||
config = lib.mkMerge [
|
config = lib.mkMerge [
|
||||||
(setAttrByPath providerRoot {
|
(setAttrByPath providerRoot {
|
||||||
request = (getAttrFromPath requesterRoot config).backup;
|
request = (getAttrFromPath requesterRoot config).databasebackup;
|
||||||
settings = settings "/opt/repos/database";
|
settings = settings "/opt/repos/database";
|
||||||
})
|
})
|
||||||
(mkIf (username != "root") {
|
(mkIf (username != "root") {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue