Merge lp:~tribaal/landscape-charm/add-root-url into lp:~landscape/landscape-charm/trunk

Proposed by Chris Glass
Status: Merged
Approved by: Chris Glass
Approved revision: 256
Merged at revision: 253
Proposed branch: lp:~tribaal/landscape-charm/add-root-url
Merge into: lp:~landscape/landscape-charm/trunk
Diff against target: 282 lines (+167/-5)
10 files modified
config.yaml (+6/-0)
hooks/lib/relations/config.py (+23/-0)
hooks/lib/relations/haproxy.py (+2/-1)
hooks/lib/relations/tests/test_config.py (+59/-0)
hooks/lib/relations/tests/test_landscape.py (+1/-1)
hooks/lib/services.py (+3/-2)
hooks/lib/tests/test_templates.py (+25/-1)
hooks/lib/tests/test_utils.py (+29/-0)
hooks/lib/utils.py (+14/-0)
templates/service.conf (+5/-0)
To merge this branch: bzr merge lp:~tribaal/landscape-charm/add-root-url
Reviewer Review Type Date Requested Status
Free Ekanayaka (community) Approve
Benji York (community) Approve
🤖 Landscape Builder test results Approve
Review via email: mp+256918@code.launchpad.net

Commit message

This branch introduces a root-url global configuration item, and sets it to either the
value of the root-url charm configuration option or (if it's not present), the
haproxy's public-address (as set throught the website relation).

Description of the change

This branch introduces a root-url global configuration item, and sets it to either the
value of the root-url charm configuration option or (if it's not present), the
haproxy's public-address (as set throught the website relation).

To post a comment you must log in.
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: make ci-test
Result: Success
Revno: 252
Branch: lp:~tribaal/landscape-charm/add-root-url
Jenkins: https://ci.lscape.net/job/latch-test/520/

review: Approve (test results)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: make ci-test
Result: Success
Revno: 253
Branch: lp:~tribaal/landscape-charm/add-root-url
Jenkins: https://ci.lscape.net/job/latch-test/522/

review: Approve (test results)
Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Looks good, however I have blocking comment, see below.

review: Needs Fixing
Revision history for this message
Free Ekanayaka (free.ekanayaka) :
Revision history for this message
Free Ekanayaka (free.ekanayaka) :
Revision history for this message
Alberto Donato (ack) :
Revision history for this message
Free Ekanayaka (free.ekanayaka) :
Revision history for this message
Benji York (benji) wrote :

This branch looks good.

review: Approve
254. By Chris Glass

Addressed review comments, refactoring.

Revision history for this message
Chris Glass (tribaal) wrote :

All comments addressed.

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Thanks! +1 with a couple of minors

review: Approve
255. By Chris Glass

Fixed last comments.

Revision history for this message
Chris Glass (tribaal) wrote :

Fixed last comments.

256. By Chris Glass

lint

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'config.yaml'
--- config.yaml 2015-04-13 10:57:02 +0000
+++ config.yaml 2015-04-23 14:34:15 +0000
@@ -48,6 +48,12 @@
48 Has no effect if changed after a deployment.48 Has no effect if changed after a deployment.
49 type: string49 type: string
50 default:50 default:
51 root-url:
52 description: |
53 The root URL for this landscape deployment. If left blank the public
54 IP of the first related HAproxy unit will be used instead.
55 type: string
56 default: ""
51 ssl-cert:57 ssl-cert:
52 type: string58 type: string
53 description: |59 description: |
5460
=== added file 'hooks/lib/relations/config.py'
--- hooks/lib/relations/config.py 1970-01-01 00:00:00 +0000
+++ hooks/lib/relations/config.py 2015-04-23 14:34:15 +0000
@@ -0,0 +1,23 @@
1from charmhelpers.core import hookenv
2from lib.hook import HookError
3from lib.utils import is_valid_url
4
5
6class ConfigRequirer(dict):
7 """Dependency manager for the service configuration.
8
9 Take care of validating and exposing configuration values for use
10 in service manager callbacks."""
11
12 def __init__(self, hookenv=hookenv):
13 config = hookenv.config()
14 self._validate(config)
15 self.update({"config": config})
16
17 def _validate(self, config):
18 root_url = config.get("root-url")
19 if root_url and not is_valid_url(root_url):
20 raise HookError(
21 "The 'root-url' configuration value is not a valid URL."
22 " Please make sure it is of the form"
23 " 'http[s]://<hostname>/'")
024
=== modified file 'hooks/lib/relations/haproxy.py'
--- hooks/lib/relations/haproxy.py 2015-04-17 09:48:05 +0000
+++ hooks/lib/relations/haproxy.py 2015-04-23 14:34:15 +0000
@@ -94,7 +94,8 @@
94 "crts": self._get_ssl_certificate(),94 "crts": self._get_ssl_certificate(),
95 "servers": [self._get_server("appserver")],95 "servers": [self._get_server("appserver")],
96 "backends": [96 "backends": [
97 self._get_backend("message", [self._get_server("message-server")]),97 self._get_backend(
98 "message", [self._get_server("message-server")]),
98 self._get_backend("api", [self._get_server("api")]),99 self._get_backend("api", [self._get_server("api")]),
99 ],100 ],
100 })101 })
101102
=== added file 'hooks/lib/relations/tests/test_config.py'
--- hooks/lib/relations/tests/test_config.py 1970-01-01 00:00:00 +0000
+++ hooks/lib/relations/tests/test_config.py 2015-04-23 14:34:15 +0000
@@ -0,0 +1,59 @@
1from lib.tests.helpers import HookenvTest
2from lib.relations.config import ConfigRequirer
3from lib.hook import HookError
4
5
6class ServicesHookTest(HookenvTest):
7
8 def test_root_url_is_set_and_invalid(self):
9 """
10 If an invalid root-url config option is set, a HookeError is raised.
11 """
12 self.hookenv.config().update({"root-url": "asd"})
13
14 with self.assertRaises(HookError) as error:
15 ConfigRequirer(hookenv=self.hookenv)
16
17 expected = ("The 'root-url' configuration value is not a valid URL."
18 " Please make sure it is of the form"
19 " 'http[s]://<hostname>/'")
20 self.assertEqual(expected, error.exception.message)
21
22 def test_root_url_is_set_without_protocol(self):
23 """
24 If an invalid root-url config option is set without a protocol, a
25 HookError is raised.
26 """
27 self.hookenv.config().update({"root-url": "example.com/"})
28 with self.assertRaises(HookError) as error:
29 ConfigRequirer(hookenv=self.hookenv)
30
31 expected = ("The 'root-url' configuration value is not a valid URL."
32 " Please make sure it is of the form"
33 " 'http[s]://<hostname>/'")
34 self.assertEqual(expected, error.exception.message)
35
36 def test_root_url_is_set_without_trailing_slash(self):
37 """
38 If an invalid root-url config option is set without a trailing slash,
39 a HookError is raised.
40 """
41 self.hookenv.config().update({"root-url": "https://example.com"})
42 with self.assertRaises(HookError) as error:
43 ConfigRequirer(hookenv=self.hookenv)
44
45 expected = ("The 'root-url' configuration value is not a valid URL."
46 " Please make sure it is of the form"
47 " 'http[s]://<hostname>/'")
48 self.assertEqual(expected, error.exception.message)
49
50 def test_a_valid_root_url_configuration_key_is_set(self):
51 """No excpetion is raised if the root-url config entry is valid.
52
53 The resulting ConfigRequirer is a dict with a "config" entry that
54 matches the contents of the config passed.
55 """
56 self.hookenv.config().update({"root-url": "https://example.com/"})
57 result = ConfigRequirer(hookenv=self.hookenv)
58 self.assertEqual({"config": {"root-url": "https://example.com/"}},
59 result)
060
=== modified file 'hooks/lib/relations/tests/test_landscape.py'
--- hooks/lib/relations/tests/test_landscape.py 2015-04-17 09:40:16 +0000
+++ hooks/lib/relations/tests/test_landscape.py 2015-04-23 14:34:15 +0000
@@ -138,7 +138,7 @@
138 "secret-token": "old-token",138 "secret-token": "old-token",
139 "leader-ip": "old-ip"}))139 "leader-ip": "old-ip"}))
140 context = LandscapeLeaderContext(140 context = LandscapeLeaderContext(
141 host=self.host, path=self.path, hookenv=self.hookenv)141 host=self.host, path=self.path, hookenv=self.hookenv)
142 self.assertEqual({"database-password": "old-sekret",142 self.assertEqual({"database-password": "old-sekret",
143 "secret-token": "old-token",143 "secret-token": "old-token",
144 "leader-ip": "old-ip"}, context)144 "leader-ip": "old-ip"}, context)
145145
=== modified file 'hooks/lib/services.py'
--- hooks/lib/services.py 2015-04-17 11:34:11 +0000
+++ hooks/lib/services.py 2015-04-23 14:34:15 +0000
@@ -13,6 +13,7 @@
13 HAProxyProvider, HAProxyRequirer, OFFLINE_FOLDER)13 HAProxyProvider, HAProxyRequirer, OFFLINE_FOLDER)
14from lib.relations.landscape import (14from lib.relations.landscape import (
15 LandscapeLeaderContext, LandscapeRequirer, LandscapeProvider)15 LandscapeLeaderContext, LandscapeRequirer, LandscapeProvider)
16from lib.relations.config import ConfigRequirer
16from lib.relations.hosted import HostedRequirer17from lib.relations.hosted import HostedRequirer
17from lib.callbacks.scripts import SchemaBootstrap, LSCtl18from lib.callbacks.scripts import SchemaBootstrap, LSCtl
18from lib.callbacks.filesystem import (19from lib.callbacks.filesystem import (
@@ -60,12 +61,12 @@
60 # Required data is available to the render_template calls below.61 # Required data is available to the render_template calls below.
61 "required_data": [62 "required_data": [
62 LandscapeRequirer(leader_context),63 LandscapeRequirer(leader_context),
64 ConfigRequirer(self._hookenv),
63 PostgreSQLRequirer(),65 PostgreSQLRequirer(),
64 RabbitMQRequirer(),66 RabbitMQRequirer(),
65 HAProxyRequirer(),67 HAProxyRequirer(),
66 HostedRequirer(),68 HostedRequirer(),
67 {"config": hookenv.config(),69 {"is_leader": is_leader},
68 "is_leader": is_leader},
69 ],70 ],
70 "data_ready": [71 "data_ready": [
71 render_template(72 render_template(
7273
=== modified file 'hooks/lib/tests/test_templates.py'
--- hooks/lib/tests/test_templates.py 2015-04-20 08:42:13 +0000
+++ hooks/lib/tests/test_templates.py 2015-04-23 14:34:15 +0000
@@ -4,7 +4,7 @@
4from lib.tests.helpers import TemplateTest4from lib.tests.helpers import TemplateTest
5from lib.tests.sample import (5from lib.tests.sample import (
6 SAMPLE_DB_UNIT_DATA, SAMPLE_LEADER_CONTEXT_DATA, SAMPLE_AMQP_UNIT_DATA,6 SAMPLE_DB_UNIT_DATA, SAMPLE_LEADER_CONTEXT_DATA, SAMPLE_AMQP_UNIT_DATA,
7 SAMPLE_HOSTED_DATA)7 SAMPLE_HOSTED_DATA, SAMPLE_WEBSITE_UNIT_DATA)
88
99
10class ServiceConfTest(TemplateTest):10class ServiceConfTest(TemplateTest):
@@ -16,6 +16,7 @@
16 self.context = {16 self.context = {
17 "db": [SAMPLE_DB_UNIT_DATA.copy()],17 "db": [SAMPLE_DB_UNIT_DATA.copy()],
18 "amqp": [SAMPLE_AMQP_UNIT_DATA.copy()],18 "amqp": [SAMPLE_AMQP_UNIT_DATA.copy()],
19 "haproxy": SAMPLE_WEBSITE_UNIT_DATA,
19 "leader": SAMPLE_LEADER_CONTEXT_DATA.copy(),20 "leader": SAMPLE_LEADER_CONTEXT_DATA.copy(),
20 "hosted": [SAMPLE_HOSTED_DATA.copy()],21 "hosted": [SAMPLE_HOSTED_DATA.copy()],
21 "config": {},22 "config": {},
@@ -93,6 +94,29 @@
93 config.readfp(buffer)94 config.readfp(buffer)
94 self.assertEqual("1.2.3.4", config.get("package-search", "host"))95 self.assertEqual("1.2.3.4", config.get("package-search", "host"))
9596
97 def test_render_with_haproxy_address_as_root_url(self):
98 """
99 The service.conf file has root-url set to the haproxy public IP if the
100 config doesn't have a root-url entry.
101 """
102 self.context["haproxy"]["public-address"] = "4.3.2.1"
103
104 buffer = StringIO(self.template.render(self.context))
105 config = ConfigParser()
106 config.readfp(buffer)
107 self.assertEqual("https://4.3.2.1/", config.get("global", "root-url"))
108
109 def test_render_with_config_provided_root_url(self):
110 """
111 The service.conf file has root-url set to the content of the root-url
112 charm config option if it is specified.
113 """
114 self.context["config"]["root-url"] = "https://8.8.8.8/"
115 buffer = StringIO(self.template.render(self.context))
116 config = ConfigParser()
117 config.readfp(buffer)
118 self.assertEqual("https://8.8.8.8/", config.get("global", "root-url"))
119
96120
97class LandscapeDefaultsTest(TemplateTest):121class LandscapeDefaultsTest(TemplateTest):
98122
99123
=== added file 'hooks/lib/tests/test_utils.py'
--- hooks/lib/tests/test_utils.py 1970-01-01 00:00:00 +0000
+++ hooks/lib/tests/test_utils.py 2015-04-23 14:34:15 +0000
@@ -0,0 +1,29 @@
1import unittest
2
3from lib.utils import is_valid_url
4
5
6class IsValidURLTestCase(unittest.TestCase):
7
8 def test_missing_trailing_slash(self):
9 """No trailing slash means the URL is invalid for root-url."""
10 self.assertFalse(is_valid_url("https://google.com"))
11
12 def test_missing_protocol(self):
13 """A protocol (http or https) is needed for a URL to be a valid
14 root-url."""
15 self.assertFalse(is_valid_url("ftp://google.com/"))
16
17 def test_http_or_https_protocol(self):
18 """The URL for the root-url should have either http or https as a
19 protocol."""
20 self.assertTrue(is_valid_url("http://google.com/"))
21 self.assertTrue(is_valid_url("https://google.com/"))
22
23 def test_missing_protocol_delimiter(self):
24 """We need the URL to have the protocol field"""
25 self.assertFalse(is_valid_url("httpisawesome.com/"))
26
27 def test_correct_url_is_validated(self):
28 """A "correct" URL is validated by the is_valid_url function."""
29 self.assertTrue(is_valid_url("http://example.com:9090/blah/"))
030
=== added file 'hooks/lib/utils.py'
--- hooks/lib/utils.py 1970-01-01 00:00:00 +0000
+++ hooks/lib/utils.py 2015-04-23 14:34:15 +0000
@@ -0,0 +1,14 @@
1
2
3def is_valid_url(value):
4 """
5 A helper to validate a string is a URL suitable to use as root-url.
6 """
7 if not value[-1] == "/":
8 return False
9 if not value.startswith("http"):
10 return False
11 if not "://" in value:
12 return False
13
14 return True
015
=== modified file 'templates/service.conf'
--- templates/service.conf 2015-04-20 08:56:42 +0000
+++ templates/service.conf 2015-04-23 14:34:15 +0000
@@ -4,6 +4,11 @@
4# Syslog address is either a socket path or server:port string.4# Syslog address is either a socket path or server:port string.
5syslog-address = /dev/log5syslog-address = /dev/log
6deployment-mode = {{ hosted[0]["deployment-mode"] }}6deployment-mode = {{ hosted[0]["deployment-mode"] }}
7{% if config["root-url"] %}
8root-url = {{ config["root-url"] }}
9{% else %}
10root-url = https://{{ haproxy["public-address"] }}/
11{% endif %}
712
8[stores]13[stores]
9host = {{ postgres["host"] }}:{{ postgres["port"] }}14host = {{ postgres["host"] }}:{{ postgres["port"] }}

Subscribers

People subscribed via source and target branches