From 81f60399948878d0a3fd458190148f3e1c4fdab3 Mon Sep 17 00:00:00 2001 From: dvirlabs Date: Mon, 16 Feb 2026 18:50:03 +0200 Subject: [PATCH] Update app deployment of meteo --- README.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ app/service.py | 24 ++++++++++++------ 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2b749dd..e852044 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,73 @@ A FastAPI-based microservice that queries the Open-Meteo Geocoding API to retrie - Prometheus: http://localhost:9090 - Grafana: http://localhost:3000 (admin/admin) +## Helm (Kubernetes) + +The Helm chart in the `helm` folder deploys the FastAPI app and, by default, Prometheus and Grafana. + +### What it deploys +- **App**: FastAPI service with `/healthz` probes and Prometheus scrape annotations +- **Prometheus**: Scrapes the app metrics at `/metrics` +- **Grafana**: Pre-provisioned datasource and dashboard for the app metrics + +### Prerequisites +- Kubernetes cluster +- Helm v3 + +### Install +```bash +helm install open-meteo-service ./helm +``` + +### Upgrade +```bash +helm upgrade open-meteo-service ./helm +``` + +### Access the services +```bash +kubectl port-forward svc/open-meteo-service-open-meteo-service 8080:8000 +kubectl port-forward svc/open-meteo-service-open-meteo-service-prometheus 9090:9090 +kubectl port-forward svc/open-meteo-service-open-meteo-service-grafana 3000:3000 +``` + +### Values overview +You can customize the chart via `helm/values.yaml` or `--set` flags. + +**App values** +- `image.repository`, `image.tag`, `image.pullPolicy` +- `service.type`, `service.port` +- `env.cacheFile` +- `persistence.enabled`, `persistence.size`, `persistence.storageClassName` + +**Prometheus values** +- `prometheus.enabled` +- `prometheus.image` +- `prometheus.service.type`, `prometheus.service.port` +- `prometheus.persistence.enabled`, `prometheus.persistence.size`, `prometheus.persistence.storageClassName` + +**Grafana values** +- `grafana.enabled` +- `grafana.image` +- `grafana.service.type`, `grafana.service.port` +- `grafana.adminUser`, `grafana.adminPassword` +- `grafana.persistence.enabled`, `grafana.persistence.size`, `grafana.persistence.storageClassName` + +### Example: disable Grafana and Prometheus +```bash +helm install open-meteo-service ./helm \ + --set grafana.enabled=false \ + --set prometheus.enabled=false +``` + +### Example: enable persistence for all components +```bash +helm install open-meteo-service ./helm \ + --set persistence.enabled=true \ + --set prometheus.persistence.enabled=true \ + --set grafana.persistence.enabled=true +``` + ## API Documentation ### Endpoints diff --git a/app/service.py b/app/service.py index da86b8e..58b9d5d 100644 --- a/app/service.py +++ b/app/service.py @@ -6,7 +6,7 @@ import requests from .metrics import CACHE_HITS_TOTAL, CACHE_MISSES_TOTAL, OPENMETEO_CALLS_TOTAL API_URL = "https://geocoding-api.open-meteo.com/v1/search" -CITIES = ["Tel Aviv", "Beer Sheva", "Jerusalem", "Szeged"] +CITIES = ["Tel Aviv", "Beersheba", "Jerusalem", "Szeged"] CACHE_FILE = os.environ.get("CACHE_FILE", "coordinates_cache.json") @@ -45,18 +45,26 @@ def _save_cache(data: Dict[str, Any]) -> None: def get_all_coordinates() -> Dict[str, Any]: cached = _load_cache() - if cached: + + # Check if all cities from CITIES list are in cache + if cached and all(city in cached for city in CITIES): CACHE_HITS_TOTAL.inc() - return {"source": "cache", "data": cached} + # Filter cache to only include cities from CITIES list + filtered_data = {city: cached[city] for city in CITIES} + return {"source": "cache", "data": filtered_data} CACHE_MISSES_TOTAL.inc() - - results: Dict[str, Any] = {} + + # Fetch missing cities + results = cached if cached else {} for city in CITIES: - results[city] = _fetch_coordinates(city) - + if city not in results: + results[city] = _fetch_coordinates(city) + _save_cache(results) - return {"source": "open-meteo", "data": results} + # Return only cities from CITIES list + filtered_data = {city: results[city] for city in CITIES} + return {"source": "open-meteo", "data": filtered_data} def get_coordinates_for_city(city: str) -> Dict[str, Any]: