From d50728d100271299dfe33a8d2fc1b0b47b538a70 Mon Sep 17 00:00:00 2001 From: Kurbanov Bulat Date: Sat, 5 Feb 2022 10:36:35 +0300 Subject: [PATCH] Add healthcheck --- docker/build.dockerfile | 1 + fastapi_book_server/app/views/__init__.py | 2 + fastapi_book_server/app/views/healthcheck.py | 15 +++ poetry.lock | 104 +++++++++++++++++-- pyproject.toml | 1 + scripts/healthcheck.py | 11 ++ 6 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 fastapi_book_server/app/views/healthcheck.py create mode 100644 scripts/healthcheck.py diff --git a/docker/build.dockerfile b/docker/build.dockerfile index 5bfcb5d..32dd4c4 100644 --- a/docker/build.dockerfile +++ b/docker/build.dockerfile @@ -32,6 +32,7 @@ COPY --from=build-image $VENV_PATH $VENV_PATH ENV PATH="$VENV_PATH/bin:$PATH" COPY ./scripts/start.sh /root/ +COPY ./scripts/healthcheck.py /root/ EXPOSE 8080 diff --git a/fastapi_book_server/app/views/__init__.py b/fastapi_book_server/app/views/__init__.py index bb242c4..5aae682 100644 --- a/fastapi_book_server/app/views/__init__.py +++ b/fastapi_book_server/app/views/__init__.py @@ -2,6 +2,7 @@ from app.views.author import author_router, translator_router from app.views.author_annotation import author_annotation_router from app.views.book import book_router from app.views.book_annotation import book_annotation_router +from app.views.healthcheck import healtcheck_router from app.views.sequence import sequence_router from app.views.source import source_router from app.views.translation import translation_router @@ -16,4 +17,5 @@ routers = [ book_annotation_router, translation_router, sequence_router, + healtcheck_router, ] diff --git a/fastapi_book_server/app/views/healthcheck.py b/fastapi_book_server/app/views/healthcheck.py new file mode 100644 index 0000000..81fa36b --- /dev/null +++ b/fastapi_book_server/app/views/healthcheck.py @@ -0,0 +1,15 @@ +from fastapi import APIRouter, Depends + +from app.depends import check_token + + +healtcheck_router = APIRouter( + prefix="/api/v1", + tags=["healthcheck"], + dependencies=[Depends(check_token)], +) + + +@healtcheck_router.get("/healthcheck") +async def healthcheck(): + return "Ok!" diff --git a/poetry.lock b/poetry.lock index 086c447..f85fd53 100644 --- a/poetry.lock +++ b/poetry.lock @@ -124,6 +124,25 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] +[[package]] +name = "certifi" +version = "2021.10.8" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "charset-normalizer" +version = "2.0.11" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + [[package]] name = "click" version = "8.0.3" @@ -193,13 +212,13 @@ pydantic = ">=1.7.2" [package.extras] gino = ["gino[starlette] (>=1.0.1)", "SQLAlchemy (>=1.3.20)"] -all = ["gino[starlette] (>=1.0.1)", "SQLAlchemy (>=1.3.20)", "databases[mysql,sqlite,postgresql] (>=0.4.0)", "orm (>=0.1.5)", "tortoise-orm[aiomysql,asyncpg,aiosqlite] (>=0.16.18,<0.18.0)", "asyncpg (>=0.24.0)", "ormar (>=0.10.5)", "Django (<3.3.0)", "piccolo (>=0.29,<0.35)", "motor (>=2.5.1,<3.0.0)"] +all = ["gino[starlette] (>=1.0.1)", "SQLAlchemy (>=1.3.20)", "databases[postgresql,mysql,sqlite] (>=0.4.0)", "orm (>=0.1.5)", "tortoise-orm[aiosqlite,asyncpg,aiomysql] (>=0.16.18,<0.18.0)", "asyncpg (>=0.24.0)", "ormar (>=0.10.5)", "Django (<3.3.0)", "piccolo (>=0.29,<0.35)", "motor (>=2.5.1,<3.0.0)"] sqlalchemy = ["SQLAlchemy (>=1.3.20)"] asyncpg = ["SQLAlchemy (>=1.3.20)", "asyncpg (>=0.24.0)"] -databases = ["databases[mysql,sqlite,postgresql] (>=0.4.0)"] -orm = ["databases[mysql,sqlite,postgresql] (>=0.4.0)", "orm (>=0.1.5)", "typesystem (>=0.2.0,<0.3.0)"] -django = ["databases[mysql,sqlite,postgresql] (>=0.4.0)", "Django (<3.3.0)"] -tortoise = ["tortoise-orm[aiomysql,asyncpg,aiosqlite] (>=0.16.18,<0.18.0)"] +databases = ["databases[postgresql,mysql,sqlite] (>=0.4.0)"] +orm = ["databases[postgresql,mysql,sqlite] (>=0.4.0)", "orm (>=0.1.5)", "typesystem (>=0.2.0,<0.3.0)"] +django = ["databases[postgresql,mysql,sqlite] (>=0.4.0)", "Django (<3.3.0)"] +tortoise = ["tortoise-orm[aiosqlite,asyncpg,aiomysql] (>=0.16.18,<0.18.0)"] ormar = ["ormar (>=0.10.5)"] piccolo = ["piccolo (>=0.29,<0.35)"] motor = ["motor (>=2.5.1,<3.0.0)"] @@ -223,6 +242,45 @@ category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "httpcore" +version = "0.14.7" +description = "A minimal low-level HTTP client." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +anyio = ">=3.0.0,<4.0.0" +certifi = "*" +h11 = ">=0.11,<0.13" +sniffio = ">=1.0.0,<2.0.0" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + +[[package]] +name = "httpx" +version = "0.22.0" +description = "The next generation HTTP client." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +certifi = "*" +charset-normalizer = "*" +httpcore = ">=0.14.5,<0.15.0" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotlicffi", "brotli"] +cli = ["click (>=8.0.0,<9.0.0)", "rich (>=10.0.0,<11.0.0)", "pygments (>=2.0.0,<3.0.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (>=1.0.0,<2.0.0)"] + [[package]] name = "idna" version = "3.3" @@ -387,6 +445,20 @@ python-versions = ">=3.5" [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + [[package]] name = "sniffio" version = "1.2.0" @@ -476,7 +548,7 @@ python-versions = "*" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "5b4ac04ebb04c19722d632a991d225da3daa519bdc045d7b46cf11f7ca11cad0" +content-hash = "c6b178194a961ae863f1db7834a3ee79a29311871268547446e5776c2ccebfa3" [metadata.files] aiologger = [ @@ -529,6 +601,14 @@ attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] +certifi = [ + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.11.tar.gz", hash = "sha256:98398a9d69ee80548c762ba991a4728bfc3836768ed226b3945908d1a688371c"}, + {file = "charset_normalizer-2.0.11-py3-none-any.whl", hash = "sha256:2842d8f5e82a1f6aa437380934d5e1cd4fcf2003b06fed6940769c164a480a45"}, +] click = [ {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, @@ -605,6 +685,14 @@ h11 = [ {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, ] +httpcore = [ + {file = "httpcore-0.14.7-py3-none-any.whl", hash = "sha256:47d772f754359e56dd9d892d9593b6f9870a37aeb8ba51e9a88b09b3d68cfade"}, + {file = "httpcore-0.14.7.tar.gz", hash = "sha256:7503ec1c0f559066e7e39bc4003fd2ce023d01cf51793e3c173b864eb456ead1"}, +] +httpx = [ + {file = "httpx-0.22.0-py3-none-any.whl", hash = "sha256:e35e83d1d2b9b2a609ef367cc4c1e66fd80b750348b20cc9e19d1952fc2ca3f6"}, + {file = "httpx-0.22.0.tar.gz", hash = "sha256:d8e778f76d9bbd46af49e7f062467e3157a5a3d2ae4876a4bbfd8a51ed9c9cb4"}, +] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, @@ -761,6 +849,10 @@ python-dotenv = [ {file = "python-dotenv-0.19.1.tar.gz", hash = "sha256:14f8185cc8d494662683e6914addcb7e95374771e707601dfc70166946b4c4b8"}, {file = "python_dotenv-0.19.1-py2.py3-none-any.whl", hash = "sha256:bbd3da593fc49c249397cbfbcc449cf36cb02e75afc8157fcc6a81df6fb7750a"}, ] +rfc3986 = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] sniffio = [ {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, diff --git a/pyproject.toml b/pyproject.toml index c59c616..6dd6061 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ fastapi-pagination = {extras = ["ormar"], version = "^0.9.0"} aiologger = "^0.6.1" orjson = "^3.6.4" aioredis = "^2.0.0" +httpx = "^0.22.0" [tool.poetry.dev-dependencies] pytest = "^5.2" diff --git a/scripts/healthcheck.py b/scripts/healthcheck.py new file mode 100644 index 0000000..b7bd530 --- /dev/null +++ b/scripts/healthcheck.py @@ -0,0 +1,11 @@ +import os + +import httpx + + +response = httpx.get( + "http://localhost:8080/api/v1/healthcheck", + headers={"Authorization": os.environ["API_KEY"]}, +) +print(f"HEALTHCHECK STATUS: {response.status_code}") +exit(0 if response.status_code == 200 else 1)