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