From b846922d87bad5adeb18dff93afd68eda27ced14 Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Thu, 14 Sep 2023 16:37:06 -0500 Subject: [PATCH 01/15] =?UTF-8?q?refactor:=20=E2=AC=86=EF=B8=8F=20upgrade?= =?UTF-8?q?=20djlabhub?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/.env | 9 +- config/djlab_config.yaml | 10 - config/jupyterhub_config.py | 1490 +++++++++++++++++++++++++++++++ dist/alpine/Dockerfile | 2 +- dist/alpine/docker-compose.yaml | 27 +- dist/debian/Dockerfile | 13 +- dist/debian/docker-compose.yaml | 38 +- utilities/add_entrypoint.sh | 16 +- 8 files changed, 1546 insertions(+), 59 deletions(-) delete mode 100644 config/djlab_config.yaml create mode 100644 config/jupyterhub_config.py diff --git a/config/.env b/config/.env index 4210d39..4ec08a0 100644 --- a/config/.env +++ b/config/.env @@ -1,8 +1,8 @@ ## build -# PY_VER='3.10' +PY_VER='3.10' -PY_VER=3.9 +# PY_VER=3.9 # PY_VER=3.8 @@ -10,7 +10,7 @@ PY_VER=3.9 JUPYTERHUB_VER=1.4.2 -BASE_IMAGE_HASH=8121b5d +BASE_IMAGE_HASH=eb6a760 @@ -28,3 +28,6 @@ HOST_UID=1000 DISTRO=debian PACKAGE_MANAGER=apt + +MYSQL_VER=8 +DJ_PASS=example diff --git a/config/djlab_config.yaml b/config/djlab_config.yaml deleted file mode 100644 index 9fb4588..0000000 --- a/config/djlab_config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -djlab: - jupyter_server: - display_filepath: '/home/README.md' - password: 'datajoint' - save_output: 'FALSE' -djlabhub: - server_name: 'NULL' - notebook_repo: - target: 'NULL' - subpath: '.' \ No newline at end of file diff --git a/config/jupyterhub_config.py b/config/jupyterhub_config.py new file mode 100644 index 0000000..72e6020 --- /dev/null +++ b/config/jupyterhub_config.py @@ -0,0 +1,1490 @@ +import os +import pwd +from traitlets.config import Config + +c = Config() if "c" not in locals() else c + +# get the current user +user = [u for u in pwd.getpwall() if u.pw_uid == os.getuid()][0] + +# Configuration file for jupyterhub. + +#------------------------------------------------------------------------------ +# Application(SingletonConfigurable) configuration +#------------------------------------------------------------------------------ +## This is an application. + +## The date format used by logging formatters for %(asctime)s +# Default: '%Y-%m-%d %H:%M:%S' +# c.Application.log_datefmt = '%Y-%m-%d %H:%M:%S' + +## The Logging format template +# Default: '[%(name)s]%(highlevel)s %(message)s' +# c.Application.log_format = '[%(name)s]%(highlevel)s %(message)s' + +## Set the log level by value or name. +# Choices: any of [0, 10, 20, 30, 40, 50, 'DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL'] +# Default: 30 +# c.Application.log_level = 30 + +## Instead of starting the Application, dump configuration to stdout +# Default: False +# c.Application.show_config = False + +## Instead of starting the Application, dump configuration to stdout (as JSON) +# Default: False +# c.Application.show_config_json = False + +#------------------------------------------------------------------------------ +# JupyterHub(Application) configuration +#------------------------------------------------------------------------------ +## An Application for starting a Multi-User Jupyter Notebook server. + +## Maximum number of concurrent servers that can be active at a time. +# +# Setting this can limit the total resources your users can consume. +# +# An active server is any server that's not fully stopped. It is considered +# active from the time it has been requested until the time that it has +# completely stopped. +# +# If this many user servers are active, users will not be able to launch new +# servers until a server is shutdown. Spawn requests will be rejected with a 429 +# error asking them to try again. +# +# If set to 0, no limit is enforced. +# Default: 0 +# c.JupyterHub.active_server_limit = 0 + +## Duration (in seconds) to determine the number of active users. +# Default: 1800 +# c.JupyterHub.active_user_window = 1800 + +## Resolution (in seconds) for updating activity +# +# If activity is registered that is less than activity_resolution seconds more +# recent than the current value, the new value will be ignored. +# +# This avoids too many writes to the Hub database. +# Default: 30 +# c.JupyterHub.activity_resolution = 30 + +## DEPRECATED since version 2.0.0. +# +# The default admin role has full permissions, use custom RBAC scopes instead to +# create restricted administrator roles. +# https://jupyterhub.readthedocs.io/en/stable/rbac/index.html +# Default: False +# c.JupyterHub.admin_access = False + +## DEPRECATED since version 0.7.2, use Authenticator.admin_users instead. +# Default: set() +# c.JupyterHub.admin_users = set() + +## Allow named single-user servers per user +# Default: False +# c.JupyterHub.allow_named_servers = False +c.JupyterHub.allow_named_servers = True + +## Answer yes to any questions (e.g. confirm overwrite) +# Default: False +# c.JupyterHub.answer_yes = False + +## The default amount of records returned by a paginated endpoint +# Default: 50 +# c.JupyterHub.api_page_default_limit = 50 + +## The maximum amount of records that can be returned at once +# Default: 200 +# c.JupyterHub.api_page_max_limit = 200 + +## PENDING DEPRECATION: consider using services +# +# Dict of token:username to be loaded into the database. +# +# Allows ahead-of-time generation of API tokens for use by externally managed services, +# which authenticate as JupyterHub users. +# +# Consider using services for general services that talk to the +# JupyterHub API. +# Default: {} +# c.JupyterHub.api_tokens = {} + +## Authentication for prometheus metrics +# Default: True +# c.JupyterHub.authenticate_prometheus = True + +## Class for authenticating users. +# +# This should be a subclass of :class:`jupyterhub.auth.Authenticator` +# +# with an :meth:`authenticate` method that: +# +# - is a coroutine (asyncio or tornado) +# - returns username on success, None on failure +# - takes two arguments: (handler, data), +# where `handler` is the calling web.RequestHandler, +# and `data` is the POST form data from the login page. +# +# .. versionchanged:: 1.0 +# authenticators may be registered via entry points, +# e.g. `c.JupyterHub.authenticator_class = 'pam'` +# +# Currently installed: +# - default: jupyterhub.auth.PAMAuthenticator +# - dummy: jupyterhub.auth.DummyAuthenticator +# - null: jupyterhub.auth.NullAuthenticator +# - pam: jupyterhub.auth.PAMAuthenticator +# Default: 'jupyterhub.auth.PAMAuthenticator' +# c.JupyterHub.authenticator_class = 'jupyterhub.auth.PAMAuthenticator' +# c.JupyterHub.authenticator_class = os.getenv("JUPYTER_HUB_AUTHENTICATOR_CLASS", "jupyterhub.auth.DummyAuthenticator") + +## The base URL of the entire application. +# +# Add this to the beginning of all JupyterHub URLs. +# Use base_url to run JupyterHub within an existing website. +# +# .. deprecated: 0.9 +# Use JupyterHub.bind_url +# Default: '/' +# c.JupyterHub.base_url = '/' + +## The public facing URL of the whole JupyterHub application. +# +# This is the address on which the proxy will bind. +# Sets protocol, ip, base_url +# Default: 'http://:8000' +# c.JupyterHub.bind_url = 'http://:8000' + +## Whether to shutdown the proxy when the Hub shuts down. +# +# Disable if you want to be able to teardown the Hub while leaving the +# proxy running. +# +# Only valid if the proxy was starting by the Hub process. +# +# If both this and cleanup_servers are False, sending SIGINT to the Hub will +# only shutdown the Hub, leaving everything else running. +# +# The Hub should be able to resume from database state. +# Default: True +# c.JupyterHub.cleanup_proxy = True + +## Whether to shutdown single-user servers when the Hub shuts down. +# +# Disable if you want to be able to teardown the Hub while leaving the +# single-user servers running. +# +# If both this and cleanup_proxy are False, sending SIGINT to the Hub will +# only shutdown the Hub, leaving everything else running. +# +# The Hub should be able to resume from database state. +# Default: True +# c.JupyterHub.cleanup_servers = True + +## Maximum number of concurrent users that can be spawning at a time. +# +# Spawning lots of servers at the same time can cause performance problems for +# the Hub or the underlying spawning system. Set this limit to prevent bursts of +# logins from attempting to spawn too many servers at the same time. +# +# This does not limit the number of total running servers. See +# active_server_limit for that. +# +# If more than this many users attempt to spawn at a time, their requests will +# be rejected with a 429 error asking them to try again. Users will have to wait +# for some of the spawning services to finish starting before they can start +# their own. +# +# If set to 0, no limit is enforced. +# Default: 100 +# c.JupyterHub.concurrent_spawn_limit = 100 + +## The config file to load +# Default: 'jupyterhub_config.py' +# c.JupyterHub.config_file = 'jupyterhub_config.py' + +## DEPRECATED: does nothing +# Default: False +# c.JupyterHub.confirm_no_ssl = False + +## Number of days for a login cookie to be valid. +# Default is two weeks. +# Default: 14 +# c.JupyterHub.cookie_max_age_days = 14 + +## The cookie secret to use to encrypt cookies. +# +# Loaded from the JPY_COOKIE_SECRET env variable by default. +# +# Should be exactly 256 bits (32 bytes). +# Default: traitlets.Undefined +# c.JupyterHub.cookie_secret = traitlets.Undefined + +## File in which to store the cookie secret. +# Default: 'jupyterhub_cookie_secret' +# c.JupyterHub.cookie_secret_file = 'jupyterhub_cookie_secret' + +## Custom scopes to define. +# +# For use when defining custom roles, +# to grant users granular permissions +# +# All custom scopes must have a description, +# and must start with the prefix `custom:`. +# +# For example:: +# +# custom_scopes = { +# "custom:jupyter_server:read": { +# "description": "read-only access to a single-user server", +# }, +# } +# Default: {} +# c.JupyterHub.custom_scopes = {} + +## The location of jupyterhub data files (e.g. /usr/local/share/jupyterhub) +# Default: '/home/drewyang/anaconda3/share/jupyterhub' +# c.JupyterHub.data_files_path = '/home/drewyang/anaconda3/share/jupyterhub' + +## Include any kwargs to pass to the database connection. +# See sqlalchemy.create_engine for details. +# Default: {} +# c.JupyterHub.db_kwargs = {} + +## url for the database. e.g. `sqlite:///jupyterhub.sqlite` +# Default: 'sqlite:///jupyterhub.sqlite' +# c.JupyterHub.db_url = 'sqlite:///jupyterhub.sqlite' + +## log all database transactions. This has A LOT of output +# Default: False +# c.JupyterHub.debug_db = False + +## DEPRECATED since version 0.8: Use ConfigurableHTTPProxy.debug +# Default: False +# c.JupyterHub.debug_proxy = False + +## If named servers are enabled, default name of server to spawn or open when no +# server is specified, e.g. by user-redirect. +# +# Note: This has no effect if named servers are not enabled, and does _not_ +# change the existence or behavior of the default server named `''` (the empty +# string). This only affects which named server is launched when no server is +# specified, e.g. by links to `/hub/user-redirect/lab/tree/mynotebook.ipynb`. +# Default: '' +# c.JupyterHub.default_server_name = '' + +## The default URL for users when they arrive (e.g. when user directs to "/") +# +# By default, redirects users to their own server. +# +# Can be a Unicode string (e.g. '/hub/home') or a callable based on the handler +# object: +# +# :: +# +# def default_url_fn(handler): +# user = handler.current_user +# if user and user.admin: +# return '/hub/admin' +# return '/hub/home' +# +# c.JupyterHub.default_url = default_url_fn +# Default: traitlets.Undefined +# c.JupyterHub.default_url = traitlets.Undefined + +## Dict authority:dict(files). Specify the key, cert, and/or +# ca file for an authority. This is useful for externally managed +# proxies that wish to use internal_ssl. +# +# The files dict has this format (you must specify at least a cert):: +# +# { +# 'key': '/path/to/key.key', +# 'cert': '/path/to/cert.crt', +# 'ca': '/path/to/ca.crt' +# } +# +# The authorities you can override: 'hub-ca', 'notebooks-ca', +# 'proxy-api-ca', 'proxy-client-ca', and 'services-ca'. +# +# Use with internal_ssl +# Default: {} +# c.JupyterHub.external_ssl_authorities = {} + +## DEPRECATED. +# +# If you need to register additional HTTP endpoints please use services instead. +# Default: [] +# c.JupyterHub.extra_handlers = [] + +## DEPRECATED: use output redirection instead, e.g. +# +# jupyterhub &>> /var/log/jupyterhub.log +# Default: '' +# c.JupyterHub.extra_log_file = '' + +## Extra log handlers to set on JupyterHub logger +# Default: [] +# c.JupyterHub.extra_log_handlers = [] + +## Alternate header to use as the Host (e.g., X-Forwarded-Host) +# when determining whether a request is cross-origin +# +# This may be useful when JupyterHub is running behind a proxy that rewrites +# the Host header. +# Default: '' +# c.JupyterHub.forwarded_host_header = '' + +## Generate certs used for internal ssl +# Default: False +# c.JupyterHub.generate_certs = False + +## Generate default config file +# Default: False +# c.JupyterHub.generate_config = False + +## The URL on which the Hub will listen. This is a private URL for internal +# communication. Typically set in combination with hub_connect_url. If a unix +# socket, hub_connect_url **must** also be set. +# +# For example: +# +# "http://127.0.0.1:8081" +# "unix+http://%2Fsrv%2Fjupyterhub%2Fjupyterhub.sock" +# +# .. versionadded:: 0.9 +# Default: '' +# c.JupyterHub.hub_bind_url = '' + +## The ip or hostname for proxies and spawners to use +# for connecting to the Hub. +# +# Use when the bind address (`hub_ip`) is 0.0.0.0, :: or otherwise different +# from the connect address. +# +# Default: when `hub_ip` is 0.0.0.0 or ::, use `socket.gethostname()`, +# otherwise use `hub_ip`. +# +# Note: Some spawners or proxy implementations might not support hostnames. Check your +# spawner or proxy documentation to see if they have extra requirements. +# +# .. versionadded:: 0.8 +# Default: '' +# c.JupyterHub.hub_connect_ip = '' + +## DEPRECATED +# +# Use hub_connect_url +# +# .. versionadded:: 0.8 +# +# .. deprecated:: 0.9 +# Use hub_connect_url +# Default: 0 +# c.JupyterHub.hub_connect_port = 0 + +## The URL for connecting to the Hub. Spawners, services, and the proxy will use +# this URL to talk to the Hub. +# +# Only needs to be specified if the default hub URL is not connectable (e.g. +# using a unix+http:// bind url). +# +# .. seealso:: +# JupyterHub.hub_connect_ip +# JupyterHub.hub_bind_url +# +# .. versionadded:: 0.9 +# Default: '' +# c.JupyterHub.hub_connect_url = '' + +## The ip address for the Hub process to *bind* to. +# +# By default, the hub listens on localhost only. This address must be accessible from +# the proxy and user servers. You may need to set this to a public ip or '' for all +# interfaces if the proxy or user servers are in containers or on a different host. +# +# See `hub_connect_ip` for cases where the bind and connect address should differ, +# or `hub_bind_url` for setting the full bind URL. +# Default: '127.0.0.1' +# c.JupyterHub.hub_ip = '127.0.0.1' + +## The internal port for the Hub process. +# +# This is the internal port of the hub itself. It should never be accessed directly. +# See JupyterHub.port for the public port to use when accessing jupyterhub. +# It is rare that this port should be set except in cases of port conflict. +# +# See also `hub_ip` for the ip and `hub_bind_url` for setting the full +# bind URL. +# Default: 8081 +# c.JupyterHub.hub_port = 8081 + +## The routing prefix for the Hub itself. +# +# Override to send only a subset of traffic to the Hub. Default is to use the +# Hub as the default route for all requests. +# +# This is necessary for normal jupyterhub operation, as the Hub must receive +# requests for e.g. `/user/:name` when the user's server is not running. +# +# However, some deployments using only the JupyterHub API may want to handle +# these events themselves, in which case they can register their own default +# target with the proxy and set e.g. `hub_routespec = /hub/` to serve only the +# hub's own pages, or even `/hub/api/` for api-only operation. +# +# Note: hub_routespec must include the base_url, if any. +# +# .. versionadded:: 1.4 +# Default: '/' +# c.JupyterHub.hub_routespec = '/' + +## Trigger implicit spawns after this many seconds. +# +# When a user visits a URL for a server that's not running, +# they are shown a page indicating that the requested server +# is not running with a button to spawn the server. +# +# Setting this to a positive value will redirect the user +# after this many seconds, effectively clicking this button +# automatically for the users, +# automatically beginning the spawn process. +# +# Warning: this can result in errors and surprising behavior +# when sharing access URLs to actual servers, +# since the wrong server is likely to be started. +# Default: 0 +# c.JupyterHub.implicit_spawn_seconds = 0 + +## Timeout (in seconds) to wait for spawners to initialize +# +# Checking if spawners are healthy can take a long time if many spawners are +# active at hub start time. +# +# If it takes longer than this timeout to check, init_spawner will be left to +# complete in the background and the http server is allowed to start. +# +# A timeout of -1 means wait forever, which can mean a slow startup of the Hub +# but ensures that the Hub is fully consistent by the time it starts responding +# to requests. This matches the behavior of jupyterhub 1.0. +# +# .. versionadded: 1.1.0 +# Default: 10 +# c.JupyterHub.init_spawners_timeout = 10 + +## The location to store certificates automatically created by +# JupyterHub. +# +# Use with internal_ssl +# Default: 'internal-ssl' +# c.JupyterHub.internal_certs_location = 'internal-ssl' + +## Enable SSL for all internal communication +# +# This enables end-to-end encryption between all JupyterHub components. +# JupyterHub will automatically create the necessary certificate +# authority and sign notebook certificates as they're created. +# Default: False +# c.JupyterHub.internal_ssl = False + +## The public facing ip of the whole JupyterHub application +# (specifically referred to as the proxy). +# +# This is the address on which the proxy will listen. The default is to +# listen on all interfaces. This is the only address through which JupyterHub +# should be accessed by users. +# +# .. deprecated: 0.9 +# Use JupyterHub.bind_url +# Default: '' +# c.JupyterHub.ip = '' + +## Supply extra arguments that will be passed to Jinja environment. +# Default: {} +# c.JupyterHub.jinja_environment_options = {} + +## Interval (in seconds) at which to update last-activity timestamps. +# Default: 300 +# c.JupyterHub.last_activity_interval = 300 + +## Dict of `{'group': {'users':['usernames'], 'properties': {}}` to load at +# startup. +# +# Example:: +# +# c.JupyterHub.load_groups = { +# 'groupname': { +# 'users': ['usernames'], +# 'properties': {'key': 'value'}, +# }, +# } +# +# This strictly *adds* groups and users to groups. Properties, if defined, +# replace all existing properties. +# +# Loading one set of groups, then starting JupyterHub again with a different set +# will not remove users or groups from previous launches. That must be done +# through the API. +# +# .. versionchanged:: 3.2 +# Changed format of group from list of usernames to dict +# Default: {} +# c.JupyterHub.load_groups = {} + +## List of predefined role dictionaries to load at startup. +# +# For instance:: +# +# load_roles = [ +# { +# 'name': 'teacher', +# 'description': 'Access to users' information and group membership', +# 'scopes': ['users', 'groups'], +# 'users': ['cyclops', 'gandalf'], +# 'services': [], +# 'groups': [] +# } +# ] +# +# All keys apart from 'name' are optional. +# See all the available scopes in the JupyterHub REST API documentation. +# +# Default roles are defined in roles.py. +# Default: [] +# c.JupyterHub.load_roles = [] + +## The date format used by logging formatters for %(asctime)s +# See also: Application.log_datefmt +# c.JupyterHub.log_datefmt = '%Y-%m-%d %H:%M:%S' + +## The Logging format template +# See also: Application.log_format +# c.JupyterHub.log_format = '[%(name)s]%(highlevel)s %(message)s' + +## Set the log level by value or name. +# See also: Application.log_level +# c.JupyterHub.log_level = 30 + +## Specify path to a logo image to override the Jupyter logo in the banner. +# Default: '' +# c.JupyterHub.logo_file = '' + +## Maximum number of concurrent named servers that can be created by a user at a +# time. +# +# Setting this can limit the total resources a user can consume. +# +# If set to 0, no limit is enforced. +# +# Can be an integer or a callable/awaitable based on the handler object: +# +# :: +# +# def named_server_limit_per_user_fn(handler): +# user = handler.current_user +# if user and user.admin: +# return 0 +# return 5 +# +# c.JupyterHub.named_server_limit_per_user = named_server_limit_per_user_fn +# Default: 0 +# c.JupyterHub.named_server_limit_per_user = 0 + +## Expiry (in seconds) of OAuth access tokens. +# +# The default is to expire when the cookie storing them expires, +# according to `cookie_max_age_days` config. +# +# These are the tokens stored in cookies when you visit +# a single-user server or service. +# When they expire, you must re-authenticate with the Hub, +# even if your Hub authentication is still valid. +# If your Hub authentication is valid, +# logging in may be a transparent redirect as you refresh the page. +# +# This does not affect JupyterHub API tokens in general, +# which do not expire by default. +# Only tokens issued during the oauth flow +# accessing services and single-user servers are affected. +# +# .. versionadded:: 1.4 +# OAuth token expires_in was not previously configurable. +# .. versionchanged:: 1.4 +# Default now uses cookie_max_age_days so that oauth tokens +# which are generally stored in cookies, +# expire when the cookies storing them expire. +# Previously, it was one hour. +# Default: 0 +# c.JupyterHub.oauth_token_expires_in = 0 + +## File to write PID +# Useful for daemonizing JupyterHub. +# Default: '' +# c.JupyterHub.pid_file = '' + +## The public facing port of the proxy. +# +# This is the port on which the proxy will listen. +# This is the only port through which JupyterHub +# should be accessed by users. +# +# .. deprecated: 0.9 +# Use JupyterHub.bind_url +# Default: 8000 +# c.JupyterHub.port = 8000 + +## DEPRECATED since version 0.8 : Use ConfigurableHTTPProxy.api_url +# Default: '' +# c.JupyterHub.proxy_api_ip = '' + +## DEPRECATED since version 0.8 : Use ConfigurableHTTPProxy.api_url +# Default: 0 +# c.JupyterHub.proxy_api_port = 0 + +## DEPRECATED since version 0.8: Use ConfigurableHTTPProxy.auth_token +# Default: '' +# c.JupyterHub.proxy_auth_token = '' + +## DEPRECATED since version 0.8: Use ConfigurableHTTPProxy.check_running_interval +# Default: 5 +# c.JupyterHub.proxy_check_interval = 5 + +## The class to use for configuring the JupyterHub proxy. +# +# Should be a subclass of :class:`jupyterhub.proxy.Proxy`. +# +# .. versionchanged:: 1.0 +# proxies may be registered via entry points, +# e.g. `c.JupyterHub.proxy_class = 'traefik'` +# +# Currently installed: +# - configurable-http-proxy: jupyterhub.proxy.ConfigurableHTTPProxy +# - default: jupyterhub.proxy.ConfigurableHTTPProxy +# Default: 'jupyterhub.proxy.ConfigurableHTTPProxy' +# c.JupyterHub.proxy_class = 'jupyterhub.proxy.ConfigurableHTTPProxy' + +## DEPRECATED since version 0.8. Use ConfigurableHTTPProxy.command +# Default: [] +# c.JupyterHub.proxy_cmd = [] + +## Recreate all certificates used within JupyterHub on restart. +# +# Note: enabling this feature requires restarting all notebook servers. +# +# Use with internal_ssl +# Default: False +# c.JupyterHub.recreate_internal_certs = False + +## Redirect user to server (if running), instead of control panel. +# Default: True +# c.JupyterHub.redirect_to_server = True + +## Purge and reset the database. +# Default: False +# c.JupyterHub.reset_db = False + +## Interval (in seconds) at which to check connectivity of services with web +# endpoints. +# Default: 60 +# c.JupyterHub.service_check_interval = 60 + +## Dict of token:servicename to be loaded into the database. +# +# Allows ahead-of-time generation of API tokens for use by externally +# managed services. +# Default: {} +# c.JupyterHub.service_tokens = {} + +## List of service specification dictionaries. +# +# A service +# +# For instance:: +# +# services = [ +# { +# 'name': 'cull_idle', +# 'command': ['/path/to/cull_idle_servers.py'], +# }, +# { +# 'name': 'formgrader', +# 'url': 'http://127.0.0.1:1234', +# 'api_token': 'super-secret', +# 'environment': +# } +# ] +# Default: [] +# c.JupyterHub.services = [] + +## Instead of starting the Application, dump configuration to stdout +# See also: Application.show_config +# c.JupyterHub.show_config = False + +## Instead of starting the Application, dump configuration to stdout (as JSON) +# See also: Application.show_config_json +# c.JupyterHub.show_config_json = False + +## Shuts down all user servers on logout +# Default: False +# c.JupyterHub.shutdown_on_logout = False + +## The class to use for spawning single-user servers. +# +# Should be a subclass of :class:`jupyterhub.spawner.Spawner`. +# +# .. versionchanged:: 1.0 +# spawners may be registered via entry points, +# e.g. `c.JupyterHub.spawner_class = 'localprocess'` +# +# Currently installed: +# - default: jupyterhub.spawner.LocalProcessSpawner +# - localprocess: jupyterhub.spawner.LocalProcessSpawner +# - simple: jupyterhub.spawner.SimpleLocalProcessSpawner +# Default: 'jupyterhub.spawner.LocalProcessSpawner' +# c.JupyterHub.spawner_class = 'jupyterhub.spawner.LocalProcessSpawner' +# c.JupyterHub.spawner_class = os.getenv("JUPYTER_HUB_SPAWNER", "jupyterhub.spawner.LocalProcessSpawner") + +## Path to SSL certificate file for the public facing interface of the proxy +# +# When setting this, you should also set ssl_key +# Default: '' +# c.JupyterHub.ssl_cert = '' + +## Path to SSL key file for the public facing interface of the proxy +# +# When setting this, you should also set ssl_cert +# Default: '' +# c.JupyterHub.ssl_key = '' + +## Host to send statsd metrics to. An empty string (the default) disables sending +# metrics. +# Default: '' +# c.JupyterHub.statsd_host = '' + +## Port on which to send statsd metrics about the hub +# Default: 8125 +# c.JupyterHub.statsd_port = 8125 + +## Prefix to use for all metrics sent by jupyterhub to statsd +# Default: 'jupyterhub' +# c.JupyterHub.statsd_prefix = 'jupyterhub' + +## Run single-user servers on subdomains of this host. +# +# This should be the full `https://hub.domain.tld[:port]`. +# +# Provides additional cross-site protections for javascript served by +# single-user servers. +# +# Requires `.hub.domain.tld` to resolve to the same host as +# `hub.domain.tld`. +# +# In general, this is most easily achieved with wildcard DNS. +# +# When using SSL (i.e. always) this also requires a wildcard SSL +# certificate. +# Default: '' +# c.JupyterHub.subdomain_host = '' + +## Paths to search for jinja templates, before using the default templates. +# Default: [] +# c.JupyterHub.template_paths = [] + +## Extra variables to be passed into jinja templates +# Default: {} +# c.JupyterHub.template_vars = {} + +## Extra settings overrides to pass to the tornado application. +# Default: {} +# c.JupyterHub.tornado_settings = {} + +## Trust user-provided tokens (via JupyterHub.service_tokens) +# to have good entropy. +# +# If you are not inserting additional tokens via configuration file, +# this flag has no effect. +# +# In JupyterHub 0.8, internally generated tokens do not +# pass through additional hashing because the hashing is costly +# and does not increase the entropy of already-good UUIDs. +# +# User-provided tokens, on the other hand, are not trusted to have good entropy by default, +# and are passed through many rounds of hashing to stretch the entropy of the key +# (i.e. user-provided tokens are treated as passwords instead of random keys). +# These keys are more costly to check. +# +# If your inserted tokens are generated by a good-quality mechanism, +# e.g. `openssl rand -hex 32`, then you can set this flag to True +# to reduce the cost of checking authentication tokens. +# Default: False +# c.JupyterHub.trust_user_provided_tokens = False + +## Names to include in the subject alternative name. +# +# These names will be used for server name verification. This is useful +# if JupyterHub is being run behind a reverse proxy or services using ssl +# are on different hosts. +# +# Use with internal_ssl +# Default: [] +# c.JupyterHub.trusted_alt_names = [] + +## Downstream proxy IP addresses to trust. +# +# This sets the list of IP addresses that are trusted and skipped when processing +# the `X-Forwarded-For` header. For example, if an external proxy is used for TLS +# termination, its IP address should be added to this list to ensure the correct +# client IP addresses are recorded in the logs instead of the proxy server's IP +# address. +# Default: [] +# c.JupyterHub.trusted_downstream_ips = [] + +## Upgrade the database automatically on start. +# +# Only safe if database is regularly backed up. +# Only SQLite databases will be backed up to a local file automatically. +# Default: False +# c.JupyterHub.upgrade_db = False + +## Return 503 rather than 424 when request comes in for a non-running server. +# +# Prior to JupyterHub 2.0, we returned a 503 when any request came in for a user +# server that was currently not running. By default, JupyterHub 2.0 will return +# a 424 - this makes operational metric dashboards more useful. +# +# JupyterLab < 3.2 expected the 503 to know if the user server is no longer +# running, and prompted the user to start their server. Set this config to true +# to retain the old behavior, so JupyterLab < 3.2 can continue to show the +# appropriate UI when the user server is stopped. +# +# This option will be removed in a future release. +# Default: False +# c.JupyterHub.use_legacy_stopped_server_status_code = False + +## Callable to affect behavior of /user-redirect/ +# +# Receives 4 parameters: 1. path - URL path that was provided after /user- +# redirect/ 2. request - A Tornado HTTPServerRequest representing the current +# request. 3. user - The currently authenticated user. 4. base_url - The +# base_url of the current hub, for relative redirects +# +# It should return the new URL to redirect to, or None to preserve current +# behavior. +# Default: None +# c.JupyterHub.user_redirect_hook = None + +#------------------------------------------------------------------------------ +# Spawner(LoggingConfigurable) configuration +#------------------------------------------------------------------------------ +## Base class for spawning single-user notebook servers. +# +# Subclass this, and override the following methods: +# +# - load_state +# - get_state +# - start +# - stop +# - poll +# +# As JupyterHub supports multiple users, an instance of the Spawner subclass +# is created for each user. If there are 20 JupyterHub users, there will be 20 +# instances of the subclass. + +## Extra arguments to be passed to the single-user server. +# +# Some spawners allow shell-style expansion here, allowing you to use +# environment variables here. Most, including the default, do not. Consult the +# documentation for your spawner to verify! +# Default: [] +# c.Spawner.args = [] + +## An optional hook function that you can implement to pass `auth_state` to the +# spawner after it has been initialized but before it starts. The `auth_state` +# dictionary may be set by the `.authenticate()` method of the authenticator. +# This hook enables you to pass some or all of that information to your spawner. +# +# Example:: +# +# def userdata_hook(spawner, auth_state): +# spawner.userdata = auth_state["userdata"] +# +# c.Spawner.auth_state_hook = userdata_hook +# Default: None +# c.Spawner.auth_state_hook = None + +## The command used for starting the single-user server. +# +# Provide either a string or a list containing the path to the startup script +# command. Extra arguments, other than this path, should be provided via `args`. +# +# This is usually set if you want to start the single-user server in a different +# python environment (with virtualenv/conda) than JupyterHub itself. +# +# Some spawners allow shell-style expansion here, allowing you to use +# environment variables. Most, including the default, do not. Consult the +# documentation for your spawner to verify! +# Default: ['jupyterhub-singleuser'] +# c.Spawner.cmd = ['jupyterhub-singleuser'] + +## Maximum number of consecutive failures to allow before shutting down +# JupyterHub. +# +# This helps JupyterHub recover from a certain class of problem preventing +# launch in contexts where the Hub is automatically restarted (e.g. systemd, +# docker, kubernetes). +# +# A limit of 0 means no limit and consecutive failures will not be tracked. +# Default: 0 +# c.Spawner.consecutive_failure_limit = 0 + +## Minimum number of cpu-cores a single-user notebook server is guaranteed to +# have available. +# +# If this value is set to 0.5, allows use of 50% of one CPU. If this value is +# set to 2, allows use of up to 2 CPUs. +# +# **This is a configuration setting. Your spawner must implement support for the +# limit to work.** The default spawner, `LocalProcessSpawner`, does **not** +# implement this support. A custom spawner **must** add support for this setting +# for it to be enforced. +# Default: None +# c.Spawner.cpu_guarantee = None + +## Maximum number of cpu-cores a single-user notebook server is allowed to use. +# +# If this value is set to 0.5, allows use of 50% of one CPU. If this value is +# set to 2, allows use of up to 2 CPUs. +# +# The single-user notebook server will never be scheduled by the kernel to use +# more cpu-cores than this. There is no guarantee that it can access this many +# cpu-cores. +# +# **This is a configuration setting. Your spawner must implement support for the +# limit to work.** The default spawner, `LocalProcessSpawner`, does **not** +# implement this support. A custom spawner **must** add support for this setting +# for it to be enforced. +# Default: None +# c.Spawner.cpu_limit = None + +## Enable debug-logging of the single-user server +# Default: False +# c.Spawner.debug = False + +## The URL the single-user server should start in. +# +# `{username}` will be expanded to the user's username +# +# Example uses: +# +# - You can set `notebook_dir` to `/` and `default_url` to `/tree/home/{username}` to allow people to +# navigate the whole filesystem from their notebook server, but still start in their home directory. +# - Start with `/notebooks` instead of `/tree` if `default_url` points to a notebook instead of a directory. +# - You can set this to `/lab` to have JupyterLab start by default, rather than Jupyter Notebook. +# Default: '' +# c.Spawner.default_url = '' +# c.Spawner.default_url = os.getenv("JUPYTER_HUB_SPAWNER_DEFAULT_URL", "/lab") + +## Disable per-user configuration of single-user servers. +# +# When starting the user's single-user server, any config file found in the +# user's $HOME directory will be ignored. +# +# Note: a user could circumvent this if the user modifies their Python +# environment, such as when they have their own conda environments / virtualenvs +# / containers. +# Default: False +# c.Spawner.disable_user_config = False + +## List of environment variables for the single-user server to inherit from the +# JupyterHub process. +# +# This list is used to ensure that sensitive information in the JupyterHub +# process's environment (such as `CONFIGPROXY_AUTH_TOKEN`) is not passed to the +# single-user server's process. +# Default: ['PATH', 'PYTHONPATH', 'CONDA_ROOT', 'CONDA_DEFAULT_ENV', 'VIRTUAL_ENV', 'LANG', 'LC_ALL', 'JUPYTERHUB_SINGLEUSER_APP'] +# c.Spawner.env_keep = ['PATH', 'PYTHONPATH', 'CONDA_ROOT', 'CONDA_DEFAULT_ENV', 'VIRTUAL_ENV', 'LANG', 'LC_ALL', 'JUPYTERHUB_SINGLEUSER_APP'] + +## Extra environment variables to set for the single-user server's process. +# +# Environment variables that end up in the single-user server's process come from 3 sources: +# - This `environment` configurable +# - The JupyterHub process' environment variables that are listed in `env_keep` +# - Variables to establish contact between the single-user notebook and the hub (such as JUPYTERHUB_API_TOKEN) +# +# The `environment` configurable should be set by JupyterHub administrators to +# add installation specific environment variables. It is a dict where the key is +# the name of the environment variable, and the value can be a string or a +# callable. If it is a callable, it will be called with one parameter (the +# spawner instance), and should return a string fairly quickly (no blocking +# operations please!). +# +# Note that the spawner class' interface is not guaranteed to be exactly same +# across upgrades, so if you are using the callable take care to verify it +# continues to work after upgrades! +# +# .. versionchanged:: 1.2 +# environment from this configuration has highest priority, +# allowing override of 'default' env variables, +# such as JUPYTERHUB_API_URL. +# Default: {} +# c.Spawner.environment = {} + +## Timeout (in seconds) before giving up on a spawned HTTP server +# +# Once a server has successfully been spawned, this is the amount of time we +# wait before assuming that the server is unable to accept connections. +# Default: 30 +# c.Spawner.http_timeout = 30 + +## The URL the single-user server should connect to the Hub. +# +# If the Hub URL set in your JupyterHub config is not reachable from spawned +# notebooks, you can set differnt URL by this config. +# +# Is None if you don't need to change the URL. +# Default: None +# c.Spawner.hub_connect_url = None + +## The IP address (or hostname) the single-user server should listen on. +# +# Usually either '127.0.0.1' (default) or '0.0.0.0'. +# +# The JupyterHub proxy implementation should be able to send packets to this +# interface. +# +# Subclasses which launch remotely or in containers should override the default +# to '0.0.0.0'. +# +# .. versionchanged:: 2.0 +# Default changed to '127.0.0.1', from ''. +# In most cases, this does not result in a change in behavior, +# as '' was interpreted as 'unspecified', +# which used the subprocesses' own default, itself usually '127.0.0.1'. +# Default: '127.0.0.1' +# c.Spawner.ip = '127.0.0.1' +# c.Spawner.ip = os.getenv("JUPYTER_HUB_SPAWNER_IP", "127.0.0.1") + +## Minimum number of bytes a single-user notebook server is guaranteed to have +# available. +# +# Allows the following suffixes: +# - K -> Kilobytes +# - M -> Megabytes +# - G -> Gigabytes +# - T -> Terabytes +# +# **This is a configuration setting. Your spawner must implement support for the +# limit to work.** The default spawner, `LocalProcessSpawner`, does **not** +# implement this support. A custom spawner **must** add support for this setting +# for it to be enforced. +# Default: None +# c.Spawner.mem_guarantee = None + +## Maximum number of bytes a single-user notebook server is allowed to use. +# +# Allows the following suffixes: +# - K -> Kilobytes +# - M -> Megabytes +# - G -> Gigabytes +# - T -> Terabytes +# +# If the single user server tries to allocate more memory than this, it will +# fail. There is no guarantee that the single-user notebook server will be able +# to allocate this much memory - only that it can not allocate more than this. +# +# **This is a configuration setting. Your spawner must implement support for the +# limit to work.** The default spawner, `LocalProcessSpawner`, does **not** +# implement this support. A custom spawner **must** add support for this setting +# for it to be enforced. +# Default: None +# c.Spawner.mem_limit = None + +## Path to the notebook directory for the single-user server. +# +# The user sees a file listing of this directory when the notebook interface is +# started. The current interface does not easily allow browsing beyond the +# subdirectories in this directory's tree. +# +# `~` will be expanded to the home directory of the user, and {username} will be +# replaced with the name of the user. +# +# Note that this does *not* prevent users from accessing files outside of this +# path! They can do so with many other means. +# Default: '' +# c.Spawner.notebook_dir = '' + +## Allowed scopes for oauth tokens issued by this server's oauth client. +# +# This sets the maximum and default scopes +# assigned to oauth tokens issued by a single-user server's +# oauth client (i.e. tokens stored in browsers after authenticating with the server), +# defining what actions the server can take on behalf of logged-in users. +# +# Default is an empty list, meaning minimal permissions to identify users, +# no actions can be taken on their behalf. +# +# If callable, will be called with the Spawner as a single argument. +# Callables may be async. +# Default: traitlets.Undefined +# c.Spawner.oauth_client_allowed_scopes = traitlets.Undefined + +## Allowed roles for oauth tokens. +# +# Deprecated in 3.0: use oauth_client_allowed_scopes +# +# This sets the maximum and default roles +# assigned to oauth tokens issued by a single-user server's +# oauth client (i.e. tokens stored in browsers after authenticating with the server), +# defining what actions the server can take on behalf of logged-in users. +# +# Default is an empty list, meaning minimal permissions to identify users, +# no actions can be taken on their behalf. +# Default: traitlets.Undefined +# c.Spawner.oauth_roles = traitlets.Undefined + +## An HTML form for options a user can specify on launching their server. +# +# The surrounding `
` element and the submit button are already provided. +# +# For example: +# +# .. code:: html +# +# Set your key: +# +#
+# Choose a letter: +# +# +# The data from this form submission will be passed on to your spawner in +# `self.user_options` +# +# Instead of a form snippet string, this could also be a callable that takes as +# one parameter the current spawner instance and returns a string. The callable +# will be called asynchronously if it returns a future, rather than a str. Note +# that the interface of the spawner class is not deemed stable across versions, +# so using this functionality might cause your JupyterHub upgrades to break. +# Default: traitlets.Undefined +# c.Spawner.options_form = traitlets.Undefined + +## Interpret HTTP form data +# +# Form data will always arrive as a dict of lists of strings. Override this +# function to understand single-values, numbers, etc. +# +# This should coerce form data into the structure expected by self.user_options, +# which must be a dict, and should be JSON-serializeable, though it can contain +# bytes in addition to standard JSON data types. +# +# This method should not have any side effects. Any handling of `user_options` +# should be done in `.start()` to ensure consistent behavior across servers +# spawned via the API and form submission page. +# +# Instances will receive this data on self.user_options, after passing through +# this function, prior to `Spawner.start`. +# +# .. versionchanged:: 1.0 +# user_options are persisted in the JupyterHub database to be reused +# on subsequent spawns if no options are given. +# user_options is serialized to JSON as part of this persistence +# (with additional support for bytes in case of uploaded file data), +# and any non-bytes non-jsonable values will be replaced with None +# if the user_options are re-used. +# Default: traitlets.Undefined +# c.Spawner.options_from_form = traitlets.Undefined + +## Interval (in seconds) on which to poll the spawner for single-user server's +# status. +# +# At every poll interval, each spawner's `.poll` method is called, which checks +# if the single-user server is still running. If it isn't running, then +# JupyterHub modifies its own state accordingly and removes appropriate routes +# from the configurable proxy. +# Default: 30 +# c.Spawner.poll_interval = 30 + +## The port for single-user servers to listen on. +# +# Defaults to `0`, which uses a randomly allocated port number each time. +# +# If set to a non-zero value, all Spawners will use the same port, which only +# makes sense if each server is on a different address, e.g. in containers. +# +# New in version 0.7. +# Default: 0 +# c.Spawner.port = 0 + +## An optional hook function that you can implement to do work after the spawner +# stops. +# +# This can be set independent of any concrete spawner implementation. +# Default: None +# c.Spawner.post_stop_hook = None + +## An optional hook function that you can implement to do some bootstrapping work +# before the spawner starts. For example, create a directory for your user or +# load initial content. +# +# This can be set independent of any concrete spawner implementation. +# +# This maybe a coroutine. +# +# Example:: +# +# from subprocess import check_call +# def my_hook(spawner): +# username = spawner.user.name +# check_call(['./examples/bootstrap-script/bootstrap.sh', username]) +# +# c.Spawner.pre_spawn_hook = my_hook +# Default: None +# c.Spawner.pre_spawn_hook = None + +## The list of scopes to request for $JUPYTERHUB_API_TOKEN +# +# If not specified, the scopes in the `server` role will be used +# (unchanged from pre-4.0). +# +# If callable, will be called with the Spawner instance as its sole argument +# (JupyterHub user available as spawner.user). +# +# JUPYTERHUB_API_TOKEN will be assigned the _subset_ of these scopes +# that are held by the user (as in oauth_client_allowed_scopes). +# +# .. versionadded:: 4.0 +# Default: traitlets.Undefined +# c.Spawner.server_token_scopes = traitlets.Undefined + +## List of SSL alt names +# +# May be set in config if all spawners should have the same value(s), +# or set at runtime by Spawner that know their names. +# Default: [] +# c.Spawner.ssl_alt_names = [] + +## Whether to include `DNS:localhost`, `IP:127.0.0.1` in alt names +# Default: True +# c.Spawner.ssl_alt_names_include_local = True + +## Timeout (in seconds) before giving up on starting of single-user server. +# +# This is the timeout for start to return, not the timeout for the server to +# respond. Callers of spawner.start will assume that startup has failed if it +# takes longer than this. start should return when the server process is started +# and its location is known. +# Default: 60 +# c.Spawner.start_timeout = 60 + +#------------------------------------------------------------------------------ +# Authenticator(LoggingConfigurable) configuration +#------------------------------------------------------------------------------ +## Base class for implementing an authentication provider for JupyterHub + +## Set of users that will have admin rights on this JupyterHub. +# +# Note: As of JupyterHub 2.0, full admin rights should not be required, and more +# precise permissions can be managed via roles. +# +# Admin users have extra privileges: +# - Use the admin panel to see list of users logged in +# - Add / remove users in some authenticators +# - Restart / halt the hub +# - Start / stop users' single-user servers +# - Can access each individual users' single-user server (if configured) +# +# Admin access should be treated the same way root access is. +# +# Defaults to an empty set, in which case no user has admin access. +# Default: set() +# c.Authenticator.admin_users = set() + +## Set of usernames that are allowed to log in. +# +# Use this with supported authenticators to restrict which users can log in. +# This is an additional list that further restricts users, beyond whatever +# restrictions the authenticator has in place. Any user in this list is granted +# the 'user' role on hub startup. +# +# If empty, does not perform any additional restriction. +# +# .. versionchanged:: 1.2 +# `Authenticator.whitelist` renamed to `allowed_users` +# Default: set() +# c.Authenticator.allowed_users = set() + +## The max age (in seconds) of authentication info +# before forcing a refresh of user auth info. +# +# Refreshing auth info allows, e.g. requesting/re-validating auth +# tokens. +# +# See :meth:`.refresh_user` for what happens when user auth info is refreshed +# (nothing by default). +# Default: 300 +# c.Authenticator.auth_refresh_age = 300 + +## Automatically begin the login process +# +# rather than starting with a "Login with..." link at `/hub/login` +# +# To work, `.login_url()` must give a URL other than the default `/hub/login`, +# such as an oauth handler or another automatic login handler, +# registered with `.get_handlers()`. +# +# .. versionadded:: 0.8 +# Default: False +# c.Authenticator.auto_login = False + +## Automatically begin login process for OAuth2 authorization requests +# +# When another application is using JupyterHub as OAuth2 provider, it sends +# users to `/hub/api/oauth2/authorize`. If the user isn't logged in already, and +# auto_login is not set, the user will be dumped on the hub's home page, without +# any context on what to do next. +# +# Setting this to true will automatically redirect users to login if they aren't +# logged in *only* on the `/hub/api/oauth2/authorize` endpoint. +# +# .. versionadded:: 1.5 +# Default: False +# c.Authenticator.auto_login_oauth2_authorize = False + +## Set of usernames that are not allowed to log in. +# +# Use this with supported authenticators to restrict which users can not log in. +# This is an additional block list that further restricts users, beyond whatever +# restrictions the authenticator has in place. +# +# If empty, does not perform any additional restriction. +# +# .. versionadded: 0.9 +# +# .. versionchanged:: 1.2 +# `Authenticator.blacklist` renamed to `blocked_users` +# Default: set() +# c.Authenticator.blocked_users = set() + +## Delete any users from the database that do not pass validation +# +# When JupyterHub starts, `.add_user` will be called +# on each user in the database to verify that all users are still valid. +# +# If `delete_invalid_users` is True, +# any users that do not pass validation will be deleted from the database. +# Use this if users might be deleted from an external system, +# such as local user accounts. +# +# If False (default), invalid users remain in the Hub's database +# and a warning will be issued. +# This is the default to avoid data loss due to config changes. +# Default: False +# c.Authenticator.delete_invalid_users = False + +## Enable persisting auth_state (if available). +# +# auth_state will be encrypted and stored in the Hub's database. +# This can include things like authentication tokens, etc. +# to be passed to Spawners as environment variables. +# +# Encrypting auth_state requires the cryptography package. +# +# Additionally, the JUPYTERHUB_CRYPT_KEY environment variable must +# contain one (or more, separated by ;) 32B encryption keys. +# These can be either base64 or hex-encoded. +# +# If encryption is unavailable, auth_state cannot be persisted. +# +# New in JupyterHub 0.8 +# Default: False +# c.Authenticator.enable_auth_state = False + +## Let authenticator manage user groups +# +# If True, Authenticator.authenticate and/or .refresh_user +# may return a list of group names in the 'groups' field, +# which will be assigned to the user. +# +# All group-assignment APIs are disabled if this is True. +# Default: False +# c.Authenticator.manage_groups = False + +## An optional hook function that you can implement to do some bootstrapping work +# during authentication. For example, loading user account details from an +# external system. +# +# This function is called after the user has passed all authentication checks +# and is ready to successfully authenticate. This function must return the +# authentication dict reguardless of changes to it. +# +# This maybe a coroutine. +# +# .. versionadded: 1.0 +# +# Example:: +# +# import os, pwd +# def my_hook(authenticator, handler, authentication): +# user_data = pwd.getpwnam(authentication['name']) +# spawn_data = { +# 'pw_data': user_data +# 'gid_list': os.getgrouplist(authentication['name'], user_data.pw_gid) +# } +# +# if authentication['auth_state'] is None: +# authentication['auth_state'] = {} +# authentication['auth_state']['spawn_data'] = spawn_data +# +# return authentication +# +# c.Authenticator.post_auth_hook = my_hook +# Default: None +# c.Authenticator.post_auth_hook = None + +## Force refresh of auth prior to spawn. +# +# This forces :meth:`.refresh_user` to be called prior to launching +# a server, to ensure that auth state is up-to-date. +# +# This can be important when e.g. auth tokens that may have expired +# are passed to the spawner via environment variables from auth_state. +# +# If refresh_user cannot refresh the user auth data, +# launch will fail until the user logs in again. +# Default: False +# c.Authenticator.refresh_pre_spawn = False + +## Dictionary mapping authenticator usernames to JupyterHub users. +# +# Primarily used to normalize OAuth user names to local users. +# Default: {} +# c.Authenticator.username_map = {} + +## Regular expression pattern that all valid usernames must match. +# +# If a username does not match the pattern specified here, authentication will +# not be attempted. +# +# If not set, allow any username. +# Default: '' +# c.Authenticator.username_pattern = '' + +## Deprecated, use `Authenticator.allowed_users` +# Default: set() +# c.Authenticator.whitelist = set() + +#------------------------------------------------------------------------------ +# CryptKeeper(SingletonConfigurable) configuration +#------------------------------------------------------------------------------ +## Encapsulate encryption configuration +# +# Use via the encryption_config singleton below. + +# Default: [] +# c.CryptKeeper.keys = [] + +## The number of threads to allocate for encryption +# Default: 16 +# c.CryptKeeper.n_threads = 16 diff --git a/dist/alpine/Dockerfile b/dist/alpine/Dockerfile index 5249427..399ed6c 100644 --- a/dist/alpine/Dockerfile +++ b/dist/alpine/Dockerfile @@ -48,7 +48,7 @@ RUN \ \# Source shell intercept\r~g' | tr -d '\n' | tr -d '\t')" \ -e "$(echo 's~\t# Install Conda dependencies\r~\ \t# Post start hook\r\ - \t[ -z \"$Djlabhub_NotebookRepo_Target\" ] || \ + \t[ -z \"$DJLABHUB_REPO_TARGET\" ] || \ run_post_start_jobs\r\ \t# Install Conda dependencies\r~g' | \ tr -d '\n' | tr -d '\t')" | \ diff --git a/dist/alpine/docker-compose.yaml b/dist/alpine/docker-compose.yaml index 182f0ed..93f4889 100644 --- a/dist/alpine/docker-compose.yaml +++ b/dist/alpine/docker-compose.yaml @@ -1,7 +1,8 @@ version: '3.7' services: app: - build: # Uncomment to enable local build + build: + # Uncomment to enable local build context: ../.. dockerfile: ./dist/alpine/Dockerfile args: @@ -10,25 +11,25 @@ services: - JUPYTERHUB_VER image: datajoint/djlabhub:${JUPYTERHUB_VER}-py${PY_VER}-alpine environment: - - DISPLAY # Necessary to allow GUI to route to Docker host - - NEW_USER=dja # Rename user and adjust HOME directory + - DISPLAY # Necessary to allow GUI to route to Docker host + - NEW_USER=dja # Rename user and adjust HOME directory # - Djlab_JupyterServer_Password=datajoint # Jupyter login password # - Djlab_JupyterServer_DisplayFilepath=/home/README.md # Display on login # - Djlab_JupyterServer_SaveOutput=FALSE # Set if notebook save includes output - - Djlabhub_ServerName=general # Specify the server reference name - - Djlabhub_NotebookRepo_Target=https://github.com/guzman-raphael/playground_tutorial # GitHub repo for reference notebooks - # - Djlabhub_NotebookRepo_Subpath=. # Relative subpath within GitHub repo to be included - - DJ_HOST=tutorial-db.datajoint.io # Specify DataJoint database host - - DJ_USER=example_user # Specify DataJoint database user - - DJ_PASS=example_password # Specify DataJoint database password - - FILEPATH_FEATURE_SWITCH=TRUE # Enable experimental datatype: filepath - - ADAPTED_TYPE_SWITCH=TRUE # Enable experimental datatype: adapted types + - DJLABHUB_SERVERNAME=general # Specify the server reference name + - DJLABHUB_REPO_TARGET=https://github.com/guzman-raphael/playground_tutorial # GitHub repo for reference notebooks + # - DJLABHUB_REPO_TARGET_SUBPATH=. # Relative subpath within GitHub repo to be included + - DJ_HOST=tutorial-db.datajoint.io # Specify DataJoint database host + - DJ_USER=example_user # Specify DataJoint database user + - DJ_PASS=example_password # Specify DataJoint database password + - FILEPATH_FEATURE_SWITCH=TRUE # Enable experimental datatype: filepath + - ADAPTED_TYPE_SWITCH=TRUE # Enable experimental datatype: adapted types ports: - 8888:8888 command: jupyter lab - user: ${HOST_UID}:anaconda # Necessary to allow GUI to route to Docker host + user: ${HOST_UID}:anaconda # Necessary to allow GUI to route to Docker host volumes: - - /tmp/.X11-unix:/tmp/.X11-unix:rw # Necessary to allow GUI to route to Docker host + - /tmp/.X11-unix:/tmp/.X11-unix:rw # Necessary to allow GUI to route to Docker host # - ../../config/datajoint_config.json:/usr/local/bin/.datajoint_config.json # Override default datajoint config for environment # - ../../common:/home/common # Mount point for common directory between all of user's environments # - ../../home_user:/home/.anaconda # Mount point for user's HOME directory diff --git a/dist/debian/Dockerfile b/dist/debian/Dockerfile index 27a076b..7d14740 100644 --- a/dist/debian/Dockerfile +++ b/dist/debian/Dockerfile @@ -14,14 +14,14 @@ RUN \ chmod g+w /home/common USER anaconda:anaconda # Install jupyterhub client, included utilities, and hide static HOME directory -COPY --chown=anaconda:anaconda ./dist/debian/apt_requirements.txt \ - ./config/pip_requirements.txt /tmp/ +COPY --chown=anaconda:anaconda \ + ./dist/debian/apt_requirements.txt \ + ./config/pip_requirements.txt \ + /tmp/ COPY ./utilities/dj_config_backup.sh /usr/local/bin/dj_config_backup.sh RUN \ umask u+rwx,g+rwx,o-rwx && \ sed -ie "s|{{JUPYTERHUB_VER}}|${JUPYTERHUB_VER}|g" "$PIP_REQUIREMENTS" && \ - # conda install -yc conda-forge python=$PY_VER jupyterhub=$JUPYTERHUB_VER && \ - # conda install -yc conda-forge python=$PY_VER nodejs=10.12.0 && \ export NEW_HOME=/home/.anaconda && \ /entrypoint.sh echo "Requirements updated..." && \ rm "$APT_REQUIREMENTS" && \ @@ -33,7 +33,7 @@ RUN \ ENV HOME /home/.anaconda COPY --chown=anaconda:anaconda ./config/README.md /home/ COPY ./config/datajoint_config.json /usr/local/bin/.datajoint_config.json -COPY --chown=anaconda:anaconda ./config/djlab_config.yaml $DJLAB_CONFIG +# COPY ./config/jupyterhub_config.py /etc/jupyterhub/jupyterhub_config.py COPY ./utilities/add_entrypoint.sh /tmp/add_entrypoint.sh # Add post start hook and copy over HOME template files on first boot (if mounted) USER root:anaconda @@ -48,7 +48,7 @@ RUN \ \# Source shell intercept\r~g' | tr -d '\n' | tr -d '\t')" \ -e "$(echo 's~\t# Install Conda dependencies\r~\ \t# Post start hook\r\ - \t[ -z \"$Djlabhub_NotebookRepo_Target\" ] || \ + \t[ -z \"$DJLABHUB_REPO_TARGET\" ] || \ run_post_start_jobs\r\ \t# Install Conda dependencies\r~g' | \ tr -d '\n' | tr -d '\t')" | \ @@ -71,7 +71,6 @@ ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 ENV APT_REQUIREMENTS /tmp/apt_requirements.txt ENV PIP_REQUIREMENTS /tmp/pip_requirements.txt ENV CONDA_REQUIREMENTS /tmp/conda_requirements.txt -ENV DJLAB_CONFIG /tmp/djlab_config.yaml ENV PATH /opt/conda/bin:$PATH ENTRYPOINT ["/entrypoint.sh"] WORKDIR /home/.anaconda diff --git a/dist/debian/docker-compose.yaml b/dist/debian/docker-compose.yaml index 680858f..052e927 100644 --- a/dist/debian/docker-compose.yaml +++ b/dist/debian/docker-compose.yaml @@ -1,7 +1,8 @@ version: '3.7' services: app: - build: # Uncomment to enable local build + build: + # Uncomment to enable local build context: ../.. dockerfile: ./dist/debian/Dockerfile args: @@ -9,26 +10,29 @@ services: - PY_VER - JUPYTERHUB_VER image: datajoint/djlabhub:${JUPYTERHUB_VER}-py${PY_VER}-debian + container_name: djlabhub-${JUPYTERHUB_VER}-py${PY_VER}-debian environment: - - DISPLAY # Necessary to allow GUI to route to Docker host - - NEW_USER=dja # Rename user and adjust HOME directory - # - Djlab_JupyterServer_Password=datajoint # Jupyter login password - # - Djlab_JupyterServer_DisplayFilepath=/home/README.md # Display on login - # - Djlab_JupyterServer_SaveOutput=FALSE # Set if notebook save includes output - - Djlabhub_ServerName=general # Specify the server reference name - - Djlabhub_NotebookRepo_Target=https://github.com/guzman-raphael/playground_tutorial # GitHub repo for reference notebooks - # - Djlabhub_NotebookRepo_Subpath=. # Relative subpath within GitHub repo to be included - - DJ_HOST=tutorial-db.datajoint.io # Specify DataJoint database host - - DJ_USER=example_user # Specify DataJoint database user - - DJ_PASS=example_password # Specify DataJoint database password - - FILEPATH_FEATURE_SWITCH=TRUE # Enable experimental datatype: filepath - - ADAPTED_TYPE_SWITCH=TRUE # Enable experimental datatype: adapted types + - DISPLAY # Necessary to allow GUI to route to Docker host + - NEW_USER=dja # Rename user and adjust HOME directory + - JUPYTER_SERVER_APP_PASSWORD=datajoint # Jupyter login password + - JUPYTER_LAB_APP_DEFAULT_URL=/home/README.md # Landing page display on login + - JUPYTER_FILE_CONTENTS_MANAGER_SAVE_OUTPUT=FALSE # Set if notebook save includes output + - DJLABHUB_SERVERNAME=general # Specify the server reference name + # - DJLABHUB_REPO_TARGET=https://github.com/datajoint/datajoint-tutorials # GitHub repo for reference notebooks + # - DJLABHUB_REPO_TARGET_SUBPATH=. # Relative subpath within GitHub repo to be included + - DJ_HOST=db # Specify DataJoint database host + - DJ_USER=root # Specify DataJoint database user + - DJ_PASS=${DJ_PASS} # Specify DataJoint database password + - FILEPATH_FEATURE_SWITCH=TRUE # Enable experimental datatype: filepath + - ADAPTED_TYPE_SWITCH=TRUE # Enable experimental datatype: adapted types + command: jupyter lab ports: - 8888:8888 - command: jupyter lab - user: ${HOST_UID}:anaconda # Necessary to allow GUI to route to Docker host + # command: jupyter lab + user: ${HOST_UID}:anaconda # Necessary to allow GUI to route to Docker host volumes: - - /tmp/.X11-unix:/tmp/.X11-unix:rw # Necessary to allow GUI to route to Docker host + - /tmp/.X11-unix:/tmp/.X11-unix:rw # Necessary to allow GUI to route to Docker host # - ../../config/datajoint_config.json:/usr/local/bin/.datajoint_config.json # Override default datajoint config for environment # - ../../common:/home/common # Mount point for common directory between all of user's environments # - ../../home_user:/home/.anaconda # Mount point for user's HOME directory + - ../../config/jupyterhub_config.py:/etc/jupyterhub/jupyterhub_config.py # Override default jupyterhub config for environment diff --git a/utilities/add_entrypoint.sh b/utilities/add_entrypoint.sh index 96b2ccc..d056645 100755 --- a/utilities/add_entrypoint.sh +++ b/utilities/add_entrypoint.sh @@ -12,8 +12,8 @@ run_post_start_jobs() { ~/.datajoint_config.json elif [ -z "${DJ_PASS}" ] && [ ! -f "~/.datajoint_config.json" ]; then # Password may be unset but config has not been initialized - rm ~/../common/.${Djlabhub_ServerName}_datajoint_config.json || \ - echo "No ${Djlabhub_ServerName} datajoint config backup detected" + rm ~/../common/.${DJLABHUB_SERVERNAME}_datajoint_config.json || \ + echo "No ${DJLABHUB_SERVERNAME} datajoint config backup detected" cp /usr/local/bin/.datajoint_config.json ~/ sed -i "s|\"database.host\": null|\"database.host\": \"${DJ_HOST}\"|g" \ ~/.datajoint_config.json @@ -33,18 +33,18 @@ run_post_start_jobs() { fi done fi - cp ~/.datajoint_config.json ~/../common/.${Djlabhub_ServerName}_datajoint_config.json + cp ~/.datajoint_config.json ~/../common/.${DJLABHUB_SERVERNAME}_datajoint_config.json # Start monitoring global config - BACKUP_TARGET=~/../common/.${Djlabhub_ServerName}_datajoint_config.json + BACKUP_TARGET=~/../common/.${DJLABHUB_SERVERNAME}_datajoint_config.json sh - <<-EOF & otumat watch -f ~/.datajoint_config.json -s dj_config_backup.sh "${BACKUP_TARGET}" EOF - if [ ! -z "${Djlabhub_NotebookRepo_Target}" ]; then + if [ ! -z "${DJLABHUB_REPO_TARGET}" ]; then # Remove files and hidden files rm -R /home/notebooks/* || echo "no files to remove" rm -rf /home/notebooks/.* 2> /dev/null || echo "no hidden files to remove" # Clone reference notebooks - git clone $Djlabhub_NotebookRepo_Target /home/notebooks + git clone $DJLABHUB_REPO_TARGET /home/notebooks # Pip install requirements from reference notebooks repo if [ -f "/home/notebooks/requirements.txt" ]; then @@ -56,8 +56,8 @@ run_post_start_jobs() { fi # Copy over only subpath mkdir /tmp/notebooks - cp -R /home/notebooks/${Djlabhub_NotebookRepo_Subpath}/* /tmp/notebooks - cp -r /home/notebooks/${Djlabhub_NotebookRepo_Subpath}/.[^.]* /tmp/notebooks + cp -R /home/notebooks/${DJLABHUB_REPO_TARGET_SUBPATH}/* /tmp/notebooks + cp -r /home/notebooks/${DJLABHUB_REPO_TARGET_SUBPATH}/.[^.]* /tmp/notebooks # Remove files and hidden files rm -R /home/notebooks/* rm -rf /home/notebooks/.* 2> /dev/null From 3b71b0900542ef454e45299b0fe2507a32d8c1b5 Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Tue, 31 Oct 2023 22:30:36 -0500 Subject: [PATCH 02/15] =?UTF-8?q?feat:=20=F0=9F=9A=A7=20chain=20breaker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chain-breaker/.env | 18 ++++ chain-breaker/Dockerfile | 25 +++++ chain-breaker/README.md | 14 +++ chain-breaker/config/apt_install.sh | 12 +++ chain-breaker/config/before_start_hook.sh | 25 +++++ chain-breaker/config/conda_requirements.txt | 2 + .../jupyter_jupyterlab_server_config.py | 25 +++++ chain-breaker/config/jupyter_server_config.py | 94 +++++++++++++++++++ chain-breaker/config/jupyterhub_config.py | 13 +++ chain-breaker/config/pip_requirements.txt | 0 chain-breaker/docker-compose.yaml | 19 ++++ config/.env | 3 +- 12 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 chain-breaker/.env create mode 100644 chain-breaker/Dockerfile create mode 100644 chain-breaker/README.md create mode 100644 chain-breaker/config/apt_install.sh create mode 100755 chain-breaker/config/before_start_hook.sh create mode 100644 chain-breaker/config/conda_requirements.txt create mode 100644 chain-breaker/config/jupyter_jupyterlab_server_config.py create mode 100644 chain-breaker/config/jupyter_server_config.py create mode 100644 chain-breaker/config/jupyterhub_config.py create mode 100644 chain-breaker/config/pip_requirements.txt create mode 100644 chain-breaker/docker-compose.yaml diff --git a/chain-breaker/.env b/chain-breaker/.env new file mode 100644 index 0000000..5e6776f --- /dev/null +++ b/chain-breaker/.env @@ -0,0 +1,18 @@ +## Build Args +PYTHON_VERSION=3.10 +JUPYTERHUB_VERSION=4.0.2 + +## Jupyter Official Environment Variables +DOCKER_STACKS_JUPYTER_CMD=lab +## Extended by Datajoint +## Before Start Hook +DJLABHUB_REPO=https://github.com/datajoint/datajoint-tutorials.git +DJLABHUB_REPO_BRANCH=main +DJLABHUB_REPO_INSTALL=TRUE +## Jupyter Config +JUPYTER_SERVER_APP_IP=0.0.0.0 +JUPYTER_SERVER_APP_PASSWORD=djlabhub +JUPYTER_SERVER_APP_PORT=8889 +JUPYTER_SERVER_APP_ROOT_DIR=/home/jovyan +JUPYTER_FILE_CONTENTS_MANAGER_ROOT_DIR=/home/jovyan +JUPYTER_YDOCEXTENSION_DISABLE_RTC=TRUE \ No newline at end of file diff --git a/chain-breaker/Dockerfile b/chain-breaker/Dockerfile new file mode 100644 index 0000000..06e08ad --- /dev/null +++ b/chain-breaker/Dockerfile @@ -0,0 +1,25 @@ +ARG JUPYTERHUB_VERSION +FROM quay.io/jupyter/minimal-notebook:hub-${JUPYTERHUB_VERSION} + +COPY ./config /tmp/config + +USER root +RUN \ + # Install dependencies: apt + bash /tmp/config/apt_install.sh \ + # Add startup hook + && cp /tmp/config/before_start_hook.sh /usr/local/bin/before-notebook.d/ \ + && chmod +x /usr/local/bin/before-notebook.d/before_start_hook.sh \ + # Add jupyterhub_config + && cp /tmp/config/jupyter*config*.py /etc/jupyter/ + +USER ${NB_UID} +ARG PYTHON_VERSION +RUN \ + # remove default work directory + [ -d "/home/jovyan/work" ] && rm -r /home/jovyan/work \ + # Install dependencies: pip and conda + && conda install -y -n base python=${PYTHON_VERSION} \ + && pip install -r /tmp/config/pip_requirements.txt \ + && conda install --yes --file /tmp/config/conda_requirements.txt \ + && conda clean --all -f -y diff --git a/chain-breaker/README.md b/chain-breaker/README.md new file mode 100644 index 0000000..78edbc1 --- /dev/null +++ b/chain-breaker/README.md @@ -0,0 +1,14 @@ +TODO - will make this a proper README.md later + +Pro +- Break the chain of dependencies of self-managed base images + - Reduce image maintenance hell + - Use jupyter/minimal-notebook as base image instead +- Compatible with previous supported feature as many as possible +- Provide simpler way for mainteners to manage system dependencies and for users to install packages +- Provide a way to config jupyter through environment variables +- This can be used as jupyter lab, so we can archive djlab-docker and only focus on this repo + +Con +- Username if jovyan by default, consider this is a minor issue, since it's only visible in the terminal +- Larger image size, this can be optimized when it becomes a problem diff --git a/chain-breaker/config/apt_install.sh b/chain-breaker/config/apt_install.sh new file mode 100644 index 0000000..11dc477 --- /dev/null +++ b/chain-breaker/config/apt_install.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Add any other apt repos here +sudo apt update + +# Install +sudo apt-get install mysql-client -y --no-install-recommends +sudo apt-get clean +rm -rf /var/lib/apt/lists/* + +# Other installation +wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq +chmod +x /usr/bin/yq \ No newline at end of file diff --git a/chain-breaker/config/before_start_hook.sh b/chain-breaker/config/before_start_hook.sh new file mode 100755 index 0000000..53a9a73 --- /dev/null +++ b/chain-breaker/config/before_start_hook.sh @@ -0,0 +1,25 @@ +#!/bin/bash +echo "INFO::Datajoint Startup Hook" + +# Changing Markdown Preview to preview as default +echo "INFO::Changing Markdown Preview to preview as default" +yq '.properties.defaultViewers.default = {"markdown":"Markdown Preview"}' \ + /opt/conda/share/jupyter/lab/schemas/@jupyterlab/docmanager-extension/plugin.json -o json -i + +# clone and install DJLABHUB_REPO or DJLABHUB_REPO_SUBPATH +# for private repo, include PAT(Personal Access Token) in the https url +if [[ ! -z "${DJLABHUB_REPO}" ]]; then + REPO_NAME=$(basename $DJLABHUB_REPO | sed 's/.git//') + + echo "INFO::Cloning repo $DJLABHUB_REPO" + git clone $DJLABHUB_REPO $HOME/$REPO_NAME + if [[ ! -z "${DJLABHUB_REPO_BRANCH}" ]]; then + echo "INFO::Switch to branch $DJLABHUB_REPO_BRANCH" + git -C $HOME/$REPO_NAME switch $DJLABHUB_REPO_BRANCH + fi + + if [[ $DJLABHUB_REPO_INSTALL == "TRUE" ]]; then + echo "INFO::Installing repo" + pip install -e $HOME/$REPO_NAME + fi +fi \ No newline at end of file diff --git a/chain-breaker/config/conda_requirements.txt b/chain-breaker/config/conda_requirements.txt new file mode 100644 index 0000000..ab697d3 --- /dev/null +++ b/chain-breaker/config/conda_requirements.txt @@ -0,0 +1,2 @@ +gh +jupyter-collaboration diff --git a/chain-breaker/config/jupyter_jupyterlab_server_config.py b/chain-breaker/config/jupyter_jupyterlab_server_config.py new file mode 100644 index 0000000..70881cd --- /dev/null +++ b/chain-breaker/config/jupyter_jupyterlab_server_config.py @@ -0,0 +1,25 @@ +import os +import pwd + +# Configuration file for lab. + +# c = get_config() # noqa +from traitlets.config import Config + +c = Config() if "c" not in locals() else c + +# get the current user +user = [u for u in pwd.getpwall() if u.pw_uid == os.getuid()][0] + +## The default URL to redirect to from `/` +# Default: '/lab' +jupyter_lab_default_url = os.getenv("JUPYTER_LAB_APP_DEFAULT_URL") +c.LabApp.default_url = ( + "/lab/tree{}".format( + jupyter_lab_default_url.replace( + os.getenv("JUPYTER_FILE_CONTENTS_MANAGER_ROOT_DIR", "/home/jovyan"), "" + ) + ) + if jupyter_lab_default_url + else "/lab" +) diff --git a/chain-breaker/config/jupyter_server_config.py b/chain-breaker/config/jupyter_server_config.py new file mode 100644 index 0000000..086d9ab --- /dev/null +++ b/chain-breaker/config/jupyter_server_config.py @@ -0,0 +1,94 @@ +import os +import pwd +import hashlib +import random +import json + +# Configuration file for jupyter-server. + +# c = get_config() # noqa +from traitlets.config import Config + +c = Config() if "c" not in locals() else c + +# get the current user +user = [u for u in pwd.getpwall() if u.pw_uid == os.getuid()][0] + +## Whether to allow the user to run the server as root. +# Default: False +c.ServerApp.allow_root = False + +## The IP address the Jupyter server will listen on. +# Default: 'localhost' +c.ServerApp.ip = os.getenv("JUPYTER_SERVER_APP_IP", "0.0.0.0") + + +## DEPRECATED in 2.0. Use PasswordIdentityProvider.hashed_password +# Default: '' +# c.ServerApp.password = '' +def passwd(passphrase: str): + salt_len = 12 + h = hashlib.new("sha256") + salt = ("%0" + str(salt_len) + "x") % random.getrandbits(4 * salt_len) + h.update(passphrase.encode("utf-8") + salt.encode("ascii")) + return ":".join(("sha256", salt, h.hexdigest())) + + +jupyter_server_password = os.getenv("JUPYTER_SERVER_APP_PASSWORD", "datajoint") +c.PasswordIdentityProvider.hashed_password = ( + passwd(jupyter_server_password) if jupyter_server_password else "" +) + +## The port the server will listen on (env: JUPYTER_PORT). +# Default: 0 +c.ServerApp.port = int(os.getenv("JUPYTER_SERVER_APP_PORT", 8888)) + +## The directory to use for notebooks and kernels. +# Default: '' +c.ServerApp.root_dir = os.getenv("JUPYTER_SERVER_APP_ROOT_DIR", user.pw_dir) + +## Supply overrides for terminado. Currently only supports "shell_command". +# Default: {} +c.ServerApp.terminado_settings = json.loads( + os.getenv( + "JUPYTER_SERVER_APP_TERMINADO_SETTINGS", + f'{{"shell_command": ["{user.pw_shell}"]}}', + ) +) + + +## Python callable or importstring thereof +# See also: ContentsManager.pre_save_hook +# c.FileContentsManager.pre_save_hook = None +def scrub_output_pre_save(model, **kwargs): + """scrub output before saving notebooks""" + if not os.getenv("JUPYTER_FILE_CONTENTS_MANAGER_SAVE_OUTPUT", "FALSE") == "TRUE": + # only run on notebooks + if model["type"] != "notebook": + return + # only run on nbformat v4 + if model["content"]["nbformat"] != 4: + return + + model["content"]["metadata"].pop("signature", None) + for cell in model["content"]["cells"]: + if cell["cell_type"] != "code": + continue + cell["outputs"] = [] + cell["execution_count"] = None + else: + return + + +c.FileContentsManager.pre_save_hook = scrub_output_pre_save + + +# Default: '' +c.FileContentsManager.root_dir = os.getenv( + "JUPYTER_FILE_CONTENTS_MANAGER_ROOT_DIR", "/home/jovyan" +) + +## Jupyter collaboration extension +c.YDocExtension.disable_rtc = ( + os.getenv("JUPYTER_YDOCEXTENSION_DISABLE_RTC", "FALSE").upper() == "TRUE" +) diff --git a/chain-breaker/config/jupyterhub_config.py b/chain-breaker/config/jupyterhub_config.py new file mode 100644 index 0000000..200a895 --- /dev/null +++ b/chain-breaker/config/jupyterhub_config.py @@ -0,0 +1,13 @@ +import os +import pwd +from traitlets.config import Config + +c = Config() if "c" not in locals() else c + +# get the current user +user = [u for u in pwd.getpwall() if u.pw_uid == os.getuid()][0] + +## Allow named single-user servers per user +# Default: False +# c.JupyterHub.allow_named_servers = False +c.JupyterHub.allow_named_servers = True diff --git a/chain-breaker/config/pip_requirements.txt b/chain-breaker/config/pip_requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/chain-breaker/docker-compose.yaml b/chain-breaker/docker-compose.yaml new file mode 100644 index 0000000..f354920 --- /dev/null +++ b/chain-breaker/docker-compose.yaml @@ -0,0 +1,19 @@ +version: '3.7' +services: + app: + build: + context: . + dockerfile: Dockerfile + args: + - PYTHON_VERSION + - JUPYTERHUB_VERSION + image: datajoint/djlabhub:${JUPYTERHUB_VERSION}-py${PYTHON_VERSION} + container_name: djlabhub + env_file: .env + ports: + - $JUPYTER_SERVER_APP_PORT:$JUPYTER_SERVER_APP_PORT + volumes: + - ./config/before_start_hook.sh:/usr/local/bin/before-notebook.d/before_start_hook.sh + - ./config/jupyter_server_config.py:/etc/jupyter/jupyter_server_config.py + - ./config/jupyter_jupyterlab_server_config.py:/etc/jupyter/jupyter_jupyterlab_server_config.py + - ./config/jupyterhub_config.py:/etc/jupyter/jupyterhub_config.py diff --git a/config/.env b/config/.env index 4ec08a0..5dea230 100644 --- a/config/.env +++ b/config/.env @@ -8,7 +8,8 @@ PY_VER='3.10' # PY_VER=3.7 -JUPYTERHUB_VER=1.4.2 +# JUPYTERHUB_VER=1.4.2 +JUPYTERHUB_VER=4.0.2 BASE_IMAGE_HASH=eb6a760 From 66c12a2ef06046608603223831d68e88424d8717 Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Tue, 31 Oct 2023 22:42:21 -0500 Subject: [PATCH 03/15] =?UTF-8?q?fix:=20=F0=9F=90=9B=20remove=20example=20?= =?UTF-8?q?password?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chain-breaker/.gitignore | 1 + chain-breaker/Dockerfile | 2 +- chain-breaker/{.env => example.env} | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 chain-breaker/.gitignore rename chain-breaker/{.env => example.env} (93%) diff --git a/chain-breaker/.gitignore b/chain-breaker/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/chain-breaker/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/chain-breaker/Dockerfile b/chain-breaker/Dockerfile index 06e08ad..7106ec1 100644 --- a/chain-breaker/Dockerfile +++ b/chain-breaker/Dockerfile @@ -10,7 +10,7 @@ RUN \ # Add startup hook && cp /tmp/config/before_start_hook.sh /usr/local/bin/before-notebook.d/ \ && chmod +x /usr/local/bin/before-notebook.d/before_start_hook.sh \ - # Add jupyterhub_config + # Add jupyter*config*.py && cp /tmp/config/jupyter*config*.py /etc/jupyter/ USER ${NB_UID} diff --git a/chain-breaker/.env b/chain-breaker/example.env similarity index 93% rename from chain-breaker/.env rename to chain-breaker/example.env index 5e6776f..5aeb13c 100644 --- a/chain-breaker/.env +++ b/chain-breaker/example.env @@ -11,7 +11,7 @@ DJLABHUB_REPO_BRANCH=main DJLABHUB_REPO_INSTALL=TRUE ## Jupyter Config JUPYTER_SERVER_APP_IP=0.0.0.0 -JUPYTER_SERVER_APP_PASSWORD=djlabhub +JUPYTER_SERVER_APP_PASSWORD= JUPYTER_SERVER_APP_PORT=8889 JUPYTER_SERVER_APP_ROOT_DIR=/home/jovyan JUPYTER_FILE_CONTENTS_MANAGER_ROOT_DIR=/home/jovyan From b0278b47ecb281c894abf7f1b2052f4543377b8d Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Wed, 1 Nov 2023 00:38:02 -0500 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20=F0=9F=9A=A7=20enable=20local=20d?= =?UTF-8?q?ocker=20jhub?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chain-breaker/README.md | 1 + chain-breaker/config/jupyterhub_config.py | 13 --- chain-breaker/hub/Dockerfile | 4 + chain-breaker/hub/config/jupyterhub_config.py | 79 +++++++++++++++++++ chain-breaker/hub/docker-compose.yaml | 28 +++++++ chain-breaker/hub/example.env | 2 + chain-breaker/{ => singleuser}/Dockerfile | 0 .../{ => singleuser}/config/apt_install.sh | 0 .../config/before_start_hook.sh | 0 .../config/conda_requirements.txt | 0 .../jupyter_jupyterlab_server_config.py | 0 .../config/jupyter_server_config.py | 0 .../config/pip_requirements.txt | 0 .../{ => singleuser}/docker-compose.yaml | 5 +- chain-breaker/{ => singleuser}/example.env | 0 15 files changed, 116 insertions(+), 16 deletions(-) delete mode 100644 chain-breaker/config/jupyterhub_config.py create mode 100644 chain-breaker/hub/Dockerfile create mode 100644 chain-breaker/hub/config/jupyterhub_config.py create mode 100644 chain-breaker/hub/docker-compose.yaml create mode 100644 chain-breaker/hub/example.env rename chain-breaker/{ => singleuser}/Dockerfile (100%) rename chain-breaker/{ => singleuser}/config/apt_install.sh (100%) rename chain-breaker/{ => singleuser}/config/before_start_hook.sh (100%) rename chain-breaker/{ => singleuser}/config/conda_requirements.txt (100%) rename chain-breaker/{ => singleuser}/config/jupyter_jupyterlab_server_config.py (100%) rename chain-breaker/{ => singleuser}/config/jupyter_server_config.py (100%) rename chain-breaker/{ => singleuser}/config/pip_requirements.txt (100%) rename chain-breaker/{ => singleuser}/docker-compose.yaml (75%) rename chain-breaker/{ => singleuser}/example.env (100%) diff --git a/chain-breaker/README.md b/chain-breaker/README.md index 78edbc1..48812d1 100644 --- a/chain-breaker/README.md +++ b/chain-breaker/README.md @@ -8,6 +8,7 @@ Pro - Provide simpler way for mainteners to manage system dependencies and for users to install packages - Provide a way to config jupyter through environment variables - This can be used as jupyter lab, so we can archive djlab-docker and only focus on this repo +- Add jupyterhub docker for single user image testing at local Con - Username if jovyan by default, consider this is a minor issue, since it's only visible in the terminal diff --git a/chain-breaker/config/jupyterhub_config.py b/chain-breaker/config/jupyterhub_config.py deleted file mode 100644 index 200a895..0000000 --- a/chain-breaker/config/jupyterhub_config.py +++ /dev/null @@ -1,13 +0,0 @@ -import os -import pwd -from traitlets.config import Config - -c = Config() if "c" not in locals() else c - -# get the current user -user = [u for u in pwd.getpwall() if u.pw_uid == os.getuid()][0] - -## Allow named single-user servers per user -# Default: False -# c.JupyterHub.allow_named_servers = False -c.JupyterHub.allow_named_servers = True diff --git a/chain-breaker/hub/Dockerfile b/chain-breaker/hub/Dockerfile new file mode 100644 index 0000000..217c6a8 --- /dev/null +++ b/chain-breaker/hub/Dockerfile @@ -0,0 +1,4 @@ +ARG JUPYTERHUB_VERSION +FROM jupyterhub/jupyterhub:${JUPYTERHUB_VERSION} + +RUN pip install dockerspawner \ No newline at end of file diff --git a/chain-breaker/hub/config/jupyterhub_config.py b/chain-breaker/hub/config/jupyterhub_config.py new file mode 100644 index 0000000..f5c2410 --- /dev/null +++ b/chain-breaker/hub/config/jupyterhub_config.py @@ -0,0 +1,79 @@ +import os +import pwd +from traitlets.config import Config + +c = Config() if "c" not in locals() else c + +# get the current user +user = [u for u in pwd.getpwall() if u.pw_uid == os.getuid()][0] + +## Class for authenticating users. +# +# This should be a subclass of :class:`jupyterhub.auth.Authenticator` +# +# with an :meth:`authenticate` method that: +# +# - is a coroutine (asyncio or tornado) +# - returns username on success, None on failure +# - takes two arguments: (handler, data), +# where `handler` is the calling web.RequestHandler, +# and `data` is the POST form data from the login page. +# +# .. versionchanged:: 1.0 +# authenticators may be registered via entry points, +# e.g. `c.JupyterHub.authenticator_class = 'pam'` +# +# Currently installed: +# - default: jupyterhub.auth.PAMAuthenticator +# - dummy: jupyterhub.auth.DummyAuthenticator +# - null: jupyterhub.auth.NullAuthenticator +# - pam: jupyterhub.auth.PAMAuthenticator +# Default: 'jupyterhub.auth.PAMAuthenticator' +c.JupyterHub.authenticator_class = "jupyterhub.auth.DummyAuthenticator" + +## The class to use for spawning single-user servers. +# +# Should be a subclass of :class:`jupyterhub.spawner.Spawner`. +# +# .. versionchanged:: 1.0 +# spawners may be registered via entry points, +# e.g. `c.JupyterHub.spawner_class = 'localprocess'` +# +# Currently installed: +# - default: jupyterhub.spawner.LocalProcessSpawner +# - localprocess: jupyterhub.spawner.LocalProcessSpawner +# - simple: jupyterhub.spawner.SimpleLocalProcessSpawner +# Default: 'jupyterhub.spawner.LocalProcessSpawner' +c.JupyterHub.spawner_class = "dockerspawner.DockerSpawner" + +## The ip address for the Hub process to *bind* to. +# +# By default, the hub listens on localhost only. This address must be accessible from +# the proxy and user servers. You may need to set this to a public ip or '' for all +# interfaces if the proxy or user servers are in containers or on a different host. +# +# See `hub_connect_ip` for cases where the bind and connect address should differ, +# or `hub_bind_url` for setting the full bind URL. +# Default: '127.0.0.1' +c.JupyterHub.hub_ip = "" + +c.DockerSpawner.network_name = os.getenv("DOCKER_NETWORK_NAME", "jupyterhub_network") + +c.DockerSpawner.container_image = "datajoint/djlabhub:singleuser-4.0.2-py3.10-qa" + +c.DockerSpawner.environment = { + ## Jupyter Official Environment Variables + "DOCKER_STACKS_JUPYTER_CMD": "lab", + ## Extended by Datajoint + ## Before Start Hook + "DJLABHUB_REPO": "https://github.com/datajoint/datajoint-tutorials.git", + "DJLABHUB_REPO_BRANCH": "main", + "DJLABHUB_REPO_INSTALL": "TRUE", + ## Jupyter Config + # "JUPYTER_SERVER_APP_IP": "0.0.0.0", + # "JUPYTER_SERVER_APP_PASSWORD": "", + # "JUPYTER_SERVER_APP_PORT": "8889", + # "JUPYTER_SERVER_APP_ROOT_DIR": "/home/jovyan", + "JUPYTER_FILE_CONTENTS_MANAGER_ROOT_DIR": "/home/jovyan", + "JUPYTER_YDOCEXTENSION_DISABLE_RTC": "TRUE", +} diff --git a/chain-breaker/hub/docker-compose.yaml b/chain-breaker/hub/docker-compose.yaml new file mode 100644 index 0000000..e57e4c3 --- /dev/null +++ b/chain-breaker/hub/docker-compose.yaml @@ -0,0 +1,28 @@ +version: '3.7' +services: + hub: + build: + context: . + dockerfile: Dockerfile + args: + - JUPYTERHUB_VERSION + image: datajoint/djlabhub:hub-${JUPYTERHUB_VERSION} + container_name: djlabhub-hub + user: root + env_file: .env + networks: + - jupyterhub_network + environment: + - DOCKER_NETWORK_NAME=jupyterhub_network + command: jupyterhub -f /etc/jupyterhub/jupyterhub_config.py + ports: + - 8000:8000 + volumes: + - ./config/jupyterhub_config.py:/etc/jupyterhub/jupyterhub_config.py + - /var/run/docker.sock:/var/run/docker.sock + +networks: + jupyterhub_network: + name: jupyterhub_network + driver: bridge + diff --git a/chain-breaker/hub/example.env b/chain-breaker/hub/example.env new file mode 100644 index 0000000..e8c4099 --- /dev/null +++ b/chain-breaker/hub/example.env @@ -0,0 +1,2 @@ +# build +JUPYTERHUB_VERSION=4.0.2 \ No newline at end of file diff --git a/chain-breaker/Dockerfile b/chain-breaker/singleuser/Dockerfile similarity index 100% rename from chain-breaker/Dockerfile rename to chain-breaker/singleuser/Dockerfile diff --git a/chain-breaker/config/apt_install.sh b/chain-breaker/singleuser/config/apt_install.sh similarity index 100% rename from chain-breaker/config/apt_install.sh rename to chain-breaker/singleuser/config/apt_install.sh diff --git a/chain-breaker/config/before_start_hook.sh b/chain-breaker/singleuser/config/before_start_hook.sh similarity index 100% rename from chain-breaker/config/before_start_hook.sh rename to chain-breaker/singleuser/config/before_start_hook.sh diff --git a/chain-breaker/config/conda_requirements.txt b/chain-breaker/singleuser/config/conda_requirements.txt similarity index 100% rename from chain-breaker/config/conda_requirements.txt rename to chain-breaker/singleuser/config/conda_requirements.txt diff --git a/chain-breaker/config/jupyter_jupyterlab_server_config.py b/chain-breaker/singleuser/config/jupyter_jupyterlab_server_config.py similarity index 100% rename from chain-breaker/config/jupyter_jupyterlab_server_config.py rename to chain-breaker/singleuser/config/jupyter_jupyterlab_server_config.py diff --git a/chain-breaker/config/jupyter_server_config.py b/chain-breaker/singleuser/config/jupyter_server_config.py similarity index 100% rename from chain-breaker/config/jupyter_server_config.py rename to chain-breaker/singleuser/config/jupyter_server_config.py diff --git a/chain-breaker/config/pip_requirements.txt b/chain-breaker/singleuser/config/pip_requirements.txt similarity index 100% rename from chain-breaker/config/pip_requirements.txt rename to chain-breaker/singleuser/config/pip_requirements.txt diff --git a/chain-breaker/docker-compose.yaml b/chain-breaker/singleuser/docker-compose.yaml similarity index 75% rename from chain-breaker/docker-compose.yaml rename to chain-breaker/singleuser/docker-compose.yaml index f354920..b996b01 100644 --- a/chain-breaker/docker-compose.yaml +++ b/chain-breaker/singleuser/docker-compose.yaml @@ -7,8 +7,8 @@ services: args: - PYTHON_VERSION - JUPYTERHUB_VERSION - image: datajoint/djlabhub:${JUPYTERHUB_VERSION}-py${PYTHON_VERSION} - container_name: djlabhub + image: datajoint/djlabhub:singleuser-${JUPYTERHUB_VERSION}-py${PYTHON_VERSION} + container_name: djlabhub-singleuser env_file: .env ports: - $JUPYTER_SERVER_APP_PORT:$JUPYTER_SERVER_APP_PORT @@ -16,4 +16,3 @@ services: - ./config/before_start_hook.sh:/usr/local/bin/before-notebook.d/before_start_hook.sh - ./config/jupyter_server_config.py:/etc/jupyter/jupyter_server_config.py - ./config/jupyter_jupyterlab_server_config.py:/etc/jupyter/jupyter_jupyterlab_server_config.py - - ./config/jupyterhub_config.py:/etc/jupyter/jupyterhub_config.py diff --git a/chain-breaker/example.env b/chain-breaker/singleuser/example.env similarity index 100% rename from chain-breaker/example.env rename to chain-breaker/singleuser/example.env From b40f0cef8bff632f9df0fdad8f91c4a4d882c4d9 Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Wed, 1 Nov 2023 00:42:19 -0500 Subject: [PATCH 05/15] =?UTF-8?q?feat:=20=F0=9F=9A=A7=20add=20djlab=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chain-breaker/singleuser/docker-compose.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/chain-breaker/singleuser/docker-compose.yaml b/chain-breaker/singleuser/docker-compose.yaml index b996b01..a2be739 100644 --- a/chain-breaker/singleuser/docker-compose.yaml +++ b/chain-breaker/singleuser/docker-compose.yaml @@ -1,6 +1,6 @@ version: '3.7' services: - app: + singleuser: build: context: . dockerfile: Dockerfile @@ -16,3 +16,6 @@ services: - ./config/before_start_hook.sh:/usr/local/bin/before-notebook.d/before_start_hook.sh - ./config/jupyter_server_config.py:/etc/jupyter/jupyter_server_config.py - ./config/jupyter_jupyterlab_server_config.py:/etc/jupyter/jupyter_jupyterlab_server_config.py + djlab: + extends: singleuser + image: datajoint/djlab:py${PYTHON_VERSION} From 499f71788b9820e445b3d90bd6b34392101508c9 Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Wed, 1 Nov 2023 00:43:44 -0500 Subject: [PATCH 06/15] =?UTF-8?q?fix:=20=F0=9F=90=9B=20add=20hub=20config?= =?UTF-8?q?=20in=20the=20image?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chain-breaker/hub/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/chain-breaker/hub/Dockerfile b/chain-breaker/hub/Dockerfile index 217c6a8..07e100e 100644 --- a/chain-breaker/hub/Dockerfile +++ b/chain-breaker/hub/Dockerfile @@ -1,4 +1,5 @@ ARG JUPYTERHUB_VERSION FROM jupyterhub/jupyterhub:${JUPYTERHUB_VERSION} +COPY ./config/jupyterhub_config.py /etc/jupyterhub/jupyterhub_config.py RUN pip install dockerspawner \ No newline at end of file From 6517250cccfef574bf2b09966a3a65044598308b Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Wed, 8 Nov 2023 13:16:47 -0600 Subject: [PATCH 07/15] =?UTF-8?q?chore:=20=F0=9F=9A=A7=20try=20oauth=20on?= =?UTF-8?q?=20local=20but=20it=20doesn't=20work=20because=20of=20SSL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chain-breaker/hub/Dockerfile | 2 +- chain-breaker/hub/config/jupyterhub_config.py | 15 +++++++++++++++ chain-breaker/hub/docker-compose.yaml | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/chain-breaker/hub/Dockerfile b/chain-breaker/hub/Dockerfile index 07e100e..54c4aee 100644 --- a/chain-breaker/hub/Dockerfile +++ b/chain-breaker/hub/Dockerfile @@ -2,4 +2,4 @@ ARG JUPYTERHUB_VERSION FROM jupyterhub/jupyterhub:${JUPYTERHUB_VERSION} COPY ./config/jupyterhub_config.py /etc/jupyterhub/jupyterhub_config.py -RUN pip install dockerspawner \ No newline at end of file +RUN pip install dockerspawner oauthenticator \ No newline at end of file diff --git a/chain-breaker/hub/config/jupyterhub_config.py b/chain-breaker/hub/config/jupyterhub_config.py index f5c2410..42a84b2 100644 --- a/chain-breaker/hub/config/jupyterhub_config.py +++ b/chain-breaker/hub/config/jupyterhub_config.py @@ -31,6 +31,21 @@ # Default: 'jupyterhub.auth.PAMAuthenticator' c.JupyterHub.authenticator_class = "jupyterhub.auth.DummyAuthenticator" +# ## TODO - callback_url needs to enable ssl +# c.JupyterHub.authenticator_class = "oauthenticator.generic.GenericOAuthenticator" +# c.GenericOAuthenticator.client_id = os.getenv("OAUTH2_CLIENT_ID") +# c.GenericOAuthenticator.client_secret = os.getenv("OAUTH2_CLIENT_SECRET") +# c.GenericOAuthenticator.oauth_callback_url = "https://127.0.0.1:8000/hub/oauth_callback" +# c.GenericOAuthenticator.authorize_url = "https://keycloak-qa.datajoint.io/realms/datajoint/protocol/openid-connect/auth" +# c.GenericOAuthenticator.token_url = "https://keycloak-qa.datajoint.io/realms/datajoint/protocol/openid-connect/token" +# c.GenericOAuthenticator.userdata_url = "https://keycloak-qa.datajoint.io/realms/datajoint/protocol/openid-connect/userinfo" +# c.GenericOAuthenticator.login_service = "Datajoint" +# c.GenericOAuthenticator.username_claim = "preferred_username" +# c.GenericOAuthenticator.enable_auth_state = True +# c.GenericOAuthenticator.scope = ["openid"] +# c.GenericOAuthenticator.claim_groups_key = "groups" +# c.GenericOAuthenticator.admin_groups = ["datajoint"] + ## The class to use for spawning single-user servers. # # Should be a subclass of :class:`jupyterhub.spawner.Spawner`. diff --git a/chain-breaker/hub/docker-compose.yaml b/chain-breaker/hub/docker-compose.yaml index e57e4c3..2bd72a0 100644 --- a/chain-breaker/hub/docker-compose.yaml +++ b/chain-breaker/hub/docker-compose.yaml @@ -10,6 +10,7 @@ services: container_name: djlabhub-hub user: root env_file: .env + # network to connect the hub and singleusers networks: - jupyterhub_network environment: From 36d5f49e2464f364acb8af468ea0ee0fba732f87 Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Wed, 21 Feb 2024 18:03:42 -0600 Subject: [PATCH 08/15] =?UTF-8?q?feat:=20=E2=9C=A8=20adding=20more=20env?= =?UTF-8?q?=20config=20and=20comment=20on=20config=20reference=20doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jupyter_jupyterlab_server_config.py | 2 +- .../jupyter_jupyternotebook_server_config.py | 25 +++++++++++++++++++ .../config/jupyter_server_config.py | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 chain-breaker/singleuser/config/jupyter_jupyternotebook_server_config.py diff --git a/chain-breaker/singleuser/config/jupyter_jupyterlab_server_config.py b/chain-breaker/singleuser/config/jupyter_jupyterlab_server_config.py index 70881cd..ebb6be6 100644 --- a/chain-breaker/singleuser/config/jupyter_jupyterlab_server_config.py +++ b/chain-breaker/singleuser/config/jupyter_jupyterlab_server_config.py @@ -1,7 +1,7 @@ import os import pwd -# Configuration file for lab. +# Configuration file for lab. https://jupyterlab-server.readthedocs.io/en/latest/api/app-config.html # c = get_config() # noqa from traitlets.config import Config diff --git a/chain-breaker/singleuser/config/jupyter_jupyternotebook_server_config.py b/chain-breaker/singleuser/config/jupyter_jupyternotebook_server_config.py new file mode 100644 index 0000000..612dbdd --- /dev/null +++ b/chain-breaker/singleuser/config/jupyter_jupyternotebook_server_config.py @@ -0,0 +1,25 @@ +import os +import pwd + +# Configuration file for notebook. https://jupyter-notebook.readthedocs.io/en/5.7.4/config.html + +# c = get_config() # noqa +from traitlets.config import Config + +c = Config() if "c" not in locals() else c + +# get the current user +user = [u for u in pwd.getpwall() if u.pw_uid == os.getuid()][0] + +## The default URL to redirect to from `/` +# Default: '/lab' +jupyter_lab_default_url = os.getenv("JUPYTER_LAB_APP_DEFAULT_URL") +c.LabApp.default_url = ( + "/lab/tree{}".format( + jupyter_lab_default_url.replace( + os.getenv("JUPYTER_FILE_CONTENTS_MANAGER_ROOT_DIR", "/home/jovyan"), "" + ) + ) + if jupyter_lab_default_url + else "/lab" +) diff --git a/chain-breaker/singleuser/config/jupyter_server_config.py b/chain-breaker/singleuser/config/jupyter_server_config.py index 086d9ab..459ff23 100644 --- a/chain-breaker/singleuser/config/jupyter_server_config.py +++ b/chain-breaker/singleuser/config/jupyter_server_config.py @@ -4,7 +4,7 @@ import random import json -# Configuration file for jupyter-server. +# Configuration file for jupyter-server. https://jupyter-server.readthedocs.io/en/latest/other/full-config.html#other-full-config # c = get_config() # noqa from traitlets.config import Config From 05f4296fdbb27c4cee3f9468eb97374e32dcfcfc Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Fri, 23 Feb 2024 11:09:41 -0600 Subject: [PATCH 09/15] =?UTF-8?q?build:=20=F0=9F=9A=9A=20requires=20legacy?= =?UTF-8?q?=20files=20to=20pass=20the=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/djlab_config.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 config/djlab_config.yaml diff --git a/config/djlab_config.yaml b/config/djlab_config.yaml new file mode 100644 index 0000000..9fb4588 --- /dev/null +++ b/config/djlab_config.yaml @@ -0,0 +1,10 @@ +djlab: + jupyter_server: + display_filepath: '/home/README.md' + password: 'datajoint' + save_output: 'FALSE' +djlabhub: + server_name: 'NULL' + notebook_repo: + target: 'NULL' + subpath: '.' \ No newline at end of file From de9e55e1f51e8961d0bae6e5ca9dfc750172fa1c Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Fri, 23 Feb 2024 11:11:34 -0600 Subject: [PATCH 10/15] fix: disable cicd before refactor --- .github/workflows/development.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/development.yaml b/.github/workflows/development.yaml index a9903f2..32eddd8 100644 --- a/.github/workflows/development.yaml +++ b/.github/workflows/development.yaml @@ -1,7 +1,8 @@ name: Development on: - push: - pull_request: + # push: + # pull_request: + workflow_dispatch: jobs: build: runs-on: ubuntu-latest From 3084ecfc94f58783d8dc89d2129faf7f7ba26ce2 Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Fri, 8 Mar 2024 01:25:52 -0600 Subject: [PATCH 11/15] =?UTF-8?q?fix:=20=F0=9F=90=9B=20add=20djlab=20conta?= =?UTF-8?q?iner=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chain-breaker/singleuser/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/chain-breaker/singleuser/docker-compose.yaml b/chain-breaker/singleuser/docker-compose.yaml index a2be739..afdee80 100644 --- a/chain-breaker/singleuser/docker-compose.yaml +++ b/chain-breaker/singleuser/docker-compose.yaml @@ -18,4 +18,5 @@ services: - ./config/jupyter_jupyterlab_server_config.py:/etc/jupyter/jupyter_jupyterlab_server_config.py djlab: extends: singleuser + container_name: djlab image: datajoint/djlab:py${PYTHON_VERSION} From fd5218f5c352abcc7e76aea03e9e3d9141de183b Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Mon, 11 Mar 2024 10:50:43 -0500 Subject: [PATCH 12/15] =?UTF-8?q?refactor:=20=E2=99=BB=EF=B8=8F=20put=20pr?= =?UTF-8?q?evious=20image=20in=20the=20legacy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/singleuser-release.yaml | 28 +++++++++++++++ .gitignore | 7 +--- chain-breaker/README.md => README.md | 0 chain-breaker/.gitignore | 1 - chain-breaker/hub/example.env | 2 -- config/.env | 34 ------------------- {chain-breaker/hub => hub}/Dockerfile | 0 .../hub => hub}/config/jupyterhub_config.py | 0 .../hub => hub}/docker-compose.yaml | 0 hub/example.env | 7 ++++ .../.github}/workflows/development.yaml | 0 legacy/.gitignore | 6 ++++ README.rst => legacy/README.rst | 0 {config => legacy/config}/README.md | 0 .../config}/datajoint_config.json | 0 {config => legacy/config}/djlab_config.yaml | 0 .../config}/jupyterhub_config.py | 0 .../config}/pip_requirements.txt | 0 {dist => legacy/dist}/alpine/Dockerfile | 0 .../dist}/alpine/apk_requirements.txt | 0 .../dist}/alpine/docker-compose.yaml | 0 {dist => legacy/dist}/debian/Dockerfile | 0 .../dist}/debian/apt_requirements.txt | 0 .../dist}/debian/docker-compose.yaml | 0 {docs => legacy/docs}/Makefile | 0 {docs => legacy/docs}/conf.py | 0 {docs => legacy/docs}/index.rst | 0 {tests => legacy/tests}/main.sh | 0 .../utilities}/add_entrypoint.sh | 0 .../utilities}/dj_config_backup.sh | 0 .../singleuser => singleuser}/Dockerfile | 1 - .../config/apt_install.sh | 0 .../config/before_start_hook.sh | 0 .../config/conda_requirements.txt | 0 .../jupyter_jupyterlab_server_config.py | 0 .../jupyter_jupyternotebook_server_config.py | 0 .../config/jupyter_server_config.py | 0 .../config/pip_requirements.txt | 0 .../docker-compose.yaml | 0 .../singleuser => singleuser}/example.env | 0 40 files changed, 42 insertions(+), 44 deletions(-) create mode 100644 .github/workflows/singleuser-release.yaml rename chain-breaker/README.md => README.md (100%) delete mode 100644 chain-breaker/.gitignore delete mode 100644 chain-breaker/hub/example.env delete mode 100644 config/.env rename {chain-breaker/hub => hub}/Dockerfile (100%) rename {chain-breaker/hub => hub}/config/jupyterhub_config.py (100%) rename {chain-breaker/hub => hub}/docker-compose.yaml (100%) create mode 100644 hub/example.env rename {.github => legacy/.github}/workflows/development.yaml (100%) create mode 100644 legacy/.gitignore rename README.rst => legacy/README.rst (100%) rename {config => legacy/config}/README.md (100%) rename {config => legacy/config}/datajoint_config.json (100%) rename {config => legacy/config}/djlab_config.yaml (100%) rename {config => legacy/config}/jupyterhub_config.py (100%) rename {config => legacy/config}/pip_requirements.txt (100%) rename {dist => legacy/dist}/alpine/Dockerfile (100%) rename {dist => legacy/dist}/alpine/apk_requirements.txt (100%) rename {dist => legacy/dist}/alpine/docker-compose.yaml (100%) rename {dist => legacy/dist}/debian/Dockerfile (100%) rename {dist => legacy/dist}/debian/apt_requirements.txt (100%) rename {dist => legacy/dist}/debian/docker-compose.yaml (100%) rename {docs => legacy/docs}/Makefile (100%) rename {docs => legacy/docs}/conf.py (100%) rename {docs => legacy/docs}/index.rst (100%) rename {tests => legacy/tests}/main.sh (100%) rename {utilities => legacy/utilities}/add_entrypoint.sh (100%) rename {utilities => legacy/utilities}/dj_config_backup.sh (100%) rename {chain-breaker/singleuser => singleuser}/Dockerfile (98%) rename {chain-breaker/singleuser => singleuser}/config/apt_install.sh (100%) rename {chain-breaker/singleuser => singleuser}/config/before_start_hook.sh (100%) rename {chain-breaker/singleuser => singleuser}/config/conda_requirements.txt (100%) rename {chain-breaker/singleuser => singleuser}/config/jupyter_jupyterlab_server_config.py (100%) rename {chain-breaker/singleuser => singleuser}/config/jupyter_jupyternotebook_server_config.py (100%) rename {chain-breaker/singleuser => singleuser}/config/jupyter_server_config.py (100%) rename {chain-breaker/singleuser => singleuser}/config/pip_requirements.txt (100%) rename {chain-breaker/singleuser => singleuser}/docker-compose.yaml (100%) rename {chain-breaker/singleuser => singleuser}/example.env (100%) diff --git a/.github/workflows/singleuser-release.yaml b/.github/workflows/singleuser-release.yaml new file mode 100644 index 0000000..3e61bb6 --- /dev/null +++ b/.github/workflows/singleuser-release.yaml @@ -0,0 +1,28 @@ +name: singleuser-relase-wip +on: + workflow_dispatch: + inputs: + jupyterhub_version: + required: true + default: '4.0.2' + type: choice + options: + - '4.0.2' + - '1.4.2' +jobs: + release-image: + runs-on: ubuntu-latest + strategy: + matrix: + include: + # following python version end of life: https://devguide.python.org/versions/ + - py_ver: '3.10' + - py_ver: '3.9' + - py_ver: '3.8' + env: + PY_VER: ${{matrix.py_ver}} + JUPYTERHUB_VER: ${{github.event.inputs.jupyterhub_version}} + steps: + - uses: actions/checkout@v4 + # TODO - use docker build action + # TODO - use conventional commit action to make release and changelog diff --git a/.gitignore b/.gitignore index 18bb659..2eea525 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1 @@ -.vscode -*.tar.gz -docs/_build -sphinx-validator.yaml -common -home_user \ No newline at end of file +.env \ No newline at end of file diff --git a/chain-breaker/README.md b/README.md similarity index 100% rename from chain-breaker/README.md rename to README.md diff --git a/chain-breaker/.gitignore b/chain-breaker/.gitignore deleted file mode 100644 index 2eea525..0000000 --- a/chain-breaker/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.env \ No newline at end of file diff --git a/chain-breaker/hub/example.env b/chain-breaker/hub/example.env deleted file mode 100644 index e8c4099..0000000 --- a/chain-breaker/hub/example.env +++ /dev/null @@ -1,2 +0,0 @@ -# build -JUPYTERHUB_VERSION=4.0.2 \ No newline at end of file diff --git a/config/.env b/config/.env deleted file mode 100644 index 5dea230..0000000 --- a/config/.env +++ /dev/null @@ -1,34 +0,0 @@ -## build - -PY_VER='3.10' - -# PY_VER=3.9 - -# PY_VER=3.8 - -# PY_VER=3.7 - -# JUPYTERHUB_VER=1.4.2 -JUPYTERHUB_VER=4.0.2 - -BASE_IMAGE_HASH=eb6a760 - - - - -## test - -PLATFORM=linux/amd64 -# PLATFORM=linux/arm64/v8 -# PLATFORM=linux/arm64 - -HOST_UID=1000 - -# DISTRO=alpine -# PACKAGE_MANAGER=apk - -DISTRO=debian -PACKAGE_MANAGER=apt - -MYSQL_VER=8 -DJ_PASS=example diff --git a/chain-breaker/hub/Dockerfile b/hub/Dockerfile similarity index 100% rename from chain-breaker/hub/Dockerfile rename to hub/Dockerfile diff --git a/chain-breaker/hub/config/jupyterhub_config.py b/hub/config/jupyterhub_config.py similarity index 100% rename from chain-breaker/hub/config/jupyterhub_config.py rename to hub/config/jupyterhub_config.py diff --git a/chain-breaker/hub/docker-compose.yaml b/hub/docker-compose.yaml similarity index 100% rename from chain-breaker/hub/docker-compose.yaml rename to hub/docker-compose.yaml diff --git a/hub/example.env b/hub/example.env new file mode 100644 index 0000000..6391493 --- /dev/null +++ b/hub/example.env @@ -0,0 +1,7 @@ +# build +JUPYTERHUB_VERSION=4.0.2 + +OAUTH2_CLIENT_ID= +OAUTH2_CLIENT_SECRET= +# Need to generate by `openssl rand -hex 32` +JUPYTERHUB_CRYPT_KEY= \ No newline at end of file diff --git a/.github/workflows/development.yaml b/legacy/.github/workflows/development.yaml similarity index 100% rename from .github/workflows/development.yaml rename to legacy/.github/workflows/development.yaml diff --git a/legacy/.gitignore b/legacy/.gitignore new file mode 100644 index 0000000..18bb659 --- /dev/null +++ b/legacy/.gitignore @@ -0,0 +1,6 @@ +.vscode +*.tar.gz +docs/_build +sphinx-validator.yaml +common +home_user \ No newline at end of file diff --git a/README.rst b/legacy/README.rst similarity index 100% rename from README.rst rename to legacy/README.rst diff --git a/config/README.md b/legacy/config/README.md similarity index 100% rename from config/README.md rename to legacy/config/README.md diff --git a/config/datajoint_config.json b/legacy/config/datajoint_config.json similarity index 100% rename from config/datajoint_config.json rename to legacy/config/datajoint_config.json diff --git a/config/djlab_config.yaml b/legacy/config/djlab_config.yaml similarity index 100% rename from config/djlab_config.yaml rename to legacy/config/djlab_config.yaml diff --git a/config/jupyterhub_config.py b/legacy/config/jupyterhub_config.py similarity index 100% rename from config/jupyterhub_config.py rename to legacy/config/jupyterhub_config.py diff --git a/config/pip_requirements.txt b/legacy/config/pip_requirements.txt similarity index 100% rename from config/pip_requirements.txt rename to legacy/config/pip_requirements.txt diff --git a/dist/alpine/Dockerfile b/legacy/dist/alpine/Dockerfile similarity index 100% rename from dist/alpine/Dockerfile rename to legacy/dist/alpine/Dockerfile diff --git a/dist/alpine/apk_requirements.txt b/legacy/dist/alpine/apk_requirements.txt similarity index 100% rename from dist/alpine/apk_requirements.txt rename to legacy/dist/alpine/apk_requirements.txt diff --git a/dist/alpine/docker-compose.yaml b/legacy/dist/alpine/docker-compose.yaml similarity index 100% rename from dist/alpine/docker-compose.yaml rename to legacy/dist/alpine/docker-compose.yaml diff --git a/dist/debian/Dockerfile b/legacy/dist/debian/Dockerfile similarity index 100% rename from dist/debian/Dockerfile rename to legacy/dist/debian/Dockerfile diff --git a/dist/debian/apt_requirements.txt b/legacy/dist/debian/apt_requirements.txt similarity index 100% rename from dist/debian/apt_requirements.txt rename to legacy/dist/debian/apt_requirements.txt diff --git a/dist/debian/docker-compose.yaml b/legacy/dist/debian/docker-compose.yaml similarity index 100% rename from dist/debian/docker-compose.yaml rename to legacy/dist/debian/docker-compose.yaml diff --git a/docs/Makefile b/legacy/docs/Makefile similarity index 100% rename from docs/Makefile rename to legacy/docs/Makefile diff --git a/docs/conf.py b/legacy/docs/conf.py similarity index 100% rename from docs/conf.py rename to legacy/docs/conf.py diff --git a/docs/index.rst b/legacy/docs/index.rst similarity index 100% rename from docs/index.rst rename to legacy/docs/index.rst diff --git a/tests/main.sh b/legacy/tests/main.sh similarity index 100% rename from tests/main.sh rename to legacy/tests/main.sh diff --git a/utilities/add_entrypoint.sh b/legacy/utilities/add_entrypoint.sh similarity index 100% rename from utilities/add_entrypoint.sh rename to legacy/utilities/add_entrypoint.sh diff --git a/utilities/dj_config_backup.sh b/legacy/utilities/dj_config_backup.sh similarity index 100% rename from utilities/dj_config_backup.sh rename to legacy/utilities/dj_config_backup.sh diff --git a/chain-breaker/singleuser/Dockerfile b/singleuser/Dockerfile similarity index 98% rename from chain-breaker/singleuser/Dockerfile rename to singleuser/Dockerfile index 7106ec1..7b6ece9 100644 --- a/chain-breaker/singleuser/Dockerfile +++ b/singleuser/Dockerfile @@ -13,7 +13,6 @@ RUN \ # Add jupyter*config*.py && cp /tmp/config/jupyter*config*.py /etc/jupyter/ -USER ${NB_UID} ARG PYTHON_VERSION RUN \ # remove default work directory diff --git a/chain-breaker/singleuser/config/apt_install.sh b/singleuser/config/apt_install.sh similarity index 100% rename from chain-breaker/singleuser/config/apt_install.sh rename to singleuser/config/apt_install.sh diff --git a/chain-breaker/singleuser/config/before_start_hook.sh b/singleuser/config/before_start_hook.sh similarity index 100% rename from chain-breaker/singleuser/config/before_start_hook.sh rename to singleuser/config/before_start_hook.sh diff --git a/chain-breaker/singleuser/config/conda_requirements.txt b/singleuser/config/conda_requirements.txt similarity index 100% rename from chain-breaker/singleuser/config/conda_requirements.txt rename to singleuser/config/conda_requirements.txt diff --git a/chain-breaker/singleuser/config/jupyter_jupyterlab_server_config.py b/singleuser/config/jupyter_jupyterlab_server_config.py similarity index 100% rename from chain-breaker/singleuser/config/jupyter_jupyterlab_server_config.py rename to singleuser/config/jupyter_jupyterlab_server_config.py diff --git a/chain-breaker/singleuser/config/jupyter_jupyternotebook_server_config.py b/singleuser/config/jupyter_jupyternotebook_server_config.py similarity index 100% rename from chain-breaker/singleuser/config/jupyter_jupyternotebook_server_config.py rename to singleuser/config/jupyter_jupyternotebook_server_config.py diff --git a/chain-breaker/singleuser/config/jupyter_server_config.py b/singleuser/config/jupyter_server_config.py similarity index 100% rename from chain-breaker/singleuser/config/jupyter_server_config.py rename to singleuser/config/jupyter_server_config.py diff --git a/chain-breaker/singleuser/config/pip_requirements.txt b/singleuser/config/pip_requirements.txt similarity index 100% rename from chain-breaker/singleuser/config/pip_requirements.txt rename to singleuser/config/pip_requirements.txt diff --git a/chain-breaker/singleuser/docker-compose.yaml b/singleuser/docker-compose.yaml similarity index 100% rename from chain-breaker/singleuser/docker-compose.yaml rename to singleuser/docker-compose.yaml diff --git a/chain-breaker/singleuser/example.env b/singleuser/example.env similarity index 100% rename from chain-breaker/singleuser/example.env rename to singleuser/example.env From 30ddaed0816fcbd43a19b203abf4e216132f55f9 Mon Sep 17 00:00:00 2001 From: Drew Yang <31813282+yambottle@users.noreply.github.com> Date: Wed, 13 Mar 2024 17:29:43 -0500 Subject: [PATCH 13/15] Update hub/config/jupyterhub_config.py Co-authored-by: Ethan Ho <53266718+ethho@users.noreply.github.com> --- hub/config/jupyterhub_config.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hub/config/jupyterhub_config.py b/hub/config/jupyterhub_config.py index 42a84b2..368a4d5 100644 --- a/hub/config/jupyterhub_config.py +++ b/hub/config/jupyterhub_config.py @@ -73,7 +73,10 @@ c.JupyterHub.hub_ip = "" c.DockerSpawner.network_name = os.getenv("DOCKER_NETWORK_NAME", "jupyterhub_network") - +c.DockerSpawner.start_timeout = 60 +# https://github.com/jupyterhub/jupyterhub/issues/2913#issuecomment-580535422 +c.Spawner.http_timeout = 60 +c.Spawner.start_timeout = 60 c.DockerSpawner.container_image = "datajoint/djlabhub:singleuser-4.0.2-py3.10-qa" c.DockerSpawner.environment = { From 66f68a8eccac2579d1537727e3b0db473e61f886 Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Thu, 14 Mar 2024 10:57:02 -0500 Subject: [PATCH 14/15] =?UTF-8?q?fix:=20=F0=9F=90=9B=20remove=20conda?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- singleuser/Dockerfile | 8 ++------ singleuser/config/conda_requirements.txt | 2 -- singleuser/config/pip_requirements.txt | 2 ++ singleuser/docker-compose.yaml | 5 ++--- 4 files changed, 6 insertions(+), 11 deletions(-) delete mode 100644 singleuser/config/conda_requirements.txt diff --git a/singleuser/Dockerfile b/singleuser/Dockerfile index 7b6ece9..e9bb133 100644 --- a/singleuser/Dockerfile +++ b/singleuser/Dockerfile @@ -13,12 +13,8 @@ RUN \ # Add jupyter*config*.py && cp /tmp/config/jupyter*config*.py /etc/jupyter/ -ARG PYTHON_VERSION RUN \ # remove default work directory [ -d "/home/jovyan/work" ] && rm -r /home/jovyan/work \ - # Install dependencies: pip and conda - && conda install -y -n base python=${PYTHON_VERSION} \ - && pip install -r /tmp/config/pip_requirements.txt \ - && conda install --yes --file /tmp/config/conda_requirements.txt \ - && conda clean --all -f -y + # Install dependencies: pip + && pip install -r /tmp/config/pip_requirements.txt diff --git a/singleuser/config/conda_requirements.txt b/singleuser/config/conda_requirements.txt deleted file mode 100644 index ab697d3..0000000 --- a/singleuser/config/conda_requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -gh -jupyter-collaboration diff --git a/singleuser/config/pip_requirements.txt b/singleuser/config/pip_requirements.txt index e69de29..aaab5ca 100644 --- a/singleuser/config/pip_requirements.txt +++ b/singleuser/config/pip_requirements.txt @@ -0,0 +1,2 @@ +gh +jupyter-collaboration \ No newline at end of file diff --git a/singleuser/docker-compose.yaml b/singleuser/docker-compose.yaml index afdee80..cbed5e9 100644 --- a/singleuser/docker-compose.yaml +++ b/singleuser/docker-compose.yaml @@ -5,9 +5,8 @@ services: context: . dockerfile: Dockerfile args: - - PYTHON_VERSION - JUPYTERHUB_VERSION - image: datajoint/djlabhub:singleuser-${JUPYTERHUB_VERSION}-py${PYTHON_VERSION} + image: datajoint/djlabhub:singleuser-${JUPYTERHUB_VERSION} container_name: djlabhub-singleuser env_file: .env ports: @@ -19,4 +18,4 @@ services: djlab: extends: singleuser container_name: djlab - image: datajoint/djlab:py${PYTHON_VERSION} + image: datajoint/djlab:inherit-hub-${JUPYTERHUB_VERSION} From 684f913d715c75f2c03dfde1eace8086c22e3440 Mon Sep 17 00:00:00 2001 From: Drew Yang Date: Thu, 14 Mar 2024 12:08:04 -0500 Subject: [PATCH 15/15] =?UTF-8?q?fix:=20=F0=9F=90=9B=20unreleased=20python?= =?UTF-8?q?=20dependency=20for=20the=20base=20image=20python=20version=20r?= =?UTF-8?q?equires=20gcc=20to=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- singleuser/config/apt_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/singleuser/config/apt_install.sh b/singleuser/config/apt_install.sh index 11dc477..33e15fd 100644 --- a/singleuser/config/apt_install.sh +++ b/singleuser/config/apt_install.sh @@ -3,7 +3,7 @@ sudo apt update # Install -sudo apt-get install mysql-client -y --no-install-recommends +sudo apt-get install mysql-client gcc -y --no-install-recommends sudo apt-get clean rm -rf /var/lib/apt/lists/*