Merge lp:~james-page/charms/trusty/rabbitmq-server/network-splits into lp:charms/trusty/rabbitmq-server

Proposed by James Page
Status: Superseded
Proposed branch: lp:~james-page/charms/trusty/rabbitmq-server/network-splits
Merge into: lp:charms/trusty/rabbitmq-server
Diff against target: 325 lines (+114/-35)
4 files modified
.coveragerc (+6/-0)
hooks/rabbit_utils.py (+38/-0)
hooks/rabbitmq_server_relations.py (+64/-33)
unit_tests/test_rabbitmq_server_relations.py (+6/-2)
To merge this branch: bzr merge lp:~james-page/charms/trusty/rabbitmq-server/network-splits
Reviewer Review Type Date Requested Status
Cory Johns (community) Needs Fixing
Review Queue (community) automated testing Needs Fixing
James Page Needs Resubmitting
Tim Van Steenburgh (community) Needs Fixing
Review via email: mp+228152@code.launchpad.net

This proposal has been superseded by a proposal from 2015-01-23.

Description of the change

Add support for separate 'access-network' configuration.

To post a comment you must log in.
Revision history for this message
Tim Van Steenburgh (tvansteenburgh) wrote :

Hi James,

Thanks for this. Code review looks good, but I'm unsure how to do a functional test of this, can you provide instructions?

Also, there are some charm-proof warnings to clean up:

W: config.yaml: option access-network does not have the keys: default
W: config.yaml: option key does not have the keys: default
W: config.yaml: option source does not have the keys: default

Thanks!

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

Hi Tim

https://code.launchpad.net/~openstack-charmers/+junk/network-splits contains some helpers for testing; however basically you need servers with more than one configured network interface.

RE the charm proof warning; not touching those - not having a default key is intentional 'unset' being a valid state. The code works on the fact that a config-get on unset keys returns None in JSON.

review: Needs Resubmitting
Revision history for this message
Cory Johns (johnsca) wrote :

James,

I believe that Tim's point was that the policy was that you still need to provide a default value, even if that value is an empty string. No warnings on `charm proof` is listed on the policy: https://juju.ubuntu.com/docs/authors-charm-policy.html

Also, my understanding of the "Resubmit" status was that the review was rejected and would need to be resubmitted to be approved; is that not the case?

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

charm proof is wrong - sorry I've discussed this numerous times with Marco - not providing a default value is a perfectly acceptable situation and is used extensively in my charms - Marco did at least drop this to a warning rather than an error but I'm not re-writing all of my charms to comply with a policy that I believe is wrong in this case.

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

After some discussion with Marco on IRC, he's provided me with a way to have the key, but still leave the default unset; I've updated the charm to reflect this.

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

charm_lint_check #195 trusty-rabbitmq-server for james-page mp228152
    LINT OK: passed

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

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

charm_unit_test #224 trusty-rabbitmq-server for james-page mp228152
    UNIT FAIL: unit-test missing

UNIT Results (max last 2 lines):
INFO:root:Search string not found in makefile target commands.
ERROR:root:No make target was executed.

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

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

charm_amulet_test #244 trusty-rabbitmq-server for james-page mp228152
    AMULET FAIL: amulet-test missing

AMULET Results (max last 2 lines):
INFO:root:Search string not found in makefile target commands.
ERROR:root:No make target was executed.

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

Revision history for this message
Review Queue (review-queue) wrote :

This items has failed automated testing! Results available here http://reports.vapour.ws/charm-tests/charm-bundle-test-10759-results

review: Needs Fixing (automated testing)
Revision history for this message
Cory Johns (johnsca) wrote :

Test failure is due to a merge conflict, mainly in charmhelpers sync, but there are a couple in the charm itself, as well. I tried my hand at resolving, but there was a bit more than I felt comfortable with making decisions on without deeper knowledge of the charm.

review: Needs Fixing
72. By James Page

Rebase on next branch

73. By James Page

Optimize calls

74. By James Page

Re-instate configuration

75. By James Page

Aligment on config

76. By James Page

Run flake8 from venv as well

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.coveragerc'
2--- .coveragerc 1970-01-01 00:00:00 +0000
3+++ .coveragerc 2015-01-23 08:23:23 +0000
4@@ -0,0 +1,6 @@
5+[report]
6+# Regexes for lines to exclude from consideration
7+exclude_lines =
8+ if __name__ == .__main__.:
9+include=
10+ hooks/rabbit*
11
12=== modified file 'hooks/rabbit_utils.py'
13--- hooks/rabbit_utils.py 2015-01-19 19:02:16 +0000
14+++ hooks/rabbit_utils.py 2015-01-23 08:23:23 +0000
15@@ -37,18 +37,39 @@
16 peer_retrieve
17 )
18
19+from collections import OrderedDict
20+
21 PACKAGES = ['rabbitmq-server', 'python-amqplib']
22
23 RABBITMQ_CTL = '/usr/sbin/rabbitmqctl'
24 COOKIE_PATH = '/var/lib/rabbitmq/.erlang.cookie'
25 ENV_CONF = '/etc/rabbitmq/rabbitmq-env.conf'
26 RABBITMQ_CONF = '/etc/rabbitmq/rabbitmq.config'
27+ENABLED_PLUGINS = '/etc/rabbitmq/enabled_plugins'
28 RABBIT_USER = 'rabbitmq'
29 LIB_PATH = '/var/lib/rabbitmq/'
30 HOSTS_FILE = '/etc/hosts'
31
32 _named_passwd = '/var/lib/charm/{}/{}.passwd'
33
34+# hook_contexts are used as a convenient mechanism to render templates
35+# logically, consider building a hook_context for template rendering so
36+# the charm doesn't concern itself with template specifics etc.
37+CONFIG_FILES = OrderedDict([
38+ (RABBITMQ_CONF, {
39+ 'hook_contexts': None,
40+ 'services': ['rabbitmq-server']
41+ }),
42+ (ENV_CONF, {
43+ 'hook_contexts': None,
44+ 'services': ['rabbitmq-server']
45+ }),
46+ (ENABLED_PLUGINS, {
47+ 'hook_contexts': None,
48+ 'services': ['rabbitmq-server']
49+ }),
50+])
51+
52
53 class RabbitmqError(Exception):
54 pass
55@@ -532,3 +553,20 @@
56 if lsb_release()['DISTRIB_CODENAME'].lower() < "trusty":
57 raise Exception("IPv6 is not supported in the charms for Ubuntu "
58 "versions less than Trusty 14.04")
59+
60+
61+def restart_map():
62+ '''Determine the correct resource map to be passed to
63+ charmhelpers.core.restart_on_change() based on the services configured.
64+
65+ :returns: dict: A dictionary mapping config file to lists of services
66+ that should be restarted when file changes.
67+ '''
68+ _map = []
69+ for f, ctxt in CONFIG_FILES.iteritems():
70+ svcs = []
71+ for svc in ctxt['services']:
72+ svcs.append(svc)
73+ if svcs:
74+ _map.append((f, svcs))
75+ return OrderedDict(_map)
76
77=== modified file 'hooks/rabbitmq_server_relations.py'
78--- hooks/rabbitmq_server_relations.py 2015-01-12 21:16:00 +0000
79+++ hooks/rabbitmq_server_relations.py 2015-01-23 08:23:23 +0000
80@@ -31,11 +31,15 @@
81 from charmhelpers.fetch import (
82 add_source,
83 apt_update,
84- apt_install)
85+ apt_install,
86+)
87
88 from charmhelpers.core.hookenv import (
89- open_port, close_port,
90- log, ERROR,
91+ open_port,
92+ close_port,
93+ log,
94+ ERROR,
95+ INFO,
96 relation_get,
97 relation_set,
98 relation_ids,
99@@ -50,7 +54,11 @@
100 UnregisteredHookError
101 )
102 from charmhelpers.core.host import (
103- rsync, service_stop, service_restart, cmp_pkgrevno
104+ cmp_pkgrevno,
105+ restart_on_change,
106+ rsync,
107+ service_stop,
108+ service_restart,
109 )
110 from charmhelpers.contrib.charmsupport.nrpe import NRPE
111 from charmhelpers.contrib.ssl.service import ServiceCA
112@@ -63,6 +71,8 @@
113 peer_retrieve_by_prefix,
114 )
115
116+from charmhelpers.contrib.network.ip import get_address_in_network
117+
118 hooks = Hooks()
119
120 SERVICE_NAME = os.getenv('JUJU_UNIT_NAME').split('/')[0]
121@@ -150,7 +160,16 @@
122 if config('prefer-ipv6'):
123 relation_settings['private-address'] = get_ipv6_addr()[0]
124 else:
125- relation_settings['hostname'] = unit_get('private-address')
126+ # NOTE(jamespage)
127+ # override private-address settings if access-network is
128+ # configured and an appropriate network interface is configured.
129+ relation_settings['hostname'] = \
130+ get_address_in_network(config('access-network'),
131+ unit_get('private-address'))
132+ relation_settings['private-address'] = \
133+ get_address_in_network(config('access-network'),
134+ unit_get('private-address'))
135+
136
137 configure_client_ssl(relation_settings)
138
139@@ -167,6 +186,7 @@
140 # set if need HA queues or not
141 if cmp_pkgrevno('rabbitmq-server', '3.0.1') < 0:
142 relation_settings['ha_queues'] = True
143+
144 peer_store_and_set(relation_id=relation_id,
145 relation_settings=relation_settings)
146
147@@ -200,9 +220,10 @@
148 # then use the current hostname
149 nodename = socket.gethostname()
150
151- if nodename:
152+ if nodename and rabbit.get_node_name() != nodename:
153 log('forcing nodename=%s' % nodename)
154- # need to stop it under current nodename
155+ # would like to have used the restart_on_change decorator, but
156+ # need to stop it under current nodename prior to updating env
157 service_stop('rabbitmq-server')
158 rabbit.update_rmq_env_conf(hostname='rabbit@%s' % nodename,
159 ipv6=config('prefer-ipv6'))
160@@ -212,7 +233,6 @@
161 log('cluster_joined: Relation greater.')
162 return
163
164- rabbit.COOKIE_PATH = '/var/lib/rabbitmq/.erlang.cookie'
165 if not os.path.isfile(rabbit.COOKIE_PATH):
166 log('erlang cookie missing from %s' % rabbit.COOKIE_PATH,
167 level=ERROR)
168@@ -225,7 +245,7 @@
169 def cluster_changed():
170 rdata = relation_get()
171 if 'cookie' not in rdata:
172- log('cluster_joined: cookie not yet set.')
173+ log('cluster_joined: cookie not yet set.', level=INFO)
174 return
175
176 if config('prefer-ipv6') and rdata.get('hostname'):
177@@ -239,21 +259,13 @@
178 whitelist = [a for a in rdata.keys() if a not in blacklist]
179 peer_echo(includes=whitelist)
180
181- # sync cookie
182- cookie = peer_retrieve('cookie')
183- if open(rabbit.COOKIE_PATH, 'r').read().strip() == cookie:
184- log('Cookie already synchronized with peer.')
185- else:
186- log('Synchronizing erlang cookie from peer.')
187- rabbit.service('stop')
188- with open(rabbit.COOKIE_PATH, 'wb') as out:
189- out.write(cookie)
190- rabbit.service('start')
191+ # sync the cookie with peers if necessary
192+ update_cookie()
193
194 if is_relation_made('ha') and \
195 config('ha-vip-only') is False:
196 log('hacluster relation is present, skipping native '
197- 'rabbitmq cluster config.')
198+ 'rabbitmq cluster config.', level=INFO)
199 return
200
201 # cluster with node
202@@ -268,6 +280,24 @@
203 amqp_changed(relation_id=rid, remote_unit=unit)
204
205
206+def update_cookie():
207+ # sync cookie
208+ cookie = peer_retrieve('cookie')
209+ cookie_local = None
210+ with open(rabbit.COOKIE_PATH, 'r') as f:
211+ cookie_local = f.read().strip()
212+
213+ if cookie_local == cookie:
214+ log('Cookie already synchronized with peer.')
215+ return
216+
217+ log('Synchronizing erlang cookie from peer.', level=INFO)
218+ service_stop('rabbitmq-server')
219+ with open(rabbit.COOKIE_PATH, 'wb') as out:
220+ out.write(cookie)
221+ service_restart('rabbitmq-server')
222+
223+
224 @hooks.hook('cluster-relation-departed')
225 def cluster_departed():
226 if is_relation_made('ha') and \
227@@ -407,13 +437,13 @@
228 rbd_size = config('rbd-size')
229 sizemb = int(rbd_size.split('G')[0]) * 1024
230 blk_device = '/dev/rbd/%s/%s' % (POOL_NAME, rbd_img)
231- # rbd_pool_rep_count = config('ceph-osd-replication-count')
232+ ceph.create_pool(service=SERVICE_NAME, name=POOL_NAME,
233+ replicas=int(config('ceph-osd-replication-count')))
234 ceph.ensure_ceph_storage(service=SERVICE_NAME, pool=POOL_NAME,
235 rbd_img=rbd_img, sizemb=sizemb,
236 fstype='ext4', mount_point=RABBIT_DIR,
237 blk_device=blk_device,
238- system_services=['rabbitmq-server']) # ,
239- # rbd_pool_replicas=rbd_pool_rep_count)
240+ system_services=['rabbitmq-server'])
241 subprocess.check_call(['chown', '-R', '%s:%s' %
242 (RABBIT_USER, RABBIT_GROUP), RABBIT_DIR])
243 else:
244@@ -595,12 +625,8 @@
245 open_port(ssl_port)
246
247
248-def restart_rabbit_update_nrpe():
249- service_restart('rabbitmq-server')
250- update_nrpe_checks()
251-
252-
253 @hooks.hook('config-changed')
254+@restart_on_change(rabbit.restart_map())
255 def config_changed():
256 if config('prefer-ipv6'):
257 rabbit.assert_charm_supports_ipv6()
258@@ -622,9 +648,7 @@
259 chmod(RABBIT_DIR, 0o775)
260
261 if config('prefer-ipv6'):
262- service_stop('rabbitmq-server')
263 rabbit.update_rmq_env_conf(ipv6=config('prefer-ipv6'))
264- service_restart('rabbitmq-server')
265
266 if config('management_plugin') is True:
267 rabbit.enable_plugin(MAN_PLUGIN)
268@@ -641,15 +665,22 @@
269 ha_is_active_active = config("ha-vip-only")
270
271 if ha_is_active_active:
272- restart_rabbit_update_nrpe()
273+ update_nrpe_checks()
274 else:
275 if is_elected_leader('res_rabbitmq_vip'):
276- restart_rabbit_update_nrpe()
277+ update_nrpe_checks()
278 else:
279 log("hacluster relation is present but this node is not active"
280 " skipping update nrpe checks")
281 else:
282- restart_rabbit_update_nrpe()
283+ update_nrpe_checks()
284+
285+ # NOTE(jamespage)
286+ # trigger amqp_changed to pickup and changes to network
287+ # configuration via the access-network config option.
288+ for rid in relation_ids('amqp'):
289+ for unit in related_units(rid):
290+ amqp_changed(relation_id=rid, remote_unit=unit)
291
292
293 def pre_install_hooks():
294
295=== modified file 'unit_tests/test_rabbitmq_server_relations.py'
296--- unit_tests/test_rabbitmq_server_relations.py 2014-11-27 10:52:50 +0000
297+++ unit_tests/test_rabbitmq_server_relations.py 2015-01-23 08:23:23 +0000
298@@ -69,14 +69,16 @@
299 self.fake_repo = {'rabbitmq-server': {'pkg_vers': '3.0'}}
300 rabbitmq_server_relations.amqp_changed(None, None)
301 mock_peer_store_and_set.assert_called_with(
302- relation_settings={'hostname': host_addr,
303+ relation_settings={'private-address': '10.1.2.3',
304+ 'hostname': host_addr,
305 'ha_queues': True},
306 relation_id=None)
307
308 self.fake_repo = {'rabbitmq-server': {'pkg_vers': '3.0.2'}}
309 rabbitmq_server_relations.amqp_changed(None, None)
310 mock_peer_store_and_set.assert_called_with(
311- relation_settings={'hostname': host_addr},
312+ relation_settings={'private-address': '10.1.2.3',
313+ 'hostname': host_addr},
314 relation_id=None)
315
316 @patch('rabbitmq_server_relations.peer_store_and_set')
317@@ -108,6 +110,8 @@
318 mock_config.side_effect = config
319 ipv6_addr = "2001:db8:1:0:f816:3eff:fed6:c140"
320 mock_get_ipv6_addr.return_value = [ipv6_addr]
321+ host_addr = "10.1.2.3"
322+ unit_get.return_value = host_addr
323 is_elected_leader.return_value = True
324 relation_get.return_value = {}
325 is_clustered.return_value = False

Subscribers

People subscribed via source and target branches