summary refs log tree commit diff
path: root/nixos/tests/etcd.nix
blob: 4eca6558dde057b6179eb2fb7057d5ca762752c8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# This test runs simple etcd cluster

import ./make-test.nix ({ pkgs, ... } : let
  certs = pkgs.runCommand "certs" {
    buildInputs = [pkgs.openssl];
  } ''
    mkdir -p $out
    openssl genrsa -out $out/ca-key.pem 2048
    openssl req -x509 -new -nodes -key $out/ca-key.pem -days 10000 -out $out/ca.pem -subj "/CN=etcd-ca"

    cat << EOF > openssl.cnf
    ions = v3_req
    distinguished_name = req_distinguished_name
    [req_distinguished_name]
    [ v3_req ]
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = node1
    DNS.2 = node2
    DNS.3 = node3
    IP.1 = 127.0.0.1
    EOF

    openssl genrsa -out $out/etcd-key.pem 2048
    openssl req -new -key $out/etcd-key.pem -out etcd.csr -subj "/CN=etcd" -config openssl.cnf
    openssl x509 -req -in etcd.csr -CA $out/ca.pem -CAkey $out/ca-key.pem -CAcreateserial -out $out/etcd.pem -days 365 -extensions v3_req -extfile openssl.cnf

    cat << EOF > client-openssl.cnf
    ions = v3_req
    distinguished_name = req_distinguished_name
    [req_distinguished_name]
    [ v3_req ]
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth
    EOF

    openssl genrsa -out $out/etcd-client-key.pem 2048
    openssl req -new -key $out/etcd-client-key.pem -out etcd-client.csr -subj "/CN=etcd-client" -config client-openssl.cnf
    openssl x509 -req -in etcd-client.csr -CA $out/ca.pem -CAkey $out/ca-key.pem -CAcreateserial -out $out/etcd-client.pem -days 365 -extensions v3_req -extfile client-openssl.cnf
  '';

  nodeConfig = {
    services = {
      etcd = {
        enable = true;
        keyFile = "${certs}/etcd-key.pem";
        certFile = "${certs}/etcd.pem";
        trustedCaFile = "${certs}/ca.pem";
        peerClientCertAuth = true;
        listenClientUrls = ["https://127.0.0.1:2379"];
        listenPeerUrls = ["https://0.0.0.0:2380"];
      };
    };

    environment.variables = {
      ETCDCTL_CERT_FILE = "${certs}/etcd-client.pem";
      ETCDCTL_KEY_FILE = "${certs}/etcd-client-key.pem";
      ETCDCTL_CA_FILE = "${certs}/ca.pem";
      ETCDCTL_PEERS = "https://127.0.0.1:2379";
    };

    networking.firewall.allowedTCPPorts = [ 2380 ];
  };
in {
  name = "etcd";

  meta = with pkgs.stdenv.lib.maintainers; {
    maintainers = [ offline ];
  };

  nodes = {
    node1 = { config, pkgs, nodes, ... }: {
      require = [nodeConfig];
      services.etcd = {
        initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380"];
        initialAdvertisePeerUrls = ["https://node1:2380"];
      };
    };

    node2 = { config, pkgs, ... }: {
      require = [nodeConfig];
      services.etcd = {
        initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380"];
        initialAdvertisePeerUrls = ["https://node2:2380"];
      };
    };

    node3 = { config, pkgs, ... }: {
      require = [nodeConfig];
      services.etcd = {
        initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380" "node3=https://node3:2380"];
        initialAdvertisePeerUrls = ["https://node3:2380"];
        initialClusterState = "existing";
      };
    };
  };

  testScript = ''
    subtest "should start etcd cluster", sub {
      $node1->start();
      $node2->start();
      $node1->waitForUnit("etcd.service");
      $node2->waitForUnit("etcd.service");
      $node2->waitUntilSucceeds("etcdctl cluster-health");
      $node1->succeed("etcdctl set /foo/bar 'Hello world'");
      $node2->succeed("etcdctl get /foo/bar | grep 'Hello world'");
    };

    subtest "should add another member", sub {
      $node1->succeed("etcdctl member add node3 https://node3:2380");
      $node3->start();
      $node3->waitForUnit("etcd.service");
      $node3->waitUntilSucceeds("etcdctl member list | grep 'node3'");
      $node3->succeed("etcdctl cluster-health");
    };

    subtest "should survive member crash", sub {
      $node3->crash;
      $node1->succeed("etcdctl cluster-health");
      $node1->succeed("etcdctl set /foo/bar 'Hello degraded world'");
      $node1->succeed("etcdctl get /foo/bar | grep 'Hello degraded world'");
    };
  '';
})