Bring everything up-to-date
This commit is contained in:
parent
0042d2367a
commit
c4fbfe53a3
21
Dockerfile
21
Dockerfile
|
@ -1,20 +1,19 @@
|
||||||
FROM python:3.8-slim-buster
|
FROM python:3.11-alpine AS base
|
||||||
|
|
||||||
# Keeps Python from generating .pyc files in the container
|
# Keeps Python from generating .pyc files in the container
|
||||||
ENV PYTHONDONTWRITEBYTECODE 1
|
|
||||||
|
|
||||||
# Turns off buffering for easier container logging
|
|
||||||
ENV PYTHONUNBUFFERED 1
|
|
||||||
|
|
||||||
|
FROM base AS build
|
||||||
ADD requirements.txt .
|
ADD requirements.txt .
|
||||||
RUN python -m pip install -r requirements.txt
|
RUN python -m pip install --user -r requirements.txt
|
||||||
|
|
||||||
|
FROM base AS app
|
||||||
|
RUN adduser -h /app -D appuser
|
||||||
|
COPY --from=build --chown=appuser /root/.local /app/.local
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY main.py /app
|
COPY --chown=appuser main.py /app
|
||||||
|
|
||||||
RUN useradd appuser && chown -R appuser /app
|
|
||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
|
FROM app
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
|
||||||
ENTRYPOINT ["python", "main.py"]
|
ENTRYPOINT ["python", "main.py"]
|
||||||
|
|
139
main.py
139
main.py
|
@ -4,7 +4,7 @@ Present observational weather data to Prometheus
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from typing import Dict, List, TypedDict
|
from typing import TypedDict
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from flask import Flask, Response
|
from flask import Flask, Response
|
||||||
|
@ -21,33 +21,28 @@ class PromData(TypedDict):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key: str
|
key: str
|
||||||
labels: Dict[str, str]
|
labels: dict[str, str]
|
||||||
type: str
|
type: str
|
||||||
value: float
|
value: float
|
||||||
|
|
||||||
|
|
||||||
def fetch_weather_data(location: int, apikey: str) -> Dict[str, Dict[str, float]]:
|
def fetch_weather_data(location: str, apikey: str) -> dict[str, dict[str, float]]:
|
||||||
"""
|
"""
|
||||||
Fetch current data from the Met Office for the provided postcode
|
Fetch current data from the Met Office for the provided postcode
|
||||||
"""
|
"""
|
||||||
|
|
||||||
obs_data = requests.get(
|
obs_data = requests.get(f"http://api.weatherapi.com/v1/current.json?key={apikey}&q={location}&aqi=yes", timeout=30)
|
||||||
f'http://api.weatherapi.com/v1/current.json?'
|
|
||||||
f'key={apikey}&'
|
|
||||||
f'q={location}&'
|
|
||||||
f'aqi=yes'
|
|
||||||
)
|
|
||||||
|
|
||||||
return dict(json.loads(obs_data.content))
|
return dict(json.loads(obs_data.content))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/metrics')
|
@app.route("/metrics")
|
||||||
def metrics() -> Response:
|
def metrics() -> Response:
|
||||||
"""
|
"""
|
||||||
Output Prometheus-style metrics
|
Output Prometheus-style metrics
|
||||||
"""
|
"""
|
||||||
apikey = os.environ.get('WEATHER_APIKEY')
|
apikey = os.environ.get("WEATHER_APIKEY")
|
||||||
location = os.environ.get('WEATHER_LOCATION')
|
location = os.environ.get("WEATHER_LOCATION")
|
||||||
|
|
||||||
if not location:
|
if not location:
|
||||||
return Response(
|
return Response(
|
||||||
|
@ -55,88 +50,88 @@ def metrics() -> Response:
|
||||||
status=500,
|
status=500,
|
||||||
)
|
)
|
||||||
|
|
||||||
latest_data = fetch_weather_data(int(location), str(apikey))['current']
|
latest_data = fetch_weather_data(location, str(apikey))["current"]
|
||||||
|
|
||||||
ret_data: List[PromData] = [
|
ret_data: list[PromData] = [
|
||||||
{
|
{
|
||||||
'key': 'sensor_weather_outdoor_temperature_celsius',
|
"key": "sensor_weather_outdoor_temperature_celsius",
|
||||||
'labels': {
|
"labels": {
|
||||||
'location': location,
|
"location": location,
|
||||||
},
|
},
|
||||||
'type': 'gauge',
|
"type": "gauge",
|
||||||
'value': float(latest_data['temp_c']),
|
"value": float(latest_data["temp_c"]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'key': 'sensor_weather_outdoor_humidity_percent',
|
"key": "sensor_weather_outdoor_humidity_percent",
|
||||||
'labels': {
|
"labels": {
|
||||||
'location': location,
|
"location": location,
|
||||||
},
|
},
|
||||||
'type': 'gauge',
|
"type": "gauge",
|
||||||
'value': float(latest_data['humidity']),
|
"value": float(latest_data["humidity"]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'key': 'sensor_weather_outdoor_uv',
|
"key": "sensor_weather_outdoor_uv",
|
||||||
'labels': {
|
"labels": {
|
||||||
'location': location,
|
"location": location,
|
||||||
},
|
},
|
||||||
'type': 'gauge',
|
"type": "gauge",
|
||||||
'value': float(latest_data['uv']),
|
"value": float(latest_data["uv"]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'key': 'sensor_weather_outdoor_cloud_percent',
|
"key": "sensor_weather_outdoor_cloud_percent",
|
||||||
'labels': {
|
"labels": {
|
||||||
'location': location,
|
"location": location,
|
||||||
},
|
},
|
||||||
'type': 'gauge',
|
"type": "gauge",
|
||||||
'value': float(latest_data['cloud']),
|
"value": float(latest_data["cloud"]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'key': 'sensor_weather_outdoor_precip',
|
"key": "sensor_weather_outdoor_precip",
|
||||||
'labels': {
|
"labels": {
|
||||||
'location': location,
|
"location": location,
|
||||||
},
|
},
|
||||||
'type': 'gauge',
|
"type": "gauge",
|
||||||
'value': float(latest_data['precip_mm']),
|
"value": float(latest_data["precip_mm"]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'key': 'sensor_weather_outdoor_wind_speed',
|
"key": "sensor_weather_outdoor_wind_speed",
|
||||||
'labels': {
|
"labels": {
|
||||||
'location': location,
|
"location": location,
|
||||||
},
|
},
|
||||||
'type': 'gauge',
|
"type": "gauge",
|
||||||
'value': float(latest_data['wind_kph']),
|
"value": float(latest_data["wind_kph"]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'key': 'sensor_weather_outdoor_wind_gust',
|
"key": "sensor_weather_outdoor_wind_gust",
|
||||||
'labels': {
|
"labels": {
|
||||||
'location': location,
|
"location": location,
|
||||||
},
|
},
|
||||||
'type': 'gauge',
|
"type": "gauge",
|
||||||
'value': float(latest_data['gust_kph']),
|
"value": float(latest_data["gust_kph"]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'key': 'sensor_weather_outdoor_wind_direction',
|
"key": "sensor_weather_outdoor_wind_direction",
|
||||||
'labels': {
|
"labels": {
|
||||||
'location': location,
|
"location": location,
|
||||||
},
|
},
|
||||||
'type': 'gauge',
|
"type": "gauge",
|
||||||
'value': float(latest_data['wind_degree']),
|
"value": float(latest_data["wind_degree"]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'key': 'sensor_weather_outdoor_temp_feel_c',
|
"key": "sensor_weather_outdoor_temp_feel_c",
|
||||||
'labels': {
|
"labels": {
|
||||||
'location': location,
|
"location": location,
|
||||||
},
|
},
|
||||||
'type': 'gauge',
|
"type": "gauge",
|
||||||
'value': float(latest_data['feelslike_c']),
|
"value": float(latest_data["feelslike_c"]),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'key': 'sensor_weather_outdoor_pressure',
|
"key": "sensor_weather_outdoor_pressure",
|
||||||
'labels': {
|
"labels": {
|
||||||
'location': location,
|
"location": location,
|
||||||
},
|
},
|
||||||
'type': 'gauge',
|
"type": "gauge",
|
||||||
'value': float(latest_data['pressure_mb']),
|
"value": float(latest_data["pressure_mb"]),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -146,17 +141,19 @@ def metrics() -> Response:
|
||||||
ret_strs.append(f'# TYPE {item["key"]} {item["type"]}')
|
ret_strs.append(f'# TYPE {item["key"]} {item["type"]}')
|
||||||
ret_strs.append(
|
ret_strs.append(
|
||||||
str(item["key"])
|
str(item["key"])
|
||||||
+ '{'
|
+ "{"
|
||||||
+ " ".join([f'{key}="{val}"' for key, val in item["labels"].items()])
|
+ " ".join([f'{key}="{val}"' for key, val in item["labels"].items()])
|
||||||
+ '} '
|
+ "} "
|
||||||
+ str(item["value"])
|
+ str(
|
||||||
|
item["value"],
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
resp = Response('\n'.join(ret_strs))
|
resp = Response("\n".join(ret_strs))
|
||||||
resp.headers['Content-type'] = 'text/plain'
|
resp.headers["Content-type"] = "text/plain"
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run(host='0.0.0.0')
|
app.run(host="0.0.0.0")
|
||||||
|
|
Loading…
Reference in a new issue