mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-14 21:43:01 +00:00
Update service table schema
This commit is contained in:

committed by
Duc Trung LE

parent
95781880c5
commit
bf565ece3b
@@ -35,7 +35,7 @@ def upgrade():
|
||||
if 'services' in tables:
|
||||
op.add_column(
|
||||
'services',
|
||||
sa.Column('from_config', sa.Boolean, nullable=True, default=True),
|
||||
sa.Column('from_config', sa.Boolean, default=True),
|
||||
)
|
||||
op.execute('UPDATE services SET from_config = true')
|
||||
for item in COL_DATA:
|
||||
|
@@ -32,10 +32,12 @@ def access_scopes(oauth_client: orm.OAuthClient, db: Session):
|
||||
if spawner:
|
||||
scopes.add(f"access:servers!server={spawner.user.name}/{spawner.name}")
|
||||
else:
|
||||
statement = f"SELECT * FROM services WHERE oauth_client_id = '{oauth_client.identifier}'"
|
||||
service = db.execute(text(statement)).fetchall()
|
||||
statement = "SELECT * FROM services WHERE oauth_client_id = :identifier"
|
||||
service = db.execute(
|
||||
text(statement), {"identifier": oauth_client.identifier}
|
||||
).fetchall()
|
||||
if len(service) > 0:
|
||||
scopes.add(f"access:services!service={service.name}")
|
||||
scopes.add(f"access:services!service={service[0].name}")
|
||||
|
||||
return frozenset(scopes)
|
||||
|
||||
|
@@ -63,9 +63,21 @@ class ServiceAPIHandler(APIHandler):
|
||||
note='generated at runtime'
|
||||
)
|
||||
if new_service.managed or new_service.url:
|
||||
self.app.start_service(service_name, new_service)
|
||||
service_status = self.app.start_service(service_name, new_service)
|
||||
if not service_status:
|
||||
self.log.error(
|
||||
'Failed to start service %s',
|
||||
service_name,
|
||||
exc_info=True,
|
||||
)
|
||||
self.app.toggle_service_health_check()
|
||||
|
||||
if new_service.oauth_no_confirm:
|
||||
oauth_no_confirm_list = self.settings.get('oauth_no_confirm_list')
|
||||
msg = f"Allowing service {new_service.name} to complete OAuth without confirmation on an authorization web page"
|
||||
self.log.warning(msg)
|
||||
oauth_no_confirm_list.add(new_service.oauth_client_id)
|
||||
|
||||
return new_service
|
||||
|
||||
@needs_scope('admin:services')
|
||||
@@ -136,6 +148,10 @@ class ServiceAPIHandler(APIHandler):
|
||||
if orm_server is not None:
|
||||
self.db.delete(orm_server)
|
||||
|
||||
if service.oauth_no_confirm:
|
||||
oauth_no_confirm_list = self.settings.get('oauth_no_confirm_list')
|
||||
oauth_no_confirm_list.remove(service.oauth_client_id)
|
||||
|
||||
self.db.delete(orm_service)
|
||||
self.db.commit()
|
||||
|
||||
|
@@ -2375,7 +2375,7 @@ class JupyterHub(Application):
|
||||
)
|
||||
traits = service.traits(input=True)
|
||||
for key in traits:
|
||||
value = getattr(orm_service, key, None)
|
||||
value = orm_service.get_column(key)
|
||||
if value is not None:
|
||||
setattr(service, key, value)
|
||||
|
||||
@@ -2448,6 +2448,7 @@ class JupyterHub(Application):
|
||||
if key not in traits:
|
||||
raise AttributeError("No such service field: %s" % key)
|
||||
setattr(service, key, value)
|
||||
orm_service.update_column(key, value)
|
||||
|
||||
if service.api_token:
|
||||
self.service_tokens[service.api_token] = service.name
|
||||
@@ -3238,48 +3239,6 @@ class JupyterHub(Application):
|
||||
if self._check_services_health_callback is not None:
|
||||
self._check_services_health_callback.stop()
|
||||
|
||||
async def _start_service(self, service_name, service, ssl_context):
|
||||
for service_name, service in self._service_map.items():
|
||||
msg = f'{service_name} at {service.url}' if service.url else service_name
|
||||
if service.managed:
|
||||
self.log.info("Starting managed service %s", msg)
|
||||
try:
|
||||
await service.start()
|
||||
except Exception as e:
|
||||
self.log.critical(
|
||||
"Failed to start service %s", service_name, exc_info=True
|
||||
)
|
||||
self.exit(1)
|
||||
else:
|
||||
self.log.info("Adding external service %s", msg)
|
||||
|
||||
if service.url:
|
||||
tries = 10 if service.managed else 1
|
||||
for i in range(tries):
|
||||
try:
|
||||
await Server.from_orm(service.orm.server).wait_up(
|
||||
http=True, timeout=1, ssl_context=ssl_context
|
||||
)
|
||||
except AnyTimeoutError:
|
||||
if service.managed:
|
||||
status = await service.spawner.poll()
|
||||
if status is not None:
|
||||
self.log.error(
|
||||
"Service %s exited with status %s",
|
||||
service_name,
|
||||
status,
|
||||
)
|
||||
break
|
||||
else:
|
||||
break
|
||||
else:
|
||||
self.log.error(
|
||||
"Cannot connect to %s service %s at %s. Is it running?",
|
||||
service.kind,
|
||||
service_name,
|
||||
service.url,
|
||||
)
|
||||
|
||||
async def start(self):
|
||||
"""Start the whole thing"""
|
||||
self.io_loop = loop = IOLoop.current()
|
||||
@@ -3365,12 +3324,21 @@ class JupyterHub(Application):
|
||||
|
||||
# start the service(s)
|
||||
for service_name, service in self._service_map.items():
|
||||
service_status = await self._start_service(
|
||||
service_status = await self.start_service(
|
||||
service_name, service, ssl_context
|
||||
)
|
||||
# if not service_status:
|
||||
# # Stop the application if a service failed to start.
|
||||
# self.exit(1)
|
||||
if not service_status:
|
||||
if service.from_config:
|
||||
# Stop the application if a config-based service failed to start.
|
||||
self.exit(1)
|
||||
else:
|
||||
# Only warn for database-based service, so that admin can connect
|
||||
# to hub to remove the service.
|
||||
self.log.error(
|
||||
"Failed to start database-based service %s",
|
||||
service_name,
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
await self.proxy.check_routes(self.users, self._service_map)
|
||||
|
||||
|
@@ -396,23 +396,23 @@ class Service(Base):
|
||||
|
||||
url = Column(Unicode(2047), nullable=True)
|
||||
|
||||
oauth_client_allowed_scopes = Column(
|
||||
JSONList, nullable=True, default=[]
|
||||
) # List of string
|
||||
oauth_client_allowed_scopes = Column(JSONList, nullable=True)
|
||||
|
||||
info = Column(JSONDict, nullable=True, default={}) # Dict
|
||||
info = Column(JSONDict, nullable=True)
|
||||
|
||||
display = Column(Boolean, default=True, nullable=True)
|
||||
display = Column(Boolean, nullable=True)
|
||||
|
||||
oauth_no_confirm = Column(Boolean, default=False, nullable=True)
|
||||
oauth_no_confirm = Column(Boolean, nullable=True)
|
||||
|
||||
command = Column(JSONList, nullable=True, default=[]) # List of string
|
||||
command = Column(JSONList, nullable=True)
|
||||
|
||||
cwd = Column(Unicode, nullable=True)
|
||||
cwd = Column(Unicode(4095), nullable=True)
|
||||
|
||||
environment = Column(JSONDict, nullable=True, default={}) # Dict
|
||||
environment = Column(JSONDict, nullable=True)
|
||||
|
||||
user = Column(Unicode, nullable=True)
|
||||
user = Column(Unicode(255), nullable=True)
|
||||
|
||||
from_config = Column(Boolean, default=True)
|
||||
|
||||
api_tokens = relationship(
|
||||
"APIToken", back_populates="service", cascade="all, delete-orphan"
|
||||
@@ -437,8 +437,6 @@ class Service(Base):
|
||||
),
|
||||
)
|
||||
|
||||
from_config = Column(Boolean, default=True, nullable=True)
|
||||
|
||||
oauth_client = relationship(
|
||||
'OAuthClient',
|
||||
back_populates="service",
|
||||
@@ -460,6 +458,43 @@ class Service(Base):
|
||||
"""
|
||||
return db.query(cls).filter(cls.name == name).first()
|
||||
|
||||
def update_column(self, column_name: str, value: any) -> bool:
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
column_name (str): _description_
|
||||
value (any): _description_
|
||||
|
||||
Returns:
|
||||
bool: _description_
|
||||
"""
|
||||
if (
|
||||
hasattr(self, column_name)
|
||||
and not getattr(Service, column_name).foreign_keys
|
||||
):
|
||||
setattr(self, column_name, value)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_column(self, column_name: str):
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
column_name (str): _description_
|
||||
value (any): _description_
|
||||
|
||||
Returns:
|
||||
bool: _description_
|
||||
"""
|
||||
if (
|
||||
hasattr(self, column_name)
|
||||
and not getattr(Service, column_name).foreign_keys
|
||||
):
|
||||
return getattr(self, column_name)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class Expiring:
|
||||
"""Mixin for expiring entries
|
||||
|
Reference in New Issue
Block a user