Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(extension): Enable Async Gunicorn workers for Flask and Django e… #747

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
c0d3102
Feat(extension): Enable Async Gunicorn workers for Flask and Django e…
alithethird Nov 8, 2024
009fe4c
chore(lint): Ran static checkers
alithethird Nov 8, 2024
a6d156f
chore(): Fix flask cli unit test
alithethird Nov 8, 2024
408db1d
chore(docs): Docs lint
alithethird Nov 8, 2024
39671e1
chore(docs): Improved wordlist
alithethird Nov 8, 2024
4220cd7
Merge branch 'main' into flask-django-extention-async-workers
alithethird Nov 15, 2024
8d88e8c
Chore(): Update docs, update cli init test, update `rockcraft.yaml` f…
alithethird Nov 19, 2024
fbd636c
Merge branch 'flask-django-extention-async-workers' of github.com:ali…
alithethird Nov 19, 2024
3280aa6
Chore(): Try to make linter happy
alithethird Nov 19, 2024
c9fab57
Chore(docs): Lint docs
alithethird Nov 19, 2024
a1df1b5
Chore(docs): Update async doc link
alithethird Nov 20, 2024
8446f7e
Merge branch 'main' into flask-django-extention-async-workers
alithethird Nov 20, 2024
0ea1838
Run CI
alithethird Nov 20, 2024
a8e14d2
Merge branch 'flask-django-extention-async-workers' of github.com:ali…
alithethird Nov 20, 2024
99982f8
Chore(Docs): Small doc improvements
alithethird Nov 26, 2024
a645957
Merge branch 'main' into flask-django-extention-async-workers
alithethird Nov 26, 2024
f35db9e
Chore(lint): Doc lint
alithethird Nov 26, 2024
b433599
XMerge branch 'flask-django-extention-async-workers' of github.com:al…
alithethird Nov 26, 2024
56979d4
Chore(docs): Add words to list
alithethird Nov 26, 2024
7c57358
Chore(): Undone import format
alithethird Nov 26, 2024
5b98290
Chore(): Addressed comments.
alithethird Nov 27, 2024
dbd88b3
Chore(): Consistent spacing
alithethird Nov 28, 2024
9338ad9
Chore(Format): Format imports
alithethird Nov 28, 2024
69fc1c1
Merge branch 'main' into flask-django-extention-async-workers
alithethird Nov 28, 2024
7935d3b
Chore(Lint): Add word
alithethird Nov 28, 2024
8837f79
Merge branch 'flask-django-extention-async-workers' of github.com:ali…
alithethird Nov 28, 2024
2665251
Chore(): Update docs, update gunicorn conf for async
alithethird Nov 28, 2024
415807d
Chore(lint): Format
alithethird Nov 28, 2024
f8e9488
Chore(tests): Fix tests
alithethird Nov 28, 2024
c67ea0b
Chore(docs): Fix refs
alithethird Nov 28, 2024
41e4ce9
Chore(docs): Update refs
alithethird Nov 28, 2024
da397aa
Chore(): Updated docs, async conf file
alithethird Nov 29, 2024
071fa2e
Chore(docs): Fix lint
alithethird Nov 29, 2024
b1e4835
Chore(): Add a spread test for async
alithethird Nov 29, 2024
e6b7083
Chore(test): Removed unnecessary fixture
alithethird Nov 29, 2024
7d5b613
Chore(): Format
alithethird Nov 29, 2024
dd95620
Merge main
alithethird Nov 29, 2024
7f35f80
Merge branch 'main' into flask-django-extention-async-workers
alithethird Nov 29, 2024
1c2bf63
Chore(test): Simplify test and fix typo
alithethird Nov 29, 2024
e00dacd
Merge branch 'flask-django-extention-async-workers' of github.com:ali…
alithethird Nov 29, 2024
b2086af
Chore(): Improve code
alithethird Nov 29, 2024
dad6563
Chore(lint): Make linter happy
alithethird Nov 29, 2024
69f740f
Chore(): Make linter happier
alithethird Nov 29, 2024
ab3a635
Merge branch 'main' into flask-django-extention-async-workers
alithethird Nov 29, 2024
b2cd5dc
Chore(test): Fix unit test
alithethird Nov 29, 2024
e82b75e
Merge branch 'flask-django-extention-async-workers' of github.com:ali…
alithethird Nov 29, 2024
aaf21a5
Chore(lint): Make Mypy happy
alithethird Nov 29, 2024
27ce02d
Chore(): Fix shell lint
alithethird Nov 29, 2024
5f58c54
Chore(test): Fix test comment
alithethird Dec 2, 2024
8e9fd49
Chore(test): Add async flask/django tests. Created test dir
alithethird Dec 3, 2024
6eef44a
Chore(lint): Run autoformat
alithethird Dec 3, 2024
0b2be19
Chore(test): Ignore import errors in django data
alithethird Dec 3, 2024
757defc
Chore(test): Fix cli test
alithethird Dec 3, 2024
47b4136
Chore(format): format
alithethird Dec 3, 2024
efb4e23
Chore(): Change async flask implementation
alithethird Dec 4, 2024
16cd05b
Chore(test): Fix spread test
alithethird Dec 4, 2024
c7dbf84
Chore(): Update docs and spread test
alithethird Dec 4, 2024
22d18ea
Chore(test): Removed unnecessary django files
alithethird Dec 4, 2024
a6aaacf
Chore(doc): Lint docs
alithethird Dec 4, 2024
9e0efd1
Update docs/reference/extensions/django-framework.rst
alithethird Dec 5, 2024
fc2caf1
Update docs/reference/extensions/django-framework.rst
alithethird Dec 5, 2024
dbade98
Update docs/reference/extensions/django-framework.rst
alithethird Dec 5, 2024
517532f
Update docs/reference/extensions/django-framework.rst
alithethird Dec 5, 2024
46f82f8
Chore(): Update gevent install check
alithethird Dec 5, 2024
3b4602f
Merge branch 'flask-django-extention-async-workers' of github.com:ali…
alithethird Dec 5, 2024
6fb5bfd
Chore(docs): Style update
alithethird Dec 5, 2024
9a25b90
Merge branch 'main' into flask-django-extention-async-workers
alithethird Dec 5, 2024
e6e6d8d
Chore(): Updated pip package check
alithethird Dec 5, 2024
722e5ff
Merge branch 'flask-django-extention-async-workers' of github.com:ali…
alithethird Dec 5, 2024
eac7d06
Chore(Deps): Add `packaging` dependency
alithethird Dec 5, 2024
18b16fc
Chore(lint): Format code
alithethird Dec 5, 2024
6bc31bb
Chore(format): Format
alithethird Dec 5, 2024
9a6dc45
Chore(docs): Format doc
alithethird Dec 5, 2024
6956da0
Chore(): Fix spread test, change async check
alithethird Dec 5, 2024
537a19d
Chore(): Except specific exception
alithethird Dec 5, 2024
b4ae759
Chore(): Uncomment restore
alithethird Dec 5, 2024
6b6f00c
Update docs/reference/extensions/django-framework.rst
alithethird Dec 9, 2024
4dc5791
Update docs/reference/extensions/flask-framework.rst
alithethird Dec 9, 2024
7612690
Chore(): Addressed comments. Add comment to test_cli.py. fix doc issu…
alithethird Dec 9, 2024
b993abb
Chore(test): Add unit async gunicorn tests
alithethird Dec 9, 2024
508afc0
Merge branch 'main' into flask-django-extention-async-workers
alithethird Dec 9, 2024
aeee726
Chore(lint): Format code
alithethird Dec 9, 2024
e6a8d44
Chore(): Changed copy_tree() to shutil.copytree()
alithethird Dec 10, 2024
ae05f6d
chore(doc): Fix doc format
alithethird Dec 10, 2024
cdd9134
chore(doc): Format
alithethird Dec 10, 2024
6aba111
chore(doc): Format
alithethird Dec 10, 2024
9846735
chore(doc): Format
alithethird Dec 10, 2024
5617d29
chore(doc): Format
alithethird Dec 10, 2024
b6550ef
chore(test): Update tests
alithethird Dec 10, 2024
3b214dc
chore(doc): Change testing port in doc
alithethird Dec 11, 2024
ead9f9c
chore(update): Update statsd-exporter version in flask/django plugin
alithethird Dec 13, 2024
8454a69
Merge branch 'flask-django-extention-async-workers' of github.com:ali…
alithethird Dec 13, 2024
97e8bcb
chore(): Revert version change in statsd
alithethird Dec 13, 2024
ec5db1e
Merge branch 'main' into flask-django-extention-async-workers
alithethird Dec 16, 2024
d517d98
chore(doc): Update async docs
alithethird Dec 17, 2024
75f95c1
Merge branch 'main' into flask-django-extention-async-workers
alithethird Dec 17, 2024
2b4a3bb
Update docs/reference/extensions/django-framework.rst
alithethird Dec 18, 2024
1321bd2
Update docs/reference/extensions/flask-framework.rst
alithethird Dec 18, 2024
cae3f7d
Merge branch 'main' into flask-django-extention-async-workers
alithethird Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions docs/reference/extensions/django-framework.rst
alithethird marked this conversation as resolved.
Show resolved Hide resolved
alithethird marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ server metrics.
The Django extension supports both synchronous and asynchronous
Gunicorn workers. If you want asynchronous workers, you have to add
the ``gevent`` package to the ``requirements.txt`` file.
Read more :ref:`Using asynchronous Gunicorn workers <django-async-gunicorn-workers>`.
Read more
:ref:`Using asynchronous Gunicorn workers <django-async-gunicorn-workers>`.

