Merge lp:~gnuoy/charms/trusty/ceph-radosgw/unit-tests into lp:~openstack-charmers-archive/charms/trusty/ceph-radosgw/next

Proposed by Liam Young
Status: Merged
Merged at revision: 36
Proposed branch: lp:~gnuoy/charms/trusty/ceph-radosgw/unit-tests
Merge into: lp:~openstack-charmers-archive/charms/trusty/ceph-radosgw/next
Diff against target: 788 lines (+728/-2)
8 files modified
.bzrignore (+2/-0)
.coveragerc (+7/-0)
Makefile (+4/-1)
hooks/utils.py (+3/-1)
unit_tests/__init__.py (+3/-0)
unit_tests/test_ceph.py (+197/-0)
unit_tests/test_hooks.py (+393/-0)
unit_tests/test_utils.py (+119/-0)
To merge this branch: bzr merge lp:~gnuoy/charms/trusty/ceph-radosgw/unit-tests
Reviewer Review Type Date Requested Status
OpenStack Charmers Pending
Review via email: mp+249167@code.launchpad.net
To post a comment you must log in.
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #1661 ceph-radosgw-next for gnuoy mp249167
    UNIT FAIL: unit-test failed

UNIT Results (max last 2 lines):
  FAILED (errors=1)
  make: *** [unit_test] Error 1

Full unit test output: http://paste.ubuntu.com/10156943/
Build: http://10.245.162.77:8080/job/charm_unit_test/1661/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #1833 ceph-radosgw-next for gnuoy mp249167
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/1833/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_amulet_test #1852 ceph-radosgw-next for gnuoy mp249167
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
  ERROR subprocess encountered error code 124
  make: *** [test] Error 124

Full amulet test output: http://paste.ubuntu.com/10157281/
Build: http://10.245.162.77:8080/job/charm_amulet_test/1852/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #1664 ceph-radosgw-next for gnuoy mp249167
    UNIT FAIL: unit-test failed

UNIT Results (max last 2 lines):
  FAILED (errors=1)
  make: *** [unit_test] Error 1

Full unit test output: http://paste.ubuntu.com/10158241/
Build: http://10.245.162.77:8080/job/charm_unit_test/1664/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #1666 ceph-radosgw-next for gnuoy mp249167
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/1666/

37. By Liam Young

Hack to patch out implicit install of python-dns and subsequent import of dns.python that is triggered by importing utils

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #1947 ceph-radosgw-next for gnuoy mp249167
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/1947/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #1737 ceph-radosgw-next for gnuoy mp249167
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/1737/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_amulet_test #1888 ceph-radosgw-next for gnuoy mp249167
    AMULET OK: passed

