Skip to content

Commit 093554c

Browse files
authored
šŸ› Retrieving service key/version from running service to use regex instead of split (ITISFoundation#2738)
* use regex instead of split
1 parent 6a57852 commit 093554c

File tree

2 files changed

+91
-5
lines changed

2 files changed

+91
-5
lines changed

ā€Žservices/director/src/simcore_service_director/producer.pyā€Ž

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,12 @@ async def _get_dependant_repos(
638638
return dependent_repositories
639639

640640

641+
_TAG_REGEX = re.compile(r"^\d+\.\d+\.\d+$")
642+
_SERVICE_KEY_REGEX = re.compile(
643+
r"^(simcore/services/(comp|dynamic|frontend)(/[\w/-]+)+):(\d+\.\d+\.\d+).*$"
644+
)
645+
646+
641647
async def _find_service_tag(
642648
list_of_images: Dict, service_key: str, service_tag: str
643649
) -> str:
@@ -646,8 +652,7 @@ async def _find_service_tag(
646652
service_name=service_key, service_tag=service_tag
647653
)
648654
# filter incorrect chars
649-
regex = re.compile(r"^\d+\.\d+\.\d+$")
650-
filtered_tags_list = filter(regex.search, list_of_images[service_key])
655+
filtered_tags_list = filter(_TAG_REGEX.search, list_of_images[service_key])
651656
# sort them now
652657
available_tags_list = sorted(filtered_tags_list, key=StrictVersion)
653658
# not tags available... probably an undefined service there...
@@ -813,11 +818,18 @@ async def _get_service_key_version_from_docker_service(
813818
service_full_name = str(service["Spec"]["TaskTemplate"]["ContainerSpec"]["Image"])
814819
if not service_full_name.startswith(config.REGISTRY_PATH):
815820
raise exceptions.DirectorException(
816-
msg="Invalid service {}".format(service_full_name)
821+
msg=f"Invalid service '{service_full_name}', it is missing {config.REGISTRY_PATH}"
817822
)
818823

819824
service_full_name = service_full_name[len(config.REGISTRY_PATH) :].strip("/")
820-
return service_full_name.split(":")[0], service_full_name.split(":")[1]
825+
service_re_match = _SERVICE_KEY_REGEX.match(service_full_name)
826+
if not service_re_match:
827+
raise exceptions.DirectorException(
828+
msg=f"Invalid service '{service_full_name}', it does not follow pattern '{_SERVICE_KEY_REGEX.pattern}'"
829+
)
830+
service_key = service_re_match.group(1)
831+
service_tag = service_re_match.group(4)
832+
return service_key, service_tag
821833

822834

823835
async def _get_service_basepath_from_docker_service(service: Dict) -> str:

ā€Žservices/director/tests/test_producer.pyā€Ž

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import asyncio
88
import time
99
import uuid
10+
from dataclasses import dataclass
1011

1112
import docker
1213
import pytest
@@ -77,7 +78,7 @@ async def push_start_services(number_comp, number_dyn, dependant=False):
7778
start_time = time.perf_counter()
7879
max_time = 2 * 60
7980
while node_details["service_state"] != "running":
80-
asyncio.sleep(2)
81+
await asyncio.sleep(2)
8182
if (time.perf_counter() - start_time) > max_time:
8283
assert True, "waiting too long to start service"
8384
node_details = await producer.get_service_details(
@@ -254,3 +255,76 @@ async def test_dependent_services_have_common_network(run_services):
254255
list_of_services[0].attrs["Spec"]["Networks"][0]["Target"]
255256
== list_of_services[1].attrs["Spec"]["Networks"][0]["Target"]
256257
)
258+
259+
260+
@dataclass
261+
class FakeDockerService:
262+
service_str: str
263+
expected_key: str
264+
expected_tag: str
265+
266+
267+
@pytest.mark.parametrize(
268+
"fake_service",
269+
[
270+
FakeDockerService(
271+
"/simcore/services/dynamic/some/sub/folder/my_service-key:123.456.3214",
272+
"simcore/services/dynamic/some/sub/folder/my_service-key",
273+
"123.456.3214",
274+
),
275+
FakeDockerService(
276+
"/simcore/services/dynamic/some/sub/folder/my_service-key:123.456.3214@sha256:2aef165ab4f30fbb109e88959271d8b57489790ea13a77d27c02d8adb8feb20f",
277+
"simcore/services/dynamic/some/sub/folder/my_service-key",
278+
"123.456.3214",
279+
),
280+
],
281+
)
282+
async def test_get_service_key_version_from_docker_service(
283+
fake_service: FakeDockerService,
284+
):
285+
docker_service_partial_inspect = {
286+
"Spec": {
287+
"TaskTemplate": {
288+
"ContainerSpec": {
289+
"Image": f"{config.REGISTRY_PATH}{fake_service.service_str}"
290+
}
291+
}
292+
}
293+
}
294+
(
295+
service_key,
296+
service_tag,
297+
) = await producer._get_service_key_version_from_docker_service(
298+
docker_service_partial_inspect
299+
)
300+
assert service_key == fake_service.expected_key
301+
assert service_tag == fake_service.expected_tag
302+
303+
304+
@pytest.mark.parametrize(
305+
"fake_service_str",
306+
[
307+
"postgres:10.11@sha256:2aef165ab4f30fbb109e88959271d8b57489790ea13a77d27c02d8adb8feb20f",
308+
"/simcore/postgres:10.11@sha256:2aef165ab4f30fbb109e88959271d8b57489790ea13a77d27c02d8adb8feb20f",
309+
"itisfoundation/postgres:10.11@sha256:2aef165ab4f30fbb109e88959271d8b57489790ea13a77d27c02d8adb8feb20f",
310+
"/simcore/services/stuff/postgres:10.11",
311+
"/simcore/services/dynamic/postgres:10.11",
312+
"/simcore/services/dynamic/postgres:10",
313+
],
314+
)
315+
async def test_get_service_key_version_from_docker_service_except_invalid_keys(
316+
fake_service_str: str,
317+
):
318+
docker_service_partial_inspect = {
319+
"Spec": {
320+
"TaskTemplate": {
321+
"ContainerSpec": {
322+
"Image": f"{config.REGISTRY_PATH if fake_service_str.startswith('/') else ''}{fake_service_str}"
323+
}
324+
}
325+
}
326+
}
327+
with pytest.raises(exceptions.DirectorException):
328+
await producer._get_service_key_version_from_docker_service(
329+
docker_service_partial_inspect
330+
)

0 commit comments

Comments
Ā (0)