Skip to content

Commit

Permalink
plugins {list,status}: Add --id option, to inquire individual plugins
Browse files Browse the repository at this point in the history
Also, test status inquiry after plugin uninstall.
  • Loading branch information
amotl committed Sep 21, 2023
1 parent 5bbc1fb commit 3885ff5
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 37 deletions.
14 changes: 10 additions & 4 deletions grafana_wtf/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ def run():
grafana-wtf [options] find [<search-expression>]
grafana-wtf [options] replace <search-expression> <replacement> [--dry-run]
grafana-wtf [options] log [<dashboard_uid>] [--number=<count>] [--head=<count>] [--tail=<count>] [--reverse] [--sql=<sql>]
grafana-wtf [options] plugins list
grafana-wtf [options] plugins status
grafana-wtf [options] plugins list [--id=]
grafana-wtf [options] plugins status [--id=]
grafana-wtf --version
grafana-wtf (-h | --help)
Expand Down Expand Up @@ -324,9 +324,15 @@ def run():

if options.plugins:
if options.list:
response = engine.plugins_list()
if options.id:
response = engine.plugins_list_by_id(options.id)
else:
response = engine.plugins_list()
elif options.status:
response = engine.plugins_status()
if options.id:
response = engine.plugins_status_by_id(options.id)
else:
response = engine.plugins_status()
else:
raise DocoptExit('Subcommand "plugins" only provides "list" and "status"')
output_results(output_format, response)
63 changes: 37 additions & 26 deletions grafana_wtf/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,39 +493,50 @@ def explore_dashboards(self, with_data_details: bool = False):
return results

def plugins_list(self):
return self.grafana.plugin.get_installed_plugins()
return self.grafana.plugin.list()

def plugins_status(self):
status = []
plugins = self.grafana.plugin.get_installed_plugins()
plugins = self.grafana.plugin.list()
for plugin in plugins:
plugin = munchify(plugin)
item = Munch(
name=plugin.name,
type=plugin.type,
id=plugin.id,
enabled=plugin.enabled,
category=plugin.category,
version=plugin.info.version,
signature=plugin.get("signature"),
)

# Status inquiry is not provided by all plugins. Let's filter them.
# Effectively, run it only on non-internal "app" and "datasource" items.
if item.type != "panel" and item.signature != "internal":
try:
item.health = self.grafana.plugin.health_check_plugin(plugin.id)
except Exception as ex:
log.warning(f"Health check failed for plugin {item.id}, type={item.type}: {ex}")
try:
item.metrics = self.grafana.plugin.get_plugin_metrics(plugin.id)
except Exception as ex:
log.warning(f"Metrics inquiry failed for plugin {item.id}, type={item.type}: {ex}")
else:
log.info(f"Skipping status inquiry for plugin {item.id}, type={item.type}")
item = self.get_plugin_status(plugin)
status.append(item)
return status

def get_plugin_status(self, plugin):
plugin = munchify(plugin)
item = Munch(
name=plugin.name,
type=plugin.type,
id=plugin.id,
enabled=plugin.enabled,
category=plugin.category,
version=plugin.info.version,
signature=plugin.get("signature"),
)

# Status inquiry is not provided by all plugins. Let's filter them.
# Effectively, run it only on non-internal "app" and "datasource" items.
if item.type != "panel" and item.signature != "internal":
try:
item.health = self.grafana.plugin.health(plugin.id)
except Exception as ex:
log.warning(f"Health check failed for plugin {item.id}, type={item.type}: {ex}")
try:
item.metrics = self.grafana.plugin.metrics(plugin.id)
except Exception as ex:
log.warning(f"Metrics inquiry failed for plugin {item.id}, type={item.type}: {ex}")
else:
log.info(f"Skipping status inquiry for plugin {item.id}, type={item.type}")
return item

def plugins_list_by_id(self, plugin_id):
return self.grafana.plugin.by_id(plugin_id=plugin_id)

def plugins_status_by_id(self, plugin_id):
plugin = self.plugins_list_by_id(plugin_id)
return self.get_plugin_status(plugin)


class Indexer:
def __init__(self, engine: GrafanaWtf):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
f"duckdb<0.9; {no_linux_on_arm}",
# Grafana
"requests>=2.26,<3",
"grafana-client>=3.8.2,<4",
"grafana-client>=3.9.1,<4",
"jsonpath-rw>=1.4.0,<2",
# Caching
"requests-cache>=0.8.0,<2",
Expand Down
48 changes: 42 additions & 6 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import warnings

import grafana_client
from grafana_client.elements.plugin import filter_plugin_by_id
from grafana_client.elements.plugin import get_plugin_by_id
from munch import munchify
from packaging import version

Expand Down Expand Up @@ -576,7 +576,7 @@ def test_plugins_list(docker_grafana, capsys, caplog):
assert len(data) >= 28

# Proof the output is correct.
plugin = munchify(filter_plugin_by_id(plugin_list=data, plugin_id="alertlist"))
plugin = munchify(get_plugin_by_id(plugin_list=data, plugin_id="alertlist"))
assert plugin.name.title() == "Alert List"
assert plugin.type == "panel"
assert plugin.id == "alertlist"
Expand All @@ -598,7 +598,7 @@ def test_plugins_status_datasource(grafana_version, docker_grafana, capsys, capl

# Before conducting a plugin status test, install a non-internal one.
grafana = grafana_client.GrafanaApi.from_url(url=docker_grafana, timeout=15)
grafana.plugin.install_plugin("yesoreyeram-infinity-datasource")
grafana.plugin.install("yesoreyeram-infinity-datasource")

# Which subcommand to test?
set_command("plugins status", "--format=yaml")
Expand All @@ -613,7 +613,7 @@ def test_plugins_status_datasource(grafana_version, docker_grafana, capsys, capl
assert len(data) >= 28

# Proof the output is correct.
plugin = munchify(filter_plugin_by_id(plugin_list=data, plugin_id="yesoreyeram-infinity-datasource"))
plugin = munchify(get_plugin_by_id(plugin_list=data, plugin_id="yesoreyeram-infinity-datasource"))
assert "go_gc_duration_seconds" in plugin.metrics


Expand All @@ -626,7 +626,7 @@ def test_plugins_status_app(grafana_version, docker_grafana, capsys, caplog):

# Before conducting a plugin status test, install a non-internal one.
grafana = grafana_client.GrafanaApi.from_url(url=docker_grafana, timeout=15)
grafana.plugin.install_plugin("aws-datasource-provisioner-app")
grafana.plugin.install("aws-datasource-provisioner-app")

# Which subcommand to test?
set_command("plugins status", "--format=yaml")
Expand All @@ -641,6 +641,42 @@ def test_plugins_status_app(grafana_version, docker_grafana, capsys, caplog):
assert len(data) >= 28

# Proof the output is correct.
plugin = munchify(filter_plugin_by_id(plugin_list=data, plugin_id="aws-datasource-provisioner-app"))
plugin = munchify(get_plugin_by_id(plugin_list=data, plugin_id="aws-datasource-provisioner-app"))
assert "process_virtual_memory_max_bytes" in plugin.metrics
assert plugin.health == {"message": "", "status": "OK"}


def test_plugins_install_uninstall(grafana_version, docker_grafana, capsys, caplog):
"""
Verify the plugin status when installing/uninstalling a plugin.
"""
if version.parse(grafana_version) < version.parse("8"):
raise pytest.skip(f"Plugin status inquiry only works on Grafana 8 and newer")

plugin_name = "yesoreyeram-infinity-datasource"

# Before conducting a plugin status test, install a non-internal one.
grafana = grafana_client.GrafanaApi.from_url(url=docker_grafana, timeout=15)
grafana.plugin.install(plugin_name)

# Which subcommand to test?
set_command(f"plugins status --id={plugin_name}", "--format=yaml")

# Run command and capture YAML output.
with caplog.at_level(logging.DEBUG):
grafana_wtf.commands.run()
captured = capsys.readouterr()
plugin_status = munchify(yaml.safe_load(captured.out))

# Proof the output is correct.
assert plugin_status.id == plugin_name
assert version.parse(plugin_status.version) >= version.parse("2.0.0")
assert "go_gc_duration_seconds" in plugin_status.metrics

# Uninstall the plugin again.
grafana.plugin.uninstall(plugin_name)

# Verify uninstalling worked.
with pytest.raises(KeyError) as ex:
grafana_wtf.commands.run()
assert ex.match("Plugin not found: yesoreyeram-infinity-datasource")

0 comments on commit 3885ff5

Please sign in to comment.