Project requirements
====================
Expand Down Expand Up @@ -60,14 +61,10 @@ updates the Pebble plan to use the asynchronous workers. If you have ``gevent``
installed in your rock but decided to use ``sync`` workers instead you can do
so by using the ``--args`` parameter of ``docker run``:

.. code-block:: shell
:caption: Use sync workers instead of gevent
$ docker run \
--name django-container \
-d -p 8138:8000 \
django-image:1.0 \
--args django sync
.. code-block:: bash

docker run --name django-container -d -p 8138:8000 django-image:1.0 \
--args django sync
alithethird marked this conversation as resolved.
Show resolved Hide resolved

.. note::
The Django extension is compatible with the ``bare``, ``[email protected]`` and
Expand Down
20 changes: 9 additions & 11 deletions docs/reference/extensions/flask-framework.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ server metrics.
The Flask extension supports both synchronous and asynchronous
Gunicorn workers. If you want asynchronous workers, you have to add
``gevent`` package to the ``requirements.txt`` file.
Read more :ref:`Using asynchronous Gunicorn workers <flask-async-gunicorn-workers>`.
Read more
:ref:`Using asynchronous Gunicorn workers <flask-async-gunicorn-workers>`.

Project requirements
====================
Expand Down Expand Up @@ -55,16 +56,13 @@ If you want to use asynchronous workers, you have to add ``gevent`` package to
the ``requirements.txt`` file. Rockcraft automatically detects this and updates
the pebble plan to use the asynchronous workers. If you have ``gevent``
installed in your rock but decided to use ``sync`` workers instead you can use
the ``--args`` parameter of ``docker run`` to use ``sync`` workers instead of the
default ``gevent``:

