Merge lp:~ahasenack/landscape-charm/service-names-1379133 into lp:~landscape/landscape-charm/trunk

Proposed by Andreas Hasenack
Status: Merged
Approved by: Andreas Hasenack
Approved revision: 225
Merged at revision: 210
Proposed branch: lp:~ahasenack/landscape-charm/service-names-1379133
Merge into: lp:~landscape/landscape-charm/trunk
Diff against target: 270 lines (+118/-22)
3 files modified
hooks/hooks.py (+46/-9)
hooks/lib/util.py (+2/-2)
hooks/test_hooks.py (+70/-11)
To merge this branch: bzr merge lp:~ahasenack/landscape-charm/service-names-1379133
Reviewer Review Type Date Requested Status
Chad Smith Approve
Benji York (community) Approve
Review via email: mp+238299@code.launchpad.net

Commit message

Update the haproxy jinja variables in the apache vhost template files according to whatever service name haproxy was deployed as.

Description of the change

Update the haproxy jinja variables in the apache vhost template files according to whatever service name haproxy was deployed as.

The apache charm expects the jinja variables in the vhost template files to be named after the services it relates to. So if haproxy is deployed as "landscape-haproxy", the variable needs to contain "landscapehaproxy" (sic).

An additional complication is that jinja variables need to be proper python, and the apache charm chose to swallow the "-". So we need to apply the same transformation in our side.

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch looks good. I had one regex simplification and a couple of style nitpicks, but once you consider those you're good to go.

review: Approve
219. By Andreas Hasenack

Simplify regexp

220. By Andreas Hasenack

Other review comments.

Revision history for this message
Andreas Hasenack (ahasenack) :
Revision history for this message
Benji York (benji) wrote :

Inline comment about my memory of PEP 257 failing.

221. By Andreas Hasenack

docstrings

Revision history for this message
Chad Smith (chad.smith) wrote :

[1] & [2] inline comments that are nits. I'd like to avoid truncating unacceptable characters in var names if we can replace them with underscores instead. Maybe the hyphens in service names were there for readability.

[3] Also can you run "make lint" target and sort the lint errors in this branch.

Revision history for this message
Andreas Hasenack (ahasenack) wrote :

Chad, I'm doing what the apache charm already does. If haproxy is deployed as "landscape-haproxy", the apache charm will be looking for a variable prefixed with "landscapehaproxy". They don't replace "-" with "_" :(

Revision history for this message
Chad Smith (chad.smith) :
Revision history for this message
Chad Smith (chad.smith) :
Revision history for this message
Chad Smith (chad.smith) wrote :

minor inline docstring comment.

I'll check a full deployment with a hyphenated service name to confirm but +1 otherwise.

review: Approve
Revision history for this message
Chad Smith (chad.smith) wrote :

validated both 400 errors on trunk and the fix. +1

review: Approve
222. By Andreas Hasenack

lint

223. By Andreas Hasenack

docstrings

224. By Andreas Hasenack

- haproxy_service_name is now a mandatory argument
- docstrings

225. By Andreas Hasenack

test_no_haproxy_service_name_if_not_related_to_haproxy()

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hooks/hooks.py'
2--- hooks/hooks.py 2014-10-01 20:16:43 +0000
3+++ hooks/hooks.py 2014-10-16 12:49:51 +0000
4@@ -98,22 +98,51 @@
5 services=yaml.safe_dump(_get_services_haproxy()))
6
7
8-def notify_vhost_config_relation(relation_id=None):
9+def _get_haproxy_service_name():
10+ """
11+ Find out what service name was used to deploy haproxy and sanitize it
12+ according to the jinja requirements. The service name is used as a
13+ variable name in the apache vhost jinja template.
14+
15+ For example, if haproxy is deployed as "landscape-haproxy", the apache
16+ charm will transform that into landscapehaproxy.
17+ """
18+ haproxy_relations = juju.relation_ids("website")
19+ if not haproxy_relations:
20+ return None
21+ haproxy_relation_units = juju.relation_list(haproxy_relations[0])
22+ haproxy_service = haproxy_relation_units[0].rsplit("/", 1)[0]
23+ # jinja2 templates require python-type variables, remove all characters
24+ # that do not comply
25+ haproxy_service = re.sub("\W", "", haproxy_service)
26+ return haproxy_service
27+
28+
29+def _get_vhost_template(template_filename, haproxy_service_name):
30+ """Expand the template with the provided haproxy service name."""
31+ with open("%s/config/%s" % (ROOT, template_filename), "r") as handle:
32+ contents = handle.read()
33+ contents = re.sub(r"{{ haproxy_([^}]+) }}", r"{{ %s_\1 }}" %
34+ haproxy_service_name, contents)
35+ return contents
36+
37+
38+def notify_vhost_config_relation(haproxy_service_name, relation_id=None):
39 """
40 Notify the vhost-config relation.
41
42 This will mark it "ready to proceed". If relation_id is specified
43 use that as the relation context, otherwise look up and notify all
44 vhost-config relations.
45+
46+ The haproxy_service_name is needed so that the vhost template can be
47+ adjusted with the correct jinja variable that apache will look for.
48 """
49 vhosts = []
50- with open("%s/config/vhostssl.tmpl" % ROOT, 'r') as handle:
51- vhosts.append({
52- "port": "443", "template": b64encode(handle.read())})
53- with open("%s/config/vhost.tmpl" % ROOT, 'r') as handle:
54- vhosts.append({
55- "port": "80", "template": b64encode(handle.read())})
56-
57+ contents = _get_vhost_template("vhostssl.tmpl", haproxy_service_name)
58+ vhosts.append({"port": "443", "template": b64encode(contents)})
59+ contents = _get_vhost_template("vhost.tmpl", haproxy_service_name)
60+ vhosts.append({"port": "80", "template": b64encode(contents)})
61 relation_ids = [relation_id]
62 if relation_id is None:
63 relation_ids = juju.relation_ids("vhost-config")
64@@ -394,7 +423,15 @@
65 if not juju.relation_ids("vhost-config"):
66 return
67
68- notify_vhost_config_relation(os.environ.get("JUJU_RELATION_ID", None))
69+ # If we are not related to haproxy yet, noop, because we need to know the
70+ # haproxy service name so we can set the template variable to the correct
71+ # name in the apache vhost template.
72+ haproxy_service_name = _get_haproxy_service_name()
73+ if not haproxy_service_name:
74+ return
75+
76+ notify_vhost_config_relation(haproxy_service_name,
77+ os.environ.get("JUJU_RELATION_ID", None))
78
79 access_details = _get_db_access_details()
80 if not access_details:
81
82=== modified file 'hooks/lib/util.py'
83--- hooks/lib/util.py 2014-10-01 20:16:43 +0000
84+++ hooks/lib/util.py 2014-10-16 12:49:51 +0000
85@@ -18,7 +18,7 @@
86 Returns true if the given email is safe to use and has no "funny"
87 characters. We don't go overboard and look for an RFC compliant email
88 here.
89-
90+
91 @param email: string containing the email to be validated
92 """
93 valid_email_re = r"^[\w.+-]+@[\w-]+\.[\w.]+$"
94@@ -103,7 +103,7 @@
95 # let the exception be raised and stderr go through as usual.
96 check_output(cmd, cwd="/opt/canonical/landscape", env=env)
97 juju.juju_log("Administrator called %s with email %s created" %
98- (admin_name, admin_email))
99+ (admin_name, admin_email))
100 return True
101 else:
102 juju.juju_log("DB not empty, skipping first admin creation")
103
104=== modified file 'hooks/test_hooks.py'
105--- hooks/test_hooks.py 2014-10-01 20:16:43 +0000
106+++ hooks/test_hooks.py 2014-10-16 12:49:51 +0000
107@@ -1,5 +1,4 @@
108 from configobj import ConfigObj
109-from itertools import product
110 import base64
111 import hooks
112 import mocker
113@@ -62,7 +61,10 @@
114 Hardcode expected relation_list for tests. Feel free to expand
115 as more tests are added.
116 """
117- return list(self._relation_list)
118+ if relation_id == "website:1":
119+ return ["landscape-haproxy/0"]
120+ else:
121+ return list(self._relation_list)
122
123 def unit_get(self, *args):
124 """
125@@ -204,9 +206,10 @@
126 self.mocker.replay()
127 with unittest.TestCase.assertRaises(self, ValueError) as invalid_email:
128 hooks.util.create_landscape_admin(db_user, db_password, db_host,
129- admin_name, admin_email, admin_password)
130+ admin_name, admin_email,
131+ admin_password)
132 self.assertEqual("Invalid administrator email %s" % admin_email,
133- invalid_email.exception.message)
134+ invalid_email.exception.message)
135
136 def test_first_admin_not_created_if_account_not_empty(self):
137 """
138@@ -1575,6 +1578,43 @@
139 self._service_conf.seek(0)
140 self.assertFalse(hooks._is_db_up())
141
142+ def test__get_haproxy_service_name(self):
143+ """
144+ _get_haproxy_service_name() returns the jinja-ready service name used
145+ to deploy haproxy.
146+ """
147+ haproxy_service_name = hooks._get_haproxy_service_name()
148+ self.assertEqual(haproxy_service_name, "landscapehaproxy")
149+
150+ def test_no_haproxy_service_name_if_not_related_to_haproxy(self):
151+ """
152+ _get_haproxy_service_name() returns None if we are not related to
153+ haproxy.
154+ """
155+ def no_website_relation(relation_name):
156+ return None
157+
158+ self.addCleanup(setattr, hooks.juju, "relation_ids",
159+ hooks.juju.relation_ids)
160+ hooks.juju.relation_ids = no_website_relation
161+ haproxy_service_name = hooks._get_haproxy_service_name()
162+ self.assertIsNone(haproxy_service_name)
163+
164+ def test__get_vhost_template(self):
165+ """
166+ The haproxy prefix in the template variables is replaced by the
167+ name of the actual haproxy service that is part of the deployment.
168+ """
169+ template_file = "vhostssl.tmpl"
170+ with open("%s/config/%s" % (hooks.ROOT, template_file), "r") as t:
171+ original_template = t.read()
172+ new_template = hooks._get_vhost_template(template_file,
173+ "landscape-haproxy")
174+ self.assertIn("{{ haproxy_msgserver }}", original_template)
175+ self.assertNotIn("{{ landscape-haproxy_msgserver }}",
176+ original_template)
177+ self.assertIn("{{ landscape-haproxy_msgserver }}", new_template)
178+
179
180 class TestHooksServiceMock(TestHooks):
181
182@@ -1759,7 +1799,7 @@
183 """
184 notify the vhost-config relation on a separate ID.
185 """
186- hooks.notify_vhost_config_relation("foo/0")
187+ hooks.notify_vhost_config_relation("haproxy", "foo/0")
188 with open("%s/config/vhostssl.tmpl" % hooks.ROOT, 'r') as f:
189 vhostssl_template = f.read()
190 with open("%s/config/vhost.tmpl" % hooks.ROOT, 'r') as f:
191@@ -1772,7 +1812,7 @@
192
193 def test_notify_vhost_config_relation(self):
194 """notify the vhost-config relation on the "current" ID."""
195- hooks.notify_vhost_config_relation()
196+ hooks.notify_vhost_config_relation("haproxy")
197 with open("%s/config/vhostssl.tmpl" % hooks.ROOT, 'r') as f:
198 vhostssl_template = f.read()
199 with open("%s/config/vhost.tmpl" % hooks.ROOT, 'r') as f:
200@@ -1802,7 +1842,7 @@
201 "user": "user",
202 "password": "password"}})
203 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
204- notify_vhost(None)
205+ notify_vhost(hooks._get_haproxy_service_name(), None)
206 self.mocker.replay()
207 self.assertRaises(SystemExit, hooks.vhost_config_relation_changed)
208 self.assertIn('Waiting for data from apache', hooks.juju._logs[-1])
209@@ -1820,7 +1860,7 @@
210 "user": "user",
211 "password": "password"}})
212 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
213- notify_vhost(None)
214+ notify_vhost(hooks._get_haproxy_service_name(), None)
215 is_db_up = self.mocker.replace(hooks._is_db_up)
216 is_db_up()
217 self.mocker.result(False)
218@@ -1843,7 +1883,7 @@
219 "user": "user",
220 "password": "password"}})
221 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
222- notify_vhost(None)
223+ notify_vhost(hooks._get_haproxy_service_name(), None)
224 is_db_up = self.mocker.replace(hooks._is_db_up)
225 is_db_up()
226 self.mocker.result(True)
227@@ -1868,7 +1908,7 @@
228 "user": "user",
229 "password": "password"}})
230 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
231- notify_vhost(None)
232+ notify_vhost(hooks._get_haproxy_service_name(), None)
233 mock_conn = self.mocker.mock()
234 mock_conn.close()
235 connect_exclusive = self.mocker.replace(hooks.util.connect_exclusive)
236@@ -1906,7 +1946,7 @@
237 "user": "user",
238 "password": "password"}})
239 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
240- notify_vhost(None)
241+ notify_vhost(hooks._get_haproxy_service_name(), None)
242 is_db_up = self.mocker.replace(hooks._is_db_up)
243 is_db_up()
244 self.mocker.result(True)
245@@ -1926,6 +1966,25 @@
246 with open(hooks.SSL_CERT_LOCATION, 'r') as f:
247 self.assertEqual("foobar", f.read())
248
249+ def test_vhost_config_relation_exits_if_haproxy_not_ready(self):
250+ """
251+ notify_vhost_config_relation() is not called if the haproxy relation
252+ is not there.
253+ """
254+ def should_not_be_here(*args):
255+ raise AssertionError("notify_vhost_config_relation() should not "
256+ "be called")
257+
258+ self.addCleanup(setattr, hooks, "vhost_config_relation_changed",
259+ hooks.vhost_config_relation_changed)
260+ hooks.notify_vhost_config_relation = should_not_be_here
261+ get_haproxy_service_name = self.mocker.replace(
262+ hooks._get_haproxy_service_name)
263+ get_haproxy_service_name()
264+ self.mocker.result(None)
265+ self.mocker.replay()
266+ hooks.vhost_config_relation_changed()
267+
268
269 class TestHooksUtils(TestHooks):
270 def test__setup_apache(self):

Subscribers

People subscribed via source and target branches