at 18.09-beta 5.3 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.uwsgi; 7 8 uwsgi = pkgs.uwsgi.override { 9 plugins = cfg.plugins; 10 }; 11 12 buildCfg = name: c: 13 let 14 plugins = 15 if any (n: !any (m: m == n) cfg.plugins) (c.plugins or []) 16 then throw "`plugins` attribute in UWSGI configuration contains plugins not in config.services.uwsgi.plugins" 17 else c.plugins or cfg.plugins; 18 19 hasPython = v: filter (n: n == "python${v}") plugins != []; 20 hasPython2 = hasPython "2"; 21 hasPython3 = hasPython "3"; 22 23 python = 24 if hasPython2 && hasPython3 then 25 throw "`plugins` attribute in UWSGI configuration shouldn't contain both python2 and python3" 26 else if hasPython2 then uwsgi.python2 27 else if hasPython3 then uwsgi.python3 28 else null; 29 30 pythonEnv = python.withPackages (c.pythonPackages or (self: [])); 31 32 uwsgiCfg = { 33 uwsgi = 34 if c.type == "normal" 35 then { 36 inherit plugins; 37 } // removeAttrs c [ "type" "pythonPackages" ] 38 // optionalAttrs (python != null) { 39 pythonpath = "${pythonEnv}/${python.sitePackages}"; 40 env = 41 # Argh, uwsgi expects list of key-values there instead of a dictionary. 42 let env' = c.env or []; 43 getPath = 44 x: if hasPrefix "PATH=" x 45 then substring (stringLength "PATH=") (stringLength x) x 46 else null; 47 oldPaths = filter (x: x != null) (map getPath env'); 48 in env' ++ [ "PATH=${optionalString (oldPaths != []) "${last oldPaths}:"}${pythonEnv}/bin" ]; 49 } 50 else if c.type == "emperor" 51 then { 52 emperor = if builtins.typeOf c.vassals != "set" then c.vassals 53 else pkgs.buildEnv { 54 name = "vassals"; 55 paths = mapAttrsToList buildCfg c.vassals; 56 }; 57 } // removeAttrs c [ "type" "vassals" ] 58 else throw "`type` attribute in UWSGI configuration should be either 'normal' or 'emperor'"; 59 }; 60 61 in pkgs.writeTextDir "${name}.json" (builtins.toJSON uwsgiCfg); 62 63in { 64 65 options = { 66 services.uwsgi = { 67 68 enable = mkOption { 69 type = types.bool; 70 default = false; 71 description = "Enable uWSGI"; 72 }; 73 74 runDir = mkOption { 75 type = types.string; 76 default = "/run/uwsgi"; 77 description = "Where uWSGI communication sockets can live"; 78 }; 79 80 instance = mkOption { 81 type = types.attrs; 82 default = { 83 type = "normal"; 84 }; 85 example = literalExample '' 86 { 87 type = "emperor"; 88 vassals = { 89 moin = { 90 type = "normal"; 91 pythonPackages = self: with self; [ moinmoin ]; 92 socket = "${config.services.uwsgi.runDir}/uwsgi.sock"; 93 }; 94 }; 95 } 96 ''; 97 description = '' 98 uWSGI configuration. It awaits an attribute <literal>type</literal> inside which can be either 99 <literal>normal</literal> or <literal>emperor</literal>. 100 101 For <literal>normal</literal> mode you can specify <literal>pythonPackages</literal> as a function 102 from libraries set into a list of libraries. <literal>pythonpath</literal> will be set accordingly. 103 104 For <literal>emperor</literal> mode, you should use <literal>vassals</literal> attribute 105 which should be either a set of names and configurations or a path to a directory. 106 107 Other attributes will be used in configuration file as-is. Notice that you can redefine 108 <literal>plugins</literal> setting here. 109 ''; 110 }; 111 112 plugins = mkOption { 113 type = types.listOf types.str; 114 default = []; 115 description = "Plugins used with uWSGI"; 116 }; 117 118 user = mkOption { 119 type = types.str; 120 default = "uwsgi"; 121 description = "User account under which uwsgi runs."; 122 }; 123 124 group = mkOption { 125 type = types.str; 126 default = "uwsgi"; 127 description = "Group account under which uwsgi runs."; 128 }; 129 }; 130 }; 131 132 config = mkIf cfg.enable { 133 systemd.services.uwsgi = { 134 wantedBy = [ "multi-user.target" ]; 135 preStart = '' 136 mkdir -p ${cfg.runDir} 137 chown ${cfg.user}:${cfg.group} ${cfg.runDir} 138 ''; 139 serviceConfig = { 140 Type = "notify"; 141 ExecStart = "${uwsgi}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${buildCfg "server" cfg.instance}/server.json"; 142 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 143 ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; 144 NotifyAccess = "main"; 145 KillSignal = "SIGQUIT"; 146 }; 147 }; 148 149 users.users = optionalAttrs (cfg.user == "uwsgi") (singleton 150 { name = "uwsgi"; 151 group = cfg.group; 152 uid = config.ids.uids.uwsgi; 153 }); 154 155 users.groups = optionalAttrs (cfg.group == "uwsgi") (singleton 156 { name = "uwsgi"; 157 gid = config.ids.gids.uwsgi; 158 }); 159 }; 160}