From 459a63f20a8634ca081f68df5ca793ff1885ad60 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 30 Dec 2024 20:28:55 +0100 Subject: [PATCH 1/7] services.mysqlBackup: cleanup --- nixos/modules/services/backup/mysql-backup.nix | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/nixos/modules/services/backup/mysql-backup.nix b/nixos/modules/services/backup/mysql-backup.nix index b30579103699..56ec17917753 100644 --- a/nixos/modules/services/backup/mysql-backup.nix +++ b/nixos/modules/services/backup/mysql-backup.nix @@ -5,9 +5,6 @@ ... }: let - - inherit (pkgs) mariadb gzip; - cfg = config.services.mysqlBackup; defaultUser = "mysqlbackup"; @@ -22,7 +19,7 @@ let ''; backupDatabaseScript = db: '' dest="${cfg.location}/${db}.gz" - if ${mariadb}/bin/mysqldump ${lib.optionalString cfg.singleTransaction "--single-transaction"} ${db} | ${gzip}/bin/gzip -c ${cfg.gzipOptions} > $dest.tmp; then + if ${pkgs.mariadb}/bin/mysqldump ${lib.optionalString cfg.singleTransaction "--single-transaction"} ${db} | ${pkgs.gzip}/bin/gzip -c ${cfg.gzipOptions} > $dest.tmp; then mv $dest.tmp $dest echo "Backed up to $dest" else @@ -33,12 +30,9 @@ let ''; in - { options = { - services.mysqlBackup = { - enable = lib.mkEnableOption "MySQL backups"; calendar = lib.mkOption { @@ -106,7 +100,6 @@ in { name = cfg.user; ensurePermissions = - with lib; let privs = "SELECT, SHOW VIEW, TRIGGER, LOCK TABLES"; grant = db: lib.nameValuePair "${db}.*" privs; From ec9d4c4ae6425d68a4d2b5749b506eddc6eabd3e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 30 Dec 2024 20:33:18 +0100 Subject: [PATCH 2/7] services.mysqlBackup: add option to change compression tool --- .../modules/services/backup/mysql-backup.nix | 91 ++++++++++++++++++- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/nixos/modules/services/backup/mysql-backup.nix b/nixos/modules/services/backup/mysql-backup.nix index 56ec17917753..c4264120f997 100644 --- a/nixos/modules/services/backup/mysql-backup.nix +++ b/nixos/modules/services/backup/mysql-backup.nix @@ -8,6 +8,41 @@ let cfg = config.services.mysqlBackup; defaultUser = "mysqlbackup"; + compressionPkg = + { + gzip = pkgs.gzip; + xz = pkgs.xz; + zstd = pkgs.zstd; + } + .${cfg.compressionAlg}; + + fileExt = + { + gzip = ".gz"; + zstd = ".zst"; + xz = ".xz"; + } + .${cfg.compressionAlg}; + + validCompressionLevels = { + zstd = range: 1 <= range && range <= 19; + xz = range: 0 <= range && range <= 9; + gzip = range: 1 <= range && range <= 9; + }; + + compressionCmd = + let + compressionLevelFlag = lib.optionalString (cfg.compressionLevel != null) ( + "-" + toString cfg.compressionLevel + ); + in + { + gzip = "${pkgs.gzip}/bin/gzip -c ${cfg.gzipOptions} ${compressionLevelFlag}"; + xz = "${pkgs.xz}/bin/xz -z -c ${compressionLevelFlag} -"; + zstd = "${pkgs.zstd}/bin/zstd ${compressionLevelFlag} -"; + } + .${cfg.compressionAlg}; + backupScript = '' set -o pipefail failed="" @@ -17,9 +52,10 @@ let exit 1 fi ''; + backupDatabaseScript = db: '' - dest="${cfg.location}/${db}.gz" - if ${pkgs.mariadb}/bin/mysqldump ${lib.optionalString cfg.singleTransaction "--single-transaction"} ${db} | ${pkgs.gzip}/bin/gzip -c ${cfg.gzipOptions} > $dest.tmp; then + dest="${cfg.location}/${db}${fileExt}" + if ${pkgs.mariadb}/bin/mysqldump ${lib.optionalString cfg.singleTransaction "--single-transaction"} ${db} | ${compressionCmd} > $dest.tmp; then mv $dest.tmp $dest echo "Backed up to $dest" else @@ -43,6 +79,29 @@ in ''; }; + compressionAlg = lib.mkOption { + type = lib.types.enum [ + "gzip" + "zstd" + "xz" + ]; + default = "gzip"; + description = '' + Compression algorithm to use for database dumps. + ''; + }; + + compressionLevel = lib.mkOption { + type = lib.types.nullOr lib.types.int; + default = null; + description = '' + Compression level to use for gzip, zstd or xz. + For gzip: 1-9 (note: if compression level is also specified in gzipOptions, the gzipOptions value will be overwritten) + For zstd: 1-19 + For xz: 0-9 + ''; + }; + user = lib.mkOption { type = lib.types.str; default = defaultUser; @@ -63,7 +122,7 @@ in type = lib.types.path; default = "/var/backup/mysql"; description = '' - Location to put the gzipped MySQL database dumps. + Location to put the compressed MySQL database dumps. ''; }; @@ -80,13 +139,32 @@ in type = lib.types.str; description = '' Command line options to use when invoking `gzip`. + Only used when compression is set to "gzip". + If compression level is specified both here and in compressionLevel, the compressionLevel value will take precedence. ''; }; }; - }; config = lib.mkIf cfg.enable { + # assert config to be correct + assertions = [ + { + assertion = + cfg.compressionLevel == null || validCompressionLevels.${cfg.compressionAlg} cfg.compressionLevel; + message = + let + rangeMsg = { + "zstd" = "zstd compression level must be between 1 and 19"; + "xz" = "xz compression level must be between 0 and 9"; + "gzip" = "gzip compression level must be between 1 and 9"; + }; + in + rangeMsg.${cfg.compressionAlg}; + } + ]; + + # ensure unix user to be used to perform backup exist. users.users = lib.optionalAttrs (cfg.user == defaultUser) { ${defaultUser} = { isSystemUser = true; @@ -96,6 +174,10 @@ in }; }; + # add the compression tool to the system environment. + environment.systemPackages = [ compressionPkg ]; + + # ensure database user to be used to perform backup exist. services.mysql.ensureUsers = [ { name = cfg.user; @@ -132,5 +214,4 @@ in ]; }; }; - } From 8b4703426c0e129ca8425fbf5e396158ccd4b8fe Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 6 Jan 2025 15:42:16 +0100 Subject: [PATCH 3/7] services.mysqlBackup: add myselfe as maintainer --- nixos/modules/services/backup/mysql-backup.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/modules/services/backup/mysql-backup.nix b/nixos/modules/services/backup/mysql-backup.nix index c4264120f997..ef6e94ddd1d5 100644 --- a/nixos/modules/services/backup/mysql-backup.nix +++ b/nixos/modules/services/backup/mysql-backup.nix @@ -214,4 +214,6 @@ in ]; }; }; + + meta.maintainers = [ lib.maintainers._6543 ]; } From 085ebf0376df870bc410d6441647b511946f959e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 8 Jan 2025 14:41:02 +0100 Subject: [PATCH 4/7] services.mysqlBackup: apply suggested refactoring --- .../modules/services/backup/mysql-backup.nix | 94 ++++++++----------- 1 file changed, 40 insertions(+), 54 deletions(-) diff --git a/nixos/modules/services/backup/mysql-backup.nix b/nixos/modules/services/backup/mysql-backup.nix index ef6e94ddd1d5..9819c5bda443 100644 --- a/nixos/modules/services/backup/mysql-backup.nix +++ b/nixos/modules/services/backup/mysql-backup.nix @@ -8,40 +8,36 @@ let cfg = config.services.mysqlBackup; defaultUser = "mysqlbackup"; - compressionPkg = - { - gzip = pkgs.gzip; - xz = pkgs.xz; - zstd = pkgs.zstd; - } - .${cfg.compressionAlg}; - - fileExt = - { - gzip = ".gz"; - zstd = ".zst"; - xz = ".xz"; - } - .${cfg.compressionAlg}; - - validCompressionLevels = { - zstd = range: 1 <= range && range <= 19; - xz = range: 0 <= range && range <= 9; - gzip = range: 1 <= range && range <= 9; + compressionAlgs = { + gzip = rec { + pkg = pkgs.gzip; + ext = ".gz"; + minLevel = 1; + maxLevel = 9; + cmd = compressionLevelFlag: "${pkg}/bin/gzip -c ${cfg.gzipOptions} ${compressionLevelFlag}"; + }; + xz = rec { + pkg = pkgs.xz; + ext = ".xz"; + minLevel = 0; + maxLevel = 9; + cmd = compressionLevelFlag: "${pkg}/bin/xz -z -c ${compressionLevelFlag} -"; + }; + zstd = rec { + pkg = pkgs.zstd; + ext = ".zst"; + minLevel = 1; + maxLevel = 19; + cmd = compressionLevelFlag: "${pkg}/bin/zstd ${compressionLevelFlag} -"; + }; }; - compressionCmd = - let - compressionLevelFlag = lib.optionalString (cfg.compressionLevel != null) ( - "-" + toString cfg.compressionLevel - ); - in - { - gzip = "${pkgs.gzip}/bin/gzip -c ${cfg.gzipOptions} ${compressionLevelFlag}"; - xz = "${pkgs.xz}/bin/xz -z -c ${compressionLevelFlag} -"; - zstd = "${pkgs.zstd}/bin/zstd ${compressionLevelFlag} -"; - } - .${cfg.compressionAlg}; + compressionLevelFlag = lib.optionalString (cfg.compressionLevel != null) ( + "-" + toString cfg.compressionLevel + ); + + selectedAlg = compressionAlgs.${cfg.compressionAlg}; + compressionCmd = selectedAlg.cmd compressionLevelFlag; backupScript = '' set -o pipefail @@ -54,7 +50,7 @@ let ''; backupDatabaseScript = db: '' - dest="${cfg.location}/${db}${fileExt}" + dest="${cfg.location}/${db}${selectedAlg.ext}" if ${pkgs.mariadb}/bin/mysqldump ${lib.optionalString cfg.singleTransaction "--single-transaction"} ${db} | ${compressionCmd} > $dest.tmp; then mv $dest.tmp $dest echo "Backed up to $dest" @@ -80,11 +76,7 @@ in }; compressionAlg = lib.mkOption { - type = lib.types.enum [ - "gzip" - "zstd" - "xz" - ]; + type = lib.types.enum (lib.attrNames compressionAlgs); default = "gzip"; description = '' Compression algorithm to use for database dumps. @@ -95,10 +87,13 @@ in type = lib.types.nullOr lib.types.int; default = null; description = '' - Compression level to use for gzip, zstd or xz. - For gzip: 1-9 (note: if compression level is also specified in gzipOptions, the gzipOptions value will be overwritten) - For zstd: 1-19 - For xz: 0-9 + Compression level to use for ${lib.concatStringsSep ", " (lib.init (lib.attrNames compressionAlgs))} or ${lib.last (lib.attrNames compressionAlgs)}. + ${lib.concatStringsSep "\n" ( + lib.mapAttrsToList ( + name: algo: " For ${name}: ${toString algo.minLevel}-${toString algo.maxLevel}" + ) compressionAlgs + )} + (note: if compression level is also specified in gzipOptions, the gzipOptions value will be overwritten) ''; }; @@ -150,17 +145,8 @@ in # assert config to be correct assertions = [ { - assertion = - cfg.compressionLevel == null || validCompressionLevels.${cfg.compressionAlg} cfg.compressionLevel; - message = - let - rangeMsg = { - "zstd" = "zstd compression level must be between 1 and 19"; - "xz" = "xz compression level must be between 0 and 9"; - "gzip" = "gzip compression level must be between 1 and 9"; - }; - in - rangeMsg.${cfg.compressionAlg}; + assertion = cfg.compressionLevel == null || selectedAlg.minLevel <= cfg.compressionLevel && cfg.compressionLevel <= selectedAlg.maxLevel; + message = "${cfg.compressionAlg} compression level must be between ${toString selectedAlg.minLevel} and ${toString selectedAlg.maxLevel}"; } ]; @@ -175,7 +161,7 @@ in }; # add the compression tool to the system environment. - environment.systemPackages = [ compressionPkg ]; + environment.systemPackages = [ selectedAlg.pkg ]; # ensure database user to be used to perform backup exist. services.mysql.ensureUsers = [ From 710b189b1477656abc6fed7172e12a9a865cce22 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 12 Jan 2025 19:46:45 +0100 Subject: [PATCH 5/7] services.mysqlBackup: apply markdown to description Co-authored-by: Silvan Mosberger --- nixos/modules/services/backup/mysql-backup.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nixos/modules/services/backup/mysql-backup.nix b/nixos/modules/services/backup/mysql-backup.nix index 9819c5bda443..494871acb6d1 100644 --- a/nixos/modules/services/backup/mysql-backup.nix +++ b/nixos/modules/services/backup/mysql-backup.nix @@ -90,10 +90,13 @@ in Compression level to use for ${lib.concatStringsSep ", " (lib.init (lib.attrNames compressionAlgs))} or ${lib.last (lib.attrNames compressionAlgs)}. ${lib.concatStringsSep "\n" ( lib.mapAttrsToList ( - name: algo: " For ${name}: ${toString algo.minLevel}-${toString algo.maxLevel}" + name: algo: "- For ${name}: ${toString algo.minLevel}-${toString algo.maxLevel}" ) compressionAlgs )} - (note: if compression level is also specified in gzipOptions, the gzipOptions value will be overwritten) + + :::{.note} + If compression level is also specified in gzipOptions, the gzipOptions value will be overwritten + ::: ''; }; From 233db85960bc6037b25c11af84c278a5eb3812b4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 12 Jan 2025 19:58:18 +0100 Subject: [PATCH 6/7] ci/OWNERS: add myselfe as maintainer for mysql-backup --- ci/OWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci/OWNERS b/ci/OWNERS index bf6e070d80e4..53bc85394e46 100644 --- a/ci/OWNERS +++ b/ci/OWNERS @@ -251,6 +251,9 @@ pkgs/development/python-modules/buildcatrust/ @ajs124 @lukegb @mweinelt /nixos/modules/services/databases/postgresql.nix @NixOS/postgres /nixos/tests/postgresql @NixOS/postgres +# MySQL/MariaDB and related stuff +/nixos/modules/services/backup/mysql-backup.nix @6543 + # Hardened profile & related modules /nixos/modules/profiles/hardened.nix @joachifm /nixos/modules/security/lock-kernel-modules.nix @joachifm From 917be2fef67d5bf8fa0567a5f3e192b968ae1cd7 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 12 Jan 2025 20:00:54 +0100 Subject: [PATCH 7/7] services.mysqlBackup: nixfmt --- nixos/modules/services/backup/mysql-backup.nix | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nixos/modules/services/backup/mysql-backup.nix b/nixos/modules/services/backup/mysql-backup.nix index 494871acb6d1..33dc1c35ebeb 100644 --- a/nixos/modules/services/backup/mysql-backup.nix +++ b/nixos/modules/services/backup/mysql-backup.nix @@ -93,7 +93,7 @@ in name: algo: "- For ${name}: ${toString algo.minLevel}-${toString algo.maxLevel}" ) compressionAlgs )} - + :::{.note} If compression level is also specified in gzipOptions, the gzipOptions value will be overwritten ::: @@ -148,7 +148,9 @@ in # assert config to be correct assertions = [ { - assertion = cfg.compressionLevel == null || selectedAlg.minLevel <= cfg.compressionLevel && cfg.compressionLevel <= selectedAlg.maxLevel; + assertion = + cfg.compressionLevel == null + || selectedAlg.minLevel <= cfg.compressionLevel && cfg.compressionLevel <= selectedAlg.maxLevel; message = "${cfg.compressionAlg} compression level must be between ${toString selectedAlg.minLevel} and ${toString selectedAlg.maxLevel}"; } ];