{ config, lib, pkgs, ... }: with lib; let cfg = config.services.uwsgi; python2Pkgs = pkgs.python2Packages.override { python = pkgs.uwsgi.python2; self = python2Pkgs; }; python3Pkgs = pkgs.python3Packages.override { python = pkgs.uwsgi.python3; self = python3Pkgs; }; buildCfg = c: if builtins.typeOf c != "set" then builtins.readFile c else builtins.toJSON { uwsgi = if c.type == "normal" then { pythonpath = (if c ? python2Packages then builtins.map (x: "${x}/${pkgs.uwsgi.python2.sitePackages}") (c.python2Packages python2Pkgs) else []) ++ (if c ? python3Packages then builtins.map (x: "${x}/${pkgs.uwsgi.python3.sitePackages}") (c.python3Packages python3Pkgs) else []); plugins = cfg.plugins; } // removeAttrs c [ "type" "python2Packages" "python3Packages" ] else if c.type == "emperor" then { emperor = if builtins.typeOf c.vassals != "set" then c.vassals else pkgs.buildEnv { name = "vassals"; paths = mapAttrsToList (n: c: pkgs.writeTextDir "${n}.json" (buildCfg c)) c.vassals; }; } // removeAttrs c [ "type" "vassals" ] else abort "type should be either 'normal' or 'emperor'"; }; uwsgi = pkgs.uwsgi.override { plugins = cfg.plugins; }; in { options = { services.uwsgi = { enable = mkOption { type = types.bool; default = false; description = "Enable uWSGI"; }; runDir = mkOption { type = types.string; default = "/run/uwsgi"; description = "Where uWSGI communication sockets can live"; }; instance = mkOption { type = types.attrs; default = { type = "normal"; }; example = literalExample '' { type = "emperor"; vassals = { moin = { type = "normal"; python2Packages = self: with self; [ moinmoin ]; socket = "${config.services.uwsgi.runDir}/uwsgi.sock"; }; }; } ''; description = '' uWSGI configuration. This awaits either a path to file or a set which will be made into one. If given a set, it awaits an attribute type which can be either normal or emperor. For normal mode you can specify python2Packages and python3Packages as functions from libraries set into lists of libraries. For emperor mode, you should use vassals attribute which should be either a set of names and configurations or a path to a directory. ''; }; plugins = mkOption { type = types.listOf types.str; default = []; description = "Plugins used with uWSGI"; }; user = mkOption { type = types.str; default = "uwsgi"; description = "User account under which uwsgi runs."; }; group = mkOption { type = types.str; default = "uwsgi"; description = "Group account under which uwsgi runs."; }; }; }; config = mkIf cfg.enable { systemd.services.uwsgi = { wantedBy = [ "multi-user.target" ]; preStart = '' mkdir -p ${cfg.runDir} chown ${cfg.user}:${cfg.group} ${cfg.runDir} ''; serviceConfig = { Type = "notify"; ExecStart = "${uwsgi}/bin/uwsgi --uid ${cfg.user} --gid ${cfg.group} --json ${pkgs.writeText "uwsgi.json" (buildCfg cfg.instance)}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; NotifyAccess = "main"; KillSignal = "SIGQUIT"; }; }; users.extraUsers = optionalAttrs (cfg.user == "uwsgi") (singleton { name = "uwsgi"; group = cfg.group; uid = config.ids.uids.uwsgi; }); users.extraGroups = optionalAttrs (cfg.group == "uwsgi") (singleton { name = "uwsgi"; gid = config.ids.gids.uwsgi; }); }; }