diff --git a/docs/logging_monitoring.md b/docs/logging_monitoring.md index cd3a037dd..9674979ad 100644 --- a/docs/logging_monitoring.md +++ b/docs/logging_monitoring.md @@ -62,10 +62,11 @@ aggregate logs and data from Bitcoin RPC queries into a web-based dashboard. ## Connect to logging dashboard The logging stack including the user interface web server runs inside the kubernetes cluster. -To access that from a local web browser, you must use kubernetes port-forwarding. +Warnet will forward port `2019` locally from the cluster, and the landing page for all +web based interfaces will be available at `localhost:2019`. + +This page can also be opened quickly with the command [`warnet dashboard`](/docs/warnet.md#warnet-dashboard) -Run the script `./resources/scripts/connect_logging.sh` to forward port 3000. -The Grafana dashboard will then be available locally at `localhost:3000`. ### Prometheus @@ -122,21 +123,7 @@ web-based interface. #### Dashboards -To view the default metrics in the included default dashboard, upload the dashboard -JSON file to the Grafana server: - -```sh -curl localhost:3000/api/dashboards/db \ - -H "Content-Type: application/json" \ - --data "{\"dashboard\": $(cat resources/configs/grafana/default_dashboard.json)}" -``` - -Note the URL in the reply from the server (example): - -```sh -{"folderUid":"","id":2,"slug":"default-warnet-dashboard","status":"success","uid":"fdu0pda1z6a68b","url":"/d/fdu0pda1z6a68b/default-warnet-dashboard","version":1}( -``` - -Open the dashboard in your browser (example): - -`http://localhost:3000/d/fdu0pda1z6a68b/default-warnet-dashboard` +Grafana dashboards are described in JSON files. A default Warnet dashboard +is included and any other json files in the `/resources/charts/grafana-dashboards/files` directory +will also be deployed to the web server. The Grafana UI itself also has an API +for creating, exporting, and importing other dashboard files. diff --git a/resources/charts/grafana-dashboards/.helmignore b/resources/charts/grafana-dashboards/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/resources/charts/grafana-dashboards/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/resources/charts/grafana-dashboards/Chart.yaml b/resources/charts/grafana-dashboards/Chart.yaml new file mode 100644 index 000000000..a1e7b5605 --- /dev/null +++ b/resources/charts/grafana-dashboards/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: grafana-dashboards +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/resources/configs/grafana/default_dashboard.json b/resources/charts/grafana-dashboards/files/default_dashboard.json similarity index 95% rename from resources/configs/grafana/default_dashboard.json rename to resources/charts/grafana-dashboards/files/default_dashboard.json index 0ed627e22..97cb02245 100644 --- a/resources/configs/grafana/default_dashboard.json +++ b/resources/charts/grafana-dashboards/files/default_dashboard.json @@ -24,7 +24,8 @@ ], "title": "Outbound connections", "type": "timeseries", - "gridPos": { + "gridPos": + { "h": 8, "w": 24, "x": 0, @@ -52,7 +53,8 @@ ], "title": "Inbound connections", "type": "timeseries", - "gridPos": { + "gridPos": + { "h": 8, "w": 24, "x": 0, @@ -80,7 +82,8 @@ ], "title": "Mempool size", "type": "timeseries", - "gridPos": { + "gridPos": + { "h": 8, "w": 24, "x": 0, @@ -108,7 +111,8 @@ ], "title": "Blocks", "type": "timeseries", - "gridPos": { + "gridPos": + { "h": 8, "w": 24, "x": 0, diff --git a/resources/charts/grafana-dashboards/templates/NOTES.txt b/resources/charts/grafana-dashboards/templates/NOTES.txt new file mode 100644 index 000000000..33048e5f0 --- /dev/null +++ b/resources/charts/grafana-dashboards/templates/NOTES.txt @@ -0,0 +1 @@ +Grafana dashboards deployed \ No newline at end of file diff --git a/resources/charts/grafana-dashboards/templates/configmap.yaml b/resources/charts/grafana-dashboards/templates/configmap.yaml new file mode 100644 index 000000000..564c3f615 --- /dev/null +++ b/resources/charts/grafana-dashboards/templates/configmap.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: grafana-dashboards-config +data: +{{- $files := .Files.Glob "files/*.json" }} +{{- range $path, $file := $files }} + {{ base $path }}: |- +{{ $file | toString | indent 4 }} +{{- end }} diff --git a/resources/manifests/grafana_values.yaml b/resources/manifests/grafana_values.yaml index a6d16e3ab..1155e12d9 100644 --- a/resources/manifests/grafana_values.yaml +++ b/resources/manifests/grafana_values.yaml @@ -12,6 +12,25 @@ datasources: - name: Loki type: loki url: http://loki-gateway.warnet-logging:80 +dashboardProviders: + dashboardproviders.yaml: + apiVersion: 1 + providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + editable: true + options: + path: /var/lib/grafana/dashboards/default +extraVolumeMounts: + - name: grafana-dashboards-volume + mountPath: /var/lib/grafana/dashboards/default +extraVolumes: + - name: grafana-dashboards-volume + configMap: + name: grafana-dashboards-config grafana.ini: auth: disable_login_form: true diff --git a/resources/scripts/connect_logging.sh b/resources/scripts/connect_logging.sh deleted file mode 100755 index f09ffeeaf..000000000 --- a/resources/scripts/connect_logging.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -# NO `set -e` here so an error does not exit the script - -POD_NAME=$(kubectl get pods --namespace warnet-logging -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=loki-grafana" -o jsonpath="{.items[0].metadata.name}") - -echo "Go to http://localhost:3000" -echo "Grafana pod name: ${POD_NAME}" - -while true; do - echo "Attempting to start Grafana port forwarding" - echo "Please wait... it might take a few minutes for all logging pods to start" - kubectl --namespace warnet-logging port-forward "${POD_NAME}" 3000 2>&1 - echo "Grafana port forwarding exited with status: $?" - sleep 5 -done; - -echo "warnet-logging port-forward exited" diff --git a/src/warnet/constants.py b/src/warnet/constants.py index 3f837328d..4ce1ef2a5 100644 --- a/src/warnet/constants.py +++ b/src/warnet/constants.py @@ -95,5 +95,6 @@ f"helm upgrade --install --namespace warnet-logging --create-namespace --values {MANIFESTS_DIR}/loki_values.yaml loki grafana/loki --version 5.47.2", "helm upgrade --install --namespace warnet-logging promtail grafana/promtail", "helm upgrade --install --namespace warnet-logging prometheus prometheus-community/kube-prometheus-stack --namespace warnet-logging --set grafana.enabled=false", + f"helm upgrade --install grafana-dashboards {CHARTS_DIR}/grafana-dashboards --namespace warnet-logging", f"helm upgrade --install --namespace warnet-logging loki-grafana grafana/grafana --values {MANIFESTS_DIR}/grafana_values.yaml", ] diff --git a/test/logging_test.py b/test/logging_test.py index 218f51380..64abc0846 100755 --- a/test/logging_test.py +++ b/test/logging_test.py @@ -1,11 +1,8 @@ #!/usr/bin/env python3 -import logging import os -import threading from datetime import datetime from pathlib import Path -from subprocess import PIPE, Popen import requests from test_base import TestBase @@ -18,39 +15,15 @@ def __init__(self): super().__init__() self.network_dir = Path(os.path.dirname(__file__)) / "data" / "logging" self.scripts_dir = Path(os.path.dirname(__file__)) / ".." / "resources" / "scripts" - self.connect_logging_process = None - self.connect_logging_thread = None - self.connect_logging_logger = logging.getLogger("cnct_log") def run_test(self): try: self.setup_network() - self.start_logging() + self.wait_for_endpoint_ready() self.test_prometheus_and_grafana() finally: - if self.connect_logging_process is not None: - self.log.info("Terminating background connect_logging.sh process...") - self.connect_logging_process.terminate() self.cleanup() - def start_logging(self): - # Stays alive in background - self.connect_logging_process = Popen( - [f"{self.scripts_dir / 'connect_logging.sh'}"], - stdout=PIPE, - stderr=PIPE, - bufsize=1, - universal_newlines=True, - ) - self.log.info("connect_logging.sh started...") - self.connect_logging_thread = threading.Thread( - target=self.output_reader, - args=(self.connect_logging_process.stdout, self.connect_logging_logger.info), - ) - self.connect_logging_thread.daemon = True - self.connect_logging_thread.start() - self.wait_for_endpoint_ready() - def setup_network(self): self.log.info("Setting up network") self.log.info(self.warnet(f"deploy {self.network_dir}")) @@ -118,6 +91,10 @@ def get_five_values_for_metric(metric): self.wait_for_predicate(lambda: get_five_values_for_metric("blocks")) self.wait_for_predicate(lambda: get_five_values_for_metric("txrate")) + # Verify default dashboard exists + dbs = requests.get(f"{GRAFANA_URL}api/search").json() + assert dbs[0]["title"] == "Default Warnet Dashboard" + if __name__ == "__main__": test = LoggingTest()