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