diff --git a/CHANGELOG.md b/CHANGELOG.md index bf078b8..60c1b7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ Template: # Upcoming Release +## Breaking Changes + +- Options `before_backup` and `after_backup` for backup contract have been renamed to + `beforeBackup` and `afterBackup`. + ## Other Changes - Show how to pin Self Host Blocks flake input to a tag. diff --git a/docs/redirects.json b/docs/redirects.json index 0832a34..3e19ac8 100644 --- a/docs/redirects.json +++ b/docs/redirects.json @@ -188,11 +188,11 @@ "blocks-restic-options-shb.restic.instances._name_.request.hooks": [ "blocks-restic.html#blocks-restic-options-shb.restic.instances._name_.request.hooks" ], - "blocks-restic-options-shb.restic.instances._name_.request.hooks.after_backup": [ - "blocks-restic.html#blocks-restic-options-shb.restic.instances._name_.request.hooks.after_backup" + "blocks-restic-options-shb.restic.instances._name_.request.hooks.afterBackup": [ + "blocks-restic.html#blocks-restic-options-shb.restic.instances._name_.request.hooks.afterBackup" ], - "blocks-restic-options-shb.restic.instances._name_.request.hooks.before_backup": [ - "blocks-restic.html#blocks-restic-options-shb.restic.instances._name_.request.hooks.before_backup" + "blocks-restic-options-shb.restic.instances._name_.request.hooks.beforeBackup": [ + "blocks-restic.html#blocks-restic-options-shb.restic.instances._name_.request.hooks.beforeBackup" ], "blocks-restic-options-shb.restic.instances._name_.request.sourceDirectories": [ "blocks-restic.html#blocks-restic-options-shb.restic.instances._name_.request.sourceDirectories" @@ -338,6 +338,9 @@ "blocks-sops-usage-requester": [ "blocks-sops.html#blocks-sops-usage-requester" ], + "blocks-sops-usage-manual": [ + "blocks-sops.html#blocks-sops-usage-manual" + ], "block-ssl": [ "blocks-ssl.html#block-ssl" ], @@ -578,11 +581,11 @@ "contracts-backup-options-shb.contracts.backup.request.hooks": [ "contracts-backup.html#contracts-backup-options-shb.contracts.backup.request.hooks" ], - "contracts-backup-options-shb.contracts.backup.request.hooks.after_backup": [ - "contracts-backup.html#contracts-backup-options-shb.contracts.backup.request.hooks.after_backup" + "contracts-backup-options-shb.contracts.backup.request.hooks.afterBackup": [ + "contracts-backup.html#contracts-backup-options-shb.contracts.backup.request.hooks.afterBackup" ], - "contracts-backup-options-shb.contracts.backup.request.hooks.before_backup": [ - "contracts-backup.html#contracts-backup-options-shb.contracts.backup.request.hooks.before_backup" + "contracts-backup-options-shb.contracts.backup.request.hooks.beforeBackup": [ + "contracts-backup.html#contracts-backup-options-shb.contracts.backup.request.hooks.beforeBackup" ], "contracts-backup-options-shb.contracts.backup.request.sourceDirectories": [ "contracts-backup.html#contracts-backup-options-shb.contracts.backup.request.sourceDirectories" @@ -599,6 +602,9 @@ "contracts-backup-options-shb.contracts.backup.result.restoreScript": [ "contracts-backup.html#contracts-backup-options-shb.contracts.backup.result.restoreScript" ], + "contracts-backup-options-shb.contracts.backup.settings": [ + "contracts-backup.html#contracts-backup-options-shb.contracts.backup.settings" + ], "contracts-databasebackup-options-shb.contracts.databasebackup": [ "contracts-databasebackup.html#contracts-databasebackup-options-shb.contracts.databasebackup" ], @@ -848,23 +854,35 @@ "services-forgejo-options-shb.forgejo.backup": [ "services-forgejo.html#services-forgejo-options-shb.forgejo.backup" ], - "services-forgejo-options-shb.forgejo.backup.excludePatterns": [ - "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.excludePatterns" + "services-forgejo-options-shb.forgejo.backup.request": [ + "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.request" + ], + "services-forgejo-options-shb.forgejo.backup.request.excludePatterns": [ + "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.request.excludePatterns" + ], + "services-forgejo-options-shb.forgejo.backup.request.hooks": [ + "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.request.hooks" ], - "services-forgejo-options-shb.forgejo.backup.hooks": [ - "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.hooks" + "services-forgejo-options-shb.forgejo.backup.request.hooks.afterBackup": [ + "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.request.hooks.afterBackup" ], - "services-forgejo-options-shb.forgejo.backup.hooks.after_backup": [ - "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.hooks.after_backup" + "services-forgejo-options-shb.forgejo.backup.request.hooks.beforeBackup": [ + "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.request.hooks.beforeBackup" ], - "services-forgejo-options-shb.forgejo.backup.hooks.before_backup": [ - "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.hooks.before_backup" + "services-forgejo-options-shb.forgejo.backup.request.sourceDirectories": [ + "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.request.sourceDirectories" ], - "services-forgejo-options-shb.forgejo.backup.sourceDirectories": [ - "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.sourceDirectories" + "services-forgejo-options-shb.forgejo.backup.request.user": [ + "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.request.user" ], - "services-forgejo-options-shb.forgejo.backup.user": [ - "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.user" + "services-forgejo-options-shb.forgejo.backup.result": [ + "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.result" + ], + "services-forgejo-options-shb.forgejo.backup.result.backupService": [ + "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.result.backupService" + ], + "services-forgejo-options-shb.forgejo.backup.result.restoreScript": [ + "services-forgejo.html#services-forgejo-options-shb.forgejo.backup.result.restoreScript" ], "services-forgejo-options-shb.forgejo.databasePassword": [ "services-forgejo.html#services-forgejo-options-shb.forgejo.databasePassword" @@ -1322,23 +1340,35 @@ "services-nextcloudserver-options-shb.nextcloud.backup": [ "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup" ], - "services-nextcloudserver-options-shb.nextcloud.backup.excludePatterns": [ - "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.excludePatterns" + "services-nextcloudserver-options-shb.nextcloud.backup.request": [ + "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.request" + ], + "services-nextcloudserver-options-shb.nextcloud.backup.request.excludePatterns": [ + "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.request.excludePatterns" ], - "services-nextcloudserver-options-shb.nextcloud.backup.hooks": [ - "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.hooks" + "services-nextcloudserver-options-shb.nextcloud.backup.request.hooks": [ + "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.request.hooks" ], - "services-nextcloudserver-options-shb.nextcloud.backup.hooks.after_backup": [ - "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.hooks.after_backup" + "services-nextcloudserver-options-shb.nextcloud.backup.request.hooks.afterBackup": [ + "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.request.hooks.afterBackup" ], - "services-nextcloudserver-options-shb.nextcloud.backup.hooks.before_backup": [ - "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.hooks.before_backup" + "services-nextcloudserver-options-shb.nextcloud.backup.request.hooks.beforeBackup": [ + "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.request.hooks.beforeBackup" ], - "services-nextcloudserver-options-shb.nextcloud.backup.sourceDirectories": [ - "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.sourceDirectories" + "services-nextcloudserver-options-shb.nextcloud.backup.request.sourceDirectories": [ + "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.request.sourceDirectories" ], - "services-nextcloudserver-options-shb.nextcloud.backup.user": [ - "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.user" + "services-nextcloudserver-options-shb.nextcloud.backup.request.user": [ + "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.request.user" + ], + "services-nextcloudserver-options-shb.nextcloud.backup.result": [ + "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.result" + ], + "services-nextcloudserver-options-shb.nextcloud.backup.result.backupService": [ + "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.result.backupService" + ], + "services-nextcloudserver-options-shb.nextcloud.backup.result.restoreScript": [ + "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.backup.result.restoreScript" ], "services-nextcloudserver-options-shb.nextcloud.dataDir": [ "services-nextcloud.html#services-nextcloudserver-options-shb.nextcloud.dataDir" @@ -1472,23 +1502,35 @@ "services-vaultwarden-options-shb.vaultwarden.backup": [ "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup" ], - "services-vaultwarden-options-shb.vaultwarden.backup.excludePatterns": [ - "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.excludePatterns" + "services-vaultwarden-options-shb.vaultwarden.backup.request": [ + "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.request" + ], + "services-vaultwarden-options-shb.vaultwarden.backup.request.excludePatterns": [ + "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.request.excludePatterns" + ], + "services-vaultwarden-options-shb.vaultwarden.backup.request.hooks": [ + "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.request.hooks" + ], + "services-vaultwarden-options-shb.vaultwarden.backup.request.hooks.afterBackup": [ + "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.request.hooks.afterBackup" + ], + "services-vaultwarden-options-shb.vaultwarden.backup.request.hooks.beforeBackup": [ + "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.request.hooks.beforeBackup" ], - "services-vaultwarden-options-shb.vaultwarden.backup.hooks": [ - "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.hooks" + "services-vaultwarden-options-shb.vaultwarden.backup.request.sourceDirectories": [ + "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.request.sourceDirectories" ], - "services-vaultwarden-options-shb.vaultwarden.backup.hooks.after_backup": [ - "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.hooks.after_backup" + "services-vaultwarden-options-shb.vaultwarden.backup.request.user": [ + "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.request.user" ], - "services-vaultwarden-options-shb.vaultwarden.backup.hooks.before_backup": [ - "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.hooks.before_backup" + "services-vaultwarden-options-shb.vaultwarden.backup.result": [ + "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.result" ], - "services-vaultwarden-options-shb.vaultwarden.backup.sourceDirectories": [ - "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.sourceDirectories" + "services-vaultwarden-options-shb.vaultwarden.backup.result.backupService": [ + "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.result.backupService" ], - "services-vaultwarden-options-shb.vaultwarden.backup.user": [ - "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.user" + "services-vaultwarden-options-shb.vaultwarden.backup.result.restoreScript": [ + "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.backup.result.restoreScript" ], "services-vaultwarden-options-shb.vaultwarden.databasePassword": [ "services-vaultwarden.html#services-vaultwarden-options-shb.vaultwarden.databasePassword" diff --git a/lib/default.nix b/lib/default.nix index 9350a52..978628c 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -1,7 +1,7 @@ { pkgs, lib }: let inherit (builtins) isAttrs hasAttr; - inherit (lib) concatMapStringsSep concatStringsSep mapAttrsToList; + inherit (lib) any concatMapStringsSep concatStringsSep mapAttrsToList; in rec { # Replace secrets in a file. @@ -292,4 +292,6 @@ rec { "${concatStringsSep "_" secret.path}:${secret.${sourceField}}"; in map genLoadCredentials allSecrets; + + anyNotNull = any (x: x != null); } diff --git a/modules/blocks/borgbackup.nix b/modules/blocks/borgbackup.nix index ebf0a04..331bab6 100644 --- a/modules/blocks/borgbackup.nix +++ b/modules/blocks/borgbackup.nix @@ -90,13 +90,13 @@ let default = {}; type = lib.types.submodule { options = { - before_backup = lib.mkOption { + beforeBackup = lib.mkOption { description = "Hooks to run before backup"; type = lib.types.listOf lib.types.str; default = []; }; - after_backup = lib.mkOption { + afterBackup = lib.mkOption { description = "Hooks to run after backup"; type = lib.types.listOf lib.types.str; default = []; @@ -279,11 +279,11 @@ in lib.attrsets.mapAttrsToList mkCheck instance.consistency; # hooks = lib.mkMerge [ - # lib.optionalAttrs (builtins.length instance.hooks.before_backup > 0) { - # inherit (instance.hooks) before_backup; + # lib.optionalAttrs (builtins.length instance.hooks.beforeBackup > 0) { + # inherit (instance.hooks) beforeBackup; # } - # lib.optionalAttrs (builtins.length instance.hooks.after_backup > 0) { - # inherit (instance.hooks) after_backup; + # lib.optionalAttrs (builtins.length instance.hooks.afterBackup > 0) { + # inherit (instance.hooks) afterBackup; # } # ]; }; diff --git a/modules/blocks/ldap.nix b/modules/blocks/ldap.nix index ef2ed61..bda49e4 100644 --- a/modules/blocks/ldap.nix +++ b/modules/blocks/ldap.nix @@ -103,31 +103,19 @@ in }; backup = lib.mkOption { - type = contracts.backup.request; 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."lldap" = { - request = config.shb.lldap.backup; - settings = { - enable = true; - }; - }; - ``` + Backup configuration. ''; - readOnly = true; - default = { - # TODO: is there a workaround that avoid needing to use root? - # root because otherwise we cannot access the private StateDiretory - user = "root"; - # /private because the systemd service uses DynamicUser=true - sourceDirectories = [ - "/var/lib/private/lldap" - ]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + # TODO: is there a workaround that avoid needing to use root? + # root because otherwise we cannot access the private StateDiretory + user = "root"; + # /private because the systemd service uses DynamicUser=true + sourceDirectories = [ + "/var/lib/private/lldap" + ]; + }; }; }; }; diff --git a/modules/blocks/restic.nix b/modules/blocks/restic.nix index 0f51fb5..1b52d24 100644 --- a/modules/blocks/restic.nix +++ b/modules/blocks/restic.nix @@ -111,17 +111,7 @@ in description = "Files to backup following the [backup contract](./contracts-backup.html)."; default = {}; type = attrsOf (submodule ({ name, config, ... }: { - options = { - request = mkOption { - description = '' - Request part of the backup contract. - - Accepts values from a requester. - ''; - - type = contracts.backup.request; - }; - + options = contracts.backup.mkProvider { settings = mkOption { description = '' Settings specific to the Restic provider. @@ -132,26 +122,11 @@ in }; }; - result = mkOption { - description = '' - Result part of the backup contract. - - Contains the output of the Restic provider. - ''; - default = { - restoreScript = fullName name config.settings.repository; - backupService = "${fullName name config.settings.repository}.service"; - }; - defaultText = { - restoreScriptText = "${fullName "" { path = "path/to/repository"; }}"; - backupServiceText = "${fullName "" { path = "path/to/repository"; }}.service"; - }; - type = contracts.backup.result { - restoreScript = fullName name config.settings.repository; - backupService = "${fullName name config.settings.repository}.service"; - restoreScriptText = "${fullName "" { path = "path/to/repository"; }}"; - backupServiceText = "${fullName "" { path = "path/to/repository"; }}.service"; - }; + resultCfg = { + restoreScript = fullName name config.settings.repository; + restoreScriptText = "${fullName "" { path = "path/to/repository"; }}"; + backupService = "${fullName name config.settings.repository}.service"; + backupServiceText = "${fullName "" { path = "path/to/repository"; }}.service"; }; }; })); @@ -272,9 +247,9 @@ in "--${builtins.replaceStrings ["_"] ["-"] name} ${builtins.toString value}" ) instance.settings.retention; - backupPrepareCommand = concatStringsSep "\n" instance.request.hooks.before_backup; + backupPrepareCommand = concatStringsSep "\n" instance.request.hooks.beforeBackup; - backupCleanupCommand = concatStringsSep "\n" instance.request.hooks.after_backup; + backupCleanupCommand = concatStringsSep "\n" instance.request.hooks.afterBackup; extraBackupArgs = (optionals (instance.settings.limitUploadKiBs != null) [ diff --git a/modules/contracts/backup.nix b/modules/contracts/backup.nix index 1422dbb..b603ccc 100644 --- a/modules/contracts/backup.nix +++ b/modules/contracts/backup.nix @@ -1,91 +1,170 @@ -{ lib, ... }: +{ pkgs, lib, ... }: let - inherit (lib) mkOption; + inherit (lib) concatStringsSep literalMD mkOption optionalAttrs optionalString; inherit (lib.types) listOf nonEmptyListOf submodule str; + + shblib = pkgs.callPackage ../../lib {}; + inherit (shblib) anyNotNull; in { - request = submodule { - options = { - user = mkOption { - description = '' - Unix user doing the backups. - ''; - type = str; - }; + mkRequest = + { user ? "", + userText ? null, + sourceDirectories ? [ "/var/lib/example" ], + sourceDirectoriesText ? null, + excludePatterns ? [], + excludePatternsText ? null, + beforeBackup ? [], + beforeBackupText ? null, + afterBackup ? [], + afterBackupText ? null, + }: mkOption { + description = '' + Request part of the backup contract. + + Options set by the requester module + enforcing how to backup files. + ''; - sourceDirectories = mkOption { - description = "Directories to backup."; - type = nonEmptyListOf str; + default = { + inherit user sourceDirectories excludePatterns; + inherit beforeBackup afterBackup; }; - excludePatterns = mkOption { - description = "File patterns to exclude."; - type = listOf str; - default = []; + defaultText = optionalString (anyNotNull [ + userText + sourceDirectoriesText + excludePatternsText + beforeBackupText + afterBackupText + ]) (literalMD '' + { + user = ${if userText != null then userText else user}; + sourceDirectories = ${if sourceDirectoriesText != null then sourceDirectoriesText else "[ " + concatStringsSep " " sourceDirectories + " ]"}; + excludePatterns = ${if excludePatternsText != null then excludePatternsText else "[ " + concatStringsSep " " excludePatterns + " ]"}; + hooks.beforeBackup = ${if beforeBackupText != null then beforeBackupText else "[ " + concatStringsSep " " beforeBackup + " ]"}; + hooks.afterBackup = ${if afterBackupText != null then afterBackupText else "[ " + concatStringsSep " " afterBackup + " ]"}; }; + ''); - hooks = mkOption { - description = "Hooks to run around the backup."; - default = {}; - type = submodule { - options = { - before_backup = mkOption { - description = "Hooks to run before backup."; - type = listOf str; - default = []; - }; + type = submodule { + options = { + user = mkOption { + description = '' + Unix user doing the backups. + ''; + type = str; + default = user; + } // optionalAttrs (userText != null) { + defaultText = literalMD userText; + }; - after_backup = mkOption { - description = "Hooks to run after backup."; - type = listOf str; - default = []; + sourceDirectories = mkOption { + description = "Directories to backup."; + type = nonEmptyListOf str; + default = sourceDirectories; + } // optionalAttrs (sourceDirectoriesText != null) { + defaultText = literalMD sourceDirectoriesText; + }; + + excludePatterns = mkOption { + description = "File patterns to exclude."; + type = listOf str; + default = excludePatterns; + } // optionalAttrs (excludePatternsText != null) { + defaultText = literalMD excludePatternsText; + }; + + hooks = mkOption { + description = "Hooks to run around the backup."; + default = {}; + type = submodule { + options = { + beforeBackup = mkOption { + description = "Hooks to run before backup."; + type = listOf str; + default = beforeBackup; + } // optionalAttrs (beforeBackupText != null) { + defaultText = literalMD beforeBackupText; + }; + + afterBackup = mkOption { + description = "Hooks to run after backup."; + type = listOf str; + default = afterBackup; + } // optionalAttrs (afterBackupText != null) { + defaultText = literalMD afterBackupText; + }; + }; }; }; }; }; }; - }; - 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: - backupService = mkOption { - description = '' - Name of service backing up the database. + ```bash + $ ${if restoreScriptText != null then restoreScriptText else restoreScript} snapshots + ``` - This script can be ran manually to backup the database: + And restore the database with: - ```bash - $ systemctl start ${if backupServiceText != null then backupServiceText else backupService} - ``` - ''; - type = str; - default = backupService; - defaultText = backupServiceText; + ```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. + + This script can be ran manually to backup the database: + + ```bash + $ systemctl start ${if backupServiceText != null then backupServiceText else backupService} + ``` + ''; + type = str; + default = backupService; + } // optionalAttrs (backupServiceText != null) { + defaultText = literalMD backupServiceText; + }; }; }; }; diff --git a/modules/contracts/backup/dummyModule.nix b/modules/contracts/backup/dummyModule.nix index 2d77984..05d7230 100644 --- a/modules/contracts/backup/dummyModule.nix +++ b/modules/contracts/backup/dummyModule.nix @@ -23,24 +23,7 @@ in ''; type = submodule { - options = { - request = mkOption { - description = '' - Options set by a requester module of the backup contract. - ''; - type = contracts.backup.request; - }; - - result = mkOption { - description = '' - Options set by a provider module of the backup contract. - ''; - type = contracts.backup.result { - restoreScript = "my_restore_script"; - backupService = "my_backup_service.service"; - }; - }; - }; + options = contracts.backup.contract; }; }; } diff --git a/modules/contracts/default.nix b/modules/contracts/default.nix index 46140d4..2023e35 100644 --- a/modules/contracts/default.nix +++ b/modules/contracts/default.nix @@ -45,10 +45,8 @@ let }; in { - inherit mkContractFunctions; - databasebackup = import ./databasebackup.nix { inherit lib; }; - backup = import ./backup.nix { inherit lib; }; + backup = importContract ./backup.nix; mount = import ./mount.nix { inherit lib; }; secret = importContract ./secret.nix; ssl = import ./ssl.nix { inherit lib; }; diff --git a/modules/contracts/secret.nix b/modules/contracts/secret.nix index ef3ac6c..72af943 100644 --- a/modules/contracts/secret.nix +++ b/modules/contracts/secret.nix @@ -1,14 +1,19 @@ -{ lib, ... }: +{ pkgs, lib, ... }: let inherit (lib) concatStringsSep literalMD mkOption optionalAttrs optionalString; inherit (lib.types) listOf submodule str; + + shblib = pkgs.callPackage ../../lib {}; + inherit (shblib) anyNotNull; in { mkRequest = { mode ? "0400", + modeText ? null, owner ? "root", ownerText ? null, group ? "root", + groupText ? null, restartUnits ? [], restartUnitsText ? null, }: mkOption { @@ -23,11 +28,16 @@ in inherit mode owner group restartUnits; }; - defaultText = optionalString (ownerText != null || restartUnitsText != null) (literalMD '' + defaultText = optionalString (anyNotNull [ + modeText + ownerText + groupText + restartUnitsText + ]) (literalMD '' { - mode = ${mode}; + mode = ${if modeText != null then modeText else mode}; owner = ${if ownerText != null then ownerText else owner}; - group = ${group}; + group = ${if groupText != null then groupText else group}; restartUnits = ${if restartUnitsText != null then restartUnitsText else "[ " + concatStringsSep " " restartUnits + " ]"}; } ''); @@ -40,6 +50,8 @@ in ''; type = str; default = mode; + } // optionalAttrs (modeText != null) { + defaultText = literalMD modeText; }; owner = mkOption ({ @@ -58,6 +70,8 @@ in ''; type = str; default = group; + } // optionalAttrs (groupText != null) { + defaultText = literalMD groupText; }; restartUnits = mkOption ({ diff --git a/modules/services/arr.nix b/modules/services/arr.nix index ca17b31..f5c58ea 100644 --- a/modules/services/arr.nix +++ b/modules/services/arr.nix @@ -316,29 +316,17 @@ let }; backup = lib.mkOption { - type = contracts.backup.request; 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."${name}" = { - request = config.shb.${name}.backup; - settings = { - enable = true; - }; - } - ``` + Backup configuration. ''; - readOnly = true; - default = { - user = name; - sourceDirectories = [ - cfg.${name}.dataDir - ]; - excludePatterns = [".db-shm" ".db-wal" ".mono"]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + user = name; + sourceDirectories = [ + cfg.${name}.dataDir + ]; + excludePatterns = [".db-shm" ".db-wal" ".mono"]; + }; }; }; } // (c.moreOptions or {}); diff --git a/modules/services/audiobookshelf.nix b/modules/services/audiobookshelf.nix index 29ee5dd..b69758e 100644 --- a/modules/services/audiobookshelf.nix +++ b/modules/services/audiobookshelf.nix @@ -88,28 +88,16 @@ in }; backup = lib.mkOption { - type = contracts.backup.request; 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."audiobookshelf" = { - request = config.shb.audiobookshelf.backup; - settings = { - enable = true; - }; - }; - ``` + Backup configuration. ''; - readOnly = true; - default = { - user = "audiobookshelf"; - sourceDirectories = [ - "/var/lib/audiobookshelf" - ]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + user = "audiobookshelf"; + sourceDirectories = [ + "/var/lib/audiobookshelf" + ]; + }; }; }; diff --git a/modules/services/deluge.nix b/modules/services/deluge.nix index cf69d51..11256b1 100644 --- a/modules/services/deluge.nix +++ b/modules/services/deluge.nix @@ -244,28 +244,16 @@ in }; backup = lib.mkOption { - type = contracts.backup.request; 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."vaultwarden" = { - request = config.shb.vaultwarden.backup; - settings = { - enable = true; - }; - }; - ``` + Backup configuration. ''; - readOnly = true; - default = { - user = "deluge"; - sourceDirectories = [ - cfg.dataDir - ]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + user = "deluge"; + sourceDirectories = [ + cfg.dataDir + ]; + }; }; }; diff --git a/modules/services/forgejo.nix b/modules/services/forgejo.nix index 894373e..3d5b1cb 100644 --- a/modules/services/forgejo.nix +++ b/modules/services/forgejo.nix @@ -241,30 +241,18 @@ in }; backup = mkOption { - type = contracts.backup.request; 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."forgejo" = { - request = config.shb.forgejo.backup; - settings = { - enable = true; - }; - }; - ``` + Backup configuration. ''; - readOnly = true; - default = { - user = options.services.forgejo.user.value; - sourceDirectories = [ - options.services.forgejo.dump.backupDir.value - ] ++ optionals (cfg.repositoryRoot != null) [ - cfg.repositoryRoot - ]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + user = options.services.forgejo.user.value; + sourceDirectories = [ + options.services.forgejo.dump.backupDir.value + ] ++ optionals (cfg.repositoryRoot != null) [ + cfg.repositoryRoot + ]; + }; }; }; diff --git a/modules/services/grocy.nix b/modules/services/grocy.nix index e1dc3bb..8d4b36e 100644 --- a/modules/services/grocy.nix +++ b/modules/services/grocy.nix @@ -63,28 +63,17 @@ in }; backup = lib.mkOption { - type = contracts.backup.request; 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."grocy" = { - request = config.shb.grocy.backup; - settings = { - enable = true; - }; - }; - ``` + Backup configuration. ''; readOnly = true; - default = { - user = "grocy"; - sourceDirectories = [ - cfg.dataDir - ]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + user = "grocy"; + sourceDirectories = [ + cfg.dataDir + ]; + }; }; }; diff --git a/modules/services/hledger.nix b/modules/services/hledger.nix index ee4c3c9..99bbd03 100644 --- a/modules/services/hledger.nix +++ b/modules/services/hledger.nix @@ -55,28 +55,16 @@ in }; backup = lib.mkOption { - type = contracts.backup.request; 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."hledger" = { - request = config.shb.hledger.backup; - settings = { - enable = true; - }; - }; - ``` + Backup configuration. ''; - readOnly = true; - default = { - user = "hledger"; - sourceDirectories = [ - cfg.dataDir - ]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + user = "hledger"; + sourceDirectories = [ + cfg.dataDir + ]; + }; }; }; }; diff --git a/modules/services/home-assistant.nix b/modules/services/home-assistant.nix index 43214a4..8f2189f 100644 --- a/modules/services/home-assistant.nix +++ b/modules/services/home-assistant.nix @@ -140,29 +140,17 @@ in }; backup = lib.mkOption { - type = contracts.backup.request; 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."home-assistant" = { - request = config.shb.home-assistant.backup; - settings = { - enable = true; - }; - }; - ``` + Backup configuration. ''; - readOnly = true; - default = { - user = "hass"; - # No need for backup hooks as we use an hourly automation job in home assistant directly with a cron job. - sourceDirectories = [ - "/var/lib/hass/backups" - ]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + user = "hass"; + # No need for backup hooks as we use an hourly automation job in home assistant directly with a cron job. + sourceDirectories = [ + "/var/lib/hass/backups" + ]; + }; }; }; }; diff --git a/modules/services/jellyfin.nix b/modules/services/jellyfin.nix index 40cd697..adaeef8 100644 --- a/modules/services/jellyfin.nix +++ b/modules/services/jellyfin.nix @@ -151,28 +151,16 @@ in }; backup = lib.mkOption { - type = contracts.backup.request; 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."jellyfin" = { - request = config.shb.jellyfin.backup; - settings = { - enable = true; - }; - }; - ``` + Backup configuration. ''; - readOnly = true; - default = { - user = "jellyfin"; - sourceDirectories = [ - "/var/lib/jellyfin" - ]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + user = "jellyfin"; + sourceDirectories = [ + "/var/lib/jellyfin" + ]; + }; }; }; }; diff --git a/modules/services/nextcloud-server.nix b/modules/services/nextcloud-server.nix index 426fddc..c0fed18 100644 --- a/modules/services/nextcloud-server.nix +++ b/modules/services/nextcloud-server.nix @@ -519,29 +519,17 @@ in backup = lib.mkOption { - type = contracts.backup.request; 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."nextcloud" = { - request = config.shb.nextcloud.backup; - settings = { - enable = true; - }; - }; - ``` + Backup configuration. ''; - readOnly = true; - default = { - user = "nextcloud"; - sourceDirectories = [ - cfg.dataDir - ]; - excludePatterns = [".rnd"]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + user = "nextcloud"; + sourceDirectories = [ + cfg.dataDir + ]; + excludePatterns = [".rnd"]; + }; }; }; diff --git a/modules/services/vaultwarden.nix b/modules/services/vaultwarden.nix index 680fbeb..38e8b5b 100644 --- a/modules/services/vaultwarden.nix +++ b/modules/services/vaultwarden.nix @@ -128,28 +128,16 @@ in }; backup = lib.mkOption { - type = contracts.backup.request; 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."vaultwarden" = { - request = config.shb.vaultwarden.backup; - settings = { - enable = true; - }; - }; - ``` + Backup configuration. ''; - readOnly = true; - default = { - user = "vaultwarden"; - sourceDirectories = [ - dataFolder - ]; + type = lib.types.submodule { + options = contracts.backup.mkRequester { + user = "vaultwarden"; + sourceDirectories = [ + dataFolder + ]; + }; }; }; diff --git a/test/blocks/restic.nix b/test/blocks/restic.nix index a5406bd..9ec4a0c 100644 --- a/test/blocks/restic.nix +++ b/test/blocks/restic.nix @@ -69,7 +69,7 @@ let "/opt/files/B" ]; - hooks.before_backup = ['' + hooks.beforeBackup = ['' echo $RUNTIME_DIRECTORY if [ "$RUNTIME_DIRECTORY" = /run/restic-backups-testinstance_opt_repos_A ]; then if ! [ -f /run/secrets_restic/restic-backups-testinstance_opt_repos_A ]; then