Skip to content

Commit

Permalink
Test examples automatically
Browse files Browse the repository at this point in the history
	- Add python3.8 (current on Ubuntu 20.04) to tox and CircleCI
	- decouple main github README from grafanalib docs RTD where we
	  can use sphinx' "literalinclude"
	- fix flake linter issues
	- make examples run at all
	- test examples as part of general tests

	fixes: #55
  • Loading branch information
Daniel Holbach committed Mar 23, 2020
1 parent 327b386 commit 1b07473
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 120 deletions.
5 changes: 3 additions & 2 deletions .circleci/config.yml
Expand Up @@ -3,16 +3,17 @@ version: 2
jobs:
build:
docker:
- image: circleci/python:3.7.5
- image: circleci/python:3.8.2
steps:
- checkout
- setup_remote_docker
- run:
name: Dependencies
command: |
git clone --depth 1 -b v1.2.15 https://github.com/pyenv/pyenv.git $HOME/.pyenv
git clone --depth 1 -b v1.2.17 https://github.com/pyenv/pyenv.git $HOME/.pyenv
$HOME/.pyenv/bin/pyenv install 3.5.8
$HOME/.pyenv/bin/pyenv install 3.7.5
$HOME/.pyenv/bin/pyenv install 3.8.2
pip3 install --user tox flake8
pip3 install --user -r docs/requirements.txt
make deps
Expand Down
2 changes: 2 additions & 0 deletions .flake8
@@ -0,0 +1,2 @@
[flake8]
ignore = E501
123 changes: 22 additions & 101 deletions README.rst
Expand Up @@ -11,122 +11,43 @@ so, grafanalib is for you.

grafanalib lets you generate Grafana dashboards from simple Python scripts.

Writing dashboards
==================

The following will configure a dashboard with a single row, with one QPS graph
broken down by status code and another latency graph showing median and 99th
percentile latency:

.. code-block:: python
from grafanalib.core import *
dashboard = Dashboard(
title="Frontend Stats",
rows=[
Row(panels=[
Graph(
title="Frontend QPS",
dataSource='My Prometheus',
targets=[
Target(
expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"1.."}[1m]))',
legendFormat="1xx",
refId='A',
),
Target(
expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"2.."}[1m]))',
legendFormat="2xx",
refId='B',
),
Target(
expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"3.."}[1m]))',
legendFormat="3xx",
refId='C',
),
Target(
expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"4.."}[1m]))',
legendFormat="4xx",
refId='D',
),
Target(
expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"5.."}[1m]))',
legendFormat="5xx",
refId='E',
),
],
yAxes=YAxes(
YAxis(format=OPS_FORMAT),
YAxis(format=SHORT_FORMAT),
),
alert=Alert(
name="Too many 500s on Nginx",
message="More than 5 QPS of 500s on Nginx for 5 minutes",
alertConditions=[
AlertCondition(
Target(
expr='sum(irate(nginx_http_requests_total{job="default/frontend",status=~"5.."}[1m]))',
legendFormat="5xx",
refId='A',
),
timeRange=TimeRange("5m", "now"),
evaluator=GreaterThan(5),
operator=OP_AND,
reducerType=RTYPE_SUM,
),
],
)
),
Graph(
title="Frontend latency",
dataSource='My Prometheus',
targets=[
Target(
expr='histogram_quantile(0.5, sum(irate(nginx_http_request_duration_seconds_bucket{job="default/frontend"}[1m])) by (le))',
legendFormat="0.5 quantile",
refId='A',
),
Target(
expr='histogram_quantile(0.99, sum(irate(nginx_http_request_duration_seconds_bucket{job="default/frontend"}[1m])) by (le))',
legendFormat="0.99 quantile",
refId='B',
),
],
yAxes=single_y_axis(format=SECONDS_FORMAT),
),
]),
],
).auto_panel_ids()
There is a fair bit of repetition here, but once you figure out what works for
your needs, you can factor that out.
How it works
============

Take a look at `the examples directory
<https://github.com/weaveworks/grafanalib/blob/master/grafanalib/tests/examples/>`_,
e.g. ` this dashboard
<https://github.com/weaveworks/grafanalib/blob/master/grafanalib/tests/examples/example.dashboard.py>`_
will configure a dashboard with a single row, with one QPS graph broken down
by status code and another latency graph showing median and 99th percentile
latency.

In the code is a fair bit of repetition here, but once you figure out what
works for your needs, you can factor that out.
See `our Weave-specific customizations
<https://github.com/weaveworks/grafanalib/blob/master/grafanalib/weave.py>`_
for inspiration.

You can read the entire grafanlib documentation on `readthedocs.io
<https://grafanalib.readthedocs.io/en/latest/>`_.
<https://grafanalib.readthedocs.io/>`_.

Generating dashboards
=====================
Getting started
===============

If you save the above as ``frontend.dashboard.py`` (the suffix must be
``.dashboard.py``), you can then generate the JSON dashboard with:
grafanalib is just a Python package, so:

.. code-block:: console
$ generate-dashboard -o frontend.json frontend.dashboard.py
$ pip install grafanalib
Installation
============
grafanalib is just a Python package, so:
Generate the JSON dashboard like so:

.. code-block:: console
$ pip install grafanalib
$ curl https://raw.githubusercontent.com/weaveworks/grafanalib/master/grafanalib/tests/examples/example.dashboard.py
$ generate-dashboard -o frontend.json example.dashboard.py
Support
=======
Expand Down
1 change: 0 additions & 1 deletion docs/README.rst

This file was deleted.

