 25bef2d8f9
			
		
	
	
		25bef2d8f9
		
	
	
	
	
		
			
			The library does not depend on stdenv, that `stdenv` exposes `lib` is an artifact of the ancient origins of nixpkgs.
		
			
				
	
	
		
			155 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| # 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
 | |
|     extendedKeyUsage = serverAuth
 | |
|     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;
 | |
|         peerClientCertAuth = true;
 | |
|         listenClientUrls = ["https://127.0.0.1:2379"];
 | |
|         listenPeerUrls = ["https://0.0.0.0:2380"];
 | |
|       };
 | |
|     };
 | |
| 
 | |
|     environment.variables = {
 | |
|       ETCDCTL_CERT_FILE = "${etcd_client_cert}";
 | |
|       ETCDCTL_KEY_FILE = "${etcd_client_key}";
 | |
|       ETCDCTL_CA_FILE = "${ca_pem}";
 | |
|       ETCDCTL_PEERS = "https://127.0.0.1:2379";
 | |
|     };
 | |
| 
 | |
|     networking.firewall.allowedTCPPorts = [ 2380 ];
 | |
|   };
 | |
| in {
 | |
|   name = "etcd";
 | |
| 
 | |
|   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")
 | |
|         node2.wait_until_succeeds("etcdctl cluster-health")
 | |
|         node1.succeed("etcdctl set /foo/bar 'Hello world'")
 | |
|         node2.succeed("etcdctl get /foo/bar | grep 'Hello world'")
 | |
| 
 | |
|     with subtest("should add another member"):
 | |
|         node1.wait_until_succeeds("etcdctl member add node3 https://node3:2380")
 | |
|         node3.start()
 | |
|         node3.wait_for_unit("etcd.service")
 | |
|         node3.wait_until_succeeds("etcdctl member list | grep 'node3'")
 | |
|         node3.succeed("etcdctl cluster-health")
 | |
| 
 | |
|     with subtest("should survive member crash"):
 | |
|         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'")
 | |
|   '';
 | |
| })
 |