Skip to content

Commit

Permalink
Fix the missing env variable issue (#5467)
Browse files Browse the repository at this point in the history
Fixes #5448, #5453, #5457

- Relaxed env var requirements in the images, no mandatory ones
- RQ workers now use a custom python class for remote debugging
- Factored out common remote debugging implementation
  • Loading branch information
zhiltsov-max committed Dec 16, 2022
1 parent 7b13216 commit f6d2a8f
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 82 deletions.
6 changes: 3 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
"rqworker",
"default",
"--worker-class",
"cvat.simpleworker.SimpleWorker",
"cvat.rqworker.SimpleWorker",
],
"django": true,
"cwd": "${workspaceFolder}",
Expand Down Expand Up @@ -179,7 +179,7 @@
"rqworker",
"low",
"--worker-class",
"cvat.simpleworker.SimpleWorker",
"cvat.rqworker.SimpleWorker",
],
"django": true,
"cwd": "${workspaceFolder}",
Expand All @@ -198,7 +198,7 @@
"rqworker",
"webhooks",
"--worker-class",
"cvat.simpleworker.SimpleWorker",
"cvat.rqworker.SimpleWorker",
],
"django": true,
"cwd": "${workspaceFolder}",
Expand Down
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,7 @@ COPY --from=build-image /opt/ffmpeg /usr

# These variables are required for supervisord substitutions in files
# This library allows remote python debugging with VS Code
ARG CVAT_DEBUG_ENABLED='no'
ENV CVAT_DEBUG_PORT=''
ARG CVAT_DEBUG_ENABLED
RUN if [ "${CVAT_DEBUG_ENABLED}" = 'yes' ]; then \
python3 -m pip install --no-cache-dir debugpy; \
fi
Expand Down
56 changes: 56 additions & 0 deletions cvat/rqworker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright (C) 2018-2022 Intel Corporation
# Copyright (C) 2022 CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT

from rq import Worker

import cvat.utils.remote_debugger as debug


DefaultWorker = Worker


class BaseDeathPenalty:
def __init__(self, timeout, exception, **kwargs):
pass

def __enter__(self):
pass

def __exit__(self, exc_type, exc_value, traceback):
pass


class SimpleWorker(Worker):
"""
Allows to work with at most 1 worker thread. Useful for debugging.
"""

death_penalty_class = BaseDeathPenalty

def main_work_horse(self, *args, **kwargs):
raise NotImplementedError("Test worker does not implement this method")

def execute_job(self, *args, **kwargs):
"""Execute job in same thread/process, do not fork()"""
return self.perform_job(*args, **kwargs)


if debug.is_debugging_enabled():
class RemoteDebugWorker(SimpleWorker):
"""
Support for VS code debugger
"""

def __init__(self, *args, **kwargs):
self.__debugger = debug.RemoteDebugger()
super().__init__(*args, **kwargs)

def execute_job(self, *args, **kwargs):
"""Execute job in same thread/process, do not fork()"""
self.__debugger.attach_current_thread()

return super().execute_job(*args, **kwargs)

DefaultWorker = RemoteDebugWorker
28 changes: 0 additions & 28 deletions cvat/simpleworker.py

This file was deleted.

59 changes: 59 additions & 0 deletions cvat/utils/remote_debugger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright (C) 2022 CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT

import os


def is_debugging_enabled() -> bool:
return os.environ.get('CVAT_DEBUG_ENABLED') == 'yes'

if is_debugging_enabled():
import debugpy

class RemoteDebugger:
"""
Support for VS code debugger.
Supports both single- and multi-thread scenarios.
Read docs: https://github.com/microsoft/debugpy
Read more: https://modwsgi.readthedocs.io/en/develop/user-guides/debugging-techniques.html
"""

ENV_VAR_PORT = 'CVAT_DEBUG_PORT'
ENV_VAR_WAIT = 'CVAT_DEBUG_WAIT'
__debugger_initialized = False

@classmethod
def _singleton_init(cls):
if cls.__debugger_initialized:
return

try:
port = int(os.environ[cls.ENV_VAR_PORT])

# The only intended use is in Docker.
# Using 127.0.0.1 will not allow host connections
addr = ('0.0.0.0', port) # nosec - B104:hardcoded_bind_all_interfaces

# Debugpy is a singleton
# We put it in the main thread of the process and then report new threads
debugpy.listen(addr)

# In most cases it makes no sense to debug subprocesses
# Feel free to enable if needed.
debugpy.configure({"subProcess": False})

if os.environ.get(cls.ENV_VAR_WAIT) == 'yes':
debugpy.wait_for_client()
except Exception as ex:
raise Exception("failed to set debugger") from ex

cls.__debugger_initialized = True

def __init__(self) -> None:
self._singleton_init()

def attach_current_thread(self) -> None:
debugpy.debug_this_thread()
41 changes: 8 additions & 33 deletions cvat/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,55 +14,30 @@
"""

import os

from django.core.wsgi import get_wsgi_application

import cvat.utils.remote_debugger as debug


os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cvat.settings.{}" \
.format(os.environ.get("DJANGO_CONFIGURATION", "development")))

application = get_wsgi_application()


if os.environ.get('CVAT_DEBUG_ENABLED') == 'yes':
import debugpy

class Debugger:
if debug.is_debugging_enabled():
class DebuggerApp:
"""
Support for VS code debugger
Read docs: https://github.com/microsoft/debugpy
Read more: https://modwsgi.readthedocs.io/en/develop/user-guides/debugging-techniques.html
"""

ENV_VAR_PORT = 'CVAT_DEBUG_PORT'
ENV_VAR_WAIT = 'CVAT_DEBUG_WAIT'

def __init__(self, obj):
self.__object = obj

port = int(os.environ[self.ENV_VAR_PORT])

# The only intended use is in Docker.
# Using 127.0.0.1 will not allow host connections
addr = ('0.0.0.0', port) # nosec - B104:hardcoded_bind_all_interfaces

try:
# Debugpy is a singleton
# We put it in the main thread of the process and then report new threads
debugpy.listen(addr)

# In most cases it makes no sense to debug subprocesses
# Feel free to enable if needed.
debugpy.configure({"subProcess": False})

if os.environ.get(self.ENV_VAR_WAIT) == 'yes':
debugpy.wait_for_client()
except Exception as ex:
print("failed to set debugger:", ex)
self.__debugger = debug.RemoteDebugger()

def __call__(self, *args, **kwargs):
debugpy.debug_this_thread()
self.__debugger.attach_current_thread()

return self.__object(*args, **kwargs)

application = Debugger(application)
application = DebuggerApp(application)
4 changes: 2 additions & 2 deletions site/content/en/docs/contributing/running-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ pytest ./tests/python --rebuild

**Debugging**

Currently, this is only supported in docker-compose deployments, which should be
enough to fix errors arising in REST API tests.
Currently, this is only supported in `docker-compose`-based deployments,
which should be enough to fix errors arising in REST API tests.

To debug a server deployed with Docker, you need to do the following:

Expand Down
9 changes: 2 additions & 7 deletions supervisord/worker.default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,8 @@ autorestart=true

[program:rqworker_default]
command=%(ENV_HOME)s/wait-for-it.sh %(ENV_CVAT_REDIS_HOST)s:6379 -t 0 -- bash -ic " \
if [ \"%(ENV_CVAT_DEBUG_ENABLED)s\" = 'yes' ]; then \
_DEBUG_CMD=\"-m debugpy --listen 0.0.0.0:%(ENV_CVAT_DEBUG_PORT)s\";
else
_DEBUG_CMD=\"\";
fi && \
\
exec python3 ${_DEBUG_CMD} %(ENV_HOME)s/manage.py rqworker -v 3 default \
exec python3 %(ENV_HOME)s/manage.py rqworker -v 3 default \
--worker-class cvat.rqworker.DefaultWorker \
"
environment=SSH_AUTH_SOCK="/tmp/ssh-agent.sock"
numprocs=%(ENV_NUMPROCS)s
Expand Down
9 changes: 2 additions & 7 deletions supervisord/worker.low.conf
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,8 @@ autorestart=true

[program:rqworker_low]
command=%(ENV_HOME)s/wait-for-it.sh %(ENV_CVAT_REDIS_HOST)s:6379 -t 0 -- bash -ic " \
if [ \"%(ENV_CVAT_DEBUG_ENABLED)s\" = 'yes' ]; then \
_DEBUG_CMD=\"-m debugpy --listen 0.0.0.0:%(ENV_CVAT_DEBUG_PORT)s\";
else
_DEBUG_CMD=\"\";
fi && \
\
exec python3 ${_DEBUG_CMD} %(ENV_HOME)s/manage.py rqworker -v 3 low \
exec python3 %(ENV_HOME)s/manage.py rqworker -v 3 low \
--worker-class cvat.rqworker.DefaultWorker \
"
environment=SSH_AUTH_SOCK="/tmp/ssh-agent.sock"
numprocs=%(ENV_NUMPROCS)s
Expand Down

0 comments on commit f6d2a8f

Please sign in to comment.