Django Celery issues within Docker - django-celery

Having a major issue I cannot track down and I'm spinning my wheels. Any help would be greatly appreciated. I have django, celerybeat and celeryworker containers that all use the same base image of django.
local.yml
version: "3"
volumes:
local_postgres_data: {}
local_postgres_data_backups: {}
services:
django: &django
build:
context: .
dockerfile: ./compose/local/django/Dockerfile
image: local_django
container_name: django
depends_on:
- postgres
- mailhog
- redis
volumes:
- .:/app
env_file:
- ./.envs/.local/.django
- ./.envs/.local/.postgres
ports:
- "8000:8000"
command: /start
restart: always
postgres:
build:
context: .
dockerfile: ./compose/production/postgres/Dockerfile
image: production_postgres
container_name: postgres
volumes:
- local_postgres_data:/var/lib/postgresql/data
- local_postgres_data_backups:/backups
env_file:
- ./.envs/.local/.postgres
ports:
- "5432:5432"
mailhog:
image: mailhog/mailhog:v1.0.0
container_name: mailhog
ports:
- "8025:8025"
logging:
driver: "none" # disable saving logs
redis:
image: redis:6
container_name: redis
celeryworker:
<<: *django
image: local_celeryworker
container_name: celeryworker
depends_on:
- redis
- postgres
- mailhog
ports: []
command: /start-celeryworker
restart: always
celerybeat:
<<: *django
image: local_celerybeat
container_name: celerybeat
depends_on:
- redis
- postgres
- mailhog
ports: []
command: /start-celerybeat
restart: always
node:
build:
context: .
dockerfile: ./compose/local/node/Dockerfile
image: local_node
container_name: node
depends_on:
- django
volumes:
- .:/app
# http://jdlm.info/articles/2016/03/06/lessons-building-node-app-docker.html
- /app/node_modules
command: npm run dev
ports:
- "3000:3000"
# Expose browsersync UI: https://www.browsersync.io/docs/options/#option-ui
- "3001:3001"
compose/local/django/Dockerfile
ARG PYTHON_VERSION=3.9-slim-bullseye
# define an alias for the specfic python version used in this file.
FROM python:${PYTHON_VERSION} as python
# Python build stage
FROM python as python-build-stage
ARG BUILD_ENVIRONMENT=local
# Install apt packages
RUN apt-get update && apt-get install --no-install-recommends -y \
# dependencies for building Python packages
build-essential \
# psycopg2 dependencies
libpq-dev
# Requirements are installed here to ensure they will be cached.
COPY ./requirements .
# Create Python Dependency and Sub-Dependency Wheels.
RUN pip wheel --wheel-dir /usr/src/app/wheels \
-r ${BUILD_ENVIRONMENT}.txt
# Python 'run' stage
FROM python as python-run-stage
ARG BUILD_ENVIRONMENT=local
ARG APP_HOME=/app
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
ENV BUILD_ENV ${BUILD_ENVIRONMENT}
WORKDIR ${APP_HOME}
# Install required system dependencies
RUN apt-get update && apt-get install --no-install-recommends -y \
# psycopg2 dependencies
libpq-dev \
# Translations dependencies
gettext \
# cleaning up unused files
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists/*
# All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction
# copy python dependency wheels from python-build-stage
COPY --from=python-build-stage /usr/src/app/wheels /wheels/
# use wheels to install python dependencies
RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \
&& rm -rf /wheels/
COPY ./compose/production/django/entrypoint /entrypoint
RUN sed -i 's/\r$//g' /entrypoint
RUN chmod +x /entrypoint
COPY ./compose/local/django/start /start
RUN sed -i 's/\r$//g' /start
RUN chmod +x /start
COPY ./compose/local/django/celery/worker/start /start-celeryworker
RUN sed -i 's/\r$//g' /start-celeryworker
RUN chmod +x /start-celeryworker
COPY ./compose/local/django/celery/beat/start /start-celerybeat
RUN sed -i 's/\r$//g' /start-celerybeat
RUN chmod +x /start-celerybeat
COPY ./compose/local/django/celery/flower/start /start-flower
RUN sed -i 's/\r$//g' /start-flower
RUN chmod +x /start-flower
# copy application code to WORKDIR
COPY . ${APP_HOME}
ENTRYPOINT ["/entrypoint"]
config/celery_app.py
import os
from celery import Celery
# set the default Django settings module for the 'celery' program.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
app = Celery("myapp")
# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object("django.conf:settings", namespace="CELERY")
# Load task modules from all registered Django app configs.
app.autodiscover_tasks()
config/__init__.py
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery_app import app as celery_app
__all__ = ("celery_app",)
compose/local/django/celery/beat/start
#!/bin/bash
set -o errexit
set -o nounset
rm -f './celerybeat.pid'
celery -A config.celery_app beat -l INFO
compose/local/django/celery/worker/start
#!/bin/bash
set -o errexit
set -o nounset
watchgod celery.__main__.main --args -A config.celery_app worker -l INFO
When running docker-compose up without celeryworker or celerybeat the modules are found fine in the django container. But as soon as I add the celeryworker or celeryworker I get the following about not being able to find modules.
celery beat v5.2.7 (dawn-chorus) is starting.
2022-08-09 14:07:41,879 [celery.utils.dispatch.signal:280] ERROR - Signal handler <bound method DjangoFixup.on_import_modules of <celery.fixups.django.DjangoFixup object at 0x7f6cff68e670>> raised: ModuleNotFoundError("No module named 'customadmin'")
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/celery/utils/dispatch/signal.py", line 276, in send
response = receiver(signal=self, sender=sender, **named)
File "/usr/local/lib/python3.9/site-packages/celery/fixups/django.py", line 82, in on_import_modules
self.worker_fixup.validate_models()
File "/usr/local/lib/python3.9/site-packages/celery/fixups/django.py", line 120, in validate_models
self.django_setup()
File "/usr/local/lib/python3.9/site-packages/celery/fixups/django.py", line 116, in django_setup
django.setup()
File "/usr/local/lib/python3.9/site-packages/django/__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "/usr/local/lib/python3.9/site-packages/django/apps/registry.py", line 91, in populate
app_config = AppConfig.create(entry)
File "/usr/local/lib/python3.9/site-packages/django/apps/config.py", line 224, in create
import_module(entry)
File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'customadmin'
2022-08-09 14:07:41,880 [celery.utils.dispatch.signal:280] ERROR - Signal handler <promise#0x7f6cff6491f0 --> <bound method Celery._autodiscover_tasks of <Celery bahamar at 0x7f6cff68eee0>>> raised: AppRegistryNotReady("Apps aren't loaded yet.")
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/celery/utils/dispatch/signal.py", line 276, in send
response = receiver(signal=self, sender=sender, **named)
File "/usr/local/lib/python3.9/site-packages/vine/promises.py", line 160, in __call__
return self.throw()
File "/usr/local/lib/python3.9/site-packages/vine/promises.py", line 157, in __call__
retval = fun(*final_args, **final_kwargs)
File "/usr/local/lib/python3.9/site-packages/celery/app/base.py", line 689, in _autodiscover_tasks
return self._autodiscover_tasks_from_fixups(related_name)
File "/usr/local/lib/python3.9/site-packages/celery/app/base.py", line 698, in _autodiscover_tasks_from_fixups
return self._autodiscover_tasks_from_names([
File "/usr/local/lib/python3.9/site-packages/celery/app/base.py", line 701, in <listcomp>
for pkg in fixup.autodiscover_tasks()
File "/usr/local/lib/python3.9/site-packages/celery/fixups/django.py", line 92, in autodiscover_tasks
return [config.name for config in apps.get_app_configs()]
File "/usr/local/lib/python3.9/site-packages/django/apps/registry.py", line 145, in get_app_configs
self.check_apps_ready()
File "/usr/local/lib/python3.9/site-packages/django/apps/registry.py", line 136, in check_apps_ready
raise AppRegistryNotReady("Apps aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
Traceback (most recent call last):
File "/usr/local/bin/celery", line 8, in <module>
sys.exit(main())
File "/usr/local/lib/python3.9/site-packages/celery/__main__.py", line 15, in main
sys.exit(_main())
File "/usr/local/lib/python3.9/site-packages/celery/bin/celery.py", line 217, in main
return celery(auto_envvar_prefix="CELERY")
File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1130, in __call__
return self.main(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1055, in main
rv = self.invoke(ctx)
File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/lib/python3.9/site-packages/click/core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/click/decorators.py", line 26, in new_func
return f(get_current_context(), *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/celery/bin/base.py", line 134, in caller
return f(ctx, *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/celery/bin/beat.py", line 72, in beat
return beat().run()
File "/usr/local/lib/python3.9/site-packages/celery/apps/beat.py", line 75, in run
self.init_loader()
File "/usr/local/lib/python3.9/site-packages/celery/apps/beat.py", line 124, in init_loader
self.app.loader.init_worker()
File "/usr/local/lib/python3.9/site-packages/celery/loaders/base.py", line 111, in init_worker
self.import_default_modules()
File "/usr/local/lib/python3.9/site-packages/celery/loaders/base.py", line 105, in import_default_modules
raise response
File "/usr/local/lib/python3.9/site-packages/celery/utils/dispatch/signal.py", line 276, in send
response = receiver(signal=self, sender=sender, **named)
File "/usr/local/lib/python3.9/site-packages/celery/fixups/django.py", line 82, in on_import_modules
self.worker_fixup.validate_models()
File "/usr/local/lib/python3.9/site-packages/celery/fixups/django.py", line 120, in validate_models
self.django_setup()
File "/usr/local/lib/python3.9/site-packages/celery/fixups/django.py", line 116, in django_setup
django.setup()
File "/usr/local/lib/python3.9/site-packages/django/__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "/usr/local/lib/python3.9/site-packages/django/apps/registry.py", line 91, in populate
app_config = AppConfig.create(entry)
File "/usr/local/lib/python3.9/site-packages/django/apps/config.py", line 224, in create
import_module(entry)
File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'customadmin'
customadmin just so happens to be the first app of many apps in my LOCAL_APPS.
Django 3.2.14
celery 5.2.7
django-celery-beat 2.3.0
django-celery-results 2.2.0

Related

Ansible - HTTP Error 502: Bad Gateway when using script with XMLRPC

I have this script, that connects to service via XMLRPC, like:
import odoorpc
odoo = odoorpc.ODOO(host, protocol=protocol, port=port)
Task from yaml file:
- name: Change Odoo admin user password
ansible.builtin.script: "change-user-password \
{{ secrets_odoo_init_user_passwords_file }} --ignore-failed-login \
--host {{ ansible_host }} --port 443 --protocol jsonrpc+ssl"
become: true
become_user: "{{ app_user }}"
Call looks like this (from ansible logs):
<my-host> SSH: EXEC ssh -o ControlMaster=auto -o ControlPersist=30m -o PreferredAuthentications=publickey -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/tmp/ansible-ssh-%h-%p-%r -tt my-host '/bin/sh -c '"'"'sudo -H -S -n -u odoo /bin/sh -c '"'"'"'"'"'"'"'"'echo BECOME-SUCCESS-lsrscdzqfzfliptrdleqjxcwaseuaggn ; /var/tmp/ansible-tmp-1635367335.375884-2165879-123865031409885/change-user-password /opt/odoo/.secrets/odoo_init_user_passwords_file --ignore-failed-login --host my-host --port 443 --protocol jsonrpc+ssl'"'"'"'"'"'"'"'"' && sleep 0'"'"''
The full traceback is:
NoneType: None
fatal: [dev]: FAILED! => {
"changed": true,
"msg": "non-zero return code",
"rc": 1,
"stderr": "Shared connection to my-host closed.\r\n",
"stderr_lines": [
"Shared connection to my-host closed."
],
"stdout": "Traceback (most recent call last):\r\n File \"/var/tmp/ansible-tmp-1635367335.375884-2165879-123865031409885/change-user-password\", line 109, in <module>\r\n odoo = login(\r\n File \"/var/tmp/ansible-tmp-1635367335.375884-2165879-123865031409885/change-user-password\", line 75, in login\r\n odoo = odoorpc.ODOO(host, protocol=protocol, port=port)\r\n File \"/opt/odoo/.local/lib/python3.9/site-packages/odoorpc/odoo.py\", line 95, in __init__\r\n self._connector = rpc.PROTOCOLS[protocol](\r\n File \"/opt/odoo/.local/lib/python3.9/site-packages/odoorpc/rpc/__init__.py\", line 288, in __init__\r\n super(ConnectorJSONRPCSSL, self).__init__(\r\n File \"/opt/odoo/.local/lib/python3.9/site-packages/odoorpc/rpc/__init__.py\", line 211, in __init__\r\n self._proxy_json, self._proxy_http = self._get_proxies()\r\n File \"/opt/odoo/.local/lib/python3.9/site-packages/odoorpc/rpc/__init__.py\", line 235, in _get_proxies\r\n result = proxy_json('/web/webclient/version_info')['result']\r\n File \"/opt/odoo/.local/lib/python3.9/site-packages/odoorpc/rpc/jsonrpclib.py\", line 113, in __call__\r\n response = self._opener.open(request, timeout=self._timeout)\r\n File \"/usr/lib/python3.9/urllib/request.py\", line 523, in open\r\n response = meth(req, response)\r\n File \"/usr/lib/python3.9/urllib/request.py\", line 632, in http_response\r\n response = self.parent.error(\r\n File \"/usr/lib/python3.9/urllib/request.py\", line 561, in error\r\n return self._call_chain(*args)\r\n File \"/usr/lib/python3.9/urllib/request.py\", line 494, in _call_chain\r\n result = func(*args)\r\n File \"/usr/lib/python3.9/urllib/request.py\", line 641, in http_error_default\r\n raise HTTPError(req.full_url, code, msg, hdrs, fp)\r\nurllib.error.HTTPError: HTTP Error 502: Bad Gateway\r\n",
"stdout_lines": [
"Traceback (most recent call last):",
" File \"/var/tmp/ansible-tmp-1635367335.375884-2165879-123865031409885/change-user-password\", line 109, in <module>",
" odoo = login(",
" File \"/var/tmp/ansible-tmp-1635367335.375884-2165879-123865031409885/change-user-password\", line 75, in login",
" odoo = odoorpc.ODOO(host, protocol=protocol, port=port)",
" File \"/opt/odoo/.local/lib/python3.9/site-packages/odoorpc/odoo.py\", line 95, in __init__",
" self._connector = rpc.PROTOCOLS[protocol](",
" File \"/opt/odoo/.local/lib/python3.9/site-packages/odoorpc/rpc/__init__.py\", line 288, in __init__",
" super(ConnectorJSONRPCSSL, self).__init__(",
" File \"/opt/odoo/.local/lib/python3.9/site-packages/odoorpc/rpc/__init__.py\", line 211, in __init__",
" self._proxy_json, self._proxy_http = self._get_proxies()",
" File \"/opt/odoo/.local/lib/python3.9/site-packages/odoorpc/rpc/__init__.py\", line 235, in _get_proxies",
" result = proxy_json('/web/webclient/version_info')['result']",
" File \"/opt/odoo/.local/lib/python3.9/site-packages/odoorpc/rpc/jsonrpclib.py\", line 113, in __call__",
" response = self._opener.open(request, timeout=self._timeout)",
" File \"/usr/lib/python3.9/urllib/request.py\", line 523, in open",
" response = meth(req, response)",
" File \"/usr/lib/python3.9/urllib/request.py\", line 632, in http_response",
" response = self.parent.error(",
" File \"/usr/lib/python3.9/urllib/request.py\", line 561, in error",
" return self._call_chain(*args)",
" File \"/usr/lib/python3.9/urllib/request.py\", line 494, in _call_chain",
" result = func(*args)",
" File \"/usr/lib/python3.9/urllib/request.py\", line 641, in http_error_default",
" raise HTTPError(req.full_url, code, msg, hdrs, fp)",
"urllib.error.HTTPError: HTTP Error 502: Bad Gateway"
]
}
I read that people workaround this error by using headers={'User-Agent': 'Mozilla/5.0'}. But if I use script module via ansible, how can I specify such header?
The thing is, I only get this error if I run script via ansible. I tried running that script explicitly from my machine and from target machine. In those cases, it works fine.
ansible [core 2.11.6]
config file = /home/oerp/.ansible.cfg
configured module search path = ['/home/oerp/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/oerp/.local/lib/python3.8/site-packages/ansible
ansible collection location = /home/oerp/.ansible/collections:/usr/share/ansible/collections
executable location = /home/oerp/.local/bin/ansible
python version = 3.8.10 (default, Sep 28 2021, 16:10:42) [GCC 9.3.0]
jinja version = 3.0.1
libyaml = True
Update
Turn out the problem when XMLPRC call is made, service is not yet running, thus the error.
So I have to use RPC only when service is already running.
I tried using wait_for like this (note, my Odoo service port is not exposed via compose, its only accessible through docker network by nginx):
- name: Save Odoo container status
set_fact:
odoo_ip: "{{ docker_compose_output.services.odoo.app_odoo_1.networks.app_backend.IPAddress }}"
- name: Wait for IP to be active
wait_for:
host: "{{ odoo_ip }}"
delay: 1
state: drained
I do get this error:
The full traceback is:
Traceback (most recent call last):
File "/root/.ansible/tmp/ansible-tmp-1635403378.5625799-2174550-176521610555803/AnsiballZ_wait_for.py", line 100, in <module>
_ansiballz_main()
File "/root/.ansible/tmp/ansible-tmp-1635403378.5625799-2174550-176521610555803/AnsiballZ_wait_for.py", line 92, in _ansiballz_main
invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
File "/root/.ansible/tmp/ansible-tmp-1635403378.5625799-2174550-176521610555803/AnsiballZ_wait_for.py", line 40, in invoke_module
runpy.run_module(mod_name='ansible.modules.wait_for', init_globals=dict(_module_fqn='ansible.modules.wait_for', _modlib_path=modlib_path),
File "/usr/lib/python3.9/runpy.py", line 210, in run_module
return _run_module_code(code, init_globals, run_name, mod_spec)
File "/usr/lib/python3.9/runpy.py", line 97, in _run_module_code
_run_code(code, mod_globals, init_globals,
File "/usr/lib/python3.9/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "/tmp/ansible_wait_for_payload_pic_t21b/ansible_wait_for_payload.zip/ansible/modules/wait_for.py", line 674, in <module>
File "/tmp/ansible_wait_for_payload_pic_t21b/ansible_wait_for_payload.zip/ansible/modules/wait_for.py", line 657, in main
File "/tmp/ansible_wait_for_payload_pic_t21b/ansible_wait_for_payload.zip/ansible/modules/wait_for.py", line 356, in __init__
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'
fatal: [dev]: FAILED! => {
"changed": false,
"module_stderr": "Shared connection to my-host closed.\r\n",
"module_stdout": "Traceback (most recent call last):\r\n File \"/root/.ansible/tmp/ansible-tmp-1635403378.5625799-2174550-176521610555803/AnsiballZ_wait_for.py\", line 100, in <module>\r\n _ansiballz_main()\r\n File \"/root/.ansible/tmp/ansible-tmp-1635403378.5625799-2174550-176521610555803/AnsiballZ_wait_for.py\", line 92, in _ansiballz_main\r\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n File \"/root/.ansible/tmp/ansible-tmp-1635403378.5625799-2174550-176521610555803/AnsiballZ_wait_for.py\", line 40, in invoke_module\r\n runpy.run_module(mod_name='ansible.modules.wait_for', init_globals=dict(_module_fqn='ansible.modules.wait_for', _modlib_path=modlib_path),\r\n File \"/usr/lib/python3.9/runpy.py\", line 210, in run_module\r\n return _run_module_code(code, init_globals, run_name, mod_spec)\r\n File \"/usr/lib/python3.9/runpy.py\", line 97, in _run_module_code\r\n _run_code(code, mod_globals, init_globals,\r\n File \"/usr/lib/python3.9/runpy.py\", line 87, in _run_code\r\n exec(code, run_globals)\r\n File \"/tmp/ansible_wait_for_payload_pic_t21b/ansible_wait_for_payload.zip/ansible/modules/wait_for.py\", line 674, in <module>\r\n File \"/tmp/ansible_wait_for_payload_pic_t21b/ansible_wait_for_payload.zip/ansible/modules/wait_for.py\", line 657, in main\r\n File \"/tmp/ansible_wait_for_payload_pic_t21b/ansible_wait_for_payload.zip/ansible/modules/wait_for.py\", line 356, in __init__\r\nTypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'\r\n",
"msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
"rc": 1
}
Its unclear what argument it is about.
Turns out, the problem was that service (container) I was trying to connect with RPC was not yet running and that was causing bad gateway problem.
In my playbook, container is stopped and then started and just after initiating start, it tries to use RPC, which would fail (as my reverse proxy, which is nginx, would not yet see odoo service).
So simple fix is this:
- name: Pause for 3 seconds for Odoo service to load
pause:
seconds: 3
Though its not ideal as I can't be sure that I would need 3 seconds. It might be too long or too fast.
I tried using wait_for, but got some confusing errors. I think wait_for is better approach as it could react better than simple pause.
Update
Better way would be to use wait_for, so we don't need to specify specific delay time. Like (this way it waits till service is ready to be used):
- name: Save Odoo container status
set_fact:
odoo_ip: "{{ docker_compose_output.services.odoo.app_odoo_1.networks\
.app_backend.IPAddress }}"
- name: Wait for IP to be active
wait_for:
host: "{{ odoo_ip }}"
port: 8069
state: started

Running CI tests with PyQT

I am setting up a CI Bitbucket pipeline for my team. We are using pytest and pytest-qt to test our software. The tests run locally without any issue, but the build fails using pipeline. Here is the pipeline yml:
image: python:3.9
pipelines:
default:
- parallel:
- step:
name: Test
caches:
- pip
script:
- if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- pip install pytest
- pip install pytest-qt
- pytest -v tools/IntegrationTests.py --junitxml=test-reports/report.xml
The file requirements.txt contains several modules including:
PyQt5==5.15.4
PyQt5-Qt5==5.15.2
And here is the output when trying to build:
+ pytest -v tools/IntegrationTests.py --junitxml=test-reports/report.xml
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/_pytest/main.py", line 265, in wrap_session
INTERNALERROR> config._do_configure()
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/_pytest/config/__init__.py", line 982, in _do_configure
INTERNALERROR> self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/pluggy/hooks.py", line 308, in call_historic
INTERNALERROR> res = self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR> return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/pluggy/manager.py", line 84, in <lambda>
INTERNALERROR> self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR> return outcome.get_result()
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR> raise ex[1].with_traceback(ex[2])
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR> res = hook_impl.function(*args)
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/pytestqt/plugin.py", line 203, in pytest_configure
INTERNALERROR> qt_api.set_qt_api(config.getini("qt_api"))
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/pytestqt/qt_compat.py", line 104, in set_qt_api
INTERNALERROR> self.QtGui = _import_module("QtGui")
INTERNALERROR> File "/usr/local/lib/python3.9/site-packages/pytestqt/qt_compat.py", line 100, in _import_module
INTERNALERROR> m = __import__(_root_module, globals(), locals(), [module_name], 0)
INTERNALERROR> ImportError: libGL.so.1: cannot open shared object file: No such file or directory
What I get from that is that Qt is not properly installed remotely, thus the tests cannot run.
My question is: can GUI be tested in CI, and if yes, how?
EDIT:
I have broke down to test only the import of PyQt and it boiled down to this test suite:
def test_import_widgets():
from PyQt5 import QtWidgets
def test_import_core():
from PyQt5 import QtCore
def test_import_gui():
from PyQt5 import QtGui
def test_import_qt():
from PyQt5.QtCore import Qt
Which have resulted in the following results:
2 / 4 tests failed
BasicsTests.test_import_guitools
<1s
ImportError: libGL.so.1: cannot open shared object file: No such file or directory
def test_import_gui():
> from PyQt5 import QtGui
E ImportError: libGL.so.1: cannot open shared object file: No such file or directory
tools/BasicsTests.py:50: ImportError
BasicsTests.test_import_widgetstools
<1s
ImportError: libGL.so.1: cannot open shared object file: No such file or directory
def test_import_widgets():
> from PyQt5 import QtWidgets
E ImportError: libGL.so.1: cannot open shared object file: No such file or directory
tools/BasicsTests.py:42: ImportError
You have to install the Qt dependencies:
image: python:3.9
pipelines:
default:
- parallel:
- step:
name: Test
caches:
- pip
script:
- apt-get update && apt-get autoclean
- apt-get install -y '^libxcb.*-dev' libx11-xcb-dev libglu1-mesa-dev libxrender-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev
- if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- pip install pytest
- pip install pytest-qt
- pytest -v tools/IntegrationTests.py --junitxml=test-reports/report.xml

How to connect to MySQL with a Flask app on Heroku using SQLAlchemy [duplicate]

I uploaded a Django app to Heroku and than provision the cleardb add-on using these 3 commands from Heroku documentation:
heroku addons:create cleardb:ignite
heroku config | grep CLEARDB_DATABASE_URL
heroku config:set DATABASE_URL='mysql://adffdadf2341:adf4234#us-cdbr-east.cleardb.com/heroku_db?reconnect=true'
it seems to be O.K and the app is running (but without database).
now I try to run:
$ heroku run python manage.py migrate
and this is the error I get:
Traceback (most recent call last):
File "manage.py", line 22, in <module>
execute_from_command_line(sys.argv)
File "/app/.heroku/python/lib/python3.5/site-packages/django/core/management/__init__.py", line 363, in execute_from_command_line
utility.execute()
File "/app/.heroku/python/lib/python3.5/site-packages/django/core/management/__init__.py", line 355, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/app/.heroku/python/lib/python3.5/site-packages/django/core/management/base.py", line 283, in run_from_argv
self.execute(*args, **cmd_options)
File "/app/.heroku/python/lib/python3.5/site-packages/django/core/management/base.py", line 327, in execute
self.check()
File "/app/.heroku/python/lib/python3.5/site-packages/django/core/management/base.py", line 359, in check
include_deployment_checks=include_deployment_checks,
File "/app/.heroku/python/lib/python3.5/site-packages/django/core/management/commands/migrate.py", line 61, in _run_checks
issues = run_checks(tags=[Tags.database])
File "/app/.heroku/python/lib/python3.5/site-packages/django/core/checks/registry.py", line 81, in run_checks
new_errors = check(app_configs=app_configs)
File "/app/.heroku/python/lib/python3.5/site-packages/django/core/checks/database.py", line 10, in check_database_backends
issues.extend(conn.validation.check(**kwargs))
File "/app/.heroku/python/lib/python3.5/site-packages/django/db/backends/mysql/validation.py", line 9, in check
issues.extend(self._check_sql_mode(**kwargs))
File "/app/.heroku/python/lib/python3.5/site-packages/django/db/backends/mysql/validation.py", line 13, in _check_sql_mode
with self.connection.cursor() as cursor:
File "/app/.heroku/python/lib/python3.5/site-packages/django/db/backends/base/base.py", line 254, in cursor
return self._cursor()
File "/app/.heroku/python/lib/python3.5/site-packages/django/db/backends/base/base.py", line 229, in _cursor
self.ensure_connection()
File "/app/.heroku/python/lib/python3.5/site-packages/django/db/backends/base/base.py", line 213, in ensure_connection
self.connect()
File "/app/.heroku/python/lib/python3.5/site-packages/django/db/backends/base/base.py", line 189, in connect
self.connection = self.get_new_connection(conn_params)
File "/app/.heroku/python/lib/python3.5/site-packages/django/db/backends/mysql/base.py", line 274, in get_new_connection
conn = Database.connect(**conn_params)
File "/app/.heroku/python/lib/python3.5/site-packages/MySQLdb/__init__.py", line 86, in Connect
return Connection(*args, **kwargs)
File "/app/.heroku/python/lib/python3.5/site-packages/MySQLdb/connections.py", line 204, in __init__
super(Connection, self).__init__(*args, **kwargs2)
TypeError: 'reconnect' is an invalid keyword argument for this function
Where does the problem comes from and how can I fix it?
Remove ?reconnect=true from the end of DATABASE_URL
The parameters after the database are arguments to the MySQL server. In this case, it asks to reconnect if the connection is dropped. It looks like the MySQLdb package doesn't support that argument.

Failing to install PM2 using NPM package on Ansible 2.7 on Debian stretch

I am setting up a new server based on Debian 9 (stretch).
I have an Ansible playbook and when I try to install PM2 using the npm ansible module it fails and I am stuck with this for the last couple of days.
I am using ansible 2.7:
ansible 2.7.10
config file = None
configured module search path = ['/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.7/site-packages/ansible
executable location = /usr/local/bin/ansible
python version = 3.7.3 (default, Mar 27 2019, 09:23:15) [Clang 10.0.1 (clang-1001.0.46.3)]
I tried to run the playbook with python 2 and 3 with no luck using ansible_python_interpreter=/usr/bin/python3 on the Inventory.
The weird thing is that if I ssh on the server and I run npm install pm2 -g it works perfectly fine.
Nvm also works fine on the server.
Before reaching this point the playbook installs several other packages:
mongodb
mysql-client
redis
various libraries
etc
This is the extract from the playbook
tasks:
...
- name: Install nvm
sudo: no
git: repo=https://github.com/creationix/nvm.git dest=~/.nvm version={{ NVM_VERSION }}
tags: nvm
- name: Source nvm in ~/.profile
sudo: no
lineinfile: >
dest=~/.profile
line="source ~/.nvm/nvm.sh"
create=yes
tags: nvm
- name: Install versions
shell: bash -lc "nvm install {{ item }}"
register: output
changed_when: "'already installed.' not in output.stderr"
with_items: "{{ NVM_NODE_VERSIONS }}"
sudo: no
- name: Set default node version to {{ NVM_DEFAULT_NODE_VERSION }}
command: sudo -iu admin nvm alias default {{ NVM_DEFAULT_NODE_VERSION }}
tags: nvm
- name: Install PM2 packages
npm:
executable: /home/admin/.nvm/versions/node/v{{ NVM_DEFAULT_NODE_VERSION }}/bin/npm
name: pm2
global: yes
state: present
....
The variables are defined as follow:
NVM_VERSION : "v0.33.0"
NVM_INSTALL_SCRIPT : "/home/admin/nvm_install.sh"
NVM_NODE_VERSIONS :
- v10.15.3
- v6.9.4
- v8.10.0
- v8.11.1
NVM_DEFAULT_NODE_VERSION: "10.15.3"
This is the error that I get:
{
"changed":false,
"module_stderr":"Shared connection to 52.209.248.173 closed.\r\n",
"module_stdout":"Traceback (most recent call last):\r\n File \"/home/admin/.ansible/tmp/ansible-tmp-1554992741.43544-36230788449164/AnsiballZ_npm.py\", line 113, in <module>\r\n _ansiballz_main()\r\n File \"/home/admin/.ansible/tmp/ansible-tmp-1554992741.43544-36230788449164/AnsiballZ_npm.py\", line 105, in _ansiballz_main\r\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n File \"/home/admin/.ansible/tmp/ansible-tmp-1554992741.43544-36230788449164/AnsiballZ_npm.py\", line 48, in invoke_module\r\n imp.load_module('__main__', mod, module, MOD_DESC)\r\n File \"/usr/lib/python3.5/imp.py\", line 234, in load_module\r\n return load_source(name, filename, file)\r\n File \"/usr/lib/python3.5/imp.py\", line 170, in load_source\r\n module = _exec(spec, sys.modules[name])\r\n File \"<frozen importlib._bootstrap>\", line 626, in _exec\r\n File \"<frozen importlib._bootstrap_external>\", line 673, in exec_module\r\n File \"<frozen importlib._bootstrap>\", line 222, in _call_with_frames_removed\r\n File \"/tmp/ansible_npm_payload_qlnd0iyr/__main__.py\", line 284, in <module>\r\n File \"/tmp/ansible_npm_payload_qlnd0iyr/__main__.py\", line 261, in main\r\n File \"/tmp/ansible_npm_payload_qlnd0iyr/__main__.py\", line 185, in list\r\n File \"/usr/lib/python3.5/json/__init__.py\", line 319, in loads\r\n return _default_decoder.decode(s)\r\n File \"/usr/lib/python3.5/json/decoder.py\", line 339, in decode\r\n obj, end = self.raw_decode(s, idx=_w(s, 0).end())\r\n File \"/usr/lib/python3.5/json/decoder.py\", line 357, in raw_decode\r\n raise JSONDecodeError(\"Expecting value\", s, err.value) from None\r\njson.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)\r\n",
"msg":"MODULE FAILURE\nSee stdout/stderr for the exact error",
"rc":1
}
Clearer error:
MODULE FAILURE
See stdout/stderr for the exact error
MODULE_STDOUT:
Traceback (most recent call last):
File "/home/admin/.ansible/tmp/ansible-tmp-1555331069.891913-182901060644739/AnsiballZ_npm.py", line 113, in <module>
_ansiballz_main()
File "/home/admin/.ansible/tmp/ansible-tmp-1555331069.891913-182901060644739/AnsiballZ_npm.py", line 105, in _ansiballz_main
invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
File "/home/admin/.ansible/tmp/ansible-tmp-1555331069.891913-182901060644739/AnsiballZ_npm.py", line 48, in invoke_module
imp.load_module('__main__', mod, module, MOD_DESC)
File "/usr/lib/python3.5/imp.py", line 234, in load_module
return load_source(name, filename, file)
File "/usr/lib/python3.5/imp.py", line 170, in load_source
module = _exec(spec, sys.modules[name])
File "<frozen importlib._bootstrap>", line 626, in _exec
File "<frozen importlib._bootstrap_external>", line 673, in exec_module
File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
File "/tmp/ansible_npm_payload_qvtn2qwx/__main__.py", line 284, in <module>
File "/tmp/ansible_npm_payload_qvtn2qwx/__main__.py", line 261, in main
File "/tmp/ansible_npm_payload_qvtn2qwx/__main__.py", line 185, in list
File "/usr/lib/python3.5/json/__init__.py", line 319, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.5/json/decoder.py", line 339, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.5/json/decoder.py", line 357, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
MODULE_STDERR:
OpenSSH_7.9p1, LibreSSL 2.7.3
debug1: Reading configuration data ~/.ssh/config
debug1: ~/.ssh/config line 13: Applying options for *
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 48: Applying options for *
debug2: resolve_canonicalize: hostname <ip> is address
debug1: auto-mux: Trying existing master
debug2: fd 3 setting O_NONBLOCK
debug2: mux_client_hello_exchange: master version 4
debug3: mux_client_forwards: request forwardings: 0 local, 0 remote
debug3: mux_client_request_session: entering
debug3: mux_client_request_alive: entering
debug3: mux_client_request_alive: done pid = 16293
debug3: mux_client_request_session: session request sent
debug3: mux_client_read_packet: read header failed: Broken pipe
debug2: Received exit status from master 1
Shared connection to <ip> closed.
Any help would be much appreciated.
Installation of the global version of node has resolved the issue.

How to publicly view website that I am hosting on amazon-ec2?

I have started an amazon-ec2 instance with public a DNS of ec2-12-34-567-89.us-west-2.compute.amazonaws.com. There I have set up a security group, where type=HTTP, protocol=TCP, port range=80, source=0.0.0.0/. I log in to the EC2 instance and launch my app:
$ python3 run.py
* Running on http://0.0.0.0:0/
Then with browser I try to open: http://ec2-12-34-567-89.us-west-2.compute.amazonaws.com and get an "Unable to connect" message.
What am I missing here?
Edit
With port 80, it ends up like this:
$ python3 run.py
* Running on http://0.0.0.0:80/
Traceback (most recent call last):
File "run.py", line 5, in <module>
app.run(host="0.0.0.0", port=80)
File "/opt/python/3.4.1/lib/python3.4/site-packages/flask/app.py", line 772, in run
run_simple(host, port, self, **options)
File "/opt/python/3.4.1/lib/python3.4/site-packages/werkzeug/serving.py", line 710, in run_simple
inner()
File "/opt/python/3.4.1/lib/python3.4/site-packages/werkzeug/serving.py", line 692, in inner
passthrough_errors, ssl_context).serve_forever()
File "/opt/python/3.4.1/lib/python3.4/site-packages/werkzeug/serving.py", line 486, in make_server
passthrough_errors, ssl_context)
File "/opt/python/3.4.1/lib/python3.4/site-packages/werkzeug/serving.py", line 410, in __init__
HTTPServer.__init__(self, (host, int(port)), handler)
File "/opt/python/3.4.1/lib/python3.4/socketserver.py", line 429, in __init__
self.server_bind()
File "/opt/python/3.4.1/lib/python3.4/http/server.py", line 133, in server_bind
socketserver.TCPServer.server_bind(self)
File "/opt/python/3.4.1/lib/python3.4/socketserver.py", line 440, in server_bind
self.socket.bind(self.server_address)
PermissionError: [Errno 13] Permission denied
Appearently I needed to run my code as superuser.
$ **sudo** python3 run.py
* Running on http://0.0.0.0:80/

Resources