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

Accept an unset command to proxy to an already started process (unmanaged process) #339

Merged
merged 18 commits into from
Jan 2, 2023

Conversation

ryshoooo
Copy link
Contributor

@ryshoooo ryshoooo commented Jun 21, 2022

Updated PR description by Erik

jupyter-server-proxy enables users to proxy to already started processes by {base_url}/proxy/{port}/{proxied_path}, but for a better user experience it could be nice to just be able to use {base_url}/{proxied_path}. This would rely on someone having configured how that should be proxied ahead of time. This PR adds support for this.

By providing a server process configuration like this, omitting the previousy required command, jupyter-server-proxy will assume the server process isn't to be managed/started by jupyter-server-proxy, and that its already started and accepting traffic. jupyter-server-proxy won't try to await it either before proxying traffic to it.

# Below we configure jupyter-server-proxy to
# provide the route {base_url}/already-started-process
# as a convenience for the user un-friendly route
# {base_url}/proxy/54321/already-started-process,
# note how command is omitted!
c.ServerProxy.servers = {
{
    "already-started-process": {
        "port": 54321,
    },
}

Not providing a command to a server process definition means that its an "jupyter-server-proxy unmanaged server process" that we proxy to, supposed to be started ahead of time.

Outdated PR description

This PR introduces additional parameter for the server proxy configuration, which specifies whether the port availability checking is necessary to perform or not. This is useful to disable if the application is already running (started by something else) and the user just wants to create a simple proxy for it (not start it through proxy directly).

@welcome

This comment was marked as resolved.

@ryanlovett
Copy link
Collaborator

Hi @ryshoooo, if/when I need to start the proxied service manually I use the anonymous proxy URL, e.g. {base_url}/proxy/{port}/{proxied_path} rather than {base_url}/{name}/{proxied_path}. Is this method not desired or sufficient? Is this what you mean by, "(not start it through proxy directly)" ?

@ryshoooo
Copy link
Contributor Author

Hi @ryshoooo, if/when I need to start the proxied service manually I use the anonymous proxy URL, e.g. {base_url}/proxy/{port}/{proxied_path} rather than {base_url}/{name}/{proxied_path}. Is this method not desired or sufficient? Is this what you mean by, "(not start it through proxy directly)" ?

Hi @ryanlovett, yes it is exactly not desired :) To give you a specific example of what we're facing, we're using Z2JH with Kubernetes and we wish to provide code-server for each single-user environment. The old way we used to do this, was that we pre-installed code-server into the single-user base image and then used j-s-p the way it's supposed to be used, i.e. the command starts code-server and we open it up at some random open port. We're moving away from this solution in order to have even more micro-service architecture in place, so now we start the code-server as an additional extra container in the single-user pod during spawning. This approach has quite a few advantages, mainly that now we can do upgrades of code-server without touching the single-user base image. Anyway, the users got very much used to the fact that URL user/{username}/code points to the vscode, so we'd like to preserve that with the new setup :)

@manics
Copy link
Member

manics commented Jun 23, 2022

I think the general idea sounds good. Did you consider any other options, e.g. something like managed_service=True|False to control whether JSP starts the service or not, which is easier to debug (since you're not in an indeterminate state)

@ryshoooo
Copy link
Contributor Author

I think the general idea sounds good. Did you consider any other options, e.g. something like managed_service=True|False to control whether JSP starts the service or not, which is easier to debug (since you're not in an indeterminate state)

A little bit yes, I just decided not to go this way fully just yet :) I merely just wanted to have something quickly working without too many changes to the codebase. Which this does for me :) But I do like your suggestion and I do think it is the better solution with the managed_service parameter. But then it makes command parameter conditionally optional, which I'm not sure how I feel about it, feels like spaghetti. So that's mainly why I didn't go that way :)

If you think it's the right way, I can try and turn it into managed_service parameter. It'll take a bit of time :)

@manics
Copy link
Member

manics commented Jul 1, 2022

