1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.railcar;
7 generateUnit = name: containerConfig:
8 let
9 container = pkgs.ociTools.buildContainer {
10 args = [
11 (pkgs.writeShellScript "run.sh" containerConfig.cmd).outPath
12 ];
13 };
14 in
15 nameValuePair "railcar-${name}" {
16 enable = true;
17 wantedBy = [ "multi-user.target" ];
18 serviceConfig = {
19 ExecStart = ''
20 ${cfg.package}/bin/railcar -r ${cfg.stateDir} run ${name} -b ${container}
21 '';
22 Type = containerConfig.runType;
23 };
24 };
25 mount = with types; (submodule {
26 options = {
27 type = mkOption {
28 type = str;
29 default = "none";
30 description = ''
31 The type of the filesystem to be mounted.
32 Linux: filesystem types supported by the kernel as listed in
33 `/proc/filesystems` (e.g., "minix", "ext2", "ext3", "jfs", "xfs",
34 "reiserfs", "msdos", "proc", "nfs", "iso9660"). For bind mounts
35 (when options include either bind or rbind), the type is a dummy,
36 often "none" (not listed in /proc/filesystems).
37 '';
38 };
39 source = mkOption {
40 type = str;
41 description = "Source for the in-container mount";
42 };
43 options = mkOption {
44 type = listOf str;
45 default = [ "bind" ];
46 description = ''
47 Mount options of the filesystem to be used.
48
49 Support options are listed in the mount(8) man page. Note that
50 both filesystem-independent and filesystem-specific options
51 are listed.
52 '';
53 };
54 };
55 });
56in
57{
58 options.services.railcar = {
59 enable = mkEnableOption "railcar";
60
61 containers = mkOption {
62 default = {};
63 description = "Declarative container configuration";
64 type = with types; attrsOf (submodule ({ name, config, ... }: {
65 options = {
66 cmd = mkOption {
67 type = types.lines;
68 description = "Command or script to run inside the container";
69 };
70
71 mounts = mkOption {
72 type = with types; attrsOf mount;
73 default = {};
74 description = ''
75 A set of mounts inside the container.
76
77 The defaults have been chosen for simple bindmounts, meaning
78 that you only need to provide the "source" parameter.
79 '';
80 example = { "/data" = { source = "/var/lib/data"; }; };
81 };
82
83 runType = mkOption {
84 type = types.str;
85 default = "oneshot";
86 description = "The systemd service run type";
87 };
88
89 os = mkOption {
90 type = types.str;
91 default = "linux";
92 description = "OS type of the container";
93 };
94
95 arch = mkOption {
96 type = types.str;
97 default = "x86_64";
98 description = "Computer architecture type of the container";
99 };
100 };
101 }));
102 };
103
104 stateDir = mkOption {
105 type = types.path;
106 default = "/var/railcar";
107 description = "Railcar persistent state directory";
108 };
109
110 package = mkOption {
111 type = types.package;
112 default = pkgs.railcar;
113 defaultText = literalExpression "pkgs.railcar";
114 description = "Railcar package to use";
115 };
116 };
117
118 config = mkIf cfg.enable {
119 systemd.services = flip mapAttrs' cfg.containers (name: containerConfig:
120 generateUnit name containerConfig
121 );
122 };
123}
124