.. code-block:: shell
:caption: Use sync workers instead of gevent
$ docker run \
--name flask-container \
-d -p 8138:8000 \
flask-image:1.0 \
--args flask sync
the ``--args`` parameter of ``docker run`` to use ``sync`` workers instead of
the default ``gevent``:

.. code-block:: bash

docker run --name flask-container -d -p 8138:8000 flask-image:1.0 \
--args flask sync

``parts`` > ``flask-framework/install-app`` > ``prime``
=======================================================
Expand Down
172 changes: 17 additions & 155 deletions tests/unit/extensions/test_gunicorn.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,85 +51,14 @@ def django_input_yaml_fixture():

@pytest.mark.usefixtures("flask_extension")
@pytest.mark.parametrize("packages", ["other\nflask", "flask", "Flask", " flask == 99"])
def test_flask_extension_default(tmp_path, flask_input_yaml, packages):
(tmp_path / "requirements.txt").write_text(packages)
(tmp_path / "app.py").write_text("app = object()")
(tmp_path / "static").mkdir()
(tmp_path / "node_modules").mkdir()
(tmp_path / "test").write_text("test")
applied = extensions.apply_extensions(tmp_path, flask_input_yaml)
source = applied["parts"]["flask-framework/config-files"]["source"]
del applied["parts"]["flask-framework/config-files"]["source"]
suffix = "share/rockcraft/extensions/flask-framework"
assert source[-len(suffix) :].replace("\\", "/") == suffix

