summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
authorAugustin Borsu <a.borsu@gmail.com>2018-01-12 00:13:55 +0100
committerFrederik Rietdijk <fridh@fridh.nl>2018-08-26 12:00:54 +0200
commit4d3ce5ca36a145748a4938cb0127c115d8fbacf0 (patch)
tree6729dcf6049561e39e517df0993291b4b2781e30 /nixos/modules
parent359368f76ff02a7f7229f2104d66de7643017f2d (diff)
downloadnixlib-4d3ce5ca36a145748a4938cb0127c115d8fbacf0.tar
nixlib-4d3ce5ca36a145748a4938cb0127c115d8fbacf0.tar.gz
nixlib-4d3ce5ca36a145748a4938cb0127c115d8fbacf0.tar.bz2
nixlib-4d3ce5ca36a145748a4938cb0127c115d8fbacf0.tar.lz
nixlib-4d3ce5ca36a145748a4938cb0127c115d8fbacf0.tar.xz
nixlib-4d3ce5ca36a145748a4938cb0127c115d8fbacf0.tar.zst
nixlib-4d3ce5ca36a145748a4938cb0127c115d8fbacf0.zip
nixos/jupyter: init service
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/development/jupyter/default.nix184
-rw-r--r--nixos/modules/services/development/jupyter/kernel-options.nix60
3 files changed, 245 insertions, 0 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 85440a8025c9..12944857af4e 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -250,6 +250,7 @@
   ./services/desktops/zeitgeist.nix
   ./services/development/bloop.nix
   ./services/development/hoogle.nix
+  ./services/development/jupyter/default.nix
   ./services/editors/emacs.nix
   ./services/editors/infinoted.nix
   ./services/games/factorio.nix
diff --git a/nixos/modules/services/development/jupyter/default.nix b/nixos/modules/services/development/jupyter/default.nix
new file mode 100644
index 000000000000..9fcc00431865
--- /dev/null
+++ b/nixos/modules/services/development/jupyter/default.nix
@@ -0,0 +1,184 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.jupyter;
+
+  # NOTE: We don't use top-level jupyter because we don't
+  # want to pass in JUPYTER_PATH but use .environment instead,
+  # saving a rebuild.
+  package = pkgs.python3.pkgs.notebook;
+
+  kernels = (pkgs.jupyter-kernel.create  {
+    definitions = if cfg.kernels != null
+      then cfg.kernels
+      else  pkgs.jupyter-kernel.default;
+  });
+
+  notebookConfig = pkgs.writeText "jupyter_config.py" ''
+    ${cfg.notebookConfig}
+
+    c.NotebookApp.password = ${cfg.password}
+  '';
+
+in {
+  meta.maintainers = with maintainers; [ aborsu ];
+
+  options.services.jupyter = {
+    enable = mkEnableOption "Jupyter development server";
+
+    ip = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = ''
+        IP address Jupyter will be listening on.
+      '';
+    };
+
+    port = mkOption {
+      type = types.int;
+      default = 8888;
+      description = ''
+        Port number Jupyter will be listening on.
+      '';
+    };
+
+    notebookDir = mkOption {
+      type = types.str;
+      default = "~/";
+      description = ''
+        Root directory for notebooks.
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = "jupyter";
+      description = ''
+        Name of the user used to run the jupyter service.
+        For security reason, jupyter should really not be run as root.
+        If not set (jupyter), the service will create a jupyter user with appropriate settings.
+      '';
+      example = "aborsu";
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "jupyter";
+      description = ''
+        Name of the group used to run the jupyter service.
+        Use this if you want to create a group of users that are able to view the notebook directory's content.
+      '';
+      example = "users";
+    };
+
+    password = mkOption {
+      type = types.str;
+      description = ''
+        Password to use with notebook.
+        Can be generated using:
+          In [1]: from notebook.auth import passwd
+          In [2]: passwd('test')
+          Out[2]: 'sha1:1b961dc713fb:88483270a63e57d18d43cf337e629539de1436ba'
+          NOTE: you need to keep the single quote inside the nix string.
+        Or you can use a python oneliner:
+          "open('/path/secret_file', 'r', encoding='utf8').read().strip()"
+        It will be interpreted at the end of the notebookConfig.
+      '';
+      example = [
+        "'sha1:1b961dc713fb:88483270a63e57d18d43cf337e629539de1436ba'"
+        "open('/path/secret_file', 'r', encoding='utf8').read().strip()"
+      ];
+    };
+
+    notebookConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        Raw jupyter config.
+      '';
+    };
+
+    kernels = mkOption {
+      type = types.nullOr (types.attrsOf(types.submodule (import ./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.sitePackages}/ipykernel/resources/logo-32x32.png";
+            logo64 = "$ {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.
+      ";
+    };
+  };
+
+  config = mkMerge [
+    (mkIf cfg.enable  {
+      systemd.services.jupyter = {
+        description = "Jupyter development server";
+
+        wantedBy = [ "multi-user.target" ];
+
+        # TODO: Patch notebook so we can explicitly pass in a shell
+        path = [ pkgs.bash ]; # needed for sh in cell magic to work
+
+        environment = {
+          JUPYTER_PATH = toString kernels;
+        };
+
+        serviceConfig = {
+          Restart = "always";
+          ExecStart = ''${package}/bin/jupyter-notebook \
+            --no-browser \
+            --ip=${cfg.ip} \
+            --port=${toString cfg.port} --port-retries 0 \
+            --notebook-dir=${cfg.notebookDir} \
+            --NotebookApp.config_file=${notebookConfig}
+          '';
+          User = cfg.user;
+          Group = cfg.group;
+          WorkingDirectory = "~";
+        };
+      };
+    })
+    (mkIf (cfg.enable && (cfg.group == "jupyter")) {
+      users.groups.jupyter = {};
+    })
+    (mkIf (cfg.enable && (cfg.user == "jupyter")) {
+      users.extraUsers.jupyter = {
+        extraGroups = [ cfg.group ];
+        home = "/var/lib/jupyter";
+        createHome = true;
+        useDefaultShell = true; # needed so that the user can start a terminal.
+      };
+    })
+  ];
+}
diff --git a/nixos/modules/services/development/jupyter/kernel-options.nix b/nixos/modules/services/development/jupyter/kernel-options.nix
new file mode 100644
index 000000000000..03547637449a
--- /dev/null
+++ b/nixos/modules/services/development/jupyter/kernel-options.nix
@@ -0,0 +1,60 @@
+# Options that can be used for creating a jupyter kernel.
+{lib }:
+
+with lib;
+
+{
+  options = {
+
+    displayName = mkOption {
+      type = types.str;
+      default = "";
+      example = [
+        "Python 3"
+        "Python 3 for Data Science"
+      ];
+      description = ''
+        Name that will be shown to the user.
+      '';
+    };
+
+    argv = mkOption {
+      type = types.listOf types.str;
+      example = [
+        "{customEnv.interpreter}"
+        "-m"
+        "ipykernel_launcher"
+        "-f"
+        "{connection_file}"
+      ];
+      description = ''
+        Command and arguments to start the kernel.
+      '';
+    };
+
+    language = mkOption {
+      type = types.str;
+      example = "python";
+      description = ''
+        Language of the environment. Typically the name of the binary.
+      '';
+    };
+
+    logo32 = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "{env.sitePackages}/ipykernel/resources/logo-32x32.png";
+      description = ''
+        Path to 32x32 logo png.
+      '';
+    };
+    logo64 = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "{env.sitePackages}/ipykernel/resources/logo-64x64.png";
+      description = ''
+        Path to 64x64 logo png.
+      '';
+    };
+  };
+}