fires: resolve FIRMS_MAP_KEY from Postgres keystore, not just env
All checks were successful
build-and-deploy / build (push) Successful in 1m40s

Key saved via the dashboard Keys page sat unused in api_keys while
ingest_fires only checked the env var. Now env first, keystore fallback,
picked up on next poll without a restart.
This commit is contained in:
Sirius DevOps 2026-08-24 21:21:15 -04:00
parent b1c611d4c4
commit e6648ec1ed
2 changed files with 72 additions and 6 deletions

View file

@ -25,12 +25,14 @@ import csv
import io
import json
import logging
import os
from datetime import datetime, timezone
import httpx
import nats
from config import FIRMS_MAP_KEY, FIRMS_DATASET, FIRMS_BBOX, FIRMS_TIMEOUT, NATS_URL
from config import FIRMS_DATASET, FIRMS_BBOX, FIRMS_TIMEOUT, NATS_URL
from keystore import get_api_key
logger = logging.getLogger("osint.firms")
@ -159,16 +161,21 @@ async def ingest_fires(bbox: str | None = None) -> int:
warning when FIRMS_MAP_KEY is not set, so the ingester keeps running for
the other sources.
"""
if not FIRMS_MAP_KEY:
# Key resolution: env var first (12-factor), then the shared Postgres
# api_keys table so a key saved via the dashboard Keys page is picked up
# on the next poll without a restart.
map_key = os.getenv("FIRMS_MAP_KEY", "") or await get_api_key("FIRMS_MAP_KEY")
if not map_key:
logger.warning(
"FIRMS_MAP_KEY not set — fire ingest disabled. "
"Get a free key at https://firms.modaps.eosdis.nasa.gov/api/map_key_info/"
"Set it in the dashboard Keys page or .env. "
"Free key: https://firms.modaps.eosdis.nasa.gov/api/map_key_info/"
)
return 0
area = bbox or FIRMS_BBOX
url = FIRMS_AREA_CSV.format(
key=FIRMS_MAP_KEY, dataset=FIRMS_DATASET, bbox=area
key=map_key, dataset=FIRMS_DATASET, bbox=area
)
async with httpx.AsyncClient(timeout=FIRMS_TIMEOUT) as client:

View file

@ -73,6 +73,65 @@ def test_parse_firms_csv_empty_and_garbage():
def test_ingest_fires_idles_without_map_key(monkeypatch):
# No key -> returns 0 without attempting a network call.
monkeypatch.setattr("fire_sources.FIRMS_MAP_KEY", "")
# No key anywhere -> returns 0 without attempting a network call.
monkeypatch.setenv("FIRMS_MAP_KEY", "")
monkeypatch.setattr(
"fire_sources.get_api_key", lambda name: _async_none()
)
assert asyncio.run(ingest_fires()) == 0
def test_ingest_fires_uses_keystore_key(monkeypatch):
# Key saved via the dashboard Keys page (Postgres) is picked up.
monkeypatch.setenv("FIRMS_MAP_KEY", "")
monkeypatch.setattr(
"fire_sources.get_api_key",
lambda name: _async_return("a" * 32),
)
captured = {}
class FakeResp:
status_code = 200
text = SAMPLE_CSV
def raise_for_status(self):
pass
class FakeClient:
def __init__(self, **kw):
pass
async def __aenter__(self):
return self
async def __aexit__(self, *exc):
return False
async def get(self, url):
captured["url"] = url
return FakeResp()
monkeypatch.setattr("fire_sources.httpx.AsyncClient", FakeClient)
published = []
async def fake_publish(points):
published.extend(points)
return len(points)
monkeypatch.setattr("fire_sources.publish_fire_batch", fake_publish)
assert asyncio.run(ingest_fires()) == 5
assert "a" * 32 in captured["url"]
def _async_return(value):
async def inner():
return value
return inner()
def _async_none():
return _async_return(None)