Browse Source

Add logging framework and split up runit files

Signed-off-by: Magic_RB <magic_rb@redalder.org>
master
Magic_RB 1 month ago
parent
commit
8bb5fd19bb
Signed by: Magic_RB GPG Key ID: 08D5287CC5DDCA0E
6 changed files with 471 additions and 269 deletions
  1. +239
    -125
      modules/init.nix
  2. +27
    -82
      modules/runit/default.nix
  3. +64
    -0
      modules/runit/finish.nix
  4. +44
    -0
      modules/runit/log.nix
  5. +73
    -0
      modules/runit/run.nix
  6. +24
    -62
      modules/services/gitea.nix

+ 239
- 125
modules/init.nix View File

@ -2,6 +2,221 @@
with lib;
let
cfg = config.init;
log = mkOption {
description = "Logging settings.";
type = with types; nullOr (submodule ({ config, ... }:
let
cfg = config;
in
{
options = {
file = mkOption {
description = "Log to a plain file, without rotation.";
type = nullOr (submodule {
options = {
dst = mkOption {
description = "The file to which to log to.";
type = path;
};
rotate = mkOption {
description = "The size after which the file should rotated in kilo bytes.";
type = int;
default = 0; # 1 MB
};
};
config = {
assertions = [
{
assertion = cfg.fle.rotate >= 0;
message = "init.service.<name>.log.file.rotate can't be less than 0";
}
];
};
});
default = null;
};
syslog = mkOption {
description = "Log via syslog, either to a UDS or over TCP/UDP.";
type = nullOr (submodule ({ config, ... }:
let
cfg = config;
in {
options = {
type = mkOption {
description = "Syslog type, UDS, TCP, or UDP.";
type = enum [ "uds" "tcp" "udp" ];
};
dst = mkOption {
description = "The endpoint to log to, format depends on the type.";
type = str;
};
time = mkOption {
description = "Whether the complete sender timestamp should be included in log messages";
type = bool;
default = false;
};
host = mkOption {
description = "Whether the hostname should be included in log messages.";
type = bool;
default = true;
};
timeQuality = mkOption {
description = "Whether time quality information should be included in the log messages.";
type = bool;
default = false;
};
tag = mkOption {
description = "Every message will be marked with this tag.";
type = nullOr str;
default = null;
};
priority = mkOption {
description = "Mark every message with a priority.";
type = nullOr str;
default = null;
};
};
config = {
dst =
if cfg.type == "uds" then
mkDefault "/dev/log"
else if cfg.type == "tcp" || cfg.type == "udp" then
mkDefault "127.0.0.1:514"
else
abort "Unknown syslog type, this should have been caught by the module system!";
};
}));
default = null;
};
};
config = {};
}));
default = {};
};
ensureSomething = mkOption {
description = "Files or directories which need to exist before service is started, no overwriting though.";
default = {};
type = with types;
submodule {
options = {
link = mkOption {
type = attrsOf (submodule {
options = {
src = mkOption {
description = "The source of the link.";
type = path;
};
dst = mkOption {
description = "The destination of the link.";
type = path;
};
persistent = mkOption {
description = "Whether the created something should be kept after service stop.";
type = bool;
default = false;
};
};
});
default = {};
};
copy = mkOption {
type = attrsOf (submodule {
options = {
src = mkOption {
description = "The source of the copy.";
type = path;
};
dst = mkOption {
description = "The destination of copy.";
type = path;
};
persistent = mkOption {
description = "Whether the created something should be kept after service stop.";
type = bool;
default = false;
};
# TODO add mode?
};
});
default = {};
};
linkFarm = mkOption {
type = attrsOf (submodule {
options = {
src = mkOption {
description = "The source of the link farm.";
type = path;
};
dst = mkOption {
description = "The destination of the link farm.";
type = path;
};
persistent = mkOption {
description = "Whether the created something should be kept after service stop.";
type = bool;
default = false;
};
};
});
default = {};
};
exec = mkOption {
description = "Execute file to create a file or directory.";
type = attrsOf (submodule {
options = {
dst = mkOption {
description = "Where should the executable output and what file or folder to check whether it should be run.";
type = path;
};
executable = mkOption {
description = "The path to the executable to execute. Use $out.";
type = path;
};
persistent = mkOption {
description = "Whether the created something should be kept after service stop.";
type = bool;
default = false;
};
};
});
default = {};
};
create = mkOption {
description = "Creates either an empty file or directory.";
type = attrsOf (submodule {
options = {
type = mkOption {
description = "Whether to create a direcotroy or file.";
type = enum [ "directory" "file" ];
};
mode = mkOption {
description = "Mode to set for the new creation, if set to `null`, its up to the implementation.";
default = null;
type = nullOr str;
};
owner = mkOption {
description = "Owner of new creation.";
default = "root:root";
type = str;
};
dst = mkOption {
description = "Wheret to create it.";
type = path;
};
persistent = mkOption {
description = "Whether the created something should be kept after service stop.";
type = bool;
default = false;
};
};
});
default = {};
};
};
};
};
in
{
options.init = {
@ -17,6 +232,10 @@ in
description = "init script.";
type = types.path;
};
shutdown = mkOption {
description = "A script which successfully shuts down the system.";
type = types.path;
};
services = mkOption {
type = types.attrsOf (types.submodule {
options = {
@ -26,134 +245,21 @@ in
default = [];
};
ensureSomething = mkOption {
description = "Files or directories which need to exist before service is started, no overwriting though";
default = {};
type = with types;
submodule {
options = {
link = mkOption {
type = attrsOf (submodule {
options = {
src = mkOption {
description = "The source of the link.";
type = path;
};
dst = mkOption {
description = "The destination of the link.";
type = path;
};
persistent = mkOption {
description = "Whether the created something should be kept after service stop.";
type = bool;
default = false;
};
};
});
default = {};
};
copy = mkOption {
type = attrsOf (submodule {
options = {
src = mkOption {
description = "The source of the copy.";
type = path;
};
dst = mkOption {
description = "The destination of copy.";
type = path;
};
persistent = mkOption {
description = "Whether the created something should be kept after service stop.";
type = bool;
default = false;
};
# TODO add mode?
};
});
default = {};
};
linkFarm = mkOption {
type = attrsOf (submodule {
options = {
src = mkOption {
description = "The source of the link farm.";
type = path;
};
dst = mkOption {
description = "The destination of the link farm.";
type = path;
};
persistent = mkOption {
description = "Whether the created something should be kept after service stop.";
type = bool;
default = false;
};
};
});
default = {};
};
exec = mkOption {
description = "Execute file to create a file or directory.";
type = attrsOf (submodule {
options = {
dst = mkOption {
description = "Where should the executable output and what file or folder to check whether it should be run.";
type = path;
};
executable = mkOption {
description = "The path to the executable to execute. Use $out.";
type = path;
};
persistent = mkOption {
description = "Whether the created something should be kept after service stop.";
type = bool;
default = false;
};
};
});
default = {};
};
create = mkOption {
description = "Creates either an empty file or directory.";
type = attrsOf (submodule {
options = {
type = mkOption {
description = "Whether to create a direcotroy or file.";
type = enum [ "directory" "file" ];
};
mode = mkOption {
description = "Mode to set for the new creation, if set to `null`, its up to the implementation.";
default = null;
type = nullOr str;
};
owner = mkOption {
description = "Owner of new creation.";
default = "root:root";
type = str;
};
dst = mkOption {
description = "Wheret to create it.";
type = path;
};
persistent = mkOption {
description = "Whether the created something should be kept after service stop.";
type = bool;
default = false;
};
};
});
default = {};
};
};
};
};
inherit ensureSomething log;
shutdownOnExit = mkEnableOption "Whether the init system should enter shutdown when this particular service exits";
script = mkOption {
description = "Service script to start the program.";
type = types.path;
default = "";
};
finish = mkOption {
description = "Script to run upon service stop.";
type = with types; nullOr path;
default = null;
};
};
});
description = "Service definitions.";
@ -163,8 +269,16 @@ in
config = {
# TODO add assertions for this module
assertions = [
];
assertions = mapAttrsToList (n: v:
{
assertion =
let
selectedCount =
(count (x: x) (mapAttrsToList (n: v: if v == null then false else true) v.log));
in
if selectedCount == 1 || selectedCount == 0 then true else false;
message = "You can only select one log type, in service ${n}.";
}
) cfg.services;
};
}

+ 27
- 82
modules/runit/default.nix View File

@ -69,86 +69,23 @@ in
};
config = {
runit =
{
serviceDir = pkgs.runCommandNoCCLocal "service-dir" {} ''
runit = {
serviceDir = pkgs.runCommandNoCCLocal "service-dir" {} ''
mkdir $out
${concatStringsSep "\n" (mapAttrsToList (n: s:
let
run = pkgs.writeShellScript "${n}-run" ''
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
''
if ! [[ -e ${dst} ]] ; then
echo '${n}: linking `${src}` to `${dst}`'
mkdir -p "$(dirname '${dst}')"
ln -s '${src}' '${dst}'
fi
''
) s.ensureSomething.link)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
''
if ! [[ -e ${dst} ]] ; then
echo '${n}: copying `${src}` to `${dst}`'
mkdir -p "$(dirname '${dst}')"
cp -r '${src}' '${dst}'
fi
''
) s.ensureSomething.copy)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
abort "linkFarm is not implemented yet in runit!"
) s.ensureSomething.linkFarm)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
''
if ! [[ -e ${dst} ]] ; then
echo '${n}: executing `${executable}` to create `${dst}`'
mkdir -p "$(dirname '${dst}')"
out=${dst} ${executable}
if ! [[ -e ${dst} ]] ; then
echo '${n}: executed `${executable}` but `${dst}`
fi
''
) s.ensureSomething.exec)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
''
if ! [[ -e ${dst} ]] ; then
echo '${n}: creating `${dst}`'
${if (type == "directory") then
"mkdir -p ${dst}"
else if (type == "file") then
''
mkdir -p "$(dirname '${dst}')"
touch ${dst}
''
else
abort "Unsupported init create type, module system should have caught this!"
}
chown ${owner} ${dst}
${optionalString (mode != null) "chmod ${mode} ${dst}"}
fi
''
) s.ensureSomething.create)}
exec ${s.script}
'';
run = pkgs.callPackage ./run.nix {} { inherit n s; };
finish = pkgs.callPackage ./finish.nix {} { inherit n s cfgInit; };
log = pkgs.callPackage ./log.nix {} { inherit n s; };
in
assert s.dependencies == [];
assert s.dependencies == [];
''
mkdir $out/${n}
ln -s ${run} $out/${n}/run
''
''
mkdir -p $out/${n}/log
ln -s ${run} $out/${n}/run
ln -s ${finish} $out/${n}/finish
ln -s ${log} $out/${n}/log/run
''
) cfgInit.services)}
'';
};
@ -159,17 +96,25 @@ in
}
(mkIf cfg.enable {
type = "runit";
shutdown = pkgs.writeShellScript "runit-shutdown"
''
mkdir -p /etc/runit
touch /etc/runit/stopit
chmod 544 /etc/runit/stopit
kill -SIGCONT 1
'';
script = pkgs.writeShellScript "init"
''
export PATH=${pkgs.busybox}/bin:${cfg.pkg}/bin
mkdir -p /etc/runit
export PATH=${pkgs.busybox}/bin:${cfg.pkg}/bin
mkdir -p /etc/runit
ln -sf ${cfg.stages.stage-1} /etc/runit/1
ln -sf ${cfg.stages.stage-2} /etc/runit/2
ln -sf ${cfg.stages.stage-3} /etc/runit/3
ln -sf ${cfg.stages.stage-1} /etc/runit/1
ln -sf ${cfg.stages.stage-2} /etc/runit/2
ln -sf ${cfg.stages.stage-3} /etc/runit/3
exec runit-init
'';
exec runit-init
'';
})
];
};


