From a6f8b5fc5466cc3711bba543c9ca19d0ff7d8250 Mon Sep 17 00:00:00 2001 From: Guillaume Demonet Date: Wed, 4 Aug 2021 23:07:48 +0200 Subject: [PATCH 1/3] charts, salt: List and check dashboard UIDs The dashboards we provision with MetalK8s have (or should have, which is why some of this commit's changes are setting some UIDs) a UID. Starting with Grafana v8.0, linking dashboards using their slug is not available anymore (see https://grafana.com/docs/grafana/latest/release-notes/release-notes-8-0-0/#breaking-changes ). To make sure we can use these UIDs safely in links (mostly from MetalK8s UI), we start by listing all of them in a JSON file, and enforce that they are correct when rendering Helm charts. Verifying the final deployed dashboards will be done in a subsequent commit, through a post-install E2E test. See: #3475 --- charts/grafana_dashboard_uids.json | 33 + charts/render.py | 41 +- .../prometheus-operator/deployed/chart.sls | 658 ++++++------------ .../deployed/files/node-exporter-full.json | 1 + 4 files changed, 291 insertions(+), 442 deletions(-) create mode 100644 charts/grafana_dashboard_uids.json diff --git a/charts/grafana_dashboard_uids.json b/charts/grafana_dashboard_uids.json new file mode 100644 index 0000000000..37d7a68a3c --- /dev/null +++ b/charts/grafana_dashboard_uids.json @@ -0,0 +1,33 @@ +{ + "Alertmanager / Overview": "alertmanager-overview", + "Kubernetes / API server": "09ec8aa1e996d6ffcd6817bbaff4db1b", + "Kubernetes / Networking / Cluster": "ff635a025bcfea7bc3dd4f508990a3e9", + "Kubernetes / Controller Manager": "72e0e05bef5099e5f049b05fdc429ed4", + "etcd": "c2f4e12cdf69feb95caa41a5a1b423d9", + "CoreDNS": "vkQ0UHxik", + "Kubernetes / Compute Resources / Cluster": "efa86fd1d0c121a26444b636a3f509a8", + "Kubernetes / Compute Resources / Namespace (Pods)": "85a562078cdf77779eaa1add43ccec1e", + "Kubernetes / Compute Resources / Node (Pods)": "200ac8fdbfbb74b39aff88118e4d1c2c", + "Kubernetes / Compute Resources / Pod": "6581e46e4e5c7ba40a07646395ef7b23", + "Kubernetes / Compute Resources / Workload": "a164a7f0339f99e89cea5cb47e9be617", + "Kubernetes / Compute Resources / Namespace (Workloads)": "a87fb0d919ec0ea5f6543124e16c42a5", + "Kubernetes / Kubelet": "3138fa155d5915769fbded898ac09fd9", + "Kubernetes / Networking / Namespace (Pods)": "8b7a8b326d7a6f1f04244066368c67af", + "Kubernetes / Networking / Namespace (Workload)": "bbb2a765a623ae38130206c7d94a160f", + "USE Method / Cluster": "3e97d1d02672cdd0861f4c97c64f89b2", + "USE Method / Node": "fac67cfbe174d3ef53eb473d73d9212f", + "Nodes": "fa49a4706d07a042595b664c87fb33ea", + "Nodes (Detailed)": "node-exporter-full", + "Kubernetes / Persistent Volumes": "919b92a8e8041bd567af9edab12c840c", + "Kubernetes / Networking / Pod": "7a18067ce943a40ae25454675c19ff5c", + "Prometheus / Overview": "prometheus-overview", + "Kubernetes / Proxy": "632e265de029684c40b21cb76bca4f94", + "Kubernetes / Scheduler": "2e6b6a3b4bddf1427b3a55aa1311c656", + "Kubernetes / StatefulSets": "a31c1f46e6f727cb37c0d731a7245005", + "Kubernetes / Networking / Workload": "728bf77cc1166d2f3133bf25846876cc", + "Loki": "g6fe30815b172c9da7e813c15ddfe607", + "Logs": "a7e130cb82be229d6f3edbfd0a438001", + "Fluent Bit": "fluentbit", + "NGINX Ingress controller": "nginx", + "Request Handling Performance": "4GFbkOsZk" +} diff --git a/charts/render.py b/charts/render.py index 38d15c4f98..6f89a9b201 100755 --- a/charts/render.py +++ b/charts/render.py @@ -29,6 +29,8 @@ import argparse import copy import io +import json +import pathlib import re import sys import subprocess @@ -210,6 +212,29 @@ def remove_prometheus_rules(template, drop_rules): return updated_template +DASHBOARD_UIDS_FILE = pathlib.Path(__file__).parent / "grafana_dashboard_uids.json" +DASHBOARD_UIDS = json.loads(DASHBOARD_UIDS_FILE.read_text()) + + +def patch_grafana_dashboards(manifest): + for fname in manifest["data"]: + dashboard = json.loads(manifest["data"][fname]) + title = dashboard.get("title") + assert title in DASHBOARD_UIDS, f"Found unknown Grafana dashboard: '{title}'" + found_uid = dashboard.get("uid") + expected_uid = DASHBOARD_UIDS[title] + if found_uid: + assert found_uid == expected_uid, ( + f"UID mismatch for Grafana dashboard '{title}': " + f"found '{found_uid}', expected '{expected_uid}'" + ) + else: + dashboard["uid"] = expected_uid + manifest["data"][fname] = json.dumps(dashboard, indent=4, sort_keys=True) + + return manifest + + def main(): parser = argparse.ArgumentParser() parser.add_argument("name", help="Denotes the name of the chart") @@ -307,12 +332,16 @@ def __call__(self, parser, args, values, option_string=None): drop_prometheus_rules = yaml.safe_load(fd) def fixup(doc): - if ( - drop_prometheus_rules - and isinstance(doc, dict) - and doc.get("kind") == "PrometheusRule" - ): - doc = remove_prometheus_rules(doc, drop_prometheus_rules) + if isinstance(doc, dict): + kind = doc.get("kind") + if drop_prometheus_rules and kind == "PrometheusRule": + doc = remove_prometheus_rules(doc, drop_prometheus_rules) + if ( + kind == "ConfigMap" + and doc.get("metadata", {}).get("labels", {}).get("grafana_dashboard") + == "1" + ): + doc = patch_grafana_dashboards(doc) return ( fixup_metadata(namespace=args.namespace, doc=fixup_doc(doc=doc)) diff --git a/salt/metalk8s/addons/prometheus-operator/deployed/chart.sls b/salt/metalk8s/addons/prometheus-operator/deployed/chart.sls index eefbc52974..a35e3e268c 100644 --- a/salt/metalk8s/addons/prometheus-operator/deployed/chart.sls +++ b/salt/metalk8s/addons/prometheus-operator/deployed/chart.sls @@ -27108,6 +27108,7 @@ data: }, "timezone": "browser", "title": "etcd", + "uid": "c2f4e12cdf69feb95caa41a5a1b423d9", "version": 215 } kind: ConfigMap @@ -46353,15 +46354,13 @@ data: node-cluster-rsrc-use.json: |- { "annotations": { - "list": [ - ] + "list": [] }, "editable": true, "gnetId": null, "graphTooltip": 0, "hideControls": false, - "links": [ - ], + "links": [], "refresh": "10s", "rows": [ { @@ -46369,8 +46368,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -46388,15 +46386,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -46411,8 +46407,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "CPU Utilisation", @@ -46427,8 +46422,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -46450,8 +46444,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -46469,15 +46462,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -46492,8 +46483,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "CPU Saturation (load1 per CPU)", @@ -46508,8 +46498,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -46543,8 +46532,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -46562,15 +46550,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -46585,8 +46571,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Memory Utilisation", @@ -46601,8 +46586,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -46624,8 +46608,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -46643,15 +46626,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -46666,8 +46647,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Memory Saturation (Major Page Faults)", @@ -46682,8 +46662,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -46717,8 +46696,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -46736,8 +46714,7 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, @@ -46776,8 +46753,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Net Utilisation (Bytes Receive/Transmit)", @@ -46792,8 +46768,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -46815,8 +46790,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -46834,8 +46808,7 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, @@ -46874,8 +46847,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Net Saturation (Drops Receive/Transmit)", @@ -46890,8 +46862,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -46925,8 +46896,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -46944,15 +46914,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -46967,8 +46935,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Disk IO Utilisation", @@ -46983,8 +46950,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -47006,8 +46972,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -47025,15 +46990,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -47048,8 +47011,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Disk IO Saturation", @@ -47064,8 +47026,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -47099,8 +47060,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -47118,15 +47078,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 12, "stack": true, @@ -47141,8 +47099,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Disk Space Utilisation", @@ -47157,8 +47114,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -47190,8 +47146,7 @@ data: ], "schemaVersion": 14, "style": "dark", - "tags": [ - ], + "tags": [], "templating": { "list": [ { @@ -47202,8 +47157,7 @@ data: "hide": 0, "label": null, "name": "datasource", - "options": [ - ], + "options": [], "query": "prometheus", "refresh": 1, "regex": "", @@ -47242,7 +47196,7 @@ data: }, "timezone": "utc", "title": "USE Method / Cluster", - "uid": "", + "uid": "3e97d1d02672cdd0861f4c97c64f89b2", "version": 0 } kind: ConfigMap @@ -47268,15 +47222,13 @@ data: node-rsrc-use.json: |- { "annotations": { - "list": [ - ] + "list": [] }, "editable": true, "gnetId": null, "graphTooltip": 0, "hideControls": false, - "links": [ - ], + "links": [], "refresh": "10s", "rows": [ { @@ -47284,8 +47236,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -47303,15 +47254,13 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": false, @@ -47326,8 +47275,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "CPU Utilisation", @@ -47342,8 +47290,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -47365,8 +47312,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -47384,15 +47330,13 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": false, @@ -47407,8 +47351,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "CPU Saturation (Load1 per CPU)", @@ -47423,8 +47366,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -47458,8 +47400,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -47477,15 +47418,13 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": false, @@ -47500,8 +47439,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Memory Utilisation", @@ -47516,8 +47454,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -47539,8 +47476,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -47558,15 +47494,13 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": false, @@ -47581,8 +47515,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Memory Saturation (Major Page Faults)", @@ -47597,8 +47530,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -47632,8 +47564,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -47651,8 +47582,7 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, @@ -47691,8 +47621,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Net Utilisation (Bytes Receive/Transmit)", @@ -47707,8 +47636,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -47730,8 +47658,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -47749,8 +47676,7 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, @@ -47789,8 +47715,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Net Saturation (Drops Receive/Transmit)", @@ -47805,8 +47730,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -47840,8 +47764,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -47859,15 +47782,13 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": false, @@ -47882,8 +47803,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Disk IO Utilisation", @@ -47898,8 +47818,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -47921,8 +47840,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -47940,15 +47858,13 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": false, @@ -47963,8 +47879,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Disk IO Saturation", @@ -47979,8 +47894,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -48014,8 +47928,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -48033,15 +47946,13 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 12, "stack": false, @@ -48056,8 +47967,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Disk Space Utilisation", @@ -48072,8 +47982,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -48105,8 +48014,7 @@ data: ], "schemaVersion": 14, "style": "dark", - "tags": [ - ], + "tags": [], "templating": { "list": [ { @@ -48117,8 +48025,7 @@ data: "hide": 0, "label": null, "name": "datasource", - "options": [ - ], + "options": [], "query": "prometheus", "refresh": 1, "regex": "", @@ -48136,15 +48043,13 @@ data: "label": "instance", "multi": false, "name": "instance", - "options": [ - ], + "options": [], "query": "label_values(up{job=\"node-exporter\"}, instance)", "refresh": 1, "regex": "", "sort": 2, "tagValuesQuery": "", - "tags": [ - ], + "tags": [], "tagsQuery": "", "type": "query", "useTags": false @@ -48182,7 +48087,7 @@ data: }, "timezone": "utc", "title": "USE Method / Node", - "uid": "", + "uid": "fac67cfbe174d3ef53eb473d73d9212f", "version": 0 } kind: ConfigMap @@ -48207,21 +48112,17 @@ apiVersion: v1 data: nodes.json: |- { - "__inputs": [ - ], - "__requires": [ - ], + "__inputs": [], + "__requires": [], "annotations": { - "list": [ - ] + "list": [] }, "editable": false, "gnetId": null, "graphTooltip": 0, "hideControls": false, "id": null, - "links": [ - ], + "links": [], "refresh": "", "rows": [ { @@ -48229,16 +48130,14 @@ data: "collapsed": false, "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "$datasource", "fill": 1, "fillGradient": 0, - "gridPos": { - }, + "gridPos": {}, "id": 2, "legend": { "alignAsTable": false, @@ -48254,16 +48153,14 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "repeat": null, - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -48277,8 +48174,7 @@ data: "refId": "A" } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "CPU Usage", @@ -48293,8 +48189,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -48316,16 +48211,14 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "$datasource", "fill": 0, "fillGradient": 0, - "gridPos": { - }, + "gridPos": {}, "id": 3, "legend": { "alignAsTable": false, @@ -48341,16 +48234,14 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "repeat": null, - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": false, @@ -48385,8 +48276,7 @@ data: "refId": "D" } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Load Average", @@ -48401,8 +48291,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -48437,16 +48326,14 @@ data: "collapsed": false, "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "$datasource", "fill": 1, "fillGradient": 0, - "gridPos": { - }, + "gridPos": {}, "id": 4, "legend": { "alignAsTable": false, @@ -48462,16 +48349,14 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "repeat": null, - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 9, "stack": true, @@ -48506,8 +48391,7 @@ data: "refId": "D" } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Memory Usage", @@ -48522,8 +48406,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -48562,12 +48445,10 @@ data: "thresholdLabels": false, "thresholdMarkers": true }, - "gridPos": { - }, + "gridPos": {}, "id": 5, "interval": null, - "links": [ - ], + "links": [], "mappingType": 1, "mappingTypes": [ { @@ -48637,16 +48518,14 @@ data: "collapsed": false, "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "$datasource", "fill": 0, "fillGradient": 0, - "gridPos": { - }, + "gridPos": {}, "id": 6, "legend": { "alignAsTable": false, @@ -48662,8 +48541,7 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, @@ -48707,8 +48585,7 @@ data: "refId": "C" } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Disk I/O", @@ -48723,8 +48600,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -48745,17 +48621,15 @@ data: } ] }, - { - "aliasColors": { - }, + { + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "$datasource", "fill": 1, "fillGradient": 0, - "gridPos": { - }, + "gridPos": {}, "id": 7, "legend": { "alignAsTable": false, @@ -48771,8 +48645,7 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, @@ -48809,8 +48682,7 @@ data: "refId": "B" } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Disk Space Usage", @@ -48825,8 +48697,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -48861,16 +48732,14 @@ data: "collapsed": false, "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "$datasource", "fill": 0, "fillGradient": 0, - "gridPos": { - }, + "gridPos": {}, "id": 8, "legend": { "alignAsTable": false, @@ -48886,16 +48755,14 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "repeat": null, - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": false, @@ -48909,8 +48776,7 @@ data: "refId": "A" } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Network Received", @@ -48925,8 +48791,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -48948,16 +48813,14 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "$datasource", "fill": 0, "fillGradient": 0, - "gridPos": { - }, + "gridPos": {}, "id": 9, "legend": { "alignAsTable": false, @@ -48973,16 +48836,14 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "repeat": null, - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": false, @@ -48996,8 +48857,7 @@ data: "refId": "A" } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Network Transmitted", @@ -49012,8 +48872,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -49046,8 +48905,7 @@ data: ], "schemaVersion": 14, "style": "dark", - "tags": [ - ], + "tags": [], "templating": { "list": [ { @@ -49058,8 +48916,7 @@ data: "hide": 0, "label": null, "name": "datasource", - "options": [ - ], + "options": [], "query": "prometheus", "refresh": 1, "regex": "", @@ -49067,23 +48924,20 @@ data: }, { "allValue": null, - "current": { - }, + "current": {}, "datasource": "$datasource", "hide": 0, "includeAll": false, "label": null, "multi": false, "name": "instance", - "options": [ - ], + "options": [], "query": "label_values(node_exporter_build_info{job=\"node-exporter\"}, instance)", "refresh": 2, "regex": "", "sort": 0, "tagValuesQuery": "", - "tags": [ - ], + "tags": [], "tagsQuery": "", "type": "query", "useTags": false @@ -49121,6 +48975,7 @@ data: }, "timezone": "browser", "title": "Nodes", + "uid": "fa49a4706d07a042595b664c87fb33ea", "version": 0 } kind: ConfigMap @@ -50865,15 +50720,13 @@ data: prometheus.json: |- { "annotations": { - "list": [ - ] + "list": [] }, "editable": true, "gnetId": null, "graphTooltip": 0, "hideControls": false, - "links": [ - ], + "links": [], "refresh": "60s", "rows": [ { @@ -50881,8 +50734,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -50900,15 +50752,13 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 12, "stack": false, @@ -50923,8 +50773,7 @@ data: { "alias": "Count", "colorMode": null, - "colors": [ - ], + "colors": [], "dateFormat": "YYYY-MM-DD HH:mm:ss", "decimals": 2, "link": false, @@ -50932,16 +50781,14 @@ data: "linkTooltip": "Drill down", "linkUrl": "", "pattern": "Value #A", - "thresholds": [ - ], + "thresholds": [], "type": "hidden", "unit": "short" }, { "alias": "Uptime", "colorMode": null, - "colors": [ - ], + "colors": [], "dateFormat": "YYYY-MM-DD HH:mm:ss", "decimals": 2, "link": false, @@ -50949,16 +50796,14 @@ data: "linkTooltip": "Drill down", "linkUrl": "", "pattern": "Value #B", - "thresholds": [ - ], + "thresholds": [], "type": "number", "unit": "short" }, { "alias": "Instance", "colorMode": null, - "colors": [ - ], + "colors": [], "dateFormat": "YYYY-MM-DD HH:mm:ss", "decimals": 2, "link": false, @@ -50966,16 +50811,14 @@ data: "linkTooltip": "Drill down", "linkUrl": "", "pattern": "instance", - "thresholds": [ - ], + "thresholds": [], "type": "number", "unit": "short" }, { "alias": "Job", "colorMode": null, - "colors": [ - ], + "colors": [], "dateFormat": "YYYY-MM-DD HH:mm:ss", "decimals": 2, "link": false, @@ -50983,16 +50826,14 @@ data: "linkTooltip": "Drill down", "linkUrl": "", "pattern": "job", - "thresholds": [ - ], + "thresholds": [], "type": "number", "unit": "short" }, { "alias": "Version", "colorMode": null, - "colors": [ - ], + "colors": [], "dateFormat": "YYYY-MM-DD HH:mm:ss", "decimals": 2, "link": false, @@ -51000,21 +50841,18 @@ data: "linkTooltip": "Drill down", "linkUrl": "", "pattern": "version", - "thresholds": [ - ], + "thresholds": [], "type": "number", "unit": "short" }, { "alias": "", "colorMode": null, - "colors": [ - ], + "colors": [], "dateFormat": "YYYY-MM-DD HH:mm:ss", "decimals": 2, "pattern": "/.*/", - "thresholds": [ - ], + "thresholds": [], "type": "string", "unit": "short" } @@ -51039,8 +50877,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Prometheus Stats", @@ -51056,8 +50893,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -51091,8 +50927,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -51110,15 +50945,13 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": false, @@ -51133,8 +50966,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Target Sync", @@ -51149,8 +50981,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -51172,8 +51003,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -51191,15 +51021,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -51214,8 +51042,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Targets", @@ -51230,8 +51057,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -51265,8 +51091,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -51284,15 +51109,13 @@ data: }, "lines": true, "linewidth": 1, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 4, "stack": false, @@ -51307,8 +51130,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Average Scrape Interval Duration", @@ -51323,8 +51145,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -51346,8 +51167,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -51365,15 +51185,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 4, "stack": true, @@ -51412,8 +51230,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Scrape failures", @@ -51428,8 +51245,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -51451,8 +51267,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -51470,15 +51285,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 4, "stack": true, @@ -51493,8 +51306,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Appended Samples", @@ -51509,8 +51321,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -51544,8 +51355,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -51563,15 +51373,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -51586,8 +51394,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Head Series", @@ -51602,8 +51409,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -51625,8 +51431,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -51644,15 +51449,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -51667,8 +51470,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Head Chunks", @@ -51683,8 +51485,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -51718,8 +51519,7 @@ data: "height": "250px", "panels": [ { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -51737,15 +51537,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -51760,8 +51558,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Query Rate", @@ -51776,8 +51573,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -51799,8 +51595,7 @@ data: ] }, { - "aliasColors": { - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -51818,15 +51613,13 @@ data: }, "lines": true, "linewidth": 0, - "links": [ - ], + "links": [], "nullPointMode": "null as zero", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - ], + "seriesOverrides": [], "spaceLength": 10, "span": 6, "stack": true, @@ -51841,8 +51634,7 @@ data: "step": 10 } ], - "thresholds": [ - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Stage Duration", @@ -51857,8 +51649,7 @@ data: "mode": "time", "name": null, "show": true, - "values": [ - ] + "values": [] }, "yaxes": [ { @@ -51903,8 +51694,7 @@ data: "hide": 0, "label": null, "name": "datasource", - "options": [ - ], + "options": [], "query": "prometheus", "refresh": 1, "regex": "", @@ -51923,15 +51713,13 @@ data: "label": "job", "multi": true, "name": "job", - "options": [ - ], + "options": [], "query": "label_values(prometheus_build_info, job)", "refresh": 1, "regex": "", "sort": 2, "tagValuesQuery": "", - "tags": [ - ], + "tags": [], "tagsQuery": "", "type": "query", "useTags": false @@ -51949,15 +51737,13 @@ data: "label": "instance", "multi": true, "name": "instance", - "options": [ - ], + "options": [], "query": "label_values(prometheus_build_info, instance)", "refresh": 1, "regex": "", "sort": 2, "tagValuesQuery": "", - "tags": [ - ], + "tags": [], "tagsQuery": "", "type": "query", "useTags": false @@ -51995,7 +51781,7 @@ data: }, "timezone": "utc", "title": "Prometheus / Overview", - "uid": "", + "uid": "prometheus-overview", "version": 0 } kind: ConfigMap diff --git a/salt/metalk8s/addons/prometheus-operator/deployed/files/node-exporter-full.json b/salt/metalk8s/addons/prometheus-operator/deployed/files/node-exporter-full.json index b680ce9501..defd8fe994 100644 --- a/salt/metalk8s/addons/prometheus-operator/deployed/files/node-exporter-full.json +++ b/salt/metalk8s/addons/prometheus-operator/deployed/files/node-exporter-full.json @@ -20029,5 +20029,6 @@ }, "timezone": "browser", "title": "Node Exporter Full", + "uid": "node-exporter-full", "version": 64 } From fc0fa3ba38e12d2a7d99f6ecf0d33b75a0b35ca9 Mon Sep 17 00:00:00 2001 From: Guillaume Demonet Date: Wed, 4 Aug 2021 23:08:24 +0200 Subject: [PATCH 2/3] ui: Use dashboard UIDs for stable links TODO: add a simple unit-test to verify the UIDs are aligned with `charts/grafana_dashboard_uids.json` Fixes: #3475 --- ui/cypress/integration/node/nodetabs.spec.js | 2 +- ui/cypress/integration/volume/volumetabs.spec.js | 2 +- ui/src/components/DashboardMetrics.js | 3 ++- ui/src/components/NodePagePartitionTab.js | 4 ++-- ui/src/components/NodePagePodsTab.js | 3 ++- ui/src/components/VolumeMetricsTab.js | 3 ++- ui/src/constants.js | 7 +++++++ ui/src/containers/NodePageMetricsTab.js | 3 ++- 8 files changed, 19 insertions(+), 8 deletions(-) diff --git a/ui/cypress/integration/node/nodetabs.spec.js b/ui/cypress/integration/node/nodetabs.spec.js index 3d24d1e69c..0128028565 100644 --- a/ui/cypress/integration/node/nodetabs.spec.js +++ b/ui/cypress/integration/node/nodetabs.spec.js @@ -87,7 +87,7 @@ describe('Node page metrics tab', () => { .should('have.attr', 'href') .and( 'to.have.string', - 'grafana/dashboard/db/nodes-detailed?var-DS_PROMETHEUS=Prometheus&var-job=node-exporter&var-name=zenkotda-master-0.novalocal', + 'grafana/d/node-exporter-full?var-DS_PROMETHEUS=Prometheus&var-job=node-exporter&var-name=zenkotda-master-0.novalocal', ); }); diff --git a/ui/cypress/integration/volume/volumetabs.spec.js b/ui/cypress/integration/volume/volumetabs.spec.js index d296299fc4..fef3ea67c1 100644 --- a/ui/cypress/integration/volume/volumetabs.spec.js +++ b/ui/cypress/integration/volume/volumetabs.spec.js @@ -97,7 +97,7 @@ describe('Volume page metrics tab', () => { .should('have.attr', 'href') .and( 'to.have.string', - 'grafana/dashboard/db/kubernetes-persistent-volumes?var-namespace=metalk8s-monitoring&var-volume=alertmanager-prometheus-operator-alertmanager-db-alertmanager-prometheus-operator-alertmanager-0', + 'grafana/d/919b92a8e8041bd567af9edab12c840c?var-namespace=metalk8s-monitoring&var-volume=alertmanager-prometheus-operator-alertmanager-db-alertmanager-prometheus-operator-alertmanager-0', ); }); diff --git a/ui/src/components/DashboardMetrics.js b/ui/src/components/DashboardMetrics.js index 51f3cfd7ed..4629ab0a9c 100644 --- a/ui/src/components/DashboardMetrics.js +++ b/ui/src/components/DashboardMetrics.js @@ -19,6 +19,7 @@ import { lineColor4, SAMPLE_DURATION_LAST_SEVEN_DAYS, REFRESH_METRICS_GRAPH, + GRAFANA_DASHBOARDS, } from '../constants'; import { getTooltipConfig } from './LinechartSpec'; import { @@ -168,7 +169,7 @@ const DashboardMetrics = () => { variant={'buttonSecondary'} icon={} size={'small'} - href={`${configQuery.data.url_grafana}/dashboard/db/nodes-detailed`} + href={`${configQuery.data.url_grafana}/d/${GRAFANA_DASHBOARDS.nodes}`} target="_blank" rel="noopener noreferrer" data-cy="advanced_metrics_node_detailed" diff --git a/ui/src/components/NodePagePartitionTab.js b/ui/src/components/NodePagePartitionTab.js index 8d16092a49..0a710617fd 100644 --- a/ui/src/components/NodePagePartitionTab.js +++ b/ui/src/components/NodePagePartitionTab.js @@ -6,7 +6,7 @@ import { NodeTab } from './style/CommonLayoutStyle'; import { useIntl } from 'react-intl'; import { padding } from '@scality/core-ui/dist/style/theme'; import NodePartitionTable from './NodePartitionTable'; -import { PORT_NODE_EXPORTER } from '../constants'; +import { GRAFANA_DASHBOARDS, PORT_NODE_EXPORTER } from '../constants'; import { useTypedSelector } from '../hooks'; const TitleContainer = styled.div` @@ -43,7 +43,7 @@ const NodePagePartitionTab = (props: Object) => { size={'small'} // We can't redirect to the Node(detailed) Filesystem Detail catagory. // So we hardcode the panel ID to redirect to 'File Nodes Free' chart - href={`${api.url_grafana}/dashboard/db/nodes-detailed?var-DS_PROMETHEUS=Prometheus&var-job=node-exporter&var-name=${hostnameLabel}&viewPanel=41`} + href={`${api.url_grafana}/d/${GRAFANA_DASHBOARDS.nodes}?var-DS_PROMETHEUS=Prometheus&var-job=node-exporter&var-name=${hostnameLabel}&viewPanel=41`} target="_blank" rel="noopener noreferrer" data-cy="advanced_metrics_node_detailed_file_node_free" diff --git a/ui/src/components/NodePagePodsTab.js b/ui/src/components/NodePagePodsTab.js index 519dd725c2..f267176220 100644 --- a/ui/src/components/NodePagePodsTab.js +++ b/ui/src/components/NodePagePodsTab.js @@ -13,6 +13,7 @@ import { STATUS_PENDING, STATUS_FAILED, STATUS_UNKNOWN, + GRAFANA_DASHBOARDS, } from '../constants'; import { useIntl } from 'react-intl'; @@ -199,7 +200,7 @@ const NodePagePodsTab = (props) => { } > diff --git a/ui/src/components/VolumeMetricsTab.js b/ui/src/components/VolumeMetricsTab.js index e325121df6..f221bf2a53 100644 --- a/ui/src/components/VolumeMetricsTab.js +++ b/ui/src/components/VolumeMetricsTab.js @@ -26,6 +26,7 @@ import { SAMPLE_FREQUENCY_LAST_TWENTY_FOUR_HOURS, SAMPLE_FREQUENCY_LAST_ONE_HOUR, queryTimeSpansCodes, + GRAFANA_DASHBOARDS, } from '../constants'; import { useIntl } from 'react-intl'; import { @@ -320,7 +321,7 @@ const MetricsTab = (props) => { onClick={() => {}} icon={} size={'small'} - href={`${config.api.url_grafana}/dashboard/db/kubernetes-persistent-volumes?var-namespace=${volumeNamespace}&var-volume=${volumePVCName}`} + href={`${config.api.url_grafana}/d/${GRAFANA_DASHBOARDS.volumes}?var-namespace=${volumeNamespace}&var-volume=${volumePVCName}`} target="_blank" rel="noopener noreferrer" data-cy="advanced_metrics_volume_detailed" diff --git a/ui/src/constants.js b/ui/src/constants.js index ef5d475bd0..9db4daa42a 100644 --- a/ui/src/constants.js +++ b/ui/src/constants.js @@ -124,3 +124,10 @@ export const lineColor1 = '#245A83'; export const lineColor2 = '#808080'; export const lineColor3 = '#A04EC9'; export const lineColor4 = '#C6B38A'; + +// Grafana dashboard UIDs (for stable links) +export const GRAFANA_DASHBOARDS = { + logs: "a7e130cb82be229d6f3edbfd0a438001", + nodes: "node-exporter-full", + volumes: "919b92a8e8041bd567af9edab12c840c", +}; diff --git a/ui/src/containers/NodePageMetricsTab.js b/ui/src/containers/NodePageMetricsTab.js index 8fc9bb1cf6..caf7b5e42e 100644 --- a/ui/src/containers/NodePageMetricsTab.js +++ b/ui/src/containers/NodePageMetricsTab.js @@ -38,6 +38,7 @@ import { SAMPLE_FREQUENCY_LAST_ONE_HOUR, queryTimeSpansCodes, PORT_NODE_EXPORTER, + GRAFANA_DASHBOARDS, } from '../constants'; import { useIntl } from 'react-intl'; import { useTypedSelector } from '../hooks'; @@ -531,7 +532,7 @@ const NodePageMetricsTab = ({ variant={'buttonSecondary'} icon={} size={'small'} - href={`${api.url_grafana}/dashboard/db/nodes-detailed?var-DS_PROMETHEUS=Prometheus&var-job=node-exporter&var-name=${hostnameLabel}`} + href={`${api.url_grafana}/d/${GRAFANA_DASHBOARDS.nodes}?var-DS_PROMETHEUS=Prometheus&var-job=node-exporter&var-name=${hostnameLabel}`} target="_blank" rel="noopener noreferrer" data-cy="advanced_metrics_node_detailed" From 18aff8eb6e5ff758c5c7e29bf16e3a0dc158f6d0 Mon Sep 17 00:00:00 2001 From: Guillaume Demonet Date: Thu, 5 Aug 2021 09:59:37 +0200 Subject: [PATCH 3/3] tests: Ensure expected Grafana dashboards are available We add a test which compares the deployed Grafana dashboards with what is declared in `charts/grafana_dashboard_uids.json`. It will detect: - missing and extra dashboards - UID mismatches This will ensure we can trust the listing in this file, and use it for documenation and/or UI links. See: #3475 --- tests/conftest.py | 7 +- tests/post/features/monitoring.feature | 4 + tests/post/steps/test_monitoring.py | 100 +++++++++++++++++++------ tests/utils.py | 57 +++++++++----- 4 files changed, 127 insertions(+), 41 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 7c1bf519a4..5b7754972f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -265,7 +265,12 @@ def ssh_config(request): @pytest.fixture def prometheus_api(control_plane_ingress_ep): - return utils.PrometheusApi(endpoint=control_plane_ingress_ep) + return utils.PrometheusApi(endpoint=f"{control_plane_ingress_ep}/api/prometheus") + + +@pytest.fixture +def grafana_api(control_plane_ingress_ep): + return utils.GrafanaAPI(endpoint=f"{control_plane_ingress_ep}/grafana") def count_running_pods(request, k8s_client, pods_count, label, namespace, node): diff --git a/tests/post/features/monitoring.feature b/tests/post/features/monitoring.feature index c8455194ef..caa92d0271 100644 --- a/tests/post/features/monitoring.feature +++ b/tests/post/features/monitoring.feature @@ -52,3 +52,7 @@ Feature: Monitoring is up and running And the Prometheus API is available And a test Volume 'test-monitoring1' exists Then I can get I/O stats for this test Volume's device + + Scenario: Expected Grafana dashboards are available + Given the Grafana API is available + Then the deployed dashboards match the expected ones diff --git a/tests/post/steps/test_monitoring.py b/tests/post/steps/test_monitoring.py index 44f7a11aa0..feb75073e0 100644 --- a/tests/post/steps/test_monitoring.py +++ b/tests/post/steps/test_monitoring.py @@ -15,17 +15,9 @@ # Constants {{{ -ALERT_RULE_FILE_NAME = "alerting_rules.json" -ALERT_RULE_FILE_PATH = ( - pathlib.Path(__file__) - / ".." - / ".." - / ".." - / ".." - / "tools" - / "rule_extractor" - / ALERT_RULE_FILE_NAME -).resolve() +REPO_ROOT = pathlib.Path(__file__).parent / "../../.." +ALERT_RULE_FILE = REPO_ROOT / "tools/rule_extractor/alerting_rules.json" +DASHBOARD_UIDS_FILE = REPO_ROOT / "charts/grafana_dashboard_uids.json" NODE_EXPORTER_PORT = 9100 @@ -86,6 +78,11 @@ def test_volume_metrics(host): pass +@scenario("../features/monitoring.feature", "Expected Grafana dashboards are available") +def test_expected_grafana_dashboards(host): + pass + + # }}} # Given {{{ @@ -98,6 +95,14 @@ def check_prometheus_api(prometheus_api): pytest.fail(str(exc)) +@given("the Grafana API is available") +def check_grafana_api(grafana_api): + try: + grafana_api.get_admin_stats() + except utils.GrafanaApiError as exc: + pytest.fail(str(exc)) + + @given(parsers.parse("the '{name}' APIService exists")) def apiservice_exists(host, name, k8s_apiclient, request): client = kubernetes.client.ApiregistrationV1Api(api_client=k8s_apiclient) @@ -257,7 +262,7 @@ def _volume_has_io_stats(): utils.retry(_volume_has_io_stats, times=60, wait=3) -@then("the deployed Prometheus alert rules are the same as the default " "alert rules") +@then("the deployed Prometheus alert rules are the same as the default alert rules") def check_deployed_rules(host, prometheus_api): try: deployed_rules = prometheus_api.get_rules() @@ -285,21 +290,72 @@ def check_deployed_rules(host, prometheus_api): deployed_alert_rules.append(fixup_alerting_rule) try: - with open(ALERT_RULE_FILE_PATH) as f: - try: - default_alert_rules = json.load(f) - except json.JSONDecodeError as exc: - pytest.fail( - "Failed to decode JSON from {}: {!s}".format( - ALERT_RULE_FILE_PATH, exc - ) - ) + alert_rules_str = ALERT_RULE_FILE.read_text(encoding="utf-8") except IOError as exc: - pytest.fail("Failed to open file {}: {!s}".format(ALERT_RULE_FILE_NAME, exc)) + pytest.fail(f"Failed to read file {ALERT_RULE_FILE}: {exc!s}") + + try: + default_alert_rules = json.loads(alert_rules_str) + except json.JSONDecodeError as exc: + pytest.fail(f"Failed to decode JSON from {ALERT_RULE_FILE}: {exc!s}") assert ( default_alert_rules == deployed_alert_rules ), "Expected default Prometheus rules to be equal to deployed rules." +@then("the deployed dashboards match the expected ones") +def check_grafana_dashboards(host, grafana_api): + try: + deployed_dashboards = grafana_api.get_dashboards() + except utils.GrafanaAPIError as exc: + pytest.fail(str(exc)) + + try: + expected_dashboards_str = DASHBOARD_UIDS_FILE.read_text(encoding="utf-8") + except IOError as exc: + pytest.fail(f"Failed to read file {DASHBOARD_UIDS_FILE}: {exc!s}") + + try: + expected_dashboards = json.loads(expected_dashboards_str) + except json.JSONDecodeError as exc: + pytest.fail(f"Failed to decode JSON from {DASHBOARD_UIDS_FILE}: {exc!s}") + + uid_mismatches = [] + extra_dashboards = [] + missing_dashboards = list(expected_dashboards.keys()) + for dashboard in deployed_dashboards: + db_title = dashboard["title"] + if db_title not in expected_dashboards: + extra_dashboards.append(db_title) + continue + + missing_dashboards.remove(db_title) + db_uid = dashboard["uid"] + expected_uid = expected_dashboards[db_title] + if db_uid != expected_uid: + uid_mismatches.append((db_title, expected_uid, db_uid)) + + errors = [] + if missing_dashboards: + db_list = "', '".join(missing_dashboards) + errors.append(f"Missing dashboard(s): '{db_list}'") + + if extra_dashboards: + db_list = "', '".join(extra_dashboards) + errors.append(f"Extra dashboard(s): '{db_list}'") + + if uid_mismatches: + mismatches_list = ", ".join( + f"'{title}' (expected '{expected}', found '{found}')" + for title, expected, found in uid_mismatches + ) + errors.append(f"UID mismatch(es): {mismatches_list}") + + if errors: + pytest.fail( + "\n".join(["Deployed dashboards do not match expectations:", *errors]) + ) + + # }}} diff --git a/tests/utils.py b/tests/utils.py index dd2e18aada..fbef6d52f2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -189,48 +189,52 @@ def get_pillar(host, key, local=False): return json.loads(output)["local"] -class PrometheusApiError(Exception): - pass - +class BaseAPIError(Exception): + """Some error occurred when using a `BaseAPI` subclass.""" -class PrometheusApi: - def __init__(self, host=None, port=9090, endpoint=None): - self.endpoint = endpoint - if not self.endpoint: - self.endpoint = "https://{}:{}".format(host, port) +class BaseAPI: + ERROR_CLS = BaseAPIError + def __init__(self, endpoint): + self.endpoint = endpoint.rstrip("/") self.session = requests_retry_session() def request(self, method, route, **kwargs): + kwargs.setdefault("verify", False) try: - kwargs.setdefault("verify", False) response = self.session.request( - method, - "{}/api/prometheus/api/v1/{}".format(self.endpoint, route), - **kwargs + method, f"{self.endpoint}/{route.lstrip('/')}", **kwargs ) response.raise_for_status() except requests.exceptions.RequestException as exc: - raise PrometheusApiError(exc) + raise self.ERROR_CLS(exc) try: return response.json() except ValueError as exc: - raise PrometheusApiError(exc) + raise self.ERROR_CLS(exc) + + +class PrometheusApiError(BaseAPIError): + pass + + +class PrometheusApi(BaseAPI): + ERROR_CLS = PrometheusApiError def query(self, metric_name, **query_matchers): matchers = [ '{}="{}"'.format(key, value) for key, value in query_matchers.items() ] query_string = metric_name + "{" + ",".join(matchers) + "}" - return self.request("GET", "query", params={"query": query_string}) + return self.request("GET", "api/v1/query", params={"query": query_string}) def get_alerts(self, **kwargs): - return self.request("GET", "alerts", **kwargs) + return self.request("GET", "api/v1/alerts", **kwargs) def get_rules(self, **kwargs): - return self.request("GET", "rules", **kwargs) + return self.request("GET", "api/v1/rules", **kwargs) def find_rules(self, name=None, group=None, labels=None, **kwargs): if not labels: @@ -251,4 +255,21 @@ def find_rules(self, name=None, group=None, labels=None, **kwargs): return rules def get_targets(self, **kwargs): - return self.request("GET", "targets", **kwargs) + return self.request("GET", "api/v1/targets", **kwargs) + + +class GrafanaAPIError(BaseAPIError): + pass + + +class GrafanaAPI(BaseAPI): + ERROR_CLS = GrafanaAPIError + + def get_admin_stats(self): + # FIXME: this user should not exist... but it's helpful in tests O:) + return self.request("GET", "api/admin/stats", auth=("admin", "admin")) + + def get_dashboards(self): + return self.request( + "GET", "api/search", auth=("admin", "admin"), params={"type": "dash-db"} + )