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
1=== modified file 'charmhelpers/contrib/peerstorage/__init__.py'
2--- charmhelpers/contrib/peerstorage/__init__.py 2015-01-22 06:06:03 +0000
3+++ charmhelpers/contrib/peerstorage/__init__.py 2015-04-29 13:11:45 +0000
4@@ -73,6 +73,8 @@
5 exc_list = exc_list if exc_list else []
6 peerdb_settings = peer_retrieve('-', relation_name=relation_name)
7 matched = {}
8+ if peerdb_settings is None:
9+ return matched
10 for k, v in peerdb_settings.items():
11 full_prefix = prefix + delimiter
12 if k.startswith(full_prefix):
13
14=== modified file 'charmhelpers/contrib/unison/__init__.py'
15--- charmhelpers/contrib/unison/__init__.py 2015-01-28 22:09:21 +0000
16+++ charmhelpers/contrib/unison/__init__.py 2015-04-29 13:11:45 +0000
17@@ -63,6 +63,7 @@
18 from charmhelpers.core.host import (
19 adduser,
20 add_user_to_group,
21+ pwgen,
22 )
23
24 from charmhelpers.core.hookenv import (
25@@ -140,7 +141,7 @@
26 ssh_dir = os.path.join(home_dir, '.ssh')
27 auth_keys = os.path.join(ssh_dir, 'authorized_keys')
28 log('Syncing authorized_keys @ %s.' % auth_keys)
29- with open(auth_keys, 'wb') as out:
30+ with open(auth_keys, 'w') as out:
31 for k in keys:
32 out.write('%s\n' % k)
33
34@@ -152,16 +153,16 @@
35 khosts = []
36 for host in hosts:
37 cmd = ['ssh-keyscan', '-H', '-t', 'rsa', host]
38- remote_key = check_output(cmd).strip()
39+ remote_key = check_output(cmd, universal_newlines=True).strip()
40 khosts.append(remote_key)
41 log('Syncing known_hosts @ %s.' % known_hosts)
42- with open(known_hosts, 'wb') as out:
43+ with open(known_hosts, 'w') as out:
44 for host in khosts:
45 out.write('%s\n' % host)
46
47
48 def ensure_user(user, group=None):
49- adduser(user)
50+ adduser(user, pwgen())
51 if group:
52 add_user_to_group(user, group)
53
54
55=== modified file 'charmhelpers/core/hookenv.py'
56--- charmhelpers/core/hookenv.py 2015-04-07 16:12:51 +0000
57+++ charmhelpers/core/hookenv.py 2015-04-29 13:11:45 +0000
58@@ -21,6 +21,7 @@
59 # Charm Helpers Developers <juju@lists.ubuntu.com>
60
61 from __future__ import print_function
62+from functools import wraps
63 import os
64 import json
65 import yaml
66@@ -58,15 +59,17 @@
67
68 will cache the result of unit_get + 'test' for future calls.
69 """
70+ @wraps(func)
71 def wrapper(*args, **kwargs):
72 global cache
73 key = str((func, args, kwargs))
74 try:
75 return cache[key]
76 except KeyError:
77- res = func(*args, **kwargs)
78- cache[key] = res
79- return res
80+ pass # Drop out of the exception handler scope.
81+ res = func(*args, **kwargs)
82+ cache[key] = res
83+ return res
84 return wrapper
85
86
87@@ -178,7 +181,7 @@
88
89 def remote_unit():
90 """The remote unit for the current relation hook"""
91- return os.environ['JUJU_REMOTE_UNIT']
92+ return os.environ.get('JUJU_REMOTE_UNIT', None)
93
94
95 def service_name():
96@@ -250,6 +253,12 @@
97 except KeyError:
98 return (self._prev_dict or {})[key]
99
100+ def get(self, key, default=None):
101+ try:
102+ return self[key]
103+ except KeyError:
104+ return default
105+
106 def keys(self):
107 prev_keys = []
108 if self._prev_dict is not None:
109@@ -509,6 +518,11 @@
110 return None
111
112
113+def unit_public_ip():
114+ """Get this unit's public IP address"""
115+ return unit_get('public-address')
116+
117+
118 def unit_private_ip():
119 """Get this unit's private IP address"""
120 return unit_get('private-address')
121
122=== modified file 'charmhelpers/core/host.py'
123--- charmhelpers/core/host.py 2015-03-12 10:56:57 +0000
124+++ charmhelpers/core/host.py 2015-04-29 13:11:45 +0000
125@@ -90,7 +90,7 @@
126 ['service', service_name, 'status'],
127 stderr=subprocess.STDOUT).decode('UTF-8')
128 except subprocess.CalledProcessError as e:
129- return 'unrecognized service' not in e.output
130+ return b'unrecognized service' not in e.output
131 else:
132 return True
133
134
135=== modified file 'charmhelpers/core/services/base.py'
136--- charmhelpers/core/services/base.py 2015-01-22 06:06:03 +0000
137+++ charmhelpers/core/services/base.py 2015-04-29 13:11:45 +0000
138@@ -17,7 +17,7 @@
139 import os
140 import re
141 import json
142-from collections import Iterable
143+from collections import Iterable, OrderedDict
144
145 from charmhelpers.core import host
146 from charmhelpers.core import hookenv
147@@ -119,7 +119,7 @@
148 """
149 self._ready_file = os.path.join(hookenv.charm_dir(), 'READY-SERVICES.json')
150 self._ready = None
151- self.services = {}
152+ self.services = OrderedDict()
153 for service in services or []:
154 service_name = service['service']
155 self.services[service_name] = service
156
157=== modified file 'charmhelpers/fetch/__init__.py'
158--- charmhelpers/fetch/__init__.py 2015-01-22 06:11:15 +0000
159+++ charmhelpers/fetch/__init__.py 2015-04-29 13:11:45 +0000
160@@ -158,7 +158,7 @@
161
162 def apt_cache(in_memory=True):
163 """Build and return an apt cache"""
164- import apt_pkg
165+ from apt import apt_pkg
166 apt_pkg.init()
167 if in_memory:
168 apt_pkg.config.set("Dir::Cache::pkgcache", "")
169
170=== modified file 'tests/contrib/peerstorage/test_peerstorage.py'
171--- tests/contrib/peerstorage/test_peerstorage.py 2014-08-19 09:38:07 +0000
172+++ tests/contrib/peerstorage/test_peerstorage.py 2015-04-29 13:11:45 +0000
173@@ -146,6 +146,14 @@
174 self.assertEquals(peerstorage.peer_retrieve_by_prefix(rel_id), settings)
175
176 @patch.object(peerstorage, 'peer_retrieve')
177+ def test_peer_retrieve_by_prefix_empty_relation(self, peer_retrieve):
178+ # If relation-get returns None, peer_retrieve_by_prefix returns
179+ # an empty dictionary.
180+ peer_retrieve.return_value = None
181+ rel_id = 'db:2'
182+ self.assertEquals(peerstorage.peer_retrieve_by_prefix(rel_id), {})
183+
184+ @patch.object(peerstorage, 'peer_retrieve')
185 def test_peer_retrieve_by_prefix_exc_list(self, peer_retrieve):
186 rel_id = 'db:2'
187 settings = {
188
189=== modified file 'tests/contrib/unison/test_unison.py'
190--- tests/contrib/unison/test_unison.py 2015-01-05 10:30:39 +0000
191+++ tests/contrib/unison/test_unison.py 2015-04-29 13:11:45 +0000
192@@ -1,5 +1,5 @@
193
194-from mock import call, patch, MagicMock
195+from mock import call, patch, MagicMock, sentinel
196 from testtools import TestCase
197
198 from tests.helpers import patch_open, FakeRelation
199@@ -146,7 +146,7 @@
200 ]
201 with patch_open() as (_open, _file):
202 unison.write_authorized_keys('foo', keys)
203- _open.assert_called_with('/home/foo/.ssh/authorized_keys', 'wb')
204+ _open.assert_called_with('/home/foo/.ssh/authorized_keys', 'w')
205 for k in keys:
206 self.assertIn(call('%s\n' % k), _file.write.call_args_list)
207
208@@ -160,15 +160,17 @@
209 self.check_output.side_effect = keys
210 with patch_open() as (_open, _file):
211 unison.write_known_hosts('foo', ['10.0.0.1', '10.0.0.2'])
212- _open.assert_called_with('/home/foo/.ssh/known_hosts', 'wb')
213+ _open.assert_called_with('/home/foo/.ssh/known_hosts', 'w')
214 for k in keys:
215 self.assertIn(call('%s\n' % k), _file.write.call_args_list)
216
217+ @patch.object(unison, 'pwgen')
218 @patch.object(unison, 'add_user_to_group')
219 @patch.object(unison, 'adduser')
220- def test_ensure_user(self, adduser, to_group):
221+ def test_ensure_user(self, adduser, to_group, pwgen):
222+ pwgen.return_value = sentinel.password
223 unison.ensure_user('foo', group='foobar')
224- adduser.assert_called_with('foo')
225+ adduser.assert_called_with('foo', sentinel.password)
226 to_group.assert_called_with('foo', 'foobar')
227
228 @patch.object(unison, '_run_as_user')
229
230=== modified file 'tests/core/test_hookenv.py'
231--- tests/core/test_hookenv.py 2015-03-12 10:31:22 +0000
232+++ tests/core/test_hookenv.py 2015-04-29 13:11:45 +0000
233@@ -3,8 +3,7 @@
234 from subprocess import CalledProcessError
235 import shutil
236 import tempfile
237-from mock import patch, call, mock_open
238-from mock import MagicMock
239+from mock import call, MagicMock, mock_open, patch, sentinel
240 from testtools import TestCase
241 import yaml
242
243@@ -128,11 +127,47 @@
244 self.assertEqual(c['foo'], 'bar')
245 self.assertEqual(c['baz'], 'bam')
246
247+ def test_get(self):
248+ c = hookenv.Config(dict(foo='bar'))
249+ c.save()
250+ c = hookenv.Config(dict(baz='bam'))
251+
252+ self.assertIsNone(c.get('missing'))
253+ self.assertIs(c.get('missing', sentinel.missing), sentinel.missing)
254+ self.assertEqual(c.get('foo'), 'bar')
255+ self.assertEqual(c.get('baz'), 'bam')
256+
257 def test_keys(self):
258 c = hookenv.Config(dict(foo='bar'))
259 c["baz"] = "bar"
260 self.assertEqual(sorted([six.u("foo"), "baz"]), sorted(c.keys()))
261
262+ def test_in(self):
263+ # Test behavior of the in operator.
264+
265+ # Items that exist in the dict exist. Items that don't don't.
266+ c = hookenv.Config(dict(foo='one'))
267+ self.assertTrue('foo' in c)
268+ self.assertTrue('bar' not in c)
269+ c.save()
270+ self.assertTrue('foo' in c)
271+ self.assertTrue('bar' not in c)
272+
273+ # Adding items works as expected.
274+ c['foo'] = 'two'
275+ c['bar'] = 'two'
276+ self.assertTrue('foo' in c)
277+ self.assertTrue('bar' in c)
278+ c.save()
279+ self.assertTrue('foo' in c)
280+ self.assertTrue('bar' in c)
281+
282+ # Removing items works as expected.
283+ del c['foo']
284+ self.assertTrue('foo' not in c)
285+ c.save()
286+ self.assertTrue('foo' not in c)
287+
288
289 class SerializableTest(TestCase):
290 def test_serializes_object_to_json(self):
291@@ -298,10 +333,16 @@
292 self.assertEqual(hookenv.local_unit(), 'foo')
293
294 @patch('charmhelpers.core.hookenv.unit_get')
295+ def test_gets_unit_public_ip(self, _unitget):
296+ _unitget.return_value = sentinel.public_ip
297+ self.assertEqual(sentinel.public_ip, hookenv.unit_public_ip())
298+ _unitget.assert_called_once_with('public-address')
299+
300+ @patch('charmhelpers.core.hookenv.unit_get')
301 def test_gets_unit_private_ip(self, _unitget):
302- _unitget.return_value = 'foo'
303- self.assertEqual("foo", hookenv.unit_private_ip())
304- _unitget.assert_called_with('private-address')
305+ _unitget.return_value = sentinel.private_ip
306+ self.assertEqual(sentinel.private_ip, hookenv.unit_private_ip())
307+ _unitget.assert_called_once_with('private-address')
308
309 @patch('charmhelpers.core.hookenv.os')
310 def test_checks_that_is_running_in_relation_hook(self, os_):
311@@ -447,6 +488,11 @@
312
313 self.assertEqual(hookenv.remote_unit(), 'foo')
314
315+ @patch('charmhelpers.core.hookenv.os')
316+ def test_no_remote_unit(self, os_):
317+ os_.environ = {}
318+ self.assertEqual(hookenv.remote_unit(), None)
319+
320 @patch('charmhelpers.core.hookenv.remote_unit')
321 @patch('charmhelpers.core.hookenv.relation_get')
322 def test_gets_relation_for_unit(self, relation_get, remote_unit):
323
324=== modified file 'tests/core/test_services.py'
325--- tests/core/test_services.py 2015-03-19 15:00:13 +0000
326+++ tests/core/test_services.py 2015-04-29 13:11:45 +0000
327@@ -27,6 +27,18 @@
328 'qux': 'baz'},
329 })
330
331+ def test_register_preserves_order(self):
332+ service_list = [dict(service='a'), dict(service='b')]
333+
334+ # Test that the services list order is preserved by checking
335+ # both forwards and backwards - only one of these will be
336+ # dictionary order, and if both work we know order is being
337+ # preserved.
338+ manager = services.ServiceManager(service_list)
339+ self.assertEqual(list(manager.services.keys()), ['a', 'b'])
340+ manager = services.ServiceManager(reversed(service_list))
341+ self.assertEqual(list(manager.services.keys()), ['b', 'a'])
342+
343 @mock.patch.object(services.ServiceManager, 'reconfigure_services')
344 @mock.patch.object(services.ServiceManager, 'stop_services')
345 @mock.patch.object(hookenv, 'hook_name')
346
347=== modified file 'tests/core/test_templating.py'
348--- tests/core/test_templating.py 2015-02-03 21:52:36 +0000
349+++ tests/core/test_templating.py 2015-04-29 13:11:45 +0000
350@@ -1,9 +1,9 @@
351-import os
352 import pkg_resources
353 import shutil
354 import tempfile
355 import unittest
356 import jinja2
357+import os.path
358 import pwd
359 import grp
360
361@@ -17,7 +17,8 @@
362 class TestTemplating(unittest.TestCase):
363 def setUp(self):
364 self.charm_dir = pkg_resources.resource_filename(__name__, '')
365- self._charm_dir_patch = mock.patch.object(templating.hookenv, 'charm_dir')
366+ self._charm_dir_patch = mock.patch.object(templating.hookenv,
367+ 'charm_dir')
368 self._charm_dir_mock = self._charm_dir_patch.start()
369 self._charm_dir_mock.side_effect = lambda: self.charm_dir
370
371@@ -28,9 +29,8 @@
372 @mock.patch.object(templating.host, 'mkdir')
373 @mock.patch.object(templating.host, 'log')
374 def test_render(self, log, mkdir, fchown):
375- _, fn1 = tempfile.mkstemp()
376- _, fn2 = tempfile.mkstemp()
377- try:
378+ with tempfile.NamedTemporaryFile() as fn1, \
379+ tempfile.NamedTemporaryFile() as fn2:
380 context = {
381 'nats': {
382 'port': '1234',
383@@ -41,23 +41,19 @@
384 },
385 'nginx_port': 80,
386 }
387- templating.render('fake_cc.yml', fn1,
388+ templating.render('fake_cc.yml', fn1.name,
389 context, templates_dir=TEMPLATES_DIR)
390- contents = open(fn1).read()
391+ contents = open(fn1.name).read()
392 self.assertRegexpMatches(contents, 'port: 1234')
393 self.assertRegexpMatches(contents, 'host: example.com')
394 self.assertRegexpMatches(contents, 'domain: api.foo.com')
395
396- templating.render('test.conf', fn2, context,
397+ templating.render('test.conf', fn2.name, context,
398 templates_dir=TEMPLATES_DIR)
399- contents = open(fn2).read()
400+ contents = open(fn2.name).read()
401 self.assertRegexpMatches(contents, 'listen 80')
402 self.assertEqual(fchown.call_count, 2)
403 self.assertEqual(mkdir.call_count, 2)
404- finally:
405- for fn in (fn1, fn2):
406- if os.path.exists(fn):
407- os.remove(fn)
408
409 @mock.patch.object(templating.host.os, 'fchown')
410 @mock.patch.object(templating.host, 'log')
411@@ -80,8 +76,10 @@
412 @mock.patch.object(templating, 'hookenv')
413 @mock.patch('jinja2.Environment')
414 def test_load_error(self, Env, hookenv):
415- Env().get_template.side_effect = jinja2.exceptions.TemplateNotFound('fake_cc.yml')
416+ Env().get_template.side_effect = jinja2.exceptions.TemplateNotFound(
417+ 'fake_cc.yml')
418 self.assertRaises(
419 jinja2.exceptions.TemplateNotFound, templating.render,
420 'fake.src', 'fake.tgt', {}, templates_dir='tmpl')
421- hookenv.log.assert_called_once_with('Could not load template fake.src from tmpl.', level=hookenv.ERROR)
422+ hookenv.log.assert_called_once_with(
423+ 'Could not load template fake.src from tmpl.', level=hookenv.ERROR)

Subscribers

People subscribed via source and target branches