Merge lp:~james-page/charm-helpers/is-relation-made into lp:charm-helpers

Proposed by James Page
Status: Merged
Merged at revision: 92
Proposed branch: lp:~james-page/charm-helpers/is-relation-made
Merge into: lp:charm-helpers
Diff against target: 260 lines (+109/-18)
7 files modified
charmhelpers/cli/host.py (+1/-0)
charmhelpers/contrib/ssl/__init__.py (+7/-8)
charmhelpers/core/hookenv.py (+20/-0)
charmhelpers/fetch/bzrurl.py (+1/-1)
tests/contrib/ssl/test_ssl.py (+1/-5)
tests/core/test_hookenv.py (+79/-0)
tests/fetch/test_bzrurl.py (+0/-4)
To merge this branch: bzr merge lp:~james-page/charm-helpers/is-relation-made
Reviewer Review Type Date Requested Status
James Page Needs Resubmitting
Adam Gandelman (community) Needs Fixing
Review via email: mp+191920@code.launchpad.net

Description of the change

Add is_relation_made helper for deep testing of relation state.

For example, the ceph charm only passes an auth key to clients once
its bootstrapped; until then the client can do anything:

  if is_relation_made('ceph:0', 'key'):
     configure_and_do_stuff()
  else:
     don_t()

Unit tests as well.

Thanks for looking.

To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote :

I also tidied the majority of lint in the project;

make lint is a good check when reviewing merge proposals; don't think everyone has been doing that!

82. By James Page

Rebase against parent branch

83. By James Page

Rebase on parent

Revision history for this message
Adam Gandelman (gandelman-a) wrote :

This looks good, but any chance we can allow is_relation_made() to also take a list of keys? That way we could use the same for relations that require a bit more data, ie:

keys = ['password, 'db-host']
if is_relation_made('shared-db:0', settings):
    configure_db()

Revision history for this message
Adam Gandelman (gandelman-a) wrote :

Also, I think the current behavior of relation_get() will cause this to evalutate as True even if the relation setting has not been set:

if relation_get(key, rid=r_id, unit=unit):
   return True

Might be a good idea to also check whatever relation_get() returns != ''

review: Needs Fixing
84. By James Page

Add support for multiple key checks in is_relation_made

85. By James Page

Rework a bit for lists

Revision history for this message
James Page (james-page) wrote :

relation_get for a missing key should always return None so I think that is OK.

I updated to support lists as well as single strings as keys.

review: Needs Resubmitting

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/cli/host.py'
2--- charmhelpers/cli/host.py 2013-06-20 15:03:24 +0000
3+++ charmhelpers/cli/host.py 2013-10-21 22:54:42 +0000
4@@ -7,6 +7,7 @@
5 "List mounts"
6 return host.mounts()
7
8+
9 @cmdline.subcommand_builder('service', description="Control system services")
10 def service(subparser):
11 subparser.add_argument("action", help="The action to perform (start, stop, etc...)")
12
13=== modified file 'charmhelpers/contrib/ssl/__init__.py'
14--- charmhelpers/contrib/ssl/__init__.py 2013-08-28 09:12:05 +0000
15+++ charmhelpers/contrib/ssl/__init__.py 2013-10-21 22:54:42 +0000
16@@ -34,7 +34,7 @@
17 cmd = ["/usr/bin/openssl", "req", "-new", "-newkey",
18 "rsa:{}".format(keysize), "-days", "365", "-nodes", "-x509",
19 "-keyout", keyfile,
20- "-out", certfile, "-config", config]
21+ "-out", certfile, "-config", config]
22 elif subject:
23 ssl_subject = ""
24 if "country" in subject:
25@@ -50,8 +50,8 @@
26 if "cn" in subject:
27 ssl_subject = ssl_subject + "/CN={}".format(subject["cn"])
28 else:
29- hookenv.log("When using \"subject\" argument you must " \
30- "provide \"cn\" field at very least")
31+ hookenv.log("When using \"subject\" argument you must "
32+ "provide \"cn\" field at very least")
33 return False
34 if "email" in subject:
35 ssl_subject = ssl_subject + "/emailAddress={}".format(subject["email"])
36@@ -59,16 +59,16 @@
37 cmd = ["/usr/bin/openssl", "req", "-new", "-newkey",
38 "rsa:{}".format(keysize), "-days", "365", "-nodes", "-x509",
39 "-keyout", keyfile,
40- "-out", certfile, "-subj", ssl_subject]
41+ "-out", certfile, "-subj", ssl_subject]
42 elif cn:
43 cmd = ["/usr/bin/openssl", "req", "-new", "-newkey",
44 "rsa:{}".format(keysize), "-days", "365", "-nodes", "-x509",
45 "-keyout", keyfile,
46- "-out", certfile, "-subj", "/CN={}".format(cn)]
47+ "-out", certfile, "-subj", "/CN={}".format(cn)]
48
49 if not cmd:
50- hookenv.log("No config, subject or cn provided," \
51- "unable to generate self signed SSL certificates")
52+ hookenv.log("No config, subject or cn provided,"
53+ "unable to generate self signed SSL certificates")
54 return False
55 try:
56 subprocess.check_call(cmd)
57@@ -76,4 +76,3 @@
58 except Exception as e:
59 print "Execution of openssl command failed:\n{}".format(e)
60 return False
61-
62
63=== modified file 'charmhelpers/core/hookenv.py'
64--- charmhelpers/core/hookenv.py 2013-10-21 08:34:03 +0000
65+++ charmhelpers/core/hookenv.py 2013-10-21 22:54:42 +0000
66@@ -285,6 +285,26 @@
67 return rels
68
69
70+@cached
71+def is_relation_made(relation, keys='private-address'):
72+ '''
73+ Determine whether a relation is established by checking for
74+ presence of key(s). If a list of keys is provided, they
75+ must all be present for the relation to be identified as made
76+ '''
77+ if isinstance(keys, str):
78+ keys = [keys]
79+ for r_id in relation_ids(relation):
80+ for unit in related_units(r_id):
81+ context = {}
82+ for k in keys:
83+ context[k] = relation_get(k, rid=r_id,
84+ unit=unit)
85+ if None not in context.values():
86+ return True
87+ return False
88+
89+
90 def open_port(port, protocol="TCP"):
91 """Open a service network port"""
92 _args = ['open-port']
93
94=== modified file 'charmhelpers/fetch/bzrurl.py'
95--- charmhelpers/fetch/bzrurl.py 2013-08-22 10:19:51 +0000
96+++ charmhelpers/fetch/bzrurl.py 2013-10-21 22:54:42 +0000
97@@ -12,6 +12,7 @@
98 apt_install("python-bzrlib")
99 from bzrlib.branch import Branch
100
101+
102 class BzrUrlFetchHandler(BaseFetchHandler):
103 """Handler for bazaar branches via generic and lp URLs"""
104 def can_handle(self, source):
105@@ -46,4 +47,3 @@
106 except OSError as e:
107 raise UnhandledSource(e.strerror)
108 return dest_dir
109-
110
111=== modified file 'tests/contrib/ssl/test_ssl.py'
112--- tests/contrib/ssl/test_ssl.py 2013-08-27 17:08:44 +0000
113+++ tests/contrib/ssl/test_ssl.py 2013-10-21 22:54:42 +0000
114@@ -1,6 +1,4 @@
115-import subprocess
116-
117-from mock import patch, call, MagicMock
118+from mock import patch
119 from testtools import TestCase
120
121 from charmhelpers.contrib import ssl
122@@ -39,7 +37,6 @@
123 result = ssl.generate_selfsigned("mykey.key", "mycert.crt", subject=subject)
124 self.assertFalse(result)
125
126-
127 @patch('subprocess.check_call')
128 def test_generate_selfsigned_file(self, mock_call):
129 ssl.generate_selfsigned("mykey.key", "mycert.crt", config="test.cnf")
130@@ -49,7 +46,6 @@
131 'mykey.key', '-out', 'mycert.crt',
132 '-config', 'test.cnf'])
133
134-
135 @patch('subprocess.check_call')
136 def test_generate_selfsigned_cn_key(self, mock_call):
137 ssl.generate_selfsigned("mykey.key", "mycert.crt", keysize="2048", cn="mysite.example.com")
138
139=== modified file 'tests/core/test_hookenv.py'
140--- tests/core/test_hookenv.py 2013-10-21 08:34:03 +0000
141+++ tests/core/test_hookenv.py 2013-10-21 22:54:42 +0000
142@@ -498,6 +498,85 @@
143 },
144 })
145
146+ @patch('charmhelpers.core.hookenv.relation_ids')
147+ @patch('charmhelpers.core.hookenv.related_units')
148+ @patch('charmhelpers.core.hookenv.relation_get')
149+ def test_is_relation_made(self, relation_get, related_units,
150+ relation_ids):
151+ relation_get.return_value = 'hostname'
152+ related_units.return_value = ['test/1']
153+ relation_ids.return_value = ['test:0']
154+ self.assertTrue(hookenv.is_relation_made('test'))
155+ relation_get.assert_called_with('private-address',
156+ rid='test:0', unit='test/1')
157+
158+ @patch('charmhelpers.core.hookenv.relation_ids')
159+ @patch('charmhelpers.core.hookenv.related_units')
160+ @patch('charmhelpers.core.hookenv.relation_get')
161+ def test_is_relation_made_multi_unit(self, relation_get, related_units,
162+ relation_ids):
163+ relation_get.side_effect = [None, 'hostname']
164+ related_units.return_value = ['test/1', 'test/2']
165+ relation_ids.return_value = ['test:0']
166+ self.assertTrue(hookenv.is_relation_made('test'))
167+
168+ @patch('charmhelpers.core.hookenv.relation_ids')
169+ @patch('charmhelpers.core.hookenv.related_units')
170+ @patch('charmhelpers.core.hookenv.relation_get')
171+ def test_is_relation_made_different_key(self,
172+ relation_get, related_units,
173+ relation_ids):
174+ relation_get.return_value = 'hostname'
175+ related_units.return_value = ['test/1']
176+ relation_ids.return_value = ['test:0']
177+ self.assertTrue(hookenv.is_relation_made('test', keys='auth'))
178+ relation_get.assert_called_with('auth',
179+ rid='test:0', unit='test/1')
180+
181+ @patch('charmhelpers.core.hookenv.relation_ids')
182+ @patch('charmhelpers.core.hookenv.related_units')
183+ @patch('charmhelpers.core.hookenv.relation_get')
184+ def test_is_relation_made_multiple_keys(self,
185+ relation_get, related_units,
186+ relation_ids):
187+ relation_get.side_effect = ['password', 'hostname']
188+ related_units.return_value = ['test/1']
189+ relation_ids.return_value = ['test:0']
190+ self.assertTrue(hookenv.is_relation_made('test',
191+ keys=['auth', 'host']))
192+ relation_get.assert_has_calls(
193+ [call('auth', rid='test:0', unit='test/1'),
194+ call('host', rid='test:0', unit='test/1')]
195+ )
196+
197+ @patch('charmhelpers.core.hookenv.relation_ids')
198+ @patch('charmhelpers.core.hookenv.related_units')
199+ @patch('charmhelpers.core.hookenv.relation_get')
200+ def test_is_relation_made_not_made(self,
201+ relation_get, related_units,
202+ relation_ids):
203+ relation_get.return_value = None
204+ related_units.return_value = ['test/1']
205+ relation_ids.return_value = ['test:0']
206+ self.assertFalse(hookenv.is_relation_made('test'))
207+
208+ @patch('charmhelpers.core.hookenv.relation_ids')
209+ @patch('charmhelpers.core.hookenv.related_units')
210+ @patch('charmhelpers.core.hookenv.relation_get')
211+ def test_is_relation_made_not_made_multiple_keys(self,
212+ relation_get,
213+ related_units,
214+ relation_ids):
215+ relation_get.side_effect = ['password', None]
216+ related_units.return_value = ['test/1']
217+ relation_ids.return_value = ['test:0']
218+ self.assertFalse(hookenv.is_relation_made('test',
219+ keys=['auth', 'host']))
220+ relation_get.assert_has_calls(
221+ [call('auth', rid='test:0', unit='test/1'),
222+ call('host', rid='test:0', unit='test/1')]
223+ )
224+
225 @patch('charmhelpers.core.hookenv.config')
226 @patch('charmhelpers.core.hookenv.relation_type')
227 @patch('charmhelpers.core.hookenv.local_unit')
228
229=== modified file 'tests/fetch/test_bzrurl.py'
230--- tests/fetch/test_bzrurl.py 2013-08-21 11:45:37 +0000
231+++ tests/fetch/test_bzrurl.py 2013-10-21 22:54:42 +0000
232@@ -41,7 +41,6 @@
233 )
234 self.fh = bzrurl.BzrUrlFetchHandler()
235
236-
237 def test_handles_bzr_urls(self):
238 for url in self.valid_urls:
239 result = self.fh.can_handle(url)
240@@ -50,7 +49,6 @@
241 result = self.fh.can_handle(url)
242 self.assertNotEqual(result, True, url)
243
244-
245 @patch('bzrlib.branch.Branch.open')
246 def test_branch(self, _open):
247 dest_path = "/destination/path"
248@@ -65,7 +63,6 @@
249 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):
250 self.assertRaises(UnhandledSource, self.fh.branch, url, dest_path)
251
252-
253 @patch('charmhelpers.fetch.bzrurl.mkdir')
254 def test_installs(self, _mkdir):
255 self.fh.branch = MagicMock()
256@@ -77,4 +74,3 @@
257 where = self.fh.install(url)
258 self.assertEqual(where, dest)
259 _mkdir.assert_called_with(where, perms=0755)
260-

Subscribers

People subscribed via source and target branches