Merge pull request #13125 from abbradar/uwsgi

Refactor uWSGI

+113 -61
+85 -40
nixos/modules/services/web-servers/uwsgi.nix
··· 5 5 let 6 6 cfg = config.services.uwsgi; 7 7 8 - python2Pkgs = pkgs.python2Packages.override { 9 - python = pkgs.uwsgi.python2; 10 - self = python2Pkgs; 8 + uwsgi = pkgs.uwsgi.override { 9 + plugins = cfg.plugins; 11 10 }; 12 11 13 - python3Pkgs = pkgs.python3Packages.override { 14 - python = pkgs.uwsgi.python3; 15 - self = python3Pkgs; 16 - }; 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 + json = builtins.toJSON { 36 + uwsgi = 37 + if c.type == "normal" 38 + then { 39 + inherit plugins; 40 + } // removeAttrs c [ "type" "pythonPackages" ] 41 + // optionalAttrs (python != null) { 42 + pythonpath = "@PYTHONPATH@"; 43 + env = (c.env or {}) // { 44 + PATH = optionalString (c ? env.PATH) "${c.env.PATH}:" + "@PATH@"; 45 + }; 46 + } 47 + else if c.type == "emperor" 48 + then { 49 + emperor = if builtins.typeOf c.vassals != "set" then c.vassals 50 + else pkgs.buildEnv { 51 + name = "vassals"; 52 + paths = mapAttrsToList buildCfg c.vassals; 53 + }; 54 + } // removeAttrs c [ "type" "vassals" ] 55 + else throw "`type` attribute in UWSGI configuration should be either 'normal' or 'emperor'"; 56 + }; 17 57 18 - buildCfg = c: if builtins.typeOf c != "set" then builtins.readFile c else builtins.toJSON { 19 - uwsgi = 20 - if c.type == "normal" 21 - then { 22 - pythonpath = 23 - (if c ? python2Packages 24 - then builtins.map (x: "${x}/${pkgs.uwsgi.python2.sitePackages}") (c.python2Packages python2Pkgs) 25 - else []) 26 - ++ (if c ? python3Packages 27 - then builtins.map (x: "${x}/${pkgs.uwsgi.python3.sitePackages}") (c.python3Packages python3Pkgs) 28 - else []); 29 - plugins = cfg.plugins; 30 - } // removeAttrs c [ "type" "python2Packages" "python3Packages" ] 31 - else if c.type == "emperor" 32 - then { 33 - emperor = if builtins.typeOf c.vassals != "set" then c.vassals 34 - else pkgs.buildEnv { 35 - name = "vassals"; 36 - paths = mapAttrsToList (n: c: pkgs.writeTextDir "${n}.json" (buildCfg c)) c.vassals; 37 - }; 38 - } // removeAttrs c [ "type" "vassals" ] 39 - else abort "type should be either 'normal' or 'emperor'"; 40 - }; 58 + in 59 + if python == null || c.type != "normal" 60 + then pkgs.writeTextDir "${name}.json" json 61 + else pkgs.stdenv.mkDerivation { 62 + name = "uwsgi-config"; 63 + inherit json; 64 + passAsFile = [ "json" ]; 65 + nativeBuildInputs = [ pythonPackages.wrapPython ]; 66 + pythonInputs = (c.pythonPackages or (self: [])) pythonPackages; 41 67 42 - uwsgi = pkgs.uwsgi.override { 43 - plugins = cfg.plugins; 44 - }; 68 + buildCommand = '' 69 + mkdir $out 70 + declare -A pythonPathsSeen=() 71 + program_PYTHONPATH= 72 + program_PATH= 73 + if [ -n "$pythonInputs" ]; then 74 + for i in $pythonInputs; do 75 + _addToPythonPath $i 76 + done 77 + fi 78 + # A hack to replace "@PYTHONPATH@" with a JSON list 79 + if [ -n "$program_PYTHONPATH" ]; then 80 + program_PYTHONPATH="\"''${program_PYTHONPATH//:/\",\"}\"" 81 + fi 82 + substitute $jsonPath $out/${name}.json \ 83 + --replace '"@PYTHONPATH@"' "[$program_PYTHONPATH]" \ 84 + --subst-var-by PATH "$program_PATH" 85 + ''; 86 + }; 45 87 46 88 in { 47 89 ··· 71 113 vassals = { 72 114 moin = { 73 115 type = "normal"; 74 - python2Packages = self: with self; [ moinmoin ]; 116 + pythonPackages = self: with self; [ moinmoin ]; 75 117 socket = "${config.services.uwsgi.runDir}/uwsgi.sock"; 76 118 }; 77 119 }; 78 120 } 79 121 ''; 80 122 description = '' 81 - uWSGI configuration. This awaits either a path to file or a set which will be made into one. 82 - If given a set, it awaits an attribute <literal>type</literal> which can be either <literal>normal</literal> 83 - or <literal>emperor</literal>. 123 + uWSGI configuration. It awaits an attribute <literal>type</literal> inside which can be either 124 + <literal>normal</literal> or <literal>emperor</literal>. 125 + 126 + For <literal>normal</literal> mode you can specify <literal>pythonPackages</literal> as a function 127 + from libraries set into a list of libraries. <literal>pythonpath</literal> will be set accordingly. 84 128 85 - For <literal>normal</literal> mode you can specify <literal>python2Packages</literal> and 86 - <literal>python3Packages</literal> as functions from libraries set into lists of libraries. 87 129 For <literal>emperor</literal> mode, you should use <literal>vassals</literal> attribute 88 130 which should be either a set of names and configurations or a path to a directory. 131 + 132 + Other attributes will be used in configuration file as-is. Notice that you can redefine 133 + <literal>plugins</literal> setting here. 89 134 ''; 90 135 }; 91 136 ··· 118 163 ''; 119 164 serviceConfig = { 120 165 Type = "notify"; 121 - ExecStart = "${uwsgi}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${pkgs.writeText "uwsgi.json" (buildCfg cfg.instance)}"; 166 + ExecStart = "${uwsgi}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${buildCfg "server" cfg.instance}/server.json"; 122 167 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 123 168 ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; 124 169 NotifyAccess = "main";
+25 -21
pkgs/servers/uwsgi/default.nix
··· 1 1 { stdenv, lib, fetchurl, pkgconfig, jansson 2 - # plugins: list of strings, eg. [python2, python3] 2 + # plugins: list of strings, eg. [ "python2" "python3" ] 3 3 , plugins 4 - , pam, withPAM ? stdenv.isLinux 5 - , systemd, withSystemd ? stdenv.isLinux 4 + , pam, withPAM ? false 5 + , systemd, withSystemd ? false 6 6 , python2, python3, ncurses 7 7 }: 8 8 9 - let pythonPlugin = pkg : { name = "python${if pkg ? isPy2 then "2" else "3"}"; 10 - interpreter = pkg; 9 + let pythonPlugin = pkg : lib.nameValuePair "python${if pkg ? isPy2 then "2" else "3"}" { 10 + interpreter = pkg.interpreter; 11 11 path = "plugins/python"; 12 - deps = [ pkg ncurses ]; 12 + inputs = [ pkg ncurses ]; 13 13 install = '' 14 14 install -Dm644 uwsgidecorators.py $out/${pkg.sitePackages}/uwsgidecorators.py 15 15 ${pkg.executable} -m compileall $out/${pkg.sitePackages}/ 16 16 ${pkg.executable} -O -m compileall $out/${pkg.sitePackages}/ 17 17 ''; 18 18 }; 19 - available = [ (pythonPlugin python2) 19 + 20 + available = lib.listToAttrs [ 21 + (pythonPlugin python2) 20 22 (pythonPlugin python3) 21 23 ]; 22 - needed = builtins.filter (x: lib.any (y: x.name == y) plugins) available; 23 - in 24 24 25 - assert builtins.filter (x: lib.all (y: y.name != x) available) plugins == []; 25 + getPlugin = name: 26 + let all = lib.concatStringsSep ", " (lib.attrNames available); 27 + in if lib.hasAttr name available 28 + then lib.getAttr name available // { inherit name; } 29 + else throw "Unknown UWSGI plugin ${name}, available : ${all}"; 30 + 31 + needed = builtins.map getPlugin plugins; 32 + in 26 33 27 34 stdenv.mkDerivation rec { 28 35 name = "uwsgi-2.0.11.2"; ··· 34 41 35 42 nativeBuildInputs = [ python3 pkgconfig ]; 36 43 37 - buildInputs = with stdenv.lib; 38 - [ jansson ] 39 - ++ optional withPAM pam 40 - ++ optional withSystemd systemd 41 - ++ lib.concatMap (x: x.deps) needed 44 + buildInputs = [ jansson ] 45 + ++ lib.optional withPAM pam 46 + ++ lib.optional withSystemd systemd 47 + ++ lib.concatMap (x: x.inputs) needed 42 48 ; 43 49 44 - basePlugins = with stdenv.lib; 45 - concatStringsSep "," 46 - ( optional withPAM "pam" 47 - ++ optional withSystemd "systemd_logger" 50 + basePlugins = lib.concatStringsSep "," 51 + ( lib.optional withPAM "pam" 52 + ++ lib.optional withSystemd "systemd_logger" 48 53 ); 49 54 50 55 passthru = { ··· 59 64 buildPhase = '' 60 65 mkdir -p $pluginDir 61 66 python3 uwsgiconfig.py --build nixos 62 - ${lib.concatMapStringsSep ";" (x: "${x.interpreter.interpreter} uwsgiconfig.py --plugin ${x.path} nixos ${x.name}") needed} 67 + ${lib.concatMapStringsSep ";" (x: "${x.interpreter} uwsgiconfig.py --plugin ${x.path} nixos ${x.name}") needed} 63 68 ''; 64 69 65 70 installPhase = '' 66 71 install -Dm755 uwsgi $out/bin/uwsgi 67 - #cp *_plugin.so $pluginDir || true 68 72 ${lib.concatMapStringsSep "\n" (x: x.install) needed} 69 73 ''; 70 74
+1
pkgs/servers/uwsgi/nixos.ini
··· 2 2 plugin_dir = @pluginDir@ 3 3 main_plugin = @basePlugins@ 4 4 json = true 5 + yaml = false 5 6 inherit = base
+2
pkgs/top-level/all-packages.nix
··· 3521 3521 3522 3522 uwsgi = callPackage ../servers/uwsgi { 3523 3523 plugins = []; 3524 + withPAM = stdenv.isLinux; 3525 + withSystemd = stdenv.isLinux; 3524 3526 }; 3525 3527 3526 3528 vacuum = callPackage ../applications/networking/instant-messengers/vacuum {};