Merge lp:~evarlast/charms/trusty/apache2/add-logs-interface into lp:charms/trusty/apache2
- Trusty Tahr (14.04)
- add-logs-interface
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 68 |
Proposed branch: | lp:~evarlast/charms/trusty/apache2/add-logs-interface |
Merge into: | lp:charms/trusty/apache2 |
Diff against target: |
341 lines (+141/-24) 8 files modified
Makefile (+3/-3) README.md (+6/-0) hooks/hooks.py (+52/-0) hooks/tests/test_cert.py (+12/-0) hooks/tests/test_config_changed.py (+6/-2) hooks/tests/test_log_relation.py (+56/-0) hooks/tests/test_vhost_config_relation.py (+4/-19) metadata.yaml (+2/-0) |
To merge this branch: | bzr merge lp:~evarlast/charms/trusty/apache2/add-logs-interface |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jay R. Wren (community) | Needs Resubmitting | ||
Andrew McLeod (community) | Needs Fixing | ||
Review Queue (community) | automated testing | Needs Fixing | |
charmers | Pending | ||
Review via email: mp+278222@code.launchpad.net |
Commit message
Description of the change
Add logs relation.
This exposes log files written by apache via the logs relation. Logger services such as logstash can be related to this service and this will configure the logger to read these files.
- 70. By Jay R. Wren
-
merge parent
Review Queue (review-queue) wrote : | # |
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
- 71. By Jay R. Wren
-
fix broken tests
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Andrew McLeod (admcleod) wrote : | # |
Hi Jay,
As per my comments in your 'apt source' merge request, this one is failing with the following:
http://
Andrew
- 72. By Jay R. Wren
-
make utopic+ tests pass on trusty
Jay R. Wren (evarlast) wrote : | # |
I don't see how these tests EVER passed this criteria. Evidence to the contrary is appreciated.
I've disabled the tests on trusty as the hook code in the charm doesn't work in trusty and is documented as such.
Please consider for merging again.
Cory Johns (johnsca) wrote : | # |
Jay,
Thanks for addressing those test failures. I think your solution is reasonable, and the tests now pass for me. I think that this mostly looks good, except for two small items.
First, I think there's a corner case in how you write out to the logs relation, that I mentioned in my diff comment below. I think just dropping the call to relation_ids() entirely should resolve it without issue.
Second, I would like to see some documentation in the README about how this is supposed to be used. You mention logstash in the MR, above, but that charm doesn't support this relation directly. I'm guessing that you intend this to be used via the beaver charm (e.g., https:/
With those two small changes, I would give this my +1.
- 73. By Jay R. Wren
-
use relation_ids always, and add a section about logs relation to the README
Jay R. Wren (evarlast) wrote : | # |
I have updated with those two things.
Cory Johns (johnsca) wrote : | # |
Jay,
Thank you for addressing those items. This has now been merged, and should be ingested into the charm store within the hour.
Preview Diff
1 | === modified file 'Makefile' |
2 | --- Makefile 2015-03-10 10:56:19 +0000 |
3 | +++ Makefile 2016-01-15 23:00:04 +0000 |
4 | @@ -18,7 +18,7 @@ |
5 | @test `cat revision` = 0 && rm revision |
6 | |
7 | .venv: |
8 | - sudo apt-get install -y python-apt python-virtualenv python-jinja2 |
9 | + sudo apt-get install -y python-apt python-virtualenv python-jinja2 python-openssl |
10 | virtualenv .venv --system-site-packages |
11 | .venv/bin/pip install -I nose testtools mock pyyaml |
12 | |
13 | @@ -28,8 +28,8 @@ |
14 | |
15 | lint: |
16 | @echo Checking for Python syntax... |
17 | - @flake8 $(HOOKS_DIR) --ignore=E123 --exclude=$(HOOKS_DIR)/charmhelpers && echo hooks OK |
18 | - @flake8 tests --ignore=E123 && echo tests OK |
19 | + @flake8 $(HOOKS_DIR) --ignore=E123,E402 --exclude=$(HOOKS_DIR)/charmhelpers && echo hooks OK |
20 | + @flake8 tests --ignore=E123,E402 && echo tests OK |
21 | |
22 | sourcedeps: $(PWD)/config-manager.txt |
23 | @echo Updating source dependencies... |
24 | |
25 | === modified file 'README.md' |
26 | --- README.md 2015-04-15 22:59:59 +0000 |
27 | +++ README.md 2016-01-15 23:00:04 +0000 |
28 | @@ -265,6 +265,12 @@ |
29 | |
30 | * `ports` - A space-separated list of ports that the site uses. |
31 | |
32 | +### Using the logs relation |
33 | + |
34 | +The logs relation is for use with a logging subordinate charm. The beaver |
35 | +subordinate can be deployed and related to apache and logstash. Beaver will |
36 | +tail apache logs and send the logs to logstash. |
37 | + |
38 | ## Certs, keys and chains |
39 | |
40 | `ssl_keylocation`, `ssl_certlocation` and `ssl_chainlocation` are |
41 | |
42 | === modified file 'hooks/hooks.py' |
43 | --- hooks/hooks.py 2015-04-15 20:23:01 +0000 |
44 | +++ hooks/hooks.py 2016-01-15 23:00:04 +0000 |
45 | @@ -22,6 +22,7 @@ |
46 | relations_of_type, |
47 | relation_set, |
48 | relation_ids, |
49 | + relation_id, |
50 | unit_get |
51 | ) |
52 | from charmhelpers.contrib.charmsupport import nrpe |
53 | @@ -737,6 +738,8 @@ |
54 | ensure_ports(all_ports) |
55 | update_nrpe_checks() |
56 | ship_logrotate_conf() |
57 | + if config_get().changed('servername'): |
58 | + logs_relation_joined() |
59 | |
60 | |
61 | def ensure_disabled(sites): |
62 | @@ -956,6 +959,53 @@ |
63 | yaml.safe_dump(ports, pfile) |
64 | |
65 | |
66 | +def get_log_files(): |
67 | + """ |
68 | + Read all of the apache config files from __ and get ErrorLog and AccessLog |
69 | + values. |
70 | + |
71 | + Returns a tuple with first value list of access log files and second value |
72 | + list of error log files. |
73 | + """ |
74 | + access_logs = [] |
75 | + error_logs = [] |
76 | + for protocol in ['http', 'https']: |
77 | + vhost_name = '%s_%s' % (config_get()['servername'], protocol) |
78 | + vhost_file = site_filename(vhost_name) |
79 | + try: |
80 | + # Using read().split('\n') here to work around a mocks open_mock |
81 | + # inadequacy: http://bugs.python.org/issue17467 |
82 | + for line in open(vhost_file, 'r').read().split('\n'): |
83 | + if 'CustomLog' in line: |
84 | + access_logs.append(line.split()[1]) |
85 | + elif 'ErrorLog' in line: |
86 | + error_logs.append(line.split()[1]) |
87 | + except: |
88 | + pass |
89 | + return access_logs, error_logs |
90 | + |
91 | + |
92 | +def logs_relation_joined(): |
93 | + """ |
94 | + Sets relation value with filenames |
95 | + """ |
96 | + access_log_files, error_log_files = get_log_files() |
97 | + log_files = access_log_files[:] |
98 | + log_files.extend(error_log_files) |
99 | + types = ['apache_access' for a in access_log_files] |
100 | + types.extend(['apache_error' for a in error_log_files]) |
101 | + data = {'files': '\n'.join(log_files), |
102 | + 'types': '\n'.join(types), |
103 | + } |
104 | + _relation_ids = relation_ids('logs') |
105 | + for _relation_id in _relation_ids: |
106 | + log("logs-relation-joined setting relation data for {} to {}".format( |
107 | + _relation_id, data)) |
108 | + relation_set( |
109 | + relation_id=_relation_id, |
110 | + relation_settings=data) |
111 | + |
112 | + |
113 | ############################################################################### |
114 | # Main section |
115 | ############################################################################### |
116 | @@ -995,6 +1045,8 @@ |
117 | update_nrpe_checks() |
118 | elif hook_name == "vhost-config-relation-changed": |
119 | config_changed() |
120 | + elif hook_name == "logs-relation-joined": |
121 | + logs_relation_joined() |
122 | else: |
123 | print "Unknown hook" |
124 | sys.exit(1) |
125 | |
126 | === added symlink 'hooks/logs-relation-joined' |
127 | === target is u'hooks.py' |
128 | === modified file 'hooks/tests/test_cert.py' |
129 | --- hooks/tests/test_cert.py 2014-05-24 06:33:34 +0000 |
130 | +++ hooks/tests/test_cert.py 2016-01-15 23:00:04 +0000 |
131 | @@ -73,6 +73,12 @@ |
132 | def test_is_selfsigned_cert_stale_public_address_changed( |
133 | self, unit_get, log): |
134 | """With different servername, cert *should* be regenerated.""" |
135 | + try: |
136 | + from pyasn1.codec.der import decoder |
137 | + from pyasn1_modules import rfc2459 |
138 | + except ImportError: |
139 | + # This test is utopic+ only |
140 | + return |
141 | config = {"servername": "servername"} |
142 | cert_file = os.path.join(self.tempdir, "cert") |
143 | key_file = os.path.join(self.tempdir, "key") |
144 | @@ -86,6 +92,12 @@ |
145 | def test_is_selfsigned_cert_stale_private_address_changed( |
146 | self, unit_get, log): |
147 | """With different servername, cert *should* be regenerated.""" |
148 | + try: |
149 | + from pyasn1.codec.der import decoder |
150 | + from pyasn1_modules import rfc2459 |
151 | + except ImportError: |
152 | + # This test is utopic+ only |
153 | + return |
154 | config = {"servername": "servername"} |
155 | cert_file = os.path.join(self.tempdir, "cert") |
156 | key_file = os.path.join(self.tempdir, "key") |
157 | |
158 | === modified file 'hooks/tests/test_config_changed.py' |
159 | --- hooks/tests/test_config_changed.py 2015-04-15 20:23:01 +0000 |
160 | +++ hooks/tests/test_config_changed.py 2016-01-15 23:00:04 +0000 |
161 | @@ -4,6 +4,10 @@ |
162 | import shutil |
163 | import tempfile |
164 | import os |
165 | +import sys |
166 | + |
167 | +sys.path.insert(0, os.path.join(os.environ['CHARM_DIR'], 'lib')) |
168 | +from charmhelpers.core import hookenv |
169 | |
170 | |
171 | class ConfigChangedTest(TestCase): |
172 | @@ -47,7 +51,7 @@ |
173 | mock_conf_disable, mock_open_port, mock_close_port, mock_call, |
174 | mock_set_open_ports, mock_get_open_ports): |
175 | """config-changed hook: Site directories should be empty.""" |
176 | - mock_config_get.return_value = { |
177 | + mock_config_get.return_value = hookenv.Config({ |
178 | "ssl_cert": "", |
179 | "ssl_key": "", |
180 | "package_status": "", |
181 | @@ -63,7 +67,7 @@ |
182 | "servername": "foobar", |
183 | "vhost_http_template": "", |
184 | "vhost_https_template": "", |
185 | - } |
186 | + }) |
187 | base = patch.object(hooks, 'default_apache_base_dir', self.dirname) |
188 | config22 = patch.object(hooks, 'default_apache22_config_dir', |
189 | "%s/conf.d" % self.dirname) |
190 | |
191 | === added file 'hooks/tests/test_log_relation.py' |
192 | --- hooks/tests/test_log_relation.py 1970-01-01 00:00:00 +0000 |
193 | +++ hooks/tests/test_log_relation.py 2016-01-15 23:00:04 +0000 |
194 | @@ -0,0 +1,56 @@ |
195 | +from testtools import TestCase |
196 | +import mock |
197 | +import hooks |
198 | +hooks.log = mock.MagicMock() |
199 | + |
200 | + |
201 | +class TestLogRelation(TestCase): |
202 | + |
203 | + @mock.patch('hooks.config_get') |
204 | + def test_get_log_files_no_raise(self, config_get): |
205 | + 'does not raise' |
206 | + hooks.get_log_files() |
207 | + |
208 | + @mock.patch('hooks.config_get') |
209 | + def test_get_log_files_parses(self, config_get): |
210 | + 'parses files for real' |
211 | + config_get.return_value = {'servername': 'hello'} |
212 | + m = mock.mock_open(read_data='''junk |
213 | + CustomLog /var/log/apache2/access.log combined\n") |
214 | + ErrorLog /var/log/apache2/error.log\n") |
215 | + junk\n"''') |
216 | + |
217 | + with mock.patch('hooks.open', m, create=True): |
218 | + access_logs, error_logs = hooks.get_log_files() |
219 | + self.assertEquals('/var/log/apache2/access.log', access_logs[0]) |
220 | + self.assertEquals('/var/log/apache2/error.log', error_logs[0]) |
221 | + |
222 | + @mock.patch('hooks.relation_id') |
223 | + @mock.patch('hooks.relation_set') |
224 | + def test_log_relation_joined(self, relation_set, relation_id): |
225 | + with mock.patch('hooks.get_log_files') as get_log_files: |
226 | + get_log_files.return_value = ['myaccess_log'], ['myerror_log'] |
227 | + hooks.logs_relation_joined() |
228 | + self.assertTrue(relation_set.called) |
229 | + self.assertEquals({'files': 'myaccess_log\nmyerror_log', |
230 | + 'types': 'apache_access\napache_error', |
231 | + }, |
232 | + relation_set.call_args[1]['relation_settings']) |
233 | + |
234 | + @mock.patch('hooks.relation_ids') |
235 | + @mock.patch('hooks.relation_id') |
236 | + @mock.patch('hooks.relation_set') |
237 | + def test_log_relation_joined_config_changed(self, |
238 | + relation_set, |
239 | + relation_id, |
240 | + relation_ids): |
241 | + relation_id.return_value = None |
242 | + relation_ids.return_value = ['logs/0'] |
243 | + with mock.patch('hooks.get_log_files') as get_log_files: |
244 | + get_log_files.return_value = ['myaccess_log'], ['myerror_log'] |
245 | + hooks.logs_relation_joined() |
246 | + self.assertTrue(relation_set.called) |
247 | + self.assertEquals({'files': 'myaccess_log\nmyerror_log', |
248 | + 'types': 'apache_access\napache_error', |
249 | + }, |
250 | + relation_set.call_args[1]['relation_settings']) |
251 | |
252 | === modified file 'hooks/tests/test_vhost_config_relation.py' |
253 | --- hooks/tests/test_vhost_config_relation.py 2014-12-29 19:37:05 +0000 |
254 | +++ hooks/tests/test_vhost_config_relation.py 2016-01-15 23:00:04 +0000 |
255 | @@ -27,23 +27,19 @@ |
256 | |
257 | @patch('hooks.log') |
258 | @patch('subprocess.call') |
259 | - @patch('hooks.close_port') |
260 | def test_create_vhost_missing_template( |
261 | - self, mock_close_port, mock_call, mock_log): |
262 | + self, mock_call, mock_log): |
263 | """Create a vhost file, check contents.""" |
264 | hooks.create_vhost(80) |
265 | mock_log.assert_called_once_with( |
266 | "Vhost Template not provided, not configuring: %s" % 80) |
267 | - mock_close_port.assert_called_once() |
268 | self.assertEqual( |
269 | len(os.listdir("%s/%s" % (self.dirname, "sites-available"))), 0) |
270 | |
271 | @patch('hooks.log') |
272 | @patch('subprocess.call') |
273 | - @patch('hooks.close_port') |
274 | - @patch('hooks.open_port') |
275 | def test_create_vhost_template_through_config_no_protocol( |
276 | - self, mock_open_port, mock_close_port, mock_call, mock_log): |
277 | + self, mock_call, mock_log): |
278 | """Create a vhost file, check contents.""" |
279 | template = b64encode("http://{{ variable }}/") |
280 | config_data = { |
281 | @@ -59,17 +55,13 @@ |
282 | with open(filename, 'r') as file: |
283 | contents = file.read() |
284 | self.assertEqual(contents, 'http://fantastic/') |
285 | - mock_open_port.assert_called_once() |
286 | - mock_close_port.assert_called_once() |
287 | self.assertEqual( |
288 | len(os.listdir("%s/%s" % (self.dirname, "sites-available"))), 1) |
289 | |
290 | @patch('hooks.log') |
291 | @patch('subprocess.call') |
292 | - @patch('hooks.close_port') |
293 | - @patch('hooks.open_port') |
294 | def test_create_vhost_template_through_config_with_protocol( |
295 | - self, mock_open_port, mock_close_port, mock_call, mock_log): |
296 | + self, mock_call, mock_log): |
297 | """Create a vhost file, check contents.""" |
298 | template = b64encode("http://{{ variable }}/") |
299 | config_data = { |
300 | @@ -85,18 +77,13 @@ |
301 | with open(filename, 'r') as file: |
302 | contents = file.read() |
303 | self.assertEqual(contents, 'http://fantastic/') |
304 | - mock_open_port.assert_called_once() |
305 | - mock_close_port.assert_called_once() |
306 | self.assertEqual( |
307 | len(os.listdir("%s/%s" % (self.dirname, "sites-available"))), 1) |
308 | |
309 | @patch('hooks.log') |
310 | @patch('subprocess.call') |
311 | - @patch('hooks.close_port') |
312 | - @patch('hooks.open_port') |
313 | def test_create_vhost_template_directly( |
314 | - self, mock_open_port, mock_close_port, |
315 | - mock_call, mock_log): |
316 | + self, mock_call, mock_log): |
317 | """Create a vhost file, check contents.""" |
318 | template = b64encode("http://{{ variable }}/") |
319 | config_data = { |
320 | @@ -111,8 +98,6 @@ |
321 | with open(filename, 'r') as file: |
322 | contents = file.read() |
323 | self.assertEqual(contents, 'http://fantastic/') |
324 | - mock_open_port.assert_called_once() |
325 | - mock_close_port.assert_called_once() |
326 | self.assertEqual( |
327 | len(os.listdir("%s/%s" % (self.dirname, "sites-available"))), 1) |
328 | |
329 | |
330 | === modified file 'metadata.yaml' |
331 | --- metadata.yaml 2014-12-18 01:21:23 +0000 |
332 | +++ metadata.yaml 2016-01-15 23:00:04 +0000 |
333 | @@ -18,6 +18,8 @@ |
334 | scope: container |
335 | website: |
336 | interface: http |
337 | + logs: |
338 | + interface: logs |
339 | apache-website: |
340 | interface: apache-website |
341 | scope: container |
This item has failed automated testing! Results available here http:// juju-ci. vapour. ws:8080/ job/charm- bundle- test-lxc/ 1485/