1{
2 nixpkgs,
3 system,
4 hostSystem,
5 self,
6}: let
7 envVar = name: let
8 var = builtins.getEnv name;
9 in
10 if var == ""
11 then throw "\$${name} must be defined, see docs/hacking.md for more details"
12 else var;
13in
14 nixpkgs.lib.nixosSystem {
15 inherit system;
16 modules = [
17 self.nixosModules.knot
18 self.nixosModules.spindle
19 ({
20 lib,
21 config,
22 pkgs,
23 ...
24 }: {
25 virtualisation.vmVariant.virtualisation = {
26 host.pkgs = import nixpkgs {system = hostSystem;};
27
28 graphics = false;
29 memorySize = 2048;
30 diskSize = 10 * 1024;
31 cores = 2;
32 forwardPorts = [
33 # ssh
34 {
35 from = "host";
36 host.port = 2222;
37 guest.port = 22;
38 }
39 # knot
40 {
41 from = "host";
42 host.port = 6000;
43 guest.port = 6000;
44 }
45 # spindle
46 {
47 from = "host";
48 host.port = 6555;
49 guest.port = 6555;
50 }
51 ];
52 sharedDirectories = {
53 # We can't use the 9p mounts directly for most of these
54 # as SQLite is incompatible with them. So instead we
55 # mount the shared directories to a different location
56 # and copy the contents around on service start/stop.
57 knotData = {
58 source = "$TANGLED_VM_DATA_DIR/knot";
59 target = "/mnt/knot-data";
60 };
61 spindleData = {
62 source = "$TANGLED_VM_DATA_DIR/spindle";
63 target = "/mnt/spindle-data";
64 };
65 spindleLogs = {
66 source = "$TANGLED_VM_DATA_DIR/spindle-logs";
67 target = "/var/log/spindle";
68 };
69 };
70 };
71 # This is fine because any and all ports that are forwarded to host are explicitly marked above, we don't need a separate guest firewall
72 networking.firewall.enable = false;
73 time.timeZone = "Europe/London";
74 services.getty.autologinUser = "root";
75 environment.systemPackages = with pkgs; [curl vim git sqlite litecli];
76 services.tangled-knot = {
77 enable = true;
78 motd = "Welcome to the development knot!\n";
79 server = {
80 owner = envVar "TANGLED_VM_KNOT_OWNER";
81 hostname = "localhost:6000";
82 listenAddr = "0.0.0.0:6000";
83 };
84 };
85 services.tangled-spindle = {
86 enable = true;
87 server = {
88 owner = envVar "TANGLED_VM_SPINDLE_OWNER";
89 hostname = "localhost:6555";
90 listenAddr = "0.0.0.0:6555";
91 dev = true;
92 queueSize = 100;
93 maxJobCount = 2;
94 secrets = {
95 provider = "sqlite";
96 };
97 };
98 };
99 users = {
100 # So we don't have to deal with permission clashing between
101 # blank disk VMs and existing state
102 users.${config.services.tangled-knot.gitUser}.uid = 666;
103 groups.${config.services.tangled-knot.gitUser}.gid = 666;
104
105 # TODO: separate spindle user
106 };
107 systemd.services = let
108 mkDataSyncScripts = source: target: {
109 enableStrictShellChecks = true;
110
111 preStart = lib.mkBefore ''
112 mkdir -p ${target}
113 ${lib.getExe pkgs.rsync} -a ${source}/ ${target}
114 '';
115
116 postStop = lib.mkAfter ''
117 ${lib.getExe pkgs.rsync} -a ${target}/ ${source}
118 '';
119
120 serviceConfig.PermissionsStartOnly = true;
121 };
122 in {
123 knot = mkDataSyncScripts "/mnt/knot-data" config.services.tangled-knot.stateDir;
124 spindle = mkDataSyncScripts "/mnt/spindle-data" (builtins.dirOf config.services.tangled-spindle.server.dbPath);
125 };
126 })
127 ];
128 }