From 28a30b094a32fd56f9c6ddc5aae29ce10a726874 Mon Sep 17 00:00:00 2001 From: "michael.yak" Date: Wed, 15 Jul 2020 21:29:36 +0300 Subject: [PATCH 1/4] README.md should link to supported web frameworks Solves #20 --- README.md | 2 +- pyctuator/pyctuator.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a730a26..95e6391 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Monitor Python web apps using [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin). -Pyctuator supports **Flask**, **FastAPI** and **aiohttp**. **Django** support is planned as well. +Pyctuator supports **[Flask](https://palletsprojects.com/p/flask/)**, **[FastAPI](https://fastapi.tiangolo.com/)** and **[aiohttp](docs.aiohttp.org)**. **Django** support is planned as well. The following video shows a FastAPI web app being monitored and controled using Spring Boot Admin. diff --git a/pyctuator/pyctuator.py b/pyctuator/pyctuator.py index 3f5e43d..130905f 100644 --- a/pyctuator/pyctuator.py +++ b/pyctuator/pyctuator.py @@ -122,7 +122,7 @@ def __init__( return # Fail in case no framework was found for the target app - raise EnvironmentError("No framework was found that is matching the target app" + raise EnvironmentError("No framework was found that is matching the target app " "(is it properly installed and imported?)") def stop(self) -> None: From c68b163cd626fd778dccd539112f0931425ee3b8 Mon Sep 17 00:00:00 2001 From: "michael.yak" Date: Thu, 16 Jul 2020 01:05:41 +0300 Subject: [PATCH 2/4] deregister_from_admin_server should be covered by E2E E2E's test_recurring_registration was extended to verify that deregistration is working properly for all the web frameworks. Note that the actual invocation of test_recurring_registration is registered in `atexit` however in the test its too hard to invoke `atexit` therefore it is called directly. Solves #25 --- pyctuator/impl/aiohttp_pyctuator.py | 2 +- pyctuator/impl/spring_boot_admin_registration.py | 5 +---- tests/aiohttp_test_server.py | 6 ++++++ tests/conftest.py | 15 ++++++++++++++- tests/fast_api_test_server.py | 11 +++++++++++ tests/flask_test_server.py | 6 +++++- tests/test_pyctuator_e2e.py | 13 ++++++++++--- 7 files changed, 48 insertions(+), 10 deletions(-) diff --git a/pyctuator/impl/aiohttp_pyctuator.py b/pyctuator/impl/aiohttp_pyctuator.py index b0e2abb..7b18390 100644 --- a/pyctuator/impl/aiohttp_pyctuator.py +++ b/pyctuator/impl/aiohttp_pyctuator.py @@ -4,7 +4,7 @@ from datetime import datetime from functools import partial from http import HTTPStatus -from typing import Any, Callable, List, Mapping, Optional +from typing import Any, Callable, List, Mapping from aiohttp import web from multidict import CIMultiDictProxy diff --git a/pyctuator/impl/spring_boot_admin_registration.py b/pyctuator/impl/spring_boot_admin_registration.py index 6bc3c33..44b0c4f 100644 --- a/pyctuator/impl/spring_boot_admin_registration.py +++ b/pyctuator/impl/spring_boot_admin_registration.py @@ -36,10 +36,7 @@ def __init__( self.should_continue_registration_schedule: bool = False - def _schedule_next_registration( - self, - registration_interval_sec: int - ) -> None: + def _schedule_next_registration(self, registration_interval_sec: int) -> None: timer = threading.Timer( registration_interval_sec, self._register_with_admin_server, diff --git a/tests/aiohttp_test_server.py b/tests/aiohttp_test_server.py index 21e05c7..a3ae649 100644 --- a/tests/aiohttp_test_server.py +++ b/tests/aiohttp_test_server.py @@ -79,6 +79,12 @@ def start(self) -> None: time.sleep(0.01) def stop(self) -> None: + logging.info("Stopping aiohttp server") self.pyctuator.stop() self.should_stop_server = True self.thread.join() + logging.info("aiohttp server stopped") + + def atexit(self) -> None: + if self.pyctuator.boot_admin_registration_handler: + self.pyctuator.boot_admin_registration_handler.deregister_from_admin_server() diff --git a/tests/conftest.py b/tests/conftest.py index 06d4c70..d2f9ce6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -43,6 +43,7 @@ class RegistrationTrackerFixture: registration: Optional[RegistrationRequest] count: int start_time: Optional[str] + deregistration_time: Optional[datetime] test_start_time: datetime @@ -53,7 +54,7 @@ def install_signal_handlers(self) -> None: @pytest.fixture def registration_tracker() -> RegistrationTrackerFixture: - return RegistrationTrackerFixture(None, 0, None, datetime.now(timezone.utc)) + return RegistrationTrackerFixture(None, 0, None, None, datetime.now(timezone.utc)) @pytest.fixture @@ -75,6 +76,13 @@ def register(request: Request, registration: RegistrationRequest) -> Dict[str, s registration_tracker.start_time = registration.metadata["startup"] return {"id": "JB007"} + # pylint: disable=unused-argument,unused-variable + @boot_admin_app.delete("/register/{registration_id}", tags=["admin-server"]) + def deregister(registration_id: str) -> None: + logging.debug("Got deregistration, delete %s (previous deregistration time is %s)", + registration_id, registration_tracker.deregistration_time) + registration_tracker.deregistration_time = datetime.now(timezone.utc) + # Start the mock boot-admin server that is needed to test pyctuator's registration boot_admin_config = Config(app=boot_admin_app, port=8001, loop="asyncio") boot_admin_server = CustomServer(config=boot_admin_config) @@ -87,6 +95,7 @@ def register(request: Request, registration: RegistrationRequest) -> Dict[str, s yield None boot_admin_server.should_exit = True + boot_admin_server.force_exit = True boot_admin_thread.join() @@ -127,3 +136,7 @@ def start(self) -> None: @abstractmethod def stop(self) -> None: pass + + @abstractmethod + def atexit(self) -> None: + pass diff --git a/tests/fast_api_test_server.py b/tests/fast_api_test_server.py index 4b5d6fd..d0b0851 100644 --- a/tests/fast_api_test_server.py +++ b/tests/fast_api_test_server.py @@ -55,6 +55,17 @@ def start(self) -> None: time.sleep(0.01) def stop(self) -> None: + logging.info("Stopping FastAPI server") self.pyctuator.stop() + + # Allow the recurring registration to complete any in-progress request before stopping FastAPI + time.sleep(1) + self.server.should_exit = True + self.server.force_exit = True self.thread.join() + logging.info("FastAPI server stopped") + + def atexit(self) -> None: + if self.pyctuator.boot_admin_registration_handler: + self.pyctuator.boot_admin_registration_handler.deregister_from_admin_server() diff --git a/tests/flask_test_server.py b/tests/flask_test_server.py index 62c7656..8034fce 100644 --- a/tests/flask_test_server.py +++ b/tests/flask_test_server.py @@ -71,4 +71,8 @@ def stop(self) -> None: self.pyctuator.stop() requests.post("http://localhost:5000/shutdown") self.thread.join() - logging.info("Flask server is shutdown") + logging.info("Flask server stopped") + + def atexit(self) -> None: + if self.pyctuator.boot_admin_registration_handler: + self.pyctuator.boot_admin_registration_handler.deregister_from_admin_server() diff --git a/tests/test_pyctuator_e2e.py b/tests/test_pyctuator_e2e.py index c1183a9..7dd3f02 100644 --- a/tests/test_pyctuator_e2e.py +++ b/tests/test_pyctuator_e2e.py @@ -32,7 +32,7 @@ def pyctuator_server(request) -> Generator: # type: ignore pyctuator_server.start() # Yield back to pytest until the module is done - yield None + yield pyctuator_server # Once the module is done, stop the pyctuator-server pyctuator_server.stop() @@ -176,14 +176,17 @@ def test_metrics_endpoint(endpoints: Endpoints) -> None: @pytest.mark.usefixtures("boot_admin_server", "pyctuator_server") @pytest.mark.mark_recurring_registration -def test_recurring_registration(registration_tracker: RegistrationTrackerFixture) -> None: +def test_recurring_registration_and_deregistration( + registration_tracker: RegistrationTrackerFixture, + pyctuator_server: PyctuatorServer +) -> None: # Verify that at least 4 registrations occurred within 10 seconds since the test started start = time.time() while registration_tracker.count < 4: time.sleep(0.5) if time.time() - start > 15: pytest.fail( - f"Expected at least 4 recurring registrations within 10 seconds but got {registration_tracker.count}") + "Expected at least 4 recurring registrations within 10 seconds but got {registration_tracker.count}") # Verify that the reported startup time is the same across all the registrations and that its later then the test's # start time @@ -192,6 +195,10 @@ def test_recurring_registration(registration_tracker: RegistrationTrackerFixture registration_start_time = datetime.fromisoformat(registration_tracker.start_time) assert registration_start_time > registration_tracker.test_start_time - timedelta(seconds=10) + # Ask to deregister (in real life, called by atexit) and verify it was registered + pyctuator_server.atexit() + assert registration_tracker.deregistration_time > registration_start_time + @pytest.mark.usefixtures("boot_admin_server", "pyctuator_server") @pytest.mark.mark_threads_endpoint From 57710e36cc3bec580bbe45f00649625345f8c526 Mon Sep 17 00:00:00 2001 From: "michael.yak" Date: Fri, 17 Jul 2020 00:56:56 +0300 Subject: [PATCH 3/4] Verify Pyctuator can register when SBA requires basic-auth Add tests for verifying Pyctuator's support for basic-auth registration with Spring Boot Admin. Solves #28 --- tests/conftest.py | 35 +++++++- tests/test_spring_boot_admin_registration.py | 86 ++++++++++++++++++++ 2 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 tests/test_spring_boot_admin_registration.py diff --git a/tests/conftest.py b/tests/conftest.py index d2f9ce6..629803c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import logging +import secrets import threading import time from abc import ABC, abstractmethod @@ -8,9 +9,10 @@ import pytest import requests -from fastapi import FastAPI +from fastapi import FastAPI, Depends, HTTPException +from fastapi.security import HTTPBasic, HTTPBasicCredentials from pydantic import BaseModel -from starlette.requests import Request +from starlette import status from uvicorn.config import Config from uvicorn.main import Server @@ -65,9 +67,21 @@ def boot_admin_server(registration_tracker: RegistrationTrackerFixture) -> Gener docs_url="/api", ) - # pylint: disable=unused-argument,unused-variable + security = HTTPBasic() + + def get_current_username(credentials: HTTPBasicCredentials = Depends(security)) -> str: + correct_username = secrets.compare_digest(credentials.username, "moo") + correct_password = secrets.compare_digest(credentials.password, "haha") + if not (correct_username and correct_password): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Moo haha", + ) + return credentials.username + + # pylint: disable=unused-variable @boot_admin_app.post("/register", tags=["admin-server"]) - def register(request: Request, registration: RegistrationRequest) -> Dict[str, str]: + def register(registration: RegistrationRequest) -> Dict[str, str]: logging.debug("Got registration post %s, %d registrations since %s", registration, registration_tracker.count, registration_tracker.start_time) registration_tracker.registration = registration @@ -76,6 +90,19 @@ def register(request: Request, registration: RegistrationRequest) -> Dict[str, s registration_tracker.start_time = registration.metadata["startup"] return {"id": "JB007"} + # pylint: disable=unused-variable + @boot_admin_app.post("/register-with-basic-auth", tags=["admin-server"]) + def register_with_basic_auth( + registration: RegistrationRequest, + username: str = Depends(get_current_username)) -> Dict[str, str]: + logging.debug("Got registration post %s from %s, %d registrations since %s", + registration, username, registration_tracker.count, registration_tracker.start_time) + registration_tracker.registration = registration + registration_tracker.count += 1 + if registration_tracker.start_time is None: + registration_tracker.start_time = registration.metadata["startup"] + return {"id": "JB007"} + # pylint: disable=unused-argument,unused-variable @boot_admin_app.delete("/register/{registration_id}", tags=["admin-server"]) def deregister(registration_id: str) -> None: diff --git a/tests/test_spring_boot_admin_registration.py b/tests/test_spring_boot_admin_registration.py new file mode 100644 index 0000000..d56c977 --- /dev/null +++ b/tests/test_spring_boot_admin_registration.py @@ -0,0 +1,86 @@ +from datetime import datetime +from typing import Optional, Any + +import pytest + +from pyctuator.auth import Auth, BasicAuth +from pyctuator.impl.spring_boot_admin_registration import BootAdminRegistrationHandler +from tests.conftest import RegistrationTrackerFixture + + +@pytest.mark.usefixtures("boot_admin_server") +def test_registration_no_auth(registration_tracker: RegistrationTrackerFixture) -> None: + registration_handler = get_registration_handler("http://localhost:8001/register", None) + + try: + registration_handler.start() + assert registration_tracker.count == 1 + + finally: + registration_handler.stop() + + +@pytest.mark.usefixtures("boot_admin_server") +def test_registration_basic_auth_no_creds(registration_tracker: RegistrationTrackerFixture, caplog: Any) -> None: + registration_handler = get_registration_handler("http://localhost:8001/register-with-basic-auth", None) + + try: + registration_handler.start() + assert registration_tracker.count == 0 + + error_message = "Failed registering with boot-admin, got %s - %s" + assert error_message in [record.msg for record in caplog.records] + + error_args = (401, b'{"detail":"Not authenticated"}') + assert error_args in [record.args for record in caplog.records if record.msg == error_message] + + finally: + registration_handler.stop() + + +@pytest.mark.usefixtures("boot_admin_server") +def test_registration_basic_auth_bad_creds(registration_tracker: RegistrationTrackerFixture, caplog: Any) -> None: + registration_handler = get_registration_handler( + "http://localhost:8001/register-with-basic-auth", + BasicAuth("kuki", "puki") + ) + + try: + registration_handler.start() + assert registration_tracker.count == 0 + + error_message = "Failed registering with boot-admin, got %s - %s" + assert error_message in [record.msg for record in caplog.records] + + error_args = (401, b'{"detail":"Moo haha"}') + assert error_args in [record.args for record in caplog.records if record.msg == error_message] + + finally: + registration_handler.stop() + + +@pytest.mark.usefixtures("boot_admin_server") +def test_registration_basic_auth(registration_tracker: RegistrationTrackerFixture) -> None: + registration_handler = get_registration_handler( + "http://localhost:8001/register-with-basic-auth", + BasicAuth("moo", "haha") + ) + + try: + registration_handler.start() + assert registration_tracker.count == 1 + + finally: + registration_handler.stop() + + +def get_registration_handler(registration_url: str, registration_auth: Optional[Auth]) -> BootAdminRegistrationHandler: + return BootAdminRegistrationHandler( + registration_url=registration_url, + registration_auth=registration_auth, + application_name="noauth", + pyctuator_base_url="http://whatever/pyctuator", + start_time=datetime.now(), + service_url="http://whatever/service", + registration_interval_sec=100 + ) From 6418662e5b3c113ff59718de434260b9aeae7d09 Mon Sep 17 00:00:00 2001 From: "michael.yak" Date: Fri, 17 Jul 2020 21:02:23 +0300 Subject: [PATCH 4/4] Apend result of the two pytest runs into one coverage report --- .github/workflows/python_package_build.yml | 25 ++++-- Makefile | 2 +- poetry.lock | 93 +++++++++++----------- tests/health/test_redis_health_provider.py | 11 ++- 4 files changed, 76 insertions(+), 55 deletions(-) diff --git a/.github/workflows/python_package_build.yml b/.github/workflows/python_package_build.yml index d25b587..486d9a4 100644 --- a/.github/workflows/python_package_build.yml +++ b/.github/workflows/python_package_build.yml @@ -12,19 +12,34 @@ jobs: container: image: matanrubin/python-poetry:3.7 + env: + TEST_REDIS_SERVER: True + REDIS_HOST: redis + + services: + # User a redis container for testing the redis health-provider + redis: + image: redis:5.0.3 + steps: - uses: actions/checkout@v2 - run: make bootstrap - run: poetry build -vvv + + # Install all dependencies except for psutil and run the tests with coverage - this tests handling missing psutil - run: poetry install --extras flask --extras fastapi --extras aiohttp --extras db --extras redis - run: make coverage + + # Run pylint+mypy after installing psutil so they don't complain on missing dependencies + - run: poetry install --extras psutil + - run: make check + + # Run tests with coverage again - this adds tests that require psutil + - run: make coverage + + # Upload coverage files to codecov - uses: actions/upload-artifact@v2 with: name: htmlcov.zip path: htmlcov/ - uses: codecov/codecov-action@v1 - - # Install the extra psutil module and run linting+tests in the "psutil enabled" env - - run: poetry install --extras psutil - - run: make check - - run: make test diff --git a/Makefile b/Makefile index e9b1a6e..fc096ba 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ test: poetry run pytest --log-cli-level=4 -vv tests coverage: - poetry run pytest --cov-report xml:./coverage.xml --cov-report html --cov-report term --cov=pyctuator --log-cli-level=4 -vv tests + poetry run pytest --cov-append --cov-report xml:./coverage.xml --cov-report html --cov-report term --cov=pyctuator --log-cli-level=4 -vv tests pylint: poetry run pylint --exit-zero pyctuator tests diff --git a/poetry.lock b/poetry.lock index fa56ee4..b42758e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -125,7 +125,7 @@ description = "Code coverage measurement for Python" name = "coverage" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "5.1" +version = "5.2" [package.extras] toml = ["toml"] @@ -349,10 +349,10 @@ description = "Cross-platform lib for process and system monitoring in Python." name = "psutil" optional = true python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "5.7.0" +version = "5.7.2" [package.extras] -enum = ["enum34"] +test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] [[package]] category = "dev" @@ -724,37 +724,40 @@ colorama = [ {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, ] coverage = [ - {file = "coverage-5.1-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65"}, - {file = "coverage-5.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2"}, - {file = "coverage-5.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04"}, - {file = "coverage-5.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6"}, - {file = "coverage-5.1-cp27-cp27m-win32.whl", hash = "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796"}, - {file = "coverage-5.1-cp27-cp27m-win_amd64.whl", hash = "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730"}, - {file = "coverage-5.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0"}, - {file = "coverage-5.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a"}, - {file = "coverage-5.1-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf"}, - {file = "coverage-5.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9"}, - {file = "coverage-5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768"}, - {file = "coverage-5.1-cp35-cp35m-win32.whl", hash = "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2"}, - {file = "coverage-5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7"}, - {file = "coverage-5.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0"}, - {file = "coverage-5.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019"}, - {file = "coverage-5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c"}, - {file = "coverage-5.1-cp36-cp36m-win32.whl", hash = "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1"}, - {file = "coverage-5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7"}, - {file = "coverage-5.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355"}, - {file = "coverage-5.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489"}, - {file = "coverage-5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd"}, - {file = "coverage-5.1-cp37-cp37m-win32.whl", hash = "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e"}, - {file = "coverage-5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a"}, - {file = "coverage-5.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55"}, - {file = "coverage-5.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c"}, - {file = "coverage-5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef"}, - {file = "coverage-5.1-cp38-cp38-win32.whl", hash = "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24"}, - {file = "coverage-5.1-cp38-cp38-win_amd64.whl", hash = "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0"}, - {file = "coverage-5.1-cp39-cp39-win32.whl", hash = "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4"}, - {file = "coverage-5.1-cp39-cp39-win_amd64.whl", hash = "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e"}, - {file = "coverage-5.1.tar.gz", hash = "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"}, + {file = "coverage-5.2-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:d9ad0a988ae20face62520785ec3595a5e64f35a21762a57d115dae0b8fb894a"}, + {file = "coverage-5.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:4bb385a747e6ae8a65290b3df60d6c8a692a5599dc66c9fa3520e667886f2e10"}, + {file = "coverage-5.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:9702e2cb1c6dec01fb8e1a64c015817c0800a6eca287552c47a5ee0ebddccf62"}, + {file = "coverage-5.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:42fa45a29f1059eda4d3c7b509589cc0343cd6bbf083d6118216830cd1a51613"}, + {file = "coverage-5.2-cp27-cp27m-win32.whl", hash = "sha256:41d88736c42f4a22c494c32cc48a05828236e37c991bd9760f8923415e3169e4"}, + {file = "coverage-5.2-cp27-cp27m-win_amd64.whl", hash = "sha256:bbb387811f7a18bdc61a2ea3d102be0c7e239b0db9c83be7bfa50f095db5b92a"}, + {file = "coverage-5.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:3740b796015b889e46c260ff18b84683fa2e30f0f75a171fb10d2bf9fb91fc70"}, + {file = "coverage-5.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ebf2431b2d457ae5217f3a1179533c456f3272ded16f8ed0b32961a6d90e38ee"}, + {file = "coverage-5.2-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:d54d7ea74cc00482a2410d63bf10aa34ebe1c49ac50779652106c867f9986d6b"}, + {file = "coverage-5.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:87bdc8135b8ee739840eee19b184804e5d57f518578ffc797f5afa2c3c297913"}, + {file = "coverage-5.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ed9a21502e9223f563e071759f769c3d6a2e1ba5328c31e86830368e8d78bc9c"}, + {file = "coverage-5.2-cp35-cp35m-win32.whl", hash = "sha256:509294f3e76d3f26b35083973fbc952e01e1727656d979b11182f273f08aa80b"}, + {file = "coverage-5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:ca63dae130a2e788f2b249200f01d7fa240f24da0596501d387a50e57aa7075e"}, + {file = "coverage-5.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:5c74c5b6045969b07c9fb36b665c9cac84d6c174a809fc1b21bdc06c7836d9a0"}, + {file = "coverage-5.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c32aa13cc3fe86b0f744dfe35a7f879ee33ac0a560684fef0f3e1580352b818f"}, + {file = "coverage-5.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1e58fca3d9ec1a423f1b7f2aa34af4f733cbfa9020c8fe39ca451b6071237405"}, + {file = "coverage-5.2-cp36-cp36m-win32.whl", hash = "sha256:3b2c34690f613525672697910894b60d15800ac7e779fbd0fccf532486c1ba40"}, + {file = "coverage-5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a4d511012beb967a39580ba7d2549edf1e6865a33e5fe51e4dce550522b3ac0e"}, + {file = "coverage-5.2-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:32ecee61a43be509b91a526819717d5e5650e009a8d5eda8631a59c721d5f3b6"}, + {file = "coverage-5.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6f91b4492c5cde83bfe462f5b2b997cdf96a138f7c58b1140f05de5751623cf1"}, + {file = "coverage-5.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bfcc811883699ed49afc58b1ed9f80428a18eb9166422bce3c31a53dba00fd1d"}, + {file = "coverage-5.2-cp37-cp37m-win32.whl", hash = "sha256:60a3d36297b65c7f78329b80120f72947140f45b5c7a017ea730f9112b40f2ec"}, + {file = "coverage-5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:12eaccd86d9a373aea59869bc9cfa0ab6ba8b1477752110cb4c10d165474f703"}, + {file = "coverage-5.2-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:d82db1b9a92cb5c67661ca6616bdca6ff931deceebb98eecbd328812dab52032"}, + {file = "coverage-5.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:214eb2110217f2636a9329bc766507ab71a3a06a8ea30cdeebb47c24dce5972d"}, + {file = "coverage-5.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8a3decd12e7934d0254939e2bf434bf04a5890c5bf91a982685021786a08087e"}, + {file = "coverage-5.2-cp38-cp38-win32.whl", hash = "sha256:1dcebae667b73fd4aa69237e6afb39abc2f27520f2358590c1b13dd90e32abe7"}, + {file = "coverage-5.2-cp38-cp38-win_amd64.whl", hash = "sha256:f50632ef2d749f541ca8e6c07c9928a37f87505ce3a9f20c8446ad310f1aa87b"}, + {file = "coverage-5.2-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:7403675df5e27745571aba1c957c7da2dacb537c21e14007ec3a417bf31f7f3d"}, + {file = "coverage-5.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:0fc4e0d91350d6f43ef6a61f64a48e917637e1dcfcba4b4b7d543c628ef82c2d"}, + {file = "coverage-5.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:25fe74b5b2f1b4abb11e103bb7984daca8f8292683957d0738cd692f6a7cc64c"}, + {file = "coverage-5.2-cp39-cp39-win32.whl", hash = "sha256:d67599521dff98ec8c34cd9652cbcfe16ed076a2209625fca9dc7419b6370e5c"}, + {file = "coverage-5.2-cp39-cp39-win_amd64.whl", hash = "sha256:10f2a618a6e75adf64329f828a6a5b40244c1c50f5ef4ce4109e904e69c71bd2"}, + {file = "coverage-5.2.tar.gz", hash = "sha256:1874bdc943654ba46d28f179c1846f5710eda3aeb265ff029e0ac2b52daae404"}, ] cryptography = [ {file = "cryptography-2.9.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e"}, @@ -919,17 +922,17 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] psutil = [ - {file = "psutil-5.7.0-cp27-none-win32.whl", hash = "sha256:298af2f14b635c3c7118fd9183843f4e73e681bb6f01e12284d4d70d48a60953"}, - {file = "psutil-5.7.0-cp27-none-win_amd64.whl", hash = "sha256:75e22717d4dbc7ca529ec5063000b2b294fc9a367f9c9ede1f65846c7955fd38"}, - {file = "psutil-5.7.0-cp35-cp35m-win32.whl", hash = "sha256:f344ca230dd8e8d5eee16827596f1c22ec0876127c28e800d7ae20ed44c4b310"}, - {file = "psutil-5.7.0-cp35-cp35m-win_amd64.whl", hash = "sha256:e2d0c5b07c6fe5a87fa27b7855017edb0d52ee73b71e6ee368fae268605cc3f5"}, - {file = "psutil-5.7.0-cp36-cp36m-win32.whl", hash = "sha256:a02f4ac50d4a23253b68233b07e7cdb567bd025b982d5cf0ee78296990c22d9e"}, - {file = "psutil-5.7.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1413f4158eb50e110777c4f15d7c759521703bd6beb58926f1d562da40180058"}, - {file = "psutil-5.7.0-cp37-cp37m-win32.whl", hash = "sha256:d008ddc00c6906ec80040d26dc2d3e3962109e40ad07fd8a12d0284ce5e0e4f8"}, - {file = "psutil-5.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:73f35ab66c6c7a9ce82ba44b1e9b1050be2a80cd4dcc3352cc108656b115c74f"}, - {file = "psutil-5.7.0-cp38-cp38-win32.whl", hash = "sha256:60b86f327c198561f101a92be1995f9ae0399736b6eced8f24af41ec64fb88d4"}, - {file = "psutil-5.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:d84029b190c8a66a946e28b4d3934d2ca1528ec94764b180f7d6ea57b0e75e26"}, - {file = "psutil-5.7.0.tar.gz", hash = "sha256:685ec16ca14d079455892f25bd124df26ff9137664af445563c1bd36629b5e0e"}, + {file = "psutil-5.7.2-cp27-none-win32.whl", hash = "sha256:f2018461733b23f308c298653c8903d32aaad7873d25e1d228765e91ae42c3f2"}, + {file = "psutil-5.7.2-cp27-none-win_amd64.whl", hash = "sha256:66c18ca7680a31bf16ee22b1d21b6397869dda8059dbdb57d9f27efa6615f195"}, + {file = "psutil-5.7.2-cp35-cp35m-win32.whl", hash = "sha256:5e9d0f26d4194479a13d5f4b3798260c20cecf9ac9a461e718eb59ea520a360c"}, + {file = "psutil-5.7.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4080869ed93cce662905b029a1770fe89c98787e543fa7347f075ade761b19d6"}, + {file = "psutil-5.7.2-cp36-cp36m-win32.whl", hash = "sha256:d8a82162f23c53b8525cf5f14a355f5d1eea86fa8edde27287dd3a98399e4fdf"}, + {file = "psutil-5.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0ee3c36428f160d2d8fce3c583a0353e848abb7de9732c50cf3356dd49ad63f8"}, + {file = "psutil-5.7.2-cp37-cp37m-win32.whl", hash = "sha256:ff1977ba1a5f71f89166d5145c3da1cea89a0fdb044075a12c720ee9123ec818"}, + {file = "psutil-5.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a5b120bb3c0c71dfe27551f9da2f3209a8257a178ed6c628a819037a8df487f1"}, + {file = "psutil-5.7.2-cp38-cp38-win32.whl", hash = "sha256:10512b46c95b02842c225f58fa00385c08fa00c68bac7da2d9a58ebe2c517498"}, + {file = "psutil-5.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:68d36986ded5dac7c2dcd42f2682af1db80d4bce3faa126a6145c1637e1b559f"}, + {file = "psutil-5.7.2.tar.gz", hash = "sha256:90990af1c3c67195c44c9a889184f84f5b2320dce3ee3acbd054e3ba0b4a7beb"}, ] py = [ {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, diff --git a/tests/health/test_redis_health_provider.py b/tests/health/test_redis_health_provider.py index a035ac5..214e01b 100644 --- a/tests/health/test_redis_health_provider.py +++ b/tests/health/test_redis_health_provider.py @@ -19,23 +19,26 @@ def require_redis_server() -> None: if not should_test_with_redis: pytest.skip("No Redis server (env TEST_REDIS_SERVER isn't True), skipping") +@pytest.fixture +def redis_host() -> str: + return os.getenv("REDIS_HOST", "localhost") @pytest.mark.usefixtures("require_redis", "require_redis_server") -def test_redis_health() -> None: +def test_redis_health(redis_host: str) -> None: import redis from pyctuator.health.health_provider import Status from pyctuator.health.redis_health_provider import RedisHealthProvider, RedisHealthStatus, RedisHealthDetails - health = RedisHealthProvider(redis.Redis()).get_health() + health = RedisHealthProvider(redis.Redis(host=redis_host)).get_health() assert health == RedisHealthStatus(Status.UP, RedisHealthDetails("5.0.3", "standalone")) @pytest.mark.usefixtures("require_redis", "require_redis_server") -def test_redis_bad_password() -> None: +def test_redis_bad_password(redis_host: str) -> None: import redis from pyctuator.health.health_provider import Status from pyctuator.health.redis_health_provider import RedisHealthProvider - health = RedisHealthProvider(redis.Redis(password="blabla")).get_health() + health = RedisHealthProvider(redis.Redis(host=redis_host, password="blabla")).get_health() assert health.status == Status.DOWN assert "Client sent AUTH, but no password is set" in str(health.details.failure)