Merge pull request #13125 from abbradar/uwsgi

Refactor uWSGI

+113 -61
+85 -40
nixos/modules/services/web-servers/uwsgi.nix
··· 5 let 6 cfg = config.services.uwsgi; 7 8 - python2Pkgs = pkgs.python2Packages.override { 9 - python = pkgs.uwsgi.python2; 10 - self = python2Pkgs; 11 }; 12 13 - python3Pkgs = pkgs.python3Packages.override { 14 - python = pkgs.uwsgi.python3; 15 - self = python3Pkgs; 16 - }; 17 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 - }; 41 42 - uwsgi = pkgs.uwsgi.override { 43 - plugins = cfg.plugins; 44 - }; 45 46 in { 47 ··· 71 vassals = { 72 moin = { 73 type = "normal"; 74 - python2Packages = self: with self; [ moinmoin ]; 75 socket = "${config.services.uwsgi.runDir}/uwsgi.sock"; 76 }; 77 }; 78 } 79 ''; 80 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>. 84 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 For <literal>emperor</literal> mode, you should use <literal>vassals</literal> attribute 88 which should be either a set of names and configurations or a path to a directory. 89 ''; 90 }; 91 ··· 118 ''; 119 serviceConfig = { 120 Type = "notify"; 121 - ExecStart = "${uwsgi}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${pkgs.writeText "uwsgi.json" (buildCfg cfg.instance)}"; 122 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 123 ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; 124 NotifyAccess = "main";
··· 5 let 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 + 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 + }; 57 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; 67 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 + }; 87 88 in { 89 ··· 113 vassals = { 114 moin = { 115 type = "normal"; 116 + pythonPackages = self: with self; [ moinmoin ]; 117 socket = "${config.services.uwsgi.runDir}/uwsgi.sock"; 118 }; 119 }; 120 } 121 ''; 122 description = '' 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. 128 129 For <literal>emperor</literal> mode, you should use <literal>vassals</literal> attribute 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. 134 ''; 135 }; 136 ··· 163 ''; 164 serviceConfig = { 165 Type = "notify"; 166 + ExecStart = "${uwsgi}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${buildCfg "server" cfg.instance}/server.json"; 167 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 168 ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; 169 NotifyAccess = "main";
+25 -21
pkgs/servers/uwsgi/default.nix
··· 1 { stdenv, lib, fetchurl, pkgconfig, jansson 2 - # plugins: list of strings, eg. [python2, python3] 3 , plugins 4 - , pam, withPAM ? stdenv.isLinux 5 - , systemd, withSystemd ? stdenv.isLinux 6 , python2, python3, ncurses 7 }: 8 9 - let pythonPlugin = pkg : { name = "python${if pkg ? isPy2 then "2" else "3"}"; 10 - interpreter = pkg; 11 path = "plugins/python"; 12 - deps = [ pkg ncurses ]; 13 install = '' 14 install -Dm644 uwsgidecorators.py $out/${pkg.sitePackages}/uwsgidecorators.py 15 ${pkg.executable} -m compileall $out/${pkg.sitePackages}/ 16 ${pkg.executable} -O -m compileall $out/${pkg.sitePackages}/ 17 ''; 18 }; 19 - available = [ (pythonPlugin python2) 20 (pythonPlugin python3) 21 ]; 22 - needed = builtins.filter (x: lib.any (y: x.name == y) plugins) available; 23 - in 24 25 - assert builtins.filter (x: lib.all (y: y.name != x) available) plugins == []; 26 27 stdenv.mkDerivation rec { 28 name = "uwsgi-2.0.11.2"; ··· 34 35 nativeBuildInputs = [ python3 pkgconfig ]; 36 37 - buildInputs = with stdenv.lib; 38 - [ jansson ] 39 - ++ optional withPAM pam 40 - ++ optional withSystemd systemd 41 - ++ lib.concatMap (x: x.deps) needed 42 ; 43 44 - basePlugins = with stdenv.lib; 45 - concatStringsSep "," 46 - ( optional withPAM "pam" 47 - ++ optional withSystemd "systemd_logger" 48 ); 49 50 passthru = { ··· 59 buildPhase = '' 60 mkdir -p $pluginDir 61 python3 uwsgiconfig.py --build nixos 62 - ${lib.concatMapStringsSep ";" (x: "${x.interpreter.interpreter} uwsgiconfig.py --plugin ${x.path} nixos ${x.name}") needed} 63 ''; 64 65 installPhase = '' 66 install -Dm755 uwsgi $out/bin/uwsgi 67 - #cp *_plugin.so $pluginDir || true 68 ${lib.concatMapStringsSep "\n" (x: x.install) needed} 69 ''; 70
··· 1 { stdenv, lib, fetchurl, pkgconfig, jansson 2 + # plugins: list of strings, eg. [ "python2" "python3" ] 3 , plugins 4 + , pam, withPAM ? false 5 + , systemd, withSystemd ? false 6 , python2, python3, ncurses 7 }: 8 9 + let pythonPlugin = pkg : lib.nameValuePair "python${if pkg ? isPy2 then "2" else "3"}" { 10 + interpreter = pkg.interpreter; 11 path = "plugins/python"; 12 + inputs = [ pkg ncurses ]; 13 install = '' 14 install -Dm644 uwsgidecorators.py $out/${pkg.sitePackages}/uwsgidecorators.py 15 ${pkg.executable} -m compileall $out/${pkg.sitePackages}/ 16 ${pkg.executable} -O -m compileall $out/${pkg.sitePackages}/ 17 ''; 18 }; 19 + 20 + available = lib.listToAttrs [ 21 + (pythonPlugin python2) 22 (pythonPlugin python3) 23 ]; 24 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 33 34 stdenv.mkDerivation rec { 35 name = "uwsgi-2.0.11.2"; ··· 41 42 nativeBuildInputs = [ python3 pkgconfig ]; 43 44 + buildInputs = [ jansson ] 45 + ++ lib.optional withPAM pam 46 + ++ lib.optional withSystemd systemd 47 + ++ lib.concatMap (x: x.inputs) needed 48 ; 49 50 + basePlugins = lib.concatStringsSep "," 51 + ( lib.optional withPAM "pam" 52 + ++ lib.optional withSystemd "systemd_logger" 53 ); 54 55 passthru = { ··· 64 buildPhase = '' 65 mkdir -p $pluginDir 66 python3 uwsgiconfig.py --build nixos 67 + ${lib.concatMapStringsSep ";" (x: "${x.interpreter} uwsgiconfig.py --plugin ${x.path} nixos ${x.name}") needed} 68 ''; 69 70 installPhase = '' 71 install -Dm755 uwsgi $out/bin/uwsgi 72 ${lib.concatMapStringsSep "\n" (x: x.install) needed} 73 ''; 74
+1
pkgs/servers/uwsgi/nixos.ini
··· 2 plugin_dir = @pluginDir@ 3 main_plugin = @basePlugins@ 4 json = true 5 inherit = base
··· 2 plugin_dir = @pluginDir@ 3 main_plugin = @basePlugins@ 4 json = true 5 + yaml = false 6 inherit = base
+2
pkgs/top-level/all-packages.nix
··· 3521 3522 uwsgi = callPackage ../servers/uwsgi { 3523 plugins = []; 3524 }; 3525 3526 vacuum = callPackage ../applications/networking/instant-messengers/vacuum {};
··· 3521 3522 uwsgi = callPackage ../servers/uwsgi { 3523 plugins = []; 3524 + withPAM = stdenv.isLinux; 3525 + withSystemd = stdenv.isLinux; 3526 }; 3527 3528 vacuum = callPackage ../applications/networking/instant-messengers/vacuum {};