Merge lp:~dpb/landscape-charm/vhost-config-relation into lp:~landscape/landscape-charm/trunk

Proposed by David Britton
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
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/remove-relation, but the defaults should serve most all users well.

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).

To post a comment you must log in.
Revision history for this message
Adam Collard (adam-collard) wrote :

SQL injection vulnerabilities, see inline comments.

review: Needs Fixing
Revision history for this message
David Britton (dpb) wrote :

Thanks Adam, please look again, I fixed all those.

Revision history for this message
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)

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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).

review: Approve
Revision history for this message
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/certs/landscape_server_ca.crt has the updated one, which is excellent:

root@juju-machine-0-lxc-14:/etc/ssl/certs# openssl x509 -in landscape_server_ca.crt -noout -subject
subject= /CN=bohr.beretstack

AFAICT, the only thing that didn't get updated was the root-url in the landscape DB.

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

Hmm, wait a second. The DB has the right root-url:

landscape-standalone-main=# select encode(key, 'escape') as key, encode(value, 'escape') as value from system_configuration ;
              key | value
-------------------------------+------------------------------
 landscape.system_email_sender | u17:noreply@10.1.4.61
 landscape.root_url | u24:https://bohr.beretstack/
(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://10.1.4.61/, and I'm accessing the UI via https://bohr.beretstack/account/standalone/standalone-settings

review: Needs Information
Revision history for this message
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.

Revision history for this message
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.

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

+1

review: Approve
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :
Download full text (6.8 KiB)

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__download_file_failure ... [OK]
    test__download_file_success ... [OK]
    test__enable_service ... [OK]
    test__enable_wrong_service ... [OK]
    test__install_license_text ... [OK]
    test__install_license_url ... [OK]
    test__replace_in_file ... [OK]
    test_amqp_relation_changed_no_hostname_password ... [OK]
    test_amqp_relation_joined ... [OK]
    test_calc_daemon_count ... [OK]
    test_config_changed ... [OK]
    test_config_changed_service_count_bare ... [OK]
    test_config_changed_service_count_labeled ... [OK]
    test_config_changed_service_count_update_haproxy ... [OK]
    test_config_changed_starts_landscape ... [OK]
    test_config_changed_without_amqp_skips_start ... [OK]
    test_config_changed_without_db_skips_start ... [OK]
    test_config_changed_zero ... [OK]
    test_data_relation_changed_creates_new_log_and_repository_paths ... [OK]
    test_data_relation_changed_error_on_mountpoint_from_subordinate ... [OK]
    test_data_relation_changed_sets_mountpoint_awaits_init ... [OK]
    test_data_relation_changed_success_no_repository_data ... [OK]
    test_data_relation_changed_success_no_repository_path ... [OK]
    test_data_relation_changed_success_with_repository_data ... [OK]
    test_db_admin_relation_changed ... [ERROR]
    test_db_admin_relation_changed_failover_state_ignore ... [OK]
    test_db_admin_relation_changed_hot_standby_state_ignore ... [OK]
    test_db_admin_relation_changed_no_config_if_db_down ... [OK]
    test_db_admin_relation_changed_no_user ... [OK]
    test_db_admin_relation_changed_not_in_allowed_units ... [OK]
    test_db_admin_relation_changed_standalone_state_ignore ... [OK]
    test_get_requested_service_count ... [OK]
    test_get_requested_services ... [OK]
    test_get_services_dict ... [OK]
    test_get_services_non_proxied ... [OK]
    test_handle_empty_license ... [OK]
    test_handle_no_license ... [OK]
    test_is_db_up_db_not_configur...

Read more...

198. By David Britton

remove lint, fix broken test, add JUJU_ENV to -p in make integration-test

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile'
--- Makefile 2014-06-06 19:57:27 +0000
+++ Makefile 2014-06-14 03:50:22 +0000
@@ -16,8 +16,16 @@
16 $(EXTRA_UPDATE_ARGUMENTS) \16 $(EXTRA_UPDATE_ARGUMENTS) \
17 apache2 postgresql juju-gui haproxy rabbitmq-server nfs17 apache2 postgresql juju-gui haproxy rabbitmq-server nfs
1818
19integration-test: verify-juju-test config/repo-file config/license-file config/vhostssl.tmpl config/vhost.tmpl19test-depends: verify-juju-test config/repo-file config/license-file config/vhostssl.tmpl config/vhost.tmpl
20 juju test --set-e -p SKIP_SLOW_TESTS,DEPLOYER_TARGET,JUJU_HOME -v --timeout 3000s20
21integration-test: test-depends
22 juju test --set-e -p SKIP_SLOW_TESTS,DEPLOYER_TARGET,JUJU_HOME,JUJU_ENV -v --timeout 3000s
23
24deploy-dense-maas: test-depends
25 SKIP_TESTS=1 DEPLOYER_TARGET=landscape-dense-maas tests/01-begin
26
27deploy-max-dense-maas: test-depends
28 SKIP_TESTS=1 DEPLOYER_TARGET=landscape-max-dense-maas tests/01-begin
2129
22lint:30lint:
23 flake8 --exclude=charmhelpers hooks31 flake8 --exclude=charmhelpers hooks
@@ -28,9 +36,13 @@
28clean: clean-integration-test36clean: clean-integration-test
2937
30.PHONY: lint \38.PHONY: lint \
39 test-depends \
40 deploy-dense-maas \
41 deploy-max-dense-maas \
31 integration-test \42 integration-test \
32 verify-juju-test \43 verify-juju-test \
33 test \44 test \
34 clean \45 clean \
35 clean-integration-test \46 clean-integration-test \
36 update-charm-revision-numbers47 update-charm-revision-numbers
48
3749
=== modified file 'config/landscape-deployments.yaml'
--- config/landscape-deployments.yaml 2014-06-12 10:38:38 +0000
+++ config/landscape-deployments.yaml 2014-06-14 03:50:22 +0000
@@ -25,8 +25,6 @@
25 ssl_cert: SELFSIGNED25 ssl_cert: SELFSIGNED
26 ssl_certlocation: apache2.cert26 ssl_certlocation: apache2.cert
27 ssl_keylocation: apache2.key27 ssl_keylocation: apache2.key
28 vhost_https_template: include-base64://vhostssl.tmpl
29 vhost_http_template: include-base64://vhost.tmpl
3028
31landscape:29landscape:
32 inherits: _common30 inherits: _common
@@ -53,6 +51,7 @@
53 relations:51 relations:
54 - [landscape, rabbitmq-server]52 - [landscape, rabbitmq-server]
55 - [landscape, haproxy]53 - [landscape, haproxy]
54 - ["landscape:vhost-config", "apache2:vhost-config"]
56 - ["landscape:db-admin", "postgresql:db-admin"]55 - ["landscape:db-admin", "postgresql:db-admin"]
57 - ["haproxy:website", "apache2:reverseproxy"]56 - ["haproxy:website", "apache2:reverseproxy"]
58 - [landscape-msg, rabbitmq-server]57 - [landscape-msg, rabbitmq-server]
@@ -119,6 +118,7 @@
119 relations:118 relations:
120 - [landscape, rabbitmq-server]119 - [landscape, rabbitmq-server]
121 - [landscape, haproxy]120 - [landscape, haproxy]
121 - ["landscape:vhost-config", "apache2:vhost-config"]
122 - ["landscape:db-admin", "postgresql:db-admin"]122 - ["landscape:db-admin", "postgresql:db-admin"]
123 - ["haproxy:website", "apache2:reverseproxy"]123 - ["haproxy:website", "apache2:reverseproxy"]
124 - [landscape-msg, rabbitmq-server]124 - [landscape-msg, rabbitmq-server]
@@ -130,6 +130,7 @@
130 - [landscape-app, rabbitmq-server]130 - [landscape-app, rabbitmq-server]
131 - [landscape-app, haproxy]131 - [landscape-app, haproxy]
132 - ["landscape-app:db-admin", "postgresql:db-admin"]132 - ["landscape-app:db-admin", "postgresql:db-admin"]
133 - ["landscape-app:vhost-config", "apache2:vhost-config"]
133134
134landscape-max-dense-maas:135landscape-max-dense-maas:
135 inherits: landscape-max136 inherits: landscape-max
136137
=== modified file 'hooks/hooks.py'
--- hooks/hooks.py 2014-06-09 20:33:41 +0000
+++ hooks/hooks.py 2014-06-14 03:50:22 +0000
@@ -9,15 +9,15 @@
9from lib import util9from lib import util
10from lib.juju import Juju10from lib.juju import Juju
1111
12from base64 import b64encode12from base64 import b64encode, b64decode
13from configobj import ConfigObj, ConfigObjError13from configobj import ConfigObj, ConfigObjError
14from contextlib import closing
14from copy import deepcopy15from copy import deepcopy
15import cStringIO16import cStringIO
16import datetime17import datetime
17import grp18import grp
18import os19import os
19import psutil20import psutil
20import psycopg2
21import pwd21import pwd
22import pycurl22import pycurl
23import re23import re
@@ -27,6 +27,9 @@
27from subprocess import check_call, check_output, CalledProcessError, call27from subprocess import check_call, check_output, CalledProcessError, call
2828
2929
30SSL_CERT_LOCATION = "/etc/ssl/certs/landscape_server_ca.crt"
31
32
30def _get_installed_version(name):33def _get_installed_version(name):
31 """Returns the version string of name using dpkg-query or returns None"""34 """Returns the version string of name using dpkg-query or returns None"""
32 try:35 try:
@@ -95,6 +98,29 @@
95 services=yaml.safe_dump(_get_services_haproxy()))98 services=yaml.safe_dump(_get_services_haproxy()))
9699
97100
101def notify_vhost_config_relation(relation_id=None):
102 """
103 Notify the vhost-config relation.
104
105 This will mark it "ready to proceed". If relation_id is specified
106 use that as the relation context, otherwise look up and notify all
107 vhost-config relations.
108 """
109 vhosts = []
110 with open("%s/config/vhostssl.tmpl" % ROOT, 'r') as handle:
111 vhosts.append({
112 "port": "443", "template": b64encode(handle.read())})
113 with open("%s/config/vhost.tmpl" % ROOT, 'r') as handle:
114 vhosts.append({
115 "port": "80", "template": b64encode(handle.read())})
116
117 relation_ids = [relation_id]
118 if relation_id is None:
119 relation_ids = juju.relation_ids("vhost-config")
120 for relation_id in relation_ids:
121 juju.relation_set(relation_id=relation_id, vhosts=yaml.dump(vhosts))
122
123
98def db_admin_relation_joined():124def db_admin_relation_joined():
99 pass125 pass
100126
@@ -150,20 +176,13 @@
150 "password": password},176 "password": password},
151 "schema": {"store_user": admin, "store_password": admin_password}})177 "schema": {"store_user": admin, "store_password": admin_password}})
152178
153 try:179 with closing(util.connect_exclusive(host, admin, admin_password)):
154 # Name as lock so we don't try to reuse it as a database connection180 util.create_user(user, password, host, admin, admin_password)
155 lock = util.connect_exclusive(host, admin, admin_password)181 _create_maintenance_user(password, host, admin, admin_password)
156 except psycopg2.Error:182 check_call("setup-landscape-server")
157 # Another unit is performing database configuration.183 juju.juju_log("Landscape database initialized!")
158 pass184
159 else:185 notify_vhost_config_relation()
160 try:
161 util.create_user(user, password, host, admin, admin_password)
162 _create_maintenance_user(password, host, admin, admin_password)
163 check_call("setup-landscape-server")
164 finally:
165 juju.juju_log("Landscape database initialized!")
166 lock.close()
167186
168 try:187 try:
169 # Handle remove-relation db-admin. This call will fail because188 # Handle remove-relation db-admin. This call will fail because
@@ -316,6 +335,65 @@
316 config_changed()335 config_changed()
317336
318337
338def vhost_config_relation_changed():
339 """Relate to apache to configure a vhost.
340
341 This hook will supply vhost configuration as well as read simple data
342 out of apache (servername, certificate). This data is necessary for
343 informing clients of the correct URL and cert to use when connecting
344 to the server.
345 """
346 notify_vhost_config_relation(os.environ.get("JUJU_RELATION_ID", None))
347
348 config_obj = _get_config_obj(LANDSCAPE_SERVICE_CONF)
349 try:
350 section = config_obj["stores"]
351 database = section["main"]
352 host = section["host"]
353 user = section["user"]
354 password = section["password"]
355 except KeyError:
356 juju.juju_log("Database not ready yet, deferring call")
357 sys.exit(0)
358
359 relids = juju.relation_ids("vhost-config")
360 if relids:
361 relid = relids[0]
362 apache2_unit = juju.relation_list(relid)[0]
363 apache_servername = juju.relation_get(
364 "servername", unit_name=apache2_unit, relation_id=relid)
365 else:
366 apache_servername = juju.relation_get("servername")
367
368 if not apache_servername:
369 juju.juju_log("Waiting for data from apache, deferring")
370 sys.exit(0)
371 apache_url = "https://%s/" % apache_servername
372
373 if not _is_db_up():
374 juju.juju_log("Waiting for database to become available, deferring.")
375 sys.exit(0)
376
377 with closing(util.connect_exclusive(host, user, password)):
378 juju.juju_log("Updating Landscape root_url: %s" % apache_url)
379 util.change_root_url(database, user, password, host, apache_url)
380
381 # This data may or may not be present, dependeing on if cert is self
382 # signed from apache.
383 ssl_cert = juju.relation_get(
384 "ssl_cert", unit_name=apache2_unit, relation_id=relid)
385 if ssl_cert:
386 juju.juju_log("Writing new SSL cert: %s" % SSL_CERT_LOCATION)
387 with open(SSL_CERT_LOCATION, 'w') as f:
388 f.write(str(b64decode(ssl_cert)))
389 else:
390 if os.path.exists(SSL_CERT_LOCATION):
391 os.remove(SSL_CERT_LOCATION)
392
393 # only starts services again if is_db_up and _is_amqp_up
394 config_changed()
395
396
319def config_changed():397def config_changed():
320 """Update and restart services based on config setting changes.398 """Update and restart services based on config setting changes.
321399
@@ -743,7 +821,8 @@
743 "data-relation-changed": data_relation_changed,821 "data-relation-changed": data_relation_changed,
744 "db-admin-relation-joined": db_admin_relation_joined,822 "db-admin-relation-joined": db_admin_relation_joined,
745 "db-admin-relation-changed": db_admin_relation_changed,823 "db-admin-relation-changed": db_admin_relation_changed,
746 "website-relation-joined": website_relation_joined}824 "website-relation-joined": website_relation_joined,
825 "vhost-config-relation-changed": vhost_config_relation_changed}
747 hook = os.path.basename(sys.argv[0])826 hook = os.path.basename(sys.argv[0])
748 # If the hook is unsupported, let it raise a KeyError and exit with error.827 # If the hook is unsupported, let it raise a KeyError and exit with error.
749 hooks[hook]()828 hooks[hook]()
750829
=== modified file 'hooks/lib/util.py'
--- hooks/lib/util.py 2014-01-31 20:38:36 +0000
+++ hooks/lib/util.py 2014-06-14 03:50:22 +0000
@@ -2,8 +2,9 @@
2Utility library for juju hooks2Utility library for juju hooks
3"""3"""
44
5from psycopg2 import connect5from psycopg2 import connect, Error as psycopg2Error
6from juju import Juju6from juju import Juju
7from contextlib import closing
78
8juju = Juju()9juju = Juju()
910
@@ -38,7 +39,7 @@
38 password=admin_password)39 password=admin_password)
39 try:40 try:
40 cur = conn.cursor()41 cur = conn.cursor()
41 cur.execute("SELECT usename FROM pg_user WHERE usename='%s'" % user)42 cur.execute("SELECT usename FROM pg_user WHERE usename=%s", (user,))
42 result = cur.fetchall()43 result = cur.fetchall()
43 if not result:44 if not result:
44 juju.juju_log("Creating postgres db user: %s" % user)45 juju.juju_log("Creating postgres db user: %s" % user)
@@ -48,6 +49,32 @@
48 conn.close()49 conn.close()
4950
5051
52def change_root_url(database, user, password, host, url):
53 """Change the root url in the database."""
54 url = "u%s:%s" % (len(url), url)
55 with closing(connect(database=database, host=host,
56 user=user, password=password)) as conn:
57 cur = conn.cursor()
58 cur.execute("SELECT encode(key, 'escape'),encode(value, 'escape') "
59 "FROM system_configuration "
60 "WHERE key='landscape.root_url' FOR UPDATE")
61 result = cur.fetchall()
62 if not result:
63 juju.juju_log("Setting new root_url: %s" % url)
64 cur.execute(
65 "INSERT INTO system_configuration "
66 "VALUES (decode('landscape.root_url', 'escape'), "
67 " decode(%s, 'escape'))", (url,))
68 else:
69 juju.juju_log("Updating root_url %s => %s" % (result, url))
70 cur.execute(
71 "UPDATE system_configuration "
72 "SET key=decode('landscape.root_url', 'escape'),"
73 " value=decode(%s, 'escape') "
74 "WHERE encode(key, 'escape')='landscape.root_url'", (url,))
75 conn.commit()
76
77
51def is_db_up(database, host, user, password):78def is_db_up(database, host, user, password):
52 """79 """
53 Return True if the database relation is configured with write permission,80 Return True if the database relation is configured with write permission,
@@ -59,10 +86,9 @@
59 # Ensure we are user with write access, to avoid hot standby dbs86 # Ensure we are user with write access, to avoid hot standby dbs
60 cur.execute(87 cur.execute(
61 'CREATE TEMP TABLE "write_access_test_%s" (id serial PRIMARY KEY) '88 'CREATE TEMP TABLE "write_access_test_%s" (id serial PRIMARY KEY) '
62 "ON COMMIT DROP"89 "ON COMMIT DROP" % juju.local_unit().replace("/", "_"))
63 % juju.local_unit().replace("/", "_"))90 except psycopg2Error as e:
64 except Exception as e:91 juju.juju_log("Database not yet up: %s" % e)
65 juju.juju_log(str(e))
66 return False92 return False
67 else:93 else:
68 return True94 return True
6995
=== modified file 'hooks/test_hooks.py'
--- hooks/test_hooks.py 2014-05-23 20:51:15 +0000
+++ hooks/test_hooks.py 2014-06-14 03:50:22 +0000
@@ -3,9 +3,11 @@
3import hooks3import hooks
4import mocker4import mocker
5import os5import os
6import psycopg2
6import pycurl7import pycurl
7import stat8import stat
8import subprocess9import subprocess
10import tempfile
9import yaml11import yaml
1012
1113
@@ -1482,6 +1484,171 @@
1482 baseline = (("services", yaml.safe_dump(self.all_services)),)1484 baseline = (("services", yaml.safe_dump(self.all_services)),)
1483 self.assertEqual(baseline, hooks.juju._outgoing_relation_data)1485 self.assertEqual(baseline, hooks.juju._outgoing_relation_data)
14841486
1487 def test_notify_vhost_config_relation_specify_id(self):
1488 """
1489 notify the vhost-config relation on a separate ID.
1490 """
1491 hooks.notify_vhost_config_relation("foo/0")
1492 with open("%s/config/vhostssl.tmpl" % hooks.ROOT, 'r') as f:
1493 vhostssl_template = f.read()
1494 with open("%s/config/vhost.tmpl" % hooks.ROOT, 'r') as f:
1495 vhost_template = f.read()
1496 baseline = yaml.dump(
1497 [{"port": "443", "template": base64.b64encode(vhostssl_template)},
1498 {"port": "80", "template": base64.b64encode(vhost_template)}])
1499 self.assertEqual(
1500 (("vhosts", baseline),), hooks.juju._outgoing_relation_data)
1501
1502 def test_notify_vhost_config_relation(self):
1503 """notify the vhost-config relation on the "current" ID."""
1504 hooks.notify_vhost_config_relation()
1505 with open("%s/config/vhostssl.tmpl" % hooks.ROOT, 'r') as f:
1506 vhostssl_template = f.read()
1507 with open("%s/config/vhost.tmpl" % hooks.ROOT, 'r') as f:
1508 vhost_template = f.read()
1509 baseline = yaml.dump(
1510 [{"port": "443", "template": base64.b64encode(vhostssl_template)},
1511 {"port": "80", "template": base64.b64encode(vhost_template)}])
1512 self.assertEqual(
1513 (("vhosts", baseline),), hooks.juju._outgoing_relation_data)
1514
1515 def test_vhost_config_relation_changed_exit_no_configuration(self):
1516 """Ensure vhost_relation_changed deferrs if db is not up."""
1517 self.assertRaises(SystemExit, hooks.vhost_config_relation_changed)
1518 self.assertEquals(len(hooks.juju._logs), 1)
1519 self.assertIn('Database not ready yet', hooks.juju._logs[0])
1520
1521 def test_vhost_config_relation_changed_wait_apache_servername(self):
1522 """Ensure vhost_relation_changed deferrs if db is not up."""
1523 _get_config_obj = self.mocker.replace(hooks._get_config_obj)
1524 _get_config_obj(hooks.LANDSCAPE_SERVICE_CONF)
1525 self.mocker.result({
1526 "stores": {
1527 "main": "database",
1528 "host": "host",
1529 "user": "user",
1530 "password": "password"}})
1531 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
1532 notify_vhost(None)
1533 self.mocker.replay()
1534 self.assertRaises(SystemExit, hooks.vhost_config_relation_changed)
1535 self.assertIn('Waiting for data from apache', hooks.juju._logs[-1])
1536
1537 def test_vhost_config_relation_changed_fail_root_url(self):
1538 """Ensure vhost_relation_changed deferrs if db is not up."""
1539 _get_config_obj = self.mocker.replace(hooks._get_config_obj)
1540 _get_config_obj(hooks.LANDSCAPE_SERVICE_CONF)
1541 hooks.juju._incoming_relation_data += (("servername", "foobar"),)
1542 self.mocker.result({
1543 "stores": {
1544 "main": "database",
1545 "host": "host",
1546 "user": "user",
1547 "password": "password"}})
1548 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
1549 notify_vhost(None)
1550 is_db_up = self.mocker.replace(hooks._is_db_up)
1551 is_db_up()
1552 self.mocker.result(False)
1553 self.mocker.replay()
1554 self.assertRaises(SystemExit, hooks.vhost_config_relation_changed)
1555 self.assertIn(
1556 'Waiting for database to become available, deferring',
1557 hooks.juju._logs[-1])
1558
1559 def test_vhost_config_relation_changed_fail_root_url_db_update(self):
1560 """vhost_config_relation_changed should error if db update fails"""
1561 _get_config_obj = self.mocker.replace(hooks._get_config_obj)
1562 _get_config_obj(hooks.LANDSCAPE_SERVICE_CONF)
1563 hooks.juju._incoming_relation_data += (("servername", "foobar"),)
1564 self.mocker.result({
1565 "stores": {
1566 "main": "database",
1567 "host": "host",
1568 "user": "user",
1569 "password": "password"}})
1570 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
1571 notify_vhost(None)
1572 is_db_up = self.mocker.replace(hooks._is_db_up)
1573 is_db_up()
1574 self.mocker.result(True)
1575 self.mocker.replay()
1576 self.assertRaises(psycopg2.Error, hooks.vhost_config_relation_changed)
1577
1578 def test_vhost_config_relation_changed_cert_not_provided(self):
1579 """
1580 Ensure vhost_relation_changed runs to completion.
1581
1582 Existing cert should be removed.
1583 """
1584 hooks.SSL_CERT_LOCATION = tempfile.NamedTemporaryFile().name
1585 _get_config_obj = self.mocker.replace(hooks._get_config_obj)
1586 _get_config_obj(hooks.LANDSCAPE_SERVICE_CONF)
1587 hooks.juju._incoming_relation_data += (("servername", "foobar"),)
1588 self.mocker.result({
1589 "stores": {
1590 "main": "database",
1591 "host": "host",
1592 "user": "user",
1593 "password": "password"}})
1594 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
1595 notify_vhost(None)
1596 mock_conn = self.mocker.mock()
1597 mock_conn.close()
1598 connect_exclusive = self.mocker.replace(hooks.util.connect_exclusive)
1599 connect_exclusive("host", "user", "password")
1600 self.mocker.result(mock_conn)
1601 change_root_url = self.mocker.replace(hooks.util.change_root_url)
1602 change_root_url(
1603 "database", "user", "password", "host", "https://foobar/")
1604 config_changed = self.mocker.replace(hooks.config_changed)
1605 config_changed()
1606 is_db_up = self.mocker.replace(hooks._is_db_up)
1607 is_db_up()
1608 self.mocker.result(True)
1609 self.mocker.replay()
1610 hooks.vhost_config_relation_changed()
1611 self.assertFalse(os.path.exists(hooks.SSL_CERT_LOCATION))
1612
1613 def test_vhost_config_relation_changed_ssl_cert_provided(self):
1614 """
1615 Ensure vhost_relation_changed runs to completion.
1616
1617 Cert passed in to other side of relation should be written on disk.
1618 """
1619 hooks.SSL_CERT_LOCATION = tempfile.NamedTemporaryFile().name
1620 _get_config_obj = self.mocker.replace(hooks._get_config_obj)
1621 _get_config_obj(hooks.LANDSCAPE_SERVICE_CONF)
1622 hooks.juju._incoming_relation_data += (("servername", "foobar"),)
1623 hooks.juju._incoming_relation_data += (
1624 ("ssl_cert", base64.b64encode("foobar")),)
1625 self.mocker.result({
1626 "stores": {
1627 "main": "database",
1628 "host": "host",
1629 "user": "user",
1630 "password": "password"}})
1631 notify_vhost = self.mocker.replace(hooks.notify_vhost_config_relation)
1632 notify_vhost(None)
1633 is_db_up = self.mocker.replace(hooks._is_db_up)
1634 is_db_up()
1635 self.mocker.result(True)
1636 mock_conn = self.mocker.mock()
1637 mock_conn.close()
1638 connect_exclusive = self.mocker.replace(hooks.util.connect_exclusive)
1639 connect_exclusive("host", "user", "password")
1640 self.mocker.result(mock_conn)
1641 change_root_url = self.mocker.replace(hooks.util.change_root_url)
1642 change_root_url(
1643 "database", "user", "password", "host", "https://foobar/")
1644 config_changed = self.mocker.replace(hooks.config_changed)
1645 config_changed()
1646 self.mocker.replay()
1647 hooks.vhost_config_relation_changed()
1648 self.assertTrue(os.path.exists(hooks.SSL_CERT_LOCATION))
1649 with open(hooks.SSL_CERT_LOCATION, 'r') as f:
1650 self.assertEqual("foobar", f.read())
1651
14851652
1486class TestHooksUtils(TestHooks):1653class TestHooksUtils(TestHooks):
1487 def test__setup_apache(self):1654 def test__setup_apache(self):
14881655
=== added symlink 'hooks/vhost-config-relation-changed'
=== target is u'hooks.py'
=== modified file 'metadata.yaml'
--- metadata.yaml 2014-04-25 15:04:36 +0000
+++ metadata.yaml 2014-06-14 03:50:22 +0000
@@ -23,3 +23,5 @@
23 optional: true23 optional: true
24 website:24 website:
25 interface: http25 interface: http
26 vhost-config:
27 interface: apache-vhost-config
2628
=== modified file 'tests/01-begin'
--- tests/01-begin 2014-06-11 12:50:56 +0000
+++ tests/01-begin 2014-06-14 03:50:22 +0000
@@ -191,6 +191,8 @@
191 self.service_name = service_name191 self.service_name = service_name
192192
193193
194@unittest.skipIf(
195 getenv("SKIP_TESTS", None), "Requested to skip all tests.")
194class BaseLandscapeTests(unittest.TestCase):196class BaseLandscapeTests(unittest.TestCase):
195 """197 """
196 Base class with some commonality between all test classes.198 Base class with some commonality between all test classes.

Subscribers

People subscribed via source and target branches