1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.etcd;
7
8in {
9
10 options.services.etcd = {
11 enable = mkOption {
12 description = "Whether to enable etcd.";
13 default = false;
14 type = types.bool;
15 };
16
17 name = mkOption {
18 description = "Etcd unique node name.";
19 default = config.networking.hostName;
20 type = types.str;
21 };
22
23 advertiseClientUrls = mkOption {
24 description = "Etcd list of this member's client URLs to advertise to the rest of the cluster.";
25 default = cfg.listenClientUrls;
26 type = types.listOf types.str;
27 };
28
29 listenClientUrls = mkOption {
30 description = "Etcd list of URLs to listen on for client traffic.";
31 default = ["http://localhost:4001"];
32 type = types.listOf types.str;
33 };
34
35 listenPeerUrls = mkOption {
36 description = "Etcd list of URLs to listen on for peer traffic.";
37 default = ["http://localhost:7001"];
38 type = types.listOf types.str;
39 };
40
41 initialAdvertisePeerUrls = mkOption {
42 description = "Etcd list of this member's peer URLs to advertise to rest of the cluster.";
43 default = cfg.listenPeerUrls;
44 type = types.listOf types.str;
45 };
46
47 initialCluster = mkOption {
48 description = "Etcd initial cluster configuration for bootstrapping.";
49 default = ["${cfg.name}=http://localhost:7001"];
50 type = types.listOf types.str;
51 };
52
53 initialClusterState = mkOption {
54 description = "Etcd initial cluster configuration for bootstrapping.";
55 default = "new";
56 type = types.enum ["new" "existing"];
57 };
58
59 initialClusterToken = mkOption {
60 description = "Etcd initial cluster token for etcd cluster during bootstrap.";
61 default = "etcd-cluster";
62 type = types.str;
63 };
64
65 discovery = mkOption {
66 description = "Etcd discovery url";
67 default = "";
68 type = types.str;
69 };
70
71 extraConf = mkOption {
72 description = ''
73 Etcd extra configuration. See
74 <link xlink:href='https://github.com/coreos/etcd/blob/master/Documentation/configuration.md#environment-variables' />
75 '';
76 type = types.attrsOf types.str;
77 default = {};
78 example = literalExample ''
79 {
80 "CORS" = "*";
81 "NAME" = "default-name";
82 "MAX_RESULT_BUFFER" = "1024";
83 "MAX_CLUSTER_SIZE" = "9";
84 "MAX_RETRY_ATTEMPTS" = "3";
85 }
86 '';
87 };
88
89 dataDir = mkOption {
90 type = types.path;
91 default = "/var/lib/etcd";
92 description = "Etcd data directory.";
93 };
94 };
95
96 config = mkIf cfg.enable {
97 systemd.services.etcd = {
98 description = "Etcd Daemon";
99 wantedBy = [ "multi-user.target" ];
100 after = [ "network-interfaces.target" ];
101
102 environment = {
103 ETCD_NAME = cfg.name;
104 ETCD_DISCOVERY = cfg.discovery;
105 ETCD_DATA_DIR = cfg.dataDir;
106 ETCD_ADVERTISE_CLIENT_URLS = concatStringsSep "," cfg.advertiseClientUrls;
107 ETCD_LISTEN_CLIENT_URLS = concatStringsSep "," cfg.listenClientUrls;
108 ETCD_LISTEN_PEER_URLS = concatStringsSep "," cfg.listenPeerUrls;
109 ETCD_INITIAL_ADVERTISE_PEER_URLS = concatStringsSep "," cfg.initialAdvertisePeerUrls;
110 } // (optionalAttrs (cfg.discovery == ""){
111 ETCD_INITIAL_CLUSTER = concatStringsSep "," cfg.initialCluster;
112 ETCD_INITIAL_CLUSTER_STATE = cfg.initialClusterState;
113 ETCD_INITIAL_CLUSTER_TOKEN = cfg.initialClusterToken;
114 }) // (mapAttrs' (n: v: nameValuePair "ETCD_${n}" v) cfg.extraConf);
115
116 serviceConfig = {
117 ExecStart = "${pkgs.etcd}/bin/etcd";
118 User = "etcd";
119 PermissionsStartOnly = true;
120 };
121 preStart = ''
122 mkdir -m 0700 -p ${cfg.dataDir}
123 if [ "$(id -u)" = 0 ]; then chown etcd ${cfg.dataDir}; fi
124 '';
125 };
126
127 environment.systemPackages = [ pkgs.etcdctl ];
128
129 users.extraUsers = singleton {
130 name = "etcd";
131 uid = config.ids.uids.etcd;
132 description = "Etcd daemon user";
133 home = cfg.dataDir;
134 };
135 };
136}