Merge lp:~stub/charm-helpers/integration into lp:charm-helpers

Proposed by Stuart Bishop
Status: Merged
Merged at revision: 371
Proposed branch: lp:~stub/charm-helpers/integration
Merge into: lp:charm-helpers
Diff against target: 423 lines (+120/-37)
11 files modified
charmhelpers/contrib/peerstorage/__init__.py (+2/-0)
charmhelpers/contrib/unison/__init__.py (+5/-4)
charmhelpers/core/hookenv.py (+18/-4)
charmhelpers/core/host.py (+1/-1)
charmhelpers/core/services/base.py (+2/-2)
charmhelpers/fetch/__init__.py (+1/-1)
tests/contrib/peerstorage/test_peerstorage.py (+8/-0)
tests/contrib/unison/test_unison.py (+7/-5)
tests/core/test_hookenv.py (+51/-5)
tests/core/test_services.py (+12/-0)
tests/core/test_templating.py (+13/-15)
To merge this branch: bzr merge lp:~stub/charm-helpers/integration
Reviewer Review Type Date Requested Status
Liam Young (community) Approve
Review via email: mp+257748@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Liam Young (gnuoy) wrote :

Approve

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charmhelpers/contrib/peerstorage/__init__.py'
--- charmhelpers/contrib/peerstorage/__init__.py 2015-01-22 06:06:03 +0000
+++ charmhelpers/contrib/peerstorage/__init__.py 2015-04-29 13:11:45 +0000
@@ -73,6 +73,8 @@
73 exc_list = exc_list if exc_list else []73 exc_list = exc_list if exc_list else []
74 peerdb_settings = peer_retrieve('-', relation_name=relation_name)74 peerdb_settings = peer_retrieve('-', relation_name=relation_name)
75 matched = {}75 matched = {}
76 if peerdb_settings is None:
77 return matched
76 for k, v in peerdb_settings.items():78 for k, v in peerdb_settings.items():
77 full_prefix = prefix + delimiter79 full_prefix = prefix + delimiter
78 if k.startswith(full_prefix):80 if k.startswith(full_prefix):
7981
=== modified file 'charmhelpers/contrib/unison/__init__.py'
--- charmhelpers/contrib/unison/__init__.py 2015-01-28 22:09:21 +0000
+++ charmhelpers/contrib/unison/__init__.py 2015-04-29 13:11:45 +0000
@@ -63,6 +63,7 @@
63from charmhelpers.core.host import (63from charmhelpers.core.host import (
64 adduser,64 adduser,
65 add_user_to_group,65 add_user_to_group,
66 pwgen,
66)67)
6768
68from charmhelpers.core.hookenv import (69from charmhelpers.core.hookenv import (
@@ -140,7 +141,7 @@
140 ssh_dir = os.path.join(home_dir, '.ssh')141 ssh_dir = os.path.join(home_dir, '.ssh')
141 auth_keys = os.path.join(ssh_dir, 'authorized_keys')142 auth_keys = os.path.join(ssh_dir, 'authorized_keys')
142 log('Syncing authorized_keys @ %s.' % auth_keys)143 log('Syncing authorized_keys @ %s.' % auth_keys)
143 with open(auth_keys, 'wb') as out:144 with open(auth_keys, 'w') as out:
144 for k in keys:145 for k in keys:
145 out.write('%s\n' % k)146 out.write('%s\n' % k)
146147
@@ -152,16 +153,16 @@
152 khosts = []153 khosts = []
153 for host in hosts:154 for host in hosts:
154 cmd = ['ssh-keyscan', '-H', '-t', 'rsa', host]155 cmd = ['ssh-keyscan', '-H', '-t', 'rsa', host]
155 remote_key = check_output(cmd).strip()156 remote_key = check_output(cmd, universal_newlines=True).strip()
156 khosts.append(remote_key)157 khosts.append(remote_key)
157 log('Syncing known_hosts @ %s.' % known_hosts)158 log('Syncing known_hosts @ %s.' % known_hosts)
158 with open(known_hosts, 'wb') as out:159 with open(known_hosts, 'w') as out:
159 for host in khosts:160 for host in khosts:
160 out.write('%s\n' % host)161 out.write('%s\n' % host)
161162
162163
163def ensure_user(user, group=None):164def ensure_user(user, group=None):
164 adduser(user)165 adduser(user, pwgen())
165 if group:166 if group:
166 add_user_to_group(user, group)167 add_user_to_group(user, group)
167168
168169
=== modified file 'charmhelpers/core/hookenv.py'
--- charmhelpers/core/hookenv.py 2015-04-07 16:12:51 +0000
+++ charmhelpers/core/hookenv.py 2015-04-29 13:11:45 +0000
@@ -21,6 +21,7 @@
21# Charm Helpers Developers <juju@lists.ubuntu.com>21# Charm Helpers Developers <juju@lists.ubuntu.com>
2222
23from __future__ import print_function23from __future__ import print_function
24from functools import wraps
24import os25import os
25import json26import json
26import yaml27import yaml
@@ -58,15 +59,17 @@
5859
59 will cache the result of unit_get + 'test' for future calls.60 will cache the result of unit_get + 'test' for future calls.
60 """61 """
62 @wraps(func)
61 def wrapper(*args, **kwargs):63 def wrapper(*args, **kwargs):
62 global cache64 global cache
63 key = str((func, args, kwargs))65 key = str((func, args, kwargs))
64 try:66 try:
65 return cache[key]67 return cache[key]
66 except KeyError:68 except KeyError:
67 res = func(*args, **kwargs)69 pass # Drop out of the exception handler scope.
68 cache[key] = res70 res = func(*args, **kwargs)
69 return res71 cache[key] = res
72 return res
70 return wrapper73 return wrapper
7174
7275
@@ -178,7 +181,7 @@
178181
179def remote_unit():182def remote_unit():
180 """The remote unit for the current relation hook"""183 """The remote unit for the current relation hook"""
181 return os.environ['JUJU_REMOTE_UNIT']184 return os.environ.get('JUJU_REMOTE_UNIT', None)
182185
183186
184def service_name():187def service_name():
@@ -250,6 +253,12 @@
250 except KeyError:253 except KeyError:
251 return (self._prev_dict or {})[key]254 return (self._prev_dict or {})[key]
252255
256 def get(self, key, default=None):
257 try:
258 return self[key]
259 except KeyError:
260 return default
261
253 def keys(self):262 def keys(self):
254 prev_keys = []263 prev_keys = []
255 if self._prev_dict is not None:264 if self._prev_dict is not None:
@@ -509,6 +518,11 @@
509 return None518 return None
510519
511520
521def unit_public_ip():
522 """Get this unit's public IP address"""
523 return unit_get('public-address')
524
525
512def unit_private_ip():526def unit_private_ip():
513 """Get this unit's private IP address"""527 """Get this unit's private IP address"""
514 return unit_get('private-address')528 return unit_get('private-address')
515529
=== modified file 'charmhelpers/core/host.py'
--- charmhelpers/core/host.py 2015-03-12 10:56:57 +0000
+++ charmhelpers/core/host.py 2015-04-29 13:11:45 +0000
@@ -90,7 +90,7 @@
90 ['service', service_name, 'status'],90 ['service', service_name, 'status'],
91 stderr=subprocess.STDOUT).decode('UTF-8')91 stderr=subprocess.STDOUT).decode('UTF-8')
92 except subprocess.CalledProcessError as e:92 except subprocess.CalledProcessError as e:
93 return 'unrecognized service' not in e.output93 return b'unrecognized service' not in e.output
94 else:94 else:
95 return True95 return True
9696
9797
=== modified file 'charmhelpers/core/services/base.py'
--- charmhelpers/core/services/base.py 2015-01-22 06:06:03 +0000
+++ charmhelpers/core/services/base.py 2015-04-29 13:11:45 +0000
@@ -17,7 +17,7 @@
17import os17import os
18import re18import re
19import json19import json
20from collections import Iterable20from collections import Iterable, OrderedDict
2121
22from charmhelpers.core import host22from charmhelpers.core import host
23from charmhelpers.core import hookenv23from charmhelpers.core import hookenv
@@ -119,7 +119,7 @@
119 """119 """
120 self._ready_file = os.path.join(hookenv.charm_dir(), 'READY-SERVICES.json')120 self._ready_file = os.path.join(hookenv.charm_dir(), 'READY-SERVICES.json')
121 self._ready = None121 self._ready = None
122 self.services = {}122 self.services = OrderedDict()
123 for service in services or []:123 for service in services or []:
124 service_name = service['service']124 service_name = service['service']
125 self.services[service_name] = service125 self.services[service_name] = service
126126
=== modified file 'charmhelpers/fetch/__init__.py'
--- charmhelpers/fetch/__init__.py 2015-01-22 06:11:15 +0000
+++ charmhelpers/fetch/__init__.py 2015-04-29 13:11:45 +0000
@@ -158,7 +158,7 @@
158158
159def apt_cache(in_memory=True):159def apt_cache(in_memory=True):
160 """Build and return an apt cache"""160 """Build and return an apt cache"""
161 import apt_pkg161 from apt import apt_pkg
162 apt_pkg.init()162 apt_pkg.init()
163 if in_memory:163 if in_memory:
164 apt_pkg.config.set("Dir::Cache::pkgcache", "")164 apt_pkg.config.set("Dir::Cache::pkgcache", "")
165165
=== modified file 'tests/contrib/peerstorage/test_peerstorage.py'
--- tests/contrib/peerstorage/test_peerstorage.py 2014-08-19 09:38:07 +0000
+++ tests/contrib/peerstorage/test_peerstorage.py 2015-04-29 13:11:45 +0000
@@ -146,6 +146,14 @@
146 self.assertEquals(peerstorage.peer_retrieve_by_prefix(rel_id), settings)146 self.assertEquals(peerstorage.peer_retrieve_by_prefix(rel_id), settings)
147147
148 @patch.object(peerstorage, 'peer_retrieve')148 @patch.object(peerstorage, 'peer_retrieve')
149 def test_peer_retrieve_by_prefix_empty_relation(self, peer_retrieve):
150 # If relation-get returns None, peer_retrieve_by_prefix returns
151 # an empty dictionary.
152 peer_retrieve.return_value = None
153 rel_id = 'db:2'
154 self.assertEquals(peerstorage.peer_retrieve_by_prefix(rel_id), {})
155
156 @patch.object(peerstorage, 'peer_retrieve')
149 def test_peer_retrieve_by_prefix_exc_list(self, peer_retrieve):157 def test_peer_retrieve_by_prefix_exc_list(self, peer_retrieve):
150 rel_id = 'db:2'158 rel_id = 'db:2'
151 settings = {159 settings = {
152160
=== modified file 'tests/contrib/unison/test_unison.py'
--- tests/contrib/unison/test_unison.py 2015-01-05 10:30:39 +0000
+++ tests/contrib/unison/test_unison.py 2015-04-29 13:11:45 +0000
@@ -1,5 +1,5 @@
11
2from mock import call, patch, MagicMock2from mock import call, patch, MagicMock, sentinel
3from testtools import TestCase3from testtools import TestCase
44
5from tests.helpers import patch_open, FakeRelation5from tests.helpers import patch_open, FakeRelation
@@ -146,7 +146,7 @@
146 ]146 ]
147 with patch_open() as (_open, _file):147 with patch_open() as (_open, _file):
148 unison.write_authorized_keys('foo', keys)148 unison.write_authorized_keys('foo', keys)
149 _open.assert_called_with('/home/foo/.ssh/authorized_keys', 'wb')149 _open.assert_called_with('/home/foo/.ssh/authorized_keys', 'w')
150 for k in keys:150 for k in keys:
151 self.assertIn(call('%s\n' % k), _file.write.call_args_list)151 self.assertIn(call('%s\n' % k), _file.write.call_args_list)
152152
@@ -160,15 +160,17 @@
160 self.check_output.side_effect = keys160 self.check_output.side_effect = keys
161 with patch_open() as (_open, _file):161 with patch_open() as (_open, _file):
162 unison.write_known_hosts('foo', ['10.0.0.1', '10.0.0.2'])162 unison.write_known_hosts('foo', ['10.0.0.1', '10.0.0.2'])
163 _open.assert_called_with('/home/foo/.ssh/known_hosts', 'wb')163 _open.assert_called_with('/home/foo/.ssh/known_hosts', 'w')
164 for k in keys:164 for k in keys:
165 self.assertIn(call('%s\n' % k), _file.write.call_args_list)165 self.assertIn(call('%s\n' % k), _file.write.call_args_list)
166166
167 @patch.object(unison, 'pwgen')
167 @patch.object(unison, 'add_user_to_group')168 @patch.object(unison, 'add_user_to_group')
168 @patch.object(unison, 'adduser')169 @patch.object(unison, 'adduser')
169 def test_ensure_user(self, adduser, to_group):170 def test_ensure_user(self, adduser, to_group, pwgen):
171 pwgen.return_value = sentinel.password
170 unison.ensure_user('foo', group='foobar')172 unison.ensure_user('foo', group='foobar')
171 adduser.assert_called_with('foo')173 adduser.assert_called_with('foo', sentinel.password)
172 to_group.assert_called_with('foo', 'foobar')174 to_group.assert_called_with('foo', 'foobar')
173175
174 @patch.object(unison, '_run_as_user')176 @patch.object(unison, '_run_as_user')
175177
=== modified file 'tests/core/test_hookenv.py'
--- tests/core/test_hookenv.py 2015-03-12 10:31:22 +0000
+++ tests/core/test_hookenv.py 2015-04-29 13:11:45 +0000
@@ -3,8 +3,7 @@
3from subprocess import CalledProcessError3from subprocess import CalledProcessError
4import shutil4import shutil
5import tempfile5import tempfile
6from mock import patch, call, mock_open6from mock import call, MagicMock, mock_open, patch, sentinel
7from mock import MagicMock
8from testtools import TestCase7from testtools import TestCase
9import yaml8import yaml
109
@@ -128,11 +127,47 @@
128 self.assertEqual(c['foo'], 'bar')127 self.assertEqual(c['foo'], 'bar')
129 self.assertEqual(c['baz'], 'bam')128 self.assertEqual(c['baz'], 'bam')
130129
130 def test_get(self):
131 c = hookenv.Config(dict(foo='bar'))
132 c.save()
133 c = hookenv.Config(dict(baz='bam'))
134
135 self.assertIsNone(c.get('missing'))
136 self.assertIs(c.get('missing', sentinel.missing), sentinel.missing)
137 self.assertEqual(c.get('foo'), 'bar')
138 self.assertEqual(c.get('baz'), 'bam')
139
131 def test_keys(self):140 def test_keys(self):
132 c = hookenv.Config(dict(foo='bar'))141 c = hookenv.Config(dict(foo='bar'))
133 c["baz"] = "bar"142 c["baz"] = "bar"
134 self.assertEqual(sorted([six.u("foo"), "baz"]), sorted(c.keys()))143 self.assertEqual(sorted([six.u("foo"), "baz"]), sorted(c.keys()))
135144
145 def test_in(self):
146 # Test behavior of the in operator.
147
148 # Items that exist in the dict exist. Items that don't don't.
149 c = hookenv.Config(dict(foo='one'))
150 self.assertTrue('foo' in c)
151 self.assertTrue('bar' not in c)
152 c.save()
153 self.assertTrue('foo' in c)
154 self.assertTrue('bar' not in c)
155
156 # Adding items works as expected.
157 c['foo'] = 'two'
158 c['bar'] = 'two'
159 self.assertTrue('foo' in c)
160 self.assertTrue('bar' in c)
161 c.save()
162 self.assertTrue('foo' in c)
163 self.assertTrue('bar' in c)
164
165 # Removing items works as expected.
166 del c['foo']
167 self.assertTrue('foo' not in c)
168 c.save()
169 self.assertTrue('foo' not in c)
170
136171
137class SerializableTest(TestCase):172class SerializableTest(TestCase):
138 def test_serializes_object_to_json(self):173 def test_serializes_object_to_json(self):
@@ -298,10 +333,16 @@
298 self.assertEqual(hookenv.local_unit(), 'foo')333 self.assertEqual(hookenv.local_unit(), 'foo')
299334
300 @patch('charmhelpers.core.hookenv.unit_get')335 @patch('charmhelpers.core.hookenv.unit_get')
336 def test_gets_unit_public_ip(self, _unitget):
337 _unitget.return_value = sentinel.public_ip
338 self.assertEqual(sentinel.public_ip, hookenv.unit_public_ip())
339 _unitget.assert_called_once_with('public-address')
340
341 @patch('charmhelpers.core.hookenv.unit_get')
301 def test_gets_unit_private_ip(self, _unitget):342 def test_gets_unit_private_ip(self, _unitget):
302 _unitget.return_value = 'foo'343 _unitget.return_value = sentinel.private_ip
303 self.assertEqual("foo", hookenv.unit_private_ip())344 self.assertEqual(sentinel.private_ip, hookenv.unit_private_ip())
304 _unitget.assert_called_with('private-address')345 _unitget.assert_called_once_with('private-address')
305346
306 @patch('charmhelpers.core.hookenv.os')347 @patch('charmhelpers.core.hookenv.os')
307 def test_checks_that_is_running_in_relation_hook(self, os_):348 def test_checks_that_is_running_in_relation_hook(self, os_):
@@ -447,6 +488,11 @@
447488
448 self.assertEqual(hookenv.remote_unit(), 'foo')489 self.assertEqual(hookenv.remote_unit(), 'foo')
449490
491 @patch('charmhelpers.core.hookenv.os')
492 def test_no_remote_unit(self, os_):
493 os_.environ = {}
494 self.assertEqual(hookenv.remote_unit(), None)
495
450 @patch('charmhelpers.core.hookenv.remote_unit')496 @patch('charmhelpers.core.hookenv.remote_unit')
451 @patch('charmhelpers.core.hookenv.relation_get')497 @patch('charmhelpers.core.hookenv.relation_get')
452 def test_gets_relation_for_unit(self, relation_get, remote_unit):498 def test_gets_relation_for_unit(self, relation_get, remote_unit):
453499
=== modified file 'tests/core/test_services.py'
--- tests/core/test_services.py 2015-03-19 15:00:13 +0000
+++ tests/core/test_services.py 2015-04-29 13:11:45 +0000
@@ -27,6 +27,18 @@
27 'qux': 'baz'},27 'qux': 'baz'},
28 })28 })
2929
30 def test_register_preserves_order(self):
31 service_list = [dict(service='a'), dict(service='b')]
32
33 # Test that the services list order is preserved by checking
34 # both forwards and backwards - only one of these will be
35 # dictionary order, and if both work we know order is being
36 # preserved.
37 manager = services.ServiceManager(service_list)
38 self.assertEqual(list(manager.services.keys()), ['a', 'b'])
39 manager = services.ServiceManager(reversed(service_list))
40 self.assertEqual(list(manager.services.keys()), ['b', 'a'])
41
30 @mock.patch.object(services.ServiceManager, 'reconfigure_services')42 @mock.patch.object(services.ServiceManager, 'reconfigure_services')
31 @mock.patch.object(services.ServiceManager, 'stop_services')43 @mock.patch.object(services.ServiceManager, 'stop_services')
32 @mock.patch.object(hookenv, 'hook_name')44 @mock.patch.object(hookenv, 'hook_name')
3345
=== modified file 'tests/core/test_templating.py'
--- tests/core/test_templating.py 2015-02-03 21:52:36 +0000
+++ tests/core/test_templating.py 2015-04-29 13:11:45 +0000
@@ -1,9 +1,9 @@
1import os
2import pkg_resources1import pkg_resources
3import shutil2import shutil
4import tempfile3import tempfile
5import unittest4import unittest
6import jinja25import jinja2
6import os.path
7import pwd7import pwd
8import grp8import grp
99
@@ -17,7 +17,8 @@
17class TestTemplating(unittest.TestCase):17class TestTemplating(unittest.TestCase):
18 def setUp(self):18 def setUp(self):
19 self.charm_dir = pkg_resources.resource_filename(__name__, '')19 self.charm_dir = pkg_resources.resource_filename(__name__, '')
20 self._charm_dir_patch = mock.patch.object(templating.hookenv, 'charm_dir')20 self._charm_dir_patch = mock.patch.object(templating.hookenv,
21 'charm_dir')
21 self._charm_dir_mock = self._charm_dir_patch.start()22 self._charm_dir_mock = self._charm_dir_patch.start()
22 self._charm_dir_mock.side_effect = lambda: self.charm_dir23 self._charm_dir_mock.side_effect = lambda: self.charm_dir
2324
@@ -28,9 +29,8 @@
28 @mock.patch.object(templating.host, 'mkdir')29 @mock.patch.object(templating.host, 'mkdir')
29 @mock.patch.object(templating.host, 'log')30 @mock.patch.object(templating.host, 'log')
30 def test_render(self, log, mkdir, fchown):31 def test_render(self, log, mkdir, fchown):
31 _, fn1 = tempfile.mkstemp()32 with tempfile.NamedTemporaryFile() as fn1, \
32 _, fn2 = tempfile.mkstemp()33 tempfile.NamedTemporaryFile() as fn2:
33 try:
34 context = {34 context = {
35 'nats': {35 'nats': {
36 'port': '1234',36 'port': '1234',
@@ -41,23 +41,19 @@
41 },41 },
42 'nginx_port': 80,42 'nginx_port': 80,
43 }43 }
44 templating.render('fake_cc.yml', fn1,44 templating.render('fake_cc.yml', fn1.name,
45 context, templates_dir=TEMPLATES_DIR)45 context, templates_dir=TEMPLATES_DIR)
46 contents = open(fn1).read()46 contents = open(fn1.name).read()
47 self.assertRegexpMatches(contents, 'port: 1234')47 self.assertRegexpMatches(contents, 'port: 1234')
48 self.assertRegexpMatches(contents, 'host: example.com')48 self.assertRegexpMatches(contents, 'host: example.com')
49 self.assertRegexpMatches(contents, 'domain: api.foo.com')49 self.assertRegexpMatches(contents, 'domain: api.foo.com')
5050
51 templating.render('test.conf', fn2, context,51 templating.render('test.conf', fn2.name, context,
52 templates_dir=TEMPLATES_DIR)52 templates_dir=TEMPLATES_DIR)
53 contents = open(fn2).read()53 contents = open(fn2.name).read()
54 self.assertRegexpMatches(contents, 'listen 80')54 self.assertRegexpMatches(contents, 'listen 80')
55 self.assertEqual(fchown.call_count, 2)55 self.assertEqual(fchown.call_count, 2)
56 self.assertEqual(mkdir.call_count, 2)56 self.assertEqual(mkdir.call_count, 2)
57 finally:
58 for fn in (fn1, fn2):
59 if os.path.exists(fn):
60 os.remove(fn)
6157
62 @mock.patch.object(templating.host.os, 'fchown')58 @mock.patch.object(templating.host.os, 'fchown')
63 @mock.patch.object(templating.host, 'log')59 @mock.patch.object(templating.host, 'log')
@@ -80,8 +76,10 @@
80 @mock.patch.object(templating, 'hookenv')76 @mock.patch.object(templating, 'hookenv')
81 @mock.patch('jinja2.Environment')77 @mock.patch('jinja2.Environment')
82 def test_load_error(self, Env, hookenv):78 def test_load_error(self, Env, hookenv):
83 Env().get_template.side_effect = jinja2.exceptions.TemplateNotFound('fake_cc.yml')79 Env().get_template.side_effect = jinja2.exceptions.TemplateNotFound(
80 'fake_cc.yml')
84 self.assertRaises(81 self.assertRaises(
85 jinja2.exceptions.TemplateNotFound, templating.render,82 jinja2.exceptions.TemplateNotFound, templating.render,
86 'fake.src', 'fake.tgt', {}, templates_dir='tmpl')83 'fake.src', 'fake.tgt', {}, templates_dir='tmpl')
87 hookenv.log.assert_called_once_with('Could not load template fake.src from tmpl.', level=hookenv.ERROR)84 hookenv.log.assert_called_once_with(
85 'Could not load template fake.src from tmpl.', level=hookenv.ERROR)

Subscribers

People subscribed via source and target branches