Browse Source

Merge "Make sure dashboard exists after we create it"

tags/0.0.1^0
Jenkins 3 years ago
parent
commit
82e48fdf08

+ 8
- 0
doc/source/api.rst View File

@@ -0,0 +1,8 @@
1
+:title: API reference
2
+
3
+API Reference
4
+=============
5
+
6
+.. automodule:: grafana_dashboards.grafana
7
+    :members:
8
+    :undoc-members:

+ 2
- 4
doc/source/conf.py View File

@@ -22,13 +22,11 @@ sys.path.insert(0, os.path.abspath('../..'))
22 22
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
23 23
 extensions = [
24 24
     'sphinx.ext.autodoc',
25
-    #'sphinx.ext.intersphinx',
26 25
     'oslosphinx'
27 26
 ]
28 27
 
29
-# autodoc generation is a bit aggressive and a nuisance when doing heavy
30
-# text edit cycles.
31
-# execute "export SPHINX_DEBUG=1" in your terminal to disable
28
+# Also document __init__
29
+autoclass_content = 'both'
32 30
 
33 31
 # The suffix of source filenames.
34 32
 source_suffix = '.rst'

+ 1
- 0
doc/source/index.rst View File

@@ -13,6 +13,7 @@ Contents
13 13
    usage
14 14
    contributing
15 15
    grafana-dashboard
16
+   api
16 17
 
17 18
 Indices and tables
18 19
 ==================

+ 6
- 6
grafana_dashboards/builder.py View File

@@ -63,10 +63,10 @@ class Builder(object):
63 63
     def update_dashboard(self, path):
64 64
         self.load_files(path)
65 65
         dashboards = self.parser.data.get('dashboard', {})
66
-        for item in dashboards:
67
-            data, md5 = self.parser.get_dashboard(item)
68
-            if self.cache.has_changed(item, md5):
69
-                self.grafana.create_dashboard(data, overwrite=True)
70
-                self.cache.set(item, md5)
66
+        for name in dashboards:
67
+            data, md5 = self.parser.get_dashboard(name)
68
+            if self.cache.has_changed(name, md5):
69
+                self.grafana.create_dashboard(name, data, overwrite=True)
70
+                self.cache.set(name, md5)
71 71
             else:
72
-                LOG.debug("'%s' has not changed" % item)
72
+                LOG.debug("'%s' has not changed" % name)

+ 82
- 7
grafana_dashboards/grafana.py View File

@@ -13,17 +13,33 @@
13 13
 # under the License.
14 14
 
15 15
 import json
16
-import requests
16
+
17 17
 try:
18 18
     from urllib.parse import urljoin
19 19
 except ImportError:
20 20
     from urlparse import urljoin
21 21
 
22
+import requests
23
+from requests import exceptions
24
+
22 25
 
23 26
 class Grafana(object):
27
+
24 28
     def __init__(self, url, key=None):
25
-        self.url = urljoin(url, 'api/dashboards/db')
29
+        """Create object for grafana instance
30
+
31
+        :param url: URL for Grafana server
32
+        :type url: str
33
+        :param key: API token used for authenticate
34
+        :type key: str
35
+
36
+        """
37
+
38
+        self.url = urljoin(url, 'api/dashboards/db/')
26 39
         self.session = requests.Session()
40
+        self.session.headers.update({
41
+            'Content-Type': 'application/json',
42
+        })
27 43
         # NOTE(pabelanger): Grafana 2.1.0 added basic auth support so now the
28 44
         # api key is optional.
29 45
         if key:
@@ -31,14 +47,73 @@ class Grafana(object):
31 47
                 'Authorization': 'Bearer %s' % key,
32 48
             })
33 49
 
34
-    def create_dashboard(self, data, overwrite=False):
50
+    def assert_dashboard_exists(self, name):
51
+        """Raise an exception if dashboard does not exist
52
+
53
+        :param name: URL friendly title of the dashboard
54
+        :type name: str
55
+        :raises Exception: if dashboard does not exist
56
+
57
+        """
58
+        if not self.is_dashboard(name):
59
+            raise Exception('dashboard[%s] does not exist' % name)
60
+
61
+    def create_dashboard(self, name, data, overwrite=False):
62
+        """Create a new dashboard
63
+
64
+        :param name: URL friendly title of the dashboard
65
+        :type name: str
66
+        :param data: Dashboard model
67
+        :type data: dict
68
+        :param overwrite: Overwrite existing dashboard with newer version or
69
+                          with the same dashboard title
70
+        :type overwrite: bool
71
+
72
+        :raises Exception: if dashboard already exists
73
+
74
+        """
35 75
         dashboard = {
36 76
             'dashboard': data,
37 77
             'overwrite': overwrite,
38 78
         }
39
-        headers = {
40
-            'Content-Type': 'application/json',
41
-        }
79
+        if not overwrite and self.is_dashboard(name):
80
+            raise Exception('dashboard[%s] already exists' % name)
81
+
42 82
         res = self.session.post(
43
-            self.url, data=json.dumps(dashboard), headers=headers)
83
+            self.url, data=json.dumps(dashboard))
84
+
44 85
         res.raise_for_status()
