nixpkgs/nixos/tests/etcd-cluster.nix

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

158 lines
4.6 KiB
Nix
Raw Normal View History

# This test runs simple etcd cluster
import ./make-test-python.nix ({ pkgs, ... } : let
runWithOpenSSL = file: cmd: pkgs.runCommand file {
buildInputs = [ pkgs.openssl ];
} cmd;
ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048";
ca_pem = runWithOpenSSL "ca.pem" ''
openssl req \
-x509 -new -nodes -key ${ca_key} \
-days 10000 -out $out -subj "/CN=etcd-ca"
'';
etcd_key = runWithOpenSSL "etcd-key.pem" "openssl genrsa -out $out 2048";
etcd_csr = runWithOpenSSL "etcd.csr" ''
openssl req \
-new -key ${etcd_key} \
-out $out -subj "/CN=etcd" \
-config ${openssl_cnf}
'';
etcd_cert = runWithOpenSSL "etcd.pem" ''
openssl x509 \
-req -in ${etcd_csr} \
-CA ${ca_pem} -CAkey ${ca_key} \
-CAcreateserial -out $out \
-days 365 -extensions v3_req \
-extfile ${openssl_cnf}
'';
etcd_client_key = runWithOpenSSL "etcd-client-key.pem"
"openssl genrsa -out $out 2048";
etcd_client_csr = runWithOpenSSL "etcd-client-key.pem" ''
openssl req \
-new -key ${etcd_client_key} \
-out $out -subj "/CN=etcd-client" \
-config ${client_openssl_cnf}
'';
etcd_client_cert = runWithOpenSSL "etcd-client.crt" ''
openssl x509 \
-req -in ${etcd_client_csr} \
-CA ${ca_pem} -CAkey ${ca_key} -CAcreateserial \
-out $out -days 365 -extensions v3_req \
-extfile ${client_openssl_cnf}
'';
openssl_cnf = pkgs.writeText "openssl.cnf" ''
ions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
2023-05-23 22:54:43 +02:00
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = node1
DNS.2 = node2
DNS.3 = node3
IP.1 = 127.0.0.1
'';
client_openssl_cnf = pkgs.writeText "client-openssl.cnf" ''
ions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
'';
nodeConfig = {
services = {
etcd = {
enable = true;
keyFile = etcd_key;
certFile = etcd_cert;
trustedCaFile = ca_pem;
clientCertAuth = true;
listenClientUrls = ["https://127.0.0.1:2379"];
listenPeerUrls = ["https://0.0.0.0:2380"];
};
};
environment.variables = {
2023-05-23 22:54:43 +02:00
ETCD_CERT_FILE = "${etcd_client_cert}";
ETCD_KEY_FILE = "${etcd_client_key}";
ETCD_CA_FILE = "${ca_pem}";
ETCDCTL_ENDPOINTS = "https://127.0.0.1:2379";
ETCDCTL_CACERT = "${ca_pem}";
ETCDCTL_CERT = "${etcd_cert}";
ETCDCTL_KEY = "${etcd_key}";
};
networking.firewall.allowedTCPPorts = [ 2380 ];
};
in {
2023-05-24 05:20:20 +02:00
name = "etcd-cluster";
meta = with pkgs.lib.maintainers; {
maintainers = [ offline ];
};
nodes = {
node1 = { ... }: {
require = [nodeConfig];
services.etcd = {
initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380"];
initialAdvertisePeerUrls = ["https://node1:2380"];
};
};
node2 = { ... }: {
require = [nodeConfig];
services.etcd = {
initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380"];
initialAdvertisePeerUrls = ["https://node2:2380"];
};
};
node3 = { ... }: {
require = [nodeConfig];
services.etcd = {
initialCluster = ["node1=https://node1:2380" "node2=https://node2:2380" "node3=https://node3:2380"];
initialAdvertisePeerUrls = ["https://node3:2380"];
initialClusterState = "existing";
};
};
};
testScript = ''
with subtest("should start etcd cluster"):
node1.start()
node2.start()
node1.wait_for_unit("etcd.service")
node2.wait_for_unit("etcd.service")
2023-05-23 22:54:43 +02:00
node2.wait_until_succeeds("etcdctl endpoint status")
node1.succeed("etcdctl put /foo/bar 'Hello world'")
node2.succeed("etcdctl get /foo/bar | grep 'Hello world'")
with subtest("should add another member"):
2023-05-23 22:54:43 +02:00
node1.wait_until_succeeds("etcdctl member add node3 --peer-urls=https://node3:2380")
node3.start()
node3.wait_for_unit("etcd.service")
node3.wait_until_succeeds("etcdctl member list | grep 'node3'")
2023-05-23 22:54:43 +02:00
node3.succeed("etcdctl endpoint status")
with subtest("should survive member crash"):
node3.crash()
2023-05-23 22:54:43 +02:00
node1.succeed("etcdctl endpoint status")
node1.succeed("etcdctl put /foo/bar 'Hello degraded world'")
node1.succeed("etcdctl get /foo/bar | grep 'Hello degraded world'")
'';
})