Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to JupyterHub 4 and TLJH 1.0, use pytest-jupyterhub #65

Merged
merged 9 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ jobs:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10"]
jupyterhub-version: [latest, 1.2.2]

steps:
- uses: actions/checkout@v2
Expand All @@ -32,10 +31,6 @@ jobs:
python -m pip install --upgrade pip setuptools
python -m pip install -r dev-requirements.txt
python -m pip install -e .
- name: Downgrade jupyterhub
if: ${{ matrix.jupyterhub-version != 'latest' }}
run: |
python -m pip install -U 'jupyterhub==${{ matrix.jupyterhub-version }}'
- name: Run Tests
run: |
python -m pytest --cov
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

TLJH plugin to build and use Docker images as user environments. The Docker images are built using [`repo2docker`](https://repo2docker.readthedocs.io/en/latest/).

## Requirements

This plugin requires [The Littlest JupyterHub](https://tljh.jupyter.org) 1.0 or later (running on JupyterHub 4+).

## Installation

During the [TLJH installation process](http://tljh.jupyter.org/en/latest/install/index.html), use the following post-installation script:
Expand All @@ -20,17 +24,17 @@ sudo apt update && sudo apt install -y docker-ce
# pull the repo2docker image
sudo docker pull quay.io/jupyterhub/repo2docker:main

# install TLJH
curl https://raw.githubusercontent.com/jupyterhub/the-littlest-jupyterhub/master/bootstrap/bootstrap.py \
# install TLJH 1.0
curl https://tljh.jupyter.org/bootstrap.py
| sudo python3 - \
--version 1.0.0 \
--admin test:test \
--plugin git+https://github.com/plasmabio/tljh-repo2docker@master
```

Refer to [The Littlest JupyterHub documentation](http://tljh.jupyter.org/en/latest/topic/customizing-installer.html?highlight=plugins#installing-tljh-plugins)
for more info on installing TLJH plugins.


## Usage

### List the environments
Expand Down
6 changes: 3 additions & 3 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
git+https://github.com/jupyterhub/the-littlest-jupyterhub
jupyterhub~=1.5
notebook<7
git+https://github.com/jupyterhub/[email protected]
jupyterhub>=4,<5
pytest
pytest-aiohttp
pytest-asyncio
pytest-cov
pytest-jupyterhub
requests-mock
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ license = { file = "LICENSE" }
dependencies = [
"aiodocker~=0.19",
"dockerspawner~=12.1",
"jupyter_client~=6.1",
"sqlalchemy<2",
"jupyter_client>=6.1,<8"
]

[project.entry-points.tljh]
Expand Down
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[pytest]
python_files = test_*.py
asyncio_mode = auto
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__import__("setuptools").setup()
__import__("setuptools").setup()
6 changes: 3 additions & 3 deletions tljh_repo2docker/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from aiodocker import Docker, DockerError
from jupyterhub.apihandlers import APIHandler
from jupyterhub.utils import admin_only
from jupyterhub.scopes import needs_scope
from tornado import web

from .docker import build_image
Expand All @@ -17,7 +17,7 @@ class BuildHandler(APIHandler):
"""

@web.authenticated
@admin_only
@needs_scope('admin-ui')
async def delete(self):
data = self.get_json_body()
name = data["name"]
Expand All @@ -31,7 +31,7 @@ async def delete(self):
self.finish(json.dumps({"status": "ok"}))

@web.authenticated
@admin_only
@needs_scope('admin-ui')
async def post(self):
data = self.get_json_body()
repo = data["repo"]
Expand Down
4 changes: 2 additions & 2 deletions tljh_repo2docker/images.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from inspect import isawaitable
from jupyterhub.handlers.base import BaseHandler
from jupyterhub.utils import admin_only
from jupyterhub.scopes import needs_scope
from tornado import web

from .docker import list_containers, list_images
Expand All @@ -12,7 +12,7 @@ class ImagesHandler(BaseHandler):
"""

@web.authenticated
@admin_only
@needs_scope('admin-ui')
async def get(self):
images = await list_images()
containers = await list_containers()
Expand Down
5 changes: 2 additions & 3 deletions tljh_repo2docker/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

from aiodocker import Docker
from jupyterhub.apihandlers import APIHandler
from jupyterhub.utils import admin_only
from jupyterhub.scopes import needs_scope
from tornado import web
from tornado.ioloop import IOLoop
from tornado.iostream import StreamClosedError


Expand All @@ -13,7 +12,7 @@ class LogsHandler(APIHandler):
Expose a handler to follow the build logs.
"""
@web.authenticated
@admin_only
@needs_scope('admin-ui')
async def get(self, name):
self.set_header("Content-Type", "text/event-stream")
self.set_header("Cache-Control", "no-cache")
Expand Down
65 changes: 11 additions & 54 deletions tljh_repo2docker/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,8 @@
import asyncio
import os
import sys

import pytest

from aiodocker import Docker, DockerError
from jupyterhub.tests.conftest import (
io_loop,
event_loop,
db,
pytest_collection_modifyitems,
)
from jupyterhub.tests.mocking import MockHub
from tljh_repo2docker import tljh_custom_jupyterhub_config
from traitlets import Bunch

class DummyConfig:
def __getattr__(self, k):
if k not in self.__dict__:
self.__dict__[k] = Bunch()
return self.__dict__[k]
from traitlets.config import Config


async def remove_docker_image(image_name):
Expand Down Expand Up @@ -50,43 +33,17 @@ def image_name():
return "tljh-repo2docker-test:HEAD"


@pytest.fixture(scope='module')
def app(request, io_loop):
"""
Adapted from:
https://github.com/jupyterhub/jupyterhub/blob/8a3790b01ff944c453ffcc0486149e2a58ffabea/jupyterhub/tests/conftest.py#L74
"""

mocked_app = MockHub.instance()
c = DummyConfig()
c.JupyterHub = mocked_app
tljh_custom_jupyterhub_config(c)

async def make_app():
await mocked_app.initialize([])
await mocked_app.start()

def fin():
# disconnect logging during cleanup because pytest closes captured FDs prematurely
mocked_app.log.handlers = []
MockHub.clear_instance()
try:
mocked_app.stop()
except Exception as e:
print("Error stopping Hub: %s" % e, file=sys.stderr)
@pytest.fixture
async def app(hub_app):
config = Config()
tljh_custom_jupyterhub_config(config)

request.addfinalizer(fin)
io_loop.run_sync(make_app)
return mocked_app
app = await hub_app(config=config)
return app


@pytest.fixture(autouse=True)
def remove_all_test_images(image_name, generated_image_name, io_loop):
try:
yield
finally:
async def _clean():
await remove_docker_image(image_name)
await remove_docker_image(generated_image_name)

io_loop.run_sync(_clean)
async def remove_all_test_images(image_name, generated_image_name, app):
yield
await remove_docker_image(image_name)
await remove_docker_image(generated_image_name)
Loading