Merge ~canonical-is-bootstack/charm-telegraf/+git/telegraf-charm:bug/1877284-func into charm-telegraf:master

Proposed by Alvaro Uria
Status: Merged
Merged at revision: 1351ea9f895b7e94a7a822b12600739b5f58dc0b
Proposed branch: ~canonical-is-bootstack/charm-telegraf/+git/telegraf-charm:bug/1877284-func
Merge into: charm-telegraf:master
Diff against target: 561 lines (+214/-116)
15 files modified
Makefile (+40/-14)
dev/null (+0/-63)
reactive/telegraf.py (+2/-2)
requirements.txt (+2/-0)
tests/bundles/bionic-mysql.yaml (+14/-0)
tests/bundles/bionic-postgres.yaml (+14/-0)
tests/bundles/overlays/local-charm-overlay.yaml.j2 (+3/-0)
tests/bundles/xenial-mysql.yaml (+14/-0)
tests/bundles/xenial-postgres.yaml (+14/-0)
tests/requirements.txt (+2/-0)
tests/test_telegraf.py (+56/-0)
tests/tests.yaml (+19/-13)
tox.ini (+24/-23)
unit_tests/requirements.txt (+6/-0)
unit_tests/test_telegraf.py (+4/-1)
Reviewer Review Type Date Requested Status
Paul Goins Approve
Zachary Zehring (community) Approve
Review via email: mp+383618@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Alvaro Uria (aluria) wrote :

This is the first part of the bug. Functional (integration) tests have been rewritten, using Zaza.

Second part will be to mock unit tests that otherwise would require privileges.

Revision history for this message
Zachary Zehring (zzehring) wrote :

Inline comments.

Revision history for this message
Zachary Zehring (zzehring) :
Revision history for this message
Alvaro Uria (aluria) wrote :

Thank you. I've replied inline and will come with a code update shortly.

Revision history for this message
Alvaro Uria (aluria) wrote :

As we talked, I've also added a try-except for requests.get. If HTTP return code is != 200, test will fail. However, network issues may be unrelated to telegraf, hence we loop through DEFAULT_RETRIES (12 times).

Revision history for this message
Zachary Zehring (zzehring) wrote :

LGTM. +1

review: Approve
Revision history for this message
Paul Goins (vultaire) wrote :

Overall, this looks like it's headed in the right direction. I've left some comments on some things I'd like to bring attention to.

review: Needs Fixing
Revision history for this message
Paul Goins (vultaire) wrote :

One more comment related to requested fixes.

Revision history for this message
Alvaro Uria (aluria) wrote :

Thank you for the review, Paul. I've pushed a new update that should fix all the suggestions.

Revision history for this message
Paul Goins (vultaire) wrote :

One last thing.

review: Needs Fixing
Revision history for this message
Paul Goins (vultaire) wrote :

