-
Notifications
You must be signed in to change notification settings - Fork 7
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
Refactor code to be more testable #22
base: master
Are you sure you want to change the base?
Conversation
We should shut down binder instance when we are done, to conserve resources and prevent ourselves from hitting usage limits. Ideally, the token received from BinderHub can be used to call the JupyterHub API to shut it down. However, the token we get from BinderHub isn't a valid JupyterHub token - just a valid Jupyter Notebook token. All JupyterHub tokens are valid Jupyter Notebook tokens, but not all Jupyter Notebook tokens are valid JupyterHub tokens! This was a decision we made early on in the process so users didn't need to have 'jupyterhub' installed inside their containers. We instead kill pid 1, which stops the container. JupyterHub should garbage collect this shortly after. This is very hacky, but works Fixes pangeo-gallery#3
BinderBot class contained two bits of functionality: 1. Starting and stopping binders on a BinderHub 2. Interacting with the notebook API (start kernels, contentsmanager, etc) This commit splits them into two different classes. This has the big advantage that we can run unit tests against a local instance of jupyter notebooks, rather than starting an instance on mybinder.org all the time. Tests are much faster, and everything works as normal since it is a straight up split We added more unit tests, but lost the CLI test. Most of its functionality is covered by the other unit tests, but it would be nice to have an integration test
Each NotebookClient can now produce multiple kernels, thus matching the notebook REST API. These can be used with async context managers, so we always clean up the kernel when done. We move OperationError out to break cyclic imports
13ac3d4
to
37f7abf
Compare
The notebook server is gone, binder can not shut down
Tested the CLI with commands for https://github.com/TomAugspurger/pangeo-dask-gateway and it seemed to work. |
This was present as a sanity-check mechanism in hubtraf, not needed here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems great Yuvi! A huge improvement over what I had hacked together. I caught one important feature that appears to have avoided testing, which I recommend we add back.
So do you think it is not necessary to have an integration test for the CLI?
await kernel.execute_notebook( | ||
fname, | ||
timeout=60, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little concerned here that the feature to pass environment variables is not being tested any more. It would be easy to cover this feature by adding env_vars
to the call to execute_notebook
.
Or maybe a separate test for that feature would be best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rabernat good catch. I'll add another unit test for it.
@rabernat for CLIs I generally prefer Integration tests rather than unit tests. So we'll move as much of CLI functionality to functions that can be unit tested, and then write integration tests that drive the CLI. |
Yuvi, I am working on binderbot again after a long hiatus. (My goal is to fix http://gallery.pangeo.io/, which is currently broken due to Pangeo Binder being down.) I'd like to get this PR merged. My plan is to just resolve the conflicts manually and keep your changes. But before doing that I thought I'd ping you again since it has been almost 2 years! 🙃 |
@rabernat wow, it has really been two years. Damn. Yes, please do what you can :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if there's still appetite to revive binderbot, but here's a few suggestions to maybe nudge things along 🙂
Context is that I'd be keen to see #35 fixed, which is partly an issue on 1) handling changes to the Binder API that is returned different HTML responses compared to a year or two ago and 2) dealing with oauth tokens required for the aws-uswest2 Pangeo Binder. This PR will probably only deal with (1) if I'm not mistaken.
@@ -54,7 +35,6 @@ def __init__(self, binder_url, repo, ref): | |||
self.binder_url = URL(binder_url) | |||
self.repo = repo | |||
self.ref = ref | |||
self.state = BinderUser.States.CLEAR | |||
self.log = logger.bind() | |||
|
|||
async def start_binder(self, timeout=3000, spawn_refresh_time=20): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At L45 below, need to change build/gh
to v2/gh
.
@@ -90,215 +70,21 @@ async def start_binder(self, timeout=3000, spawn_refresh_time=20): | |||
self.log.msg(f'Binder: Waiting on event stream (phase: {phase})', action='binder-start', phase='event-stream') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest checking that the notebook_url
and token
variables are set before proceeding to the next piece of logic. This will help a little bit with debugging #35.
self.log.msg(f'Binder: Waiting on event stream (phase: {phase})', action='binder-start', phase='event-stream') | |
self.log.msg(f'Binder: Waiting on event stream (phase: {phase})', action='binder-start', phase='event-stream') | |
try: | |
self.notebook_url | |
self.token | |
except AttributeError as e: | |
raise OperationError from e |
@@ -8,6 +8,7 @@ | |||
import nbformat | |||
|
|||
from .binderbot import BinderUser | |||
from .notebookclient import NotebookClient | |||
|
|||
# https://github.com/pallets/click/issues/85#issuecomment-43378930 | |||
def coro(f): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default binder URL https://binder.pangeo.io
at L22 below doesn't work anymore. Point to https://aws-uswest2-binder.pangeo.io
instead? Or wait for a new Binder deployment from 2i2c-org/infrastructure#919 🙂
@@ -54,7 +35,6 @@ def __init__(self, binder_url, repo, ref): | |||
self.binder_url = URL(binder_url) | |||
self.repo = repo | |||
self.ref = ref | |||
self.state = BinderUser.States.CLEAR | |||
self.log = logger.bind() | |||
|
|||
async def start_binder(self, timeout=3000, spawn_refresh_time=20): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At L54 below, I'm not quite sure if the if line.startswith('data:'):
statement works anymore. The HTML response I'm getting when testing with https://mybinder.org looks like:
<meta id="badge-base-url" data-url="https://mybinder.org/">
<meta id="build-token" data-token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpxVCJ9.eyJleHAiOjE2Njg4MzA3MTksImF1ZCI6ImdoL2dlby1zbWFydC9kZWVwaWNlZHJhaW5waXBlL3RlbXAtaXB5bmIiLCJvcmlnaW4iOiJteWJpbmRlci5vcmcifQ.pALJ9hVdHdq7-YbgIYkMU2lcdeu7MphArjnw38_DbKc">
so maybe the if-statement should be finding data-url
and data-token
instead?
This PR refactors most of the code into three classes:
REST API
Jupyter Notebook server.
This has many advantages:
NotebookClient
andKernel
againsta local ephemeral instance of Notebook Server, making it much
faster.
server, matching what the REST API does.
This PR adds unit tests, and implements functionality to shut
down binders when we are done with them (#3). We also remove
the state enum asserts in the code - they were a sanity-check
mechanism in hubtraf, and error handling should be implemented
differently here.