1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 format = pkgs.formats.json { };
9 commonOptions =
10 {
11 pkgName,
12 flavour ? pkgName,
13 }:
14 lib.mkOption {
15 default = { };
16 description = ''
17 Attribute set of ${flavour} instances.
18 Creates independent `${flavour}-''${name}.service` systemd units for each instance defined here.
19 '';
20 type =
21 with lib.types;
22 attrsOf (
23 submodule (
24 { name, ... }:
25 {
26 options = {
27 enable = lib.mkEnableOption "this ${flavour} instance" // {
28 default = true;
29 };
30
31 package = lib.mkPackageOption pkgs pkgName { };
32
33 user = lib.mkOption {
34 type = types.str;
35 default = "root";
36 description = ''
37 User under which this instance runs.
38 '';
39 };
40
41 group = lib.mkOption {
42 type = types.str;
43 default = "root";
44 description = ''
45 Group under which this instance runs.
46 '';
47 };
48
49 settings = lib.mkOption {
50 type = types.submodule {
51 freeformType = format.type;
52
53 options = {
54 pid_file = lib.mkOption {
55 default = "/run/${flavour}/${name}.pid";
56 type = types.str;
57 description = ''
58 Path to use for the pid file.
59 '';
60 };
61
62 template = lib.mkOption {
63 default = null;
64 type = with types; nullOr (listOf (attrsOf anything));
65 description =
66 let
67 upstreamDocs =
68 if flavour == "vault-agent" then
69 "https://developer.hashicorp.com/vault/docs/agent/template"
70 else
71 "https://github.com/hashicorp/consul-template/blob/main/docs/configuration.md#templates";
72 in
73 ''
74 Template section of ${flavour}.
75 Refer to <${upstreamDocs}> for supported values.
76 '';
77 };
78 };
79 };
80
81 default = { };
82
83 description =
84 let
85 upstreamDocs =
86 if flavour == "vault-agent" then
87 "https://developer.hashicorp.com/vault/docs/agent#configuration-file-options"
88 else
89 "https://github.com/hashicorp/consul-template/blob/main/docs/configuration.md#configuration-file";
90 in
91 ''
92 Free-form settings written directly to the `config.json` file.
93 Refer to <${upstreamDocs}> for supported values.
94
95 ::: {.note}
96 Resulting format is JSON not HCL.
97 Refer to <https://www.hcl2json.com/> if you are unsure how to convert HCL options to JSON.
98 :::
99 '';
100 };
101 };
102 }
103 )
104 );
105 };
106
107 createAgentInstance =
108 {
109 instance,
110 name,
111 flavour,
112 }:
113 let
114 configFile = format.generate "${name}.json" instance.settings;
115 in
116 lib.mkIf (instance.enable) {
117 description = "${flavour} daemon - ${name}";
118 wantedBy = [ "multi-user.target" ];
119 after = [ "network.target" ];
120 path = [ pkgs.getent ];
121 startLimitIntervalSec = 60;
122 startLimitBurst = 3;
123 serviceConfig = {
124 User = instance.user;
125 Group = instance.group;
126 RuntimeDirectory = flavour;
127 ExecStart = "${lib.getExe instance.package} ${
128 lib.optionalString (flavour == "vault-agent") "agent"
129 } -config ${configFile}";
130 ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
131 KillSignal = "SIGINT";
132 TimeoutStopSec = "30s";
133 Restart = "on-failure";
134 };
135 };
136in
137{
138 options = {
139 services.consul-template.instances = commonOptions { pkgName = "consul-template"; };
140 services.vault-agent.instances = commonOptions {
141 pkgName = "vault";
142 flavour = "vault-agent";
143 };
144 };
145
146 config = lib.mkMerge (
147 map
148 (
149 flavour:
150 let
151 cfg = config.services.${flavour};
152 in
153 lib.mkIf (cfg.instances != { }) {
154 systemd.services = lib.mapAttrs' (
155 name: instance:
156 lib.nameValuePair "${flavour}-${name}" (createAgentInstance {
157 inherit name instance flavour;
158 })
159 ) cfg.instances;
160 }
161 )
162 [
163 "consul-template"
164 "vault-agent"
165 ]
166 );
167
168 meta.maintainers = with lib.maintainers; [
169 emilylange
170 tcheronneau
171 ];
172}