Per discussion w/ aluria, this is totally fine. Approved.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/Makefile b/Makefile
2index 39fba04..61df584 100644
3--- a/Makefile
4+++ b/Makefile
5@@ -13,31 +13,57 @@
6 #
7 # You should have received a copy of the GNU General Public License
8 # along with this program. If not, see <http://www.gnu.org/licenses/>.
9+JUJU_REPOSITORY:=$(if $(JUJU_REPOSITORY),$(JUJU_REPOSITORY),/tmp/charm-builds)
10+CHARM_BUILD_DIR:=$(if $(CHARM_BUILD_DIR),$(CHARM_BUILD_DIR),$(JUJU_REPOSITORY)/builds)
11+BUILTCHARMDIR:=$(CHARM_BUILD_DIR)/telegraf
12+
13+.PHONY: help
14+help:
15+ @echo "This project supports the following targets"
16+ @echo ""
17+ @echo " make help - show this text"
18+ @echo " make lint - run flake8"
19+ @echo " make test - run the lint, unittests and functional"
20+ @echo " make unittest - run the tests defined in the unit_tests directory"
21+ @echo " make functional - run the tests defined in the tests directory"
22+ @echo " make release - build the charm"
23+ @echo " make clean - remove unneeded files"
24+ @echo ""
25+
26
27 .PHONY: lint
28 lint:
29- tox -e lint
30+ @echo "Checking flake8"
31+ @tox -e lint
32
33-.PHONY: test
34-test:
35- tox -e test
36+.PHONY: unittest
37+unittest:
38+ @echo "Running unit tests..."
39+ @tox -e unit
40
41-.PHONY: integration_xenial
42-integration_xenial:
43- tox -e integration_xenial
44+.PHONY: build
45+build:
46+ CHARM_BUILD_DIR="$(CHARM_BUILD_DIR)" charm build --report --force
47
48-.PHONY: integration_trusty
49-integration_trusty:
50- tox -e integration_trusty
51+.PHONY: functional
52+functional: build
53+ @echo "Running functional tests..."
54+ @CHARM_BUILD_DIR="$(CHARM_BUILD_DIR)" tox -e func
55
56-.PHONY: build
57-build: realclean
58- @charm build
59+.PHONY: test
60+test: lint unittest functional
61
62 .PHONY: clean
63-clean:
64+clean:
65 git clean -fXd
66
67 .PHONY: realclean
68 realclean:
69 git clean -fxd
70+
71+.PHONY: release
72+release:
73+ @echo ---------------------------------------------------------------------
74+ @echo charm push $(BUILTCHARMDIR)
75+ @echo charm release \<revision\>
76+ @echo ---------------------------------------------------------------------
77diff --git a/reactive/telegraf.py b/reactive/telegraf.py
78index f4ff604..c3dfa8f 100644
79--- a/reactive/telegraf.py
80+++ b/reactive/telegraf.py
81@@ -157,7 +157,7 @@ def render_base_inputs():
82 def get_extra_options():
83 extra_options = {'inputs': {}, 'outputs': {}}
84 extra_options_raw = hookenv.config()['extra_options']
85- extra_opts = yaml.load(extra_options_raw) or {}
86+ extra_opts = yaml.safe_load(extra_options_raw) or {}
87 extra_options.update(extra_opts)
88 # jsonify value, required as the telegraf config values format is similar
89 # to raw json
90@@ -260,7 +260,7 @@ def render_socket_listener_config(context):
91
92 @when('apt.installed.telegraf')
93 @when_not('telegraf.configured')
94-def configure_telegraf():
95+def configure_telegraf(): # noqa: C901 (compexity 15)
96 config_path = get_main_config_path()
97 if get_remote_unit_name() is None:
98 hookenv.status_set('waiting', 'Waiting for juju-info relation')
99diff --git a/requirements.txt b/requirements.txt
100new file mode 100644
101index 0000000..32c193e
102--- /dev/null
103+++ b/requirements.txt
104@@ -0,0 +1,2 @@
105+charms.reactive>=1.3.0
106+requests
107diff --git a/test_requirements.txt b/test_requirements.txt
108deleted file mode 100644
109index 64be687..0000000
110--- a/test_requirements.txt
111+++ /dev/null
112@@ -1,14 +0,0 @@
113-coverage
114-flake8
115-ipython
116-ipdb
117-pytest
118-pytest-cov
119-pytest-mock
120-charms.reactive
121-amulet
122-requests
123-juju-wait
124-netifaces
125-#--find-links https://launchpad.net/python-apt/+download/
126-#python-apt
127diff --git a/tests/bundles/bionic-mysql.yaml b/tests/bundles/bionic-mysql.yaml
128new file mode 100644
129index 0000000..0731f4d
130--- /dev/null
131+++ b/tests/bundles/bionic-mysql.yaml
132@@ -0,0 +1,14 @@
133+series: bionic
134+
135+applications:
136+ percona-cluster:
137+ charm: cs:percona-cluster
138+ num_units: 1
139+ telegraf:
140+ num_units: 0
141+
142+relations:
143+ - - percona-cluster:juju-info
144+ - telegraf:juju-info
145+ - - percona-cluster:db-admin
146+ - telegraf:mysql
147diff --git a/tests/bundles/bionic-postgres.yaml b/tests/bundles/bionic-postgres.yaml
148new file mode 100644
149index 0000000..a92b5c6
150--- /dev/null
151+++ b/tests/bundles/bionic-postgres.yaml
152@@ -0,0 +1,14 @@
153+series: bionic
154+
155+applications:
156+ postgresql:
157+ charm: cs:postgresql
158+ num_units: 1
159+ telegraf:
160+ num_units: 0
161+
162+relations:
163+ - - postgresql:juju-info
164+ - telegraf:juju-info
165+ - - postgresql:db-admin
166+ - telegraf:postgresql
167diff --git a/tests/bundles/overlays/local-charm-overlay.yaml.j2 b/tests/bundles/overlays/local-charm-overlay.yaml.j2
168new file mode 100644
169index 0000000..e78574f
170--- /dev/null
171+++ b/tests/bundles/overlays/local-charm-overlay.yaml.j2
172@@ -0,0 +1,3 @@
173+applications:
174+ {{ charm_name }}:
175+ charm: "{{ CHARM_BUILD_DIR }}/{{ charm_name }}"
176diff --git a/tests/bundles/xenial-mysql.yaml b/tests/bundles/xenial-mysql.yaml
177new file mode 100644
178index 0000000..8e60469
179--- /dev/null
180+++ b/tests/bundles/xenial-mysql.yaml
181@@ -0,0 +1,14 @@
182+series: xenial
183+
184+applications:
185+ percona-cluster:
186+ charm: cs:percona-cluster
187+ num_units: 1
188+ telegraf:
189+ num_units: 0
190+
191+relations:
192+ - - percona-cluster:juju-info
193+ - telegraf:juju-info
194+ - - percona-cluster:db-admin
195+ - telegraf:mysql
196diff --git a/tests/bundles/xenial-postgres.yaml b/tests/bundles/xenial-postgres.yaml
197new file mode 100644
198index 0000000..9f597b6
199--- /dev/null
200+++ b/tests/bundles/xenial-postgres.yaml
201@@ -0,0 +1,14 @@
202+series: xenial
203+
204+applications:
205+ postgresql:
206+ charm: cs:postgresql
207+ num_units: 1
208+ telegraf:
209+ num_units: 0
210+
211+relations:
212+ - - postgresql:juju-info
213+ - telegraf:juju-info
214+ - - postgresql:db-admin
215+ - telegraf:postgresql
216diff --git a/tests/requirements.txt b/tests/requirements.txt
217new file mode 100644
218index 0000000..b26080d
219--- /dev/null
220+++ b/tests/requirements.txt
221@@ -0,0 +1,2 @@
222+git+https://github.com/openstack-charmers/zaza.git#egg=zaza
223+charmhelpers>=0.20.8
224diff --git a/tests/test_mysql.py b/tests/test_mysql.py
225deleted file mode 100644
226index 1fbdd38..0000000
227--- a/tests/test_mysql.py
228+++ /dev/null
229@@ -1,65 +0,0 @@
230-#!/usr/bin/python3
231-#
232-# Copyright 2015-2016 Canonical Ltd.
233-#
234-# This file is part of the Telegraf Charm for Juju.
235-#
236-# This program is free software: you can redistribute it and/or modify
237-# it under the terms of the GNU General Public License version 3, as
238-# published by the Free Software Foundation.
239-#
240-# This program is distributed in the hope that it will be useful, but
241-# WITHOUT ANY WARRANTY; without even the implied warranties of
242-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
243-# PURPOSE. See the GNU General Public License for more details.
244-#
245-# You should have received a copy of the GNU General Public License
246-# along with this program. If not, see <http://www.gnu.org/licenses/>.
247-
248-import os
249-import subprocess
250-import unittest
251-
252-import amulet
253-import requests
254-
255-
256-class TestMySQL(unittest.TestCase):
257- @classmethod
258- def setUpClass(cls):
259- # amulet only detects the charm name correctly if run from
260- # the root directory. Work around this.
261- os.environ['JUJU_TEST_CHARM'] = 'telegraf'
262- charm_dir = os.path.join(os.path.dirname(__file__), os.pardir)
263-
264- cls.d = amulet.Deployment(series=os.environ.get('SERIES', 'trusty'))
265-
266- cls.d.add('mysql', 'cs:mysql', units=1)
267- cls.d.add('telegraf', charm_dir)
268- cls.d.expose('telegraf')
269- cls.d.relate('mysql:juju-info', 'telegraf:juju-info')
270- cls.d.relate('mysql:db-admin', 'telegraf:mysql')
271-
272- cls.d.setup(timeout=900)
273- cls.d.sentry.wait()
274-
275- subprocess.check_call(['juju', 'wait'], universal_newlines=True)
276-
277- def test_service(self):
278- for sentry in self.d.sentry['telegraf']:
279- with self.subTest(unit=sentry.info['unit_name']):
280- # test we can access over http
281- addr = sentry.info['public-address']
282- url = 'http://{}:9103/metrics'.format(addr)
283- page = requests.get(url)
284- self.assertEqual(page.status_code, 200)
285- # Confirm Mysql specific metrics exist.
286- # self.assertIn('mysql_blks_hit', page.text)
287- for i in page.text:
288- print("DEBUG: {}".format(i))
289- # Confirm general metrics exist.
290- self.assertIn('cpu_usage_idle', page.text)
291-
292-
293-if __name__ == '__main__':
294- unittest.main()
295diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py
296deleted file mode 100644
297index 6497e1a..0000000
298--- a/tests/test_postgresql.py
299+++ /dev/null
300@@ -1,63 +0,0 @@
301-#!/usr/bin/python3
302-#
303-# Copyright 2015-2016 Canonical Ltd.
304-#
305-# This file is part of the Telegraf Charm for Juju.
306-#
307-# This program is free software: you can redistribute it and/or modify
308-# it under the terms of the GNU General Public License version 3, as
309-# published by the Free Software Foundation.
310-#
311-# This program is distributed in the hope that it will be useful, but
312-# WITHOUT ANY WARRANTY; without even the implied warranties of
313-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
314-# PURPOSE. See the GNU General Public License for more details.
315-#
316-# You should have received a copy of the GNU General Public License
317-# along with this program. If not, see <http://www.gnu.org/licenses/>.
318-
319-import os
320-import subprocess
321-import unittest
322-
323-import amulet
324-import requests
325-
326-
327-class TestPostgreSQL(unittest.TestCase):
328- @classmethod
329- def setUpClass(cls):
330- # amulet only detects the charm name correctly if run from
331- # the root directory. Work around this.
332- os.environ['JUJU_TEST_CHARM'] = 'telegraf'
333- charm_dir = os.path.join(os.path.dirname(__file__), os.pardir)
334-
335- cls.d = amulet.Deployment(series=os.environ.get('SERIES', 'xenial'))
336-
337- cls.d.add('postgresql', 'cs:postgresql', units=2)
338- cls.d.add('telegraf', charm_dir)
339- cls.d.expose('telegraf')
340- cls.d.relate('postgresql:juju-info', 'telegraf:juju-info')
341- cls.d.relate('postgresql:db', 'telegraf:postgresql')
342-
343- cls.d.setup(timeout=900)
344- cls.d.sentry.wait()
345-
346- subprocess.check_call(['juju', 'wait'], universal_newlines=True)
347-
348- def test_service(self):
349- for sentry in self.d.sentry['telegraf']:
350- with self.subTest(unit=sentry.info['unit_name']):
351- # test we can access over http
352- addr = sentry.info['public-address']
353- url = 'http://{}:9103/metrics'.format(addr)
354- page = requests.get(url)
355- self.assertEqual(page.status_code, 200)
356- # Confirm PostgreSQL specific metrics exist.
357- self.assertIn('postgresql_blks_hit', page.text)
358- # Confirm general metrics exist.
359- self.assertIn('cpu_usage_idle', page.text)
360-
361-
362-if __name__ == '__main__':
363- unittest.main()
364diff --git a/tests/test_telegraf.py b/tests/test_telegraf.py
365new file mode 100644
366index 0000000..a6e9201
367--- /dev/null
368+++ b/tests/test_telegraf.py
369@@ -0,0 +1,56 @@
370+# Copyright 2015-2020 Canonical Ltd.
371+#
372+# This file is part of the Telegraf Charm for Juju.
373+#
374+# This program is free software: you can redistribute it and/or modify
375+# it under the terms of the GNU General Public License version 3, as
376+# published by the Free Software Foundation.
377+#
378+# This program is distributed in the hope that it will be useful, but
379+# WITHOUT ANY WARRANTY; without even the implied warranties of
380+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
381+# PURPOSE. See the GNU General Public License for more details.
382+#
383+# You should have received a copy of the GNU General Public License
384+# along with this program. If not, see <http://www.gnu.org/licenses/>.
385+import itertools as it
386+import logging
387+import time
388+import unittest
389+
390+import requests
391+
392+from zaza.utilities import juju
393+from zaza import model
394+
395+
396+DEFAULT_HTTPGET_TIMEOUT = 10
397+DEFAULT_RETRIES = 12
398+DEFAULT_RETRIES_TIMEOUT = 5
399+DEFAULT_TELEGRAF_EXPORTER_PORT = "9103"
400+
401+
402+class TestTelegraf(unittest.TestCase):
403+ def test_service(self):
404+ principal_units = (model.get_units(app)
405+ for app in juju.get_principle_applications("telegraf"))
406+ for unit in it.chain.from_iterable(principal_units):
407+ if not unit.public_address:
408+ continue
409+ url = "http://{}:{}/metrics".format(unit.public_address, DEFAULT_TELEGRAF_EXPORTER_PORT)
410+ for retry in range(DEFAULT_RETRIES):
411+ resp = requests.get(url, timeout=DEFAULT_HTTPGET_TIMEOUT)
412+ self.assertEqual(resp.status_code, 200)
413+
414+ if ((unit.name.startswith("postgresql/") and
415+ "postgresql_blks_hit" not in resp.text) or
416+ "cpu_usage_idle" not in resp.text):
417+ time.sleep(DEFAULT_RETRIES_TIMEOUT)
418+ continue
419+
420+ logging.info("test_service: Unit {} is reachable".format(unit.name))
421+ break
422+ else:
423+ msg = "test_service: Unit {} is NOT reachable".format(unit.name)
424+ logging.error(msg)
425+ self.fail(msg)
426diff --git a/tests/tests.yaml b/tests/tests.yaml
427index 409122d..2f77959 100644
428--- a/tests/tests.yaml
429+++ b/tests/tests.yaml
430@@ -1,13 +1,19 @@
431-# This file tells bundletester how to run the integration tests.
432-#
433-sources:
434- - ppa:juju/stable
435- - ppa:stub/juju
436-packages:
437- - juju-wait
438-reset: true
439-makefile:
440- - lint
441- - test
442- - integration_xenial
443- - integration_trusty
444+charm_name: telegraf
445+gate_bundles:
446+ - xenial-mysql
447+ - xenial-postgres
448+ - bionic-mysql
449+ - bionic-postgres
450+smoke_bundles:
451+ - bionic-mysql
452+ - bionic-postgres
453+dev_bundles:
454+ - bionic-mysql
455+ - bionic-postgres
456+target_deploy_status:
457+ telegraf:
458+ workload-status-message: Monitoring
459+ postgresql:
460+ workload-status-message: Live master
461+tests:
462+ - tests.test_telegraf.TestTelegraf
463diff --git a/tox.ini b/tox.ini
464index ee773e7..3a24f7b 100644
465--- a/tox.ini
466+++ b/tox.ini
467@@ -15,35 +15,36 @@
468 # along with this program. If not, see <http://www.gnu.org/licenses/>.
469
470 [tox]
471-envlist = lint,test
472+envlist = lint,unit,func
473 skipsdist = True
474-minversion = 2.3
475
476-[flake8]
477-ignore = E402,E501
478+[testenv]
479+basepython = python3
480+whitelist_externals = juju
481
482 [testenv:lint]
483-basepython = python3
484 deps = flake8
485 commands = flake8 {posargs:reactive/ unit_tests/ tests/}
486
487-[testenv:test]
488-basepython=python3
489-deps = -r{toxinidir}/test_requirements.txt
490-commands = py.test {posargs:-v unit_tests/}
491+[testenv:unit]
492+deps = -r{toxinidir}/unit_tests/requirements.txt
493+ -r{toxinidir}/requirements.txt
494+commands = pytest -v --ignore {toxinidir}/tests
495
496-[testenv:integration_xenial]
497-basepython=python3
498-deps = -r{toxinidir}/test_requirements.txt
499-passenv = JUJU_* HOME
500-setenv =
501- SERIES=xenial
502-commands = py.test {posargs:-v tests/}
503+[testenv:func]
504+commands = functest-run-suite --keep-model
505+deps = -r{toxinidir}/tests/requirements.txt
506+ -r{toxinidir}/requirements.txt
507+passenv =
508+ HOME
509+ CHARM_BUILD_DIR
510+ MODEL_SETTINGS
511
512-[testenv:integration_trusty]
513-basepython=python3
514-deps = -r{toxinidir}/test_requirements.txt
515-passenv = JUJU_* HOME
516-setenv =
517- SERIES=trusty
518-commands = py.test {posargs:-v tests/}
519+[flake8]
520+ignore = E402,E501,W504
521+exclude =
522+ .git,
523+ __pycache__,
524+ .tox,
525+max-line-length = 120
526+max-complexity = 10
527diff --git a/unit_tests/requirements.txt b/unit_tests/requirements.txt
528new file mode 100644
529index 0000000..6d56da6
530--- /dev/null
531+++ b/unit_tests/requirements.txt
532@@ -0,0 +1,6 @@
533+coverage
534+flake8
535+pytest
536+pytest-cov
537+pytest-mock
538+netifaces
539diff --git a/unit_tests/test_telegraf.py b/unit_tests/test_telegraf.py
540index 085e593..f3a0b1a 100644
541--- a/unit_tests/test_telegraf.py
542+++ b/unit_tests/test_telegraf.py
543@@ -47,6 +47,9 @@ def setup(monkeypatch, tmpdir):
544 monkeypatch.setitem(os.environ, 'JUJU_UNIT_NAME', 'telegraf/0')
545 monkeypatch.setattr(telegraf, 'get_remote_unit_name', lambda: 'remote-unit-0')
546 monkeypatch.setattr(telegraf, 'exec_timeout_supported', lambda: True)
547+ monkeypatch.setattr(telegraf.hookenv, 'status_set', lambda status, msg: None)
548+ monkeypatch.setattr(telegraf.hookenv, 'log', lambda msg, **kw: None)
549+ monkeypatch.setattr(telegraf.host, 'service_resume', lambda svc: None)
550
551 # patch host.write for non-root
552 user = getpass.getuser()
553@@ -105,7 +108,7 @@ def temp_config_dir(monkeypatch, tmpdir):
554
555 @pytest.fixture(autouse=True)
556 def config(monkeypatch, temp_charm_dir):
557- raw_config = yaml.load(open('config.yaml', 'r'))
558+ raw_config = yaml.safe_load(open('config.yaml', 'r'))
559 data = dict((k, v['default']) for k, v in raw_config['options'].items())
560 config = Config(data)
561 monkeypatch.setattr(telegraf.hookenv, 'config', lambda: config)

Subscribers

People subscribed via source and target branches

to all changes: