diff --git a/modules/misc/ids.nix b/modules/misc/ids.nix index d8ca11158dc..621571e1c33 100644 --- a/modules/misc/ids.nix +++ b/modules/misc/ids.nix @@ -100,6 +100,7 @@ minidlna = 91; elasticsearch = 92; tcpcryptd = 93; # tcpcryptd uses a hard-coded uid. We patch it in Nixpkgs to match this choice. + zope2 = 94; # When adding a uid, make sure it doesn't match an existing gid. diff --git a/modules/module-list.nix b/modules/module-list.nix index 931b7988f8d..c0aab2e1804 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -213,6 +213,7 @@ ./services/web-servers/nginx/default.nix ./services/web-servers/tomcat.nix ./services/web-servers/varnish/default.nix + ./services/web-servers/zope2.nix ./services/x11/desktop-managers/default.nix ./services/x11/display-managers/auto.nix ./services/x11/display-managers/default.nix diff --git a/modules/services/web-servers/zope2.nix b/modules/services/web-servers/zope2.nix new file mode 100644 index 00000000000..c92271e4660 --- /dev/null +++ b/modules/services/web-servers/zope2.nix @@ -0,0 +1,241 @@ +{ pkgs, config, ... }: + +with pkgs.lib; + +let + + cfg = config.services.zope2; + + zope2Opts = { name, config, ... }: { + options = { + + name = mkOption { + default = "${name}"; + type = types.string; + description = "The name of the zope2 instance. If undefined, the name of the attribute set will be used."; + }; + + threads = mkOption { + default = 2; + type = types.int; + description = "Specify the number of threads that Zope's ZServer web server will use to service requests. "; + }; + + http_address = mkOption { + default = "localhost:8080"; + type = types.string; + description = "Give a port and adress for the HTTP server."; + }; + + user = mkOption { + default = "zope2"; + type = types.string; + description = "The name of the effective user for the Zope process."; + }; + + extra = mkOption { + default = + '' + + mount-point / + cache-size 30000 + + blob-dir /var/lib/zope2/${name}/blobstorage + + path /var/lib/zope2/${name}/filestorage/Data.fs + + + + ''; + type = types.string; + description = "Extra zope.conf"; + }; + + packages = mkOption { + type = types.listOf types.package; + description = "The list of packages you want to make available to the zope2 instance."; + }; + + }; + }; + +in + +{ + + ###### interface + + options = { + + services.zope2.instances = mkOption { + default = {}; + type = types.loaOf types.optionSet; + example = { + plone01 = { + http_address = "127.0.0.1:8080"; + extra = + '' + + mount-point / + cache-size 30000 + + blob-dir /var/lib/zope2/plone01/blobstorage + + path /var/lib/zope2/plone01/filestorage/Data.fs + + + + ''; + + }; + }; + description = "zope2 instances to be created automaticaly by the system."; + options = [ zope2Opts ]; + }; + }; + + ###### implementation + + config = mkIf (cfg.instances != {}) { + + users.extraUsers.zope2.uid = config.ids.uids.zope2; + + systemd.services = + let + + createZope2Instance = opts: name: + let + interpreter = pkgs.writeScript "interpreter" + '' +import sys + +_interactive = True +if len(sys.argv) > 1: + _options, _args = __import__("getopt").getopt(sys.argv[1:], 'ic:m:') + _interactive = False + for (_opt, _val) in _options: + if _opt == '-i': + _interactive = True + elif _opt == '-c': + exec _val + elif _opt == '-m': + sys.argv[1:] = _args + _args = [] + __import__("runpy").run_module( + _val, {}, "__main__", alter_sys=True) + + if _args: + sys.argv[:] = _args + __file__ = _args[0] + del _options, _args + execfile(__file__) + +if _interactive: + del _interactive + __import__("code").interact(banner="", local=globals()) + ''; + env = pkgs.buildEnv { + name = "zope2-${name}-env"; + paths = [ pyenv pkgs.gnumake ]; + postBuild = + '' + echo "#!$out/bin/python" > $out/bin/interpreter + cat ${interpreter} >> $out/bin/interpreter + ''; + }; + pyenv = pkgs.buildEnv { + name = "zope2-${name}-pyenv"; + paths = [ + pkgs.python27 + pkgs.python27Packages.recursivePthLoader + pkgs.python27Packages."plone.recipe.zope2instance" + ] ++ attrValues pkgs.python27.modules + ++ opts.packages; + }; + conf = pkgs.writeText "zope2-${name}-conf" + ''%define INSTANCEHOME ${env} +instancehome $INSTANCEHOME +%define CLIENTHOME /var/lib/zope2/${name} +clienthome $CLIENTHOME + +debug-mode off +security-policy-implementation C +verbose-security off +default-zpublisher-encoding utf-8 +zserver-threads ${toString opts.threads} +effective-user ${opts.user} + +pid-filename /var/run/zope2-${name}.pid +lock-filename /var/lock/zope2-${name}.lock +python-check-interval 1000 +enable-product-installation off + + + zope_i18n_compile_mo_files false + + + +level INFO + + path /var/log/zope2/${name}.log + level INFO + + + + +level WARN + + path /var/log/zope2/${name}-Z2.log + format %(message)s + + + + +address ${opts.http_address} + + + + + name temporary storage for sessioning + +mount-point /temp_folder +container-class Products.TemporaryFolder.TemporaryContainer + + +${opts.extra} + ''; + ctl = pkgs.writeScript "zope2-${name}-ctl" + ''#!${env}/bin/python + +import sys +import plone.recipe.zope2instance.ctl + +if __name__ == '__main__': + sys.exit(plone.recipe.zope2instance.ctl.main( + ["-C", "${conf}"] + + sys.argv[1:]))''; + in { + description = "zope2 ${name} instance"; + after = [ "network.target" ]; # with RelStorage also add "postgresql.service" + wantedBy = [ "multi-user.target" ]; + path = opts.packages; + preStart = + '' + mkdir -p /var/log/zope2/ + mkdir -p /var/lib/zope2/${name}/filestorage /var/lib/zope2/${name}/blobstorage + chown ${opts.user} /var/lib/zope2/${name} -R + + ${ctl} adduser admin admin + ''; + + serviceConfig.Type = "forking"; + serviceConfig.ExecStart = "${ctl} start"; + serviceConfig.ExecStop = "${ctl} stop"; + serviceConfig.ExecReload = "${ctl} restart"; + }; + + in listToAttrs (map (name: { name = "zope2-${name}"; value = createZope2Instance (builtins.getAttr name cfg.instances) name; }) (builtins.attrNames cfg.instances)); + + }; + +}