assert applied == {
"base": "[email protected]",
"parts": {
"flask-framework/config-files": {
"organize": {
"gunicorn.conf.py": "flask/gunicorn.conf.py",
},
"plugin": "dump",
},
"flask-framework/dependencies": {
"plugin": "python",
"python-packages": ["gunicorn"],
"python-requirements": ["requirements.txt"],
"source": ".",
"stage-packages": ["python3-venv"],
"build-environment": [],
},
"flask-framework/install-app": {
"organize": {
"app.py": "flask/app/app.py",
"static": "flask/app/static",
},
"plugin": "dump",
"prime": ["flask/app/app.py", "flask/app/static"],
"source": ".",
"stage": ["flask/app/app.py", "flask/app/static"],
},
"flask-framework/runtime": {
"plugin": "nil",
"stage-packages": ["ca-certificates_data"],
},
"flask-framework/statsd-exporter": {
"build-snaps": ["go"],
"plugin": "go",
"source": "https://github.com/prometheus/statsd_exporter.git",
"source-tag": "v0.26.0",
},
},
"platforms": {"amd64": {}},
"run_user": "_daemon_",
"services": {
"flask": {
"after": ["statsd-exporter"],
"command": "/bin/python3 -m gunicorn -c "
"/flask/gunicorn.conf.py app:app -k [ sync ]",
"override": "replace",
"startup": "enabled",
"user": "_daemon_",
},
"statsd-exporter": {
"command": (
"/bin/statsd_exporter --statsd.mapping-config=/statsd-mapping.conf "
"--statsd.listen-udp=localhost:9125 "
"--statsd.listen-tcp=localhost:9125"
),
"override": "merge",
"startup": "enabled",
"summary": "statsd exporter service",
"user": "_daemon_",
},
},
}


