Added chute job, working on discourse
This commit is contained in:
parent
2503b640c9
commit
e66e349c3b
|
@ -115,15 +115,16 @@ in {
|
||||||
challenge-path = mkOption {
|
challenge-path = mkOption {
|
||||||
type = str;
|
type = str;
|
||||||
description = "Web-accessible path for responding to ACME challenges.";
|
description = "Web-accessible path for responding to ACME challenges.";
|
||||||
default = "/run/fudo-acme/challenge";
|
# Sigh. Leave it the same as nginx default, so it works whether or not
|
||||||
|
# nginx feels like helping or not.
|
||||||
|
default = "/var/lib/acme/acme-challenge";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
security.acme.certs = mapAttrs (domain: domainOpts: {
|
security.acme.certs = mapAttrs (domain: domainOpts: {
|
||||||
email = domainOpts.admin-email;
|
email = domainOpts.admin-email;
|
||||||
# IF you won't do it all the time, nginx, you can't do it at all
|
webroot = cfg.challenge-path;
|
||||||
webroot = mkForce cfg.challenge-path;
|
|
||||||
extraDomainNames = domainOpts.extra-domains;
|
extraDomainNames = domainOpts.extra-domains;
|
||||||
}) localDomains;
|
}) localDomains;
|
||||||
|
|
||||||
|
@ -136,18 +137,6 @@ in {
|
||||||
recommendedOptimisation = true;
|
recommendedOptimisation = true;
|
||||||
recommendedTlsSettings = true;
|
recommendedTlsSettings = true;
|
||||||
recommendedProxySettings = true;
|
recommendedProxySettings = true;
|
||||||
|
|
||||||
virtualHosts.${config.instance.host-fqdn} = {
|
|
||||||
serverAliases = attrNames localDomains;
|
|
||||||
locations = {
|
|
||||||
"/.well-known/acme-challenge" = {
|
|
||||||
root = cfg.challenge-path;
|
|
||||||
};
|
|
||||||
"/" = {
|
|
||||||
return = "301 https://$host$request_uri";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||||
|
|
|
@ -72,8 +72,8 @@ let
|
||||||
|
|
||||||
password-setter-script = user: password-file: sql-file: ''
|
password-setter-script = user: password-file: sql-file: ''
|
||||||
unset PASSWORD
|
unset PASSWORD
|
||||||
if [ ! -f ${password-file} ]; then
|
if [ ! -r ${password-file} ]; then
|
||||||
echo "file does not exist: ${password-file}"
|
echo "unable to read file: ${password-file}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
PASSWORD=$(cat ${password-file})
|
PASSWORD=$(cat ${password-file})
|
||||||
|
@ -82,7 +82,7 @@ let
|
||||||
'';
|
'';
|
||||||
|
|
||||||
passwords-setter-script = users:
|
passwords-setter-script = users:
|
||||||
pkgs.writeScript "postgres-set-passwords.sh" ''
|
pkgs.writeShellScript "postgres-set-passwords.sh" ''
|
||||||
if [ $# -ne 1 ]; then
|
if [ $# -ne 1 ]; then
|
||||||
echo "usage: $0 output-file.sql"
|
echo "usage: $0 output-file.sql"
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -311,7 +311,7 @@ in {
|
||||||
postgresql-password-setter = let
|
postgresql-password-setter = let
|
||||||
passwords-script = passwords-setter-script cfg.users;
|
passwords-script = passwords-setter-script cfg.users;
|
||||||
password-wrapper-script =
|
password-wrapper-script =
|
||||||
pkgs.writeScript "password-script-wrapper.sh" ''
|
pkgs.writeShellScript "password-script-wrapper.sh" ''
|
||||||
TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t postgres-XXXXXXXXXX)
|
TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t postgres-XXXXXXXXXX)
|
||||||
echo "using temp dir $TMPDIR"
|
echo "using temp dir $TMPDIR"
|
||||||
PASSWORD_SQL_FILE=$TMPDIR/user-passwords.sql
|
PASSWORD_SQL_FILE=$TMPDIR/user-passwords.sql
|
||||||
|
@ -334,18 +334,20 @@ in {
|
||||||
"A service to set postgresql user passwords after the server has started.";
|
"A service to set postgresql user passwords after the server has started.";
|
||||||
after = [ "postgresql.service" ] ++ cfg.required-services;
|
after = [ "postgresql.service" ] ++ cfg.required-services;
|
||||||
requires = [ "postgresql.service" ] ++ cfg.required-services;
|
requires = [ "postgresql.service" ] ++ cfg.required-services;
|
||||||
|
wantedBy = [ "postgresql.service" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
User = config.services.postgresql.superUser;
|
User = config.services.postgresql.superUser;
|
||||||
|
ExecStart = "${password-wrapper-script}";
|
||||||
};
|
};
|
||||||
partOf = [ cfg.systemd-target ];
|
partOf = [ cfg.systemd-target ];
|
||||||
script = "${password-wrapper-script}";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
postgresql = {
|
postgresql = {
|
||||||
requires = cfg.required-services;
|
requires = cfg.required-services;
|
||||||
after = cfg.required-services;
|
after = cfg.required-services;
|
||||||
partOf = [ cfg.systemd-target ];
|
partOf = [ cfg.systemd-target ];
|
||||||
|
wants = [ "postgresql-password-setter.service" ];
|
||||||
|
|
||||||
postStart = let
|
postStart = let
|
||||||
allow-user-login = user: "ALTER ROLE ${user} WITH LOGIN;";
|
allow-user-login = user: "ALTER ROLE ${user} WITH LOGIN;";
|
||||||
|
|
|
@ -35,7 +35,8 @@ let
|
||||||
secret-service = target-host: secret-name:
|
secret-service = target-host: secret-name:
|
||||||
{ source-file, target-file, user, group, permissions, ... }: {
|
{ source-file, target-file, user, group, permissions, ... }: {
|
||||||
description = "decrypt secret ${secret-name} for ${target-host}.";
|
description = "decrypt secret ${secret-name} for ${target-host}.";
|
||||||
wantedBy = [ "default.target" ];
|
wantedBy = [ cfg.secret-target ];
|
||||||
|
before = [ cfg.secret-target ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
ExecStart = let
|
ExecStart = let
|
||||||
|
@ -163,6 +164,12 @@ in {
|
||||||
"Paths which contain (only) secrets. The contents will be reabable by the secret-group.";
|
"Paths which contain (only) secrets. The contents will be reabable by the secret-group.";
|
||||||
default = [ ];
|
default = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
secret-target = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Target indicating that all secrets are available.";
|
||||||
|
default = "fudo-secrets.target";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
@ -219,6 +226,15 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
targets = let
|
||||||
|
strip-ext = filename: head (builtins.match "^(.+)[.]target$" filename);
|
||||||
|
in {
|
||||||
|
${strip-ext cfg.secret-target} = {
|
||||||
|
description = "Target indicating that all Fudo secrets are available.";
|
||||||
|
wantedBy = [ "default.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
paths.fudo-secrets-watcher = mkIf (length cfg.secret-paths > 0) {
|
paths.fudo-secrets-watcher = mkIf (length cfg.secret-paths > 0) {
|
||||||
wantedBy = [ "default.target" ];
|
wantedBy = [ "default.target" ];
|
||||||
description = "Watch fudo secret paths, and correct perms on changes.";
|
description = "Watch fudo secret paths, and correct perms on changes.";
|
||||||
|
|
|
@ -80,7 +80,7 @@ let
|
||||||
description = "Environment variables supplied to this service.";
|
description = "Environment variables supplied to this service.";
|
||||||
default = { };
|
default = { };
|
||||||
};
|
};
|
||||||
environment-file = mkOption {
|
environmentFile = mkOption {
|
||||||
type = nullOr str;
|
type = nullOr str;
|
||||||
description =
|
description =
|
||||||
"File containing environment variables supplied to this service.";
|
"File containing environment variables supplied to this service.";
|
||||||
|
@ -423,7 +423,7 @@ in {
|
||||||
ProtectKernelLogs = opts.protectKernelLogs;
|
ProtectKernelLogs = opts.protectKernelLogs;
|
||||||
KeyringMode = opts.keyringMode;
|
KeyringMode = opts.keyringMode;
|
||||||
EnvironmentFile =
|
EnvironmentFile =
|
||||||
mkIf (opts.environment-file != null) opts.environment-file;
|
mkIf (opts.environmentFile != null) opts.environmentFile;
|
||||||
|
|
||||||
# This is more complicated than it looks...
|
# This is more complicated than it looks...
|
||||||
# CapabilityBoundingSet = restrict-capabilities opts.requiredCapabilities;
|
# CapabilityBoundingSet = restrict-capabilities opts.requiredCapabilities;
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
currencyOpts = { ... }: {
|
||||||
|
options = {
|
||||||
|
stop-percentile = mkOption {
|
||||||
|
type = int;
|
||||||
|
description = "Percentile of observed max at which to sell.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
stageOpts = { ... }: {
|
||||||
|
options = with types; {
|
||||||
|
currencies = mkOption {
|
||||||
|
type = attrsOf (submodule currencyOpts);
|
||||||
|
description = "Map of cryptocurrencies to chute currency settings.";
|
||||||
|
};
|
||||||
|
|
||||||
|
package = mkOption {
|
||||||
|
type = package;
|
||||||
|
description = "Chute package to use for this stage.";
|
||||||
|
default = pkgs.chute;
|
||||||
|
};
|
||||||
|
|
||||||
|
credential-file = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = ''
|
||||||
|
Path to a host-local env file containing definitions for:
|
||||||
|
|
||||||
|
COINBASE_API_HOSTNAME
|
||||||
|
COINBASE_API_SECRET
|
||||||
|
COINBASE_API_PASSPHRASE
|
||||||
|
COINBASE_API_KEY
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
concatMapAttrs = f: attrs:
|
||||||
|
foldr (a: b: a // b) {} (mapAttrsToList f attrs);
|
||||||
|
|
||||||
|
chute-job-definition = { stage, credential-file, currency, stop-at-percent, package }: {
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
wantedBy = [ "chute.target" ];
|
||||||
|
partOf = [ "chute.target" ];
|
||||||
|
description = "Chute ${stage} job for ${currency}";
|
||||||
|
path = [ package ];
|
||||||
|
environmentFile = credential-file;
|
||||||
|
execStart = "chute --currency=${currency} --stop-at-percent=${stop-at-percent}";
|
||||||
|
privateNetwork = false;
|
||||||
|
addressFamilies = [ "AF_INET" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.informis.chute = with types; {
|
||||||
|
enable = mkEnableOption "Enable Chute cryptocurrency parachute.";
|
||||||
|
|
||||||
|
stages = mkOption {
|
||||||
|
type = attrsOf (submodule stageOpts);
|
||||||
|
description = "Map of stage names to stage options.";
|
||||||
|
example = {
|
||||||
|
staging = {
|
||||||
|
credential-file = "/path/to/credentials-file";
|
||||||
|
currencies = {
|
||||||
|
btc.stop-percentile = 90;
|
||||||
|
ada.stop-percentile = 85;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf (cfg.enable) {
|
||||||
|
fudo = {
|
||||||
|
system.services = concatMapAttrs (stage: stageOpts:
|
||||||
|
mapAttrs (currency: currencyOpts: {
|
||||||
|
"chute-${stage}-${currency}" = chute-job-definition {
|
||||||
|
inherit stage currency;
|
||||||
|
credential-file = stageOpts.credential-file;
|
||||||
|
package = stageOpts.package;
|
||||||
|
stop-at-percent = currencyOpts.stop-percentile;
|
||||||
|
};
|
||||||
|
}) stageOpts.currencies) cfg.stages;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
./chute.nix
|
||||||
./cl-gemini.nix
|
./cl-gemini.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue