mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-02-11 09:00:57 +03:00
Add prototype
This commit is contained in:
parent
dc9c1dde3f
commit
e56b72e411
13
examples/miniapps/monitoring-daemon-asyncio/Dockerfile
Normal file
13
examples/miniapps/monitoring-daemon-asyncio/Dockerfile
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
FROM python:3.8-buster
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
WORKDIR /code
|
||||||
|
COPY . /code/
|
||||||
|
|
||||||
|
RUN apt-get install openssl \
|
||||||
|
&& pip install --upgrade pip \
|
||||||
|
&& pip install -r requirements.txt \
|
||||||
|
&& rm -rf ~/.cache
|
||||||
|
|
||||||
|
CMD ["python", "-m", "monitoringdaemon"]
|
17
examples/miniapps/monitoring-daemon-asyncio/config.yml
Normal file
17
examples/miniapps/monitoring-daemon-asyncio/config.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
log:
|
||||||
|
level: "INFO"
|
||||||
|
format: "[%(asctime)s] [%(levelname)s] [%(name)s]: %(message)s"
|
||||||
|
|
||||||
|
monitors:
|
||||||
|
|
||||||
|
example:
|
||||||
|
method: "GET"
|
||||||
|
url: "http://example.com"
|
||||||
|
check_every: 5
|
||||||
|
expected_codes: [200]
|
||||||
|
|
||||||
|
httpbin:
|
||||||
|
method: "GET"
|
||||||
|
url: "http://httpbin.org/get"
|
||||||
|
check_every: 5
|
||||||
|
expected_codes: [200]
|
|
@ -0,0 +1,9 @@
|
||||||
|
version: "3.7"
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
monitor:
|
||||||
|
build: ./
|
||||||
|
image: monitoring-daemon
|
||||||
|
volumes:
|
||||||
|
- "./:/code"
|
|
@ -0,0 +1 @@
|
||||||
|
"""Top-level package."""
|
|
@ -0,0 +1,18 @@
|
||||||
|
"""Main module."""
|
||||||
|
|
||||||
|
from .containers import ApplicationContainer
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
"""Run the application."""
|
||||||
|
container = ApplicationContainer()
|
||||||
|
|
||||||
|
container.config.from_yaml('config.yml')
|
||||||
|
container.configure_logging()
|
||||||
|
|
||||||
|
dispatcher = container.dispatcher()
|
||||||
|
dispatcher.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,43 @@
|
||||||
|
"""Application containers module."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from dependency_injector import containers, providers
|
||||||
|
|
||||||
|
from . import http, monitors, dispatcher
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationContainer(containers.DeclarativeContainer):
|
||||||
|
"""Application container."""
|
||||||
|
|
||||||
|
config = providers.Configuration()
|
||||||
|
|
||||||
|
configure_logging = providers.Callable(
|
||||||
|
logging.basicConfig,
|
||||||
|
stream=sys.stdout,
|
||||||
|
level=config.log.level,
|
||||||
|
format=config.log.format,
|
||||||
|
)
|
||||||
|
|
||||||
|
http_client = providers.Factory(http.HttpClient)
|
||||||
|
|
||||||
|
example_monitor = providers.Factory(
|
||||||
|
monitors.HttpMonitor,
|
||||||
|
http_client=http_client,
|
||||||
|
options=config.monitors.example,
|
||||||
|
)
|
||||||
|
|
||||||
|
httpbin_monitor = providers.Factory(
|
||||||
|
monitors.HttpMonitor,
|
||||||
|
http_client=http_client,
|
||||||
|
options=config.monitors.httpbin,
|
||||||
|
)
|
||||||
|
|
||||||
|
dispatcher = providers.Factory(
|
||||||
|
dispatcher.Dispatcher,
|
||||||
|
monitors=providers.List(
|
||||||
|
example_monitor,
|
||||||
|
httpbin_monitor,
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,67 @@
|
||||||
|
"""Dispatcher module."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import signal
|
||||||
|
import time
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from .monitors import Monitor
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Dispatcher:
|
||||||
|
|
||||||
|
def __init__(self, monitors: List[Monitor]) -> None:
|
||||||
|
self._monitors = monitors
|
||||||
|
self._monitor_tasks: List[asyncio.Task] = [] # type: ignore
|
||||||
|
self._stopping = False
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
asyncio.run(self._do_work())
|
||||||
|
|
||||||
|
async def _do_work(self) -> None:
|
||||||
|
logger.info('Dispatcher is starting up')
|
||||||
|
|
||||||
|
for monitor in self._monitors:
|
||||||
|
self._monitor_tasks.append(asyncio.create_task(self._run_monitor(monitor)))
|
||||||
|
logger.info('Monitoring task has been started %s', monitor.full_name)
|
||||||
|
|
||||||
|
asyncio.get_event_loop().add_signal_handler(signal.SIGTERM, self._stop)
|
||||||
|
asyncio.get_event_loop().add_signal_handler(signal.SIGINT, self._stop)
|
||||||
|
|
||||||
|
await asyncio.gather(*self._monitor_tasks, return_exceptions=True)
|
||||||
|
|
||||||
|
self._stop()
|
||||||
|
|
||||||
|
def _stop(self) -> None:
|
||||||
|
if self._stopping:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._stopping = True
|
||||||
|
|
||||||
|
logger.info('Dispatcher is shutting down')
|
||||||
|
for task, monitor in zip(self._monitor_tasks, self._monitors):
|
||||||
|
task.cancel()
|
||||||
|
logger.info('Monitoring task has been stopped %s', monitor.full_name)
|
||||||
|
logger.info('Dispatcher shutting down finished successfully')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def _run_monitor(monitor: Monitor) -> None:
|
||||||
|
def _until_next(last: float) -> float:
|
||||||
|
time_took = time.time() - last
|
||||||
|
return monitor.check_every - time_took
|
||||||
|
|
||||||
|
while True:
|
||||||
|
time_start = time.time()
|
||||||
|
|
||||||
|
try:
|
||||||
|
await monitor.check()
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
monitor.logger.exception('Error running monitoring check')
|
||||||
|
|
||||||
|
await asyncio.sleep(_until_next(last=time_start))
|
|
@ -0,0 +1,11 @@
|
||||||
|
"""Http client module."""
|
||||||
|
|
||||||
|
from aiohttp import ClientSession, ClientResponse
|
||||||
|
|
||||||
|
|
||||||
|
class HttpClient:
|
||||||
|
|
||||||
|
async def request(self, method: str, url: str) -> ClientResponse:
|
||||||
|
async with ClientSession() as session:
|
||||||
|
async with session.request(method, url) as response:
|
||||||
|
return response
|
|
@ -0,0 +1,42 @@
|
||||||
|
"""Monitors module."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from .http import HttpClient
|
||||||
|
|
||||||
|
|
||||||
|
class Monitor:
|
||||||
|
|
||||||
|
def __init__(self, check_every: int) -> None:
|
||||||
|
self.check_every = check_every
|
||||||
|
self.logger = logging.getLogger(self.full_name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def full_name(self) -> str:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
async def check(self) -> None:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class HttpMonitor(Monitor):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
http_client: HttpClient,
|
||||||
|
options: Dict[str, Any],
|
||||||
|
) -> None:
|
||||||
|
self._client = http_client
|
||||||
|
self._method = options.pop('method')
|
||||||
|
self._url = options.pop('url')
|
||||||
|
self._expected_codes = options.pop('expected_codes')
|
||||||
|
super().__init__(check_every=options.pop('check_every'))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def full_name(self) -> str:
|
||||||
|
return '{0}.{1}(url="{2}")'.format(__name__, self.__class__.__name__, self._url)
|
||||||
|
|
||||||
|
async def check(self) -> None:
|
||||||
|
response = await self._client.request(method=self._method, url=self._url)
|
||||||
|
self.logger.info('Return response code: %s', response.status)
|
|
@ -0,0 +1,3 @@
|
||||||
|
dependency-injector
|
||||||
|
aiohttp
|
||||||
|
pyyaml
|
Loading…
Reference in New Issue
Block a user