Merge ~npochet/charm-graylog:graylog-ha into ~graylog-charmers/charm-graylog:master
- Git
- lp:~npochet/charm-graylog
- graylog-ha
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Jeremy Lounder | ||||
Approved revision: | b5487d2b9823cdffbb7b84fe34aff98518df4389 | ||||
Merged at revision: | f95473e145f97f5a7e6bec9e1b44e2e289f3b2fb | ||||
Proposed branch: | ~npochet/charm-graylog:graylog-ha | ||||
Merge into: | ~graylog-charmers/charm-graylog:master | ||||
Diff against target: |
556 lines (+149/-131) 11 files modified
Makefile (+4/-12) dev/null (+0/-112) layer.yaml (+1/-0) reactive/graylog.py (+35/-0) tests/bundles/bionic-ha.yaml (+39/-0) tests/bundles/bionic.yaml (+39/-0) tests/bundles/overlays/local-charm-overlay.yaml.j2 (+3/-0) tests/requirements.txt (+1/-0) tests/tests.yaml (+12/-5) tox.ini (+6/-0) unit_tests/test_graylog.py (+9/-2) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jeremy Lounder (community) | Approve | ||
Stuart Bishop (community) | Approve | ||
Michael Iatrou | Pending | ||
Graylog Charmers | Pending | ||
Review via email: mp+368200@code.launchpad.net |
Commit message
Support multiple nodes
* Select the inital master based on juju leadership. This information is
then stored for further use.
* The master is only changed if the actual master is departed.
* The elected leader will also send its password to the others.
* Migrate functional tests to zaza
* Fixes LP:1794631
Description of the change
Support multiple nodes
* Select the inital master based on juju leadership. This information is
then stored for further use.
* The master is only changed if the actual master is departed.
* The elected leader will also send its password to the others.
* Migrate functional tests to zaza
* Fixes LP:1794631
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Stuart Bishop (stub) wrote : | # |
I don't think we can have the graylog master follow the Juju leader, because we will have periods where there are multiple masters (the old master will only learn that it is no longer master when the new leader publishes new leadership settings), and because we will get unnecessary master changes when controller or network issues trigger leadership elections (probably exactly the times when we want logging to be stable).
Instead, the leader should appoint the master, and the master will remain master until the unit departs.
from charms import leadership
@when('
def appoint_master():
master = leadership.
if master and (master == hookenv.
return # Already have a master
leadership.
def is_master():
return leadership.
If you are worried about a failed unit, you could also have the leader perform a liveness check to graylog running on the master (which should handle the master being powered off). However, this risks a split brain with two live graylogs thinking they are master (the original proposal has the same flaw too, where a master loses network connectivity to Juju, and a new unit gets leadership and we get a new master and the old master will have no idea until the network connectivity to Juju is repaired).
Stuart Bishop (stub) wrote : | # |
(everything else is great, including the zaza tests)
Nicolas Pochet (npochet) wrote : | # |
I'll apply the changes and update the MP today
Regarding zaza, see my comment inline
Nicolas Pochet (npochet) wrote : | # |
Regarding zaza, I'm not sure that my previous comment is valid. I can see, in the openstack charms that are using zaza, that they always use master.
For example: https:/
Stuart Bishop (stub) wrote : | # |
This seems good, so +1 from ~canonical-
Jeremy Lounder (jldev) wrote : | # |
We're going to hold on this merge until we fully understand the scope of what is required to make Graylog HA. While having multi-node Graylog does have some benefits, such as distributed ingestion. I need to take the time to understand how the Graylog hostnames are communicated out via relation, and how they are consumed on relations by other charms/services.
At the moment, it would appear there is no recovery mechanism if a node fails. It may make sense to put haproxy in front of the ingestion endpoint - in addition to the web endpoint.
Jeremy Lounder (jldev) wrote : | # |
Approving this for merge and release. Additional research reveals that the HA process for our implementation is handled on the filebeats side. Filebeats does client-end loading balancing and delivery guarantee.
For other input types, we'll still need to evaluate improving the haproxy relation, and placing a load balancer in front of the inputs.
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision f95473e145f97f5
Preview Diff
1 | diff --git a/Makefile b/Makefile |
2 | index 9e5089e..44e05cc 100644 |
3 | --- a/Makefile |
4 | +++ b/Makefile |
5 | @@ -21,11 +21,8 @@ sysdeps: |
6 | |
7 | .PHONY: testdeps |
8 | testdeps: |
9 | - -sudo apt-add-repository -y ppa:juju/stable |
10 | - -sudo apt-add-repository -y ppa:tvansteenburgh/ppa |
11 | - @sudo apt update |
12 | - @sudo apt install -y juju charm amulet python-pip python3-nose |
13 | - @sudo pip2 install bundletester |
14 | + @sudo snap install juju |
15 | + @sudo snap install charm |
16 | |
17 | .PHONY: lint |
18 | lint: sysdeps |
19 | @@ -67,11 +64,6 @@ charmrelease: |
20 | @echo --------------------------------------------------------------------- |
21 | |
22 | .PHONY: test |
23 | -test: check-jujumodel testdeps |
24 | +test: testdeps charmbuild |
25 | @echo "Running functional tests (including lint and unit tests)..." |
26 | - bundletester -t $(BUILTCHARMDIR) -Fvl DEBUG -e $(JUJU_MODEL) |
27 | - |
28 | -check-jujumodel: |
29 | -ifndef JUJU_MODEL |
30 | - $(error JUJU_MODEL is undefined) |
31 | -endif |
32 | + @tox -e func |
33 | diff --git a/layer.yaml b/layer.yaml |
34 | index 3d0f751..d4fa469 100644 |
35 | --- a/layer.yaml |
36 | +++ b/layer.yaml |
37 | @@ -2,6 +2,7 @@ repo: git+ssh://git.launchpad.net/graylog-charm |
38 | includes: |
39 | - 'layer:basic' |
40 | - 'layer:snap' |
41 | + - 'layer:leadership' |
42 | - 'interface:elasticsearch' |
43 | - 'interface:elastic-beats' |
44 | - 'interface:http' |
45 | diff --git a/reactive/graylog.py b/reactive/graylog.py |
46 | index 786df04..e408e0c 100644 |
47 | --- a/reactive/graylog.py |
48 | +++ b/reactive/graylog.py |
49 | @@ -10,6 +10,7 @@ from charms.reactive import hook, when, when_not, is_state, remove_state, set_st |
50 | from charms.reactive.helpers import data_changed |
51 | from charmhelpers.core import host, hookenv, unitdata |
52 | from charmhelpers.contrib.charmsupport import nrpe |
53 | +import charms.leadership |
54 | |
55 | from charms.layer.graylog import logextract |
56 | from charms.layer.graylog.api import GraylogApi |
57 | @@ -102,6 +103,40 @@ def configure_graylog(): |
58 | report_status() |
59 | |
60 | |
61 | +@when('leadership.is_leader') |
62 | +@when_not('leadership.set.admin_password') |
63 | +def generate_admin_password(): |
64 | + admin_password = host.pwgen(18) |
65 | + charms.leadership.leader_set(admin_password=admin_password) |
66 | + |
67 | + |
68 | +@when('leadership.changed.admin_password') |
69 | +def get_leader_admin_password(): |
70 | + db = unitdata.kv() |
71 | + admin_password = charms.leadership.leader_get('admin_password') |
72 | + db.set('admin_password', admin_password) |
73 | + remove_state('graylog.configured') |
74 | + |
75 | + |
76 | +@when('leadership.is_leader') |
77 | +def appoint_master(): |
78 | + master = charms.leadership.leader_get('master') |
79 | + if master and (master == hookenv.local_unit() or master in set(hookenv.expected_peer_units())): |
80 | + # A master already exists |
81 | + return |
82 | + # Appoint myself as master |
83 | + charms.leadership.leader_set(master=hookenv.local_unit()) |
84 | + |
85 | + |
86 | +def is_master(): |
87 | + return charms.leadership.leader_get('master') == hookenv.local_unit() |
88 | + |
89 | + |
90 | +@when('leadership.changed.master') |
91 | +def set_master(): |
92 | + set_conf('is_master', is_master()) |
93 | + |
94 | + |
95 | @when('graylog.configured') # noqa: C901 |
96 | def report_status(): |
97 | beats_connected = is_state('beats.connected') |
98 | diff --git a/tests/bundles/bionic-ha.yaml b/tests/bundles/bionic-ha.yaml |
99 | new file mode 100644 |
100 | index 0000000..e266cf5 |
101 | --- /dev/null |
102 | +++ b/tests/bundles/bionic-ha.yaml |
103 | @@ -0,0 +1,39 @@ |
104 | +series: bionic |
105 | + |
106 | +applications: |
107 | + ubuntu: |
108 | + charm: cs:ubuntu |
109 | + num_units: 1 |
110 | + |
111 | + filebeat: |
112 | + charm: cs:filebeat |
113 | + num_units: 0 |
114 | + |
115 | + graylog: |
116 | + charm: ../../../graylog-built/builds/graylog |
117 | + num_units: 3 |
118 | + series: bionic |
119 | + |
120 | + elastic: |
121 | + charm: cs:elasticsearch |
122 | + num_units: 1 |
123 | + |
124 | + mongo: |
125 | + charm: cs:mongodb |
126 | + num_units: 1 |
127 | + |
128 | + haproxy: |
129 | + charm: cs:haproxy |
130 | + num_units: 1 |
131 | + |
132 | +relations: |
133 | + - - ubuntu |
134 | + - filebeat |
135 | + - - graylog:beats |
136 | + - filebeat:logstash |
137 | + - - graylog |
138 | + - mongo |
139 | + - - graylog |
140 | + - elastic |
141 | + - - graylog |
142 | + - haproxy |
143 | \ No newline at end of file |
144 | diff --git a/tests/bundles/bionic.yaml b/tests/bundles/bionic.yaml |
145 | new file mode 100644 |
146 | index 0000000..e196355 |
147 | --- /dev/null |
148 | +++ b/tests/bundles/bionic.yaml |
149 | @@ -0,0 +1,39 @@ |
150 | +series: bionic |
151 | + |
152 | +applications: |
153 | + ubuntu: |
154 | + charm: cs:ubuntu |
155 | + num_units: 1 |
156 | + |
157 | + filebeat: |
158 | + charm: cs:filebeat |
159 | + num_units: 0 |
160 | + |
161 | + graylog: |
162 | + charm: ../../../graylog-built/builds/graylog |
163 | + num_units: 1 |
164 | + series: bionic |
165 | + |
166 | + elastic: |
167 | + charm: cs:elasticsearch |
168 | + num_units: 1 |
169 | + |
170 | + mongo: |
171 | + charm: cs:mongodb |
172 | + num_units: 1 |
173 | + |
174 | + haproxy: |
175 | + charm: cs:haproxy |
176 | + num_units: 1 |
177 | + |
178 | +relations: |
179 | + - - ubuntu |
180 | + - filebeat |
181 | + - - graylog:beats |
182 | + - filebeat:logstash |
183 | + - - graylog |
184 | + - mongo |
185 | + - - graylog |
186 | + - elastic |
187 | + - - graylog |
188 | + - haproxy |
189 | \ No newline at end of file |
190 | diff --git a/tests/bundles/overlays/local-charm-overlay.yaml.j2 b/tests/bundles/overlays/local-charm-overlay.yaml.j2 |
191 | new file mode 100644 |
192 | index 0000000..b459ab1 |
193 | --- /dev/null |
194 | +++ b/tests/bundles/overlays/local-charm-overlay.yaml.j2 |
195 | @@ -0,0 +1,3 @@ |
196 | +applications: |
197 | + graylog: |
198 | + charm: ../../../graylog-built/builds/graylog |
199 | \ No newline at end of file |
200 | diff --git a/tests/requirements.txt b/tests/requirements.txt |
201 | new file mode 100644 |
202 | index 0000000..3da5fb4 |
203 | --- /dev/null |
204 | +++ b/tests/requirements.txt |
205 | @@ -0,0 +1 @@ |
206 | +git+https://github.com/openstack-charmers/zaza.git#egg=zaza |
207 | \ No newline at end of file |
208 | diff --git a/tests/test_10_basic.py b/tests/test_10_basic.py |
209 | deleted file mode 100755 |
210 | index 54133c2..0000000 |
211 | --- a/tests/test_10_basic.py |
212 | +++ /dev/null |
213 | @@ -1,152 +0,0 @@ |
214 | -#!/usr/bin/python3 |
215 | - |
216 | -import amulet |
217 | -import os |
218 | -import re |
219 | -import sys |
220 | -import time |
221 | -import unittest |
222 | -import yaml |
223 | - |
224 | -libs_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), |
225 | - '..', 'lib')) |
226 | -if libs_dir not in sys.path: |
227 | - sys.path.append(libs_dir) |
228 | - |
229 | -from graylogapi import GraylogAPI # noqa: E402 |
230 | - |
231 | - |
232 | -ADMIN_PASSWORD = 'admin' |
233 | -CLUSTER_NAME = 'amulet' |
234 | -CURL_TIMEOUT = 180 |
235 | -DEFAULT_API_PORT = '9001' |
236 | -DEFAULT_WEB_PORT = '9000' |
237 | - |
238 | - |
239 | -class TestCharm(unittest.TestCase): |
240 | - @classmethod |
241 | - def setUpClass(cls): |
242 | - cls.d = amulet.Deployment(series='xenial') |
243 | - |
244 | - cls.d.add('graylog') |
245 | - cls.d.configure('graylog', { |
246 | - 'elasticsearch_cluster_name': CLUSTER_NAME, |
247 | - }) |
248 | - cls.d.expose('graylog') |
249 | - |
250 | - cls.d.add('nrpe', 'cs:nrpe') |
251 | - cls.d.relate('graylog:nrpe-external-master', |
252 | - 'nrpe:nrpe-external-master') |
253 | - |
254 | - cls.d.add('mongodb', 'cs:mongodb') |
255 | - cls.d.configure('mongodb', { |
256 | - 'replicaset': CLUSTER_NAME, |
257 | - }) |
258 | - cls.d.relate('graylog:mongodb', 'mongodb:database') |
259 | - |
260 | - cls.d.add('elasticsearch', 'cs:elasticsearch') |
261 | - cls.d.configure('elasticsearch', { |
262 | - 'cluster-name': CLUSTER_NAME, |
263 | - }) |
264 | - cls.d.relate('graylog:elasticsearch', 'elasticsearch:client') |
265 | - |
266 | - cls.d.add('apache2', 'cs:apache2') |
267 | - cls.d.relate('graylog:website', 'apache2:reverseproxy') |
268 | - cls.d.expose('apache2') |
269 | - |
270 | - try: |
271 | - timeout = int(os.environ.get('AMULET_SETUP_TIMEOUT', 900)) |
272 | - cls.d.setup(timeout=timeout) |
273 | - cls.d.sentry.wait(timeout=timeout) |
274 | - cls.d.sentry.wait_for_messages({'graylog': re.compile('Ready with'), |
275 | - 'elasticsearch': 'Ready', |
276 | - 'nrpe': 'ready'}, |
277 | - timeout=timeout) |
278 | - except amulet.helpers.TimeoutError: |
279 | - amulet.raise_status( |
280 | - amulet.FAIL, |
281 | - msg="Deployment timed out ({}s)".format(timeout) |
282 | - ) |
283 | - except Exception: |
284 | - raise |
285 | - |
286 | - cls.elasticsearch = cls.d.sentry['elasticsearch'][0] |
287 | - cls.mongodb = cls.d.sentry['mongodb'][0] |
288 | - cls.graylog = cls.d.sentry['graylog'][0] |
289 | - cls.nrpe = cls.d.sentry['nrpe'][0] |
290 | - cls.graylog.run_action('set-admin-password', {'password': ADMIN_PASSWORD}) |
291 | - |
292 | - cls.config = cls.graylog.file_contents('/var/snap/graylog/common/server.conf') |
293 | - api_url = "http://{}:{}/api".format(cls.graylog.info['public-address'], DEFAULT_API_PORT) |
294 | - cls.api = GraylogAPI(api_url, 'admin', ADMIN_PASSWORD) |
295 | - |
296 | - # https://bugs.launchpad.net/graylog-charm/+bug/1748040 |
297 | - cls.graylog.run('open-port {}'.format(DEFAULT_API_PORT)) |
298 | - |
299 | - def test_api_ready(self): |
300 | - ''' Curl the api endpoint on the sentried graylog unit. We'll retry |
301 | - until the CURL_TIMEOUT because it may take a few seconds for the |
302 | - graylog systemd service to start.''' |
303 | - curl_command = 'curl http://localhost:{}'.format(DEFAULT_API_PORT) |
304 | - timeout = time.time() + CURL_TIMEOUT |
305 | - while time.time() < timeout: |
306 | - response = self.graylog.run(curl_command) |
307 | - # run returns a msg,retcode tuple |
308 | - if response[1] == 0: |
309 | - return |
310 | - else: |
311 | - print("Unexpected curl response: {}. " |
312 | - "Retrying in 30s.".format(response[0])) |
313 | - time.sleep(30) |
314 | - |
315 | - # we didn't get rc=0 in the alloted time; raise amulet failure |
316 | - msg = ( |
317 | - "Graylog didn't respond to the command \n" |
318 | - "'{curl_command}' as expected.\n" |
319 | - "Return code: {return_code}\n" |
320 | - "Result: {result}".format( |
321 | - curl_command=curl_command, |
322 | - return_code=response[1], |
323 | - result=response[0]) |
324 | - ) |
325 | - amulet.raise_status(amulet.FAIL, msg=msg) |
326 | - |
327 | - def test_elasticsearch_active(self): |
328 | - elasticsearch_ip = self.elasticsearch.relation('client', |
329 | - 'graylog:elasticsearch')['private-address'] |
330 | - self.assertTrue(elasticsearch_ip in self.config) |
331 | - resp = self.api.indexer_cluster_health() |
332 | - self.assertTrue(resp['status'] == 'green') |
333 | - |
334 | - def test_mongodb_active(self): |
335 | - mongodb_ip = self.mongodb.relation('database', 'graylog:mongodb')['private-address'] |
336 | - self.assertTrue(mongodb_ip in self.config) |
337 | - resp = self.api.cluster_get() |
338 | - self.assertTrue(resp[list(resp)[0]]['is_processing']) |
339 | - |
340 | - def test_website_active(self): |
341 | - relation = self.graylog.relation('website', 'apache2:reverseproxy') |
342 | - self.assertEqual(relation['port'], DEFAULT_WEB_PORT) |
343 | - |
344 | - service_ports = {} |
345 | - for service in yaml.safe_load(relation['all_services']): |
346 | - service_ports[service['service_name']] = service['service_port'] |
347 | - |
348 | - self.assertEqual(str(service_ports['web']), DEFAULT_WEB_PORT) |
349 | - self.assertEqual(str(service_ports['api']), DEFAULT_API_PORT) |
350 | - |
351 | - def test_nrpe_config(self): |
352 | - cfg = self.nrpe.file_contents( |
353 | - '/etc/nagios/nrpe.d/check_graylog_health.cfg') |
354 | - self.assertTrue( |
355 | - re.search(r'command.*check_graylog_health', cfg)) |
356 | - |
357 | - def test_nrpe_health_check(self): |
358 | - cfg = self.nrpe.file_contents( |
359 | - '/usr/local/lib/nagios/plugins/check_graylog_health.py') |
360 | - self.assertTrue( |
361 | - re.search(r'CRITICAL', cfg)) |
362 | - |
363 | - |
364 | -if __name__ == "__main__": |
365 | - unittest.main() |
366 | diff --git a/tests/test_20_clustered.py b/tests/test_20_clustered.py |
367 | deleted file mode 100755 |
368 | index 96284f7..0000000 |
369 | --- a/tests/test_20_clustered.py |
370 | +++ /dev/null |
371 | @@ -1,112 +0,0 @@ |
372 | -#!/usr/bin/python3 |
373 | - |
374 | -import amulet |
375 | -import os |
376 | -import re |
377 | -import sys |
378 | -import time |
379 | -import unittest |
380 | - |
381 | -libs_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), |
382 | - '..', 'lib')) |
383 | -if libs_dir not in sys.path: |
384 | - sys.path.append(libs_dir) |
385 | - |
386 | -from graylogapi import GraylogAPI # noqa: E402 |
387 | - |
388 | - |
389 | -ADMIN_PASSWORD = 'admin' |
390 | -CLUSTER_NAME = 'amulet' |
391 | -CURL_TIMEOUT = 180 |
392 | -DEFAULT_API_PORT = '9001' |
393 | -DEFAULT_WEB_PORT = '9000' |
394 | - |
395 | - |
396 | -class TestCharm(unittest.TestCase): |
397 | - @classmethod |
398 | - def setUpClass(cls): |
399 | - cls.d = amulet.Deployment(series='xenial') |
400 | - |
401 | - cls.d.add('graylog') |
402 | - # TODO add in clustered graylog when support for it exists |
403 | - cls.d.configure('graylog', { |
404 | - 'elasticsearch_cluster_name': CLUSTER_NAME, |
405 | - }) |
406 | - cls.d.expose('graylog') |
407 | - |
408 | - cls.d.add('mongodb', 'cs:mongodb', units=3) |
409 | - cls.d.configure('mongodb', { |
410 | - 'replicaset': CLUSTER_NAME, |
411 | - }) |
412 | - cls.d.relate('graylog:mongodb', 'mongodb:database') |
413 | - |
414 | - cls.d.add('elasticsearch', 'cs:elasticsearch', units=3) |
415 | - cls.d.configure('elasticsearch', { |
416 | - 'cluster-name': CLUSTER_NAME, |
417 | - }) |
418 | - cls.d.relate('graylog:elasticsearch', 'elasticsearch:client') |
419 | - |
420 | - try: |
421 | - cls.d.setup(timeout=900) |
422 | - cls.d.sentry.wait_for_messages({'graylog': re.compile('Ready with'), |
423 | - 'elasticsearch': 'Ready'}, |
424 | - timeout=1800) |
425 | - except amulet.helpers.TimeoutError: |
426 | - # Setup didn't complete before timeout |
427 | - pass |
428 | - |
429 | - cls.graylog = cls.d.sentry['graylog'][0] |
430 | - cls.graylog.run_action('set-admin-password', {'password': ADMIN_PASSWORD}) |
431 | - |
432 | - cls.config = cls.graylog.file_contents('/var/snap/graylog/common/server.conf') |
433 | - api_url = "http://{}:{}/api".format(cls.graylog.info['public-address'], DEFAULT_API_PORT) |
434 | - cls.api = GraylogAPI(api_url, 'admin', ADMIN_PASSWORD) |
435 | - |
436 | - # https://bugs.launchpad.net/graylog-charm/+bug/1748040 |
437 | - cls.graylog.run('open-port {}'.format(DEFAULT_API_PORT)) |
438 | - |
439 | - def test_api_ready(self): |
440 | - ''' Curl the api endpoint on the sentried graylog unit. We'll retry |
441 | - until the CURL_TIMEOUT because it may take a few seconds for the |
442 | - graylog systemd service to start.''' |
443 | - curl_command = 'curl http://localhost:{}'.format(DEFAULT_API_PORT) |
444 | - timeout = time.time() + CURL_TIMEOUT |
445 | - while time.time() < timeout: |
446 | - response = self.graylog.run(curl_command) |
447 | - # run returns a msg,retcode tuple |
448 | - if response[1] == 0: |
449 | - return |
450 | - else: |
451 | - print("Unexpected curl response: {}. " |
452 | - "Retrying in 30s.".format(response[0])) |
453 | - time.sleep(30) |
454 | - |
455 | - # we didn't get rc=0 in the alloted time; raise amulet failure |
456 | - msg = ( |
457 | - "Graylog didn't respond to the command \n" |
458 | - "'{curl_command}' as expected.\n" |
459 | - "Return code: {return_code}\n" |
460 | - "Result: {result}".format( |
461 | - curl_command=curl_command, |
462 | - return_code=response[1], |
463 | - result=response[0]) |
464 | - ) |
465 | - amulet.raise_status(amulet.FAIL, msg=msg) |
466 | - |
467 | - def test_elasticsearch_active(self): |
468 | - resp = self.api.indexer_cluster_health() |
469 | - self.assertTrue(resp['status'] == 'green') |
470 | - for elasticsearch in self.d.sentry['elasticsearch']: |
471 | - ip = elasticsearch.relation('client', 'graylog:elasticsearch')['private-address'] |
472 | - self.assertTrue(ip in self.config) |
473 | - |
474 | - def test_mongodb_active(self): |
475 | - resp = self.api.cluster_get() |
476 | - self.assertTrue(resp[list(resp)[0]]['is_processing']) |
477 | - for mongodb in self.d.sentry['mongodb']: |
478 | - ip = mongodb.relation('database', 'graylog:mongodb')['private-address'] |
479 | - self.assertTrue(ip in self.config) |
480 | - |
481 | - |
482 | -if __name__ == "__main__": |
483 | - unittest.main() |
484 | diff --git a/tests/tests.yaml b/tests/tests.yaml |
485 | index 5c4cb8e..1bb1e58 100644 |
486 | --- a/tests/tests.yaml |
487 | +++ b/tests/tests.yaml |
488 | @@ -1,5 +1,12 @@ |
489 | -makefile: |
490 | - - lint |
491 | - - unittest |
492 | -packages: |
493 | - - amulet |
494 | +charm_name: graylog |
495 | +gate_bundles: |
496 | + - bionic |
497 | + - bionic-ha |
498 | +smoke_bundles: |
499 | + - bionic |
500 | +dev_bundles: |
501 | + - bionic |
502 | +configure: |
503 | + - zaza.charm_tests.noop.setup.basic_setup |
504 | +tests: |
505 | + - zaza.charm_tests.noop.tests.NoopTest |
506 | diff --git a/tox.ini b/tox.ini |
507 | index d92a339..a1f299d 100644 |
508 | --- a/tox.ini |
509 | +++ b/tox.ini |
510 | @@ -6,12 +6,18 @@ skip_missing_interpreters = True |
511 | [testenv] |
512 | basepython = python3 |
513 | setenv = PYTHONPATH={toxinidir}/reactive:{toxinidir}/lib/ |
514 | +whitelist_externals = juju |
515 | +passenv = HOME |
516 | |
517 | [testenv:unit] |
518 | commands = pytest -v --ignore {toxinidir}/tests --cov=lib --cov=reactive --cov=actions --cov-report=term-missing --cov-branch |
519 | deps = -r{toxinidir}/unit_tests/requirements.txt |
520 | -r{toxinidir}/requirements.txt |
521 | |
522 | +[testenv:func] |
523 | +commands = functest-run-suite --keep-model |
524 | +deps = -r{toxinidir}/tests/requirements.txt |
525 | + |
526 | [testenv:lint] |
527 | commands = flake8 |
528 | deps = flake8 |
529 | diff --git a/unit_tests/test_graylog.py b/unit_tests/test_graylog.py |
530 | index 9a25473..38ac6fa 100644 |
531 | --- a/unit_tests/test_graylog.py |
532 | +++ b/unit_tests/test_graylog.py |
533 | @@ -1,14 +1,21 @@ |
534 | import os |
535 | +import sys |
536 | import tempfile |
537 | import unittest |
538 | from unittest import mock |
539 | |
540 | from charms.layer.graylog.api import GraylogApi |
541 | + |
542 | +# charms.leadership only exists in the built charm; mock it out before |
543 | +# the graylog imports since those depend on charms.leadership |
544 | +layer_mock = mock.Mock() |
545 | +sys.modules['charms.leadership'] = layer_mock |
546 | + |
547 | from reactive.graylog import ( |
548 | set_conf, |
549 | set_jvm_heap_size, |
550 | - _check_input_exists) |
551 | -from files import check_graylog_health |
552 | + _check_input_exists) # noqa: E402 |
553 | +from files import check_graylog_health # noqa: E402 |
554 | |
555 | initial_conf = u"""#key1 = value1 |
556 | key2 = value2 |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.