at 17.09-beta 7.3 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3with lib; 4let 5 6 dataDir = "/var/lib/consul"; 7 cfg = config.services.consul; 8 9 configOptions = { data_dir = dataDir; } // 10 (if cfg.webUi then { ui_dir = "${cfg.package.ui}"; } else { }) // 11 cfg.extraConfig; 12 13 configFiles = [ "/etc/consul.json" "/etc/consul-addrs.json" ] 14 ++ cfg.extraConfigFiles; 15 16 devices = attrValues (filterAttrs (_: i: i != null) cfg.interface); 17 systemdDevices = flip map devices 18 (i: "sys-subsystem-net-devices-${utils.escapeSystemdPath i}.device"); 19in 20{ 21 options = { 22 23 services.consul = { 24 25 enable = mkOption { 26 type = types.bool; 27 default = false; 28 description = '' 29 Enables the consul daemon. 30 ''; 31 }; 32 33 package = mkOption { 34 type = types.package; 35 default = pkgs.consul; 36 defaultText = "pkgs.consul"; 37 description = '' 38 The package used for the Consul agent and CLI. 39 ''; 40 }; 41 42 43 webUi = mkOption { 44 type = types.bool; 45 default = false; 46 description = '' 47 Enables the web interface on the consul http port. 48 ''; 49 }; 50 51 leaveOnStop = mkOption { 52 type = types.bool; 53 default = false; 54 description = '' 55 If enabled, causes a leave action to be sent when closing consul. 56 This allows a clean termination of the node, but permanently removes 57 it from the cluster. You probably don't want this option unless you 58 are running a node which going offline in a permanent / semi-permanent 59 fashion. 60 ''; 61 }; 62 63 interface = { 64 65 advertise = mkOption { 66 type = types.nullOr types.str; 67 default = null; 68 description = '' 69 The name of the interface to pull the advertise_addr from. 70 ''; 71 }; 72 73 bind = mkOption { 74 type = types.nullOr types.str; 75 default = null; 76 description = '' 77 The name of the interface to pull the bind_addr from. 78 ''; 79 }; 80 81 }; 82 83 forceIpv4 = mkOption { 84 type = types.bool; 85 default = false; 86 description = '' 87 Whether we should force the interfaces to only pull ipv4 addresses. 88 ''; 89 }; 90 91 dropPrivileges = mkOption { 92 type = types.bool; 93 default = true; 94 description = '' 95 Whether the consul agent should be run as a non-root consul user. 96 ''; 97 }; 98 99 extraConfig = mkOption { 100 default = { }; 101 description = '' 102 Extra configuration options which are serialized to json and added 103 to the config.json file. 104 ''; 105 }; 106 107 extraConfigFiles = mkOption { 108 default = [ ]; 109 type = types.listOf types.str; 110 description = '' 111 Additional configuration files to pass to consul 112 NOTE: These will not trigger the service to be restarted when altered. 113 ''; 114 }; 115 116 alerts = { 117 enable = mkEnableOption "consul-alerts"; 118 119 package = mkOption { 120 description = "Package to use for consul-alerts."; 121 default = pkgs.consul-alerts; 122 defaultText = "pkgs.consul-alerts"; 123 type = types.package; 124 }; 125 126 listenAddr = mkOption { 127 description = "Api listening address."; 128 default = "localhost:9000"; 129 type = types.str; 130 }; 131 132 consulAddr = mkOption { 133 description = "Consul api listening adddress"; 134 default = "localhost:8500"; 135 type = types.str; 136 }; 137 138 watchChecks = mkOption { 139 description = "Whether to enable check watcher."; 140 default = true; 141 type = types.bool; 142 }; 143 144 watchEvents = mkOption { 145 description = "Whether to enable event watcher."; 146 default = true; 147 type = types.bool; 148 }; 149 }; 150 151 }; 152 153 }; 154 155 config = mkIf cfg.enable ( 156 mkMerge [{ 157 158 users.extraUsers."consul" = { 159 description = "Consul agent daemon user"; 160 uid = config.ids.uids.consul; 161 # The shell is needed for health checks 162 shell = "/run/current-system/sw/bin/bash"; 163 }; 164 165 environment = { 166 etc."consul.json".text = builtins.toJSON configOptions; 167 # We need consul.d to exist for consul to start 168 etc."consul.d/dummy.json".text = "{ }"; 169 systemPackages = [ cfg.package ]; 170 }; 171 172 systemd.services.consul = { 173 wantedBy = [ "multi-user.target" ]; 174 after = [ "network.target" ] ++ systemdDevices; 175 bindsTo = systemdDevices; 176 restartTriggers = [ config.environment.etc."consul.json".source ] 177 ++ mapAttrsToList (_: d: d.source) 178 (filterAttrs (n: _: hasPrefix "consul.d/" n) config.environment.etc); 179 180 serviceConfig = { 181 ExecStart = "@${cfg.package.bin}/bin/consul consul agent -config-dir /etc/consul.d" 182 + concatMapStrings (n: " -config-file ${n}") configFiles; 183 ExecReload = "${cfg.package.bin}/bin/consul reload"; 184 PermissionsStartOnly = true; 185 User = if cfg.dropPrivileges then "consul" else null; 186 TimeoutStartSec = "0"; 187 } // (optionalAttrs (cfg.leaveOnStop) { 188 ExecStop = "${cfg.package.bin}/bin/consul leave"; 189 }); 190 191 path = with pkgs; [ iproute gnugrep gawk consul ]; 192 preStart = '' 193 mkdir -m 0700 -p ${dataDir} 194 chown -R consul ${dataDir} 195 196 # Determine interface addresses 197 getAddrOnce () { 198 ip addr show dev "$1" \ 199 | grep 'inet${optionalString (cfg.forceIpv4) " "}.*scope global' \ 200 | awk -F '[ /\t]*' '{print $3}' | head -n 1 201 } 202 getAddr () { 203 ADDR="$(getAddrOnce $1)" 204 LEFT=60 # Die after 1 minute 205 while [ -z "$ADDR" ]; do 206 sleep 1 207 LEFT=$(expr $LEFT - 1) 208 if [ "$LEFT" -eq "0" ]; then 209 echo "Address lookup timed out" 210 exit 1 211 fi 212 ADDR="$(getAddrOnce $1)" 213 done 214 echo "$ADDR" 215 } 216 echo "{" > /etc/consul-addrs.json 217 delim=" " 218 '' 219 + concatStrings (flip mapAttrsToList cfg.interface (name: i: 220 optionalString (i != null) '' 221 echo "$delim \"${name}_addr\": \"$(getAddr "${i}")\"" >> /etc/consul-addrs.json 222 delim="," 223 '')) 224 + '' 225 echo "}" >> /etc/consul-addrs.json 226 ''; 227 }; 228 } 229 230 (mkIf (cfg.alerts.enable) { 231 systemd.services.consul-alerts = { 232 wantedBy = [ "multi-user.target" ]; 233 after = [ "consul.service" ]; 234 235 path = [ cfg.package ]; 236 237 serviceConfig = { 238 ExecStart = '' 239 ${cfg.alerts.package.bin}/bin/consul-alerts start \ 240 --alert-addr=${cfg.alerts.listenAddr} \ 241 --consul-addr=${cfg.alerts.consulAddr} \ 242 ${optionalString cfg.alerts.watchChecks "--watch-checks"} \ 243 ${optionalString cfg.alerts.watchEvents "--watch-events"} 244 ''; 245 User = if cfg.dropPrivileges then "consul" else null; 246 Restart = "on-failure"; 247 }; 248 }; 249 }) 250 251 ]); 252}