@pytest.mark.usefixtures("flask_extension")
def test_flask_extension_async(tmp_path, flask_input_yaml):
(tmp_path / "requirements.txt").write_text("flask\ngevent")
@pytest.mark.parametrize(
("async_package", "expected_worker"), [("gevent", "gevent"), ("", "sync")]
)
def test_flask_extension_default(
alithethird marked this conversation as resolved.
Show resolved Hide resolved
tmp_path, flask_input_yaml, packages, async_package, expected_worker
):
full_packages = "\n".join([packages, async_package])
(tmp_path / "requirements.txt").write_text(full_packages)
(tmp_path / "app.py").write_text("app = object()")
(tmp_path / "static").mkdir()
(tmp_path / "node_modules").mkdir()
Expand Down Expand Up @@ -184,7 +113,7 @@ def test_flask_extension_async(tmp_path, flask_input_yaml):
"flask": {
"after": ["statsd-exporter"],
"command": "/bin/python3 -m gunicorn -c "
"/flask/gunicorn.conf.py app:app -k [ gevent ]",
f"/flask/gunicorn.conf.py app:app -k [ {expected_worker} ]",
"override": "replace",
"startup": "enabled",
"user": "_daemon_",
Expand Down Expand Up @@ -524,80 +453,13 @@ def test_flask_extension_flask_service_override_disable_wsgi_path_check(tmp_path


@pytest.mark.usefixtures("django_extension")
def test_django_extension_default(tmp_path, django_input_yaml):
(tmp_path / "requirements.txt").write_text("django")
(tmp_path / "test").mkdir()
(tmp_path / "foo_bar" / "foo_bar").mkdir(parents=True)
(tmp_path / "foo_bar" / "foo_bar" / "wsgi.py").write_text("application = object()")

applied = extensions.apply_extensions(tmp_path, django_input_yaml)

source = applied["parts"]["django-framework/config-files"]["source"]
del applied["parts"]["django-framework/config-files"]["source"]
suffix = "share/rockcraft/extensions/django-framework"
assert source[-len(suffix) :].replace("\\", "/") == suffix

assert applied == {
"base": "[email protected]",
"name": "foo-bar",
"parts": {
"django-framework/config-files": {
"organize": {"gunicorn.conf.py": "django/gunicorn.conf.py"},
"plugin": "dump",
},
"django-framework/dependencies": {
"plugin": "python",
"python-packages": ["gunicorn"],
"python-requirements": ["requirements.txt"],
"source": ".",
"stage-packages": ["python3-venv"],
"build-environment": [],
},
"django-framework/install-app": {
"organize": {"*": "django/app/", ".*": "django/app/"},
"plugin": "dump",
"source": "foo_bar",
"stage": ["-django/app/db.sqlite3"],
},
"django-framework/runtime": {
"plugin": "nil",
"stage-packages": ["ca-certificates_data"],
},
"django-framework/statsd-exporter": {
"build-snaps": ["go"],
"plugin": "go",
"source": "https://github.com/prometheus/statsd_exporter.git",
"source-tag": "v0.26.0",
},
},
"platforms": {"amd64": {}},
"run_user": "_daemon_",
"services": {
"django": {
"after": ["statsd-exporter"],
"command": "/bin/python3 -m gunicorn -c /django/gunicorn.conf.py foo_bar.wsgi:application -k [ sync ]",
"override": "replace",
"startup": "enabled",
"user": "_daemon_",
},
"statsd-exporter": {
"command": (
"/bin/statsd_exporter --statsd.mapping-config=/statsd-mapping.conf "
"--statsd.listen-udp=localhost:9125 "
"--statsd.listen-tcp=localhost:9125"
),
"override": "merge",
"startup": "enabled",
"summary": "statsd exporter service",
"user": "_daemon_",
},
},
}


@pytest.mark.usefixtures("django_extension")
def test_django_extension_async(tmp_path, django_input_yaml):
(tmp_path / "requirements.txt").write_text("django\ngevent")
@pytest.mark.parametrize(
("packages", "expected_worker"), [("Django\ngevent", "gevent"), ("Django", "sync")]
)
def test_django_extension_default(
tmp_path, django_input_yaml, packages, expected_worker
):
(tmp_path / "requirements.txt").write_text(packages)
(tmp_path / "test").mkdir()
(tmp_path / "foo_bar" / "foo_bar").mkdir(parents=True)
(tmp_path / "foo_bar" / "foo_bar" / "wsgi.py").write_text("application = object()")
Expand Down Expand Up @@ -647,7 +509,7 @@ def test_django_extension_async(tmp_path, django_input_yaml):
"services": {
"django": {
"after": ["statsd-exporter"],
"command": "/bin/python3 -m gunicorn -c /django/gunicorn.conf.py foo_bar.wsgi:application -k [ gevent ]",
"command": f"/bin/python3 -m gunicorn -c /django/gunicorn.conf.py foo_bar.wsgi:application -k [ {expected_worker} ]",
"override": "replace",
"startup": "enabled",
"user": "_daemon_",
Expand Down
Loading