Skip to content

Commit 9254ae5

Browse files
authored
Dynamic service: Do not save state for GUEST users (ITISFoundation#2348)
1 parent 5e50637 commit 9254ae5

File tree

18 files changed

+302
-104
lines changed

18 files changed

+302
-104
lines changed

api/specs/common/schemas/running_service.yaml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,23 @@ components:
66
- data
77
properties:
88
data:
9-
$ref: '#/components/schemas/RunningServicesArray'
9+
$ref: "#/components/schemas/RunningServicesArray"
1010
error:
1111
nullable: true
1212
default: null
1313

1414
RunningServicesArray:
1515
type: array
1616
items:
17-
$ref: '#/components/schemas/RunningServiceType'
17+
$ref: "#/components/schemas/RunningServiceType"
1818

1919
RunningServiceEnveloped:
2020
type: object
2121
required:
2222
- data
2323
properties:
2424
data:
25-
$ref: '#/components/schemas/RunningServiceType'
25+
$ref: "#/components/schemas/RunningServiceType"
2626
error:
2727
nullable: true
2828
default: null
@@ -37,6 +37,7 @@ components:
3737
- service_host
3838
- service_port
3939
- service_state
40+
- user_id
4041
properties:
4142
published_port:
4243
description: The ports where the service provides its interface
@@ -80,8 +81,8 @@ components:
8081
service_basepath:
8182
description: different base path where current service is mounted otherwise defaults to root
8283
type: string
83-
example: '/x/E1O2E-LAH'
84-
default: ''
84+
example: "/x/E1O2E-LAH"
85+
default: ""
8586
service_state:
8687
description: >
8788
the service state
@@ -104,3 +105,8 @@ components:
104105
the service message
105106
type: string
106107
example: no suitable node (insufficient resources on 1 node)
108+
user_id:
109+
description: >-
110+
the user that started the service
111+
type: string
112+
example: "123"

api/specs/director/openapi.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ paths:
286286
operationId: running_interactive_services_delete
287287
parameters:
288288
- $ref: "#/components/parameters/ServiceUuid"
289+
- $ref: "#/components/parameters/SaveState"
289290
responses:
290291
"204":
291292
description: Succesfully stopped and removed the service from the oSparc platform
@@ -425,6 +426,15 @@ components:
425426
- 1.0.0
426427
- 0.0.1
427428

429+
SaveState:
430+
in: query
431+
name: save_state
432+
description: Save the state prior to removing the service
433+
required: false
434+
schema:
435+
type: boolean
436+
default: true
437+
428438
schemas:
429439
ErrorEnveloped:
430440
$ref: "../common/schemas/error.yaml#/components/schemas/ErrorEnveloped"

services/director/requirements/_test.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#
1313

1414
# testing
15+
aioresponses
1516
coverage==4.5.1 # TODO: Downgraded because of a bug https://github.com/nedbat/coveragepy/issues/716
1617
pytest
1718
pytest-aiohttp # incompatible with pytest-asyncio. See https://github.com/pytest-dev/pytest-asyncio/issues/76

services/director/requirements/_test.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#
77
aiodebug==1.1.2 # via -r requirements/_base.txt, simcore-service-library
88
aiodocker==0.14.0 # via -r requirements/_base.txt
9+
aioresponses==0.7.2 # manually added. needed for mocking http calls and extensive testing.
910
git+https://github.com/ITISFoundation/aiohttp_apiset.git@5c8a61ceb6de7ed9e09db5b4609b458a0d3773df # via -r requirements/_base.txt
1011
aiohttp==3.3.2 # via -r requirements/_base.txt, aiodocker, aiohttp-apiset, aiozipkin, pytest-aiohttp, simcore-service-library
1112
aiopg==1.0.0 # via -r requirements/_base.txt, simcore-service-library

services/director/src/simcore_service_director/api/v0/openapi.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,7 @@ paths:
11231123
- service_host
11241124
- service_port
11251125
- service_state
1126+
- user_id
11261127
properties:
11271128
published_port:
11281129
description: The ports where the service provides its interface
@@ -1181,6 +1182,10 @@ paths:
11811182
description: the service message
11821183
type: string
11831184
example: no suitable node (insufficient resources on 1 node)
1185+
user_id:
1186+
description: the user that started the service
1187+
type: string
1188+
example: '123'
11841189
error:
11851190
nullable: true
11861191
default: null
@@ -1295,6 +1300,7 @@ paths:
12951300
- service_host
12961301
- service_port
12971302
- service_state
1303+
- user_id
12981304
properties:
12991305
published_port:
13001306
description: The ports where the service provides its interface
@@ -1353,6 +1359,10 @@ paths:
13531359
description: the service message
13541360
type: string
13551361
example: no suitable node (insufficient resources on 1 node)
1362+
user_id:
1363+
description: the user that started the service
1364+
type: string
1365+
example: '123'
13561366
error:
13571367
nullable: true
13581368
default: null
@@ -1561,6 +1571,7 @@ paths:
15611571
- service_host
15621572
- service_port
15631573
- service_state
1574+
- user_id
15641575
properties:
15651576
published_port:
15661577
description: The ports where the service provides its interface
@@ -1619,6 +1630,10 @@ paths:
16191630
description: the service message
16201631
type: string
16211632
example: no suitable node (insufficient resources on 1 node)
1633+
user_id:
1634+
description: the user that started the service
1635+
type: string
1636+
example: '123'
16221637
error:
16231638
nullable: true
16241639
default: null
@@ -1738,6 +1753,13 @@ paths:
17381753
schema:
17391754
type: string
17401755
example: 123e4567-e89b-12d3-a456-426655440000
1756+
- in: query
1757+
name: save_state
1758+
description: Save the state prior to removing the service
1759+
required: false
1760+
schema:
1761+
type: boolean
1762+
default: true
17411763
responses:
17421764
'204':
17431765
description: Succesfully stopped and removed the service from the oSparc platform
@@ -1948,6 +1970,14 @@ components:
19481970
example:
19491971
- 1.0.0
19501972
- 0.0.1
1973+
SaveState:
1974+
in: query
1975+
name: save_state
1976+
description: Save the state prior to removing the service
1977+
required: false
1978+
schema:
1979+
type: boolean
1980+
default: true
19511981
schemas:
19521982
ErrorEnveloped:
19531983
type: object
@@ -1994,6 +2024,7 @@ components:
19942024
- service_host
19952025
- service_port
19962026
- service_state
2027+
- user_id
19972028
properties:
19982029
published_port:
19992030
description: The ports where the service provides its interface
@@ -2052,6 +2083,10 @@ components:
20522083
description: the service message
20532084
type: string
20542085
example: no suitable node (insufficient resources on 1 node)
2086+
user_id:
2087+
description: the user that started the service
2088+
type: string
2089+
example: '123'
20552090
error:
20562091
nullable: true
20572092
default: null
@@ -2072,6 +2107,7 @@ components:
20722107
- service_host
20732108
- service_port
20742109
- service_state
2110+
- user_id
20752111
properties:
20762112
published_port:
20772113
description: The ports where the service provides its interface
@@ -2130,6 +2166,10 @@ components:
21302166
description: the service message
21312167
type: string
21322168
example: no suitable node (insufficient resources on 1 node)
2169+
user_id:
2170+
description: the user that started the service
2171+
type: string
2172+
example: '123'
21332173
error:
21342174
nullable: true
21352175
default: null

services/director/src/simcore_service_director/producer.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,7 @@ async def _start_docker_service(
728728
"service_basepath": node_base_path,
729729
"service_state": service_state.value,
730730
"service_message": service_msg,
731+
"user_id": user_id,
731732
}
732733
return container_meta_data
733734

@@ -743,7 +744,7 @@ async def _start_docker_service(
743744

744745
async def _silent_service_cleanup(app: web.Application, node_uuid: str) -> None:
745746
try:
746-
await stop_service(app, node_uuid)
747+
await stop_service(app, node_uuid, False)
747748
except exceptions.DirectorException:
748749
pass
749750

@@ -887,6 +888,7 @@ async def _get_node_details(
887888
service_state, service_msg = results[2]
888889
service_name = service["Spec"]["Name"]
889890
service_uuid = service["Spec"]["Labels"]["uuid"]
891+
user_id = service["Spec"]["Labels"]["user_id"]
890892

891893
# get the published port
892894
published_port, target_port = await _get_docker_image_port_mapping(service)
@@ -901,6 +903,7 @@ async def _get_node_details(
901903
"service_basepath": service_basepath,
902904
"service_state": service_state.value,
903905
"service_message": service_msg,
906+
"user_id": user_id,
904907
}
905908
return node_details
906909

@@ -968,7 +971,7 @@ async def get_service_details(app: web.Application, node_uuid: str) -> Dict:
968971

969972

970973
@run_sequentially_in_context(target_args=["node_uuid"])
971-
async def stop_service(app: web.Application, node_uuid: str) -> None:
974+
async def stop_service(app: web.Application, node_uuid: str, save_state: bool) -> None:
972975
log.debug("stopping service with uuid %s", node_uuid)
973976
# get the docker client
974977
async with docker_utils.docker_client() as client: # pylint: disable=not-async-context-manager
@@ -1004,27 +1007,29 @@ async def stop_service(app: web.Application, node_uuid: str) -> None:
10041007
else "",
10051008
)
10061009
log.debug("saving state of service %s...", service_host_name)
1007-
try:
1008-
session = app[APP_CLIENT_SESSION_KEY]
1009-
service_url = "http://" + service_host_name + "/" + "state"
1010-
async with session.post(
1011-
service_url,
1012-
timeout=ServicesCommonSettings().director_dynamic_service_save_timeout,
1013-
) as response:
1014-
if 199 < response.status < 300:
1015-
log.debug(
1016-
"service %s successfully saved its state", service_host_name
1017-
)
1018-
else:
1019-
log.warning(
1020-
"service %s does not allow saving state, answered %s",
1021-
service_host_name,
1022-
await response.text(),
1023-
)
1024-
except ClientConnectionError:
1025-
log.warning(
1026-
"service %s could not be contacted, state not saved", service_host_name
1027-
)
1010+
if save_state:
1011+
try:
1012+
session = app[APP_CLIENT_SESSION_KEY]
1013+
service_url = "http://" + service_host_name + "/" + "state"
1014+
async with session.post(
1015+
service_url,
1016+
timeout=ServicesCommonSettings().director_dynamic_service_save_timeout,
1017+
) as response:
1018+
if 199 < response.status < 300:
1019+
log.debug(
1020+
"service %s successfully saved its state", service_host_name
1021+
)
1022+
else:
1023+
log.warning(
1024+
"service %s does not allow saving state, answered %s",
1025+
service_host_name,
1026+
await response.text(),
1027+
)
1028+
except ClientConnectionError:
1029+
log.warning(
1030+
"service %s could not be contacted, state not saved",
1031+
service_host_name,
1032+
)
10281033

10291034
# remove the services
10301035
try:

services/director/src/simcore_service_director/rest/generated_code/models/inline_response2003_data.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class InlineResponse2003Data(Model):
1515
Do not edit the class manually.
1616
"""
1717

18-
def __init__(self, published_port: int=None, entry_point: str=None, service_uuid: str=None, service_key: str=None, service_version: str=None, service_host: str=None, service_port: int=None, service_basepath: str='', service_state: str=None, service_message: str=None):
18+
def __init__(self, published_port: int=None, entry_point: str=None, service_uuid: str=None, service_key: str=None, service_version: str=None, service_host: str=None, service_port: int=None, service_basepath: str='', service_state: str=None, service_message: str=None, user_id: str=None):
1919
"""InlineResponse2003Data - a model defined in OpenAPI
2020
2121
:param published_port: The published_port of this InlineResponse2003Data.
@@ -28,6 +28,7 @@ def __init__(self, published_port: int=None, entry_point: str=None, service_uuid
2828
:param service_basepath: The service_basepath of this InlineResponse2003Data.
2929
:param service_state: The service_state of this InlineResponse2003Data.
3030
:param service_message: The service_message of this InlineResponse2003Data.
31+
:param user_id: The user_id of this InlineResponse2003Data.
3132
"""
3233
self.openapi_types = {
3334
'published_port': int,
@@ -39,7 +40,8 @@ def __init__(self, published_port: int=None, entry_point: str=None, service_uuid
3940
'service_port': int,
4041
'service_basepath': str,
4142
'service_state': str,
42-
'service_message': str
43+
'service_message': str,
44+
'user_id': str
4345
}
4446

4547
self.attribute_map = {
@@ -52,7 +54,8 @@ def __init__(self, published_port: int=None, entry_point: str=None, service_uuid
5254
'service_port': 'service_port',
5355
'service_basepath': 'service_basepath',
5456
'service_state': 'service_state',
55-
'service_message': 'service_message'
57+
'service_message': 'service_message',
58+
'user_id': 'user_id'
5659
}
5760

5861
self._published_port = published_port
@@ -65,6 +68,7 @@ def __init__(self, published_port: int=None, entry_point: str=None, service_uuid
6568
self._service_basepath = service_basepath
6669
self._service_state = service_state
6770
self._service_message = service_message
71+
self._user_id = user_id
6872

6973
@classmethod
7074
def from_dict(cls, dikt: dict) -> 'InlineResponse2003Data':
@@ -172,8 +176,8 @@ def service_key(self, service_key):
172176
"""
173177
if service_key is None:
174178
raise ValueError("Invalid value for `service_key`, must not be `None`")
175-
if service_key is not None and not re.search(r'^(simcore)\/(services)\/(comp|dynamic)(\/[^\s\/]+)+$', service_key):
176-
raise ValueError("Invalid value for `service_key`, must be a follow pattern or equal to `/^(simcore)\/(services)\/(comp|dynamic)(\/[^\s\/]+)+$/`")
179+
if service_key is not None and not re.search(r'^(simcore)\/(services)\/(comp|dynamic)(\/[\w\/-]+)+$', service_key):
180+
raise ValueError("Invalid value for `service_key`, must be a follow pattern or equal to `/^(simcore)\/(services)\/(comp|dynamic)(\/[\w\/-]+)+$/`")
177181

178182
self._service_key = service_key
179183

@@ -330,3 +334,28 @@ def service_message(self, service_message):
330334
"""
331335

332336
self._service_message = service_message
337+
338+
@property
339+
def user_id(self):
340+
"""Gets the user_id of this InlineResponse2003Data.
341+
342+
the user that started the service
343+
344+
:return: The user_id of this InlineResponse2003Data.
345+
:rtype: str
346+
"""
347+
return self._user_id
348+
349+
@user_id.setter
350+
def user_id(self, user_id):
351+
"""Sets the user_id of this InlineResponse2003Data.
352+
353+
the user that started the service
354+
355+
:param user_id: The user_id of this InlineResponse2003Data.
356+
:type user_id: str
357+
"""
358+
if user_id is None:
359+
raise ValueError("Invalid value for `user_id`, must not be `None`")
360+
361+
self._user_id = user_id

0 commit comments

Comments
 (0)