+ 64
- 0
modules/runit/finish.nix View File

@ -0,0 +1,64 @@
{ lib
, writeShellScript
}:
{ n, s, cfgInit }:
with lib;
writeShellScript "${n}-finish" ''
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
optionalString (!cv.persistent) ''
if [[ -e ${dst} ]] ; then
echo '${n}: removing non-presistent `${dst}`'
rm -v ${dst}
fi
''
) s.ensureSomething.link)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
optionalString (!cv.persistent) ''
if [[ -e ${dst} ]] ; then
echo '${n}: removing non-presistent `${dst}`'
rm -rv ${dst}
fi
''
) s.ensureSomething.copy)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
abort "linkFarm is not implemented yet in runit!"
) s.ensureSomething.linkFarm)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
optionalString (!cv.persistent) ''
if [[ -e ${dst} ]] ; then
echo '${n}: removing non-persistent `${dst}`'
rm -rv '${dst}'
fi
''
) s.ensureSomething.exec)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
optionalString (!cv.persistent) ''
if [[ -e ${dst} ]] ; then
echo '${n}: removing non-persistent `${dst}`'
${if (type == "directory") then
"rm -rv ${dst}"
else if (type == "file") then
''
rm -v ${dst}
''
else
abort "Unsupported init create type, module system should have caught this!"
}
fi
''
) s.ensureSomething.create)}
${optionalString (s.finish != null && !s.shutdownOnExit) "exec ${s.finish}"}
${optionalString (s.finish != null && s.shutdownOnExit) "${s.finish}"}
${optionalString (s.shutdownOnExit) ("exec ${cfgInit.shutdown}")}
''

