]>
Commit | Line | Data |
---|---|---|
9f6a7862 IB |
1 | { pkgs, config, lib, ... }: |
2 | let | |
3 | cfg = config.myServices.databasesReplication.mariadb; | |
4 | in | |
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 |