diff --git a/docker/Dockerfile.api b/docker/Dockerfile.api index 1805531d37..341a2ec991 100644 --- a/docker/Dockerfile.api +++ b/docker/Dockerfile.api @@ -4,6 +4,14 @@ ENV PYTHONFAULTHANDLER=1 \ PYTHONHASHSEED=random \ PYTHONUNBUFFERED=1 +# THIS IS FOR DEBUGGING PURPOSES +# RUN apt-get update && \ +# apt-get install -y --no-install-recommends \ +# iproute2 \ +# net-tools \ +# procps && \ +# rm -rf /var/lib/apt/lists/* + RUN useradd --user-group --system --create-home --no-log-init keep WORKDIR /app diff --git a/keep/api/alert_deduplicator/alert_deduplicator.py b/keep/api/alert_deduplicator/alert_deduplicator.py index 57b4e12d0d..7b4077518d 100644 --- a/keep/api/alert_deduplicator/alert_deduplicator.py +++ b/keep/api/alert_deduplicator/alert_deduplicator.py @@ -95,7 +95,7 @@ def _apply_deduplication_rule( ) alert.isPartialDuplicate = True else: - self.logger.info( + self.logger.debug( "Alert is not deduplicated", extra={ "alert_id": alert.id, diff --git a/keep/api/api.py b/keep/api/api.py index 3a54b7124d..d16fe91ba4 100644 --- a/keep/api/api.py +++ b/keep/api/api.py @@ -1,8 +1,11 @@ import asyncio import logging import os +import time from contextlib import asynccontextmanager +from functools import wraps from importlib import metadata +from typing import Awaitable, Callable import requests import uvicorn @@ -13,6 +16,7 @@ from prometheus_fastapi_instrumentator import Instrumentator from slowapi import _rate_limit_exceeded_handler from slowapi.errors import RateLimitExceeded +from slowapi.middleware import SlowAPIMiddleware from starlette.middleware.cors import CORSMiddleware from starlette_context import plugins from starlette_context.middleware import RawContextMiddleware @@ -79,6 +83,8 @@ CONSUMER = config("CONSUMER", default="true", cast=bool) TOPOLOGY = config("KEEP_TOPOLOGY_PROCESSOR", default="false", cast=bool) KEEP_DEBUG_TASKS = config("KEEP_DEBUG_TASKS", default="false", cast=bool) +KEEP_DEBUG_MIDDLEWARES = config("KEEP_DEBUG_MIDDLEWARES", default="false", cast=bool) +KEEP_USE_LIMITER = config("KEEP_USE_LIMITER", default="false", cast=bool) AUTH_TYPE = config("AUTH_TYPE", default=IdentityManagerTypes.NOAUTH.value).lower() try: @@ -329,6 +335,8 @@ async def catch_exception(request: Request, exc: Exception): ) app.add_middleware(LoggingMiddleware) + if KEEP_USE_LIMITER: + app.add_middleware(SlowAPIMiddleware) if config("KEEP_METRICS", default="true", cast=bool): Instrumentator( @@ -339,6 +347,62 @@ async def catch_exception(request: Request, exc: Exception): if config("KEEP_OTEL_ENABLED", default="true", cast=bool): keep.api.observability.setup(app) + # if debug middlewares are enabled, instrument them + if KEEP_DEBUG_MIDDLEWARES: + logger.info("Instrumenting middlewares") + app = instrument_middleware(app) + logger.info("Instrumented middlewares") + return app + + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + + +# SHAHAR: +# This (and instrument_middleware) is a helper function to wrap the call of a middleware with timing +# It will log the time it took for the middleware to run +# It should NOT be used in production! +def wrap_call(middleware_cls, original_call): + # if the call is already wrapped, return it + if hasattr(original_call, "_timing_wrapped"): + return original_call + + @wraps(original_call) + async def timed_call( + self, + scope: dict, + receive: Callable[[], Awaitable[dict]], + send: Callable[[dict], Awaitable[None]], + ): + if scope["type"] != "http": + return await original_call(self, scope, receive, send) + + start_time = time.time() + try: + response = await original_call(self, scope, receive, send) + return response + finally: + process_time = (time.time() - start_time) * 1000 + path = scope.get("path", "") + method = scope.get("method", "") + middleware_name = self.__class__.__name__ + logger.info( + f"⏱️ {middleware_name:<40} {method} {path} took {process_time:>8.2f}ms" + ) + + timed_call._timing_wrapped = True + return timed_call + + +def instrument_middleware(app): + # Get middleware from FastAPI app + for middleware in app.user_middleware: + if hasattr(middleware.cls, "__call__"): + original_call = middleware.cls.__call__ + middleware.cls.__call__ = wraps(original_call)( + wrap_call(middleware.cls, original_call) + ) return app diff --git a/keep/api/core/dependencies.py b/keep/api/core/dependencies.py index 5e57cf171e..dc85afb21b 100644 --- a/keep/api/core/dependencies.py +++ b/keep/api/core/dependencies.py @@ -38,6 +38,7 @@ async def extract_generic_body(request: Request) -> dict | bytes | FormData: def get_pusher_client() -> Pusher | None: + logger.debug("Getting pusher client") pusher_disabled = os.environ.get("PUSHER_DISABLED", "false") == "true" pusher_host = os.environ.get("PUSHER_HOST") pusher_app_id = os.environ.get("PUSHER_APP_ID") @@ -53,7 +54,7 @@ def get_pusher_client() -> Pusher | None: return None # TODO: defaults on open source no docker - return Pusher( + pusher = Pusher( host=pusher_host, port=( int(os.environ.get("PUSHER_PORT")) @@ -66,3 +67,5 @@ def get_pusher_client() -> Pusher | None: ssl=False if os.environ.get("PUSHER_USE_SSL", False) is False else True, cluster=os.environ.get("PUSHER_CLUSTER"), ) + logging.debug("Pusher client initialized") + return pusher diff --git a/keep/api/core/limiter.py b/keep/api/core/limiter.py index 33d22ccd48..a6d37923b8 100644 --- a/keep/api/core/limiter.py +++ b/keep/api/core/limiter.py @@ -8,6 +8,10 @@ logger = logging.getLogger(__name__) limiter_enabled = config("KEEP_USE_LIMITER", default="false", cast=bool) +default_limit = config("KEEP_LIMIT_CONCURRENCY", default="100/minute", cast=str) + logger.warning(f"Rate limiter is {'enabled' if limiter_enabled else 'disabled'}") -limiter = Limiter(key_func=get_remote_address, enabled=limiter_enabled) +limiter = Limiter( + key_func=get_remote_address, enabled=limiter_enabled, default_limits=[default_limit] +) diff --git a/keep/api/logging.py b/keep/api/logging.py index 96b4b28a87..5b1054878b 100644 --- a/keep/api/logging.py +++ b/keep/api/logging.py @@ -229,7 +229,7 @@ def format(self, record): }, "dev_terminal": { "()": DevTerminalFormatter, - "format": "%(asctime)s - %(thread)s %(threadName)s %(levelname)s - %(message)s", + "format": "%(asctime)s - %(thread)s %(otelTraceID)s %(threadName)s %(levelname)s - %(message)s", }, }, "handlers": { @@ -255,6 +255,11 @@ def format(self, record): "level": LOG_LEVEL, "propagate": False, }, + "slowapi": { + "handlers": ["default"], + "level": LOG_LEVEL, + "propagate": False, + }, # shut the open telemetry logger down since it keep pprints str: try: @@ -28,9 +31,12 @@ def _extract_identity(request: Request, attribute="email") -> str: if not api_key: return "anonymous" - api_key = get_api_key(api_key) - if api_key: - return api_key.tenant_id + # allow disabling the extraction of the identity from the api key + # for high performance scenarios + if KEEP_EXTRACT_IDENTITY: + api_key = get_api_key(api_key) + if api_key: + return api_key.tenant_id return "anonymous" except Exception: return "anonymous" diff --git a/keep/api/routes/alerts.py b/keep/api/routes/alerts.py index d82c27855e..ae8837bc31 100644 --- a/keep/api/routes/alerts.py +++ b/keep/api/routes/alerts.py @@ -11,7 +11,7 @@ import celpy from arq import ArqRedis -from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query, Request +from fastapi import APIRouter, Depends, HTTPException, Query, Request from fastapi.responses import JSONResponse from pusher import Pusher @@ -28,7 +28,6 @@ ) from keep.api.core.dependencies import extract_generic_body, get_pusher_client from keep.api.core.elastic import ElasticClient -from keep.api.core.limiter import limiter from keep.api.core.metrics import running_tasks_by_process_gauge, running_tasks_gauge from keep.api.models.alert import ( AlertDto, @@ -53,7 +52,7 @@ logger = logging.getLogger(__name__) REDIS = os.environ.get("REDIS", "false") == "true" -EVENT_WORKERS = int(config("KEEP_EVENT_WORKERS", default=50, cast=int)) +EVENT_WORKERS = int(config("KEEP_EVENT_WORKERS", default=5, cast=int)) # Create dedicated threadpool process_event_executor = ThreadPoolExecutor( @@ -316,7 +315,6 @@ def discard_future( def create_process_event_task( - bg_tasks: BackgroundTasks, tenant_id: str, provider_type: str | None, provider_id: str | None, @@ -358,16 +356,13 @@ def create_process_event_task( response_model=AlertDto | list[AlertDto], status_code=202, ) -@limiter.limit(config("KEEP_LIMIT_CONCURRENCY", default="100/minute", cast=str)) async def receive_generic_event( event: AlertDto | list[AlertDto] | dict, - bg_tasks: BackgroundTasks, request: Request, fingerprint: str | None = None, authenticated_entity: AuthenticatedEntity = Depends( IdentityManagerFactory.get_auth_verifier(["write:alert"]) ), - pusher_client: Pusher = Depends(get_pusher_client), ): """ A generic webhook endpoint that can be used by any provider to send alerts to Keep. @@ -402,7 +397,6 @@ async def receive_generic_event( task_name = job.job_id else: task_name = create_process_event_task( - bg_tasks, authenticated_entity.tenant_id, None, None, @@ -447,10 +441,8 @@ async def webhook_challenge(): description="Receive an alert event from a provider", status_code=202, ) -@limiter.limit(config("KEEP_LIMIT_CONCURRENCY", default="100/minute", cast=str)) async def receive_event( provider_type: str, - bg_tasks: BackgroundTasks, request: Request, provider_id: str | None = None, fingerprint: str | None = None, @@ -458,7 +450,6 @@ async def receive_event( authenticated_entity: AuthenticatedEntity = Depends( IdentityManagerFactory.get_auth_verifier(["write:alert"]) ), - pusher_client: Pusher = Depends(get_pusher_client), ) -> dict[str, str]: trace_id = request.state.trace_id running_tasks: set = request.state.background_tasks @@ -512,7 +503,6 @@ async def receive_event( task_name = job.job_id else: task_name = create_process_event_task( - bg_tasks, authenticated_entity.tenant_id, provider_type, provider_id, diff --git a/keep/api/routes/providers.py b/keep/api/routes/providers.py index 808b3cae01..9992a6e25b 100644 --- a/keep/api/routes/providers.py +++ b/keep/api/routes/providers.py @@ -14,6 +14,7 @@ from keep.api.core.config import config from keep.api.core.db import count_alerts, get_provider_distribution, get_session +from keep.api.core.limiter import limiter from keep.api.models.db.provider import Provider from keep.api.models.provider import Provider as ProviderDTO from keep.api.models.provider import ProviderAlertsCountResponseDTO @@ -133,6 +134,7 @@ def get_provider_logs( description="export all installed providers", response_model=list[ProviderDTO], ) +@limiter.exempt def get_installed_providers( authenticated_entity: AuthenticatedEntity = Depends( IdentityManagerFactory.get_auth_verifier(["read:providers"]) diff --git a/poetry.lock b/poetry.lock index c98e7ec711..b92c27cfa9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2161,6 +2161,61 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] trio = ["trio (>=0.22.0,<1.0)"] +[[package]] +name = "httptools" +version = "0.6.4" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0"}, + {file = "httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da"}, + {file = "httptools-0.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deee0e3343f98ee8047e9f4c5bc7cedbf69f5734454a94c38ee829fb2d5fa3c1"}, + {file = "httptools-0.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca80b7485c76f768a3bc83ea58373f8db7b015551117375e4918e2aa77ea9b50"}, + {file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:90d96a385fa941283ebd231464045187a31ad932ebfa541be8edf5b3c2328959"}, + {file = "httptools-0.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:59e724f8b332319e2875efd360e61ac07f33b492889284a3e05e6d13746876f4"}, + {file = "httptools-0.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:c26f313951f6e26147833fc923f78f95604bbec812a43e5ee37f26dc9e5a686c"}, + {file = "httptools-0.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f47f8ed67cc0ff862b84a1189831d1d33c963fb3ce1ee0c65d3b0cbe7b711069"}, + {file = "httptools-0.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0614154d5454c21b6410fdf5262b4a3ddb0f53f1e1721cfd59d55f32138c578a"}, + {file = "httptools-0.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8787367fbdfccae38e35abf7641dafc5310310a5987b689f4c32cc8cc3ee975"}, + {file = "httptools-0.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b0f7fe4fd38e6a507bdb751db0379df1e99120c65fbdc8ee6c1d044897a636"}, + {file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40a5ec98d3f49904b9fe36827dcf1aadfef3b89e2bd05b0e35e94f97c2b14721"}, + {file = "httptools-0.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dacdd3d10ea1b4ca9df97a0a303cbacafc04b5cd375fa98732678151643d4988"}, + {file = "httptools-0.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:288cd628406cc53f9a541cfaf06041b4c71d751856bab45e3702191f931ccd17"}, + {file = "httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2"}, + {file = "httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44"}, + {file = "httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1"}, + {file = "httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2"}, + {file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81"}, + {file = "httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f"}, + {file = "httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970"}, + {file = "httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660"}, + {file = "httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083"}, + {file = "httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3"}, + {file = "httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071"}, + {file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5"}, + {file = "httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0"}, + {file = "httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8"}, + {file = "httptools-0.6.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d3f0d369e7ffbe59c4b6116a44d6a8eb4783aae027f2c0b366cf0aa964185dba"}, + {file = "httptools-0.6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:94978a49b8f4569ad607cd4946b759d90b285e39c0d4640c6b36ca7a3ddf2efc"}, + {file = "httptools-0.6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40dc6a8e399e15ea525305a2ddba998b0af5caa2566bcd79dcbe8948181eeaff"}, + {file = "httptools-0.6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab9ba8dcf59de5181f6be44a77458e45a578fc99c31510b8c65b7d5acc3cf490"}, + {file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fc411e1c0a7dcd2f902c7c48cf079947a7e65b5485dea9decb82b9105ca71a43"}, + {file = "httptools-0.6.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d54efd20338ac52ba31e7da78e4a72570cf729fac82bc31ff9199bedf1dc7440"}, + {file = "httptools-0.6.4-cp38-cp38-win_amd64.whl", hash = "sha256:df959752a0c2748a65ab5387d08287abf6779ae9165916fe053e68ae1fbdc47f"}, + {file = "httptools-0.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:85797e37e8eeaa5439d33e556662cc370e474445d5fab24dcadc65a8ffb04003"}, + {file = "httptools-0.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:db353d22843cf1028f43c3651581e4bb49374d85692a85f95f7b9a130e1b2cab"}, + {file = "httptools-0.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ffd262a73d7c28424252381a5b854c19d9de5f56f075445d33919a637e3547"}, + {file = "httptools-0.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c346571fa50d2e9856a37d7cd9435a25e7fd15e236c397bf224afaa355fe9"}, + {file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aafe0f1918ed07b67c1e838f950b1c1fabc683030477e60b335649b8020e1076"}, + {file = "httptools-0.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0e563e54979e97b6d13f1bbc05a96109923e76b901f786a5eae36e99c01237bd"}, + {file = "httptools-0.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:b799de31416ecc589ad79dd85a0b2657a8fe39327944998dea368c1d4c9e55e6"}, + {file = "httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c"}, +] + +[package.extras] +test = ["Cython (>=0.29.24)"] + [[package]] name = "httpx" version = "0.26.0" @@ -2740,7 +2795,7 @@ name = "ndg-httpsclient" version = "0.5.1" description = "Provides enhanced HTTPS support for httplib and urllib2 using PyOpenSSL" optional = false -python-versions = ">=2.7,<3.0.dev0 || >=3.4.dev0" +python-versions = ">=2.7,<3.0.0 || >=3.4.0" files = [ {file = "ndg_httpsclient-0.5.1-py2-none-any.whl", hash = "sha256:d2c7225f6a1c6cf698af4ebc962da70178a99bcde24ee6d1961c4f3338130d57"}, {file = "ndg_httpsclient-0.5.1-py3-none-any.whl", hash = "sha256:dd174c11d971b6244a891f7be2b32ca9853d3797a72edb34fa5d7b07d8fff7d4"}, @@ -5153,6 +5208,57 @@ h11 = ">=0.8" [package.extras] standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] +[[package]] +name = "uvloop" +version = "0.21.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, + {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f38b2e090258d051d68a5b14d1da7203a3c3677321cf32a95a6f4db4dd8b6f26"}, + {file = "uvloop-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c43e0f13022b998eb9b973b5e97200c8b90823454d4bc06ab33829e09fb9bb"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10d66943def5fcb6e7b37310eb6b5639fd2ccbc38df1177262b0640c3ca68c1f"}, + {file = "uvloop-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:67dd654b8ca23aed0a8e99010b4c34aca62f4b7fce88f39d452ed7622c94845c"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c0f3fa6200b3108919f8bdabb9a7f87f20e7097ea3c543754cabc7d717d95cf8"}, + {file = "uvloop-0.21.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0878c2640cf341b269b7e128b1a5fed890adc4455513ca710d77d5e93aa6d6a0"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9fb766bb57b7388745d8bcc53a359b116b8a04c83a2288069809d2b3466c37e"}, + {file = "uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a375441696e2eda1c43c44ccb66e04d61ceeffcd76e4929e527b7fa401b90fb"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:baa0e6291d91649c6ba4ed4b2f982f9fa165b5bbd50a9e203c416a2797bab3c6"}, + {file = "uvloop-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4509360fcc4c3bd2c70d87573ad472de40c13387f5fda8cb58350a1d7475e58d"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c"}, + {file = "uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d"}, + {file = "uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb"}, + {file = "uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281"}, + {file = "uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6"}, + {file = "uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc"}, + {file = "uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:17df489689befc72c39a08359efac29bbee8eee5209650d4b9f34df73d22e414"}, + {file = "uvloop-0.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc09f0ff191e61c2d592a752423c767b4ebb2986daa9ed62908e2b1b9a9ae206"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0ce1b49560b1d2d8a2977e3ba4afb2414fb46b86a1b64056bc4ab929efdafbe"}, + {file = "uvloop-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e678ad6fe52af2c58d2ae3c73dc85524ba8abe637f134bf3564ed07f555c5e79"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:460def4412e473896ef179a1671b40c039c7012184b627898eea5072ef6f017a"}, + {file = "uvloop-0.21.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:10da8046cc4a8f12c91a1c39d1dd1585c41162a15caaef165c2174db9ef18bdc"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c097078b8031190c934ed0ebfee8cc5f9ba9642e6eb88322b9958b649750f72b"}, + {file = "uvloop-0.21.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46923b0b5ee7fc0020bef24afe7836cb068f5050ca04caf6b487c513dc1a20b2"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e420a3afe22cdcf2a0f4846e377d16e718bc70103d7088a4f7623567ba5fb0"}, + {file = "uvloop-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88cb67cdbc0e483da00af0b2c3cdad4b7c61ceb1ee0f33fe00e09c81e3a6cb75"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:221f4f2a1f46032b403bf3be628011caf75428ee3cc204a22addf96f586b19fd"}, + {file = "uvloop-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d1f581393673ce119355d56da84fe1dd9d2bb8b3d13ce792524e1607139feff"}, + {file = "uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3"}, +] + +[package.extras] +dev = ["Cython (>=3.0,<4.0)", "setuptools (>=60)"] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["aiohttp (>=3.10.5)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + [[package]] name = "validators" version = "0.20.0" @@ -5454,4 +5560,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "1512d12ccdbb4d184d54e12db72e5e9407388581976447fef32dd09cd60156bf" +content-hash = "a34041d4abecd3f6321fe244ff43f9162a10ca156b1b938ffe5007f3a1c02f31" diff --git a/pyproject.toml b/pyproject.toml index c9b961aa4c..219ab9289b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "keep" -version = "0.34.7" +version = "0.34.8" description = "Alerting. for developers, by developers." authors = ["Keep Alerting LTD"] packages = [{include = "keep"}] @@ -91,6 +91,8 @@ psycopg2-binary = "^2.9.10" prometheus-fastapi-instrumentator = "^7.0.0" slowapi = "^0.1.9" +uvloop = "^0.21.0" +httptools = "^0.6.4" [tool.poetry.group.dev.dependencies] pre-commit = "^3.0.4" pre-commit-hooks = "^4.4.0" diff --git a/tests/fixtures/client.py b/tests/fixtures/client.py index d97e83ecb1..35db06f33a 100644 --- a/tests/fixtures/client.py +++ b/tests/fixtures/client.py @@ -11,6 +11,7 @@ @pytest.fixture def test_app(monkeypatch, request): + monkeypatch.setenv("KEEP_USE_LIMITER", "false") # Check if request.param is a dict or a string if isinstance(request.param, dict): # Set environment variables based on the provided dictionary @@ -52,11 +53,12 @@ def test_app(monkeypatch, request): # Fixture for TestClient using the test_app fixture @pytest.fixture def client(test_app, db_session, monkeypatch): - # disable pusher + # Your existing environment setup monkeypatch.setenv("PUSHER_DISABLED", "true") monkeypatch.setenv("KEEP_DEBUG_TASKS", "true") monkeypatch.setenv("LOGGING_LEVEL", "DEBUG") monkeypatch.setenv("SQLALCHEMY_WARN_20", "1") + with TestClient(test_app) as client: yield client