+ 44
- 0
modules/runit/log.nix View File

@ -0,0 +1,44 @@
{ lib
, utillinux
, writeShellScript
}:
{ n, s }:
with lib;
writeShellScript "${n}-log" ''
${
if s.log.file != null then
with s.log.file;
assert rotate == 0;
''
cat > ${dst}
''
else if s.log.syslog != null then
with s.log.syslog;
let
inetDst =
''-n "$(echo "${dst}" | cut -d : -f 1)" -P "$(echo "${dst}" | cut -d : -f 2)"'';
connection =
if type == "uds" then
"-u ${dst}"
else if type == "udp" then
''-d ${inetDst}''
else if type == "tcp" then
''-T ${inetDst}''
else
abort "Unknown log type, module system should have caught this!";
rfc5424 =
optional (!time) "notime"
++ optional (!timeQuality) "notq"
++ optional (!host) "nohost";
in
''
export PATH=${utillinux}/bin:$PATH
logger ${connection} \
${if rfc5424 == [] then "--rfc5424" else "--rfc5424=${concatStringsSep "," rfc5424}"} \
${optionalString (tag != null) "-t ${tag}"} \
${optionalString (priority != null) "-t ${priority}"} \
''
else
"cat"
}
''

+ 73
- 0
modules/runit/run.nix View File

@ -0,0 +1,73 @@
{ lib
, writeShellScript
}:
{ n, s }:
with lib;
writeShellScript "${n}-run" ''
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
''
if ! [[ -e ${dst} ]] ; then
echo '${n}: linking `${src}` to `${dst}`'
mkdir -p "$(dirname '${dst}')"
ln -s '${src}' '${dst}'
fi
''
) s.ensureSomething.link)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
''
if ! [[ -e ${dst} ]] ; then
echo '${n}: copying `${src}` to `${dst}`'
mkdir -p "$(dirname '${dst}')"
cp -r '${src}' '${dst}'
fi
''
) s.ensureSomething.copy)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
abort "linkFarm is not implemented yet in runit!"
) s.ensureSomething.linkFarm)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
''
if ! [[ -e ${dst} ]] ; then
echo '${n}: executing `${executable}` to create `${dst}`'
mkdir -p "$(dirname '${dst}')"
out=${dst} ${executable}
if ! [[ -e ${dst} ]] ; then
echo '${n}: executed `${executable}` but `${dst}` does not exist!'
exit 1
fi
fi
''
) s.ensureSomething.exec)}
${concatStringsSep "\n" (mapAttrsToList (cn: cv:
with cv;
''
if ! [[ -e ${dst} ]] ; then
echo '${n}: creating `${dst}`'
${if (type == "directory") then
"mkdir -p ${dst}"
else if (type == "file") then
''
mkdir -p "$(dirname '${dst}')"
touch ${dst}
''
else
abort "Unsupported init create type, module system should have caught this!"
}
chown ${owner} ${dst}
${optionalString (mode != null) "chmod ${mode} ${dst}"}
fi
''
) s.ensureSomething.create)}
exec ${s.script}
''

+ 24
- 62
modules/services/gitea.nix View File

