···
# Since that service is a oneshot with RemainAfterExit,
# the folder will exist during all renewal services.
27
-
concurrencyLockfiles = map (n: "${toString n}.lock") (lib.range 1 cfg.maxConcurrentRenewals);
28
-
# Assign elements of `baseList` to each element of `needAssignmentList`, until the latter is exhausted.
29
-
# returns: [{fst = "element of baseList"; snd = "element of needAssignmentList"}]
31
-
baseList: needAssignmentList:
32
-
if baseList == [ ] then [ ] else _rrCycler baseList baseList needAssignmentList;
35
-
origBaseList: workingBaseList: needAssignmentList:
36
-
if (workingBaseList == [ ] || needAssignmentList == [ ]) then
41
-
fst = head workingBaseList;
42
-
snd = head needAssignmentList;
45
-
++ _rrCycler origBaseList (
46
-
if (tail workingBaseList == [ ]) then origBaseList else tail workingBaseList
47
-
) (tail needAssignmentList);
48
-
attrsToList = lib.mapAttrsToList (
49
-
attrname: attrval: {
54
-
# for an AttrSet `funcsAttrs` having functions as values, apply single arguments from
55
-
# `argsList` to them in a round-robin manner.
56
-
# Returns an attribute set with the applied functions as values.
57
-
roundRobinApplyAttrs =
58
-
funcsAttrs: argsList:
61
-
inherit (x.snd) name;
62
-
value = x.snd.value x.fst;
63
-
}) (roundRobinAssign argsList (attrsToList funcsAttrs))
66
-
lockfilePath: script:
# explainer: https://stackoverflow.com/a/60896531
69
-
exec {LOCKFD}> ${lockfilePath}
70
-
echo "Waiting to acquire lock ${lockfilePath}"
71
-
${pkgs.flock}/bin/flock ''${LOCKFD} || exit 1
72
-
echo "Acquired lock ${lockfilePath}"
32
+
maxConcurrentRenewals=${toString cfg.maxConcurrentRenewals}
35
+
echo "Waiting to acquire lock in ${lockdir}"
37
+
for i in $(seq 1 $maxConcurrentRenewals); do
38
+
exec {LOCKFD}> "${lockdir}/$i.lock"
39
+
if ${pkgs.flock}/bin/flock -n ''${LOCKFD}; then
48
+
if [ "$maxConcurrentRenewals" -gt "0" ]; then
76
-
+ ''echo "Releasing lock ${lockfilePath}" # only released after process exit'';
# There are many services required to make cert renewals work.
# They all follow a common structure:
···
359
-
baseService = lockfileName: {
description = "Ensure certificate for ${cert}";
wantedBy = [ "multi-user.target" ];
···
# Working directory will be /tmp
# minica will output to a folder sharing the name of the first domain
# in the list, which will be ${data.domain}
402
-
script = (if (lockfileName == null) then lib.id else wrapInFlock "${lockdir}${lockfileName}") ''
378
+
script = wrapInFlock ''
# Regenerate self-signed certificates (in case the SANs change) until we
···
450
-
orderRenewService = lockfileName: {
426
+
orderRenewService = {
description = "Order (and renew) ACME certificate for ${cert}";
···
# Working directory will be /tmp
531
-
script = (if (lockfileName == null) then lib.id else wrapInFlock "${lockdir}${lockfileName}") ''
507
+
script = wrapInFlock ''
${lib.optionalString data.enableDebugLogs "set -x"}
···
1182
-
orderRenewServiceFunctions = lib.mapAttrs' (
1158
+
orderRenewServices = lib.mapAttrs' (
cert: conf: lib.nameValuePair "acme-order-renew-${cert}" conf.orderRenewService
1185
-
orderRenewServices =
1186
-
if cfg.maxConcurrentRenewals > 0 then
1187
-
roundRobinApplyAttrs orderRenewServiceFunctions concurrencyLockfiles
1189
-
lib.mapAttrs (_: f: f null) orderRenewServiceFunctions;
1190
-
baseServiceFunctions = lib.mapAttrs' (
1161
+
baseServices = lib.mapAttrs' (
cert: conf: lib.nameValuePair "acme-${cert}" conf.baseService
1194
-
if cfg.maxConcurrentRenewals > 0 then
1195
-
roundRobinApplyAttrs baseServiceFunctions concurrencyLockfiles
1197
-
lib.mapAttrs (_: f: f null) baseServiceFunctions;
acme-setup = setupService;