Skip to content

Commit 9d244de

Browse files
authored
Feature/#215 adapt platform to new definition of nodes (#230)
* added label descriptors as json\nadded utils functions for validating nodes * disabled conversion from old to new schema * git * added validation of json schema fixed teardown of fixtures * temporaily skip test that should be modified with issue #222 * refactored tests * add handlers tests * pylint * added pytest-asyncio for async testing * fixed issue while testing pylint fixes requirements * missing include to aiohttp-apiset in testing env * fix includes in travis * fixed default values in config made base image wait for 10secs for testing purposes added tests for handler * pylint * tests done through handler * fixes tests * moved init registry fixture to conftest * moved docker swarm fixture to conftest * added test producer unit test docker registry fixture allow for long running tasks in docker fixed env variables set up in director renamed is_service_up to get_service_details * bad merge * using pkg_resources * separated fixtures in more files added test to check dummmy service validity * refactoring * added test to check openapi interaface (currently does not convert the json schema part) * fix default sleep time * code cleanup * get service logs when an issue arises * onlyt start computational service * try getting logs out * travis * fix bad image * set dynamic services back in * fix container under linux * check if part of swarm * revert last change * try updating docker in travis * switch conversion back on as node description are again going to change after PM2 * fix test with conversion back on * added force flag
1 parent c3e3bc4 commit 9d244de

36 files changed

+1021
-137
lines changed

.travis.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ matrix:
2727
- python --version
2828
- uname -a
2929
- lsb_release -a
30+
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
31+
- sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
32+
- sudo apt-get update
33+
- sudo apt-get -y install docker-ce
3034
- docker --version
3135
- docker-compose --version
3236
# shutdown postgres because sometimes is is alrady up ???
@@ -37,8 +41,8 @@ matrix:
3741
install:
3842
- pip install --upgrade pip wheel setuptools && pip3 --version
3943
- pip3 install packages/s3wrapper[test]
40-
- pip3 install packages/simcore-sdk[test]
41-
- pip3 install services/director[test]
44+
- pip3 install packages/simcore-sdk[test]
45+
- pushd services/director; pip3 install -r requirements/ci.txt; popd
4246
- pip3 install packages/director-sdk/python
4347
- pushd services/web/server; pip3 install -r requirements/ci.txt; popd
4448

scripts/docker/remove-all-images.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/bin/bash
2-
docker rmi $(docker images -q)
2+
docker rmi $(docker images -q) -f

services/director/requirements/ci.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.[test]
2+
git+https://github.com/ITISFoundation/aiohttp_apiset.git@fixes_4_osparc#egg=aiohttp_apiset
+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
coveralls~=1.3
2+
jsonschema~=2.6
3+
openapi_spec_validator
24
pytest-aiohttp
35
pytest~=3.6
46
pytest-cov~=2.5
5-
pytest-docker~=0.6
7+
pytest-docker~=0.6
8+
pytest-asyncio~=0.9.0
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
1+
"""Director service configuration
2+
"""
3+
14
import logging
25
import os
3-
from pathlib import Path
46

57
logging.basicConfig(
68
level=logging.DEBUG,
79
format='%(levelname)s:%(name)s-%(lineno)d: %(message)s'
810
)
911

10-
# FIXME: adapt to new config. Issue #195
1112
CONVERT_OLD_API = True
12-
13-
OPEN_API_BASE_FOLDER = Path(__file__).parent / "oas3/v1"
14-
OPEN_API_SPEC_FILE = "openapi.yaml"
15-
JSON_SCHEMA_BASE_FOLDER = Path(__file__).parent / "oas3/v1/schemas"
16-
NODE_JSON_SCHEMA_FILE = "node-meta-v0.0.1.json"
13+
API_VERSION = "v1"
1714

1815
REGISTRY_AUTH = os.environ.get("REGISTRY_AUTH", False) in ["true", "True"]
1916
REGISTRY_USER = os.environ.get("REGISTRY_USER", "")
2017
REGISTRY_PW = os.environ.get("REGISTRY_PW", "")
2118
REGISTRY_URL = os.environ.get("REGISTRY_URL", "")
19+
REGISTRY_SSL = os.environ.get("REGISTRY_SSL", True)
2220