86
+        self.assert_dashboard_exists(name)
87
+
88
+    def get_dashboard(self, name):
89
+        """Get a dashboard
90
+
91
+        :param name: URL friendly title of the dashboard
92
+        :type name: str
93
+
94
+        :rtype: dict or None
95
+
96
+        """
97
+        url = urljoin(self.url, name)
98
+        try:
99
+            res = self.session.get(url)
100
+            res.raise_for_status()
101
+        except exceptions.HTTPError:
102
+            return None
103
+
104
+        return res.json()
105
+
106
+    def is_dashboard(self, name):
107
+        """Check if a dashboard exists
108
+
109
+        :param name: URL friendly title of the dashboard
110
+        :type name: str
111
+
112
+        :returns: True if dashboard exists
113
+        :rtype: bool
114
+
115
+        """
116
+        res = self.get_dashboard(name)
117
+        if res and res['meta']['slug'] == name:
118
+            return True
119
+        return False

+ 78
- 8
tests/test_grafana.py View File

@@ -17,12 +17,35 @@ from testtools import TestCase
17 17
 
18 18
 from grafana_dashboards.grafana import Grafana
19 19
 
20
+CREATE_NEW_DASHBOARD = {
21
+    "meta": {
22
+        "canSave": True,
23
+        "created": "0001-01-01T00:00:00Z",
24
+        "canStar": True,
25
+        "expires": "0001-01-01T00:00:00Z",
26
+        "slug": "new-dashboard",
27
+        "type": "db",
28
+        "canEdit": True
29
+    },
30
+    "dashboard": {
31
+        "rows": [],
32
+        "id": 1,
33
+        "version": 0,
34
+        "title": "New dashboard"
35
+    }
36
+}
37
+
38
+DASHBOARD_NOT_FOUND = {
39
+    "message": "Dashboard not found"
40
+}
41
+
20 42
 
21 43
 class TestCaseGrafana(TestCase):
22 44
 
23 45
     def setUp(self):
24 46
         super(TestCaseGrafana, self).setUp()
25 47
         self.url = 'http://localhost'
48
+        self.grafana = Grafana(self.url)
26 49
 
27 50
     def test_init(self):
28 51
         grafana = Grafana(self.url)
@@ -36,16 +59,63 @@ class TestCaseGrafana(TestCase):
36 59
         self.assertEqual(headers['Authorization'], 'Bearer %s' % apikey)
37 60
 
38 61
     @requests_mock.Mocker()
39
-    def test_create_dashboard_apikey(self, mock_requests):
40
-        grafana = Grafana(self.url)
41
-        mock_requests.register_uri('POST', '/api/dashboards/db')
62
+    def test_assert_dashboard_exists_failure(self, mock_requests):
63
+        mock_requests.get(
64
+            '/api/dashboards/db/new-dashboard', json=DASHBOARD_NOT_FOUND,
65
+            status_code=404)
66
+        self.assertRaises(
67
+            Exception, self.grafana.assert_dashboard_exists, 'new-dashboard')
68
+
69
+    @requests_mock.Mocker()
70
+    def test_create_dashboard_new(self, mock_requests):
71
+        def post_callback(request, context):
72
+            mock_requests.get(
73
+                '/api/dashboards/db/new-dashboard', json=CREATE_NEW_DASHBOARD)
74
+            return True
75
+
76
+        mock_requests.post('/api/dashboards/db/', json=post_callback)
77
+        mock_requests.get(
78
+            '/api/dashboards/db/new-dashboard', json=DASHBOARD_NOT_FOUND,
79
+            status_code=404)
80
+
81
+        data = {
82
+            "dashboard": {
83
+                "title": "New dashboard",
84
+            },
85
+            "slug": 'new-dashboard',
86
+        }
87
+        self.grafana.create_dashboard(
88
+            name=data['slug'], data=data['dashboard'])
89
+        self.assertEqual(mock_requests.call_count, 3)
90
+
91
+    @requests_mock.Mocker()
92
+    def test_create_dashboard_overwrite(self, mock_requests):
93
+        mock_requests.post('/api/dashboards/db/')
94
+        mock_requests.get(
95
+            '/api/dashboards/db/new-dashboard', json=CREATE_NEW_DASHBOARD)
96
+        data = {
97
+            "dashboard": {
98
+                "title": "New dashboard",
99
+            },
100
+            "slug": 'new-dashboard',
101
+        }
102
+        self.grafana.create_dashboard(
103
+            name=data['slug'], data=data['dashboard'], overwrite=True)
104
+        self.assertEqual(mock_requests.call_count, 2)
105
+
106
+    @requests_mock.Mocker()
107
+    def test_create_dashboard_existing(self, mock_requests):
108
+        mock_requests.post('/api/dashboards/db/')
109
+        mock_requests.get(
110
+            '/api/dashboards/db/new-dashboard', json=CREATE_NEW_DASHBOARD)
42 111
         data = {
43 112
             "dashboard": {
44 113
                 "title": "New dashboard",
45
-            }
114
+            },
115
+            "slug": 'new-dashboard',
46 116
         }
47
-        grafana.create_dashboard(data)
117
+        self.assertRaises(
118
+            Exception, self.grafana.create_dashboard, name=data['slug'],
119
+            data=data['dashboard'], overwrite=False)
120
+
48 121
         self.assertEqual(mock_requests.call_count, 1)
49
-        headers = mock_requests.last_request.headers
50
-        self.assertIn('Content-Type', headers)
51
-        self.assertEqual(headers['Content-Type'], 'application/json')

Loading…
Cancel
Save