Merge lp:~ricardokirkner/charms/trusty/logstash/logstash2-extra-plugins into lp:~canonical-is-sa/charms/trusty/logstash/logstash2

Proposed by Ricardo Kirkner
Status: Merged
Approved by: Alexandre Gomes
Approved revision: 75
Merged at revision: 69
Proposed branch: lp:~ricardokirkner/charms/trusty/logstash/logstash2-extra-plugins
Merge into: lp:~canonical-is-sa/charms/trusty/logstash/logstash2
Diff against target: 135 lines (+86/-3)
2 files modified
config.yaml (+10/-0)
hooks/config-changed (+76/-3)
To merge this branch: bzr merge lp:~ricardokirkner/charms/trusty/logstash/logstash2-extra-plugins
Reviewer Review Type Date Requested Status
Alexandre Gomes Approve
Guillermo Gonzalez (community) Approve
Review via email: mp+336316@code.launchpad.net

Commit message

added support for installing extra plugins

update logstash plugins during config-changed hook to allow for updating after installed

To post a comment you must log in.
Revision history for this message
Guillermo Gonzalez (verterok) wrote :

+1

review: Approve
Revision history for this message
Alexandre Gomes (alejdg) wrote :

+1

review: Approve
74. By Ricardo Kirkner

added workaround to avoid uninstall process from hanging

75. By Ricardo Kirkner

fixed typo

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'config.yaml'
2--- config.yaml 2017-12-04 14:43:22 +0000
3+++ config.yaml 2018-01-19 16:20:49 +0000
4@@ -11,6 +11,16 @@
5 type: string
6 default: ''
7 description: Space separated list of extra apt packages to install.
8+ extra-plugins:
9+ type: string
10+ default: ''
11+ descripion: |
12+ Space separated list of plugins to install.
13+ Format for each item is name:version.
14+ extra-plugins-repo:
15+ type: string
16+ default: "https://archive.admin.canonical.com/other/logstash"
17+ description: "Location to download logstash plugins from."
18 ssl_cert:
19 default: ""
20 type: string
21
22=== modified file 'hooks/config-changed'
23--- hooks/config-changed 2017-11-30 13:53:27 +0000
24+++ hooks/config-changed 2018-01-19 16:20:49 +0000
25@@ -1,13 +1,15 @@
26 #!/usr/bin/python
27
28+import base64
29+import grp
30 import os
31+import pwd
32+import re
33 import shlex
34 import subprocess
35 import sys
36-import pwd
37-import grp
38-import base64
39
40+import requests
41
42 sys.path.insert(0, os.path.join(os.environ['CHARM_DIR'], 'lib'))
43
44@@ -21,6 +23,7 @@
45
46 SERVICE = 'logstash-indexer'
47 BASEPATH = os.path.join(os.path.sep, 'opt', 'logstash')
48+PLUGIN_CMD = os.path.join(BASEPATH, 'bin', 'plugin')
49
50
51 @hooks.hook('config-changed')
52@@ -36,6 +39,12 @@
53 copy_config()
54 place_upstart_template()
55
56+ extra_plugins = config.get('extra-plugins', '').split()
57+ if extra_plugins:
58+ log('Updating logstash plugins')
59+ plugins = dict(map(lambda x: x.split(':'), extra_plugins))
60+ update_plugins(plugins)
61+
62 # This only actually opens the port if we've exposed the service in juju
63 # open port for lumberjack
64 hookenv.open_port(5043)
65@@ -132,6 +141,70 @@
66 p.write(render('{}.conf'.format(SERVICE), opts, template_dir=templ))
67
68
69+def update_plugins(plugins):
70+ """Make sure all requested plugins are installed at the right versions."""
71+ fetch_plugins(plugins)
72+
73+ for (name, version) in plugins.items():
74+ current_version = get_plugin_version(name)
75+ if current_version == version:
76+ log('Plugin {} already at version {}'.format(name, version))
77+ continue
78+ elif current_version is not None:
79+ uninstall_plugin(name)
80+ install_plugin(name, version)
81+
82+
83+def fetch_plugins(plugins):
84+ """Download plugin gems from repository."""
85+ config = hookenv.config()
86+ repo_url = config.get('extra-plugins-repo')
87+ if repo_url:
88+ for (name, version) in plugins.items():
89+ filename = '{}-{}.gem'.format(name, version)
90+ fpath = os.path.join(os.path.sep, 'tmp', filename)
91+ if os.path.exists(fpath):
92+ log("Plugin {} already fetched, skipping".format(filename))
93+ continue
94+
95+ response = requests.get('{}/{}'.format(repo_url, filename))
96+ with open(fpath, 'wb') as handle:
97+ for block in response.iter_content(1024):
98+ if not block:
99+ break
100+ handle.write(block)
101+
102+
103+def get_plugin_version(name):
104+ """Return version for named plugin.
105+
106+ Return None if version can not be found.
107+ """
108+ try:
109+ output = subprocess.check_output([PLUGIN_CMD, "list", "--verbose", name])
110+ except subprocess.CalledProcessError:
111+ return
112+
113+ match = re.match('.*\((.*)\)$', output)
114+ if match:
115+ return match.groups()[0]
116+
117+
118+def uninstall_plugin(name):
119+ """Uninstall plugin by name."""
120+ # make sure to specify a fake proxy to avoid the uninstall process
121+ # try to reach out to https://rubygems.org to fetch specs
122+ env = dict(os.environ, http_proxy="http://localhost:3128"env)
123+ subprocess.call([PLUGIN_CMD, "uninstall", name], env=env)
124+
125+
126+def install_plugin(name, version):
127+ """Install plugin from local file."""
128+ filename = '{}-{}.gem'.format(name, version)
129+ fpath = os.path.join(os.path.sep, 'tmp', filename)
130+ subprocess.check_call([PLUGIN_CMD, "install", "--no-verify", fpath])
131+
132+
133 if __name__ == "__main__":
134 # execute a hook based on the name the program is called by
135 hooks.execute(sys.argv)

Subscribers

People subscribed via source and target branches