Merge lp:~evarlast/charms/trusty/logstash-forwarder/add-logs-relation into lp:~canonical-is-sa/charms/trusty/logstash-forwarder/trunk

Proposed by Jay R. Wren
Status: Merged
Merged at revision: 16
Proposed branch: lp:~evarlast/charms/trusty/logstash-forwarder/add-logs-relation
Merge into: lp:~canonical-is-sa/charms/trusty/logstash-forwarder/trunk
Diff against target: 222 lines (+142/-3)
4 files modified
config.yaml (+3/-0)
hooks/hooks.py (+41/-2)
hooks/test_hooks.py (+94/-0)
metadata.yaml (+4/-1)
To merge this branch: bzr merge lp:~evarlast/charms/trusty/logstash-forwarder/add-logs-relation
Reviewer Review Type Date Requested Status
Greg Mason (community) Approve
Michael Nelson (community) Approve
Review via email: mp+277493@code.launchpad.net

Description of the change

add logs relation

To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :

Thanks Jay. This generally looks good. I'm not sure, but did you forget to commit the hooks/logs-relation-* symlinks, or are they no longer required?

I've a few other comments below. My main concern is the format that you're using for the relation data - in that I think it will restrict our future options (ie. if in the future we want to have two files for one type, or have extra fields for a file type). See below for more.

Let me know what you think.

Revision history for this message
Jay R. Wren (evarlast) wrote :

I addressed most the code issues.

Regarding the relation data: IMO to keep things simple on the other side of the relation, this makes the most sense. It turns out that logstash-forwarder is quite OK with single files configured with the same type. The simplicity of file, type pair is intentional. No need for added complexity. The restriction on an extra field is also by design. Parallel arrays were used for files and lists, an additional parallel array could be given by any charm implementing the logs interface, and this charm consuming the interface would ignore it. This is flexible and simple.

Can it land now?

17. By Jay R. Wren

cleanup

18. By Jay R. Wren

add missing symlinks

Revision history for this message
Michael Nelson (michael.nelson) wrote :

Great - thanks Jay.

review: Approve
Revision history for this message
Michael Nelson (michael.nelson) wrote :

BTW, you'll need someone from IS (#webops) to land it for you, afaik.