2321
POSTGRES_ENDPOINT = os.environ.get("POSTGRES_ENDPOINT", "")
22+
POSTGRES_HOST = os.environ.get("POSTGRES_HOST", "")
23+
POSTGRES_PORT = os.environ.get("POSTGRES_PORT", "")
2424
POSTGRES_USER = os.environ.get("POSTGRES_USER", "")
2525
POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "")
2626
POSTGRES_DB = os.environ.get("POSTGRES_DB", "")
2727

28-
S3_ENDPOINT = os.environ.get("S3_ENDPOINT")
29-
S3_ACCESS_KEY = os.environ.get("S3_ACCESS_KEY")
30-
S3_SECRET_KEY = os.environ.get("S3_SECRET_KEY")
31-
S3_BUCKET_NAME = os.environ.get("S3_BUCKET_NAME")
28+
S3_ENDPOINT = os.environ.get("S3_ENDPOINT", "")
29+
S3_ACCESS_KEY = os.environ.get("S3_ACCESS_KEY", "")
30+
S3_SECRET_KEY = os.environ.get("S3_SECRET_KEY", "")
31+
S3_BUCKET_NAME = os.environ.get("S3_BUCKET_NAME", "")

services/director/src/simcore_service_director/main.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
import logging
44

55
from aiohttp import web
6-
7-
from simcore_service_director import config, registry_proxy
6+
from simcore_service_director import registry_proxy, resources
87
from simcore_service_director.rest import routing
98

109
log = logging.getLogger(__name__)
@@ -14,7 +13,8 @@ def main():
1413
registry_proxy.setup_registry_connection()
1514

1615
# create web app and serve
17-
app = routing.create_web_app(config.OPEN_API_BASE_FOLDER, config.OPEN_API_SPEC_FILE)
16+
api_spec_path = resources.get_path(resources.RESOURCE_OPEN_API)
17+
app = routing.create_web_app(api_spec_path.parent, api_spec_path.name)
1818
web.run_app(app, port=8001)
1919

2020
if __name__ == "__main__":

services/director/src/simcore_service_director/producer.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ def __add_network_to_service_runtime_params(docker_service_runtime_parameters, d
138138
def __add_env_variables_to_service_runtime_params(docker_service_runtime_parameters, service_uuid):
139139
variables = [
140140
"POSTGRES_ENDPOINT=" + config.POSTGRES_ENDPOINT,
141+
"POSTGRES_HOST=" + config.POSTGRES_HOST,
142+
"POSTGRES_PORT=" + config.POSTGRES_PORT,
141143
"POSTGRES_USER=" + config.POSTGRES_USER,
142144
"POSTGRES_PASSWORD=" + config.POSTGRES_PASSWORD,
143145
"POSTGRES_DB=" + config.POSTGRES_DB,
@@ -240,6 +242,7 @@ def __wait_until_service_running_or_failed(service_id, service_name, service_uui
240242
if task_state == "running":
241243
break
242244
elif task_state in ("failed", "rejected"):
245+
log.error("Error while waiting for service")
243246
raise exceptions.ServiceStartTimeoutError(service_name, service_uuid)
244247
# TODO: all these functions should be async and here one could use await sleep which
245248
# would allow dealing with other events instead of wasting time here
@@ -288,7 +291,7 @@ def __prepare_runtime_parameters(docker_image_path, tag, service_uuid, docker_cl
288291
__add_uuid_label_to_service_runtime_params(docker_service_runtime_parameters, service_uuid)
289292
__add_env_variables_to_service_runtime_params(docker_service_runtime_parameters, service_uuid)
290293
__set_service_name(docker_service_runtime_parameters,
291-
registry_proxy.get_interactive_service_sub_name(docker_image_path),
294+
registry_proxy.get_service_last_names(docker_image_path),
292295
service_uuid)
293296
return docker_service_runtime_parameters
294297

@@ -373,12 +376,12 @@ def start_service(service_key, service_tag, service_uuid):
373376

374377
# create services
375378
__login_docker_registry(docker_client)
376-
service_name = registry_proxy.get_service_name(service_key, registry_proxy.INTERACTIVE_SERVICES_PREFIX)
379+
service_name = registry_proxy.get_service_first_name(service_key)
377380
containers_meta_data = __create_services(docker_client, list_of_images, service_name, service_tag, service_uuid)
378381
# we return only the info of the main service
379382
return containers_meta_data[0]
380383

381-
def is_service_up(service_uuid):
384+
def get_service_details(service_uuid):
382385
# get the docker client
383386
docker_client = __get_docker_client()
384387
__login_docker_registry(docker_client)

services/director/src/simcore_service_director/registry_proxy.py

+48-47
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,20 @@
1212
INTERACTIVE_SERVICES_PREFIX = 'simcore/services/dynamic/'
1313
COMPUTATIONAL_SERVICES_PREFIX = 'simcore/services/comp/'
1414
_SESSION = Session()
15-
log = logging.getLogger(__name__)
15+
_logger = logging.getLogger(__name__)
1616

1717
def setup_registry_connection():
18-
log.debug("Setup registry connection started...%s", config.REGISTRY_AUTH)
18+
_logger.debug("Setup registry connection started...%s", config.REGISTRY_AUTH)
1919

2020
# get authentication state or set default value
2121
if config.REGISTRY_AUTH:
22-
log.debug("Authentifying registry...")
22+
_logger.debug("Authentifying registry...")
2323
if not config.REGISTRY_USER:
2424
raise exceptions.DirectorException("User to access to registry is not defined")
2525
if not config.REGISTRY_PW:
2626
raise exceptions.DirectorException("PW to access to registry is not defined")
2727
_SESSION.auth = (config.REGISTRY_USER, config.REGISTRY_PW)
28-
log.debug("Session authorization complete")
28+
_logger.debug("Session authorization complete")
2929

3030
def list_computational_services():
3131
return __list_services(COMPUTATIONAL_SERVICES_PREFIX)
@@ -34,12 +34,12 @@ def list_interactive_services():
3434
return __list_services(INTERACTIVE_SERVICES_PREFIX)
3535

3636
def get_service_details(service_key, service_version):
37-
return _get_repo_version_details(service_key, service_version)
37+
return __get_repo_version_details(service_key, service_version)
3838

3939
def retrieve_list_of_images_in_repo(repository_name):
4040
request_result = __registry_request(repository_name + '/tags/list')
4141
result_json = request_result.json()
42-
log.info("retrieved list of images in %s: %s",repository_name, result_json)
42+
_logger.info("retrieved list of images in %s: %s",repository_name, result_json)
4343
return result_json
4444

4545
def list_interactive_service_dependencies(service_key):
@@ -54,9 +54,9 @@ def list_interactive_service_dependencies(service_key):
5454
# ok let's get the dependencies
5555
dependencies = []
5656
repos = __retrieve_list_of_repositories()
57-
service_name = get_service_name(service_key, prefix)
57+
service_name = get_service_first_name(service_key)
5858
for repo in repos:
59-
if get_service_name(repo, prefix) == service_name:
59+
if get_service_first_name(repo) == service_name:
6060
if repo == service_key:
6161
continue
6262
dependencies.append(repo)
@@ -67,64 +67,64 @@ def retrieve_labels_of_image(image, tag):
6767
result_json = request_result.json()
6868
labels = json.loads(result_json["history"][0]["v1Compatibility"])[
6969
"container_config"]["Labels"]
70-
log.info("retrieved labels of image %s:%s: %s", image, tag, result_json)
70+
_logger.info("retrieved labels of image %s:%s: %s", image, tag, result_json)
7171
return labels
7272

73-
def get_service_name(repository_name, service_prefix):
74-
service_name_suffixes = str(repository_name)[len(service_prefix):]
75-
log.info("retrieved service name from repo %s : %s", repository_name, service_name_suffixes)
73+
def get_service_first_name(repository_name):
74+
if str(repository_name).startswith(INTERACTIVE_SERVICES_PREFIX):
75+
service_name_suffixes = str(repository_name)[len(INTERACTIVE_SERVICES_PREFIX):]
76+
elif str(repository_name).startswith(COMPUTATIONAL_SERVICES_PREFIX):
77+
service_name_suffixes = str(repository_name)[len(COMPUTATIONAL_SERVICES_PREFIX):]
78+
else:
79+
return "invalid service"
80+
81+
_logger.info("retrieved service name from repo %s : %s", repository_name, service_name_suffixes)
7682
return service_name_suffixes.split('/')[0]
7783

78-
def get_interactive_service_sub_name(repository_name):
79-
return __get_service_sub_name(repository_name, INTERACTIVE_SERVICES_PREFIX)
84+
def get_service_last_names(repository_name):
85+
if str(repository_name).startswith(INTERACTIVE_SERVICES_PREFIX):
86+
service_name_suffixes = str(repository_name)[len(INTERACTIVE_SERVICES_PREFIX):]
87+
elif str(repository_name).startswith(COMPUTATIONAL_SERVICES_PREFIX):
88+
service_name_suffixes = str(repository_name)[len(COMPUTATIONAL_SERVICES_PREFIX):]
89+
else:
90+
return "invalid service"
91+
service_last_name = str(service_name_suffixes).replace("/", "_")
92+
_logger.info("retrieved service last name from repo %s : %s", repository_name, service_last_name)
93+
return service_last_name
8094

8195
def __registry_request(path, method="GET"):
8296
if not config.REGISTRY_URL:
8397
raise exceptions.DirectorException("URL to registry is not defined")
84-
# TODO: is is always ssh?
85-
api_url = 'https://' + config.REGISTRY_URL + '/v2/' + path
98+
99+
if config.REGISTRY_SSL:
100+
api_url = 'https://' + config.REGISTRY_URL + '/v2/' + path
101+
else:
102+
api_url = 'http://' + config.REGISTRY_URL + '/v2/' + path
86103

87104
try:
88105
# r = s.get(api_url, verify=False) #getattr(s, method.lower())(api_url)
89106
request_result = getattr(_SESSION, method.lower())(api_url)
90-
log.info("Request status: %s",request_result.status_code)
91-
if request_result.status_code > 399:
107+
_logger.info("Request status: %s",request_result.status_code)
108+
if request_result.status_code > 399:
92109
request_result.raise_for_status()
93110

94111
return request_result
95112
except HTTPError as err:
96-
log.exception("HTTP error returned while accessing registry")
113+
_logger.exception("HTTP error returned while accessing registry")
97114
if err.response.status_code == 404:
98115
raise exceptions.ServiceNotAvailableError(path, None) from err
99116
raise exceptions.RegistryConnectionError("Error while accessing docker registry" ,err) from err
100117
except RequestException as err:
101-
log.exception("Error while connecting to docker registry")
118+
_logger.exception("Error while connecting to docker registry")
102119
raise exceptions.DirectorException(str(err)) from err
103120

104121
def __retrieve_list_of_repositories():
105122
request_result = __registry_request('_catalog')
106123
result_json = request_result.json()['repositories']
107-
log.info("retrieved list of repos: %s", result_json)
124+
_logger.info("retrieved list of repos: %s", result_json)
108125
return result_json
109126

110-
111-
def __has_service_sub_name(repository_name, service_prefix):
112-
try:
113-
__get_service_sub_name(repository_name, service_prefix)
114-
return True
115-
except exceptions.ServiceNotAvailableError:
116-
return False
117-
118-
def __get_service_sub_name(repository_name, service_prefix):
119-
service_name_suffixes = str(repository_name)[len(service_prefix):]
120-
list_of_suffixes = service_name_suffixes.split('/')
121-
last_suffix_index = len(list_of_suffixes) - 1
122-
if last_suffix_index < 0:
123-
raise exceptions.ServiceNotAvailableError(repository_name, None)
124-
log.info("retrieved service sub name from repo %s : %s", repository_name, list_of_suffixes)
125-
return list_of_suffixes[last_suffix_index]
126-
127-
def _get_repo_version_details(repo_key, repo_tag):
127+
def __get_repo_version_details(repo_key, repo_tag):
128128
image_tags = {}
129129
label_request = __registry_request(repo_key + '/manifests/' + repo_tag)
130130
label_data = label_request.json()
@@ -137,31 +137,32 @@ def _get_repo_version_details(repo_key, repo_tag):
137137
image_tags[label_key] = label_data[label_key]
138138
return image_tags
139139

140-
def _get_repo_details(repo):
140+
def __get_repo_details(repo):
141141
#pylint: disable=too-many-nested-blocks
142142
current_repo = []
143143
if "/comp/" in repo or "/dynamic/" in repo:
144144
# get list of repo versions
145145
req_images = __registry_request(repo + '/tags/list')
146146
im_data = req_images.json()
147147
tags = im_data['tags']
148-
for tag in tags:
149-
image_tags = _get_repo_version_details(repo, tag)
150-
if image_tags:
151-
current_repo.append(image_tags)
148+
if tags:
149+
for tag in tags:
150+
image_tags = __get_repo_version_details(repo, tag)
151+
if image_tags:
152+
current_repo.append(image_tags)
152153

153154
return current_repo
154155

155156
def __list_services(service_prefix):
156-
log.info("getting list of computational services")
157+
_logger.info("getting list of computational services")
157158
list_all_repos = __retrieve_list_of_repositories()
158159
# get the services repos
159160
list_of_specific_repos = [repo for repo in list_all_repos if str(repo).startswith(service_prefix)]
160-
log.info("retrieved list of computational repos : %s", list_of_specific_repos)
161+
_logger.info("retrieved list of computational repos : %s", list_of_specific_repos)
161162
repositories = []
162163
# or each repo get all tags details
163164
for repo in list_of_specific_repos:
164-
details = _get_repo_details(repo)
165+
details = __get_repo_details(repo)
165166
for repo_detail in details:
166167
repositories.append(repo_detail)
167168

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import functools
2+
from pathlib import Path
3+
4+
import pkg_resources
5+
from simcore_service_director import config
6+
7+
RESOURCE_OPENAPI_ROOT = "oas3"
8+
RESOURCE_OPEN_API = "{root}/{version}/openapi.yaml".format(root=RESOURCE_OPENAPI_ROOT, version=config.API_VERSION)
9+
RESOURCE_NODE_SCHEMA = "{root}/{version}/schemas/node-meta-v0.0.1.json".format(root=RESOURCE_OPENAPI_ROOT, version=config.API_VERSION)
10+
11+
"""
12+
List of pkg_resources functions *bound* to current package with the following signature
13+
14+
function(resource_name)
15+
16+
Note that resource names must be /-separated paths and
17+
cannot be absolute (i.e. no leading /) or contain relative names like "..".
18+
Do not use os.path routines to manipulate resource paths, as they are not filesystem paths.
19+
20+
Resources are read/only files/folders
21+
"""
22+
exists = functools.partial(pkg_resources.resource_exists, __name__)
23+
stream = functools.partial(pkg_resources.resource_stream, __name__)
24+
listdir = functools.partial(pkg_resources.resource_listdir, __name__)
25+
isdir = functools.partial(pkg_resources.resource_isdir, __name__)
26+
27+
28+
def get_path(resource_name):
29+
""" Returns a path to a resource
30+
31+
WARNING: existence of file is not guaranteed. Use resources.exists
32+
WARNING: resource files are supposed to be used as read-only!
33+
"""
34+
resource_path = Path( pkg_resources.resource_filename(__name__, resource_name) )
35+
return resource_path

0 commit comments

Comments
 (0)