nixos/jupyter: init service
This commit is contained in:
parent
359368f76f
commit
4d3ce5ca36
|
@ -250,6 +250,7 @@
|
||||||
./services/desktops/zeitgeist.nix
|
./services/desktops/zeitgeist.nix
|
||||||
./services/development/bloop.nix
|
./services/development/bloop.nix
|
||||||
./services/development/hoogle.nix
|
./services/development/hoogle.nix
|
||||||
|
./services/development/jupyter/default.nix
|
||||||
./services/editors/emacs.nix
|
./services/editors/emacs.nix
|
||||||
./services/editors/infinoted.nix
|
./services/editors/infinoted.nix
|
||||||
./services/games/factorio.nix
|
./services/games/factorio.nix
|
||||||
|
|
184
nixos/modules/services/development/jupyter/default.nix
Normal file
184
nixos/modules/services/development/jupyter/default.nix
Normal file
|
@ -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.
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
|
@ -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.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue