]> git.immae.eu Git - perso/Immae/Config/Nix.git/blame - modules/private/databases/mariadb_replication.nix
Use attrs for secrets instead of lists
[perso/Immae/Config/Nix.git] / modules / private / databases / mariadb_replication.nix
CommitLineData
9f6a7862
IB
1{ pkgs, config, lib, ... }:
2let
3 cfg = config.myServices.databasesReplication.mariadb;
4in
5{
6 options.myServices.databasesReplication.mariadb = {
7 enable = lib.mkEnableOption "Enable mariadb replication";
8 base = lib.mkOption {
9 type = lib.types.path;
10 description = ''
11 Base path to put the replications
12 '';
13 };
14 hosts = lib.mkOption {
15 default = {};
16 description = ''
17 Hosts to backup
18 '';
19 type = lib.types.attrsOf (lib.types.submodule {
20 options = {
21 package = lib.mkOption {
22 type = lib.types.package;
23 default = pkgs.mariadb;
24 description = ''
25 Mariadb package for this host
26 '';
27 };
28 serverId = lib.mkOption {
29 type = lib.types.int;
30 description = ''
31 Server id to use for replication cluster (must be unique among the cluster!)
32 '';
33 };
34 host = lib.mkOption {
35 type = lib.types.str;
36 description = ''
37 Host to connect to
38 '';
39 };
40 port = lib.mkOption {
41 type = lib.types.str;
42 description = ''
43 Port to connect to
44 '';
45 };
46 user = lib.mkOption {
47 type = lib.types.str;
48 description = ''
49 User to connect as
50 '';
51 };
52 password = lib.mkOption {
53 type = lib.types.str;
54 description = ''
55 Password to use
56 '';
57 };
58 dumpUser = lib.mkOption {
59 type = lib.types.str;
60 description = ''
61 User who can do a dump
62 '';
63 };
64 dumpPassword = lib.mkOption {
65 type = lib.types.str;
66 description = ''
67 Password for the dump user
68 '';
69 };
70 };
71 });
72 };
73 };
74
75 config = lib.mkIf cfg.enable {
76 users.users.mysql = {
77 description = "MySQL server user";
78 group = "mysql";
79 uid = config.ids.uids.mysql;
80 extraGroups = [ "keys" ];
81 };
82 users.groups.mysql.gid = config.ids.gids.mysql;
83
4c4652aa
IB
84 secrets.keys = lib.listToAttrs (lib.flatten (lib.mapAttrsToList (name: hcfg: [
85 (lib.nameValuePair "mysql_replication/${name}/slave_init_commands" {
9f6a7862
IB
86 user = "mysql";
87 group = "mysql";
88 permissions = "0400";
89 text = ''
90 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;
91 START SLAVE;
92 '';
4c4652aa
IB
93 })
94 (lib.nameValuePair "mysql_replication/${name}/mysqldump_remote" {
9f6a7862
IB
95 permissions = "0400";
96 user = "root";
97 group = "root";
98 text = ''
99 [mysqldump]
100 user = ${hcfg.user}
101 password = ${hcfg.password}
102 '';
4c4652aa
IB
103 })
104 (lib.nameValuePair "mysql_replication/${name}/mysqldump" {
9f6a7862
IB
105 permissions = "0400";
106 user = "root";
107 group = "root";
108 text = ''
109 [mysqldump]
110 user = ${hcfg.dumpUser}
111 password = ${hcfg.dumpPassword}
112 '';
4c4652aa
IB
113 })
114 (lib.nameValuePair "mysql_replication/${name}/client" {
6015a3b5
IB
115 permissions = "0400";
116 user = "mysql";
117 group = "mysql";
118 text = ''
119 [client]
120 user = ${hcfg.dumpUser}
121 password = ${hcfg.dumpPassword}
122 '';
4c4652aa
IB
123 })
124 ]) cfg.hosts));
9f6a7862
IB
125
126 services.cron = {
127 enable = true;
128 systemCronJobs = lib.flatten (lib.mapAttrsToList (name: hcfg:
129 let
130 dataDir = "${cfg.base}/${name}/mysql";
131 backupDir = "${cfg.base}/${name}/mysql_backup";
132 backup_script = pkgs.writeScript "backup_mysql_${name}" ''
133 #!${pkgs.stdenv.shell}
134
135 set -euo pipefail
136
5868f9c6 137 filename=${backupDir}/$(${pkgs.coreutils}/bin/date -Iminutes).sql
9f6a7862 138 ${hcfg.package}/bin/mysqldump \
da30ae4f 139 --defaults-file=${config.secrets.fullPaths."mysql_replication/${name}/mysqldump"} \
9f6a7862
IB
140 -S /run/mysqld_${name}/mysqld.sock \
141 --gtid \
142 --master-data \
143 --flush-privileges \
ef909e24 144 --ignore-database=netdata \
5868f9c6
IB
145 --all-databases > $filename
146 ${pkgs.gzip}/bin/gzip $filename
9f6a7862
IB
147 '';
148 u = pkgs.callPackage ./utils.nix {};
5868f9c6 149 cleanup_script = pkgs.writeScript "cleanup_mysql_${name}" (u.exponentialDumps "sql.gz" backupDir);
9f6a7862
IB
150 in [
151 "0 22,4,10,16 * * * root ${backup_script}"
152 "0 3 * * * root ${cleanup_script}"
153 ]) cfg.hosts);
154 };
155
156 system.activationScripts = lib.attrsets.mapAttrs' (name: hcfg:
157 lib.attrsets.nameValuePair "mysql_replication_${name}" {
158 deps = [ "users" "groups" ];
159 text = ''
160 install -m 0700 -o mysql -g mysql -d ${cfg.base}/${name}/mysql
161 install -m 0700 -o mysql -g mysql -d ${cfg.base}/${name}/mysql_backup
162 '';
163 }) cfg.hosts;
164
165 environment.etc = lib.attrsets.mapAttrs' (name: hcfg:
166 lib.attrsets.nameValuePair "mysql/${name}_my.cnf" {
167 text = ''
168 [mysqld]
5af06538 169 skip-networking
9f6a7862
IB
170 socket = /run/mysqld_${name}/mysqld.sock
171 datadir = ${cfg.base}/${name}/mysql/
172 log-bin = mariadb-bin
173 server-id = ${builtins.toString hcfg.serverId}
174 '';
175 }
176 ) cfg.hosts;
177
178 systemd.services = lib.attrsets.mapAttrs' (name: hcfg:
179 let
180 dataDir = "${cfg.base}/${name}/mysql";
181 in
182 lib.attrsets.nameValuePair "mysql_backup_${name}" {
183 description = "Mysql replication for ${name}";
184 wantedBy = [ "multi-user.target" ];
185 after = [ "network.target" ];
186 restartTriggers = [ config.environment.etc."mysql/${name}_my.cnf".source ];
187 unitConfig.RequiresMountsFor = dataDir;
188
189 preStart = ''
190 if ! test -e ${dataDir}/mysql; then
fd7935cf
IB
191 if ! test -e ${dataDir}/initial.sql; then
192 ${hcfg.package}/bin/mysqldump \
da30ae4f 193 --defaults-file=${config.secrets.fullPaths."mysql_replication/${name}/mysqldump_remote"} \
fd7935cf
IB
194 -h ${hcfg.host} \
195 -P ${hcfg.port} \
196 --ssl \
197 --gtid \
198 --flush-privileges \
199 --master-data \
200 --all-databases > ${dataDir}/initial.sql
201 fi
9f6a7862
IB
202
203 ${hcfg.package}/bin/mysql_install_db \
204 --defaults-file=/etc/mysql/${name}_my.cnf \
205 --user=mysql \
206 --datadir=${dataDir} \
207 --basedir=${hcfg.package}
208 fi
209 '';
210
211 serviceConfig = {
212 User = "mysql";
213 Group = "mysql";
214 RuntimeDirectory = "mysqld_${name}";
215 RuntimeDirectoryMode = "0755";
216 SupplementaryGroups = "keys";
217 PermissionsStartOnly = true;
218 Type = "notify";
219
220 ExecStart = "${hcfg.package}/bin/mysqld --defaults-file=/etc/mysql/${name}_my.cnf --user=mysql --datadir=${dataDir} --basedir=${hcfg.package}";
221 ExecStartPost =
222 let
223 sql_before = pkgs.writeText "mysql-initial-before" ''
224 DROP DATABASE test;
fd7935cf 225 INSTALL SONAME 'auth_pam';
9f6a7862
IB
226 '';
227 setupScript = pkgs.writeScript "mysql-setup" ''
228 #!${pkgs.runtimeShell} -e
229
230 if test -e ${dataDir}/initial.sql; then
231 cat \
232 ${sql_before} \
233 ${dataDir}/initial.sql \
da30ae4f 234 ${config.secrets.fullPaths."mysql_replication/${name}/slave_init_commands"} \
9f6a7862
IB
235 | ${hcfg.package}/bin/mysql \
236 --defaults-file=/etc/mysql/${name}_my.cnf \
237 -S /run/mysqld_${name}/mysqld.sock \
238 --user=root
239 rm -f ${dataDir}/initial.sql
240 fi
241 '';
242 in
243 "+${setupScript}";
244 # initial dump can take a long time
245 TimeoutStartSec="infinity";
246 TimeoutStopSec = 120;
247 };
248 }) cfg.hosts;
249 };
250}
251