Merge lp:~dpb/landscape-charm/vhost-config-relation into lp:~landscape/landscape-charm/trunk
- vhost-config-relation
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | David Britton |
Approved revision: | 198 |
Merged at revision: | 181 |
Proposed branch: | lp:~dpb/landscape-charm/vhost-config-relation |
Merge into: | lp:~landscape/landscape-charm/trunk |
Diff against target: |
523 lines (+316/-27) 7 files modified
Makefile (+14/-2) config/landscape-deployments.yaml (+3/-2) hooks/hooks.py (+96/-17) hooks/lib/util.py (+32/-6) hooks/test_hooks.py (+167/-0) metadata.yaml (+2/-0) tests/01-begin (+2/-0) |
To merge this branch: | bzr merge lp:~dpb/landscape-charm/vhost-config-relation |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andreas Hasenack | Approve | ||
Adam Collard (community) | Approve | ||
Review via email: mp+222878@code.launchpad.net |
Commit message
We can now link apache2 and landscape in a relation. This will allow the rather awkward step of "specify this random vhost base64 blob in a juju set command" to be removed from our instructions and bundle.yaml. It also gets us one step closer to a real bundle that can go into the charm store.
Also:
* ssl cert stored from apache
* get apache2 servername and use it to set the root_url of landscape
* some deve cleanups
Description of the change
In this exciting change, we can now link apache2 and landscape in a relation. This will allow the rather awkward step of "specify this random vhost base64 blob in a juju set command" to be removed from our instructions and bundle.yaml.
It also gets us one step closer to a real bundle that can go into the charm store.
Instead, landsacpe will specify it over a relation to apache2 directly. The admin is still in control of this behavior with add-relation/
Major Features:
- vhost-config relation added (need latest apache2 charm to test)
- base64 blobs removed from deployer yaml.
- self-signed-SSL cert and apache "servername" auto-discovered on the same relation, cert stored in the usual place, root_url updated automatically with servername
- unit test cases
- I'm going to add an integration test case to check that the ssl cert and root url are set correctly, but maybe in a follow-on branch, I'll see.
To test:
- make lint test integration-test
- deploy and of the landscape-bundles and see if the ssl cert is passed correctly and stored on the server in the right place (landscape/0).
David Britton (dpb) wrote : | # |
Thanks Adam, please look again, I fixed all those.
David Britton (dpb) wrote : | # |
BTW, I committed r184 and tested it, and it hit a very weird error in the locking code. Not only did it hit an error, but the error was swallowed and the charm just got stuck by exiting 0 and blocking other relations.
I need to investigate not only why the change was bad, but also why it didn't get an error back to juju (exit 1)
David Britton (dpb) wrote : | # |
> BTW, I committed r184 and tested it, and it hit a very weird error in the
> locking code. Not only did it hit an error, but the error was swallowed and
> the charm just got stuck by exiting 0 and blocking other relations.
>
> I need to investigate not only why the change was bad, but also why it didn't
> get an error back to juju (exit 1)
So... I've reverted that for now.
David Britton (dpb) wrote : | # |
OK, remerged, and ready for a second review. Sorry, got distracted with the documentation earlier today and when I pushed it was not ready/tested.
During testing, I addressed a couple things that I noticed:
1) I added a make target to deploy from the current branch. Simplifies testing a bit.
2) I removed a couple of try/except blocks that were trapping errors in the lock_exclusive code path. That code shouldn't fail unless it legitimately *should* fail the hook. It blocks getting the lock by design, that try/execpt block was inserted chasing another bug and was never removed. It finally bit me during testing, and I figured it was time to remove it. Also added a test to check this case.
Adam Collard (adam-collard) wrote : | # |
Looks good! A couple of possible cleanups inline below. Successfully deployed dense MAAS target (after fighting AMT for a bit).
Andreas Hasenack (ahasenack) wrote : | # |
I deployed max, and all is good. I then destroyed the single apache2 unit (juju destroy-unit apache2/0), and added a new one, and as a new machine (not lxc). It got the host bohr.beretstack. I logged in, saw LDS as normal, but in the settings page, root-url was still set to the IP of the unit I destroyed.
Is this scenario something this branch should handle?
The certificate was regenerated, as expected, since it's a whole new unit, and landscape/0's /etc/ssl/
root@juju-
subject= /CN=bohr.beretstack
AFAICT, the only thing that didn't get updated was the root-url in the landscape DB.
Andreas Hasenack (ahasenack) wrote : | # |
Hmm, wait a second. The DB has the right root-url:
landscape-
key | value
-------
landscape.
landscape.root_url | u24:https:/
(2 rows)
system_email_sender is still wrong, btw.
What is odd is that the landscape UI is showing the wrong root_url in the standalone-settings page. There it is https:/
Andreas Hasenack (ahasenack) wrote : | # |
The root url in the settings page was the correct one only after I restarted the app services. This looks to be a bug in landscape code.
David Britton (dpb) wrote : | # |
Finalized feedback. Also added a relation between landscape-app and apache2 (vhost-config). There may yet be more bugs with the max deployment, but will fix those in follow-ups if need be.
🤖 Landscape Builder (landscape-builder) wrote : | # |
The attempt to merge lp:~davidpbritton/landscape-charm/vhost-config-relation into lp:landscape-charm/trunk failed. Below is the output from the failed tests.
trial hooks
hooks.test_hooks
TestHooksService
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
- 198. By David Britton
-
remove lint, fix broken test, add JUJU_ENV to -p in make integration-test
Preview Diff
1 | === modified file 'Makefile' |
2 | --- Makefile 2014-06-06 19:57:27 +0000 |
3 | +++ Makefile 2014-06-14 03:50:22 +0000 |
4 | @@ -16,8 +16,16 @@ |
5 | $(EXTRA_UPDATE_ARGUMENTS) \ |
6 | apache2 postgresql juju-gui haproxy rabbitmq-server nfs |
7 | |
8 | -integration-test: verify-juju-test config/repo-file config/license-file config/vhostssl.tmpl config/vhost.tmpl |
9 | - juju test --set-e -p SKIP_SLOW_TESTS,DEPLOYER_TARGET,JUJU_HOME -v --timeout 3000s |
10 | +test-depends: verify-juju-test config/repo-file config/license-file config/vhostssl.tmpl config/vhost.tmpl |
11 | + |
12 | +integration-test: test-depends |
13 | + juju test --set-e -p SKIP_SLOW_TESTS,DEPLOYER_TARGET,JUJU_HOME,JUJU_ENV -v --timeout 3000s |
14 | + |
15 | +deploy-dense-maas: test-depends |
16 | + SKIP_TESTS=1 DEPLOYER_TARGET=landscape-dense-maas tests/01-begin |
17 | + |
18 | +deploy-max-dense-maas: test-depends |
19 | + SKIP_TESTS=1 DEPLOYER_TARGET=landscape-max-dense-maas tests/01-begin |
20 | |
21 | lint: |
22 | flake8 --exclude=charmhelpers hooks |
23 | @@ -28,9 +36,13 @@ |
24 | clean: clean-integration-test |
25 | |
26 | .PHONY: lint \ |
27 | + test-depends \ |
28 | + deploy-dense-maas \ |
29 | + deploy-max-dense-maas \ |
30 | integration-test \ |
31 | verify-juju-test \ |
32 | test \ |
33 | clean \ |
34 | clean-integration-test \ |
35 | update-charm-revision-numbers |
36 | + |
37 | |
38 | === modified file 'config/landscape-deployments.yaml' |
39 | --- config/landscape-deployments.yaml 2014-06-12 10:38:38 +0000 |
40 | +++ config/landscape-deployments.yaml 2014-06-14 03:50:22 +0000 |
41 | @@ -25,8 +25,6 @@ |
42 | ssl_cert: SELFSIGNED |
43 | ssl_certlocation: apache2.cert |
44 | ssl_keylocation: apache2.key |
45 | - vhost_https_template: include-base64://vhostssl.tmpl |
46 | - vhost_http_template: include-base64://vhost.tmpl |
47 | |
48 | landscape: |
49 | inherits: _common |
50 | @@ -53,6 +51,7 @@ |
51 | relations: |
52 | - [landscape, rabbitmq-server] |
53 | - [landscape, haproxy] |
54 | + - ["landscape:vhost-config", "apache2:vhost-config"] |
55 | - ["landscape:db-admin", "postgresql:db-admin"] |
56 | - ["haproxy:website", "apache2:reverseproxy"] |
57 | - [landscape-msg, rabbitmq-server] |
58 | @@ -119,6 +118,7 @@ |
59 | relations: |
60 | - [landscape, rabbitmq-server] |
61 | - [landscape, haproxy] |
62 | + - ["landscape:vhost-config", "apache2:vhost-config"] |
63 | - ["landscape:db-admin", "postgresql:db-admin"] |
64 | - ["haproxy:website", "apache2:reverseproxy"] |
65 | - [landscape-msg, rabbitmq-server] |
66 | @@ -130,6 +130,7 @@ |
67 | - [landscape-app, rabbitmq-server] |
68 | - [landscape-app, haproxy] |
69 | - ["landscape-app:db-admin", "postgresql:db-admin"] |
70 | + - ["landscape-app:vhost-config", "apache2:vhost-config"] |
71 | |
72 | landscape-max-dense-maas: |
73 | inherits: landscape-max |
74 | |
75 | === modified file 'hooks/hooks.py' |
76 | --- hooks/hooks.py 2014-06-09 20:33:41 +0000 |
77 | +++ hooks/hooks.py 2014-06-14 03:50:22 +0000 |
78 | @@ -9,15 +9,15 @@ |
79 | from lib import util |
80 | from lib.juju import Juju |
81 | |
82 | -from base64 import b64encode |
83 | +from base64 import b64encode, b64decode |
84 | from configobj import ConfigObj, ConfigObjError |
85 | +from contextlib import closing |
86 | from copy import deepcopy |
87 | import cStringIO |
88 | import datetime |
89 | import grp |
90 | import os |
91 | import psutil |
92 | -import psycopg2 |
93 | import pwd |
94 | import pycurl |
95 | import re |
96 | @@ -27,6 +27,9 @@ |
97 | from subprocess import check_call, check_output, CalledProcessError, call |
98 | |
99 | |
100 | +SSL_CERT_LOCATION = "/etc/ssl/certs/landscape_server_ca.crt" |
101 | + |
102 | + |
103 | def _get_installed_version(name): |
104 | """Returns the version string of name using dpkg-query or returns None""" |
105 | try: |
106 | @@ -95,6 +98,29 @@ |
107 | services=yaml.safe_dump(_get_services_haproxy())) |
108 | |
109 | |
110 | +def notify_vhost_config_relation(relation_id=None): |
111 | + """ |
112 | + Notify the vhost-config relation. |
113 | + |
114 | + This will mark it "ready to proceed". If relation_id is specified |
115 | + use that as the relation context, otherwise look up and notify all |
116 | + vhost-config relations. |
117 | + """ |
118 | + vhosts = [] |
119 | + with open("%s/config/vhostssl.tmpl" % ROOT, 'r') as handle: |
120 | + vhosts.append({ |
121 | + "port": "443", "template": b64encode(handle.read())}) |
122 | + with open("%s/config/vhost.tmpl" % ROOT, 'r') as handle: |
123 | + vhosts.append({ |
124 | + "port": "80", "template": b64encode(handle.read())}) |
125 | + |
126 | + relation_ids = [relation_id] |
127 | + if relation_id is None: |
128 | + relation_ids = juju.relation_ids("vhost-config") |
129 | + for relation_id in relation_ids: |
130 | + juju.relation_set(relation_id=relation_id, vhosts=yaml.dump(vhosts)) |
131 | + |
132 | + |
133 | def db_admin_relation_joined(): |
134 | pass |
135 | |
136 | @@ -150,20 +176,13 @@ |
137 | "password": password}, |
138 | "schema": {"store_user": admin, "store_password": admin_password}}) |
139 | |
140 | - try: |
141 | - # Name as lock so we don't try to reuse it as a database connection |
142 | - lock = util.connect_exclusive(host, admin, admin_password) |
143 | - except psycopg2.Error: |
144 | - # Another unit is performing database configuration. |
145 | - pass |
146 | - else: |
147 | - try: |
148 | - util.create_user(user, password, host, admin, admin_password) |
149 | - _create_maintenance_user(password, host, admin, admin_password) |
150 | - check_call("setup-landscape-server") |
151 | - finally: |
152 | - juju.juju_log("Landscape database initialized!") |
153 | - lock.close() |
154 | + with closing(util.connect_exclusive(host, admin, admin_password)): |
155 | + util.create_user(user, password, host, admin, admin_password) |
156 | + _create_maintenance_user(password, host, admin, admin_password) |
157 | + check_call("setup-landscape-server") |
158 | + juju.juju_log("Landscape database initialized!") |
159 | + |
160 | + notify_vhost_config_relation() |
161 | |
162 | try: |
163 | # Handle remove-relation db-admin. This call will fail because |
164 | @@ -316,6 +335,65 @@ |
165 | config_changed() |
166 | |
167 | |
168 | +def vhost_config_relation_changed(): |
169 | + """Relate to apache to configure a vhost. |
170 | + |
171 | + This hook will supply vhost configuration as well as read simple data |
172 | + out of apache (servername, certificate). This data is necessary for |
173 | + informing clients of the correct URL and cert to use when connecting |
174 | + to the server. |
175 | + """ |
176 | + notify_vhost_config_relation(os.environ.get("JUJU_RELATION_ID", None)) |
177 | + |
178 | + config_obj = _get_config_obj(LANDSCAPE_SERVICE_CONF) |
179 | + try: |
180 | + section = config_obj["stores"] |
181 | + database = section["main"] |
182 | + host = section["host"] |
183 | + user = section["user"] |
184 | + password = section["password"] |
185 | + except KeyError: |
186 | + juju.juju_log("Database not ready yet, deferring call") |
187 | + sys.exit(0) |
188 | + |
189 | + relids = juju.relation_ids("vhost-config") |
190 | + if relids: |
191 | + relid = relids[0] |
192 | + apache2_unit = juju.relation_list(relid)[0] |
193 | + apache_servername = juju.relation_get( |
194 | + "servername", unit_name=apache2_unit, relation_id=relid) |
195 | + else: |
196 | + apache_servername = juju.relation_get("servername") |
197 | + |
198 | + if not apache_servername: |
199 | + juju.juju_log("Waiting for data from apache, deferring") |
200 | + sys.exit(0) |
201 | + apache_url = "https://%s/" % apache_servername |
202 | + |
203 | + if not _is_db_up(): |
204 | + juju.juju_log("Waiting for database to become available, deferring.") |
205 | + sys.exit(0) |
206 | + |
207 | + with closing(util.connect_exclusive(host, user, password)): |
208 | + juju.juju_log("Updating Landscape root_url: %s" % apache_url) |
209 | + util.change_root_url(database, user, password, host, apache_url) |
210 | + |
211 | + # This data may or may not be present, dependeing on if cert is self |
212 | + # signed from apache. |
213 | + ssl_cert = juju.relation_get( |
214 | + "ssl_cert", unit_name=apache2_unit, relation_id=relid) |
215 | + if ssl_cert: |
216 | + juju.juju_log("Writing new SSL cert: %s" % SSL_CERT_LOCATION) |
217 | + with open(SSL_CERT_LOCATION, 'w') as f: |
218 | + f.write(str(b64decode(ssl_cert))) |
219 | + else: |
220 | + if os.path.exists(SSL_CERT_LOCATION): |
221 | + os.remove(SSL_CERT_LOCATION) |
222 | + |
223 | + # only starts services again if is_db_up and _is_amqp_up |
224 | + config_changed() |
225 | + |
226 | + |
227 | def config_changed(): |
228 | """Update and restart services based on config setting changes. |
229 | |
230 | @@ -743,7 +821,8 @@ |
231 | "data-relation-changed": data_relation_changed, |
232 | "db-admin-relation-joined": db_admin_relation_joined, |
233 | "db-admin-relation-changed": db_admin_relation_changed, |
234 | - "website-relation-joined": website_relation_joined} |
235 | + "website-relation-joined": website_relation_joined, |
236 | + "vhost-config-relation-changed": vhost_config_relation_changed} |
237 | hook = os.path.basename(sys.argv[0]) |
238 | # If the hook is unsupported, let it raise a KeyError and exit with error. |
239 | hooks[hook]() |
240 | |
241 | === modified file 'hooks/lib/util.py' |
242 | --- hooks/lib/util.py 2014-01-31 20:38:36 +0000 |
243 | +++ hooks/lib/util.py 2014-06-14 03:50:22 +0000 |
244 | @@ -2,8 +2,9 @@ |
245 | Utility library for juju hooks |
246 | """ |
247 | |
248 | -from psycopg2 import connect |
249 | +from psycopg2 import connect, Error as psycopg2Error |
250 | from juju import Juju |
251 | +from contextlib import closing |
252 | |
253 | juju = Juju() |
254 | |
255 | @@ -38,7 +39,7 @@ |
256 | password=admin_password) |
257 | try: |
258 | cur = conn.cursor() |
259 | - cur.execute("SELECT usename FROM pg_user WHERE usename='%s'" % user) |
260 | + cur.execute("SELECT usename FROM pg_user WHERE usename=%s", (user,)) |
261 | result = cur.fetchall() |
262 | if not result: |
263 | juju.juju_log("Creating postgres db user: %s" % user) |
264 | @@ -48,6 +49,32 @@ |
265 | conn.close() |
266 | |
267 | |
268 | +def change_root_url(database, user, password, host, url): |
269 | + """Change the root url in the database.""" |
270 | + url = "u%s:%s" % (len(url), url) |
271 | + with closing(connect(database=database, host=host, |
272 | + user=user, password=password)) as conn: |
273 | + cur = conn.cursor() |
274 | + cur.execute("SELECT encode(key, 'escape'),encode(value, 'escape') " |
275 | + "FROM system_configuration " |
276 | + "WHERE key='landscape.root_url' FOR UPDATE") |
277 | + result = cur.fetchall() |
278 | + if not result: |
279 | + juju.juju_log("Setting new root_url: %s" % url) |
280 | + cur.execute( |
281 | + "INSERT INTO system_configuration " |
282 | + "VALUES (decode('landscape.root_url', 'escape'), " |
283 | + " decode(%s, 'escape'))", (url,)) |
284 | + else: |
285 | + juju.juju_log("Updating root_url %s => %s" % (result, url)) |
286 | + cur.execute( |
287 | + "UPDATE system_configuration " |
288 | + "SET key=decode('landscape.root_url', 'escape')," |
289 | + " value=decode(%s, 'escape') " |
290 | + "WHERE encode(key, 'escape')='landscape.root_url'", (url,)) |
291 | + conn.commit() |
292 | + |
293 | + |
294 | def is_db_up(database, host, user, password): |
295 | """ |
296 | Return True if the database relation is configured with write permission, |
297 | @@ -59,10 +86,9 @@ |
298 | # Ensure we are user with write access, to avoid hot standby dbs |
299 | cur.execute( |
300 | 'CREATE TEMP TABLE "write_access_test_%s" (id serial PRIMARY KEY) ' |
301 | - "ON COMMIT DROP" |
302 | - % juju.local_unit().replace("/", "_")) |
303 | - except Exception as e: |
304 | - juju.juju_log(str(e)) |
305 | + "ON COMMIT DROP" % juju.local_unit().replace("/", "_")) |
306 | + except psycopg2Error as e: |
307 | + juju.juju_log("Database not yet up: %s" % e) |
308 | return False |
309 | else: |
310 | return True |
311 | |
312 | === modified file 'hooks/test_hooks.py' |
313 | --- hooks/test_hooks.py 2014-05-23 20:51:15 +0000 |
314 | +++ hooks/test_hooks.py 2014-06-14 03:50:22 +0000 |
315 | @@ -3,9 +3,11 @@ |
316 | import hooks |
317 | import mocker |
318 | import os |
319 | +import psycopg2 |
320 | import pycurl |
321 | import stat |
322 | import subprocess |
323 | +import tempfile |
324 | import yaml |
325 | |
326 | |
327 | @@ -1482,6 +1484,171 @@ |
328 | baseline = (("services", yaml.safe_dump(self.all_services)),) |
329 | self.assertEqual(baseline, hooks.juju._outgoing_relation_data) |
330 | |
331 | + def test_notify_vhost_config_relation_specify_id(self): |
332 | + """ |
333 | + notify the vhost-config relation on a separate ID. |
334 | + """ |
335 | + hooks.notify_vhost_config_relation("foo/0") |
336 | + with open("%s/config/vhostssl.tmpl" % hooks.ROOT, 'r') as f: |
337 | + vhostssl_template = f.read() |
338 | + with open("%s/config/vhost.tmpl" % hooks.ROOT, 'r') as f: |
339 | + vhost_template = f.read() |
340 | + baseline = yaml.dump( |
341 | + [{"port": "443", "template": base64.b64encode(vhostssl_template)}, |
342 | + {"port": "80", "template": base64.b64encode(vhost_template)}]) |
343 | + self.assertEqual( |
344 | + (("vhosts", baseline),), hooks.juju._outgoing_relation_data) |
345 | + |
346 | + def test_notify_vhost_config_relation(self): |
347 | + """notify the vhost-config relation on the "current" ID.""" |
348 | + hooks.notify_vhost_config_relation() |
349 | + with open("%s/config/vhostssl.tmpl" % hooks.ROOT, 'r') as f: |
350 | + vhostssl_template = f.read() |
351 | + with open("%s/config/vhost.tmpl" % hooks.ROOT, 'r') as f: |
352 | + vhost_template = f.read() |
353 | + baseline = yaml.dump( |
354 | + [{"port": "443", "template": base64.b64encode(vhostssl_template)}, |
355 | + {"port": "80", "template": base64.b64encode(vhost_template)}]) |
356 | + self.assertEqual( |
357 | + (("vhosts", baseline),), hooks.juju._outgoing_relation_data) |
358 | + |
359 | + def test_vhost_config_relation_changed_exit_no_configuration(self): |
360 | + """Ensure vhost_relation_changed deferrs if db is not up.""" |
361 | + self.assertRaises(SystemExit, hooks.vhost_config_relation_changed) |
362 | + self.assertEquals(len(hooks.juju._logs), 1) |
363 | + self.assertIn('Database not ready yet', hooks.juju._logs[0]) |
364 | + |
365 | + def test_vhost_config_relation_changed_wait_apache_servername(self): |
366 | + """Ensure vhost_relation_changed deferrs if db is not up.""" |
367 | + _get_config_obj = self.mocker.replace(hooks._get_config_obj) |
368 | + _get_config_obj(hooks.LANDSCAPE_SERVICE_CONF) |
369 | + self.mocker.result({ |
370 | + "stores": { |
371 | + "main": "database", |
372 | + "host": "host", |
373 | + "user": "user", |
374 | + "password": "password"}}) |
375 | + notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation) |
376 | + notify_vhost(None) |
377 | + self.mocker.replay() |
378 | + self.assertRaises(SystemExit, hooks.vhost_config_relation_changed) |
379 | + self.assertIn('Waiting for data from apache', hooks.juju._logs[-1]) |
380 | + |
381 | + def test_vhost_config_relation_changed_fail_root_url(self): |
382 | + """Ensure vhost_relation_changed deferrs if db is not up.""" |
383 | + _get_config_obj = self.mocker.replace(hooks._get_config_obj) |
384 | + _get_config_obj(hooks.LANDSCAPE_SERVICE_CONF) |
385 | + hooks.juju._incoming_relation_data += (("servername", "foobar"),) |
386 | + self.mocker.result({ |
387 | + "stores": { |
388 | + "main": "database", |
389 | + "host": "host", |
390 | + "user": "user", |
391 | + "password": "password"}}) |
392 | + notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation) |
393 | + notify_vhost(None) |
394 | + is_db_up = self.mocker.replace(hooks._is_db_up) |
395 | + is_db_up() |
396 | + self.mocker.result(False) |
397 | + self.mocker.replay() |
398 | + self.assertRaises(SystemExit, hooks.vhost_config_relation_changed) |
399 | + self.assertIn( |
400 | + 'Waiting for database to become available, deferring', |
401 | + hooks.juju._logs[-1]) |
402 | + |
403 | + def test_vhost_config_relation_changed_fail_root_url_db_update(self): |
404 | + """vhost_config_relation_changed should error if db update fails""" |
405 | + _get_config_obj = self.mocker.replace(hooks._get_config_obj) |
406 | + _get_config_obj(hooks.LANDSCAPE_SERVICE_CONF) |
407 | + hooks.juju._incoming_relation_data += (("servername", "foobar"),) |
408 | + self.mocker.result({ |
409 | + "stores": { |
410 | + "main": "database", |
411 | + "host": "host", |
412 | + "user": "user", |
413 | + "password": "password"}}) |
414 | + notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation) |
415 | + notify_vhost(None) |
416 | + is_db_up = self.mocker.replace(hooks._is_db_up) |
417 | + is_db_up() |
418 | + self.mocker.result(True) |
419 | + self.mocker.replay() |
420 | + self.assertRaises(psycopg2.Error, hooks.vhost_config_relation_changed) |
421 | + |
422 | + def test_vhost_config_relation_changed_cert_not_provided(self): |
423 | + """ |
424 | + Ensure vhost_relation_changed runs to completion. |
425 | + |
426 | + Existing cert should be removed. |
427 | + """ |
428 | + hooks.SSL_CERT_LOCATION = tempfile.NamedTemporaryFile().name |
429 | + _get_config_obj = self.mocker.replace(hooks._get_config_obj) |
430 | + _get_config_obj(hooks.LANDSCAPE_SERVICE_CONF) |
431 | + hooks.juju._incoming_relation_data += (("servername", "foobar"),) |
432 | + self.mocker.result({ |
433 | + "stores": { |
434 | + "main": "database", |
435 | + "host": "host", |
436 | + "user": "user", |
437 | + "password": "password"}}) |
438 | + notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation) |
439 | + notify_vhost(None) |
440 | + mock_conn = self.mocker.mock() |
441 | + mock_conn.close() |
442 | + connect_exclusive = self.mocker.replace(hooks.util.connect_exclusive) |
443 | + connect_exclusive("host", "user", "password") |
444 | + self.mocker.result(mock_conn) |
445 | + change_root_url = self.mocker.replace(hooks.util.change_root_url) |
446 | + change_root_url( |
447 | + "database", "user", "password", "host", "https://foobar/") |
448 | + config_changed = self.mocker.replace(hooks.config_changed) |
449 | + config_changed() |
450 | + is_db_up = self.mocker.replace(hooks._is_db_up) |
451 | + is_db_up() |
452 | + self.mocker.result(True) |
453 | + self.mocker.replay() |
454 | + hooks.vhost_config_relation_changed() |
455 | + self.assertFalse(os.path.exists(hooks.SSL_CERT_LOCATION)) |
456 | + |
457 | + def test_vhost_config_relation_changed_ssl_cert_provided(self): |
458 | + """ |
459 | + Ensure vhost_relation_changed runs to completion. |
460 | + |
461 | + Cert passed in to other side of relation should be written on disk. |
462 | + """ |
463 | + hooks.SSL_CERT_LOCATION = tempfile.NamedTemporaryFile().name |
464 | + _get_config_obj = self.mocker.replace(hooks._get_config_obj) |
465 | + _get_config_obj(hooks.LANDSCAPE_SERVICE_CONF) |
466 | + hooks.juju._incoming_relation_data += (("servername", "foobar"),) |
467 | + hooks.juju._incoming_relation_data += ( |
468 | + ("ssl_cert", base64.b64encode("foobar")),) |
469 | + self.mocker.result({ |
470 | + "stores": { |
471 | + "main": "database", |
472 | + "host": "host", |
473 | + "user": "user", |
474 | + "password": "password"}}) |
475 | + notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation) |
476 | + notify_vhost(None) |
477 | + is_db_up = self.mocker.replace(hooks._is_db_up) |
478 | + is_db_up() |
479 | + self.mocker.result(True) |
480 | + mock_conn = self.mocker.mock() |
481 | + mock_conn.close() |
482 | + connect_exclusive = self.mocker.replace(hooks.util.connect_exclusive) |
483 | + connect_exclusive("host", "user", "password") |
484 | + self.mocker.result(mock_conn) |
485 | + change_root_url = self.mocker.replace(hooks.util.change_root_url) |
486 | + change_root_url( |
487 | + "database", "user", "password", "host", "https://foobar/") |
488 | + config_changed = self.mocker.replace(hooks.config_changed) |
489 | + config_changed() |
490 | + self.mocker.replay() |
491 | + hooks.vhost_config_relation_changed() |
492 | + self.assertTrue(os.path.exists(hooks.SSL_CERT_LOCATION)) |
493 | + with open(hooks.SSL_CERT_LOCATION, 'r') as f: |
494 | + self.assertEqual("foobar", f.read()) |
495 | + |
496 | |
497 | class TestHooksUtils(TestHooks): |
498 | def test__setup_apache(self): |
499 | |
500 | === added symlink 'hooks/vhost-config-relation-changed' |
501 | === target is u'hooks.py' |
502 | === modified file 'metadata.yaml' |
503 | --- metadata.yaml 2014-04-25 15:04:36 +0000 |
504 | +++ metadata.yaml 2014-06-14 03:50:22 +0000 |
505 | @@ -23,3 +23,5 @@ |
506 | optional: true |
507 | website: |
508 | interface: http |
509 | + vhost-config: |
510 | + interface: apache-vhost-config |
511 | |
512 | === modified file 'tests/01-begin' |
513 | --- tests/01-begin 2014-06-11 12:50:56 +0000 |
514 | +++ tests/01-begin 2014-06-14 03:50:22 +0000 |
515 | @@ -191,6 +191,8 @@ |
516 | self.service_name = service_name |
517 | |
518 | |
519 | +@unittest.skipIf( |
520 | + getenv("SKIP_TESTS", None), "Requested to skip all tests.") |
521 | class BaseLandscapeTests(unittest.TestCase): |
522 | """ |
523 | Base class with some commonality between all test classes. |
SQL injection vulnerabilities, see inline comments.