+++ /dev/null
-{ pkgs, config, lib, ... }:
-let
- cfg = config.myServices.databasesReplication.mariadb;
-in
-{
- options.myServices.databasesReplication.mariadb = {
- enable = lib.mkEnableOption "Enable mariadb replication";
- base = lib.mkOption {
- type = lib.types.path;
- description = ''
- Base path to put the replications
- '';
- };
- hosts = lib.mkOption {
- default = {};
- description = ''
- Hosts to backup
- '';
- type = lib.types.attrsOf (lib.types.submodule {
- options = {
- package = lib.mkOption {
- type = lib.types.package;
- default = pkgs.mariadb;
- description = ''
- Mariadb package for this host
- '';
- };
- serverId = lib.mkOption {
- type = lib.types.int;
- description = ''
- Server id to use for replication cluster (must be unique among the cluster!)
- '';
- };
- host = lib.mkOption {
- type = lib.types.str;
- description = ''
- Host to connect to
- '';
- };
- port = lib.mkOption {
- type = lib.types.str;
- description = ''
- Port to connect to
- '';
- };
- user = lib.mkOption {
- type = lib.types.str;
- description = ''
- User to connect as
- '';
- };
- password = lib.mkOption {
- type = lib.types.str;
- description = ''
- Password to use
- '';
- };
- dumpUser = lib.mkOption {
- type = lib.types.str;
- description = ''
- User who can do a dump
- '';
- };
- dumpPassword = lib.mkOption {
- type = lib.types.str;
- description = ''
- Password for the dump user
- '';
- };
- };
- });
- };
- };
-
- config = lib.mkIf cfg.enable {
- users.users.mysql = {
- description = "MySQL server user";
- group = "mysql";
- uid = config.ids.uids.mysql;
- extraGroups = [ "keys" ];
- };
- users.groups.mysql.gid = config.ids.gids.mysql;
-
- secrets.keys = lib.listToAttrs (lib.flatten (lib.mapAttrsToList (name: hcfg: [
- (lib.nameValuePair "mysql_replication/${name}/slave_init_commands" {
- user = "mysql";
- group = "mysql";
- permissions = "0400";
- text = ''
- CHANGE MASTER TO master_host="${hcfg.host}", master_port=${hcfg.port}, master_user="${hcfg.user}", master_password="${hcfg.password}", master_ssl=1, master_use_gtid=slave_pos;
- START SLAVE;
- '';
- })
- (lib.nameValuePair "mysql_replication/${name}/mysqldump_remote" {
- permissions = "0400";
- user = "root";
- group = "root";
- text = ''
- [mysqldump]
- user = ${hcfg.user}
- password = ${hcfg.password}
- '';
- })
- (lib.nameValuePair "mysql_replication/${name}/mysqldump" {
- permissions = "0400";
- user = "root";
- group = "root";
- text = ''
- [mysqldump]
- user = ${hcfg.dumpUser}
- password = ${hcfg.dumpPassword}
- '';
- })
- (lib.nameValuePair "mysql_replication/${name}/client" {
- permissions = "0400";
- user = "mysql";
- group = "mysql";
- text = ''
- [client]
- user = ${hcfg.dumpUser}
- password = ${hcfg.dumpPassword}
- '';
- })
- ]) cfg.hosts));
-
- services.cron = {
- enable = true;
- systemCronJobs = lib.flatten (lib.mapAttrsToList (name: hcfg:
- let
- dataDir = "${cfg.base}/${name}/mysql";
- backupDir = "${cfg.base}/${name}/mysql_backup";
- backup_script = pkgs.writeScript "backup_mysql_${name}" ''
- #!${pkgs.stdenv.shell}
-
- set -euo pipefail
-
- filename=${backupDir}/$(${pkgs.coreutils}/bin/date -Iminutes).sql
- ${hcfg.package}/bin/mysqldump \
- --defaults-file=${config.secrets.fullPaths."mysql_replication/${name}/mysqldump"} \
- -S /run/mysqld_${name}/mysqld.sock \
- --gtid \
- --master-data \
- --flush-privileges \
- --ignore-database=netdata \
- --all-databases > $filename
- ${pkgs.gzip}/bin/gzip $filename
- '';
- u = pkgs.callPackage ./utils.nix {};
- cleanup_script = pkgs.writeScript "cleanup_mysql_${name}" (u.exponentialDumps "sql.gz" backupDir);
- in [
- "0 22,4,10,16 * * * root ${backup_script}"
- "0 3 * * * root ${cleanup_script}"
- ]) cfg.hosts);
- };
-
- system.activationScripts = lib.attrsets.mapAttrs' (name: hcfg:
- lib.attrsets.nameValuePair "mysql_replication_${name}" {
- deps = [ "users" "groups" ];
- text = ''
- install -m 0700 -o mysql -g mysql -d ${cfg.base}/${name}/mysql
- install -m 0700 -o mysql -g mysql -d ${cfg.base}/${name}/mysql_backup
- '';
- }) cfg.hosts;
-
- environment.etc = lib.attrsets.mapAttrs' (name: hcfg:
- lib.attrsets.nameValuePair "mysql/${name}_my.cnf" {
- text = ''
- [mysqld]
- skip-networking
- socket = /run/mysqld_${name}/mysqld.sock
- datadir = ${cfg.base}/${name}/mysql/
- log-bin = mariadb-bin
- server-id = ${builtins.toString hcfg.serverId}
- '';
- }
- ) cfg.hosts;
-
- systemd.services = lib.attrsets.mapAttrs' (name: hcfg:
- let
- dataDir = "${cfg.base}/${name}/mysql";
- in
- lib.attrsets.nameValuePair "mysql_backup_${name}" {
- description = "Mysql replication for ${name}";
- wantedBy = [ "multi-user.target" ];
- after = [ "network.target" ];
- restartTriggers = [ config.environment.etc."mysql/${name}_my.cnf".source ];
- unitConfig.RequiresMountsFor = dataDir;
-
- preStart = ''
- if ! test -e ${dataDir}/mysql; then
- if ! test -e ${dataDir}/initial.sql; then
- ${hcfg.package}/bin/mysqldump \
- --defaults-file=${config.secrets.fullPaths."mysql_replication/${name}/mysqldump_remote"} \
- -h ${hcfg.host} \
- -P ${hcfg.port} \
- --ssl \
- --gtid \
- --flush-privileges \
- --master-data \
- --all-databases > ${dataDir}/initial.sql
- fi
-
- ${hcfg.package}/bin/mysql_install_db \
- --defaults-file=/etc/mysql/${name}_my.cnf \
- --user=mysql \
- --datadir=${dataDir} \
- --basedir=${hcfg.package}
- fi
- '';
-
- serviceConfig = {
- User = "mysql";
- Group = "mysql";
- RuntimeDirectory = "mysqld_${name}";
- RuntimeDirectoryMode = "0755";
- SupplementaryGroups = "keys";
- PermissionsStartOnly = true;
- Type = "notify";
-
- ExecStart = "${hcfg.package}/bin/mysqld --defaults-file=/etc/mysql/${name}_my.cnf --user=mysql --datadir=${dataDir} --basedir=${hcfg.package}";
- ExecStartPost =
- let
- sql_before = pkgs.writeText "mysql-initial-before" ''
- DROP DATABASE test;
- INSTALL SONAME 'auth_pam';
- '';
- setupScript = pkgs.writeScript "mysql-setup" ''
- #!${pkgs.runtimeShell} -e
-
- if test -e ${dataDir}/initial.sql; then
- cat \
- ${sql_before} \
- ${dataDir}/initial.sql \
- ${config.secrets.fullPaths."mysql_replication/${name}/slave_init_commands"} \
- | ${hcfg.package}/bin/mysql \
- --defaults-file=/etc/mysql/${name}_my.cnf \
- -S /run/mysqld_${name}/mysqld.sock \
- --user=root
- rm -f ${dataDir}/initial.sql
- fi
- '';
- in
- "+${setupScript}";
- # initial dump can take a long time
- TimeoutStartSec="infinity";
- TimeoutStopSec = 120;
- };
- }) cfg.hosts;
- };
-}
-