Build: http://10.245.162.77:8080/job/charm_amulet_test/1888/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2014-09-17 14:11:53 +0000
3+++ .bzrignore 2015-02-12 09:06:09 +0000
4@@ -1,3 +1,5 @@
5 .project
6 .pydevproject
7 bin
8+.coveragerc
9+.coverage
10
11=== added file '.coveragerc'
12--- .coveragerc 1970-01-01 00:00:00 +0000
13+++ .coveragerc 2015-02-12 09:06:09 +0000
14@@ -0,0 +1,7 @@
15+[report]
16+# Regexes for lines to exclude from consideration
17+exclude_lines =
18+ if __name__ == .__main__.:
19+include=
20+ hooks/ceph.py
21+ hooks/hooks.py
22
23=== modified file 'Makefile'
24--- Makefile 2014-09-29 01:57:43 +0000
25+++ Makefile 2015-02-12 09:06:09 +0000
26@@ -2,9 +2,12 @@
27 PYTHON := /usr/bin/env python
28
29 lint:
30- @flake8 --exclude hooks/charmhelpers hooks tests
31+ @flake8 --exclude hooks/charmhelpers hooks tests unit_tests
32 @charm proof
33
34+unit_test:
35+ @$(PYTHON) /usr/bin/nosetests --nologcapture --with-coverage unit_tests
36+
37 test:
38 @echo Starting Amulet tests...
39 # coreycb note: The -v should only be temporary until Amulet sends
40
41=== modified file 'hooks/utils.py'
42--- hooks/utils.py 2015-01-15 16:18:33 +0000
43+++ hooks/utils.py 2015-02-12 09:06:09 +0000
44@@ -83,8 +83,10 @@
45 sources.write(line)
46
47
48-def get_host_ip(hostname=unit_get('private-address')):
49+def get_host_ip(hostname=None):
50 try:
51+ if not hostname:
52+ hostname = unit_get('private-address')
53 # Test to see if already an IPv4 address
54 socket.inet_aton(hostname)
55 return hostname
56
57=== added directory 'unit_tests'
58=== added file 'unit_tests/__init__.py'
59--- unit_tests/__init__.py 1970-01-01 00:00:00 +0000
60+++ unit_tests/__init__.py 2015-02-12 09:06:09 +0000
61@@ -0,0 +1,3 @@
62+import sys
63+
64+sys.path.append('hooks/')
65
66=== added file 'unit_tests/test_ceph.py'
67--- unit_tests/test_ceph.py 1970-01-01 00:00:00 +0000
68+++ unit_tests/test_ceph.py 2015-02-12 09:06:09 +0000
69@@ -0,0 +1,197 @@
70+from test_utils import CharmTestCase
71+
72+import ceph
73+
74+TO_PATCH = [
75+ 'get_unit_hostname',
76+ 'os',
77+ 'subprocess',
78+ 'time',
79+]
80+
81+
82+class CephRadosGWCephTests(CharmTestCase):
83+
84+ def setUp(self):
85+ super(CephRadosGWCephTests, self).setUp(ceph, TO_PATCH)
86+
87+ def test_is_quorum_leader(self):
88+ self.os.path.exists.return_value = True
89+ self.get_unit_hostname.return_value = 'myhost'
90+ self.subprocess.check_output.return_value = '{"state": "leader"}'
91+ self.assertEqual(ceph.is_quorum(), True)
92+
93+ def test_is_quorum_notleader(self):
94+ self.os.path.exists.return_value = True
95+ self.get_unit_hostname.return_value = 'myhost'
96+ self.subprocess.check_output.return_value = '{"state": "notleader"}'
97+ self.assertEqual(ceph.is_quorum(), False)
98+
99+ def test_is_quorum_valerror(self):
100+ self.os.path.exists.return_value = True
101+ self.get_unit_hostname.return_value = 'myhost'
102+ self.subprocess.check_output.return_value = "'state': 'bob'}"
103+ self.assertEqual(ceph.is_quorum(), False)
104+
105+ def test_is_quorum_no_asok(self):
106+ self.os.path.exists.return_value = False
107+ self.assertEqual(ceph.is_quorum(), False)
108+
109+ def test_is_leader(self):
110+ self.get_unit_hostname.return_value = 'myhost'
111+ self.os.path.exists.return_value = True
112+ self.subprocess.check_output.return_value = '{"state": "leader"}'
113+ self.assertEqual(ceph.is_leader(), True)
114+
115+ def test_is_leader_notleader(self):
116+ self.get_unit_hostname.return_value = 'myhost'
117+ self.os.path.exists.return_value = True
118+ self.subprocess.check_output.return_value = '{"state": "notleader"}'
119+ self.assertEqual(ceph.is_leader(), False)
120+
121+ def test_is_leader_valerror(self):
122+ self.get_unit_hostname.return_value = 'myhost'
123+ self.os.path.exists.return_value = True
124+ self.subprocess.check_output.return_value = "'state': 'bob'}"
125+ self.assertEqual(ceph.is_leader(), False)
126+
127+ def test_is_leader_noasok(self):
128+ self.get_unit_hostname.return_value = 'myhost'
129+ self.os.path.exists.return_value = False
130+ self.assertEqual(ceph.is_leader(), False)
131+
132+ def test_wait_for_quorum_yes(self):
133+ results = [True, False]
134+
135+ def quorum():
136+ return results.pop()
137+ _is_quorum = self.patch('is_quorum')
138+ _is_quorum.side_effect = quorum
139+ ceph.wait_for_quorum()
140+ self.time.sleep.assert_called_with(3)
141+
142+ def test_wait_for_quorum_no(self):
143+ _is_quorum = self.patch('is_quorum')
144+ _is_quorum.return_value = True
145+ ceph.wait_for_quorum()
146+ self.assertFalse(self.time.sleep.called)
147+
148+ def test_wait_for_bootstrap(self):
149+ results = [True, False]
150+
151+ def bootstrapped():
152+ return results.pop()
153+ _is_bootstrapped = self.patch('is_bootstrapped')
154+ _is_bootstrapped.side_effect = bootstrapped
155+ ceph.wait_for_bootstrap()
156+ self.time.sleep.assert_called_with(3)
157+
158+ def test_add_bootstrap_hint(self):
159+ self.get_unit_hostname.return_value = 'myhost'
160+ cmd = [
161+ "ceph",
162+ "--admin-daemon",
163+ '/var/run/ceph/ceph-mon.myhost.asok',
164+ "add_bootstrap_peer_hint",
165+ 'mypeer'
166+ ]
167+ self.os.path.exists.return_value = True
168+ ceph.add_bootstrap_hint('mypeer')
169+ self.subprocess.call.assert_called_with(cmd)
170+
171+ def test_add_bootstrap_hint_noasok(self):
172+ self.get_unit_hostname.return_value = 'myhost'
173+ self.os.path.exists.return_value = False
174+ ceph.add_bootstrap_hint('mypeer')
175+ self.assertFalse(self.subprocess.call.called)
176+
177+ def test_is_osd_disk(self):
178+ # XXX Insert real sgdisk output
179+ self.subprocess.check_output.return_value = \
180+ 'Partition GUID code: 4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D'
181+ self.assertEqual(ceph.is_osd_disk('/dev/fmd0'), True)
182+
183+ def test_is_osd_disk_no(self):
184+ # XXX Insert real sgdisk output
185+ self.subprocess.check_output.return_value = \
186+ 'Partition GUID code: 5FBD7E29-9D25-41B8-AFD0-062C0CEFF05D'
187+ self.assertEqual(ceph.is_osd_disk('/dev/fmd0'), False)
188+
189+ def test_rescan_osd_devices(self):
190+ cmd = [
191+ 'udevadm', 'trigger',
192+ '--subsystem-match=block', '--action=add'
193+ ]
194+ ceph.rescan_osd_devices()
195+ self.subprocess.call.assert_called_with(cmd)
196+
197+ def test_zap_disk(self):
198+ cmd = [
199+ 'sgdisk', '--zap-all', '/dev/fmd0',
200+ ]
201+ ceph.zap_disk('/dev/fmd0')
202+ self.subprocess.check_call.assert_called_with(cmd)
203+
204+ def test_import_osd_bootstrap_key(self):
205+ self.os.path.exists.return_value = False
206+ cmd = [
207+ 'ceph-authtool',
208+ '/var/lib/ceph/bootstrap-osd/ceph.keyring',
209+ '--create-keyring',
210+ '--name=client.bootstrap-osd',
211+ '--add-key=mykey',
212+ ]
213+ ceph.import_osd_bootstrap_key('mykey')
214+ self.subprocess.check_call.assert_called_with(cmd)
215+
216+ def test_is_bootstrapped(self):
217+ self.os.path.exists.return_value = True
218+ self.assertEqual(ceph.is_bootstrapped(), True)
219+ self.os.path.exists.return_value = False
220+ self.assertEqual(ceph.is_bootstrapped(), False)
221+
222+ def test_import_radosgw_key(self):
223+ self.os.path.exists.return_value = False
224+ ceph.import_radosgw_key('mykey')
225+ cmd = [
226+ 'ceph-authtool',
227+ '/etc/ceph/keyring.rados.gateway',
228+ '--create-keyring',
229+ '--name=client.radosgw.gateway',
230+ '--add-key=mykey'
231+ ]
232+ self.subprocess.check_call.assert_called_with(cmd)
233+
234+ def test_get_named_key_create(self):
235+ self.get_unit_hostname.return_value = "myhost"
236+ self.subprocess.check_output.return_value = """
237+
238+[client.dummy]
239+ key = AQAPiu1RCMb4CxAAmP7rrufwZPRqy8bpQa2OeQ==
240+"""
241+ self.assertEqual(ceph.get_named_key('dummy'),
242+ 'AQAPiu1RCMb4CxAAmP7rrufwZPRqy8bpQa2OeQ==')
243+ cmd = [
244+ 'ceph',
245+ '--name', 'mon.',
246+ '--keyring',
247+ '/var/lib/ceph/mon/ceph-myhost/keyring',
248+ 'auth', 'get-or-create', 'client.dummy',
249+ 'mon', 'allow r', 'osd', 'allow rwx'
250+ ]
251+ self.subprocess.check_output.assert_called_with(cmd)
252+
253+ def test_get_named_key_get(self):
254+ self.get_unit_hostname.return_value = "myhost"
255+ key = "AQAPiu1RCMb4CxAAmP7rrufwZPRqy8bpQa2OeQ=="
256+ self.subprocess.check_output.return_value = key
257+ self.assertEqual(ceph.get_named_key('dummy'), key)
258+ cmd = [
259+ 'ceph',
260+ '--name', 'mon.',
261+ '--keyring',
262+ '/var/lib/ceph/mon/ceph-myhost/keyring',
263+ 'auth', 'get-or-create', 'client.dummy',
264+ 'mon', 'allow r', 'osd', 'allow rwx'
265+ ]
266+ self.subprocess.check_output.assert_called_with(cmd)
267
268=== added file 'unit_tests/test_hooks.py'
269--- unit_tests/test_hooks.py 1970-01-01 00:00:00 +0000
270+++ unit_tests/test_hooks.py 2015-02-12 09:06:09 +0000
271@@ -0,0 +1,393 @@
272+
273+from mock import call, patch, MagicMock
274+from test_utils import CharmTestCase, patch_open
275+
276+dnsmock = MagicMock()
277+modules = {
278+ 'dns': dnsmock,
279+ 'dns.resolver': dnsmock,
280+}
281+module_patcher = patch.dict('sys.modules', modules)
282+module_patcher.start()
283+with patch('charmhelpers.fetch.apt_install'):
284+ import utils
285+
286+_reg = utils.register_configs
287+
288+utils.register_configs = MagicMock()
289+
290+import hooks as ceph_hooks
291+
292+utils.register_configs = _reg
293+
294+TO_PATCH = [
295+ 'add_source',
296+ 'apt_update',
297+ 'apt_install',
298+ 'apt_purge',
299+ 'config',
300+ 'cmp_pkgrevno',
301+ 'execd_preinstall',
302+ 'enable_pocket',
303+ 'get_host_ip',
304+ 'get_iface_for_address',
305+ 'get_netmask_for_address',
306+ 'get_unit_hostname',
307+ 'glob',
308+ 'is_apache_24',
309+ 'log',
310+ 'lsb_release',
311+ 'open_port',
312+ 'os',
313+ 'related_units',
314+ 'relation_ids',
315+ 'relation_set',
316+ 'relation_get',
317+ 'render_template',
318+ 'resolve_address',
319+ 'shutil',
320+ 'subprocess',
321+ 'sys',
322+ 'unit_get',
323+]
324+
325+
326+class CephRadosGWTests(CharmTestCase):
327+
328+ def setUp(self):
329+ super(CephRadosGWTests, self).setUp(ceph_hooks, TO_PATCH)
330+ self.config.side_effect = self.test_config.get
331+ self.test_config.set('source', 'distro')
332+ self.test_config.set('key', 'secretkey')
333+ self.test_config.set('use-syslog', False)
334+
335+ def test_install_www_scripts(self):
336+ self.glob.glob.return_value = ['files/www/bob']
337+ ceph_hooks.install_www_scripts()
338+ self.shutil.copy.assert_called_with('files/www/bob', '/var/www/')
339+
340+ def test_install_ceph_optimised_packages(self):
341+ self.lsb_release.return_value = {'DISTRIB_CODENAME': 'vivid'}
342+ fastcgi_source = (
343+ 'http://gitbuilder.ceph.com/'
344+ 'libapache-mod-fastcgi-deb-vivid-x86_64-basic/ref/master')
345+ apache_source = (
346+ 'http://gitbuilder.ceph.com/'
347+ 'apache2-deb-vivid-x86_64-basic/ref/master')
348+ calls = [
349+ call(fastcgi_source, key='6EAEAE2203C3951A'),
350+ call(apache_source, key='6EAEAE2203C3951A'),
351+ ]
352+ ceph_hooks.install_ceph_optimised_packages()
353+ self.add_source.assert_has_calls(calls)
354+
355+ def test_install_packages(self):
356+ self.test_config.set('use-ceph-optimised-packages', '')
357+ ceph_hooks.install_packages()
358+ self.add_source.assert_called_with('distro', 'secretkey')
359+ self.apt_update.assert_called()
360+ self.apt_install.assert_called_with(['libapache2-mod-fastcgi',
361+ 'apache2'], fatal=True)
362+
363+ def test_install_optimised_packages_no_embedded(self):
364+ self.test_config.set('use-ceph-optimised-packages', True)
365+ self.test_config.set('use-embedded-webserver', False)
366+ _install_packages = self.patch('install_ceph_optimised_packages')
367+ ceph_hooks.install_packages()
368+ self.add_source.assert_called_with('distro', 'secretkey')
369+ self.apt_update.assert_called()
370+ _install_packages.assert_called()
371+ self.apt_install.assert_called_with(['libapache2-mod-fastcgi',
372+ 'apache2'], fatal=True)
373+
374+ def test_install_optimised_packages_embedded(self):
375+ self.test_config.set('use-ceph-optimised-packages', True)
376+ self.test_config.set('use-embedded-webserver', True)
377+ _install_packages = self.patch('install_ceph_optimised_packages')
378+ ceph_hooks.install_packages()
379+ self.add_source.assert_called_with('distro', 'secretkey')
380+ self.apt_update.assert_called()
381+ _install_packages.assert_called()
382+ self.apt_install.assert_called_with(['radosgw',
383+ 'ntp',
384+ 'haproxy'], fatal=True)
385+ self.apt_purge.assert_called_with(['libapache2-mod-fastcgi',
386+ 'apache2'])
387+
388+ def test_install(self):
389+ _install_packages = self.patch('install_packages')
390+ ceph_hooks.install()
391+ self.execd_preinstall.assert_called()
392+ _install_packages.assert_called()
393+ self.enable_pocket.assert_called_with('multiverse')
394+ self.os.makedirs.called_with('/var/lib/ceph/nss')
395+
396+ def test_emit_cephconf(self):
397+ _get_keystone_conf = self.patch('get_keystone_conf')
398+ _get_auth = self.patch('get_auth')
399+ _get_mon_hosts = self.patch('get_mon_hosts')
400+ _get_auth.return_value = 'cephx'
401+ _get_keystone_conf.return_value = {'keystone_key': 'keystone_value'}
402+ _get_mon_hosts.return_value = ['10.0.0.1:6789', '10.0.0.2:6789']
403+ self.get_unit_hostname.return_value = 'bob'
404+ self.os.path.exists.return_value = False
405+ cephcontext = {
406+ 'auth_supported': 'cephx',
407+ 'mon_hosts': '10.0.0.1:6789 10.0.0.2:6789',
408+ 'hostname': 'bob',
409+ 'old_auth': False,
410+ 'use_syslog': 'false',
411+ 'keystone_key': 'keystone_value',
412+ 'embedded_webserver': False,
413+ }
414+ self.cmp_pkgrevno.return_value = 1
415+ with patch_open() as (_open, _file):
416+ ceph_hooks.emit_cephconf()
417+ self.os.makedirs.assert_called_with('/etc/ceph')
418+ _open.assert_called_with('/etc/ceph/ceph.conf', 'w')
419+ self.render_template.assert_called_with('ceph.conf', cephcontext)
420+
421+ def test_emit_apacheconf(self):
422+ self.is_apache_24.return_value = True
423+ self.unit_get.return_value = '10.0.0.1'
424+ apachecontext = {
425+ "hostname": '10.0.0.1',
426+ }
427+ vhost_file = '/etc/apache2/sites-available/rgw.conf'
428+ with patch_open() as (_open, _file):
429+ ceph_hooks.emit_apacheconf()
430+ _open.assert_called_with(vhost_file, 'w')
431+ self.render_template.assert_called_with('rgw', apachecontext)
432+
433+ def test_apache_sites24(self):
434+ self.is_apache_24.return_value = True
435+ ceph_hooks.apache_sites()
436+ calls = [
437+ call(['a2dissite', '000-default']),
438+ call(['a2ensite', 'rgw']),
439+ ]
440+ self.subprocess.check_call.assert_has_calls(calls)
441+
442+ def test_apache_sites22(self):
443+ self.is_apache_24.return_value = False
444+ ceph_hooks.apache_sites()
445+ calls = [
446+ call(['a2dissite', 'default']),
447+ call(['a2ensite', 'rgw']),
448+ ]
449+ self.subprocess.check_call.assert_has_calls(calls)
450+
451+ def test_apache_modules(self):
452+ ceph_hooks.apache_modules()
453+ calls = [
454+ call(['a2enmod', 'fastcgi']),
455+ call(['a2enmod', 'rewrite']),
456+ ]
457+ self.subprocess.check_call.assert_has_calls(calls)
458+
459+ def test_apache_reload(self):
460+ ceph_hooks.apache_reload()
461+ calls = [
462+ call(['service', 'apache2', 'reload']),
463+ ]
464+ self.subprocess.call.assert_has_calls(calls)
465+
466+ def test_config_changed(self):
467+ _install_packages = self.patch('install_packages')
468+ _emit_cephconf = self.patch('emit_cephconf')
469+ _emit_apacheconf = self.patch('emit_apacheconf')
470+ _install_www_scripts = self.patch('install_www_scripts')
471+ _apache_sites = self.patch('apache_sites')
472+ _apache_modules = self.patch('apache_modules')
473+ _apache_reload = self.patch('apache_reload')
474+ ceph_hooks.config_changed()
475+ _install_packages.assert_called()
476+ _emit_cephconf.assert_called()
477+ _emit_apacheconf.assert_called()
478+ _install_www_scripts.assert_called()
479+ _apache_sites.assert_called()
480+ _apache_modules.assert_called()
481+ _apache_reload.assert_called()
482+
483+ def test_get_mon_hosts(self):
484+ self.relation_ids.return_value = ['monrelid']
485+ self.related_units.return_value = ['monunit']
486+ self.relation_get.return_value = '10.0.0.1'
487+ self.get_host_ip.return_value = '10.0.0.1'
488+ self.assertEquals(ceph_hooks.get_mon_hosts(), ['10.0.0.1:6789'])
489+
490+ def test_get_conf(self):
491+ self.relation_ids.return_value = ['monrelid']
492+ self.related_units.return_value = ['monunit']
493+ self.relation_get.return_value = 'bob'
494+ self.assertEquals(ceph_hooks.get_conf('key'), 'bob')
495+
496+ def test_get_conf_nomatch(self):
497+ self.relation_ids.return_value = ['monrelid']
498+ self.related_units.return_value = ['monunit']
499+ self.relation_get.return_value = ''
500+ self.assertEquals(ceph_hooks.get_conf('key'), None)
501+
502+ def test_get_auth(self):
503+ self.relation_ids.return_value = ['monrelid']
504+ self.related_units.return_value = ['monunit']
505+ self.relation_get.return_value = 'bob'
506+ self.assertEquals(ceph_hooks.get_auth(), 'bob')
507+
508+ def test_get_keystone_conf(self):
509+ self.test_config.set('operator-roles', 'admin')
510+ self.test_config.set('cache-size', '42')
511+ self.test_config.set('revocation-check-interval', '21')
512+ self.relation_ids.return_value = ['idrelid']
513+ self.related_units.return_value = ['idunit']
514+
515+ def _relation_get(key, unit, relid):
516+ ks_dict = {
517+ 'auth_protocol': 'https',
518+ 'auth_host': '10.0.0.2',
519+ 'auth_port': '8090',
520+ 'admin_token': 'sectocken',
521+ }
522+ return ks_dict[key]
523+ self.relation_get.side_effect = _relation_get
524+ self.assertEquals(ceph_hooks.get_keystone_conf(), {
525+ 'auth_type': 'keystone',
526+ 'auth_protocol': 'https',
527+ 'admin_token': 'sectocken',
528+ 'user_roles': 'admin',
529+ 'auth_host': '10.0.0.2',
530+ 'cache_size': '42',
531+ 'auth_port': '8090',
532+ 'revocation_check_interval': '21'})
533+
534+ def test_get_keystone_conf_missinginfo(self):
535+ self.test_config.set('operator-roles', 'admin')
536+ self.test_config.set('cache-size', '42')
537+ self.test_config.set('revocation-check-interval', '21')
538+ self.relation_ids.return_value = ['idrelid']
539+ self.related_units.return_value = ['idunit']
540+
541+ def _relation_get(key, unit, relid):
542+ ks_dict = {
543+ 'auth_protocol': 'https',
544+ 'auth_host': '10.0.0.2',
545+ 'auth_port': '8090',
546+ }
547+ return ks_dict[key] if key in ks_dict else None
548+ self.relation_get.side_effect = _relation_get
549+ self.assertEquals(ceph_hooks.get_keystone_conf(), None)
550+
551+ def test_mon_relation(self):
552+ _emit_cephconf = self.patch('emit_cephconf')
553+ _ceph = self.patch('ceph')
554+ _restart = self.patch('restart')
555+ self.relation_get.return_value = 'seckey'
556+ ceph_hooks.mon_relation()
557+ _restart.assert_called()
558+ _ceph.import_radosgw_key.assert_called_with('seckey')
559+ _emit_cephconf.assert_called()
560+
561+ def test_mon_relation_nokey(self):
562+ _emit_cephconf = self.patch('emit_cephconf')
563+ _ceph = self.patch('ceph')
564+ _restart = self.patch('restart')
565+ self.relation_get.return_value = None
566+ ceph_hooks.mon_relation()
567+ self.assertFalse(_ceph.import_radosgw_key.called)
568+ self.assertFalse(_restart.called)
569+ _emit_cephconf.assert_called()
570+
571+ def test_gateway_relation(self):
572+ self.unit_get.return_value = 'myserver'
573+ ceph_hooks.gateway_relation()
574+ self.relation_set.assert_called_with(hostname='myserver', port=80)
575+
576+ def test_start(self):
577+ ceph_hooks.start()
578+ cmd = ['service', 'radosgw', 'start']
579+ self.subprocess.call.assert_called_with(cmd)
580+
581+ def test_stop(self):
582+ ceph_hooks.stop()
583+ cmd = ['service', 'radosgw', 'stop']
584+ self.subprocess.call.assert_called_with(cmd)
585+
586+ def test_restart(self):
587+ ceph_hooks.restart()
588+ cmd = ['service', 'radosgw', 'restart']
589+ self.subprocess.call.assert_called_with(cmd)
590+
591+ def test_identity_joined_early_version(self):
592+ self.cmp_pkgrevno.return_value = -1
593+ ceph_hooks.identity_joined()
594+ self.sys.exit.assert_called_with(1)
595+
596+ def test_identity_joined(self):
597+ self.cmp_pkgrevno.return_value = 1
598+ self.resolve_address.return_value = 'myserv'
599+ self.test_config.set('region', 'region1')
600+ self.test_config.set('operator-roles', 'admin')
601+ self.unit_get.return_value = 'myserv'
602+ ceph_hooks.identity_joined(relid='rid')
603+ self.relation_set.assert_called_with(
604+ service='swift',
605+ region='region1',
606+ public_url='http://myserv:80/swift/v1',
607+ internal_url='http://myserv:80/swift/v1',
608+ requested_roles='admin',
609+ relation_id='rid',
610+ admin_url='http://myserv:80/swift')
611+
612+ def test_identity_changed(self):
613+ _emit_cephconf = self.patch('emit_cephconf')
614+ _restart = self.patch('restart')
615+ ceph_hooks.identity_changed()
616+ _emit_cephconf.assert_called()
617+ _restart.assert_called()
618+
619+ def test_canonical_url_ipv6(self):
620+ ipv6_addr = '2001:db8:85a3:8d3:1319:8a2e:370:7348'
621+ self.resolve_address.return_value = ipv6_addr
622+ self.assertEquals(ceph_hooks.canonical_url({}),
623+ 'http://[%s]' % ipv6_addr)
624+
625+ @patch.object(ceph_hooks, 'CONFIGS')
626+ def test_cluster_changed(self, configs):
627+ _id_joined = self.patch('identity_joined')
628+ self.relation_ids.return_value = ['rid']
629+ ceph_hooks.cluster_changed()
630+ configs.write_all.assert_called()
631+ _id_joined.assert_called_with(relid='rid')
632+
633+ def test_ha_relation_joined_no_vip(self):
634+ self.test_config.set('vip', '')
635+ ceph_hooks.ha_relation_joined()
636+ self.sys.exit.assert_called_with(1)
637+
638+ def test_ha_relation_joined_vip(self):
639+ self.test_config.set('ha-bindiface', 'eth8')
640+ self.test_config.set('ha-mcastport', '5000')
641+ self.test_config.set('vip', '10.0.0.10')
642+ self.get_iface_for_address.return_value = 'eth7'
643+ self.get_netmask_for_address.return_value = '255.255.0.0'
644+ ceph_hooks.ha_relation_joined()
645+ eth_params = ('params ip="10.0.0.10" cidr_netmask="255.255.0.0" '
646+ 'nic="eth7"')
647+ resources = {'res_cephrg_haproxy': 'lsb:haproxy',
648+ 'res_cephrg_eth7_vip': 'ocf:heartbeat:IPaddr2'}
649+ resource_params = {'res_cephrg_haproxy': 'op monitor interval="5s"',
650+ 'res_cephrg_eth7_vip': eth_params}
651+ self.relation_set.assert_called_with(
652+ init_services={'res_cephrg_haproxy': 'haproxy'},
653+ corosync_bindiface='eth8',
654+ corosync_mcastport='5000',
655+ resource_params=resource_params,
656+ resources=resources,
657+ clones={'cl_cephrg_haproxy': 'res_cephrg_haproxy'})
658+
659+ def test_ha_relation_changed(self):
660+ _id_joined = self.patch('identity_joined')
661+ self.relation_get.return_value = True
662+ self.relation_ids.return_value = ['rid']
663+ ceph_hooks.ha_relation_changed()
664+ _id_joined.assert_called_with(relid='rid')
665
666=== added file 'unit_tests/test_utils.py'
667--- unit_tests/test_utils.py 1970-01-01 00:00:00 +0000
668+++ unit_tests/test_utils.py 2015-02-12 09:06:09 +0000
669@@ -0,0 +1,119 @@
670+import logging
671+import os
672+import unittest
673+import yaml
674+
675+from contextlib import contextmanager
676+from mock import patch, MagicMock
677+
678+
679+def load_config():
680+ '''Walk backwords from __file__ looking for config.yaml,
681+ load and return the 'options' section'
682+ '''
683+ config = None
684+ f = __file__
685+ while config is None:
686+ d = os.path.dirname(f)
687+ if os.path.isfile(os.path.join(d, 'config.yaml')):
688+ config = os.path.join(d, 'config.yaml')
689+ break
690+ f = d
691+
692+ if not config:
693+ logging.error('Could not find config.yaml in any parent directory '
694+ 'of %s. ' % file)
695+ raise Exception
696+
697+ return yaml.safe_load(open(config).read())['options']
698+
699+
700+def get_default_config():
701+ '''Load default charm config from config.yaml return as a dict.
702+ If no default is set in config.yaml, its value is None.
703+ '''
704+ default_config = {}
705+ config = load_config()
706+ for k, v in config.iteritems():
707+ if 'default' in v:
708+ default_config[k] = v['default']
709+ else:
710+ default_config[k] = None
711+ return default_config
712+
713+
714+class CharmTestCase(unittest.TestCase):
715+
716+ def setUp(self, obj, patches):
717+ super(CharmTestCase, self).setUp()
718+ self.patches = patches
719+ self.obj = obj
720+ self.test_config = TestConfig()
721+ self.test_relation = TestRelation()
722+ self.patch_all()
723+
724+ def patch(self, method):
725+ _m = patch.object(self.obj, method)
726+ mock = _m.start()
727+ self.addCleanup(_m.stop)
728+ return mock
729+
730+ def patch_all(self):
731+ for method in self.patches:
732+ setattr(self, method, self.patch(method))
733+
734+
735+class TestConfig(object):
736+
737+ def __init__(self):
738+ self.config = get_default_config()
739+
740+ def get(self, attr=None):
741+ if not attr:
742+ return self.get_all()
743+ try:
744+ return self.config[attr]
745+ except KeyError:
746+ return None
747+
748+ def get_all(self):
749+ return self.config
750+
751+ def set(self, attr, value):
752+ if attr not in self.config:
753+ raise KeyError
754+ self.config[attr] = value
755+
756+
757+class TestRelation(object):
758+
759+ def __init__(self, relation_data={}):
760+ self.relation_data = relation_data
761+
762+ def set(self, relation_data):
763+ self.relation_data = relation_data
764+
765+ def get(self, attr=None, unit=None, rid=None):
766+ if attr is None:
767+ return self.relation_data
768+ elif attr in self.relation_data:
769+ return self.relation_data[attr]
770+ return None
771+
772+
773+@contextmanager
774+def patch_open():
775+ '''Patch open() to allow mocking both open() itself and the file that is
776+ yielded.
777+ Yields the mock for "open" and "file", respectively.
778+ '''
779+ mock_open = MagicMock(spec=open)
780+ mock_file = MagicMock(spec=file)
781+
782+ @contextmanager
783+ def stub_open(*args, **kwargs):
784+ mock_open(*args, **kwargs)
785+ yield mock_file
786+
787+ with patch('__builtin__.open', stub_open):
788+ yield mock_open, mock_file

Subscribers

People subscribed via source and target branches