fix(prometheus): a refused token stops the scraping, as Home Assistant bans an address after a few refusals (#6355)
CI / lint (push) Successful in 2s
CI / unit (push) Successful in 1m36s
CI / integration (push) Successful in 1m55s
CI / publish (push) Successful in 20s

Also names the Home Assistant connection by its address in the audit log and
timeline, where its id showed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 09:41:53 -05:00
co-authored by Claude Opus 5.5
parent a1dfa7b821
commit 66a5dd12fd
11 changed files with 94 additions and 11 deletions
+12 -2
View File
@@ -7,7 +7,7 @@ import pytest
from plugins.prometheus.models import PrometheusTarget
from plugins.prometheus.scheduler import is_due
from plugins.prometheus.scraper import ScrapeError, scrape
from plugins.prometheus.scraper import ScrapeError, TokenRefused, scrape
from plugins.prometheus.service import (FormError, fold_states, label_pairs, map_families, parse_form,
patterns, rate_name, resource_for, target_state, wanted)
from steward.core import prometheus
@@ -165,6 +165,16 @@ def test_state_and_due():
assert target_state(t, now).reach == "paused" and not is_due(t, now)
def test_a_refused_token_stops_the_scraping_and_the_page_says_so():
now = datetime(2026, 10, 12, 12, 0, tzinfo=timezone.utc)
t = PrometheusTarget(name="a", url="http://a", enabled=True, interval_seconds=60, refused=True,
last_error="Answered 401: the token was refused.",
last_scraped_at=now - timedelta(hours=5))
state = target_state(t, now)
assert state.reach == "down" and "stopped asking" in state.says and "Answered 401" in state.says
assert not is_due(t, now)
@pytest.mark.asyncio
async def test_a_scrape_sends_the_token_and_says_why_it_failed():
seen = {}
@@ -180,7 +190,7 @@ async def test_a_scrape_sends_the_token_and_says_why_it_failed():
transport = httpx.MockTransport(handler)
fams = await scrape("http://svc/metrics", "tok", transport=transport)
assert list(fams) == ["up_thing"] and seen["auth"] == "Bearer tok"
with pytest.raises(ScrapeError, match="token was refused"):
with pytest.raises(TokenRefused, match="token was refused"):
await scrape("http://svc/denied", "bad", transport=transport)
with pytest.raises(ScrapeError, match="no metrics"):
await scrape("http://svc/html", transport=transport)