5454"""
5555
5656from concurrent .futures import ProcessPoolExecutor
57+ from contextlib import suppress
5758import getpass
59+ import json
60+ import os
5861from pathlib import Path , PurePath
5962import sys
6063from typing import List
105108from cylc .uiserver .workflows_mgr import WorkflowsManager
106109
107110
111+ API_INFO_FILE = f'{ USER_CONF_ROOT / "api_token.json" } '
112+
113+
108114class PathType (TraitType ):
109115 """A pathlib traitlet type which allows string and undefined values."""
110116
@@ -400,6 +406,10 @@ def initialize_settings(self):
400406 for key , value in self .config ['CylcUIServer' ].items ()
401407 )
402408 )
409+ # Make API token available to server's user.
410+ # Do it here to avoid overwriting via server start attempt,
411+ # when server already running.
412+ self .write_api_info ()
403413 # start the async scan task running (do this on server start not init)
404414 ioloop .IOLoop .current ().add_callback (
405415 self .workflows_mgr .run
@@ -505,6 +515,21 @@ def set_auth(self):
505515 def initialize_templates (self ):
506516 """Change the jinja templating environment."""
507517
518+ def write_api_info (self ):
519+ api_info = self .serverapp .server_info ()
520+ api_token = os .environ .get ("JUPYTERHUB_API_TOKEN" )
521+ api_url = os .environ .get ("JUPYTERHUB_SERVICE_URL" )
522+ # Could be none, if server not launched by hub.
523+ if api_token :
524+ api_info ['token' ] = api_token
525+ if api_url :
526+ api_info ['url' ] = api_url
527+ with suppress (FileNotFoundError ):
528+ os .unlink (API_INFO_FILE )
529+ fd = os .open (API_INFO_FILE , os .O_CREAT | os .O_WRONLY , mode = 0o600 )
530+ os .write (fd , json .dumps (api_info ).encode ("utf-8" ))
531+ os .close (fd )
532+
508533 @classmethod
509534 def launch_instance (cls , argv = None , ** kwargs ):
510535 if argv is None :
@@ -514,6 +539,9 @@ def launch_instance(cls, argv=None, **kwargs):
514539 super ().launch_instance (argv = argv , ** kwargs )
515540
516541 async def stop_extension (self ):
542+ # Remove API token if hub spawned
543+ with suppress (FileNotFoundError ):
544+ os .unlink (API_INFO_FILE )
517545 # stop the async scan task
518546 await self .workflows_mgr .stop ()
519547 for sub in self .data_store_mgr .w_subs .values ():
0 commit comments