Merge lp:~hopem/charms/trusty/keystone/fix-db-access-before-ready into lp:~openstack-charmers-archive/charms/trusty/keystone/next

Proposed by Edward Hope-Morley
Status: Merged
Merged at revision: 110
Proposed branch: lp:~hopem/charms/trusty/keystone/fix-db-access-before-ready
Merge into: lp:~openstack-charmers-archive/charms/trusty/keystone/next
Diff against target: 208 lines (+84/-21)
4 files modified
hooks/keystone_hooks.py (+9/-18)
hooks/keystone_utils.py (+41/-0)
unit_tests/test_keystone_hooks.py (+10/-3)
unit_tests/test_keystone_utils.py (+24/-0)
To merge this branch: bzr merge lp:~hopem/charms/trusty/keystone/fix-db-access-before-ready
Reviewer Review Type Date Requested Status
Liam Young (community) Approve
Review via email: mp+247344@code.launchpad.net
To post a comment you must log in.
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #1038 keystone-next for hopem mp247344
    LINT OK: passed

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

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

charm_unit_test #1030 keystone-next for hopem mp247344
    UNIT OK: passed

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

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

charm_amulet_test #1218 keystone-next for hopem mp247344
    AMULET OK: passed

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

Revision history for this message
Liam Young (gnuoy) wrote :

Approve

review: Approve
110. By Liam Young

[hopem, r=gnuoy] Wait until DB ready before performing Keystone api ops.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hooks/keystone_hooks.py'
2--- hooks/keystone_hooks.py 2015-01-22 14:35:41 +0000
3+++ hooks/keystone_hooks.py 2015-01-22 18:46:36 +0000
4@@ -2,7 +2,6 @@
5 import hashlib
6 import json
7 import os
8-import re
9 import stat
10 import sys
11 import time
12@@ -67,6 +66,7 @@
13 get_ssl_sync_request_units,
14 is_str_true,
15 is_ssl_cert_master,
16+ is_db_ready,
17 )
18
19 from charmhelpers.contrib.hahelpers.cluster import (
20@@ -219,8 +219,7 @@
21 # Bugs 1353135 & 1187508. Dbs can appear to be ready before the
22 # units acl entry has been added. So, if the db supports passing
23 # a list of permitted units then check if we're in the list.
24- allowed_units = relation_get('allowed_units')
25- if allowed_units and local_unit() not in allowed_units.split():
26+ if not is_db_ready(use_current_context=True):
27 log('Allowed_units list provided and this unit not present')
28 return
29 # Ensure any existing service entries are updated in the
30@@ -249,21 +248,13 @@
31
32 notifications = {}
33 if is_elected_leader(CLUSTER_RES):
34- # Catch database not configured error and defer until db ready
35- from keystoneclient.apiclient.exceptions import InternalServerError
36- try:
37- add_service_to_keystone(relation_id, remote_unit)
38- except InternalServerError as exc:
39- key = re.compile("'keystone\..+' doesn't exist")
40- if re.search(key, exc.message):
41- log("Keystone database not yet ready (InternalServerError "
42- "raised) - deferring until *-db relation completes.",
43- level=WARNING)
44- return
45-
46- log("Unexpected exception occurred", level=ERROR)
47- raise
48-
49+
50+ if not is_db_ready():
51+ log("identity-service-relation-changed hook fired before db "
52+ "ready - deferring until db ready", level=WARNING)
53+ return
54+
55+ add_service_to_keystone(relation_id, remote_unit)
56 settings = relation_get(rid=relation_id, unit=remote_unit)
57 service = settings.get('service', None)
58 if service:
59
60=== modified file 'hooks/keystone_utils.py'
61--- hooks/keystone_utils.py 2015-01-22 14:35:41 +0000
62+++ hooks/keystone_utils.py 2015-01-22 18:46:36 +0000
63@@ -62,6 +62,7 @@
64 local_unit,
65 relation_get,
66 relation_set,
67+ relation_id,
68 relation_ids,
69 related_units,
70 DEBUG,
71@@ -1356,3 +1357,43 @@
72 level=DEBUG)
73 for rid in rel_ids:
74 relation_set(relation_id=rid, relation_settings=_notifications)
75+
76+
77+def is_db_ready(use_current_context=False, db_rel=None):
78+ """Database relations are expected to provide a list of 'allowed' units to
79+ confirm that the database is ready for use by those units.
80+
81+ If db relation has provided this information and local unit is a member,
82+ returns True otherwise False.
83+ """
84+ key = 'allowed_units'
85+ db_rels = ['shared-db', 'pgsql-db']
86+ if db_rel:
87+ db_rels = [db_rel]
88+
89+ rel_has_units = False
90+
91+ if use_current_context:
92+ if not any([relation_id() in relation_ids(r) for r in db_rels]):
93+ raise Exception("use_current_context=True but not in one of %s "
94+ "rel hook contexts (currently in %s)." %
95+ (', '.join(db_rels), relation_id()))
96+
97+ allowed_units = relation_get(attribute=key)
98+ if allowed_units and local_unit() in allowed_units.split():
99+ return True
100+ else:
101+ for rel in db_rels:
102+ for rid in relation_ids(rel):
103+ for unit in related_units(rid):
104+ allowed_units = relation_get(rid=rid, unit=unit,
105+ attribute=key)
106+ if allowed_units and local_unit() in allowed_units.split():
107+ return True
108+
109+ # If relation has units
110+ return False
111+
112+ # If neither relation has units then we are probably in sqllite mode return
113+ # True.
114+ return not rel_has_units
115
116=== modified file 'unit_tests/test_keystone_hooks.py'
117--- unit_tests/test_keystone_hooks.py 2015-01-22 14:35:41 +0000
118+++ unit_tests/test_keystone_hooks.py 2015-01-22 18:46:36 +0000
119@@ -203,13 +203,15 @@
120 configs.write = MagicMock()
121 hooks.pgsql_db_changed()
122
123+ @patch.object(hooks, 'is_db_ready')
124 @patch('keystone_utils.log')
125 @patch('keystone_utils.ensure_ssl_cert_master')
126 @patch.object(hooks, 'CONFIGS')
127 @patch.object(hooks, 'identity_changed')
128 def test_db_changed_allowed(self, identity_changed, configs,
129 mock_ensure_ssl_cert_master,
130- mock_log):
131+ mock_log, mock_is_db_ready):
132+ mock_is_db_ready.return_value = True
133 mock_ensure_ssl_cert_master.return_value = False
134 self.relation_ids.return_value = ['identity-service:0']
135 self.related_units.return_value = ['unit/0']
136@@ -223,12 +225,15 @@
137 relation_id='identity-service:0',
138 remote_unit='unit/0')
139
140+ @patch.object(hooks, 'is_db_ready')
141 @patch('keystone_utils.log')
142 @patch('keystone_utils.ensure_ssl_cert_master')
143 @patch.object(hooks, 'CONFIGS')
144 @patch.object(hooks, 'identity_changed')
145 def test_db_changed_not_allowed(self, identity_changed, configs,
146- mock_ensure_ssl_cert_master, mock_log):
147+ mock_ensure_ssl_cert_master, mock_log,
148+ mock_is_db_ready):
149+ mock_is_db_ready.return_value = False
150 mock_ensure_ssl_cert_master.return_value = False
151 self.relation_ids.return_value = ['identity-service:0']
152 self.related_units.return_value = ['unit/0']
153@@ -374,13 +379,15 @@
154 remote_unit='unit/0')
155 admin_relation_changed.assert_called_with('identity-service:0')
156
157+ @patch.object(hooks, 'is_db_ready')
158 @patch('keystone_utils.log')
159 @patch('keystone_utils.ensure_ssl_cert_master')
160 @patch.object(hooks, 'hashlib')
161 @patch.object(hooks, 'send_notifications')
162 def test_identity_changed_leader(self, mock_send_notifications,
163 mock_hashlib, mock_ensure_ssl_cert_master,
164- mock_log):
165+ mock_log, mock_is_db_ready):
166+ mock_is_db_ready.return_value = True
167 mock_ensure_ssl_cert_master.return_value = False
168 hooks.identity_changed(
169 relation_id='identity-service:0',
170
171=== modified file 'unit_tests/test_keystone_utils.py'
172--- unit_tests/test_keystone_utils.py 2015-01-16 14:50:19 +0000
173+++ unit_tests/test_keystone_utils.py 2015-01-22 18:46:36 +0000
174@@ -33,6 +33,10 @@
175 'service_start',
176 'relation_get',
177 'relation_set',
178+ 'relation_ids',
179+ 'relation_id',
180+ 'local_unit',
181+ 'related_units',
182 'https',
183 'is_relation_made',
184 'peer_store',
185@@ -345,3 +349,23 @@
186 isfile.return_value = False
187 self.subprocess.check_output.return_value = 'supersecretgen'
188 self.assertEqual(utils.get_admin_passwd(), 'supersecretgen')
189+
190+ def test_is_db_ready(self):
191+ self.relation_id.return_value = 'shared-db:0'
192+ self.relation_ids.return_value = [self.relation_id.return_value]
193+ self.local_unit.return_value = 'unit/0'
194+ self.relation_get.return_value = 'unit/0'
195+ self.assertTrue(utils.is_db_ready(use_current_context=True))
196+
197+ self.relation_ids.return_value = ['acme:0']
198+ self.assertRaises(utils.is_db_ready, use_current_context=True)
199+
200+ self.related_units.return_value = ['unit/0']
201+ self.relation_ids.return_value = [self.relation_id.return_value]
202+ self.assertTrue(utils.is_db_ready())
203+
204+ self.relation_get.return_value = 'unit/1'
205+ self.assertFalse(utils.is_db_ready())
206+
207+ self.related_units.return_value = []
208+ self.assertTrue(utils.is_db_ready())

Subscribers

People subscribed via source and target branches