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}