1{ pkgs, ... }:
2let
3 listenPort = 30123;
4 testString = "It works!";
5 mkCreateSmallFileService =
6 {
7 path,
8 loop ? false,
9 }:
10 {
11 script = ''
12 ${pkgs.coreutils}/bin/dd if=/dev/zero of=${path} bs=1K count=100
13 ${pkgs.lib.optionalString loop "${pkgs.util-linux}/bin/losetup --find ${path}"}
14 '';
15 serviceConfig = {
16 Type = "oneshot";
17 };
18 wantedBy = [ "multi-user.target" ];
19 before = [ "nbd-server.service" ];
20 };
21in
22{
23 name = "nbd";
24
25 nodes = {
26 server =
27 { config, pkgs, ... }:
28 {
29 # Create some small files of zeros to use as the ndb disks
30 ## `vault-pub.disk` is accessible from any IP
31 systemd.services.create-pub-file = mkCreateSmallFileService { path = "/vault-pub.disk"; };
32 ## `vault-priv.disk` is accessible only from localhost.
33 ## It's also a loopback device to test exporting /dev/...
34 systemd.services.create-priv-file = mkCreateSmallFileService {
35 path = "/vault-priv.disk";
36 loop = true;
37 };
38 ## `aaa.disk` is just here because "[aaa]" sorts before
39 ## "[generic]" lexicographically, and nbd-server breaks if
40 ## "[generic]" isn't the first section.
41 systemd.services.create-aaa-file = mkCreateSmallFileService { path = "/aaa.disk"; };
42
43 # Needed only for nbd-client used in the tests.
44 environment.systemPackages = [ pkgs.nbd ];
45
46 # Open the nbd port in the firewall
47 networking.firewall.allowedTCPPorts = [ listenPort ];
48
49 # Run the nbd server and expose the small file created above
50 services.nbd.server = {
51 enable = true;
52 exports = {
53 aaa = {
54 path = "/aaa.disk";
55 };
56 vault-pub = {
57 path = "/vault-pub.disk";
58 };
59 vault-priv = {
60 path = "/dev/loop0";
61 allowAddresses = [
62 "127.0.0.1"
63 "::1"
64 ];
65 };
66 };
67 listenAddress = "0.0.0.0";
68 listenPort = listenPort;
69 };
70 };
71
72 client =
73 { config, pkgs, ... }:
74 {
75 programs.nbd.enable = true;
76 };
77 };
78
79 testScript = ''
80 testString = "${testString}"
81
82 start_all()
83 server.wait_for_open_port(${toString listenPort})
84
85 # Client: Connect to the server, write a small string to the nbd disk, and cleanly disconnect
86 client.succeed("nbd-client server ${toString listenPort} /dev/nbd0 -name vault-pub -persist")
87 client.succeed(f"echo '{testString}' | dd of=/dev/nbd0 conv=notrunc")
88 client.succeed("nbd-client -d /dev/nbd0")
89
90 # Server: Check that the string written by the client is indeed in the file
91 foundString = server.succeed(f"dd status=none if=/vault-pub.disk count={len(testString)}")[:len(testString)]
92 if foundString != testString:
93 raise Exception(f"Read the wrong string from nbd disk. Expected: '{testString}'. Found: '{foundString}'")
94
95 # Client: Fail to connect to the private disk
96 client.fail("nbd-client server ${toString listenPort} /dev/nbd0 -name vault-priv -persist")
97
98 # Server: Successfully connect to the private disk
99 server.succeed("nbd-client localhost ${toString listenPort} /dev/nbd0 -name vault-priv -persist")
100 server.succeed(f"echo '{testString}' | dd of=/dev/nbd0 conv=notrunc")
101 foundString = server.succeed(f"dd status=none if=/dev/loop0 count={len(testString)}")[:len(testString)]
102 if foundString != testString:
103 raise Exception(f"Read the wrong string from nbd disk. Expected: '{testString}'. Found: '{foundString}'")
104 server.succeed("nbd-client -d /dev/nbd0")
105
106 # Server: Successfully connect to the aaa disk
107 server.succeed("nbd-client localhost ${toString listenPort} /dev/nbd0 -name aaa -persist")
108 server.succeed(f"echo '{testString}' | dd of=/dev/nbd0 conv=notrunc")
109 foundString = server.succeed(f"dd status=none if=/aaa.disk count={len(testString)}")[:len(testString)]
110 if foundString != testString:
111 raise Exception(f"Read the wrong string from nbd disk. Expected: '{testString}'. Found: '{foundString}'")
112 server.succeed("nbd-client -d /dev/nbd0")
113 '';
114}