···
# Since that service is a oneshot with RemainAfterExit,
# the folder will exist during all renewal services.
-
concurrencyLockfiles = map (n: "${toString n}.lock") (lib.range 1 cfg.maxConcurrentRenewals);
-
# Assign elements of `baseList` to each element of `needAssignmentList`, until the latter is exhausted.
-
# returns: [{fst = "element of baseList"; snd = "element of needAssignmentList"}]
-
baseList: needAssignmentList:
-
if baseList == [ ] then [ ] else _rrCycler baseList baseList needAssignmentList;
-
origBaseList: workingBaseList: needAssignmentList:
-
if (workingBaseList == [ ] || needAssignmentList == [ ]) then
-
fst = head workingBaseList;
-
snd = head needAssignmentList;
-
++ _rrCycler origBaseList (
-
if (tail workingBaseList == [ ]) then origBaseList else tail workingBaseList
-
) (tail needAssignmentList);
-
attrsToList = lib.mapAttrsToList (
-
# for an AttrSet `funcsAttrs` having functions as values, apply single arguments from
-
# `argsList` to them in a round-robin manner.
-
# Returns an attribute set with the applied functions as values.
-
value = x.snd.value x.fst;
-
}) (roundRobinAssign argsList (attrsToList funcsAttrs))
# explainer: https://stackoverflow.com/a/60896531
-
exec {LOCKFD}> ${lockfilePath}
-
echo "Waiting to acquire lock ${lockfilePath}"
-
${pkgs.flock}/bin/flock ''${LOCKFD} || exit 1
-
echo "Acquired lock ${lockfilePath}"
-
+ ''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:
···
-
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}
-
script = (if (lockfileName == null) then lib.id else wrapInFlock "${lockdir}${lockfileName}") ''
# Regenerate self-signed certificates (in case the SANs change) until we
···
-
orderRenewService = lockfileName: {
description = "Order (and renew) ACME certificate for ${cert}";
···
# Working directory will be /tmp
-
script = (if (lockfileName == null) then lib.id else wrapInFlock "${lockdir}${lockfileName}") ''
${lib.optionalString data.enableDebugLogs "set -x"}
···
-
orderRenewServiceFunctions = lib.mapAttrs' (
cert: conf: lib.nameValuePair "acme-order-renew-${cert}" conf.orderRenewService
-
if cfg.maxConcurrentRenewals > 0 then
-
roundRobinApplyAttrs orderRenewServiceFunctions concurrencyLockfiles
-
lib.mapAttrs (_: f: f null) orderRenewServiceFunctions;
-
baseServiceFunctions = lib.mapAttrs' (
cert: conf: lib.nameValuePair "acme-${cert}" conf.baseService
-
if cfg.maxConcurrentRenewals > 0 then
-
roundRobinApplyAttrs baseServiceFunctions concurrencyLockfiles
-
lib.mapAttrs (_: f: f null) baseServiceFunctions;
acme-setup = setupService;