225 lines
7.2 KiB
Nix
225 lines
7.2 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
cfg = config.services.flaredb;
|
|
tomlFormat = pkgs.formats.toml { };
|
|
stripLeadingZeros = digits:
|
|
if digits == "" then ""
|
|
else if lib.hasPrefix "0" digits then stripLeadingZeros (lib.removePrefix "0" digits)
|
|
else digits;
|
|
numericIdString = value:
|
|
let
|
|
captures = builtins.match ".*?([0-9]+)$" value;
|
|
digits =
|
|
if captures == null
|
|
then throw "services.flaredb.nodeId must end with digits (got '${value}')"
|
|
else builtins.elemAt captures 0;
|
|
normalized = stripLeadingZeros digits;
|
|
in
|
|
if normalized == "" then "0" else normalized;
|
|
apiAddrArg =
|
|
if cfg.apiAddr != null
|
|
then cfg.apiAddr
|
|
else "0.0.0.0:${toString cfg.port}";
|
|
peerArgs = map
|
|
(peer:
|
|
let
|
|
parts = lib.splitString "=" peer;
|
|
rawId =
|
|
if builtins.length parts == 2
|
|
then builtins.elemAt parts 0
|
|
else throw "services.flaredb.initialPeers entries must be 'storeId=host:port' (got '${peer}')";
|
|
peerAddr = builtins.elemAt parts 1;
|
|
in
|
|
"--peer ${numericIdString rawId}=${peerAddr}")
|
|
cfg.initialPeers;
|
|
chainfirePeerApiEndpoints =
|
|
if (config.services ? chainfire) && config.services.chainfire.enable
|
|
then
|
|
map
|
|
(peer:
|
|
let
|
|
parts = lib.splitString "=" peer;
|
|
raftAddr =
|
|
if builtins.length parts == 2
|
|
then builtins.elemAt parts 1
|
|
else throw "services.chainfire.initialPeers entries must be 'nodeId=host:port' (got '${peer}')";
|
|
captures = builtins.match "(.+):[0-9]+" raftAddr;
|
|
host =
|
|
if captures == null
|
|
then throw "services.chainfire.initialPeers raft address must be host:port (got '${raftAddr}')"
|
|
else builtins.elemAt captures 0;
|
|
in
|
|
"${host}:${toString config.services.chainfire.port}")
|
|
config.services.chainfire.initialPeers
|
|
else [];
|
|
defaultPdAddr =
|
|
if cfg.pdAddr != null
|
|
then cfg.pdAddr
|
|
else if (config.services ? chainfire) && config.services.chainfire.enable
|
|
then
|
|
if config.services.chainfire.apiAddr != null
|
|
then config.services.chainfire.apiAddr
|
|
else "127.0.0.1:${toString config.services.chainfire.port}"
|
|
else "127.0.0.1:2379";
|
|
pdAddrMatch = builtins.match "(.*):([0-9]+)" defaultPdAddr;
|
|
pdHost =
|
|
if pdAddrMatch == null
|
|
then throw "services.flaredb.pdAddr must be host:port (got '${defaultPdAddr}')"
|
|
else builtins.elemAt pdAddrMatch 0;
|
|
pdPort =
|
|
if pdAddrMatch == null
|
|
then throw "services.flaredb.pdAddr must be host:port (got '${defaultPdAddr}')"
|
|
else builtins.elemAt pdAddrMatch 1;
|
|
derivedPdEndpoints = lib.unique ([ defaultPdAddr ] ++ chainfirePeerApiEndpoints);
|
|
localDependencies =
|
|
lib.optionals ((config.services ? chainfire) && config.services.chainfire.enable) [ "chainfire.service" ];
|
|
flaredbConfigFile = tomlFormat.generate "flaredb.toml" (lib.recursiveUpdate {
|
|
addr = apiAddrArg;
|
|
http_addr = "0.0.0.0:${toString cfg.httpPort}";
|
|
data_dir = toString cfg.dataDir;
|
|
pd_addr = defaultPdAddr;
|
|
pd_endpoints = derivedPdEndpoints;
|
|
log_level = "info";
|
|
namespace_modes = {
|
|
default = "strong";
|
|
validation = "eventual";
|
|
plasmavmc = "strong";
|
|
lightningstor = "eventual";
|
|
prismnet = "eventual";
|
|
flashdns = "eventual";
|
|
fiberlb = "eventual";
|
|
creditservice = "strong";
|
|
k8shost = "eventual";
|
|
};
|
|
} cfg.settings);
|
|
in
|
|
{
|
|
options.services.flaredb = {
|
|
enable = lib.mkEnableOption "flaredb distributed SQL/KV service";
|
|
|
|
nodeId = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = config.networking.hostName;
|
|
description = "Unique node identifier for the Raft cluster";
|
|
};
|
|
|
|
port = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 2479;
|
|
description = "Port for flaredb API";
|
|
};
|
|
|
|
raftPort = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 2480;
|
|
description = "Port for flaredb Raft protocol";
|
|
};
|
|
|
|
raftAddr = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
description = "Full address for Raft (host:port). If null, uses 0.0.0.0:raftPort";
|
|
};
|
|
|
|
apiAddr = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
description = "Full address for API (host:port). If null, uses 0.0.0.0:port";
|
|
};
|
|
|
|
initialPeers = lib.mkOption {
|
|
type = lib.types.listOf lib.types.str;
|
|
default = [];
|
|
description = "Initial Raft peers for cluster bootstrap";
|
|
example = [ "node01=10.0.0.1:2480" "node02=10.0.0.2:2480" ];
|
|
};
|
|
|
|
httpPort = lib.mkOption {
|
|
type = lib.types.port;
|
|
default = 8082;
|
|
description = "Port for flaredb HTTP/admin API (used for cluster join)";
|
|
};
|
|
|
|
pdAddr = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.str;
|
|
default = null;
|
|
description = "ChainFire placement-driver endpoint (host:port). Defaults to the local ChainFire API when enabled.";
|
|
example = "10.0.0.1:2379";
|
|
};
|
|
|
|
dataDir = lib.mkOption {
|
|
type = lib.types.path;
|
|
default = "/var/lib/flaredb";
|
|
description = "Data directory for flaredb";
|
|
};
|
|
|
|
dbaasEnabled = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = "Expose FlareDB as the managed DBaaS data plane";
|
|
};
|
|
|
|
settings = lib.mkOption {
|
|
type = lib.types.attrs;
|
|
default = {};
|
|
description = "Additional configuration settings";
|
|
};
|
|
|
|
package = lib.mkOption {
|
|
type = lib.types.package;
|
|
default = pkgs.flaredb-server or (throw "flaredb-server package not found");
|
|
description = "Package to use for flaredb";
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
# Create system user
|
|
users.users.flaredb = {
|
|
isSystemUser = true;
|
|
group = "flaredb";
|
|
description = "FlareDB service user";
|
|
home = cfg.dataDir;
|
|
};
|
|
|
|
users.groups.flaredb = {};
|
|
|
|
# Create systemd service
|
|
systemd.services.flaredb = {
|
|
description = "FlareDB Distributed Database Service";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "network.target" ] ++ localDependencies;
|
|
requires = localDependencies;
|
|
|
|
environment = {
|
|
FLAREDB_DBAAS_ENABLED = if cfg.dbaasEnabled then "true" else "false";
|
|
};
|
|
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
User = "flaredb";
|
|
Group = "flaredb";
|
|
Restart = "on-failure";
|
|
RestartSec = "10s";
|
|
|
|
# State directory management
|
|
StateDirectory = "flaredb";
|
|
StateDirectoryMode = "0750";
|
|
|
|
# Security hardening
|
|
NoNewPrivileges = true;
|
|
PrivateTmp = true;
|
|
ProtectSystem = "strict";
|
|
ProtectHome = true;
|
|
ReadWritePaths = [ cfg.dataDir ];
|
|
|
|
ExecStartPre = "${pkgs.bash}/bin/bash -lc 'for i in $(seq 1 60); do ${pkgs.netcat}/bin/nc -z ${lib.escapeShellArg pdHost} ${lib.escapeShellArg pdPort} && exit 0; sleep 1; done; echo \"timed out waiting for FlareDB PD ${defaultPdAddr}\" >&2; exit 1'";
|
|
ExecStart = lib.concatStringsSep " " ([
|
|
"${cfg.package}/bin/flaredb-server"
|
|
"--config ${flaredbConfigFile}"
|
|
"--store-id ${numericIdString cfg.nodeId}"
|
|
] ++ peerArgs);
|
|
};
|
|
};
|
|
};
|
|
}
|