mirror of
				https://github.com/ets-labs/python-dependency-injector.git
				synced 2025-11-04 18:07:44 +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