Skip to content

Commit 33b3a96

Browse files
committed
Improve error reporting on invalid Grafana URLs, or invalid endpoints
- Rework "health" method (now a property) to raise exception on errors - Report Grafana version on startup - Improve error messages - Add software tests evaluating connectivity error handling
1 parent 6dc0015 commit 33b3a96

File tree

5 files changed

+66
-17
lines changed

5 files changed

+66
-17
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ in progress
1414
- Removed support for Python 3.7
1515
- SQLite cache: Use ``requests_cache.CachedSession`` for better concurrency
1616
behaviour. Thanks, @JensRichnow and @JWCook.
17+
- Improve error reporting and exit behavior when connecting to Grafana
18+
instance fails. Thanks, @interfan7.
1719

1820
2024-03-07 0.18.0
1921
=================

grafana_wtf/commands.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,16 @@ def run():
228228
'No Grafana URL given. Please use "--grafana-url" option or environment variable "GRAFANA_URL".'
229229
)
230230

231-
log.info(f"Using Grafana at {grafana_url}")
231+
log.info(f"Grafana location: {grafana_url}")
232232

233233
engine = GrafanaWtf(grafana_url, grafana_token)
234+
234235
engine.enable_cache(expire_after=cache_ttl, drop_cache=options["drop-cache"])
235236
engine.enable_concurrency(int(options["concurrency"]))
236237
engine.setup()
237238

239+
log.info(f"Grafana version: {engine.version}")
240+
238241
if options.replace:
239242
engine.clear_cache()
240243

grafana_wtf/core.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
class GrafanaEngine:
3838
session = niquests.Session()
3939

40-
def __init__(self, grafana_url, grafana_token):
40+
def __init__(self, grafana_url, grafana_token=None):
4141
self.grafana_url = grafana_url
4242
self.grafana_token = grafana_token
4343

@@ -263,15 +263,10 @@ async def execute_parallel(self):
263263

264264
class GrafanaWtf(GrafanaEngine):
265265
def info(self):
266-
try:
267-
health = self.grafana.client.GET("/health")
268-
except Exception as ex:
269-
log.error(f"Request to /health endpoint failed: {ex}")
270-
health = {}
271266

272267
response = OrderedDict(
273268
grafana=OrderedDict(
274-
version=health.get("version"),
269+
version=self.version,
275270
url=self.grafana_url,
276271
),
277272
statistics=OrderedDict(),
@@ -315,15 +310,29 @@ def info(self):
315310

316311
return response
317312

318-
def version(self):
313+
@property
314+
def health(self):
315+
response = None
316+
error = None
317+
error_template = f"The request to {self.grafana_url.rstrip('/')}/api/health failed"
319318
try:
320-
health = self.grafana.client.GET("/health")
319+
response = self.grafana.client.GET("/health")
320+
if not isinstance(response, dict):
321+
error = f"{error_template}: Invalid response, content was: {response}"
322+
321323
except Exception as ex:
322-
log.error(f"Request to /health endpoint failed: {ex}")
323-
health = {}
324+
error = f"{error_template}: {ex}"
324325

325-
version = health.get("version")
326-
return version
326+
if error:
327+
log.critical(error)
328+
raise ConnectionError(error)
329+
330+
if response:
331+
return Munch(response)
332+
333+
@property
334+
def version(self):
335+
return self.health.get("version")
327336

328337
def dashboard_details(self):
329338
for dashboard in self.data.dashboards:

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ def grafana_version(docker_grafana):
321321
"""
322322
engine = GrafanaWtf(grafana_url=docker_grafana, grafana_token=None)
323323
engine.setup()
324-
grafana_version = engine.version()
324+
grafana_version = engine.version
325325
return grafana_version
326326

327327

tests/test_core.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
from unittest.mock import Mock
1+
from unittest.mock import Mock, patch
22

3+
import pytest
34
from munch import Munch
45

5-
from grafana_wtf.core import Indexer
6+
from grafana_wtf.core import Indexer, GrafanaWtf
67

78

89
def test_collect_datasource_items_variable_all():
@@ -35,3 +36,37 @@ def test_collect_datasource_items_variable_all():
3536
indexer = Indexer(engine=engine_mock)
3637
result = indexer.collect_datasource_items([node])
3738
assert result == []
39+
40+
41+
def test_connect_success():
42+
wtf = GrafanaWtf("https://play.grafana.org")
43+
wtf.setup()
44+
health = wtf.health
45+
assert "commit" in health
46+
assert "version" in health
47+
assert health.database == "ok"
48+
49+
50+
def test_connect_failure():
51+
wtf = GrafanaWtf("http://localhost:1234")
52+
wtf.setup()
53+
with pytest.raises(ConnectionError) as ex:
54+
_ = wtf.health
55+
assert ex.match("The request to http://localhost:1234/api/health failed")
56+
57+
58+
@patch("grafana_client.client.GrafanaClient.__getattr__")
59+
def test_connect_version(mock_get):
60+
mock_get.return_value = Mock()
61+
mock_get.return_value.return_value = {"commit": "14e988bd22", "database": "ok", "version": "9.0.1"}
62+
wtf = GrafanaWtf("http://localhost:1234")
63+
wtf.setup()
64+
assert wtf.version == "9.0.1"
65+
66+
67+
def test_connect_non_json_response():
68+
wtf = GrafanaWtf("https://example.org/")
69+
wtf.setup()
70+
with pytest.raises(ConnectionError) as ex:
71+
_ = wtf.health
72+
assert ex.match("The request to https://example.org/api/health failed: Client Error 404")

0 commit comments

Comments
 (0)