mirror of
https://github.com/jupyter/docker-stacks.git
synced 2025-10-13 21:12:57 +00:00
Refactor to share fixtures with option tests
This commit is contained in:
@@ -1,16 +1,38 @@
|
|||||||
# Copyright (c) Jupyter Development Team.
|
# Copyright (c) Jupyter Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
|
import time
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip('placeholder')
|
def test_cli_args(container, http_client):
|
||||||
def test_cli_args():
|
"""Container should respect notebook server command line args
|
||||||
pass
|
(e.g., disabling token security)"""
|
||||||
|
container.run(
|
||||||
|
command=['start-notebook.sh', '--NotebookApp.token=""']
|
||||||
|
)
|
||||||
|
resp = http_client.get('http://localhost:8888')
|
||||||
|
resp.raise_for_status()
|
||||||
|
assert 'login_submit' not in resp.text
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip('placeholder')
|
@pytest.mark.filterwarnings('ignore:Unverified HTTPS request')
|
||||||
def test_unsigned_ssl():
|
def test_unsigned_ssl(container, http_client):
|
||||||
pass
|
"""Container should generate a self-signed SSL certificate
|
||||||
|
and notebook server should use it to enable HTTPS.
|
||||||
|
"""
|
||||||
|
c = container.run(
|
||||||
|
environment=['GEN_CERT=yes']
|
||||||
|
)
|
||||||
|
# NOTE: The requests.Session backing the http_client fixture does not retry
|
||||||
|
# properly while the server is booting up. An SSL handshake error seems to
|
||||||
|
# abort the retry logic. Forcing a long sleep for the moment until I have
|
||||||
|
# time to dig more.
|
||||||
|
time.sleep(5)
|
||||||
|
resp = http_client.get('https://localhost:8888', verify=False)
|
||||||
|
resp.raise_for_status()
|
||||||
|
assert 'login_submit' in resp.text
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip('placeholder')
|
@pytest.mark.skip('placeholder')
|
||||||
def test_uid_change():
|
def test_uid_change():
|
||||||
|
101
conftest.py
Normal file
101
conftest.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# Copyright (c) Jupyter Development Team.
|
||||||
|
# Distributed under the terms of the Modified BSD License.
|
||||||
|
import os
|
||||||
|
|
||||||
|
import docker
|
||||||
|
import pytest
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from requests.packages.urllib3.util.retry import Retry
|
||||||
|
from requests.adapters import HTTPAdapter
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def http_client():
|
||||||
|
"""Requests session with retries and backoff."""
|
||||||
|
s = requests.Session()
|
||||||
|
retries = Retry(total=5, backoff_factor=1)
|
||||||
|
s.mount('http://', HTTPAdapter(max_retries=retries))
|
||||||
|
s.mount('https://', HTTPAdapter(max_retries=retries))
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def docker_client():
|
||||||
|
"""Docker client configured based on the host environment"""
|
||||||
|
return docker.from_env()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def image_name():
|
||||||
|
"""Image name to test"""
|
||||||
|
return os.getenv('TEST_IMAGE', 'jupyter/base-notebook')
|
||||||
|
|
||||||
|
|
||||||
|
class TrackedContainer(object):
|
||||||
|
"""Wrapper that collects docker container configuration and delays
|
||||||
|
container creation/execution.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
docker_client: docker.DockerClient
|
||||||
|
Docker client instance
|
||||||
|
image_name: str
|
||||||
|
Name of the docker image to launch
|
||||||
|
**kwargs: dict, optional
|
||||||
|
Default keyword arguments to pass to docker.DockerClient.containers.run
|
||||||
|
"""
|
||||||
|
def __init__(self, docker_client, image_name, **kwargs):
|
||||||
|
self.container = None
|
||||||
|
self.docker_client = docker_client
|
||||||
|
self.image_name = image_name
|
||||||
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
def run(self, **kwargs):
|
||||||
|
"""Runs a docker container using the preconfigured image name
|
||||||
|
and a mix of the preconfigured container options and those passed
|
||||||
|
to this method.
|
||||||
|
|
||||||
|
Keeps track of the docker.Container instance spawned to kill it
|
||||||
|
later.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
**kwargs: dict, optional
|
||||||
|
Keyword arguments to pass to docker.DockerClient.containers.run
|
||||||
|
extending and/or overriding key/value pairs passed to the constructor
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
docker.Container
|
||||||
|
"""
|
||||||
|
all_kwargs = {}
|
||||||
|
all_kwargs.update(self.kwargs)
|
||||||
|
all_kwargs.update(kwargs)
|
||||||
|
self.container = self.docker_client.containers.run(self.image_name, **all_kwargs)
|
||||||
|
return self.container
|
||||||
|
|
||||||
|
def kill(self):
|
||||||
|
"""Kills the tracked docker container."""
|
||||||
|
if self.container:
|
||||||
|
self.container.kill()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def container(docker_client, image_name):
|
||||||
|
"""Notebook container with initial configuration appropriate for testing
|
||||||
|
(e.g., HTTP port exposed to the host for HTTP calls).
|
||||||
|
|
||||||
|
Yields the container instance and kills it when the caller is done with it.
|
||||||
|
"""
|
||||||
|
container = TrackedContainer(
|
||||||
|
docker_client,
|
||||||
|
image_name,
|
||||||
|
detach=True,
|
||||||
|
auto_remove=False,
|
||||||
|
ports={
|
||||||
|
'8888/tcp': 8888
|
||||||
|
}
|
||||||
|
)
|
||||||
|
yield container
|
||||||
|
container.kill()
|
@@ -1,52 +1,9 @@
|
|||||||
# Copyright (c) Jupyter Development Team.
|
# Copyright (c) Jupyter Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
import docker
|
def test_secured_server(container, http_client):
|
||||||
import pytest
|
"""Notebook server should eventually request user login."""
|
||||||
import requests
|
container.run()
|
||||||
|
resp = http_client.get('http://localhost:8888')
|
||||||
|
resp.raise_for_status()
|
||||||
@pytest.fixture(scope='session')
|
assert 'login_submit' in resp.text
|
||||||
def docker_client():
|
|
||||||
"""Docker client to use"""
|
|
||||||
return docker.from_env()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
|
||||||
def image_name():
|
|
||||||
"""Image name to test"""
|
|
||||||
return os.getenv('TEST_IMAGE', 'jupyter/base-notebook')
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function')
|
|
||||||
def nb_container(docker_client, image_name):
|
|
||||||
"""Notebook container to test"""
|
|
||||||
container = docker_client.containers.run(
|
|
||||||
image_name,
|
|
||||||
detach=True,
|
|
||||||
auto_remove=True,
|
|
||||||
ports={
|
|
||||||
'8888/tcp': 8888
|
|
||||||
}
|
|
||||||
)
|
|
||||||
yield container
|
|
||||||
container.kill()
|
|
||||||
|
|
||||||
|
|
||||||
def test_server_liveliness(nb_container):
|
|
||||||
"""Notebook server should eventually respond with HTTP 200 OK."""
|
|
||||||
for i in range(10):
|
|
||||||
try:
|
|
||||||
resp = requests.get('http://localhost:8888')
|
|
||||||
except requests.exceptions.ConnectionError:
|
|
||||||
# Wait a bit and try again. Just because the docker container
|
|
||||||
# is running doesn't mean the notebook server is ready to accept
|
|
||||||
# connections inside it.
|
|
||||||
time.sleep(i)
|
|
||||||
else:
|
|
||||||
assert resp.status_code == 200
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
assert False, 'could not connect to server'
|
|
||||||
|
Reference in New Issue
Block a user