-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Verify port number in JUPYTERHUB_SERVICE_URL #4310
base: main
Are you sure you want to change the base?
Conversation
@@ -56,6 +56,7 @@ def new_spawner(db, **kwargs): | |||
kwargs.setdefault('term_timeout', 1) | |||
kwargs.setdefault('kill_timeout', 1) | |||
kwargs.setdefault('poll_interval', 1) | |||
kwargs.setdefault('port', 5555) |
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.
Since starting a new spawner will generally not be passed a port, we shouldn't need a port here. If this is required for tests to pass, we may need to think about what's changing in a breaking way.
@@ -123,6 +123,8 @@ def user_env(self, env): | |||
|
|||
def start(self): | |||
"""Start the process""" | |||
if self.port == 0: | |||
self.port = random_port() |
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.
services already have this logic, because it's inherited from LocalProcessSpawner 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.
You override the start()
function in this class, and don't call super.start()
anywhere.
Hence this is why the tests started failing, and I copied the logic into this class: https://github.com/jupyterhub/jupyterhub/actions/runs/4005957446/jobs/6876931281#step:10:421
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.
Ah, forgot about the lack of super start. Then this change is definitely correct. Thanks!
@@ -994,6 +994,9 @@ def get_env(self): | |||
# this should only occur in mock/testing scenarios | |||
base_url = '/' | |||
|
|||
if self.port < 1 or self.port > 65535: |
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 is a breaking change that takes away the option of letting the launched process pick the port, as is done in e.g. batchspawner, which phones home the resulting port after launch - a pattern I think we want more spawners to be able to do, since it will select ports more reliably than a random number on the Hub's system which is typically not the same port namespace.
Ideally, this could be communicated better and fail when not supported, which is most of the time.
How about this:
- validate
0 <= port <= 65535
- if port is 0, log that the launched process will be picking the port, and that the Spawner better be able to handle that.
Or, we could have a small breaking change that defines an overrideable method validate_port
that can be overridden to allow for port 0 (the default would be your check). Maybe even with a boolean attribute on the spawner class to indicate whether port 0 should be allowed.
WDYT?
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 reason that I raise an error instead of logging an error is that in the case of port == 0
an invalid JUPYTERHUB_SERVICE_URL
will be generated on the next lines. Invalid in the sense that jupyter/docker-stacks
is tripping over it and starting the server on port 80 instead of the default port 8888 or even an explicitely set JUPYTER_PORT.
Maybe part of the solution is to add a check in nbviewer
to make sure that a valid port is used? I can create a pull request for this commit: twalcari/nbviewer@ef2ac83
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.
It doesn't produce an invalid string, though. It produces a string instructing the server to bind on port 0, which is asking the server to pick its own port. To work properly with JupyterHub, this must be combined with the logic required to pass the port info back to the Hub after starting, as is done in BatchSpawner.
For services, which don't allow custom spawners, the 0->random port
above will fix this. The problem with the validation here that it affects all Spawners, prohibiting a valid use case, when it should only be affecting the service case, and thus more appropriately confined to the Service class.
Maybe part of the solution is to add a check in nbviewer to make sure that a valid port is used?
Yeah, I think that's a good idea.
Do you have your configuration for the nbviewer service? It should have a I think the bug might really be that $JUPYTERHUB_SERVICE_URL is defined at all when nbviewer is registered as a service without a URL. It shouldn't be possible to specify that a service has a web endpoint without specifying the port where it will run. Unlike a regular Spawner, there are services that don't have URLs (this is the default if a URL is not specified). An |
Some context: I maintain a custom spawner for our JupyterHub service which works on top of 'GPULab' at Ghent University. We run the Our users all use JupyterLab. nbviewer is not used. I do observe that changing the port of the I honestly don't fully grasp what this environment variable does in the context of JupyterLab and the Steps to reproduce:
|
If you're launching a singleuser server, the $JUPYTERHUB_SERVICE_URL->port is handled here and there is a bug in that I do think it may be time for a small backward-incompatible change:
This will result in port 0 failing informatively and by default, as you're doing here, but still allowing for the 'user server decides' pattern to work going forward. |
deleted as this comment crossed with the reply of minrk above. |
When implementing a custom spawner, a sneaky bug can appear when Spawner.port is not implemented: when calling
def get_env()
theJUPYTERHUB_SERVICE_URL
is generated with port number 0. While this was ignored by nbviewer in the past, it now results in difficult to diagnose behavior where the URL on which the Jupyter server starts defaults to port 80 ( See jupyter/docker-stacks#1862).I think it is preferable that the Spawner crashes instead.
Instructions to solve the issue: either define a static port in your Spawner:
or initialize the port to a random port number, as happens in
LocalProcessSpawner.start
: https://github.com/twalcari/jupyterhub/blob/cbce162e7cfa7da7997df6a6c2c37248d06ac527/jupyterhub/spawner.py#L1662-L1663