From 59f2add67f95b0da7982f471c6686b4b412cdc2a Mon Sep 17 00:00:00 2001 From: Andrey Golovizin Date: Mon, 9 Mar 2020 20:32:08 +0100 Subject: [PATCH] Switch from uvicorn to gunicorn --- default.nix | 3 +- module.nix | 2 +- requirements.nix | 160 ++++-------------- requirements.txt | 3 +- requirements_frozen.txt | 21 +-- .../management/commands/runserver-gunicorn.py | 39 +++++ .../management/commands/runserver-uvicorn.py | 22 --- 7 files changed, 86 insertions(+), 164 deletions(-) create mode 100644 src/strojnadzor/management/commands/runserver-gunicorn.py delete mode 100644 src/strojnadzor/management/commands/runserver-uvicorn.py diff --git a/default.nix b/default.nix index 1d8c579..0f5a60a 100644 --- a/default.nix +++ b/default.nix @@ -30,7 +30,6 @@ python.mkDerivation { djangocms-video easy-thumbnails setuptools - uvicorn - uvloop + gunicorn ]; } diff --git a/module.nix b/module.nix index de3331e..e993089 100644 --- a/module.nix +++ b/module.nix @@ -65,7 +65,7 @@ in User = "strojnadzor"; Group = "strojnadzor"; ExecStartPre = "${strojnadzor}/bin/strojnadzor-admin migrate"; - ExecStart = "${strojnadzor}/bin/strojnadzor-admin runserver-uvicorn ${cfg.hostname} ${toString cfg.port}"; + ExecStart = "${strojnadzor}/bin/strojnadzor-admin runserver-gunicorn --bind ${cfg.hostname}:${toString cfg.port}"; }; environment.STROJNADZOR_DATA_DIR = "${cfg.stateDir}/data"; }; diff --git a/requirements.nix b/requirements.nix index 98bb787..c96e3e3 100644 --- a/requirements.nix +++ b/requirements.nix @@ -179,23 +179,6 @@ let }; }; - "click" = python.mkDerivation { - name = "click-7.0"; - src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/f8/5c/f60e9d8a1e77005f664b76ff8aeaee5bc05d0a91798afd7f53fc998dbc47/Click-7.0.tar.gz"; - sha256 = "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"; -}; - doCheck = commonDoCheck; - format = "setuptools"; - buildInputs = commonBuildInputs ++ [ ]; - propagatedBuildInputs = [ ]; - meta = with pkgs.stdenv.lib; { - homepage = "https://palletsprojects.com/p/click/"; - license = licenses.bsdOriginal; - description = "Composable command line interface toolkit"; - }; - }; - "cssselect" = python.mkDerivation { name = "cssselect-1.1.0"; src = pkgs.fetchurl { @@ -354,10 +337,10 @@ let }; "django-filer" = python.mkDerivation { - name = "django-filer-1.6.0"; + name = "django-filer-1.7.0"; src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/b2/3b/544c7de7ae9b2e28f1387974aba3b4385fc06b083c8e6c25c2203afe9255/django-filer-1.6.0.tar.gz"; - sha256 = "3f2045cfd9e53c1a29cd8a71747e984faead630ee72baab29d6b3b45584d52e0"; + url = "https://files.pythonhosted.org/packages/89/1b/137aead15fe0b39589d2628bae035a8b58caecd4e7b028262168d47a56b1/django-filer-1.7.0.tar.gz"; + sha256 = "3da256ab69edc0daed0ccc9a7de37d0548b16037d57684b5524ec34cf3e203fe"; }; doCheck = commonDoCheck; format = "setuptools"; @@ -367,6 +350,7 @@ let self."django-mptt" self."django-polymorphic" self."easy-thumbnails" + self."six" self."unidecode" ]; meta = with pkgs.stdenv.lib; { @@ -433,10 +417,10 @@ let }; "django-polymorphic" = python.mkDerivation { - name = "django-polymorphic-2.0.3"; + name = "django-polymorphic-2.1.2"; src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/ae/d6/bebbc990b5856c36603cd0d635e93137fdf67ec04386ac1bdc87707f9613/django-polymorphic-2.0.3.tar.gz"; - sha256 = "1fb5505537bcaf71cfc951ff94c4e3ba83c761eaca04b7b2ce9cb63937634ea5"; + url = "https://files.pythonhosted.org/packages/ad/52/3af2ee7283625a4e6df37a8a4a8fafbc3b6821f670a1c496191cfa7cde6d/django-polymorphic-2.1.2.tar.gz"; + sha256 = "6e08a76c91066635ccb7ef3ebbe9a0ad149febae6b30be2579716ec16d3c6461"; }; doCheck = commonDoCheck; format = "setuptools"; @@ -854,20 +838,22 @@ let }; }; - "h11" = python.mkDerivation { - name = "h11-0.9.0"; + "gunicorn" = python.mkDerivation { + name = "gunicorn-20.0.4"; src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/34/5a/abaa557d20b210117d8c3e6b0b817ce9b329b2e81f87612e60102a924323/h11-0.9.0.tar.gz"; - sha256 = "33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1"; + url = "https://files.pythonhosted.org/packages/33/b8/f5fd32e1f46fcfefd7cb5c84dee1cf657ab3540ee92b8a09fc40e4887bf0/gunicorn-20.0.4.tar.gz"; + sha256 = "1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626"; }; doCheck = commonDoCheck; format = "setuptools"; buildInputs = commonBuildInputs ++ [ ]; - propagatedBuildInputs = [ ]; + propagatedBuildInputs = [ + self."setuptools" + ]; meta = with pkgs.stdenv.lib; { - homepage = "https://github.com/python-hyper/h11"; + homepage = "http://gunicorn.org"; license = licenses.mit; - description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"; + description = "WSGI HTTP Server for UNIX"; }; }; @@ -891,28 +877,11 @@ let }; }; - "httptools" = python.mkDerivation { - name = "httptools-0.0.13"; - src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/1b/03/215969db11abe8741e9c266a4cbe803a372bd86dd35fa0084c4df6d4bd00/httptools-0.0.13.tar.gz"; - sha256 = "e00cbd7ba01ff748e494248183abc6e153f49181169d8a3d41bb49132ca01dfc"; -}; - doCheck = commonDoCheck; - format = "setuptools"; - buildInputs = commonBuildInputs ++ [ ]; - propagatedBuildInputs = [ ]; - meta = with pkgs.stdenv.lib; { - homepage = "https://github.com/MagicStack/httptools"; - license = licenses.mit; - description = "A collection of framework independent HTTP protocol utils."; - }; - }; - "idna" = python.mkDerivation { - name = "idna-2.8"; + name = "idna-2.9"; src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/ad/13/eb56951b6f7950cadb579ca166e448ba77f9d24efc03edd7e55fa57d04b7/idna-2.8.tar.gz"; - sha256 = "c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407"; + url = "https://files.pythonhosted.org/packages/cb/19/57503b5de719ee45e83472f339f617b0c01ad75cba44aba1e4c97c2b0abd/idna-2.9.tar.gz"; + sha256 = "7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"; }; doCheck = commonDoCheck; format = "setuptools"; @@ -1037,10 +1006,10 @@ let }; "requests" = python.mkDerivation { - name = "requests-2.22.0"; + name = "requests-2.23.0"; src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/01/62/ddcf76d1d19885e8579acb1b1df26a852b03472c0e46d2b959a714c90608/requests-2.22.0.tar.gz"; - sha256 = "11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4"; + url = "https://files.pythonhosted.org/packages/f5/4f/280162d4bd4d8aad241a21aecff7a6e46891b905a4341e7ab549ebaf7915/requests-2.23.0.tar.gz"; + sha256 = "b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"; }; doCheck = commonDoCheck; format = "setuptools"; @@ -1052,17 +1021,17 @@ let self."urllib3" ]; meta = with pkgs.stdenv.lib; { - homepage = "http://python-requests.org"; + homepage = "https://requests.readthedocs.io"; license = licenses.asl20; description = "Python HTTP for Humans."; }; }; "setuptools" = python.mkDerivation { - name = "setuptools-45.1.0"; + name = "setuptools-46.0.0"; src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/42/3e/2464120172859e5d103e5500315fb5555b1e908c0dacc73d80d35a9480ca/setuptools-45.1.0.zip"; - sha256 = "91f72d83602a6e5e4a9e4fe296e27185854038d7cbda49dcd7006c4d3b3b89d5"; + url = "https://files.pythonhosted.org/packages/df/ed/bea598a87a8f7e21ac5bbf464102077c7102557c07db9ff4e207bd9f7806/setuptools-46.0.0.zip"; + sha256 = "2f00f25b780fbfd0787e46891dcccd805b08d007621f24629025f48afef444b5"; }; doCheck = commonDoCheck; format = "setuptools"; @@ -1076,10 +1045,10 @@ let }; "setuptools-scm" = python.mkDerivation { - name = "setuptools-scm-3.4.3"; + name = "setuptools-scm-3.5.0"; src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/fe/bd/bc2fe0b14ce234bb5e2af5f3b574c5a8ef1b7845bfa41e7cf69a78627ec8/setuptools_scm-3.4.3.tar.gz"; - sha256 = "26b8a108783cd88f4b15ff1f0f347d6b476db25d0c226159b835d713f9487320"; + url = "https://files.pythonhosted.org/packages/b2/f7/60a645aae001a2e06cf4b8db2fba9d9f36b8fd378f10647e3e218b61b74b/setuptools_scm-3.5.0.tar.gz"; + sha256 = "5bdf21a05792903cafe7ae0c9501182ab52497614fa6b1750d9dbae7b60c1a87"; }; doCheck = commonDoCheck; format = "pyproject"; @@ -1113,10 +1082,10 @@ let }; "tablib" = python.mkDerivation { - name = "tablib-1.0.0"; + name = "tablib-1.1.0"; src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/6e/2f/5378ce8151ade53a5c7bc8a0b960a250bfe816b75a2c26383f776824f866/tablib-1.0.0.tar.gz"; - sha256 = "ff3172802e8dd7fb795867942f5238f6c90e2d4e90ad6233c1a796cdfc63bb35"; + url = "https://files.pythonhosted.org/packages/ed/2a/72b7ed65a6a5da55dad08649f1340f82b53e663e616d147e3ed2cd518390/tablib-1.1.0.tar.gz"; + sha256 = "4d1909aa3ff1c85ba97ad16176c0aeec33c8e894dc7ea6f10f2dd44701e99ba7"; }; doCheck = commonDoCheck; format = "pyproject"; @@ -1130,10 +1099,10 @@ let }; "unidecode" = python.mkDerivation { - name = "unidecode-1.0.23"; + name = "unidecode-1.1.1"; src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/9b/d8/c1b658ed7ff6e63a745eda483d7d917eb63a79c59fcb422469b85ff47e94/Unidecode-1.0.23.tar.gz"; - sha256 = "8b85354be8fd0c0e10adbf0675f6dc2310e56fda43fa8fe049123b6c475e52fb"; + url = "https://files.pythonhosted.org/packages/b1/d6/7e2a98e98c43cf11406de6097e2656d31559f788e9210326ce6544bd7d40/Unidecode-1.1.1.tar.gz"; + sha256 = "2b6aab710c2a1647e928e36d69c21e76b453cd455f4e2621000e54b2a9b8cce8"; }; doCheck = commonDoCheck; format = "setuptools"; @@ -1163,46 +1132,6 @@ let }; }; - "uvicorn" = python.mkDerivation { - name = "uvicorn-0.11.2"; - src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/5c/ed/37f168f52814d09feaf0b4f6894704a17209f84219773def5b8b14a060dc/uvicorn-0.11.2.tar.gz"; - sha256 = "11f397855c7f35dc034a3d288883382a4c16afdfe6675b70896f55bd6051da64"; -}; - doCheck = commonDoCheck; - format = "setuptools"; - buildInputs = commonBuildInputs ++ [ ]; - propagatedBuildInputs = [ - self."click" - self."h11" - self."httptools" - self."uvloop" - self."websockets" - ]; - meta = with pkgs.stdenv.lib; { - homepage = "https://github.com/encode/uvicorn"; - license = licenses.bsdOriginal; - description = "The lightning-fast ASGI server."; - }; - }; - - "uvloop" = python.mkDerivation { - name = "uvloop-0.14.0"; - src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/84/2e/462e7a25b787d2b40cf6c9864a9e702f358349fc9cfb77e83c38acb73048/uvloop-0.14.0.tar.gz"; - sha256 = "123ac9c0c7dd71464f58f1b4ee0bbd81285d96cdda8bc3519281b8973e3a461e"; -}; - doCheck = commonDoCheck; - format = "setuptools"; - buildInputs = commonBuildInputs ++ [ ]; - propagatedBuildInputs = [ ]; - meta = with pkgs.stdenv.lib; { - homepage = "http://github.com/MagicStack/uvloop"; - license = licenses.asl20; - description = "Fast implementation of asyncio event loop on top of libuv"; - }; - }; - "webencodings" = python.mkDerivation { name = "webencodings-0.5.1"; src = pkgs.fetchurl { @@ -1220,23 +1149,6 @@ let }; }; - "websockets" = python.mkDerivation { - name = "websockets-8.1"; - src = pkgs.fetchurl { - url = "https://files.pythonhosted.org/packages/e9/2b/cf738670bb96eb25cb2caf5294e38a9dc3891a6bcd8e3a51770dbc517c65/websockets-8.1.tar.gz"; - sha256 = "5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"; -}; - doCheck = commonDoCheck; - format = "setuptools"; - buildInputs = commonBuildInputs ++ [ ]; - propagatedBuildInputs = [ ]; - meta = with pkgs.stdenv.lib; { - homepage = "https://github.com/aaugustin/websockets"; - license = licenses.bsdOriginal; - description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"; - }; - }; - "wheel" = python.mkDerivation { name = "wheel-0.34.2"; src = pkgs.fetchurl { @@ -1276,7 +1188,7 @@ let localOverridesFile = ./requirements_override.nix; localOverrides = import localOverridesFile { inherit pkgs python; }; commonOverrides = [ - (let src = pkgs.fetchFromGitHub { owner = "nix-community"; repo = "pypi2nix-overrides"; rev = "ebc21a64505989717dc395ad92f0a4d7021c44bc"; sha256 = "1p1bqm80anxsnh2k26y0f066z3zpngwxpff1jldzzkbhvw8zw77i"; } ; in import "${src}/overrides.nix" { inherit pkgs python; }) + (let src = pkgs.fetchFromGitHub { owner = "nix-community"; repo = "pypi2nix-overrides"; rev = "100c15ec7dfe7d241402ecfb1e796328d0eaf1ec"; sha256 = "0akfkvdakcdxc1lrxznh1rz2811x4pafnsq3jnyr5pn3m30pc7db"; } ; in import "${src}/overrides.nix" { inherit pkgs python; }) ]; paramOverrides = [ (overrides { inherit pkgs python; }) diff --git a/requirements.txt b/requirements.txt index 04f6f69..21c0643 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,5 +20,4 @@ django-classy-tags>=0.9 django-sekizai>=1.0 django-mptt>0.9 -uvicorn -uvloop +gunicorn diff --git a/requirements_frozen.txt b/requirements_frozen.txt index aa9f851..05dd3e9 100644 --- a/requirements_frozen.txt +++ b/requirements_frozen.txt @@ -3,7 +3,6 @@ aldryn-forms==4.0.1 cachetools==4.0.0 certifi==2019.11.28 chardet==3.0.4 -Click==7.0 cssselect==1.1.0 cssutils==1.0.2 Django==2.1.15 @@ -12,11 +11,11 @@ django-appconf==1.0.3 django-classy-tags==1.0.0 django-cms==3.7.1 django-emailit==0.2.4 -django-filer==1.6.0 +django-filer==1.7.0 django-formtools==2.2 django-js-asset==1.2.2 django-mptt==0.11.0 -django-polymorphic==2.0.3 +django-polymorphic==2.1.2 django-ranged-response==0.2.0 django-sekizai==1.1.0 django-simple-captcha==0.5.12 @@ -37,24 +36,20 @@ djangocms-text-ckeditor==3.8.0 djangocms-video==2.2.0 easy-thumbnails==2.7 et-xmlfile==1.0.1 -h11==0.9.0 +gunicorn==20.0.4 html5lib==1.0.1 -httptools==0.0.13 -idna==2.8 +idna==2.9 jdcal==1.4.1 lxml==4.5.0 openpyxl==2.4.9 Pillow==7.0.0 premailer==3.6.1 pytz==2019.3 -requests==2.22.0 -setuptools-scm==3.4.3 +requests==2.23.0 +setuptools-scm==3.5.0 six==1.14.0 -tablib==1.0.0 -Unidecode==1.0.23 +tablib==1.1.0 +Unidecode==1.1.1 urllib3==1.25.8 -uvicorn==0.11.2 -uvloop==0.14.0 webencodings==0.5.1 -websockets==8.1 YURL==1.0.0 diff --git a/src/strojnadzor/management/commands/runserver-gunicorn.py b/src/strojnadzor/management/commands/runserver-gunicorn.py new file mode 100644 index 0000000..d81c986 --- /dev/null +++ b/src/strojnadzor/management/commands/runserver-gunicorn.py @@ -0,0 +1,39 @@ +import gunicorn.app.base +from django.core.management.base import BaseCommand, CommandError + +from strojnadzor.wsgi import application + + +class Command(BaseCommand): + help = "Run Uvicorn HTTP server" + + def add_arguments(self, parser): + parser.add_argument("--bind", type=str, default="127.0.0.1:9000") + parser.add_argument("-w", "--workers", type=int, default=2) + + def handle(self, bind, workers, **kwargs): + options = { + "bind": bind, + "workers": workers, + "accesslog": "-", + } + StandaloneApplication(application, options).run() + + +class StandaloneApplication(gunicorn.app.base.BaseApplication): + def __init__(self, app, options=None): + self.options = options or {} + self.application = app + super().__init__() + + def load_config(self): + config = { + key: value + for key, value in self.options.items() + if key in self.cfg.settings and value is not None + } + for key, value in config.items(): + self.cfg.set(key.lower(), value) + + def load(self): + return self.application diff --git a/src/strojnadzor/management/commands/runserver-uvicorn.py b/src/strojnadzor/management/commands/runserver-uvicorn.py deleted file mode 100644 index 50de26c..0000000 --- a/src/strojnadzor/management/commands/runserver-uvicorn.py +++ /dev/null @@ -1,22 +0,0 @@ -import uvicorn -from django.core.management.base import BaseCommand, CommandError - - -class Command(BaseCommand): - help = "Run Uvicorn HTTP server" - - def add_arguments(self, parser): - parser.add_argument("host", type=str) - parser.add_argument("port", type=int) - parser.add_argument("-w", "--workers", type=int, default=2) - - def handle(self, host, port, workers, **kwargs): - uvicorn.run( - "strojnadzor.wsgi:application", - interface="wsgi", - loop="uvloop", - workers=workers, - host=host, - port=port, - ) -