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
=== modified file 'hooks/hooks.py'
--- hooks/hooks.py 2014-10-01 20:16:43 +0000
+++ hooks/hooks.py 2014-10-16 12:49:51 +0000
@@ -98,22 +98,51 @@
98 services=yaml.safe_dump(_get_services_haproxy()))98 services=yaml.safe_dump(_get_services_haproxy()))
9999
100100
101def notify_vhost_config_relation(relation_id=None):101def _get_haproxy_service_name():
102 """
103 Find out what service name was used to deploy haproxy and sanitize it
104 according to the jinja requirements. The service name is used as a
105 variable name in the apache vhost jinja template.
106
107 For example, if haproxy is deployed as "landscape-haproxy", the apache
108 charm will transform that into landscapehaproxy.
109 """
110 haproxy_relations = juju.relation_ids("website")
111 if not haproxy_relations:
112 return None
113 haproxy_relation_units = juju.relation_list(haproxy_relations[0])
114 haproxy_service = haproxy_relation_units[0].rsplit("/", 1)[0]
115 # jinja2 templates require python-type variables, remove all characters
116 # that do not comply
117 haproxy_service = re.sub("\W", "", haproxy_service)
118 return haproxy_service
119
120
121def _get_vhost_template(template_filename, haproxy_service_name):
122 """Expand the template with the provided haproxy service name."""
123 with open("%s/config/%s" % (ROOT, template_filename), "r") as handle:
124 contents = handle.read()
125 contents = re.sub(r"{{ haproxy_([^}]+) }}", r"{{ %s_\1 }}" %
126 haproxy_service_name, contents)
127 return contents
128
129
130def notify_vhost_config_relation(haproxy_service_name, relation_id=None):
102 """131 """
103 Notify the vhost-config relation.132 Notify the vhost-config relation.
104133
105 This will mark it "ready to proceed". If relation_id is specified134 This will mark it "ready to proceed". If relation_id is specified
106 use that as the relation context, otherwise look up and notify all135 use that as the relation context, otherwise look up and notify all
107 vhost-config relations.136 vhost-config relations.
137
138 The haproxy_service_name is needed so that the vhost template can be
139 adjusted with the correct jinja variable that apache will look for.
108 """140 """
109 vhosts = []141 vhosts = []
110 with open("%s/config/vhostssl.tmpl" % ROOT, 'r') as handle:142 contents = _get_vhost_template("vhostssl.tmpl", haproxy_service_name)
111 vhosts.append({143 vhosts.append({"port": "443", "template": b64encode(contents)})
112 "port": "443", "template": b64encode(handle.read())})144 contents = _get_vhost_template("vhost.tmpl", haproxy_service_name)
113 with open("%s/config/vhost.tmpl" % ROOT, 'r') as handle:145 vhosts.append({"port": "80", "template": b64encode(contents)})
114 vhosts.append({
115 "port": "80", "template": b64encode(handle.read())})
116
117 relation_ids = [relation_id]146 relation_ids = [relation_id]
118 if relation_id is None:147 if relation_id is None:
119 relation_ids = juju.relation_ids("vhost-config")148 relation_ids = juju.relation_ids("vhost-config")
@@ -394,7 +423,15 @@
394 if not juju.relation_ids("vhost-config"):423 if not juju.relation_ids("vhost-config"):
395 return424 return
396425
397 notify_vhost_config_relation(os.environ.get("JUJU_RELATION_ID", None))426 # If we are not related to haproxy yet, noop, because we need to know the
427 # haproxy service name so we can set the template variable to the correct
428 # name in the apache vhost template.
429 haproxy_service_name = _get_haproxy_service_name()
430 if not haproxy_service_name:
431 return
432
433 notify_vhost_config_relation(haproxy_service_name,
434 os.environ.get("JUJU_RELATION_ID", None))
398435
399 access_details = _get_db_access_details()436 access_details = _get_db_access_details()
400 if not access_details:437 if not access_details:
401438
=== modified file 'hooks/lib/util.py'
--- hooks/lib/util.py 2014-10-01 20:16:43 +0000
+++ hooks/lib/util.py 2014-10-16 12:49:51 +0000
@@ -18,7 +18,7 @@
18 Returns true if the given email is safe to use and has no "funny"18 Returns true if the given email is safe to use and has no "funny"
19 characters. We don't go overboard and look for an RFC compliant email19 characters. We don't go overboard and look for an RFC compliant email
20 here.20 here.
21 21
22 @param email: string containing the email to be validated22 @param email: string containing the email to be validated
23 """23 """
24 valid_email_re = r"^[\w.+-]+@[\w-]+\.[\w.]+$"24 valid_email_re = r"^[\w.+-]+@[\w-]+\.[\w.]+$"
@@ -103,7 +103,7 @@
103 # let the exception be raised and stderr go through as usual.103 # let the exception be raised and stderr go through as usual.
104 check_output(cmd, cwd="/opt/canonical/landscape", env=env)104 check_output(cmd, cwd="/opt/canonical/landscape", env=env)
105 juju.juju_log("Administrator called %s with email %s created" %105 juju.juju_log("Administrator called %s with email %s created" %
106 (admin_name, admin_email))106 (admin_name, admin_email))
107 return True107 return True
108 else:108 else:
109 juju.juju_log("DB not empty, skipping first admin creation")109 juju.juju_log("DB not empty, skipping first admin creation")
110110
=== modified file 'hooks/test_hooks.py'
--- hooks/test_hooks.py 2014-10-01 20:16:43 +0000
+++ hooks/test_hooks.py 2014-10-16 12:49:51 +0000
@@ -1,5 +1,4 @@
1from configobj import ConfigObj1from configobj import ConfigObj
2from itertools import product
3import base642import base64
4import hooks3import hooks
5import mocker4import mocker
@@ -62,7 +61,10 @@
62 Hardcode expected relation_list for tests. Feel free to expand61 Hardcode expected relation_list for tests. Feel free to expand
63 as more tests are added.62 as more tests are added.
64 """63 """
65 return list(self._relation_list)64 if relation_id == "website:1":
65 return ["landscape-haproxy/0"]
66 else:
67 return list(self._relation_list)
6668
67 def unit_get(self, *args):69 def unit_get(self, *args):
68 """70 """
@@ -204,9 +206,10 @@
204 self.mocker.replay()206 self.mocker.replay()
205 with unittest.TestCase.assertRaises(self, ValueError) as invalid_email:207 with unittest.TestCase.assertRaises(self, ValueError) as invalid_email:
206 hooks.util.create_landscape_admin(db_user, db_password, db_host,208 hooks.util.create_landscape_admin(db_user, db_password, db_host,
207 admin_name, admin_email, admin_password)209 admin_name, admin_email,
210 admin_password)
208 self.assertEqual("Invalid administrator email %s" % admin_email,211 self.assertEqual("Invalid administrator email %s" % admin_email,
209 invalid_email.exception.message)212 invalid_email.exception.message)
210213
211 def test_first_admin_not_created_if_account_not_empty(self):214 def test_first_admin_not_created_if_account_not_empty(self):
212 """215 """
@@ -1575,6 +1578,43 @@
1575 self._service_conf.seek(0)1578 self._service_conf.seek(0)
1576 self.assertFalse(hooks._is_db_up())1579 self.assertFalse(hooks._is_db_up())
15771580
1581 def test__get_haproxy_service_name(self):
1582 """
1583 _get_haproxy_service_name() returns the jinja-ready service name used
1584 to deploy haproxy.
1585 """
1586 haproxy_service_name = hooks._get_haproxy_service_name()
1587 self.assertEqual(haproxy_service_name, "landscapehaproxy")
1588
1589 def test_no_haproxy_service_name_if_not_related_to_haproxy(self):
1590 """
1591 _get_haproxy_service_name() returns None if we are not related to
1592 haproxy.
1593 """
1594 def no_website_relation(relation_name):
1595 return None
1596
1597 self.addCleanup(setattr, hooks.juju, "relation_ids",
1598 hooks.juju.relation_ids)
1599 hooks.juju.relation_ids = no_website_relation
1600 haproxy_service_name = hooks._get_haproxy_service_name()
1601 self.assertIsNone(haproxy_service_name)
1602
1603 def test__get_vhost_template(self):
1604 """
1605 The haproxy prefix in the template variables is replaced by the
1606 name of the actual haproxy service that is part of the deployment.
1607 """
1608 template_file = "vhostssl.tmpl"
1609 with open("%s/config/%s" % (hooks.ROOT, template_file), "r") as t:
1610 original_template = t.read()
1611 new_template = hooks._get_vhost_template(template_file,
1612 "landscape-haproxy")
1613 self.assertIn("{{ haproxy_msgserver }}", original_template)
1614 self.assertNotIn("{{ landscape-haproxy_msgserver }}",
1615 original_template)
1616 self.assertIn("{{ landscape-haproxy_msgserver }}", new_template)
1617
15781618
1579class TestHooksServiceMock(TestHooks):1619class TestHooksServiceMock(TestHooks):
15801620
@@ -1759,7 +1799,7 @@
1759 """1799 """
1760 notify the vhost-config relation on a separate ID.1800 notify the vhost-config relation on a separate ID.
1761 """1801 """
1762 hooks.notify_vhost_config_relation("foo/0")1802 hooks.notify_vhost_config_relation("haproxy", "foo/0")
1763 with open("%s/config/vhostssl.tmpl" % hooks.ROOT, 'r') as f:1803 with open("%s/config/vhostssl.tmpl" % hooks.ROOT, 'r') as f:
1764 vhostssl_template = f.read()1804 vhostssl_template = f.read()
1765 with open("%s/config/vhost.tmpl" % hooks.ROOT, 'r') as f:1805 with open("%s/config/vhost.tmpl" % hooks.ROOT, 'r') as f:
@@ -1772,7 +1812,7 @@
17721812
1773 def test_notify_vhost_config_relation(self):1813 def test_notify_vhost_config_relation(self):
1774 """notify the vhost-config relation on the "current" ID."""1814 """notify the vhost-config relation on the "current" ID."""
1775 hooks.notify_vhost_config_relation()1815 hooks.notify_vhost_config_relation("haproxy")
1776 with open("%s/config/vhostssl.tmpl" % hooks.ROOT, 'r') as f:1816 with open("%s/config/vhostssl.tmpl" % hooks.ROOT, 'r') as f:
1777 vhostssl_template = f.read()1817 vhostssl_template = f.read()
1778 with open("%s/config/vhost.tmpl" % hooks.ROOT, 'r') as f:1818 with open("%s/config/vhost.tmpl" % hooks.ROOT, 'r') as f:
@@ -1802,7 +1842,7 @@
1802 "user": "user",1842 "user": "user",
1803 "password": "password"}})1843 "password": "password"}})
1804 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)1844 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
1805 notify_vhost(None)1845 notify_vhost(hooks._get_haproxy_service_name(), None)
1806 self.mocker.replay()1846 self.mocker.replay()
1807 self.assertRaises(SystemExit, hooks.vhost_config_relation_changed)1847 self.assertRaises(SystemExit, hooks.vhost_config_relation_changed)
1808 self.assertIn('Waiting for data from apache', hooks.juju._logs[-1])1848 self.assertIn('Waiting for data from apache', hooks.juju._logs[-1])
@@ -1820,7 +1860,7 @@
1820 "user": "user",1860 "user": "user",
1821 "password": "password"}})1861 "password": "password"}})
1822 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)1862 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
1823 notify_vhost(None)1863 notify_vhost(hooks._get_haproxy_service_name(), None)
1824 is_db_up = self.mocker.replace(hooks._is_db_up)1864 is_db_up = self.mocker.replace(hooks._is_db_up)
1825 is_db_up()1865 is_db_up()
1826 self.mocker.result(False)1866 self.mocker.result(False)
@@ -1843,7 +1883,7 @@
1843 "user": "user",1883 "user": "user",
1844 "password": "password"}})1884 "password": "password"}})
1845 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)1885 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
1846 notify_vhost(None)1886 notify_vhost(hooks._get_haproxy_service_name(), None)
1847 is_db_up = self.mocker.replace(hooks._is_db_up)1887 is_db_up = self.mocker.replace(hooks._is_db_up)
1848 is_db_up()1888 is_db_up()
1849 self.mocker.result(True)1889 self.mocker.result(True)
@@ -1868,7 +1908,7 @@
1868 "user": "user",1908 "user": "user",
1869 "password": "password"}})1909 "password": "password"}})
1870 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)1910 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
1871 notify_vhost(None)1911 notify_vhost(hooks._get_haproxy_service_name(), None)
1872 mock_conn = self.mocker.mock()1912 mock_conn = self.mocker.mock()
1873 mock_conn.close()1913 mock_conn.close()
1874 connect_exclusive = self.mocker.replace(hooks.util.connect_exclusive)1914 connect_exclusive = self.mocker.replace(hooks.util.connect_exclusive)
@@ -1906,7 +1946,7 @@
1906 "user": "user",1946 "user": "user",
1907 "password": "password"}})1947 "password": "password"}})
1908 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)1948 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
1909 notify_vhost(None)1949 notify_vhost(hooks._get_haproxy_service_name(), None)
1910 is_db_up = self.mocker.replace(hooks._is_db_up)1950 is_db_up = self.mocker.replace(hooks._is_db_up)
1911 is_db_up()1951 is_db_up()
1912 self.mocker.result(True)1952 self.mocker.result(True)
@@ -1926,6 +1966,25 @@
1926 with open(hooks.SSL_CERT_LOCATION, 'r') as f:1966 with open(hooks.SSL_CERT_LOCATION, 'r') as f:
1927 self.assertEqual("foobar", f.read())1967 self.assertEqual("foobar", f.read())
19281968
1969 def test_vhost_config_relation_exits_if_haproxy_not_ready(self):
1970 """
1971 notify_vhost_config_relation() is not called if the haproxy relation
1972 is not there.
1973 """
1974 def should_not_be_here(*args):
1975 raise AssertionError("notify_vhost_config_relation() should not "
1976 "be called")
1977
1978 self.addCleanup(setattr, hooks, "vhost_config_relation_changed",
1979 hooks.vhost_config_relation_changed)
1980 hooks.notify_vhost_config_relation = should_not_be_here
1981 get_haproxy_service_name = self.mocker.replace(
1982 hooks._get_haproxy_service_name)
1983 get_haproxy_service_name()
1984 self.mocker.result(None)
1985 self.mocker.replay()
1986 hooks.vhost_config_relation_changed()
1987
19291988
1930class TestHooksUtils(TestHooks):1989class TestHooksUtils(TestHooks):
1931 def test__setup_apache(self):1990 def test__setup_apache(self):

Subscribers

People subscribed via source and target branches