@ -152,79 +152,45 @@ in
};
config = {
init.services.gitea =
let
init.services.gitea = mkIf cfg.enable {
ensureSomething.create."0-server.APP_DATA_PATH" = {
type = "directory";
mode = "755";
owner = "gitea:nogroup";
persistent = true;
dst = cfg.configuration."server"."APP_DATA_PATH";
};
ensure = section: key:
mkIf (cfg.configuration."${section}" ? "${key}")(mkDefault {
inherit mode owner persistent;
type = "directory";
dst = cfg.configuration."${section}"."${key}";
});
ensureFile = section: key:
mkIf (cfg.configuration."${section}" ? "${key}")(mkDefault {
inherit mode owner persistent;
type = "file";
dst = cfg.configuration."${section}"."${key}";
});
in mkIf cfg.enable {
# ensureSomething.create."repository.ROOT" =
# ensure "repository" "ROOT";
# ensureSomething.create."repository.local.LOCAL_COPY_PATH" =
# ensure "repository.local" "LOCAL_COPY_PATH";
# ensureSomething.create."repository.upload.TEMP_PATH" =
# ensure "repository.upload" "TEMP_PATH";
# ensureSomething.create."server.LFS_CONTENT_PATH" =
# ensure "server" "LFS_CONTENT_PATH";
ensureSomething.create."0-server.APP_DATA_PATH" =
ensure "server" "APP_DATA_PATH";
# ensureSomething.create."database.PATH" =
# ensureFile "database" "PATH";
# ensureSomething.create."indexer.ISSUE_INDEXER_PATH" =
# ensure "indexer" "ISSUE_INDEXER_PATH";
# ensureSomething.create."indexer.REPO_INDEXER_PATH" =
# ensure "indexer" "REPO_INDEXER_PATH";
# ensureSomething.create."session.PROVIDER_CONFIG" =
# ensure "session" "PROVIDER_CONFIG";
# ensureSomething.create."picture.AVATAR_UPLOAD_PATH" =
# ensure "picture" "AVATAR_UPLOAD_PATH";
# ensureSomething.create."picture.REPOSITORY_AVATAR_UPLOAD_PATH" =
# ensure "picture" "REPOSITORY_AVATAR_UPLOAD_PATH";
# ensureSomething.create."attachment.PATH" =
# ensure "attachment" "PATH";
ensureSomething.create."runConfig" = {
type = "file";
mode = "400";
owner = "gitea:nogroup";
persistent = false;
dst = cfg.runConfig;
};
script = pkgs.writeShellScript "gitea-run"
(let
appIni = pkgs.writeText "app.ini"
''
ensureSomething.create."runConfig" = {
type = "file";
mode = "400";
owner = "gitea:nogroup";
persistent = false;
dst = cfg.runConfig;
};
script = pkgs.writeShellScript "gitea-run"
(let
appIni = pkgs.writeText "app.ini"
''
APP_NAME = ${cfg.appName}
RUN_MODE = ${cfg.runMode}
RUN_USER = ${cfg.user}
${generators.toINI {} cfg.configuration}
'';
inherit (cfg.secrets) secretKeyFile internalTokenFile jwtSecretFile lfsJwtSecretFile databaseUserFile databasePasswordFile databaseHostFile;
inherit (cfg.secrets) secretKeyFile internalTokenFile jwtSecretFile lfsJwtSecretFile databaseUserFile databasePasswordFile databaseHostFile;
subsSecret = source: key:
optionalString (source != null)
''
subsSecret = source: key:
optionalString (source != null)
''
if [[ -f '${source}' ]] ; then
SECRET="$(head -n1 ${source})"
sed -i "s,#${key}#,$SECRET,g" ${cfg.runConfig}
echo 'Substituted contents of `${source}` in place of `#${key}#`'
fi
'';
in
in
''
export PATH=${pkgs.busybox}/bin
@ -238,14 +204,10 @@ in
${subsSecret databasePasswordFile "databasePassword"}
${subsSecret databaseHostFile "databaseHost"}
ls -lahR /data/gitea
export HOME=${cfg.configuration.repository.ROOT}
chpst -u ${cfg.user}:nogroup ${cfg.package}/bin/gitea -c ${cfg.runConfig}
rm ${cfg.runConfig}
'');
};
};
users.users."gitea" = mkIf (cfg.enable && cfg.user == defaultUser) {
uid = ids.uids.gitea;


Loading…
Cancel
Save