forked from nixcloud/nixcloud-webservices
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdefault.nix
291 lines (261 loc) · 9.66 KB
/
default.nix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
{ config, pkgs, lib, ... }:
let
inherit (lib) mkOption types;
inherit (config) defaultDatabaseType tempDbSetupHook;
dbSpec = {
postgresql = rec {
socketPath = config.runtimeDir;
phpHostname = socketPath;
};
mysql = rec {
socketPath = "${config.runtimeDir}/.mysql.sock";
phpHostname = "localhost:${socketPath}";
};
};
dbTypes = lib.attrNames dbSpec;
dbSubmodule = { name, config, ... }: {
options.type = mkOption {
type = types.enum dbTypes;
default = defaultDatabaseType;
example = "mysql";
description = ''
The DBMS type to use for this database.
<note><para>
The default is dependant on the <option>defaultDatabaseType</option>.
</para></note>
'';
};
options.socketPath = mkOption {
type = types.str;
readOnly = true;
default = dbSpec.${config.type}.socketPath;
description = ''
The path to the database socket file.
'';
};
options.phpHostname = mkOption {
type = types.str;
readOnly = true;
default = dbSpec.${config.type}.phpHostname;
description = ''
The hostname argument suitable for PHP.
<note><para>This maps to the socket file rather than a real host
name.</para></note>
'';
};
options.name = mkOption {
type = types.str;
default = name;
description = ''
The name of the database or schema to create.
'';
};
options.user = mkOption {
type = types.str;
default = name;
description = ''
The user name which should be the owner of the database.
'';
};
options.owners = mkOption {
type = types.listOf types.str;
default = [];
description = ''
Additional users which should be mapped as the owner of the database
specified in <option>user</option>.
'';
};
options.postCreate = mkOption {
type = types.lines;
default = "";
description = ''
The commands to run shortly after the database has been created.
<note><para>
All of these commands are run in a context, where a special command
called <command>sqlsh</command> is available, which in turn reads SQL
commands from standard input and executes them on the current database.
</para></note>
'';
};
options.tempDbSetupHook = mkOption {
type = lib.mkOptionType {
name = "setupHook";
inherit (types.package) check;
};
readOnly = true;
description = ''
This is the final setup hook that can be used in derivations to
temporarily spin up a database server for preinitializing databases and
dumping them to an SQL file.
'';
};
config.tempDbSetupHook = pkgs.makeSetupHook {
name = "dump-from-temp-db";
deps = tempDbSetupHook.dependencies;
substitutions = {
eatmydata = let
soFile = "${pkgs.libeatmydata}/lib/libeatmydata.so";
in "LD_PRELOAD=${lib.escapeShellArg soFile}";
} // tempDbSetupHook.substitutions;
} (pkgs.writeText "dump-from-temp-db.hook" ''
export tempDbName=${lib.escapeShellArg config.name}
export tempDbUser=${lib.escapeShellArg config.user}
${builtins.readFile tempDbSetupHook.script}
postUnpackHooks+=(tempdbInit)
'');
};
in {
imports = map (dbtype: ./. + "/${dbtype}.nix") dbTypes;
options.database = mkOption {
type = types.attrsOf (types.submodule dbSubmodule);
default = {};
description = ''
Map database names to their corresponding options.
'';
};
options.dbShellCommand = mkOption {
type = types.attrsOf types.str;
example = {
mysql = "exec mysql --user=\"$1\" --socket=/tmp/.mysql.sock \"$2\"";
};
internal = true;
description = ''
The attribute name is the specific database module and the attribute
value is a shell script snippet which should connect to a database
specified by its first argument using the user specified by the second
argument.
This option is only needed if you want to create your own database
module.
'';
};
options.tempDbSetupHook = {
dependencies = mkOption {
type = types.listOf types.package;
internal = true;
default = [];
description = ''
The dependencies that need to be available for the setup hook.
'';
};
script = mkOption {
type = types.path;
internal = true;
description = ''
The path to the setup hook script, which automatically gets the
following environment variables assigned on each run:
<variablelist>
<varlistentry>
<term><envar>tempDbName</envar></term>
<listitem><para>The temporary database name to
create</para></listitem>
</varlistentry>
<varlistentry>
<term><envar>tempDbUser</envar></term>
<listitem><para>The temporary user name for creating/connecting to
the database</para></listitem>
</varlistentry>
</variablelist>
When using the setup hook in a derivation, the function
<function>tempdbInit</function> is called after unpacking the source.
In addition, the hook script <emphasis>must</emphasis> export the
following environment variables to the build environment:
<variablelist>
<varlistentry>
<term><envar>tempDbSocketPath</envar></term>
<listitem><para>The path to the UNIX socket file</para></listitem>
</varlistentry>
<varlistentry>
<term><envar>tempDbPhpHostname</envar></term>
<listitem><para>A hostname for PHP connection strings to connect
via UNIX socket file </para></listitem>
</varlistentry>
</variablelist>
'';
};
substitutions = mkOption {
type = types.attrsOf (types.either types.str types.package);
internal = true;
default = {};
description = ''
A mapping of keys and values, where <literal>@key@</literal> is then
replaced by its value within the hook script.
By default <literal>@eatmydata@</literal> is
<emphasis>always</emphasis> available and can be prepended to commands
that need to run without <citerefentry>
<refentrytitle>fsync</refentrytitle>
<manvolnum>2</manvolnum>
</citerefentry>.
'';
};
};
options.defaultDatabaseType = mkOption {
type = types.enum dbTypes;
default = "postgresql";
example = "mysql";
description = ''
The default type to use for all the databases where no explicit
<option>type</option> has been defined.
'';
};
config = lib.mkIf (config.enable && config.database != {}) {
# Let's assume we have two databases, one is a PostgreSQL database called
# "abc" and the second one is a MariaDB database "xyz". The following chart
# illustrates the ordering of the targets/services during startup:
#
# network.target
# |
# \|/
# .-------- db-server.target -------.
# | | |
# \|/ | |
# mysql-initdb.service | postgresql-initdb.service
# | | |
# \|/ | \|/
# mysql.service | postgresql.service
# | | | | |
# | `--------------|--------------' |
# | | |
# \|/ | \|/
# database-abc.service | database-xyz.service
# | | |
# \|/ | \|/
# database-abc-post-create.service | database-xyz-post-create.service
# | | |
# | \|/ |
# |--------- database.target -------|
# | | |
# \|/ | \|/
# database-abc.target | database-xyz.target
# | | |
# `----------------|----------------'
# |
# \|/
# multi-user.target
#
# Note that mysql.service/postgresql.service are NOT ordered before
# database.target, so that we can ensure parallel creation of these
# databases. For example if PostgreSQL is still starting up, MariaDB
# databases will still be created in the meantime.
systemd.targets = {
db-server = {
description = "Database Management Systems For ${config.uniqueName}";
after = [ "network.target" ];
};
database = {
description = "Databases For ${config.uniqueName}";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
instance.after = [ "db-server.target" ];
instance.requires = [ "db-server.target" ];
};
} // lib.mapAttrs' (lib.const (dbcfg: {
name = "database-${dbcfg.name}";
value = {
description = "Create Database ${dbcfg.name}";
instance.requiredBy = [ "database.target" ];
instance.after = [ "db-server.target" ];
instance.before = [ "database.target" ];
};
})) config.database;
};
}