Merge branch 'master' into postgresql-plugins-bin
This commit is contained in:
@@ -8,25 +8,28 @@ let
|
||||
cassandraConfig = flip recursiveUpdate cfg.extraConfig
|
||||
({ commitlog_sync = "batch";
|
||||
commitlog_sync_batch_window_in_ms = 2;
|
||||
start_native_transport = cfg.allowClients;
|
||||
cluster_name = cfg.clusterName;
|
||||
partitioner = "org.apache.cassandra.dht.Murmur3Partitioner";
|
||||
endpoint_snitch = "SimpleSnitch";
|
||||
seed_provider =
|
||||
[{ class_name = "org.apache.cassandra.locator.SimpleSeedProvider";
|
||||
parameters = [ { seeds = "127.0.0.1"; } ];
|
||||
}];
|
||||
data_file_directories = [ "${cfg.homeDir}/data" ];
|
||||
commitlog_directory = "${cfg.homeDir}/commitlog";
|
||||
saved_caches_directory = "${cfg.homeDir}/saved_caches";
|
||||
} // (if builtins.compareVersions cfg.package.version "3" >= 0
|
||||
then { hints_directory = "${cfg.homeDir}/hints"; }
|
||||
else {})
|
||||
} // (lib.optionalAttrs (cfg.seedAddresses != []) {
|
||||
seed_provider = [{
|
||||
class_name = "org.apache.cassandra.locator.SimpleSeedProvider";
|
||||
parameters = [ { seeds = concatStringsSep "," cfg.seedAddresses; } ];
|
||||
}];
|
||||
}) // (lib.optionalAttrs (lib.versionAtLeast cfg.package.version "3") {
|
||||
hints_directory = "${cfg.homeDir}/hints";
|
||||
})
|
||||
);
|
||||
cassandraConfigWithAddresses = cassandraConfig //
|
||||
( if isNull cfg.listenAddress
|
||||
( if cfg.listenAddress == null
|
||||
then { listen_interface = cfg.listenInterface; }
|
||||
else { listen_address = cfg.listenAddress; }
|
||||
) // (
|
||||
if isNull cfg.rpcAddress
|
||||
if cfg.rpcAddress == null
|
||||
then { rpc_interface = cfg.rpcInterface; }
|
||||
else { rpc_address = cfg.rpcAddress; }
|
||||
);
|
||||
@@ -39,15 +42,42 @@ let
|
||||
mkdir -p "$out"
|
||||
|
||||
echo "$cassandraYaml" > "$out/cassandra.yaml"
|
||||
ln -s "$cassandraEnvPkg" "$out/cassandra-env.sh"
|
||||
ln -s "$cassandraLogbackConfig" "$out/logback.xml"
|
||||
|
||||
cp "$cassandraEnvPkg" "$out/cassandra-env.sh"
|
||||
|
||||
# Delete default JMX Port, otherwise we can't set it using env variable
|
||||
sed -i '/JMX_PORT="7199"/d' "$out/cassandra-env.sh"
|
||||
|
||||
# Delete default password file
|
||||
sed -i '/-Dcom.sun.management.jmxremote.password.file=\/etc\/cassandra\/jmxremote.password/d' "$out/cassandra-env.sh"
|
||||
'';
|
||||
};
|
||||
defaultJmxRolesFile = builtins.foldl'
|
||||
(left: right: left + right) ""
|
||||
(map (role: "${role.username} ${role.password}") cfg.jmxRoles);
|
||||
fullJvmOptions = cfg.jvmOpts
|
||||
++ lib.optionals (cfg.jmxRoles != []) [
|
||||
"-Dcom.sun.management.jmxremote.authenticate=true"
|
||||
"-Dcom.sun.management.jmxremote.password.file=${cfg.jmxRolesFile}"
|
||||
]
|
||||
++ lib.optionals cfg.remoteJmx [
|
||||
"-Djava.rmi.server.hostname=${cfg.rpcAddress}"
|
||||
];
|
||||
in {
|
||||
options.services.cassandra = {
|
||||
enable = mkEnableOption ''
|
||||
Apache Cassandra – Scalable and highly available database.
|
||||
'';
|
||||
clusterName = mkOption {
|
||||
type = types.str;
|
||||
default = "Test Cluster";
|
||||
description = ''
|
||||
The name of the cluster.
|
||||
This setting prevents nodes in one logical cluster from joining
|
||||
another. All nodes in a cluster must have the same value.
|
||||
'';
|
||||
};
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = defaultUser;
|
||||
@@ -162,6 +192,28 @@ in {
|
||||
XML logback configuration for cassandra
|
||||
'';
|
||||
};
|
||||
seedAddresses = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ "127.0.0.1" ];
|
||||
description = ''
|
||||
The addresses of hosts designated as contact points in the cluster. A
|
||||
joining node contacts one of the nodes in the seeds list to learn the
|
||||
topology of the ring.
|
||||
Set to 127.0.0.1 for a single node cluster.
|
||||
'';
|
||||
};
|
||||
allowClients = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Enables or disables the native transport server (CQL binary protocol).
|
||||
This server uses the same address as the <literal>rpcAddress</literal>,
|
||||
but the port it uses is not <literal>rpc_port</literal> but
|
||||
<literal>native_transport_port</literal>. See the official Cassandra
|
||||
docs for more information on these variables and set them using
|
||||
<literal>extraConfig</literal>.
|
||||
'';
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
type = types.attrs;
|
||||
default = {};
|
||||
@@ -178,11 +230,11 @@ in {
|
||||
example = literalExample "null";
|
||||
description = ''
|
||||
Set the interval how often full repairs are run, i.e.
|
||||
`nodetool repair --full` is executed. See
|
||||
<literal>nodetool repair --full</literal> is executed. See
|
||||
https://cassandra.apache.org/doc/latest/operating/repair.html
|
||||
for more information.
|
||||
|
||||
Set to `null` to disable full repairs.
|
||||
Set to <literal>null</literal> to disable full repairs.
|
||||
'';
|
||||
};
|
||||
fullRepairOptions = mkOption {
|
||||
@@ -199,11 +251,11 @@ in {
|
||||
example = literalExample "null";
|
||||
description = ''
|
||||
Set the interval how often incremental repairs are run, i.e.
|
||||
`nodetool repair` is executed. See
|
||||
<literal>nodetool repair</literal> is executed. See
|
||||
https://cassandra.apache.org/doc/latest/operating/repair.html
|
||||
for more information.
|
||||
|
||||
Set to `null` to disable incremental repairs.
|
||||
Set to <literal>null</literal> to disable incremental repairs.
|
||||
'';
|
||||
};
|
||||
incrementalRepairOptions = mkOption {
|
||||
@@ -214,26 +266,135 @@ in {
|
||||
Options passed through to the incremental repair command.
|
||||
'';
|
||||
};
|
||||
maxHeapSize = mkOption {
|
||||
type = types.nullOr types.string;
|
||||
default = null;
|
||||
example = "4G";
|
||||
description = ''
|
||||
Must be left blank or set together with heapNewSize.
|
||||
If left blank a sensible value for the available amount of RAM and CPU
|
||||
cores is calculated.
|
||||
|
||||
Override to set the amount of memory to allocate to the JVM at
|
||||
start-up. For production use you may wish to adjust this for your
|
||||
environment. MAX_HEAP_SIZE is the total amount of memory dedicated
|
||||
to the Java heap. HEAP_NEWSIZE refers to the size of the young
|
||||
generation.
|
||||
|
||||
The main trade-off for the young generation is that the larger it
|
||||
is, the longer GC pause times will be. The shorter it is, the more
|
||||
expensive GC will be (usually).
|
||||
'';
|
||||
};
|
||||
heapNewSize = mkOption {
|
||||
type = types.nullOr types.string;
|
||||
default = null;
|
||||
example = "800M";
|
||||
description = ''
|
||||
Must be left blank or set together with heapNewSize.
|
||||
If left blank a sensible value for the available amount of RAM and CPU
|
||||
cores is calculated.
|
||||
|
||||
Override to set the amount of memory to allocate to the JVM at
|
||||
start-up. For production use you may wish to adjust this for your
|
||||
environment. HEAP_NEWSIZE refers to the size of the young
|
||||
generation.
|
||||
|
||||
The main trade-off for the young generation is that the larger it
|
||||
is, the longer GC pause times will be. The shorter it is, the more
|
||||
expensive GC will be (usually).
|
||||
|
||||
The example HEAP_NEWSIZE assumes a modern 8-core+ machine for decent pause
|
||||
times. If in doubt, and if you do not particularly want to tweak, go with
|
||||
100 MB per physical CPU core.
|
||||
'';
|
||||
};
|
||||
mallocArenaMax = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
example = 4;
|
||||
description = ''
|
||||
Set this to control the amount of arenas per-thread in glibc.
|
||||
'';
|
||||
};
|
||||
remoteJmx = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Cassandra ships with JMX accessible *only* from localhost.
|
||||
To enable remote JMX connections set to true.
|
||||
|
||||
Be sure to also enable authentication and/or TLS.
|
||||
See: https://wiki.apache.org/cassandra/JmxSecurity
|
||||
'';
|
||||
};
|
||||
jmxPort = mkOption {
|
||||
type = types.int;
|
||||
default = 7199;
|
||||
description = ''
|
||||
Specifies the default port over which Cassandra will be available for
|
||||
JMX connections.
|
||||
For security reasons, you should not expose this port to the internet.
|
||||
Firewall it if needed.
|
||||
'';
|
||||
};
|
||||
jmxRoles = mkOption {
|
||||
default = [];
|
||||
description = ''
|
||||
Roles that are allowed to access the JMX (e.g. nodetool)
|
||||
BEWARE: The passwords will be stored world readable in the nix-store.
|
||||
It's recommended to use your own protected file using
|
||||
<literal>jmxRolesFile</literal>
|
||||
|
||||
Doesn't work in versions older than 3.11 because they don't like that
|
||||
it's world readable.
|
||||
'';
|
||||
type = types.listOf (types.submodule {
|
||||
options = {
|
||||
username = mkOption {
|
||||
type = types.string;
|
||||
description = "Username for JMX";
|
||||
};
|
||||
password = mkOption {
|
||||
type = types.string;
|
||||
description = "Password for JMX";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
jmxRolesFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = if (lib.versionAtLeast cfg.package.version "3.11")
|
||||
then pkgs.writeText "jmx-roles-file" defaultJmxRolesFile
|
||||
else null;
|
||||
example = "/var/lib/cassandra/jmx.password";
|
||||
description = ''
|
||||
Specify your own jmx roles file.
|
||||
|
||||
Make sure the permissions forbid "others" from reading the file if
|
||||
you're using Cassandra below version 3.11.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
assertions =
|
||||
[ { assertion =
|
||||
((isNull cfg.listenAddress)
|
||||
|| (isNull cfg.listenInterface)
|
||||
) && !((isNull cfg.listenAddress)
|
||||
&& (isNull cfg.listenInterface)
|
||||
);
|
||||
[ { assertion = (cfg.listenAddress == null) != (cfg.listenInterface == null);
|
||||
message = "You have to set either listenAddress or listenInterface";
|
||||
}
|
||||
{ assertion =
|
||||
((isNull cfg.rpcAddress)
|
||||
|| (isNull cfg.rpcInterface)
|
||||
) && !((isNull cfg.rpcAddress)
|
||||
&& (isNull cfg.rpcInterface)
|
||||
);
|
||||
{ assertion = (cfg.rpcAddress == null) != (cfg.rpcInterface == null);
|
||||
message = "You have to set either rpcAddress or rpcInterface";
|
||||
}
|
||||
{ assertion = (cfg.maxHeapSize == null) == (cfg.heapNewSize == null);
|
||||
message = "If you set either of maxHeapSize or heapNewSize you have to set both";
|
||||
}
|
||||
{ assertion = cfg.remoteJmx -> cfg.jmxRolesFile != null;
|
||||
message = ''
|
||||
If you want JMX available remotely you need to set a password using
|
||||
<literal>jmxRoles</literal> or <literal>jmxRolesFile</literal> if
|
||||
using Cassandra older than v3.11.
|
||||
'';
|
||||
}
|
||||
];
|
||||
users = mkIf (cfg.user == defaultUser) {
|
||||
extraUsers."${defaultUser}" =
|
||||
@@ -251,7 +412,12 @@ in {
|
||||
after = [ "network.target" ];
|
||||
environment =
|
||||
{ CASSANDRA_CONF = "${cassandraEtc}";
|
||||
JVM_OPTS = builtins.concatStringsSep " " cfg.jvmOpts;
|
||||
JVM_OPTS = builtins.concatStringsSep " " fullJvmOptions;
|
||||
MAX_HEAP_SIZE = toString cfg.maxHeapSize;
|
||||
HEAP_NEWSIZE = toString cfg.heapNewSize;
|
||||
MALLOC_ARENA_MAX = toString cfg.mallocArenaMax;
|
||||
LOCAL_JMX = if cfg.remoteJmx then "no" else "yes";
|
||||
JMX_PORT = toString cfg.jmxPort;
|
||||
};
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig =
|
||||
@@ -276,7 +442,7 @@ in {
|
||||
};
|
||||
};
|
||||
systemd.timers.cassandra-full-repair =
|
||||
mkIf (!isNull cfg.fullRepairInterval) {
|
||||
mkIf (cfg.fullRepairInterval != null) {
|
||||
description = "Schedule full repairs on Cassandra";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig =
|
||||
@@ -300,7 +466,7 @@ in {
|
||||
};
|
||||
};
|
||||
systemd.timers.cassandra-incremental-repair =
|
||||
mkIf (!isNull cfg.incrementalRepairInterval) {
|
||||
mkIf (cfg.incrementalRepairInterval != null) {
|
||||
description = "Schedule incremental repairs on Cassandra";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig =
|
||||
|
||||
@@ -7,7 +7,7 @@ let
|
||||
crdb = cfg.package;
|
||||
|
||||
escape = builtins.replaceStrings ["%"] ["%%"];
|
||||
ifNotNull = v: s: optionalString (!isNull v) s;
|
||||
ifNotNull = v: s: optionalString (v != null) s;
|
||||
|
||||
startupCommand = lib.concatStringsSep " "
|
||||
[ # Basic startup
|
||||
@@ -164,7 +164,7 @@ in
|
||||
|
||||
config = mkIf config.services.cockroachdb.enable {
|
||||
assertions = [
|
||||
{ assertion = !cfg.insecure -> !(isNull cfg.certsDir);
|
||||
{ assertion = !cfg.insecure -> cfg.certsDir != null;
|
||||
message = "CockroachDB must have a set of SSL certificates (.certsDir), or run in Insecure Mode (.insecure = true)";
|
||||
}
|
||||
];
|
||||
|
||||
@@ -95,6 +95,11 @@ in
|
||||
|
||||
environment.systemPackages = [cfg.package];
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '${dataDir}' 0700 ${cfg.user} - - -"
|
||||
"d '${systemDir}' 0700 ${cfg.user} - - -"
|
||||
];
|
||||
|
||||
systemd.services.firebird =
|
||||
{ description = "Firebird Super-Server";
|
||||
|
||||
@@ -104,21 +109,16 @@ in
|
||||
# is a better way
|
||||
preStart =
|
||||
''
|
||||
mkdir -m 0700 -p \
|
||||
"${dataDir}" \
|
||||
"${systemDir}" \
|
||||
/var/log/firebird
|
||||
|
||||
if ! test -e "${systemDir}/security2.fdb"; then
|
||||
cp ${firebird}/security2.fdb "${systemDir}"
|
||||
fi
|
||||
|
||||
chown -R ${cfg.user} "${dataDir}" "${systemDir}" /var/log/firebird
|
||||
chmod -R 700 "${dataDir}" "${systemDir}" /var/log/firebird
|
||||
'';
|
||||
|
||||
serviceConfig.PermissionsStartOnly = true; # preStart must be run as root
|
||||
serviceConfig.User = cfg.user;
|
||||
serviceConfig.LogsDirectory = "firebird";
|
||||
serviceConfig.LogsDirectoryMode = "0700";
|
||||
serviceConfig.ExecStart = ''${firebird}/bin/fbserver -d'';
|
||||
|
||||
# TODO think about shutdown
|
||||
|
||||
@@ -36,6 +36,10 @@ let
|
||||
memory = ${cfg.memory}
|
||||
storage_memory = ${cfg.storageMemory}
|
||||
|
||||
${optionalString (lib.versionAtLeast cfg.package.version "6.1") ''
|
||||
trace_format = ${cfg.traceFormat}
|
||||
''}
|
||||
|
||||
${optionalString (cfg.tls != null) ''
|
||||
tls_plugin = ${pkg}/libexec/plugins/FDBLibTLS.so
|
||||
tls_certificate_file = ${cfg.tls.certificate}
|
||||
@@ -317,9 +321,24 @@ in
|
||||
default = "/run/foundationdb.pid";
|
||||
description = "Path to pidfile for fdbmonitor.";
|
||||
};
|
||||
|
||||
traceFormat = mkOption {
|
||||
type = types.enum [ "xml" "json" ];
|
||||
default = "xml";
|
||||
description = "Trace logging format.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
assertions = [
|
||||
{ assertion = lib.versionOlder cfg.package.version "6.1" -> cfg.traceFormat == "xml";
|
||||
message = ''
|
||||
Versions of FoundationDB before 6.1 do not support configurable trace formats (only XML is supported).
|
||||
This option has no effect for version '' + cfg.package.version + '', and enabling it is an error.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = [ pkg ];
|
||||
|
||||
users.users = optionalAttrs (cfg.user == "foundationdb") (singleton
|
||||
@@ -340,6 +359,13 @@ in
|
||||
}
|
||||
];
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /etc/foundationdb 0755 ${cfg.user} ${cfg.group} - -"
|
||||
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
|
||||
"d '${cfg.logDir}' 0770 ${cfg.user} ${cfg.group} - -"
|
||||
"F '${cfg.pidfile}' - ${cfg.user} ${cfg.group} - -"
|
||||
];
|
||||
|
||||
systemd.services.foundationdb = {
|
||||
description = "FoundationDB Service";
|
||||
|
||||
@@ -377,25 +403,12 @@ in
|
||||
path = [ pkg pkgs.coreutils ];
|
||||
|
||||
preStart = ''
|
||||
rm -f ${cfg.pidfile} && \
|
||||
touch ${cfg.pidfile} && \
|
||||
chown -R ${cfg.user}:${cfg.group} ${cfg.pidfile}
|
||||
|
||||
for x in "${cfg.logDir}" "${cfg.dataDir}"; do
|
||||
[ ! -d "$x" ] && mkdir -m 0700 -vp "$x";
|
||||
chown -R ${cfg.user}:${cfg.group} "$x";
|
||||
done
|
||||
|
||||
[ ! -d /etc/foundationdb ] && \
|
||||
mkdir -m 0775 -vp /etc/foundationdb && \
|
||||
chown -R ${cfg.user}:${cfg.group} "/etc/foundationdb"
|
||||
|
||||
if [ ! -f /etc/foundationdb/fdb.cluster ]; then
|
||||
cf=/etc/foundationdb/fdb.cluster
|
||||
desc=$(tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c8)
|
||||
rand=$(tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c8)
|
||||
echo ''${desc}:''${rand}@${initialIpAddr}:${builtins.toString cfg.listenPortStart} > $cf
|
||||
chmod 0664 $cf && chown -R ${cfg.user}:${cfg.group} $cf
|
||||
chmod 0664 $cf
|
||||
touch "${cfg.dataDir}/.first_startup"
|
||||
fi
|
||||
'';
|
||||
@@ -404,7 +417,7 @@ in
|
||||
|
||||
postStart = ''
|
||||
if [ -e "${cfg.dataDir}/.first_startup" ]; then
|
||||
fdbcli --exec "configure new single memory"
|
||||
fdbcli --exec "configure new single ssd"
|
||||
rm -f "${cfg.dataDir}/.first_startup";
|
||||
fi
|
||||
'';
|
||||
|
||||
@@ -47,14 +47,14 @@ services.foundationdb.package = pkgs.foundationdb52; # FoundationDB 5.2.x
|
||||
After running <command>nixos-rebuild</command>, you can verify whether
|
||||
FoundationDB is running by executing <command>fdbcli</command> (which is
|
||||
added to <option>environment.systemPackages</option>):
|
||||
<programlisting>
|
||||
$ sudo -u foundationdb fdbcli
|
||||
<screen>
|
||||
<prompt>$ </prompt>sudo -u foundationdb fdbcli
|
||||
Using cluster file `/etc/foundationdb/fdb.cluster'.
|
||||
|
||||
The database is available.
|
||||
|
||||
Welcome to the fdbcli. For help, type `help'.
|
||||
fdb> status
|
||||
<prompt>fdb> </prompt>status
|
||||
|
||||
Using cluster file `/etc/foundationdb/fdb.cluster'.
|
||||
|
||||
@@ -72,8 +72,8 @@ Cluster:
|
||||
|
||||
...
|
||||
|
||||
fdb>
|
||||
</programlisting>
|
||||
<prompt>fdb></prompt>
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -82,8 +82,8 @@ fdb>
|
||||
cluster status, as a quick example. (This example uses
|
||||
<command>nix-shell</command> shebang support to automatically supply the
|
||||
necessary Python modules).
|
||||
<programlisting>
|
||||
a@link> cat fdb-status.py
|
||||
<screen>
|
||||
<prompt>a@link> </prompt>cat fdb-status.py
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i python -p python pythonPackages.foundationdb52
|
||||
|
||||
@@ -103,11 +103,11 @@ def main():
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
a@link> chmod +x fdb-status.py
|
||||
a@link> ./fdb-status.py
|
||||
<prompt>a@link> </prompt>chmod +x fdb-status.py
|
||||
<prompt>a@link> </prompt>./fdb-status.py
|
||||
FoundationDB available: True
|
||||
a@link>
|
||||
</programlisting>
|
||||
<prompt>a@link></prompt>
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -266,10 +266,10 @@ services.foundationdb.dataDir = "/data/fdb";
|
||||
<emphasis>every</emphasis> node a coordinator automatically:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
fdbcli> configure double ssd
|
||||
fdbcli> coordinators auto
|
||||
</programlisting>
|
||||
<screen>
|
||||
<prompt>fdbcli> </prompt>configure double ssd
|
||||
<prompt>fdbcli> </prompt>coordinators auto
|
||||
</screen>
|
||||
|
||||
<para>
|
||||
This will transparently update all the servers within seconds, and
|
||||
@@ -386,10 +386,10 @@ services.foundationdb.extraReadWritePaths = [ "/opt/fdb-backups" ];
|
||||
You can now perform a backup:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
$ sudo -u foundationdb fdbbackup start -t default -d file:///opt/fdb-backups
|
||||
$ sudo -u foundationdb fdbbackup status -t default
|
||||
</programlisting>
|
||||
<screen>
|
||||
<prompt>$ </prompt>sudo -u foundationdb fdbbackup start -t default -d file:///opt/fdb-backups
|
||||
<prompt>$ </prompt>sudo -u foundationdb fdbbackup status -t default
|
||||
</screen>
|
||||
</section>
|
||||
<section xml:id="module-services-foundationdb-limitations">
|
||||
<title>Known limitations</title>
|
||||
|
||||
@@ -94,6 +94,11 @@ in {
|
||||
|
||||
config = mkIf config.services.hbase.enable {
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '${cfg.dataDir}' - ${cfg.user} ${cfg.group} - -"
|
||||
"d '${cfg.logDir}' - ${cfg.user} ${cfg.group} - -"
|
||||
];
|
||||
|
||||
systemd.services.hbase = {
|
||||
description = "HBase Server";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
@@ -103,19 +108,7 @@ in {
|
||||
HBASE_LOG_DIR = cfg.logDir;
|
||||
};
|
||||
|
||||
preStart =
|
||||
''
|
||||
mkdir -p ${cfg.dataDir};
|
||||
mkdir -p ${cfg.logDir};
|
||||
|
||||
if [ "$(id -u)" = 0 ]; then
|
||||
chown ${cfg.user}:${cfg.group} ${cfg.dataDir}
|
||||
chown ${cfg.user}:${cfg.group} ${cfg.logDir}
|
||||
fi
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
PermissionsStartOnly = true;
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
ExecStart = "${cfg.package}/bin/hbase --config ${configDir} master start";
|
||||
|
||||
@@ -8,12 +8,13 @@ let
|
||||
|
||||
mongodb = cfg.package;
|
||||
|
||||
mongoCnf = pkgs.writeText "mongodb.conf"
|
||||
mongoCnf = cfg: pkgs.writeText "mongodb.conf"
|
||||
''
|
||||
net.bindIp: ${cfg.bind_ip}
|
||||
${optionalString cfg.quiet "systemLog.quiet: true"}
|
||||
systemLog.destination: syslog
|
||||
storage.dbPath: ${cfg.dbpath}
|
||||
${optionalString cfg.enableAuth "security.authorization: enabled"}
|
||||
${optionalString (cfg.replSetName != "") "replication.replSetName: ${cfg.replSetName}"}
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
@@ -59,6 +60,18 @@ in
|
||||
description = "quieter output";
|
||||
};
|
||||
|
||||
enableAuth = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable client authentication. Creates a default superuser with username root!";
|
||||
};
|
||||
|
||||
initialRootPassword = mkOption {
|
||||
type = types.nullOr types.string;
|
||||
default = null;
|
||||
description = "Password for the root user if auth is enabled.";
|
||||
};
|
||||
|
||||
dbpath = mkOption {
|
||||
default = "/var/db/mongodb";
|
||||
description = "Location where MongoDB stores its files";
|
||||
@@ -84,6 +97,14 @@ in
|
||||
'';
|
||||
description = "MongoDB extra configuration in YAML format";
|
||||
};
|
||||
|
||||
initialScript = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = ''
|
||||
A file containing MongoDB statements to execute on first startup.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
@@ -92,6 +113,11 @@ in
|
||||
###### implementation
|
||||
|
||||
config = mkIf config.services.mongodb.enable {
|
||||
assertions = [
|
||||
{ assertion = !cfg.enableAuth || cfg.initialRootPassword != null;
|
||||
message = "`enableAuth` requires `initialRootPassword` to be set.";
|
||||
}
|
||||
];
|
||||
|
||||
users.users.mongodb = mkIf (cfg.user == "mongodb")
|
||||
{ name = "mongodb";
|
||||
@@ -108,7 +134,7 @@ in
|
||||
after = [ "network.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${mongodb}/bin/mongod --config ${mongoCnf} --fork --pidfilepath ${cfg.pidFile}";
|
||||
ExecStart = "${mongodb}/bin/mongod --config ${mongoCnf cfg} --fork --pidfilepath ${cfg.pidFile}";
|
||||
User = cfg.user;
|
||||
PIDFile = cfg.pidFile;
|
||||
Type = "forking";
|
||||
@@ -116,15 +142,50 @@ in
|
||||
PermissionsStartOnly = true;
|
||||
};
|
||||
|
||||
preStart = ''
|
||||
preStart = let
|
||||
cfg_ = cfg // { enableAuth = false; bind_ip = "127.0.0.1"; };
|
||||
in ''
|
||||
rm ${cfg.dbpath}/mongod.lock || true
|
||||
if ! test -e ${cfg.dbpath}; then
|
||||
install -d -m0700 -o ${cfg.user} ${cfg.dbpath}
|
||||
# See postStart!
|
||||
touch ${cfg.dbpath}/.first_startup
|
||||
fi
|
||||
if ! test -e ${cfg.pidFile}; then
|
||||
install -D -o ${cfg.user} /dev/null ${cfg.pidFile}
|
||||
fi '' + lib.optionalString cfg.enableAuth ''
|
||||
|
||||
if ! test -e "${cfg.dbpath}/.auth_setup_complete"; then
|
||||
systemd-run --unit=mongodb-for-setup --uid=${cfg.user} ${mongodb}/bin/mongod --config ${mongoCnf cfg_}
|
||||
# wait for mongodb
|
||||
while ! ${mongodb}/bin/mongo --eval "db.version()" > /dev/null 2>&1; do sleep 0.1; done
|
||||
|
||||
${mongodb}/bin/mongo <<EOF
|
||||
use admin
|
||||
db.createUser(
|
||||
{
|
||||
user: "root",
|
||||
pwd: "${cfg.initialRootPassword}",
|
||||
roles: [
|
||||
{ role: "userAdminAnyDatabase", db: "admin" },
|
||||
{ role: "dbAdminAnyDatabase", db: "admin" },
|
||||
{ role: "readWriteAnyDatabase", db: "admin" }
|
||||
]
|
||||
}
|
||||
)
|
||||
EOF
|
||||
touch "${cfg.dbpath}/.auth_setup_complete"
|
||||
systemctl stop mongodb-for-setup
|
||||
fi
|
||||
'';
|
||||
postStart = ''
|
||||
if test -e "${cfg.dbpath}/.first_startup"; then
|
||||
${optionalString (cfg.initialScript != null) ''
|
||||
${mongodb}/bin/mongo -u root -p ${cfg.initialRootPassword} admin "${cfg.initialScript}"
|
||||
''}
|
||||
rm -f "${cfg.dbpath}/.first_startup"
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -18,16 +18,12 @@ let
|
||||
in (pName mysql == pName pkgs.mysql57)
|
||||
&& ((builtins.compareVersions mysql.version "5.7") >= 0);
|
||||
|
||||
pidFile = "${cfg.pidDir}/mysqld.pid";
|
||||
|
||||
mysqldAndInstallOptions =
|
||||
"--user=${cfg.user} --datadir=${cfg.dataDir} --basedir=${mysql}";
|
||||
mysqldOptions =
|
||||
"${mysqldAndInstallOptions} --pid-file=${pidFile}";
|
||||
"--user=${cfg.user} --datadir=${cfg.dataDir} --basedir=${mysql}";
|
||||
# For MySQL 5.7+, --insecure creates the root user without password
|
||||
# (earlier versions and MariaDB do this by default).
|
||||
installOptions =
|
||||
"${mysqldAndInstallOptions} ${lib.optionalString isMysqlAtLeast57 "--insecure"}";
|
||||
"${mysqldOptions} ${lib.optionalString isMysqlAtLeast57 "--insecure"}";
|
||||
|
||||
in
|
||||
|
||||
@@ -80,11 +76,6 @@ in
|
||||
description = "Location where MySQL stores its table files";
|
||||
};
|
||||
|
||||
pidDir = mkOption {
|
||||
default = "/run/mysqld";
|
||||
description = "Location of the file which stores the PID of the MySQL server";
|
||||
};
|
||||
|
||||
extraOptions = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
@@ -133,7 +124,7 @@ in
|
||||
};
|
||||
|
||||
initialScript = mkOption {
|
||||
type = types.nullOr types.lines;
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = "A file containing SQL statements to be executed on the first startup. Can be used for granting certain permissions on the database";
|
||||
};
|
||||
@@ -213,12 +204,6 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
# FIXME: remove this option; it's a really bad idea.
|
||||
rootPassword = mkOption {
|
||||
default = null;
|
||||
description = "Path to a file containing the root password, modified on the first startup. Not specifying a root password will leave the root password empty.";
|
||||
};
|
||||
|
||||
replication = {
|
||||
role = mkOption {
|
||||
type = types.enum [ "master" "slave" "none" ];
|
||||
@@ -296,6 +281,10 @@ in
|
||||
${cfg.extraOptions}
|
||||
'';
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d '${cfg.dataDir}' 0700 ${cfg.user} mysql -"
|
||||
];
|
||||
|
||||
systemd.services.mysql = let
|
||||
hasNotify = (cfg.package == pkgs.mariadb);
|
||||
in {
|
||||
@@ -313,126 +302,122 @@ in
|
||||
pkgs.nettools
|
||||
];
|
||||
|
||||
preStart =
|
||||
''
|
||||
if ! test -e ${cfg.dataDir}/mysql; then
|
||||
mkdir -m 0700 -p ${cfg.dataDir}
|
||||
chown -R ${cfg.user} ${cfg.dataDir}
|
||||
${mysql}/bin/mysql_install_db --defaults-file=/etc/my.cnf ${installOptions}
|
||||
touch /tmp/mysql_init
|
||||
fi
|
||||
|
||||
mkdir -m 0755 -p ${cfg.pidDir}
|
||||
chown -R ${cfg.user} ${cfg.pidDir}
|
||||
'';
|
||||
preStart = ''
|
||||
if ! test -e ${cfg.dataDir}/mysql; then
|
||||
${mysql}/bin/mysql_install_db --defaults-file=/etc/my.cnf ${installOptions}
|
||||
touch /tmp/mysql_init
|
||||
fi
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
User = cfg.user;
|
||||
Group = "mysql";
|
||||
Type = if hasNotify then "notify" else "simple";
|
||||
RuntimeDirectory = "mysqld";
|
||||
RuntimeDirectoryMode = "0755";
|
||||
# The last two environment variables are used for starting Galera clusters
|
||||
ExecStart = "${mysql}/bin/mysqld --defaults-file=/etc/my.cnf ${mysqldOptions} $_WSREP_NEW_CLUSTER $_WSREP_START_POSITION";
|
||||
};
|
||||
ExecStartPost =
|
||||
let
|
||||
setupScript = pkgs.writeScript "mysql-setup" ''
|
||||
#!${pkgs.runtimeShell} -e
|
||||
|
||||
postStart = ''
|
||||
${lib.optionalString (!hasNotify) ''
|
||||
# Wait until the MySQL server is available for use
|
||||
count=0
|
||||
while [ ! -e /run/mysqld/mysqld.sock ]
|
||||
do
|
||||
if [ $count -eq 30 ]
|
||||
${optionalString (!hasNotify) ''
|
||||
# Wait until the MySQL server is available for use
|
||||
count=0
|
||||
while [ ! -e /run/mysqld/mysqld.sock ]
|
||||
do
|
||||
if [ $count -eq 30 ]
|
||||
then
|
||||
echo "Tried 30 times, giving up..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "MySQL daemon not yet started. Waiting for 1 second..."
|
||||
count=$((count++))
|
||||
sleep 1
|
||||
done
|
||||
''}
|
||||
|
||||
if [ -f /tmp/mysql_init ]
|
||||
then
|
||||
echo "Tried 30 times, giving up..."
|
||||
exit 1
|
||||
${concatMapStrings (database: ''
|
||||
# Create initial databases
|
||||
if ! test -e "${cfg.dataDir}/${database.name}"; then
|
||||
echo "Creating initial database: ${database.name}"
|
||||
( echo 'create database `${database.name}`;'
|
||||
|
||||
${optionalString (database.schema != null) ''
|
||||
echo 'use `${database.name}`;'
|
||||
|
||||
# TODO: this silently falls through if database.schema does not exist,
|
||||
# we should catch this somehow and exit, but can't do it here because we're in a subshell.
|
||||
if [ -f "${database.schema}" ]
|
||||
then
|
||||
cat ${database.schema}
|
||||
elif [ -d "${database.schema}" ]
|
||||
then
|
||||
cat ${database.schema}/mysql-databases/*.sql
|
||||
fi
|
||||
''}
|
||||
) | ${mysql}/bin/mysql -u root -N
|
||||
fi
|
||||
'') cfg.initialDatabases}
|
||||
|
||||
${optionalString (cfg.replication.role == "master")
|
||||
''
|
||||
# Set up the replication master
|
||||
|
||||
( echo "use mysql;"
|
||||
echo "CREATE USER '${cfg.replication.masterUser}'@'${cfg.replication.slaveHost}' IDENTIFIED WITH mysql_native_password;"
|
||||
echo "SET PASSWORD FOR '${cfg.replication.masterUser}'@'${cfg.replication.slaveHost}' = PASSWORD('${cfg.replication.masterPassword}');"
|
||||
echo "GRANT REPLICATION SLAVE ON *.* TO '${cfg.replication.masterUser}'@'${cfg.replication.slaveHost}';"
|
||||
) | ${mysql}/bin/mysql -u root -N
|
||||
''}
|
||||
|
||||
${optionalString (cfg.replication.role == "slave")
|
||||
''
|
||||
# Set up the replication slave
|
||||
|
||||
( echo "stop slave;"
|
||||
echo "change master to master_host='${cfg.replication.masterHost}', master_user='${cfg.replication.masterUser}', master_password='${cfg.replication.masterPassword}';"
|
||||
echo "start slave;"
|
||||
) | ${mysql}/bin/mysql -u root -N
|
||||
''}
|
||||
|
||||
${optionalString (cfg.initialScript != null)
|
||||
''
|
||||
# Execute initial script
|
||||
# using toString to avoid copying the file to nix store if given as path instead of string,
|
||||
# as it might contain credentials
|
||||
cat ${toString cfg.initialScript} | ${mysql}/bin/mysql -u root -N
|
||||
''}
|
||||
|
||||
rm /tmp/mysql_init
|
||||
fi
|
||||
|
||||
echo "MySQL daemon not yet started. Waiting for 1 second..."
|
||||
count=$((count++))
|
||||
sleep 1
|
||||
done
|
||||
''}
|
||||
${optionalString (cfg.ensureDatabases != []) ''
|
||||
(
|
||||
${concatMapStrings (database: ''
|
||||
echo "CREATE DATABASE IF NOT EXISTS \`${database}\`;"
|
||||
'') cfg.ensureDatabases}
|
||||
) | ${mysql}/bin/mysql -u root -N
|
||||
''}
|
||||
|
||||
if [ -f /tmp/mysql_init ]
|
||||
then
|
||||
${concatMapStrings (database:
|
||||
${concatMapStrings (user:
|
||||
''
|
||||
# Create initial databases
|
||||
if ! test -e "${cfg.dataDir}/${database.name}"; then
|
||||
echo "Creating initial database: ${database.name}"
|
||||
( echo 'create database `${database.name}`;'
|
||||
|
||||
${optionalString (database ? "schema") ''
|
||||
echo 'use `${database.name}`;'
|
||||
|
||||
if [ -f "${database.schema}" ]
|
||||
then
|
||||
cat ${database.schema}
|
||||
elif [ -d "${database.schema}" ]
|
||||
then
|
||||
cat ${database.schema}/mysql-databases/*.sql
|
||||
fi
|
||||
''}
|
||||
) | ${mysql}/bin/mysql -u root -N
|
||||
fi
|
||||
'') cfg.initialDatabases}
|
||||
|
||||
${optionalString (cfg.replication.role == "master")
|
||||
''
|
||||
# Set up the replication master
|
||||
|
||||
( echo "use mysql;"
|
||||
echo "CREATE USER '${cfg.replication.masterUser}'@'${cfg.replication.slaveHost}' IDENTIFIED WITH mysql_native_password;"
|
||||
echo "SET PASSWORD FOR '${cfg.replication.masterUser}'@'${cfg.replication.slaveHost}' = PASSWORD('${cfg.replication.masterPassword}');"
|
||||
echo "GRANT REPLICATION SLAVE ON *.* TO '${cfg.replication.masterUser}'@'${cfg.replication.slaveHost}';"
|
||||
( echo "CREATE USER IF NOT EXISTS '${user.name}'@'localhost' IDENTIFIED WITH ${if isMariaDB then "unix_socket" else "auth_socket"};"
|
||||
${concatStringsSep "\n" (mapAttrsToList (database: permission: ''
|
||||
echo "GRANT ${permission} ON ${database} TO '${user.name}'@'localhost';"
|
||||
'') user.ensurePermissions)}
|
||||
) | ${mysql}/bin/mysql -u root -N
|
||||
''}
|
||||
|
||||
${optionalString (cfg.replication.role == "slave")
|
||||
''
|
||||
# Set up the replication slave
|
||||
|
||||
( echo "stop slave;"
|
||||
echo "change master to master_host='${cfg.replication.masterHost}', master_user='${cfg.replication.masterUser}', master_password='${cfg.replication.masterPassword}';"
|
||||
echo "start slave;"
|
||||
) | ${mysql}/bin/mysql -u root -N
|
||||
''}
|
||||
|
||||
${optionalString (cfg.initialScript != null)
|
||||
''
|
||||
# Execute initial script
|
||||
cat ${cfg.initialScript} | ${mysql}/bin/mysql -u root -N
|
||||
''}
|
||||
|
||||
${optionalString (cfg.rootPassword != null)
|
||||
''
|
||||
# Change root password
|
||||
|
||||
( echo "use mysql;"
|
||||
echo "update user set Password=password('$(cat ${cfg.rootPassword})') where User='root';"
|
||||
echo "flush privileges;"
|
||||
) | ${mysql}/bin/mysql -u root -N
|
||||
''}
|
||||
|
||||
rm /tmp/mysql_init
|
||||
fi
|
||||
|
||||
${optionalString (cfg.ensureDatabases != []) ''
|
||||
(
|
||||
${concatMapStrings (database: ''
|
||||
echo "CREATE DATABASE IF NOT EXISTS \`${database}\`;"
|
||||
'') cfg.ensureDatabases}
|
||||
) | ${mysql}/bin/mysql -u root -N
|
||||
''}
|
||||
|
||||
${concatMapStrings (user:
|
||||
''
|
||||
( echo "CREATE USER IF NOT EXISTS '${user.name}'@'localhost' IDENTIFIED WITH ${if isMariaDB then "unix_socket" else "auth_socket"};"
|
||||
${concatStringsSep "\n" (mapAttrsToList (database: permission: ''
|
||||
echo "GRANT ${permission} ON ${database} TO '${user.name}'@'localhost';"
|
||||
'') user.ensurePermissions)}
|
||||
) | ${mysql}/bin/mysql -u root -N
|
||||
'') cfg.ensureUsers}
|
||||
|
||||
''; # */
|
||||
'') cfg.ensureUsers}
|
||||
'';
|
||||
in
|
||||
# ensureDatbases & ensureUsers depends on this script being run as root
|
||||
# when the user has secured their mysql install
|
||||
"+${setupScript}";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ let
|
||||
|
||||
super_only = ${builtins.toJSON cfg.superOnly}
|
||||
|
||||
${optionalString (!isNull cfg.loginGroup) "login_group = ${cfg.loginGroup}"}
|
||||
${optionalString (cfg.loginGroup != null) "login_group = ${cfg.loginGroup}"}
|
||||
|
||||
login_timeout = ${toString cfg.loginTimeout}
|
||||
|
||||
@@ -24,7 +24,7 @@ let
|
||||
|
||||
sql_root = ${cfg.sqlRoot}
|
||||
|
||||
${optionalString (!isNull cfg.tls) ''
|
||||
${optionalString (cfg.tls != null) ''
|
||||
tls_cert = ${cfg.tls.cert}
|
||||
tls_key = ${cfg.tls.key}
|
||||
''}
|
||||
|
||||
@@ -108,6 +108,80 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
ensureDatabases = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = ''
|
||||
Ensures that the specified databases exist.
|
||||
This option will never delete existing databases, especially not when the value of this
|
||||
option is changed. This means that databases created once through this option or
|
||||
otherwise have to be removed manually.
|
||||
'';
|
||||
example = [
|
||||
"gitea"
|
||||
"nextcloud"
|
||||
];
|
||||
};
|
||||
|
||||
ensureUsers = mkOption {
|
||||
type = types.listOf (types.submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Name of the user to ensure.
|
||||
'';
|
||||
};
|
||||
ensurePermissions = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
description = ''
|
||||
Permissions to ensure for the user, specified as an attribute set.
|
||||
The attribute names specify the database and tables to grant the permissions for.
|
||||
The attribute values specify the permissions to grant. You may specify one or
|
||||
multiple comma-separated SQL privileges here.
|
||||
|
||||
For more information on how to specify the target
|
||||
and on which privileges exist, see the
|
||||
<link xlink:href="https://www.postgresql.org/docs/current/sql-grant.html">GRANT syntax</link>.
|
||||
The attributes are used as <code>GRANT ''${attrName} ON ''${attrValue}</code>.
|
||||
'';
|
||||
example = literalExample ''
|
||||
{
|
||||
"DATABASE nextcloud" = "ALL PRIVILEGES";
|
||||
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
|
||||
}
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
default = [];
|
||||
description = ''
|
||||
Ensures that the specified users exist and have at least the ensured permissions.
|
||||
The PostgreSQL users will be identified using peer authentication. This authenticates the Unix user with the
|
||||
same name only, and that without the need for a password.
|
||||
This option will never delete existing users or remove permissions, especially not when the value of this
|
||||
option is changed. This means that users created and permissions assigned once through this option or
|
||||
otherwise have to be removed manually.
|
||||
'';
|
||||
example = literalExample ''
|
||||
[
|
||||
{
|
||||
name = "nextcloud";
|
||||
ensurePermissions = {
|
||||
"DATABASE nextcloud" = "ALL PRIVILEGES";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "superuser";
|
||||
ensurePermissions = {
|
||||
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
|
||||
};
|
||||
}
|
||||
]
|
||||
'';
|
||||
};
|
||||
|
||||
enableTCPIP = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
@@ -259,17 +333,30 @@ in
|
||||
# Wait for PostgreSQL to be ready to accept connections.
|
||||
postStart =
|
||||
''
|
||||
while ! ${pkgs.sudo}/bin/sudo -u ${cfg.superUser} psql --port=${toString cfg.port} -d postgres -c "" 2> /dev/null; do
|
||||
PSQL="${pkgs.sudo}/bin/sudo -u ${cfg.superUser} psql --port=${toString cfg.port}"
|
||||
|
||||
while ! $PSQL -d postgres -c "" 2> /dev/null; do
|
||||
if ! kill -0 "$MAINPID"; then exit 1; fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
if test -e "${cfg.dataDir}/.first_startup"; then
|
||||
${optionalString (cfg.initialScript != null) ''
|
||||
${pkgs.sudo}/bin/sudo -u ${cfg.superUser} psql -f "${cfg.initialScript}" --port=${toString cfg.port} -d postgres
|
||||
$PSQL -f "${cfg.initialScript}" -d postgres
|
||||
''}
|
||||
rm -f "${cfg.dataDir}/.first_startup"
|
||||
fi
|
||||
'' + optionalString (cfg.ensureDatabases != []) ''
|
||||
${concatMapStrings (database: ''
|
||||
$PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${database}'" | grep -q 1 || $PSQL -tAc "CREATE DATABASE ${database}"
|
||||
'') cfg.ensureDatabases}
|
||||
'' + ''
|
||||
${concatMapStrings (user: ''
|
||||
$PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc "CREATE USER ${user.name}"
|
||||
${concatStringsSep "\n" (mapAttrsToList (database: permission: ''
|
||||
$PSQL -tAc "GRANT ${permission} ON ${database} TO ${user.name}"
|
||||
'') user.ensurePermissions)}
|
||||
'') cfg.ensureUsers}
|
||||
'';
|
||||
|
||||
unitConfig.RequiresMountsFor = "${cfg.dataDir}";
|
||||
|
||||
@@ -42,11 +42,11 @@
|
||||
whether PostgreSQL works by running <command>psql</command>:
|
||||
|
||||
<screen>
|
||||
$ psql
|
||||
<prompt>$ </prompt>psql
|
||||
psql (9.2.9)
|
||||
Type "help" for help.
|
||||
|
||||
alice=>
|
||||
<prompt>alice=></prompt>
|
||||
</screen>
|
||||
-->
|
||||
|
||||
|
||||
Reference in New Issue
Block a user