New service: InfluxDB

This commit is contained in:
Peter Jones 2019-02-20 11:26:32 -07:00
parent b1ed1aff0f
commit c45f05c96c
No known key found for this signature in database
GPG key ID: 9DAFAA8D01941E49
7 changed files with 326 additions and 0 deletions

View file

@ -21,6 +21,10 @@ Module List
Start and manage PostgreSQL, including automatic user and database
creation.
* `phoebe.services.influxdb`:
Start and manage InfluxDB, including users and databases.
* `phoebe.services.rails`:
Configure and manage Ruby on Rails applications. Includes a

View file

@ -2,6 +2,7 @@
{
imports = [
./influxdb
./postgresql
];
}

View file

@ -0,0 +1,23 @@
#!/bin/sh
################################################################################
set -e
set -u
################################################################################
if [ $# -ne 1 ]; then
>&2 echo "ERROR: must give database name."
exit 1;
fi
################################################################################
# List all databases:
databases() {
influx -format csv -execute "show databases" | \
grep -E '^databases,' | sed 's/^databases,//'
}
################################################################################
if ! (databases | grep --fixed-strings --line-regexp --quiet "$1"); then
influx -execute "create database $1"
fi

View file

@ -0,0 +1,40 @@
#!/bin/sh
################################################################################
set -e
set -u
################################################################################
if [ $# -ne 3 ]; then
>&2 echo "Usage: creategrant.sh user database privilege"
exit 1
fi
################################################################################
name=$(echo "$1" | tr --complement --delete "a-zA-Z0-9_-")
db=$(echo "$2" | tr --complement --delete "a-zA-Z0-9_-")
priv=$3
################################################################################
privileges() {
influx -format csv -execute "SHOW GRANTS FOR \"${name}\"" | \
grep -v "^database,privilege$" | \
sed 's/^/,/' # Add for anchoring.
}
################################################################################
privilege_for_db() {
privileges | \
grep --fixed-strings ",${db}," | \
head -n 1 | sed -e 's/^,//' -e 's/ .*$//' | cut -d, -f2
}
################################################################################
match=$(privilege_for_db)
if [ -z "$match" ]; then
influx -execute "GRANT ${priv} ON \"${db}\" TO \"${name}\""
elif [ "$priv" != "$match" ]; then
influx -execute "REVOKE ALL ON \"${db}\" FROM \"${name}\""
influx -execute "GRANT ${priv} ON \"${db}\" TO \"${name}\""
fi

View file

@ -0,0 +1,61 @@
#!/bin/sh
################################################################################
set -e
set -u
################################################################################
if [ $# -ne 3 ]; then
>&2 echo "ERROR: Usage: createuser.sh name file isadmin"
exit 1
fi
################################################################################
name=$(echo "$1" | tr --complement --delete "a-zA-Z0-9_-")
pass=$(head -n 1 "$2" | tr --delete "'")
isadmin=$3
################################################################################
# List all users:
users() {
influx -format csv -execute "show users" | \
grep -Ev '^user,admin$' | \
sed 's/^/,/' # Allow searches to be anchored.
}
################################################################################
find_user() {
user=$1
users | \
grep --fixed-strings --ignore-case ",${user}," | \
head -n 1 | \
sed 's/^,//'
}
################################################################################
match=$(find_user "$name")
if [ -z "${match}" ]; then
if [ "$isadmin" -eq 1 ]; then
# Need to create admin user. Needs to be a separate branch
# because this is the only query allowed when authentication is
# enabled but there are no users yet.
match="${name},true"
influx -execute "CREATE USER ${name} WITH PASSWORD '${pass}' WITH ALL PRIVILEGES"
else
# Need to create user a regular user.
match="${name},false"
influx -execute "CREATE USER ${name} WITH PASSWORD '${pass}'"
fi
else
# Need to update password:
influx -execute "SET PASSWORD FOR \"${name}\" = '${pass}'"
fi
# Double check the access level:
if [ "$isadmin" -eq 1 ] && [ "$match" = "${name},false" ]; then
influx -execute "GRANT ALL PRIVILEGES TO \"${name}\""
elif [ "$isadmin" -eq 0 ] && [ "$match" = "${name},true" ]; then
influx -execute "REVOKE ALL PRIVILEGES FROM \"${name}\""
fi

View file

@ -0,0 +1,176 @@
# Configure InfluxDB:
{ config, lib, pkgs, ...}:
with lib;
let
cfg = config.phoebe.services.influxdb;
plib = config.phoebe.lib;
scripts = import ./scripts.nix { inherit config lib pkgs; };
usernameRe = "^[a-zA-Z0-9_-]+$";
##############################################################################
# User accounts:
account = { name, ... }: {
options = {
name = mkOption {
type = types.strMatching usernameRe;
example = "jdoe";
description = "Username for the accout.";
};
passwordFile = mkOption {
type = types.path;
example = "/run/keys/influxdb-jdoe";
description = ''
File containing the account password. Note that the
password may not contain single quotes. If any single
quotes are present they will be silently removed.
'';
};
isAdmin = mkOption {
type = types.bool;
default = false;
example = true;
description = "Whether to grant full admin rights to this user.";
};
};
config = {
name = mkDefault name;
};
};
##############################################################################
# Privileges:
privilege = { name, ...}: {
options = {
user = mkOption {
type = types.strMatching usernameRe;
example = "jdoe";
description = "User to grant privileges to.";
};
access = mkOption {
type = types.enum [ "READ" "WRITE" "ALL" ];
example = "read";
description = "Grant privilege. One of READ, WRITE, or ALL.";
};
};
config = {
user = mkDefault name;
};
};
##############################################################################
# Databases:
database = { name, ... }: {
options = {
name = mkOption {
type = types.strMatching usernameRe;
example = "my_database";
description = "Database name.";
};
privileges = mkOption {
type = types.attrsOf (types.submodule privilege);
default = { };
description = "Privileges to grant for this database.";
};
};
config = {
name = mkDefault name;
};
};
##############################################################################
# Create missing users:
createuser = name: file: isadmin: ''
${scripts}/bin/createuser.sh "${name}" "${file}" "${toString isadmin}"
'';
##############################################################################
# Create a missing database:
createdb = database: ''
${scripts}/bin/createdb.sh "${database.name}"
${concatMapStringsSep "\n" (p: creategrant p.user database.name p.access)
(attrValues database.privileges)}
'';
##############################################################################
# Create missing grants:
creategrant = name: database: priv:
optionalString (cfg.accounts ? "${name}") ''
${scripts}/bin/creategrant.sh "${name}" "${database}" "${priv}"
'';
##############################################################################
# A list of services that need to be waited on for keys:
keyservices =
optionals cfg.auth.enable (plib.keyService cfg.superuser.passwordFile) ++
concatMap (a: plib.keyService a.passwordFile) (attrValues cfg.accounts);
in
{
#### Interface:
options.phoebe.services.influxdb = {
enable = mkEnableOption "InfluxDB";
auth.enable = mkEnableOption "Authentication.";
superuser.name = mkOption {
type = types.strMatching usernameRe;
default = config.services.influxdb.user;
example = "root";
description = "The name of the InfluxDB internal admin user.";
};
superuser.passwordFile = mkOption {
type = types.path;
example = "/run/keys/influxdb-superuser";
description = "File holding the InfluxDB internal admin password.";
};
accounts = mkOption {
type = types.attrsOf (types.submodule account);
default = { };
description = "User accounts.";
};
databases = mkOption {
type = types.attrsOf (types.submodule database);
default = { };
description = "Databases to create.";
};
};
#### Implementation:
config = mkIf cfg.enable {
services.influxdb.enable = true;
services.influxdb.extraConfig.http.auth-enabled = cfg.auth.enable;
systemd.services.influxdb-account-manager = {
description = "InfluxDB accounts and databases";
wantedBy = [ "multi-user.target" ];
after = [ "influxdb.service" ] ++ keyservices;
wants = keyservices;
path = [ config.services.influxdb.package ];
script =
# Configure authentication:
(optionalString cfg.auth.enable ''
export INFLUX_USERNAME="${cfg.superuser.name}"
export INFLUX_PASSWORD=$(head -n 1 "${cfg.superuser.passwordFile}")
'') +
# Create superuser:
(optionalString cfg.auth.enable
(createuser cfg.superuser.name cfg.superuser.passwordFile true)) +
# Create all other users:
(concatMapStringsSep "\n"
(a: createuser a.name a.passwordFile a.isAdmin) (attrValues cfg.accounts)) +
# Create databases and grants:
(concatMapStringsSep "\n" createdb (attrValues cfg.databases));
};
};
}

View file

@ -0,0 +1,21 @@
{ config, lib, pkgs, ...}:
pkgs.stdenvNoCC.mkDerivation {
name = "influx-account-manager";
phases = [ "installPhase" "fixupPhase" ];
installPhase = ''
# Substitution variables:
mkdir -p $out/bin
install -m 0555 ${./createdb.sh} $out/bin/createdb.sh
install -m 0555 ${./createuser.sh} $out/bin/createuser.sh
install -m 0555 ${./creategrant.sh} $out/bin/creategrant.sh
'';
meta = with lib; {
description = "Automatically create InfluxDB databases and users as needed.";
homepage = https://git.devalot.com/pjones/phoebe/;
maintainers = with maintainers; [ pjones ];
platforms = platforms.all;
};
}