From 228f08035d4be4a4cf67ef489fcc0a0bd9d85ddd Mon Sep 17 00:00:00 2001 From: Chris Ostrouchov Date: Thu, 6 Aug 2020 07:52:12 -0400 Subject: [PATCH] nixos/jupyterhub: init service --- nixos/modules/module-list.nix | 1 + .../development/jupyterhub/default.nix | 190 ++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 nixos/modules/services/development/jupyterhub/default.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index bc4af404b72..1802fc4bbdc 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -331,6 +331,7 @@ ./services/development/bloop.nix ./services/development/hoogle.nix ./services/development/jupyter/default.nix + ./services/development/jupyterhub/default.nix ./services/development/lorri.nix ./services/editors/emacs.nix ./services/editors/infinoted.nix diff --git a/nixos/modules/services/development/jupyterhub/default.nix b/nixos/modules/services/development/jupyterhub/default.nix new file mode 100644 index 00000000000..3e2d0979214 --- /dev/null +++ b/nixos/modules/services/development/jupyterhub/default.nix @@ -0,0 +1,190 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.jupyterhub; + + kernels = (pkgs.jupyter-kernel.create { + definitions = if cfg.kernels != null + then cfg.kernels + else pkgs.jupyter-kernel.default; + }); + + jupyterhubConfig = pkgs.writeText "jupyterhub_config.py" '' + c.JupyterHub.bind_url = "http://${cfg.host}:${toString cfg.port}" + + c.JupyterHub.authentication_class = "${cfg.authentication}" + c.JupyterHub.spawner_class = "${cfg.spawner}" + + c.SystemdSpawner.default_url = '/lab' + c.SystemdSpawner.cmd = "${cfg.jupyterlabEnv}/bin/jupyterhub-singleuser" + c.SystemdSpawner.environment = { + 'JUPYTER_PATH': '${kernels}' + } + + ${cfg.extraConfig} + ''; +in { + meta.maintainers = with maintainers; [ costrouc ]; + + options.services.jupyterhub = { + enable = mkEnableOption "Jupyterhub development server"; + + authentication = mkOption { + type = types.str; + default = "jupyterhub.auth.PAMAuthenticator"; + description = '' + Jupyterhub authentication to use + + There are many authenticators available including: oauth, pam, + ldap, kerberos, etc. + ''; + }; + + spawner = mkOption { + type = types.str; + default = "systemdspawner.SystemdSpawner"; + description = '' + Jupyterhub spawner to use + + There are many spawners available including: local process, + systemd, docker, kubernetes, yarn, batch, etc. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra contents appended to the jupyterhub configuration + + Jupyterhub configuration is a normal python file using + Traitlets. https://jupyterhub.readthedocs.io/en/stable/getting-started/config-basics.html. The + base configuration of this module was designed to have sane + defaults for configuration but you can override anything since + this is a python file. + ''; + example = literalExample '' + c.SystemdSpawner.memory = "8G" + c.SystemdSpawner.cpus = "2" + ''; + }; + + jupyterhubEnv = mkOption { + type = types.package; + default = (pkgs.python3.withPackages (p: with p; [ + jupyterhub + jupyterhub-systemdspawner + ])); + description = '' + Python environment to run jupyterhub + + Customizing will affect the packages available in the hub and + proxy. This will allow packages to be available for the + extraConfig that you may need. This will not normally need to + be changed. + ''; + }; + + jupyterlabEnv = mkOption { + type = types.package; + default = (pkgs.python3.withPackages (p: with p; [ + jupyterhub + jupyterlab + ])); + description = '' + Python environment to run jupyterlab + + Customizing will affect the packages available in the + jupyterlab server and the default kernel provided. This is the + way to customize the jupyterlab extensions and jupyter + notebook extensions. This will not normally need to + be changed. + ''; + }; + + kernels = mkOption { + type = types.nullOr (types.attrsOf(types.submodule (import ../jupyter/kernel-options.nix { + inherit lib; + }))); + + default = null; + example = literalExample '' + { + python3 = let + env = (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [ + ipykernel + pandas + scikitlearn + ])); + in { + displayName = "Python 3 for machine learning"; + argv = [ + "''${env.interpreter}" + "-m" + "ipykernel_launcher" + "-f" + "{connection_file}" + ]; + language = "python"; + logo32 = "''${env}/''${env.sitePackages}/ipykernel/resources/logo-32x32.png"; + logo64 = "''${env}/''${env.sitePackages}/ipykernel/resources/logo-64x64.png"; + }; + } + ''; + description = '' + Declarative kernel config + + Kernels can be declared in any language that supports and has + the required dependencies to communicate with a jupyter server. + In python's case, it means that ipykernel package must always be + included in the list of packages of the targeted environment. + ''; + }; + + port = mkOption { + type = types.port; + default = 8000; + description = '' + Port number Jupyterhub will be listening on + ''; + }; + + host = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + Bind IP JupyterHub will be listening on + ''; + }; + + stateDirectory = mkOption { + type = types.str; + default = "jupyterhub"; + description = '' + Directory for jupyterhub state (token + database) + ''; + }; + }; + + config = mkMerge [ + (mkIf cfg.enable { + systemd.services.jupyterhub = { + description = "Jupyterhub development server"; + + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Restart = "always"; + ExecStart = "${cfg.jupyterhubEnv}/bin/jupyterhub --config ${jupyterhubConfig}"; + User = "root"; + StateDirectory = cfg.stateDirectory; + WorkingDirectory = "/var/lib/${cfg.stateDirectory}"; + }; + }; + }) + ]; +}