Revision history for this message
Greg Mason (gmason) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'config.yaml'
--- config.yaml 2015-11-05 04:16:47 +0000
+++ config.yaml 2015-12-10 18:17:52 +0000
@@ -26,6 +26,9 @@
26 Alternatively, this can also be the full list of hashes, as per the26 Alternatively, this can also be the full list of hashes, as per the
27 definition of the files attribute of the logstash-forwarder config at27 definition of the files attribute of the logstash-forwarder config at
28 https://github.com/elastic/logstash-forwarder28 https://github.com/elastic/logstash-forwarder
29 If this service is related to another services via the logs relation,
30 the field type and files from the logs relation are appened to this
31 value.
29 package_name:32 package_name:
30 default: "logstash-forwarder_0.4.0_amd64.deb"33 default: "logstash-forwarder_0.4.0_amd64.deb"
31 type: string34 type: string
3235
=== modified file 'hooks/hooks.py'
--- hooks/hooks.py 2015-11-05 04:16:47 +0000
+++ hooks/hooks.py 2015-12-10 18:17:52 +0000
@@ -12,7 +12,9 @@
12import shutil12import shutil
13sys.path.insert(0, os.path.join(os.environ['CHARM_DIR'], 'lib'))13sys.path.insert(0, os.path.join(os.environ['CHARM_DIR'], 'lib'))
14from charmhelpers.core.hookenv import Hooks, log, charm_dir, relations_of_type14from charmhelpers.core.hookenv import Hooks, log, charm_dir, relations_of_type
15from charmhelpers.core import host
15from charmhelpers.core.host import mkdir, service_start, service_stop, service_restart16from charmhelpers.core.host import mkdir, service_start, service_stop, service_restart
17from charmhelpers.core.services import RelationContext
16from charmhelpers.fetch import apt_install, apt_update, add_source18from charmhelpers.fetch import apt_install, apt_update, add_source
17from Cheetah.Template import Template19from Cheetah.Template import Template
18from Config import Config20from Config import Config
@@ -81,6 +83,7 @@
81 config = {}83 config = {}
82 servers = []84 servers = []
83 files = []85 files = []
86 logs_relation_data = logs_relation()
84 if not os.path.exists(conf.configDir()):87 if not os.path.exists(conf.configDir()):
85 mkdir(conf.configDir())88 mkdir(conf.configDir())
86 serverPort = conf.serverPort()89 serverPort = conf.serverPort()
@@ -97,6 +100,7 @@
97 if conf.timeout():100 if conf.timeout():
98 config["network"]["timeout"] = conf.timeout()101 config["network"]["timeout"] = conf.timeout()
99102
103 config["files"] = []
100 fileSpec = conf.files()104 fileSpec = conf.files()
101 if fileSpec is not None:105 if fileSpec is not None:
102 if isinstance(fileSpec, list):106 if isinstance(fileSpec, list):
@@ -112,8 +116,12 @@
112 })116 })
113 config["files"] = files117 config["files"] = files
114118
115 with open(conf.configFile(), "w") as f:119 if logs_relation_data is not None:
116 f.write(json.dumps(config, sort_keys=True, indent=4))120 for typ, fil in logs_relation_data:
121 config["files"].append({"paths": [fil], "fields": {"type": typ}})
122
123 host.write_file(conf.configFile(),
124 json.dumps(config, sort_keys=True, indent=4))
117125
118126
119def writeEtcHosts():127def writeEtcHosts():
@@ -224,5 +232,36 @@
224 service_stop("logstash-forwarder")232 service_stop("logstash-forwarder")
225233
226234
235@hooks.hook("logs-relation-joined")
236@hooks.hook('logs-relation-changed')
237@hooks.hook('logs-relation-departed')
238@hooks.hook('logs-relation-broken')
239def logs_relation_hooks():
240 log(os.environ['JUJU_HOOK_NAME'])
241 writeConfig()
242 service_restart("logstash-forwarder")
243
244
245def logs_relation():
246 lsr = LogsRelation()
247 log("LogsRelation: {}".format(lsr))
248 if 'logs' not in lsr:
249 return
250 logs_relation_data = lsr['logs']
251 if (not logs_relation_data or
252 'types' not in logs_relation_data[0] or
253 'files' not in logs_relation_data[0]):
254 return None
255 types = logs_relation_data[0]['types'].split()
256 files = logs_relation_data[0]['files'].split()
257 return zip(types, files)
258
259
260class LogsRelation(RelationContext):
261 name = 'logs'
262 interface = 'logs'
263 required_keys = ['types', 'files']
264
265
227if __name__ == "__main__":266if __name__ == "__main__":
228 hooks.execute(sys.argv)267 hooks.execute(sys.argv)
229268
=== added symlink 'hooks/logs-relation-broken'
=== target is u'hooks'
=== added symlink 'hooks/logs-relation-changed'
=== target is u'hooks'
=== added symlink 'hooks/logs-relation-departed'
=== target is u'hooks'
=== added symlink 'hooks/logs-relation-joined'
=== target is u'hooks'
=== added file 'hooks/test_hooks.py'
--- hooks/test_hooks.py 1970-01-01 00:00:00 +0000
+++ hooks/test_hooks.py 2015-12-10 18:17:52 +0000
@@ -0,0 +1,94 @@
1#!/usr/bin/python
2
3import json
4import mock
5import os
6import shutil
7import subprocess
8import unittest
9
10os.environ['CHARM_DIR'] = os.path.join(os.path.dirname(__file__), '..')
11os.environ['JUJU_UNIT_NAME'] = 'logstash-forwarder/0'
12
13from charmhelpers.core import hookenv, services
14from charmhelpers.core import host
15import charmhelpers.fetch
16subprocess.check_output = mock.MagicMock()
17subprocess.check_call = mock.MagicMock()
18subprocess.call = mock.MagicMock(return_value=0)
19config = hookenv.Config({"application_name": "logstash_forwarder",
20 "config_dir": "/etc/logstash-forwarder",
21 "package_name": "logstash-forwarder_0.4.0_amd64.deb",
22 })
23hookenv.config = lambda k: config.get(k)
24shutil.copyfile = mock.MagicMock()
25os.chmod = mock.MagicMock()
26
27host.mkdir = mock.MagicMock()
28host.service_start = mock.MagicMock()
29host.service_stop = mock.MagicMock()
30host.service_restart = mock.MagicMock()
31host.write_file = mock.MagicMock()
32hookenv.log = mock.MagicMock()
33
34charmhelpers.fetch.apt_install = mock.MagicMock()
35import hooks
36hooks.conf.config = config
37hooks.log = mock.MagicMock()
38
39
40class TestInstall(unittest.TestCase):
41 '''Testing that there are no exceptions in hooks.install'''
42
43 def test_install_does_not_raise(self):
44 with mock.patch('__builtin__.open'):
45 hooks.install()
46
47 def test_installs_packages(self):
48 subprocess.call.reset()
49 with mock.patch('__builtin__.open'):
50 hooks.install()
51 subprocess.call.assert_called_with(["dpkg", "-i",
52 os.path.join(
53 hookenv.charm_dir(),
54 "files",
55 hookenv.config('package_name'))
56 ])
57
58 def test_package_file_exist(self):
59 pkgFile = os.path.join(hookenv.charm_dir(), "files",
60 hookenv.config('package_name'))
61 if not os.path.exists(pkgFile):
62 self.assert_fail()
63
64
65class TestLogsRelation(unittest.TestCase):
66
67 def setUp(self):
68 os.environ['JUJU_HOOK_NAME'] = 'logs-relation-joined'
69 self.phookenv = mock.patch.object(services.helpers, 'hookenv')
70 self.mhookenv = self.phookenv.start()
71 self.mhookenv.relation_ids.return_value = ['baz']
72 self.mhookenv.related_units.side_effect = lambda i: [i + '/0']
73 self.mhookenv.relation_get.side_effect = [
74 {'files': '\n'.join(['file1', 'file2']),
75 'types': '\n'.join(['type1', 'type2'])}
76 ]
77
78 def tearDown(self):
79 self.phookenv.stop()
80 del os.environ['JUJU_HOOK_NAME']
81
82 def test_logs_hooks(self):
83 conf_path = hookenv.config("config_dir") + "/logstash_forwarder.conf"
84 host.write_file.reset_mock()
85 hooks.logs_relation_hooks()
86 self.assertTrue(host.write_file.called)
87 self.assertEqual(conf_path, host.write_file.call_args[0][0])
88 conf = json.loads(host.write_file.call_args[0][1])
89 self.assertEqual("file1", conf["files"][0]["paths"][0])
90 host.service_restart.assert_called_once_with('logstash-forwarder')
91
92 def test_logs_realtion(self):
93 data = hooks.logs_relation()
94 self.assertEqual([('type1', 'file1'), ('type2', 'file2')], data)
095
=== modified file 'metadata.yaml'
--- metadata.yaml 2015-05-29 04:52:33 +0000
+++ metadata.yaml 2015-12-10 18:17:52 +0000
@@ -4,7 +4,7 @@
4description: |4description: |
5 This charm installs and configures logstash-forwarder, a lightweight5 This charm installs and configures logstash-forwarder, a lightweight
6 alternative to logstash-agent.6 alternative to logstash-agent.
7categories:7tags:
8 - miscellaneous8 - miscellaneous
9subordinate: true9subordinate: true
10provides:10provides:
@@ -15,3 +15,6 @@
15 juju-info:15 juju-info:
16 interface: juju-info16 interface: juju-info
17 scope: container17 scope: container
18 logs:
19 interface: logs
20 scope: container

Subscribers

People subscribed via source and target branches

to all changes: