From b3bc157f7ff84a7ed0dc34901830c589d17a51e9 Mon Sep 17 00:00:00 2001 From: Jaka Hudoklin Date: Sat, 15 Nov 2014 16:27:27 +0100 Subject: nixos: add etcd module --- nixos/modules/misc/ids.nix | 1 + nixos/modules/module-list.nix | 1 + nixos/modules/services/misc/etcd.nix | 141 +++++++++++++++++++++++++++++++++++ nixos/release.nix | 1 + nixos/tests/etcd.nix | 108 +++++++++++++++++++++++++++ 5 files changed, 252 insertions(+) create mode 100644 nixos/modules/services/misc/etcd.nix create mode 100644 nixos/tests/etcd.nix diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index b08082af3529..166bb931a627 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -163,6 +163,7 @@ systemd-resolve = 153; systemd-timesync = 154; liquidsoap = 155; + etcd = 156; # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399! diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 19d69185392b..0f09ee24027c 100755 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -166,6 +166,7 @@ ./services/misc/cgminer.nix ./services/misc/dictd.nix ./services/misc/disnix.nix + ./services/misc/etcd.nix ./services/misc/felix.nix ./services/misc/folding-at-home.nix ./services/misc/gitolite.nix diff --git a/nixos/modules/services/misc/etcd.nix b/nixos/modules/services/misc/etcd.nix new file mode 100644 index 000000000000..f0693911616a --- /dev/null +++ b/nixos/modules/services/misc/etcd.nix @@ -0,0 +1,141 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.etcd; + +in { + + options.services.etcd = { + enable = mkOption { + description = "Whether to enable etcd."; + default = false; + type = types.uniq types.bool; + }; + + name = mkOption { + description = "Etcd unique node name."; + default = config.networking.hostName; + type = types.str; + }; + + advertiseClientUrls = mkOption { + description = "Etcd list of this member's client URLs to advertise to the rest of the cluster."; + default = cfg.listenClientUrls; + type = types.listOf types.str; + }; + + listenClientUrls = mkOption { + description = "Etcd list of URLs to listen on for client traffic."; + default = ["http://localhost:2379" "http://localhost:4001"]; + type = types.listOf types.str; + }; + + listenPeerUrls = mkOption { + description = "Etcd list of URLs to listen on for peer traffic."; + default = ["http://localhost:2380" "http://localhost:7001"]; + type = types.listOf types.str; + }; + + initialAdvertisePeerUrls = mkOption { + description = "Etcd list of this member's peer URLs to advertise to rest of the cluster."; + default = cfg.listenPeerUrls; + type = types.listOf types.str; + }; + + initialCluster = mkOption { + description = "Etcd initial cluster configuration for bootstrapping."; + default = ["${cfg.name}=http://localhost:2380" "${cfg.name}=http://localhost:7001"]; + type = types.listOf types.str; + }; + + initialClusterState = mkOption { + description = "Etcd initial cluster configuration for bootstrapping."; + default = "new"; + type = types.enum ["new" "existing"]; + }; + + initialClusterToken = mkOption { + description = "Etcd initial cluster token for etcd cluster during bootstrap."; + default = "etcd-cluster"; + type = types.str; + }; + + discovery = mkOption { + description = "Etcd discovery url"; + default = ""; + type = types.str; + }; + + extraConf = mkOption { + description = '' + Etcd extra configuration. See + + ''; + type = types.attrsOf types.str; + default = {}; + example = literalExample '' + { + "CORS": "*", + "NAME": "default-name", + "MAX_RESULT_BUFFER": "1024", + "MAX_CLUSTER_SIZE": "9", + "MAX_RETRY_ATTEMPTS": "3" + } + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/etcd"; + description = "Etcd data directory."; + }; + }; + + config = mkIf cfg.enable { + systemd.services.etcd = { + description = "Etcd Daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-interfaces.target" ]; + + environment = { + ETCD_NAME = cfg.name; + ETCD_DISCOVERY = cfg.discovery; + ETCD_DATA_DIR = cfg.dataDir; + ETCD_ADVERTISE_CLIENT_URLS = concatStringsSep "," cfg.advertiseClientUrls; + ETCD_LISTEN_CLIENT_URLS = concatStringsSep "," cfg.listenClientUrls; + ETCD_LISTEN_PEER_URLS = concatStringsSep "," cfg.listenPeerUrls; + ETCD_INITIAL_ADVERTISE_PEER_URLS = concatStringsSep "," cfg.initialAdvertisePeerUrls; + } // (optionalAttrs (cfg.discovery == ""){ + ETCD_INITIAL_CLUSTER = concatStringsSep "," cfg.initialCluster; + ETCD_INITIAL_CLUSTER_STATE = cfg.initialClusterState; + ETCD_INITIAL_CLUSTER_TOKEN = cfg.initialClusterToken; + }) // (mapAttrs' (n: v: nameValuePair "ETCD_${n}" v) cfg.extraConf); + + serviceConfig = { + ExecStart = "${pkgs.etcd}/bin/etcd"; + User = "etcd"; + PermissionsStartOnly = true; + }; + preStart = '' + mkdir -m 0700 -p ${cfg.dataDir} + if [ "$(id -u)" = 0 ]; then chown etcd ${cfg.dataDir}; fi + ''; + postStart = '' + until ${pkgs.curl}/bin/curl -s -o /dev/null '${head cfg.listenClientUrls}/version'; do + sleep 1; + done + ''; + }; + + environment.systemPackages = [ pkgs.etcdctl ]; + + users.extraUsers = singleton { + name = "etcd"; + uid = config.ids.uids.etcd; + description = "Etcd daemon user"; + home = cfg.dataDir; + }; + }; +} diff --git a/nixos/release.nix b/nixos/release.nix index 83594629226b..890d8d483d73 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -239,6 +239,7 @@ in rec { tests.chromium = callTest tests/chromium.nix {}; tests.cjdns = callTest tests/cjdns.nix {}; tests.containers = callTest tests/containers.nix {}; + tests.etcd = callTest tests/etcd.nix {}; tests.firefox = callTest tests/firefox.nix {}; tests.firewall = callTest tests/firewall.nix {}; tests.gnome3 = callTest tests/gnome3.nix {}; diff --git a/nixos/tests/etcd.nix b/nixos/tests/etcd.nix new file mode 100644 index 000000000000..6c6dd84f558d --- /dev/null +++ b/nixos/tests/etcd.nix @@ -0,0 +1,108 @@ +# This test runs etcd as single node, multy node and using discovery + +import ./make-test.nix { + name = "etcd"; + + nodes = { + simple = + { config, pkgs, nodes, ... }: + { + services.etcd.enable = true; + services.etcd.listenClientUrls = ["http://0.0.0.0:4001"]; + environment.systemPackages = [ pkgs.curl ]; + networking.firewall.allowedTCPPorts = [ 4001 ]; + }; + + + node1 = + { config, pkgs, nodes, ... }: + { + services = { + etcd = { + enable = true; + listenPeerUrls = ["http://0.0.0.0:7001"]; + initialAdvertisePeerUrls = ["http://node1:7001"]; + initialCluster = ["node1=http://node1:7001" "node2=http://node2:7001"]; + }; + }; + + networking.firewall.allowedTCPPorts = [ 7001 ]; + }; + + node2 = + { config, pkgs, ... }: + { + services = { + etcd = { + enable = true; + listenPeerUrls = ["http://0.0.0.0:7001"]; + initialAdvertisePeerUrls = ["http://node2:7001"]; + initialCluster = ["node1=http://node1:7001" "node2=http://node2:7001"]; + }; + }; + + networking.firewall.allowedTCPPorts = [ 7001 ]; + }; + + discovery1 = + { config, pkgs, nodes, ... }: + { + services = { + etcd = { + enable = true; + listenPeerUrls = ["http://0.0.0.0:7001"]; + initialAdvertisePeerUrls = ["http://discovery1:7001"]; + discovery = "http://simple:4001/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/"; + }; + }; + + networking.firewall.allowedTCPPorts = [ 7001 ]; + }; + + discovery2 = + { config, pkgs, ... }: + { + services = { + etcd = { + enable = true; + listenPeerUrls = ["http://0.0.0.0:7001"]; + initialAdvertisePeerUrls = ["http://discovery2:7001"]; + discovery = "http://simple:4001/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/"; + }; + }; + + networking.firewall.allowedTCPPorts = [ 7001 ]; + }; + }; + + testScript = '' + subtest "single node", sub { + $simple->start(); + $simple->waitForUnit("etcd.service"); + $simple->succeed("etcdctl set /foo/bar 'Hello world'"); + $simple->succeed("etcdctl get /foo/bar | grep 'Hello world'"); + }; + + subtest "multy node", sub { + $node1->start(); + $node2->start(); + $node1->waitForUnit("etcd.service"); + $node2->waitForUnit("etcd.service"); + $node1->succeed("etcdctl set /foo/bar 'Hello world'"); + $node2->succeed("etcdctl get /foo/bar | grep 'Hello world'"); + $node1->shutdown(); + $node2->shutdown(); + }; + + subtest "discovery", sub { + $simple->succeed("curl -X PUT http://localhost:4001/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=2"); + + $discovery1->start(); + $discovery2->start(); + $discovery1->waitForUnit("etcd.service"); + $discovery2->waitForUnit("etcd.service"); + $discovery1->succeed("etcdctl set /foo/bar 'Hello world'"); + $discovery2->succeed("etcdctl get /foo/bar | grep 'Hello world'"); + }; + ''; +} -- cgit 1.4.1