But then it makes command parameter conditionally optional, which I'm not sure how I feel about it, feels like spaghetti.

That's a good point! Maybe we could make the command argument optional instead of adding the new flag. If command is empty just setup the port-fowarding?
@ryanlovett what do you think?

@ryanlovett
Copy link
Collaborator

@manics That seems reasonable to me.

@ryshoooo
Copy link
Contributor Author

ryshoooo commented Jul 7, 2022

I've added the changes, removed the check_port parameter, and made the command argument optional. However, the tests seem to be failing due to missing jupyter-notebook binary? Seems a bit unrelated to this PR right?

@ryanlovett
Copy link
Collaborator

ryanlovett commented Jul 7, 2022

Thanks @ryshoooo ! Yes, I'm guessing we need to add jupyter-notebook as a new dependency before running tests. (in a separate PR)

@manics
Copy link
Member

manics commented Jul 8, 2022

The tests should be fixed by #340

Would you mind adding a test for this new configuration, e.g. based on

'python-http-port54321': {
'command': ['python3', './tests/resources/httpinfo.py', '{port}'],
'port': 54321,

def test_server_proxy_requested_port():
r = request_get(PORT, '/python-http-port54321/ghi', TOKEN)
assert r.code == 200
s = r.read().decode('ascii')
assert s.startswith('GET /ghi?token=')
assert 'X-Forwarded-Context: /python-http-port54321\n' in s
assert 'X-Proxycontextpath: /python-http-port54321\n' in s
direct = request_get(54321, '/ghi', TOKEN)
assert direct.code == 200

For the backend service you can just connect to one of the existing ones, so effectively the new configuration would be an alias.

@ryshoooo
Copy link
Contributor Author

ryshoooo commented Jul 8, 2022

@manics

Thanks for the guidance :) I've added an additional test for the new functionality.

@ryshoooo
Copy link
Contributor Author

Looks like the pipeline is failing again, but the logs are a bit vague. Does anybody have any idea what's going on?

@consideRatio consideRatio changed the title Make port checking optional Accept an unset command to proxy to an already started process Dec 28, 2022
@consideRatio consideRatio changed the title Accept an unset command to proxy to an already started process Accept an unset command to proxy to an already started process (unmanaged process) Dec 28, 2022
Comment on lines 657 to 659
if not cmd:
return

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this called to late? Should self.state['proc'] be set to a new SupervisedProcess object? The idea was that if the command is unset, it shouldn't be supervised - right?

If it is correct, a comment is needed to clarify why it is correct in my mind as this is quite unexpected for me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides this, the changes looks good to me!

Copy link
Member

@consideRatio consideRatio Dec 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like it is now, it won't be awaited readiness either. I'll push a commit to move this logic upwards: d6fbbd8

@consideRatio
Copy link
Member

Looks like the pipeline is failing again, but the logs are a bit vague. Does anybody have any idea what's going on?

Please ignore them. I'm not sure, but its related to jupyter_server 2 being released and that the jupyterlab extension isn't doing well with it.

Copy link
Member

@consideRatio consideRatio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM as it is now. I added two commits, one updating docs and another one relocating a return statement to happen sooner rather than later when an unmanaged process is involved.

@ryshoooo does this look good to you as it is now?

@ryshoooo
Copy link
Contributor Author

ryshoooo commented Jan 2, 2023

This LGTM as it is now. I added two commits, one updating docs and another one relocating a return statement to happen sooner rather than later when an unmanaged process is involved.

@ryshoooo does this look good to you as it is now?

Hi @consideRatio

Yes this looks totally fine to me, thanks a lot for your help! :)

@consideRatio consideRatio merged commit 15447ba into jupyterhub:main Jan 2, 2023
@welcome
Copy link

welcome bot commented Jan 2, 2023

Congrats on your first merged pull request in this project! 🎉
congrats
Thank you for contributing, we are very proud of you! ❤️

@consideRatio
Copy link
Member

Thank you @ryshoooo! ❤️ 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants