Fix serverless functions on Mac (#29)

* Fix serverless functions on Mac. Use host.docker.internal as an alias for localhost.
* Updated the CHANGELOG.md
* Fix unit tests for the lambda manager.
main
Nikita Manovich 4 years ago committed by GitHub
parent c266a9421c
commit ff30b776cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

2
.gitignore vendored

@ -4,7 +4,7 @@
/share/ /share/
/static/ /static/
/db.sqlite3 /db.sqlite3
/.env /.*env*
/keys /keys
/logs /logs
/profiles /profiles

@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- TDB - Bumped nuclio version to 1.8.14 (<https://github.com/cvat-ai/cvat/pull/29>)
### Deprecated ### Deprecated
- TDB - TDB

@ -2,7 +2,7 @@ version: '3.3'
services: services:
nuclio: nuclio:
container_name: nuclio container_name: nuclio
image: quay.io/nuclio/dashboard:1.5.16-amd64 image: quay.io/nuclio/dashboard:1.8.14-amd64
restart: always restart: always
networks: networks:
- cvat - cvat

@ -75,66 +75,65 @@ class LambdaTestCase(APITestCase):
def setUp(self): def setUp(self):
self.client = APIClient() self.client = APIClient()
patcher = mock.patch('cvat.apps.lambda_manager.views.LambdaGateway._http', side_effect = self.__get_response_data_from_lambda_gateway_http) http_patcher = mock.patch('cvat.apps.lambda_manager.views.LambdaGateway._http', side_effect = self.__get_data_from_lambda_manager_http)
self.addCleanup(patcher.stop) self.addCleanup(http_patcher.stop)
patcher.start() http_patcher.start()
invoke_patcher = mock.patch('cvat.apps.lambda_manager.views.LambdaGateway.invoke', side_effect = self.__invoke_function)
self.addCleanup(invoke_patcher.stop)
invoke_patcher.start()
images_main_task = self._generate_task_images(3) images_main_task = self._generate_task_images(3)
images_assigneed_to_user_task = self._generate_task_images(3) images_assigneed_to_user_task = self._generate_task_images(3)
self.main_task = self._create_task(tasks["main"], images_main_task) self.main_task = self._create_task(tasks["main"], images_main_task)
self.assigneed_to_user_task = self._create_task(tasks["assigneed_to_user"], images_assigneed_to_user_task) self.assigneed_to_user_task = self._create_task(tasks["assigneed_to_user"], images_assigneed_to_user_task)
def __get_data_from_lambda_manager_http(self, **kwargs):
def __get_response_data_from_lambda_gateway_http(self, *args, **kwargs):
url = kwargs["url"] url = kwargs["url"]
# POST query for get annotations if url == "/api/functions":
if url == "/api/function_invocations":
data = []
id_function = kwargs["headers"]["x-nuclio-function-name"]
type_function = functions["positive"][id_function]["metadata"]["annotations"]["type"]
if type_function == "reid":
if id_function == id_function_reid_response_data:
data = [0, 1]
else:
data = []
elif type_function == "tracker":
data = {
"shape": [12.34, 34.0, 35.01, 41.99],
"state": {"key": "value"},
}
elif type_function == "interactor":
data = [
[8, 12],
[34, 56],
[77, 77],
]
elif type_function == "detector":
data = [
{'confidence': '0.9959098', 'label': 'car', 'points': [3, 3, 15, 15], 'type': 'rectangle'},
{'confidence': '0.89535173', 'label': 'car', 'points': [20, 25, 30, 35], 'type': 'rectangle'},
{'confidence': '0.59464583', 'label': 'car', 'points': [12.17, 45.0, 69.80, 18.99], 'type': 'polygon'},
]
return data
# GET query for get all functions
elif url == "/api/functions":
return functions["positive"] return functions["positive"]
# GET query for get function
else: else:
id_function = url.split("/")[-1] func_id = url.split("/")[-1]
if id_function in functions["positive"]: if func_id in functions["positive"]:
# raise 500 Internal_Server error if func_id in [id_function_state_building, id_function_state_error]:
if id_function in [id_function_state_building, id_function_state_error]:
r = requests.RequestException() r = requests.RequestException()
r.response = HttpResponseServerError() r.response = HttpResponseServerError()
raise r raise r # raise 500 Internal_Server error
# return values
return functions["positive"][id_function] return functions["positive"][func_id]
# raise 404 Not Found error
else: else:
r = requests.HTTPError() r = requests.HTTPError()
r.response = HttpResponseNotFound() r.response = HttpResponseNotFound()
raise r raise r # raise 404 Not Found error
def __invoke_function(self, func, payload):
data = []
func_id = func.id
type_function = functions["positive"][func_id]["metadata"]["annotations"]["type"]
if type_function == "reid":
if func_id == id_function_reid_response_data:
data = [0, 1]
else:
data = []
elif type_function == "tracker":
data = {
"shape": [12.34, 34.0, 35.01, 41.99],
"state": {"key": "value"},
}
elif type_function == "interactor":
data = [
[8, 12],
[34, 56],
[77, 77],
]
elif type_function == "detector":
data = [
{'confidence': '0.9959098', 'label': 'car', 'points': [3, 3, 15, 15], 'type': 'rectangle'},
{'confidence': '0.89535173', 'label': 'car', 'points': [20, 25, 30, 35], 'type': 'rectangle'},
{'confidence': '0.59464583', 'label': 'car', 'points': [12.17, 45.0, 69.80, 18.99], 'type': 'polygon'},
]
return data
@classmethod @classmethod
def _create_db_users(cls): def _create_db_users(cls):

@ -11,6 +11,7 @@ from copy import deepcopy
import django_rq import django_rq
import requests import requests
import rq import rq
import os
from django.conf import settings from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.exceptions import ObjectDoesNotExist, ValidationError
from rest_framework import status, viewsets from rest_framework import status, viewsets
@ -75,17 +76,18 @@ class LambdaGateway:
return response return response
def invoke(self, func, payload): def invoke(self, func, payload):
# NOTE: it is overhead to invoke a function using nuclio # Note: call the function directly without the nuclio dashboard
# dashboard REST API. Better to call host.docker.internal:<port> # host.docker.internal for Linux will work only with Docker 20.10+
# Look at https://github.com/docker/for-linux/issues/264. NUCLIO_TIMEOUT = settings.NUCLIO['DEFAULT_TIMEOUT']
# host.docker.internal isn't supported by docker on Linux. if os.path.exists('/.dockerenv'): # inside a docker container
# There are many workarounds but let's try to use the url = f'http://host.docker.internal:{func.port}'
# simple solution. else:
return self._http(method="post", url='/api/function_invocations', url = f'http://localhost:{func.port}'
data=payload, headers={ reply = requests.post(url, timeout=NUCLIO_TIMEOUT, json=payload)
'x-nuclio-function-name': func.id, reply.raise_for_status()
'x-nuclio-path': '/' response = reply.json()
})
return response
class LambdaFunction: class LambdaFunction:
def __init__(self, gateway, data): def __init__(self, gateway, data):

@ -52,6 +52,8 @@ services:
- cvat_logs:/home/django/logs - cvat_logs:/home/django/logs
networks: networks:
- cvat - cvat
extra_hosts:
- "host.docker.internal:host-gateway"
cvat_ui: cvat_ui:
container_name: cvat_ui container_name: cvat_ui

@ -29,7 +29,7 @@ description: 'Information about the installation of components needed for semi-a
``` ```
- You have to install `nuctl` command line tool to build and deploy serverless - You have to install `nuctl` command line tool to build and deploy serverless
functions. Download [version 1.5.16](https://github.com/nuclio/nuclio/releases/tag/1.5.16). functions. Download [version 1.8.14](https://github.com/nuclio/nuclio/releases/tag/1.8.14).
It is important that the version you download matches the version in It is important that the version you download matches the version in
[docker-compose.serverless.yml](https://github.com/openvinotoolkit/cvat/blob/develop/components/serverless/docker-compose.serverless.yml). [docker-compose.serverless.yml](https://github.com/openvinotoolkit/cvat/blob/develop/components/serverless/docker-compose.serverless.yml).
For example, using wget. For example, using wget.

Loading…
Cancel
Save