75 changes: 75 additions & 0 deletions docs/getting-started.rst
@@ -0,0 +1,75 @@
===============================
Getting Started with grafanalib
===============================

.. image:: https://circleci.com/gh/weaveworks/grafanalib.svg?style=shield
:target: https://circleci.com/gh/weaveworks/grafanalib

Do you like `Grafana <http://grafana.org/>`_ but wish you could version your
dashboard configuration? Do you find yourself repeating common patterns? If
so, grafanalib is for you.

grafanalib lets you generate Grafana dashboards from simple Python scripts.

Writing dashboards
==================

The following will configure a dashboard with a single row, with one QPS graph
broken down by status code and another latency graph showing median and 99th
percentile latency:

.. literalinclude:: example.dashboard.py
:language: python

There is a fair bit of repetition here, but once you figure out what works for
your needs, you can factor that out.
See `our Weave-specific customizations
<https://github.com/weaveworks/grafanalib/blob/master/grafanalib/weave.py>`_
for inspiration.

You can read the entire grafanlib documentation on `readthedocs.io
<https://grafanalib.readthedocs.io/en/latest/>`_.

Generating dashboards
=====================

If you save the above as ``frontend.dashboard.py`` (the suffix must be
``.dashboard.py``), you can then generate the JSON dashboard with:

.. code-block:: console
$ generate-dashboard -o frontend.json frontend.dashboard.py
Installation
============

grafanalib is just a Python package, so:

.. code-block:: console
$ pip install grafanalib
Support
=======

This library is in its very early stages. We'll probably make changes that
break backwards compatibility, although we'll try hard not to.

grafanalib works with Python 3.4, 3.5, 3.6 and 3.7.

Developing
==========
If you're working on the project, and need to build from source, it's done as follows:

.. code-block:: console
$ virtualenv .env
$ . ./.env/bin/activate
$ pip install -e .
Configuring Grafana Datasources
===============================

This repo used to contain a program ``gfdatasource`` for configuring
Grafana data sources, but it has been retired since Grafana now has a
built-in way to do it. See https://grafana.com/docs/administration/provisioning/#datasources
3 changes: 2 additions & 1 deletion docs/index.rst
Expand Up @@ -10,7 +10,8 @@ Welcome to grafanalib's documentation!
:maxdepth: 2
:caption: Contents:

../README
getting-started

api/grafanalib
api/modules

Expand Down
18 changes: 9 additions & 9 deletions grafanalib/elasticsearch.py
Expand Up @@ -108,9 +108,9 @@ class Filter(object):

def to_json_data(self):
return {
'label': self.label,
'query': self.query,
}
'label': self.label,
'query': self.query,
}


@attr.s
Expand All @@ -127,12 +127,12 @@ class FiltersGroupBy(object):

def to_json_data(self):
return {
'id': str(self.id),
'settings': {
'filters': self.filters,
},
'type': 'filters',
}
'id': str(self.id),
'settings': {
'filters': self.filters,
},
'type': 'filters',
}


@attr.s
Expand Down
Expand Up @@ -7,8 +7,15 @@
- Max. response time per point of time of HTTP requests
"""

from grafanalib.core import *
from grafanalib.elasticsearch import *
from grafanalib.core import (
Dashboard, Graph, Legend, NULL_AS_NULL, Row, SECONDS_FORMAT,
SHORT_FORMAT, YAxes, YAxis
)

from grafanalib.elasticsearch import (
DateHistogramGroupBy, ElasticsearchTarget, Filter,
FiltersGroupBy, MaxMetricAgg
)

suc_label = "Success (200-300)"
clt_err_label = "Client Errors (400-500)"
Expand Down Expand Up @@ -69,7 +76,7 @@
"color": "#447EBC"
},
],
yAxes=G.YAxes(
yAxes=YAxes(
YAxis(
label="Count",
format=SHORT_FORMAT,
Expand Down
@@ -1,4 +1,8 @@
from grafanalib.core import *
from grafanalib.core import (
Alert, AlertCondition, Dashboard, Graph,
GreaterThan, OP_AND, OPS_FORMAT, Row, RTYPE_SUM, SECONDS_FORMAT,
SHORT_FORMAT, single_y_axis, Target, TimeRange, YAxes, YAxis
)


dashboard = Dashboard(
Expand Down Expand Up @@ -35,7 +39,7 @@
refId='E',
),
],
yAxes=G.YAxes(
yAxes=YAxes(
YAxis(format=OPS_FORMAT),
YAxis(format=SHORT_FORMAT),
),
Expand Down
23 changes: 23 additions & 0 deletions grafanalib/tests/test_examples.py
@@ -0,0 +1,23 @@
'''Run examples.'''

from contextlib import redirect_stdout
import glob
import io
import os

from grafanalib import _gen


def test_examples():
'''Run examples in ./examples directory.'''

examples_dir = os.path.join(os.path.dirname(__file__), 'examples')
examples = glob.glob('{}/*.dashboard.py'.format(examples_dir))
assert len(examples) == 2

stdout = io.StringIO()
for example in examples:
with redirect_stdout(stdout):
ret = _gen.generate_dashboard([example])
assert ret == 0
assert stdout.getvalue() != ''
2 changes: 1 addition & 1 deletion tox.ini
Expand Up @@ -4,7 +4,7 @@
# and then run "tox" from this directory.

[tox]
envlist = py35, py37
envlist = py35, py37, py38

[testenv]
commands = pytest --junitxml=test-results/junit-{envname}.xml
Expand Down

0 comments on commit 1b07473

Please sign in to comment.