async def test_start_stop(dockerspawner_configured_app): app = dockerspawner_configured_app name = "has@" add_user(app.db, app, name=name) user = app.users[name] server_name = 'also-has@' spawner = user.spawners[server_name] assert isinstance(spawner, DockerSpawner) token = user.new_api_token() # start the server r = await api_request(app, "users", name, "servers", server_name, method="post") pending = r.status_code == 202 while pending: # request again r = await api_request(app, "users", name) user_info = r.json() pending = user_info["servers"][server_name]["pending"] assert r.status_code in {201, 200}, r.text url = url_path_join(public_url(app, user), server_name, "api/status") resp = await AsyncHTTPClient().fetch( url, headers={"Authorization": "token %s" % token} ) assert resp.effective_url == url resp.rethrow() assert "kernels" in resp.body.decode("utf-8")
def test_image_whitelist(app, image): name = "checker" add_user(app.db, app, name=name) user = app.users[name] assert isinstance(user.spawner, DockerSpawner) user.spawner.remove_containers = True user.spawner.image_whitelist = { "0.9": "jupyterhub/singleuser:0.9", "0.8": "jupyterhub/singleuser:0.8", } token = user.new_api_token() # start the server r = yield api_request( app, "users", name, "server", method="post", data=json.dumps({"image": image}) ) if image not in user.spawner.image_whitelist: with pytest.raises(Exception): r.raise_for_status() return while r.status_code == 202: # request again r = yield api_request(app, "users", name, "server", method="post") yield gen.sleep(0.1) assert r.status_code == 201, r.text url = url_path_join(public_url(app, user), "api/status") r = yield async_requests.get(url, headers={"Authorization": "token %s" % token}) r.raise_for_status() assert r.headers['x-jupyterhub-version'].startswith(image) r = yield api_request( app, "users", name, "server", method="delete", ) r.raise_for_status()
async def test_post_start(dockerspawner_configured_app, caplog): app = dockerspawner_configured_app name = "post-start" add_user(app.db, app, name=name) user = app.users[name] spawner = user.spawners[''] log_name = "dockerspawner" spawner.log = logging.getLogger(log_name) spawner.remove = True # mock out ip and port, no need for it async def mock_ip_port(): return ("127.0.0.1", 1234) spawner.get_ip_and_port = mock_ip_port spawner.image = "busybox:1.29.1" spawner.cmd = ["sh", "-c", "sleep 300"] spawner.post_start_cmd = "ls /" # verify that it's called during startup finished_future = asyncio.Future() finished_future.set_result(None) mock_post_start = mock.Mock(return_value=finished_future) with mock.patch.object(spawner, 'post_start_exec', mock_post_start): await spawner.start() mock_post_start.assert_called_once() # verify log capture for 3 combinations: # - success # - failure # - no such command (different failure) for (cmd, expected_stdout, expected_stderr) in [ ("true", False, False), ("ls /", True, False), ("ls /nosuchfile", False, True), ("nosuchcommand", False, True), ("echo", False, False), ]: spawner.post_start_cmd = cmd idx = len(caplog.records) with caplog.at_level(logging.DEBUG, log_name): await spawner.post_start_exec() logged = "\n".join( f"{rec.levelname}: {rec.message}" for rec in caplog.records[idx:] ) if expected_stdout: assert "DEBUG: post_start stdout" in logged else: assert "post_start stdout" not in logged if expected_stderr: assert "WARNING: post_start stderr" in logged else: assert "post_start stderr" not in logged await spawner.stop()
async def test_image_pull_policy(dockerspawner_configured_app): app = dockerspawner_configured_app name = "gumby" add_user(app.db, app, name=name) user = app.users[name] assert isinstance(user.spawner, DockerSpawner) spawner = user.spawners[""] spawner.image = "jupyterhub/doesntexist:nosuchtag" with pytest.raises(docker.errors.NotFound): spawner.image_pull_policy = "never" await spawner.pull_image(spawner.image) repo = "busybox" tag = "1.29.1" # a version that's definitely not latest # ensure image isn't present try: await asyncio.wrap_future( spawner.docker("remove_image", "{}:{}".format(repo, tag))) except docker.errors.ImageNotFound: pass spawner.pull_policy = "ifnotpresent" image = "{}:{}".format(repo, tag) # should trigger a pull await spawner.pull_image(image) # verify that the image exists now old_image_info = await asyncio.wrap_future( spawner.docker("inspect_image", image)) print(old_image_info) # now tag busybox:latest as our current version # which is not latest! await asyncio.wrap_future(spawner.docker("tag", image, repo)) image = repo # implicit :latest spawner.pull_policy = "ifnotpresent" # check with ifnotpresent shouldn't pull await spawner.pull_image(image) image_info = await asyncio.wrap_future( spawner.docker("inspect_image", repo)) assert image_info["Id"] == old_image_info["Id"] # run again with Always, # should trigger a pull even though the image is present spawner.pull_policy = "always" spawner.pull_image(image) image_info = await asyncio.wrap_future( spawner.docker("inspect_image", repo)) assert image_info["Id"] != old_image_info["Id"] # run again with never, make sure it's still happy spawner.pull_policy = "never" spawner.pull_image(image)
def test_name_collision(dockerspawner_configured_app): app = dockerspawner_configured_app has_hyphen = "user--foo" add_user(app.db, app, name=has_hyphen) user = app.users[has_hyphen] spawner1 = user.spawners[""] assert isinstance(spawner1, DockerSpawner) assert spawner1.object_name == "{}-{}".format(spawner1.prefix, has_hyphen) part1, part2 = ["user", "foo"] add_user(app.db, app, name=part1) user2 = app.users[part1] spawner2 = user2.spawners[part2] assert spawner1.object_name != spawner2.object_name
def test_image_pull_policy(app): name = "gumby" add_user(app.db, app, name=name) user = app.users[name] assert isinstance(user.spawner, DockerSpawner) spawner = user.spawners[""] spawner.image = "jupyterhub/doesntexist:nosuchtag" with pytest.raises(docker.errors.NotFound): spawner.image_pull_policy = "never" yield spawner.pull_image(spawner.image) repo = "busybox" tag = "1.29.1" # a version that's definitely not latest # ensure image isn't present try: yield spawner.docker("remove_image", "{}:{}".format(repo, tag)) except docker.errors.ImageNotFound: pass spawner.pull_policy = "ifnotpresent" image = "{}:{}".format(repo, tag) # should trigger a pull yield spawner.pull_image(image) # verify that the image exists now old_image_info = yield spawner.docker("inspect_image", image) print(old_image_info) # now tag busybox:latest as our current version # which is not latest! yield spawner.docker("tag", image, repo) image = repo # implicit :latest spawner.pull_policy = "ifnotpresent" # check with ifnotpresent shouldn't pull yield spawner.pull_image(image) image_info = yield spawner.docker("inspect_image", repo) assert image_info["Id"] == old_image_info["Id"] # run again with Always, # should trigger a pull even though the image is present spawner.pull_policy = "always" spawner.pull_image(image) image_info = yield spawner.docker("inspect_image", repo) assert image_info["Id"] != old_image_info["Id"] # run again with never, make sure it's still happy spawner.pull_policy = "never" spawner.pull_image(image)
async def test_integration(skein_client, app): with clean_cluster(skein_client): # Create a user add_user(app.db, app, name="alice") alice = app.users["alice"] assert isinstance(alice.spawner, YarnSpawner) token = alice.new_api_token() # Not started, status should be 0 status = await alice.spawner.poll() assert status == 0 # Stop can be called before start, no-op await alice.spawner.stop() # Start the server, and wait for it to start resp = None while resp is None or resp.status_code == 202: await gen.sleep(2.0) resp = await api_request(app, "users", "alice", "server", method="post") # Check that everything is running fine url = url_path_join(public_url(app, alice), "api/status") resp = await async_requests.get( url, headers={'Authorization': 'token %s' % token}) resp.raise_for_status() assert "kernels" in resp.json() # Save the app_id to use later app_id = alice.spawner.app_id # Shutdown the server resp = await api_request(app, "users", "alice", "server", method="delete") resp.raise_for_status() assert_shutdown_in(skein_client, app_id, timeout=10) # Check status status = await alice.spawner.poll() assert status == 0
async def test_allowed_image(dockerspawner_configured_app, image): app = dockerspawner_configured_app name = "checker" add_user(app.db, app, name=name) user = app.users[name] assert isinstance(user.spawner, DockerSpawner) user.spawner.remove_containers = True user.spawner.allowed_images = { "1.0": "jupyterhub/singleuser:1.0", "1.1": "jupyterhub/singleuser:1.1", } token = user.new_api_token() # start the server r = await api_request(app, "users", name, "server", method="post", data=json.dumps({"image": image})) if image not in user.spawner.allowed_images: with pytest.raises(Exception): r.raise_for_status() return pending = r.status_code == 202 while pending: # request again r = await api_request(app, "users", name) user_info = r.json() pending = user_info["servers"][""]["pending"] url = url_path_join(public_url(app, user), "api/status") resp = await AsyncHTTPClient().fetch( url, headers={"Authorization": "token %s" % token}) assert resp.effective_url == url resp.rethrow() assert resp.headers['x-jupyterhub-version'].startswith(image) r = await api_request( app, "users", name, "server", method="delete", ) r.raise_for_status()
def test_start_stop(app): name = "somebody" add_user(app.db, app, name=name) user = app.users[name] assert isinstance(user.spawner, SwarmSpawner) token = user.new_api_token() # start the server r = yield api_request(app, "users", name, "server", method="post") while r.status_code == 202: # request again r = yield api_request(app, "users", name, "server", method="post") assert r.status_code == 201, r.text url = url_path_join(public_url(app, user), "api/status") r = yield async_requests.get(url, headers={"Authorization": "token %s" % token}) assert r.url == url r.raise_for_status() print(r.text) assert "kernels" in r.json()
def test_start_stop(app): name = getuser() add_user(app.db, app, name=name) user = app.users[name] assert isinstance(user.spawner, SystemUserSpawner) token = user.new_api_token() # start the server r = yield api_request(app, "users", name, "server", method="post") while r.status_code == 202: # request again r = yield api_request(app, "users", name, "server", method="post") assert r.status_code == 201, r.text url = url_path_join(public_url(app, user), "api/status") r = yield async_requests.get(url, headers={"Authorization": "token %s" % token}) assert r.url == url r.raise_for_status() print(r.text) assert "kernels" in r.json()
async def test_start_stop(systemuserspawner_configured_app): app = systemuserspawner_configured_app name = getuser() add_user(app.db, app, name=name) user = app.users[name] assert isinstance(user.spawner, SystemUserSpawner) token = user.new_api_token() # start the server r = await api_request(app, "users", name, "server", method="post") while r.status_code == 202: # request again r = await api_request(app, "users", name, "server", method="post") assert r.status_code == 201, r.text url = url_path_join(public_url(app, user), "api/status") resp = await AsyncHTTPClient().fetch(url, headers={"Authorization": "token %s" % token}) assert resp.effective_url == url resp.rethrow() assert "kernels" in resp.body.decode("utf-8")
def test_image_whitelist(app, image): name = "checker" add_user(app.db, app, name=name) user = app.users[name] assert isinstance(user.spawner, DockerSpawner) user.spawner.remove_containers = True user.spawner.image_whitelist = { "0.9": "jupyterhub/singleuser:0.9", "0.8": "jupyterhub/singleuser:0.8", } token = user.new_api_token() # start the server r = yield api_request(app, "users", name, "server", method="post", data=json.dumps({"image": image})) if image not in user.spawner.image_whitelist: with pytest.raises(Exception): r.raise_for_status() return while r.status_code == 202: # request again r = yield api_request(app, "users", name, "server", method="post") yield gen.sleep(0.1) assert r.status_code == 201, r.text url = url_path_join(public_url(app, user), "api/status") r = yield async_requests.get(url, headers={"Authorization": "token %s" % token}) r.raise_for_status() assert r.headers['x-jupyterhub-version'].startswith(image) r = yield api_request( app, "users", name, "server", method="delete", ) r.raise_for_status()
async def test_start_stop(swarmspawner_app): username = "******" server_name = "also-has@" app = swarmspawner_app add_user(app.db, app, name=username) user = app.users[username] spawner = user.spawners[server_name] assert isinstance(spawner, SwarmSpawner) token = user.new_api_token() # Start the server r = await api_request(app, "users", username, "servers", server_name, method="post") pending = r.status_code == 202 while pending: # Request again r = await api_request(app, "users", username) user_info = r.json() pending = user_info["servers"][server_name]["pending"] assert r.status_code in {201, 200}, r.text
async def test_integration(app, auto_login, logged_in): app.authenticator.auto_login = auto_login # Create a user add_user(app.db, app, name="alice") if auto_login: url = public_url(app, path="/hub/login") resp = await async_requests.get(url) # Sends back 401 requesting authentication assert resp.status_code == 401 # 401 page is formatted nicely assert "Failed to login with Kerberos." in resp.text assert resp.text.count("/hub/login") >= 2 # Before that was a redirect to the auth handler assert resp.history[0].status_code == 302 # Now use the redirected url with auth enabled location = resp.history[0].headers['location'] netloc = urlparse(app.bind_url).netloc url = 'http://%s%s' % (netloc, location) else: url = public_url(app, path="/hub/kerberos_login") # Go through the login procedure resp = await async_requests.get( url, auth=HTTPKerberosAuth(hostname_override="address.example.com")) if logged_in: # Successful resp.raise_for_status() # At user notebook, login successful assert resp.url.startswith(public_url(app, path="/user/alice")) else: # Unsuccessful assert resp.status_code == 401