From 9e584d1114ad7d8aaa666d23e10eb9f65ff3f470 Mon Sep 17 00:00:00 2001 From: gsd Date: Sun, 9 Mar 2025 23:26:20 +0300 Subject: [PATCH] i am first --- Dockerfile | 5 +++ docker-compose.yaml | 11 +++++++ server.py | 77 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.yaml create mode 100644 server.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8c810d6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.10 +RUN pip install fastapi uvicorn +WORKDIR /app +COPY server.py /app/ +ENTRYPOINT ["python3", "server.py"] \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..802a56e --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,11 @@ +services: + node_exporter_relay: + build: ./ + environment: + - NEC_HOST=0.0.0.0 + - NEC_PORT=9100 + - DEAD_AFTER=60 + ports: + - 9100:9100 + env_file: + - .env \ No newline at end of file diff --git a/server.py b/server.py new file mode 100644 index 0000000..0085c55 --- /dev/null +++ b/server.py @@ -0,0 +1,77 @@ +from fastapi import FastAPI +from fastapi import Request +from fastapi.responses import Response +from fastapi import BackgroundTasks +import uvicorn +import os +from time import time +import asyncio + +class Collector: + app = FastAPI() + secrets = [] + hosts = [] + store = {} + def __init__(self) -> None: + self.secrets = os.getenv("KEYS", "secret").split(",") + self.hosts = os.getenv("HOSTS", "srv").split(",") + self.dead_after = int(os.getenv("DEAD_AFTER", "60")) + self.build_routes() + + def check_headers(self, request: Request): + if not request.headers.get("Secret", ""): + print("secret empty") + return Response(status_code=400) + if not request.headers.get("Host", ""): + print("host empty") + return Response(status_code=400) + + if not request.headers.get("Secret", "") in self.secrets: + return Response(status_code=400) + + if not request.headers.get("Host", "").split(":")[0] in self.hosts: + print(request.headers.get("Host", "")) + print(request.headers) + return Response(status_code=400) + + return None + + async def deadline(self, host): + await asyncio.sleep(self.dead_after) + if host in self.store: + if time()+1 - self.store[host]['timestamp'] > self.dead_after: + print(f"{host} is dead") + del self.store[host] + + + def build_routes(self): + @self.app.get("/metrics") + async def get(request: Request): + response: Response = self.check_headers(request) + if response: + return response + + if request.headers.get("Host", "").split(":")[0] in self.store and self.store[request.headers.get("Host", "").split(":")[0]]['data']: + return Response(content=self.store[request.headers.get("Host", "").split(":")[0]]['data'], status_code=200) + else: + return Response(status_code=404) + + @self.app.post("/metrics") + async def post(request: Request, background_tasks: BackgroundTasks): + response: Response = self.check_headers(request) + if response: + return response + + host = request.headers.get("Host", "").split(":")[0] + + self.store[host] = { + "data":await request.body(), + "timestamp": time() + } + + background_tasks.add_task(self.deadline, host) + return Response(status_code=200) + +if __name__ == "__main__": + collector = Collector() + uvicorn.run(collector.app, host = os.getenv("NEC_HOST", "0.0.0.0"), port = int(os.getenv("NEC_PORT", "9100"))) \ No newline at end of file