Skip to content

Commit

Permalink
check global config for hub url and (#507)
Browse files Browse the repository at this point in the history
gui: open the hub url if configured
  • Loading branch information
markgrahamdawson authored Sep 22, 2023
1 parent b043bb9 commit 1433411
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 6 deletions.
1 change: 1 addition & 0 deletions changes.d/507.feat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added functionality for routing to a multiuser deployment when running cylc gui command.
37 changes: 35 additions & 2 deletions cylc/uiserver/scripts/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
from requests.exceptions import RequestException
import requests
import sys
from textwrap import dedent
from typing import Optional
import webbrowser
from getpass import getuser


from cylc.flow.id_cli import parse_id_async
Expand All @@ -40,6 +42,8 @@
WorkflowFilesError
)

from cylc.flow.cfgspec.glbl_cfg import glbl_cfg

from cylc.uiserver import init_log
from cylc.uiserver.app import (
CylcUIServer,
Expand All @@ -51,8 +55,32 @@

def main(*argv):
init_log()
hub_url = glbl_cfg().get(['hub', 'url'])
jp_server_opts, new_gui, workflow_id = parse_args_opts()
if '--help' not in sys.argv:
if '--help' in sys.argv and hub_url:
print(
dedent('''
cylc gui [WORKFLOW]
Open the Cylc GUI in a new web browser tab.
If WORKFLOW is specified, the GUI will open on this workflow.
This command has been configured to use a centrally configured
Jupyter Hub instance rather than start a standalone server.
To see the configuration options for the server run
"cylc gui --help-all", these options can be configured in the
Jupyter configuration files using "c.Spawner.cmd", see the Cylc
and Jupyter Hub documentation for more details.
'''))
return
if not {'--help', '--help-all'} & set(sys.argv):
if hub_url:
print(f"Running on {hub_url } as specified in global config.")
webbrowser.open(
update_url(hub_url, workflow_id), autoraise=True
)
return
# get existing jpserver-<pid>-open.html files
# check if the server is available for use
# prompt for user whether to clean files for un-usable uiservers
Expand Down Expand Up @@ -190,6 +218,7 @@ def get_arg_parser():
def update_url(url, workflow_id):
""" Update the url to open at the correct workflow in the gui.
"""
hub_url = glbl_cfg().get(['hub', 'url'])
if not url:
return
split_url = url.split('/workspace/')
Expand All @@ -212,4 +241,8 @@ def update_url(url, workflow_id):
return url.replace(old_workflow, workflow_id)
else:
# current url points to dashboard, update to point to workflow
return f"{url}/workspace/{workflow_id}"
if hub_url:
return (f"{url}/user/{getuser()}/{CylcUIServer.name}"
f"/#/workspace/{workflow_id}")
else:
return f"{url}/workspace/{workflow_id}"
51 changes: 51 additions & 0 deletions cylc/uiserver/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
from cylc.uiserver.data_store_mgr import DataStoreMgr
from cylc.uiserver.workflows_mgr import WorkflowsManager

from cylc.flow.cfgspec.globalcfg import SPEC
from cylc.flow.parsec.config import ParsecConfig
from cylc.flow.parsec.validate import cylc_config_validate

class AsyncClientFixture(WorkflowRuntimeClient):
pattern = zmq.REQ
Expand Down Expand Up @@ -360,3 +363,51 @@ def workflow_run_dir(request):
yield flow_name, log_dir
if not request.session.testsfailed:
rmtree(run_dir)

@pytest.fixture
def mock_glbl_cfg(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
"""A Pytest fixture for fiddling global config values.
* Hacks the specified `glbl_cfg` object.
* Can be called multiple times within a test function.
Args:
pypath (str):
The python-like path to the global configuation object you want
to fiddle.
E.G. if you want to hack the `glbl_cfg` in
`cylc.flow.scheduler` you would provide
`cylc.flow.scheduler.glbl_cfg`
global_config (str):
The globlal configuration as a multi-line string.
Example:
Change the value of `UTC mode` in the global config as seen from
`the scheduler` module.
def test_something(mock_glbl_cfg):
mock_glbl_cfg(
'cylc.flow.scheduler.glbl_cfg',
'''
[scheduler]
UTC mode = True
'''
)
"""
# TODO: modify Parsec so we can use StringIO rather than a temp file.
def _mock_glbl_cfg(pypath: str, global_config: str) -> None:
nonlocal tmp_path, monkeypatch
global_config_path = tmp_path / 'global.cylc'
global_config_path.write_text(global_config)
glbl_cfg = ParsecConfig(SPEC, validator=cylc_config_validate)
glbl_cfg.loadcfg(global_config_path)

def _inner(cached=False):
nonlocal glbl_cfg
return glbl_cfg

monkeypatch.setattr(pypath, _inner)

yield _mock_glbl_cfg
rmtree(tmp_path)
26 changes: 22 additions & 4 deletions cylc/uiserver/tests/test_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
from glob import glob
import os
from pathlib import Path
from getpass import getuser
import pytest
from random import randint
import requests
from shutil import rmtree
from time import sleep

from cylc.uiserver.scripts.gui import (
Expand All @@ -30,46 +30,64 @@
)

@pytest.mark.parametrize(
'existing_content,workflow_id,expected_updated_content',
'existing_content,workflow_id,expected_updated_content,hub_url',
[
pytest.param(
'http://localhost:8892/cylc/?token=1234567890some_big_long_token1234567890#',
None,
'http://localhost:8892/cylc/?token=1234567890some_big_long_token1234567890#',
'',
id='existing_no_workflow_new_no_workflow'
),
pytest.param(
'http://localhost:8892/cylc/?token=1234567890some_big_long_token1234567890#',
'some/workflow',
'http://localhost:8892/cylc/?token=1234567890some_big_long_token1234567890#/workspace/some/workflow',
'',
id='existing_no_workflow_new_workflow'
),
pytest.param(
'http://localhost:8892/cylc/?token=1234567890some_big_long_token1234567890#',
'some/hub/workflow',
f'http://localhost:8892/cylc/?token=1234567890some_big_long_token1234567890#/user/{getuser()}/cylc/#/workspace/some/hub/workflow',
'localhost:8000',
id='existing_no_workflow_new_workflow_hub'
),
pytest.param(
'http://localhost:8892/cylc/?token=1234567890some_big_long_token1234567890#/workspace/some/workflow',
'another/flow',
'http://localhost:8892/cylc/?token=1234567890some_big_long_token1234567890#/workspace/another/flow',
'',
id='existing_workflow_new_workflow'
),
pytest.param(
'http://localhost:8892/cylc/?token=1234567890some_big_long_token1234567890#/workspace/some/workflow',
None,
'http://localhost:8892/cylc/?token=1234567890some_big_long_token1234567890#',
'',
id='existing_workflow_no_new_workflow'
),
pytest.param(
'',
'another/flow',
None,
'',
id='no_url_no_change'
),
]
)

def test_update_html_file_updates_gui_file(
existing_content,
workflow_id,
expected_updated_content):
expected_updated_content,
hub_url,
mock_glbl_cfg):
"""Tests url is updated correctly"""

mock_glbl_cfg('cylc.uiserver.scripts.gui.glbl_cfg',
f'''[hub]
url = {hub_url}
''')
updated_file_content = update_url(existing_content, workflow_id)
assert updated_file_content == expected_updated_content

Expand Down

0 comments on commit 1433411

Please sign in to comment.