Merge lp:~bigdata-dev/charms/trusty/apache-flume-twitter/trunk into lp:charms/trusty/apache-flume-twitter

Proposed by Kevin W Monroe
Status: Merged
Merged at revision: 2
Proposed branch: lp:~bigdata-dev/charms/trusty/apache-flume-twitter/trunk
Merge into: lp:charms/trusty/apache-flume-twitter
Diff against target: 1829 lines (+1701/-0) (has conflicts)
20 files modified
.bzrignore (+1/-0)
LICENSE (+177/-0)
README.md (+97/-0)
config.yaml (+59/-0)
copyright (+16/-0)
dist.yaml (+18/-0)
hooks/callbacks.py (+153/-0)
hooks/common.py (+87/-0)
hooks/config-changed (+15/-0)
hooks/flume-agent-relation-changed (+15/-0)
hooks/install (+17/-0)
hooks/setup.py (+33/-0)
hooks/start (+15/-0)
hooks/stop (+15/-0)
icon.svg (+863/-0)
metadata.yaml (+10/-0)
resources.yaml (+12/-0)
templates/flume.conf.j2 (+31/-0)
tests/00-setup (+5/-0)
tests/remote/test_dist_config.py (+62/-0)
Conflict adding file .bzrignore.  Moved existing file to .bzrignore.moved.
Conflict adding file LICENSE.  Moved existing file to LICENSE.moved.
Conflict adding file README.md.  Moved existing file to README.md.moved.
Conflict adding file config.yaml.  Moved existing file to config.yaml.moved.
Conflict adding file copyright.  Moved existing file to copyright.moved.
Conflict adding file dist.yaml.  Moved existing file to dist.yaml.moved.
Conflict adding file hooks.  Moved existing file to hooks.moved.
Conflict adding file icon.svg.  Moved existing file to icon.svg.moved.
Conflict adding file metadata.yaml.  Moved existing file to metadata.yaml.moved.
Conflict adding file resources.  Moved existing file to resources.moved.
Conflict adding file resources.yaml.  Moved existing file to resources.yaml.moved.
Conflict adding file templates.  Moved existing file to templates.moved.
Conflict adding file tests.  Moved existing file to tests.moved.
To merge this branch: bzr merge lp:~bigdata-dev/charms/trusty/apache-flume-twitter/trunk
Reviewer Review Type Date Requested Status
Kevin W Monroe Approve
Review via email: mp+268664@code.launchpad.net
To post a comment you must log in.
15. By Cory Johns

Fixed permissions on test_dist_config.py

Revision history for this message
Kevin W Monroe (kwmonroe) wrote :

Realtime syslog analytics bundle test looked good. Merged.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.bzrignore'
2--- .bzrignore 1970-01-01 00:00:00 +0000
3+++ .bzrignore 2015-08-21 21:51:07 +0000
4@@ -0,0 +1,1 @@
5+secret.yaml
6
7=== renamed file '.bzrignore' => '.bzrignore.moved'
8=== added file 'LICENSE'
9--- LICENSE 1970-01-01 00:00:00 +0000
10+++ LICENSE 2015-08-21 21:51:07 +0000
11@@ -0,0 +1,177 @@
12+
13+ Apache License
14+ Version 2.0, January 2004
15+ http://www.apache.org/licenses/
16+
17+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
18+
19+ 1. Definitions.
20+
21+ "License" shall mean the terms and conditions for use, reproduction,
22+ and distribution as defined by Sections 1 through 9 of this document.
23+
24+ "Licensor" shall mean the copyright owner or entity authorized by
25+ the copyright owner that is granting the License.
26+
27+ "Legal Entity" shall mean the union of the acting entity and all
28+ other entities that control, are controlled by, or are under common
29+ control with that entity. For the purposes of this definition,
30+ "control" means (i) the power, direct or indirect, to cause the
31+ direction or management of such entity, whether by contract or
32+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
33+ outstanding shares, or (iii) beneficial ownership of such entity.
34+
35+ "You" (or "Your") shall mean an individual or Legal Entity
36+ exercising permissions granted by this License.
37+
38+ "Source" form shall mean the preferred form for making modifications,
39+ including but not limited to software source code, documentation
40+ source, and configuration files.
41+
42+ "Object" form shall mean any form resulting from mechanical
43+ transformation or translation of a Source form, including but
44+ not limited to compiled object code, generated documentation,
45+ and conversions to other media types.
46+
47+ "Work" shall mean the work of authorship, whether in Source or
48+ Object form, made available under the License, as indicated by a
49+ copyright notice that is included in or attached to the work
50+ (an example is provided in the Appendix below).
51+
52+ "Derivative Works" shall mean any work, whether in Source or Object
53+ form, that is based on (or derived from) the Work and for which the
54+ editorial revisions, annotations, elaborations, or other modifications
55+ represent, as a whole, an original work of authorship. For the purposes
56+ of this License, Derivative Works shall not include works that remain
57+ separable from, or merely link (or bind by name) to the interfaces of,
58+ the Work and Derivative Works thereof.
59+
60+ "Contribution" shall mean any work of authorship, including
61+ the original version of the Work and any modifications or additions
62+ to that Work or Derivative Works thereof, that is intentionally
63+ submitted to Licensor for inclusion in the Work by the copyright owner
64+ or by an individual or Legal Entity authorized to submit on behalf of
65+ the copyright owner. For the purposes of this definition, "submitted"
66+ means any form of electronic, verbal, or written communication sent
67+ to the Licensor or its representatives, including but not limited to
68+ communication on electronic mailing lists, source code control systems,
69+ and issue tracking systems that are managed by, or on behalf of, the
70+ Licensor for the purpose of discussing and improving the Work, but
71+ excluding communication that is conspicuously marked or otherwise
72+ designated in writing by the copyright owner as "Not a Contribution."
73+
74+ "Contributor" shall mean Licensor and any individual or Legal Entity
75+ on behalf of whom a Contribution has been received by Licensor and
76+ subsequently incorporated within the Work.
77+
78+ 2. Grant of Copyright License. Subject to the terms and conditions of
79+ this License, each Contributor hereby grants to You a perpetual,
80+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
81+ copyright license to reproduce, prepare Derivative Works of,
82+ publicly display, publicly perform, sublicense, and distribute the
83+ Work and such Derivative Works in Source or Object form.
84+
85+ 3. Grant of Patent License. Subject to the terms and conditions of
86+ this License, each Contributor hereby grants to You a perpetual,
87+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
88+ (except as stated in this section) patent license to make, have made,
89+ use, offer to sell, sell, import, and otherwise transfer the Work,
90+ where such license applies only to those patent claims licensable
91+ by such Contributor that are necessarily infringed by their
92+ Contribution(s) alone or by combination of their Contribution(s)
93+ with the Work to which such Contribution(s) was submitted. If You
94+ institute patent litigation against any entity (including a
95+ cross-claim or counterclaim in a lawsuit) alleging that the Work
96+ or a Contribution incorporated within the Work constitutes direct
97+ or contributory patent infringement, then any patent licenses
98+ granted to You under this License for that Work shall terminate
99+ as of the date such litigation is filed.
100+
101+ 4. Redistribution. You may reproduce and distribute copies of the
102+ Work or Derivative Works thereof in any medium, with or without
103+ modifications, and in Source or Object form, provided that You
104+ meet the following conditions:
105+
106+ (a) You must give any other recipients of the Work or
107+ Derivative Works a copy of this License; and
108+
109+ (b) You must cause any modified files to carry prominent notices
110+ stating that You changed the files; and
111+
112+ (c) You must retain, in the Source form of any Derivative Works
113+ that You distribute, all copyright, patent, trademark, and
114+ attribution notices from the Source form of the Work,
115+ excluding those notices that do not pertain to any part of
116+ the Derivative Works; and
117+
118+ (d) If the Work includes a "NOTICE" text file as part of its
119+ distribution, then any Derivative Works that You distribute must
120+ include a readable copy of the attribution notices contained
121+ within such NOTICE file, excluding those notices that do not
122+ pertain to any part of the Derivative Works, in at least one
123+ of the following places: within a NOTICE text file distributed
124+ as part of the Derivative Works; within the Source form or
125+ documentation, if provided along with the Derivative Works; or,
126+ within a display generated by the Derivative Works, if and
127+ wherever such third-party notices normally appear. The contents
128+ of the NOTICE file are for informational purposes only and
129+ do not modify the License. You may add Your own attribution
130+ notices within Derivative Works that You distribute, alongside
131+ or as an addendum to the NOTICE text from the Work, provided
132+ that such additional attribution notices cannot be construed
133+ as modifying the License.
134+
135+ You may add Your own copyright statement to Your modifications and
136+ may provide additional or different license terms and conditions
137+ for use, reproduction, or distribution of Your modifications, or
138+ for any such Derivative Works as a whole, provided Your use,
139+ reproduction, and distribution of the Work otherwise complies with
140+ the conditions stated in this License.
141+
142+ 5. Submission of Contributions. Unless You explicitly state otherwise,
143+ any Contribution intentionally submitted for inclusion in the Work
144+ by You to the Licensor shall be under the terms and conditions of
145+ this License, without any additional terms or conditions.
146+ Notwithstanding the above, nothing herein shall supersede or modify
147+ the terms of any separate license agreement you may have executed
148+ with Licensor regarding such Contributions.
149+
150+ 6. Trademarks. This License does not grant permission to use the trade
151+ names, trademarks, service marks, or product names of the Licensor,
152+ except as required for reasonable and customary use in describing the
153+ origin of the Work and reproducing the content of the NOTICE file.
154+
155+ 7. Disclaimer of Warranty. Unless required by applicable law or
156+ agreed to in writing, Licensor provides the Work (and each
157+ Contributor provides its Contributions) on an "AS IS" BASIS,
158+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
159+ implied, including, without limitation, any warranties or conditions
160+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
161+ PARTICULAR PURPOSE. You are solely responsible for determining the
162+ appropriateness of using or redistributing the Work and assume any
163+ risks associated with Your exercise of permissions under this License.
164+
165+ 8. Limitation of Liability. In no event and under no legal theory,
166+ whether in tort (including negligence), contract, or otherwise,
167+ unless required by applicable law (such as deliberate and grossly
168+ negligent acts) or agreed to in writing, shall any Contributor be
169+ liable to You for damages, including any direct, indirect, special,
170+ incidental, or consequential damages of any character arising as a
171+ result of this License or out of the use or inability to use the
172+ Work (including but not limited to damages for loss of goodwill,
173+ work stoppage, computer failure or malfunction, or any and all
174+ other commercial damages or losses), even if such Contributor
175+ has been advised of the possibility of such damages.
176+
177+ 9. Accepting Warranty or Additional Liability. While redistributing
178+ the Work or Derivative Works thereof, You may choose to offer,
179+ and charge a fee for, acceptance of support, warranty, indemnity,
180+ or other liability obligations and/or rights consistent with this
181+ License. However, in accepting such obligations, You may act only
182+ on Your own behalf and on Your sole responsibility, not on behalf
183+ of any other Contributor, and only if You agree to indemnify,
184+ defend, and hold each Contributor harmless for any liability
185+ incurred by, or claims asserted against, such Contributor by reason
186+ of your accepting any such warranty or additional liability.
187+
188+ END OF TERMS AND CONDITIONS
189
190=== renamed file 'LICENSE' => 'LICENSE.moved'
191=== added file 'README.md'
192--- README.md 1970-01-01 00:00:00 +0000
193+++ README.md 2015-08-21 21:51:07 +0000
194@@ -0,0 +1,97 @@
195+## Overview
196+
197+Flume is a distributed, reliable, and available service for efficiently
198+collecting, aggregating, and moving large amounts of log data. It has a simple
199+and flexible architecture based on streaming data flows. It is robust and fault
200+tolerant with tunable reliability mechanisms and many failover and recovery
201+mechanisms. It uses a simple extensible data model that allows for online
202+analytic application. Learn more at [flume.apache.org](http://flume.apache.org).
203+
204+This charm provides a Flume agent designed to process tweets from the Twitter
205+Streaming API and send them to the `apache-flume-hdfs` agent for storage in
206+the shared filesystem (HDFS) of a connected Hadoop cluster. This leverages the
207+TwitterSource jar packaged with Flume. Learn more about the
208+[1% firehose](https://flume.apache.org/FlumeUserGuide.html#twitter-1-firehose-source-experimental).
209+
210+
211+## Prerequisites
212+
213+The Twitter Streaming API requires developer credentials. You'll need to
214+configure those for this charm. Find your credentials (or create an account
215+if needed) [here](https://apps.twitter.com/).
216+
217+Create a `secret.yaml` file with your Twitter developer credentials:
218+
219+ flume-twitter:
220+ twitter_access_token: 'YOUR_TOKEN'
221+ twitter_access_token_secret: 'YOUR_TOKEN_SECRET'
222+ twitter_consumer_key: 'YOUR_CONSUMER_KEY'
223+ twitter_consumer_secret: 'YOUR_CONSUMER_SECRET'
224+
225+
226+## Usage
227+
228+This charm leverages our pluggable Hadoop model with the `hadoop-plugin`
229+interface. This means that you will need to deploy a base Apache Hadoop cluster
230+to run Flume. The suggested deployment method is to use the
231+[apache-ingestion-flume](https://jujucharms.com/u/bigdata-dev/apache-ingestion-flume/)
232+bundle. This will deploy the Apache Hadoop platform with a single Apache Flume
233+unit that communicates with the cluster by relating to the
234+`apache-hadoop-plugin` subordinate charm:
235+
236+ juju quickstart u/bigdata-dev/apache-ingestion-flume
237+
238+Alternatively, you may manually deploy the recommended environment as follows:
239+
240+ juju deploy apache-hadoop-hdfs-master hdfs-master
241+ juju deploy apache-hadoop-yarn-master yarn-master
242+ juju deploy apache-hadoop-compute-slave compute-slave
243+ juju deploy apache-hadoop-plugin plugin
244+ juju deploy apache-flume-hdfs flume-hdfs
245+
246+ juju add-relation yarn-master hdfs-master
247+ juju add-relation compute-slave yarn-master
248+ juju add-relation compute-slave hdfs-master
249+ juju add-relation plugin yarn-master
250+ juju add-relation plugin hdfs-master
251+ juju add-relation flume-hdfs plugin
252+
253+Once the bundle has been deployed, add the `apache-flume-twitter` charm and
254+relate it to the `flume-hdfs` agent:
255+
256+ juju deploy apache-flume-twitter flume-twitter --config=secret.yaml
257+ juju add-relation flume-twitter flume-hdfs
258+
259+That's it! Once the Flume agents start, tweets will start flowing into
260+HDFS in year-month-day/hour directories here: `/user/flume/events/%y-%m-%d/%H`.
261+
262+
263+## Test the deployment
264+
265+To verify this charm is working as intended, SSH to the `flume-hdfs` unit,
266+locate an event, and cat it:
267+
268+ juju ssh flume-hdfs/0
269+ hdfs dfs -ls /user/flume/events # <-- find a date
270+ hdfs dfs -ls /user/flume/events/yy-mm-dd # <-- find an hour
271+ hdfs dfs -ls /user/flume/events/yy-mm-dd/HH # <-- find an event
272+ hdfs dfs -cat /user/flume/events/yy-mm-dd/HH/FlumeData.[id].avro
273+
274+You'll see AVRO headers since that's the default format used to contain the
275+tweets. You may not recognize the body of the tweet if it's not in a
276+language you understand (remember, this is a 1% firehose from tweets all over
277+the world). You may have to cat a few different events before you find a
278+tweet worth reading. Happy hunting!
279+
280+
281+## Contact Information
282+
283+- <bigdata-dev@lists.launchpad.net>
284+
285+
286+## Help
287+
288+- [Apache Flume home page](http://flume.apache.org/)
289+- [Apache Flume bug tracker](https://issues.apache.org/jira/browse/flume)
290+- [Apache Flume mailing lists](https://flume.apache.org/mailinglists.html)
291+- `#juju` on `irc.freenode.net`
292
293=== renamed file 'README.md' => 'README.md.moved'
294=== added file 'config.yaml'
295--- config.yaml 1970-01-01 00:00:00 +0000
296+++ config.yaml 2015-08-21 21:51:07 +0000
297@@ -0,0 +1,59 @@
298+options:
299+ resources_mirror:
300+ type: string
301+ default: ''
302+ description: |
303+ URL from which to fetch resources (e.g., Hadoop binaries) instead
304+ of Launchpad.
305+ channel_capacity:
306+ type: string
307+ default: '1000'
308+ description: |
309+ The maximum number of events stored in the channel.
310+ channel_transaction_capacity:
311+ type: string
312+ default: '100'
313+ description: |
314+ The maximum number of events the channel will take from a source or
315+ give to a sink per transaction.
316+ event_dir:
317+ type: string
318+ default: 'flume-twitter'
319+ description: |
320+ The HDFS subdirectory under /user/flume where events will be stored.
321+ twitter_source:
322+ type: string
323+ default: 'org.apache.flume.source.twitter.TwitterSource'
324+ description: |
325+ The application to use for this Flume source. Deafult to TwitterSource
326+ bundled with Flume.
327+ twitter_access_token:
328+ type: string
329+ default: ''
330+ description: |
331+ OAuth Access token from your Twitter developer account
332+ twitter_access_token_secret:
333+ type: string
334+ default: ''
335+ description: |
336+ OAuth Access token secret from your Twitter developer account
337+ twitter_consumer_key:
338+ type: string
339+ default: ''
340+ description: |
341+ OAuth Consumer key from your Twitter developer account
342+ twitter_consumer_secret:
343+ type: string
344+ default: ''
345+ description: |
346+ OAth Consumer secret from your Twitter developer account
347+ twitter_max_batch_size:
348+ type: int
349+ default: 1000
350+ description: |
351+ Maximum number of twitter messages to put in a single batch
352+ twitter_max_batch_duration:
353+ type: int
354+ default: 1000
355+ description: |
356+ Maximum number of milliseconds to wait before closing a batch
357
358=== renamed file 'config.yaml' => 'config.yaml.moved'
359=== added file 'copyright'
360--- copyright 1970-01-01 00:00:00 +0000
361+++ copyright 2015-08-21 21:51:07 +0000
362@@ -0,0 +1,16 @@
363+Format: http://dep.debian.net/deps/dep5/
364+
365+Files: *
366+Copyright: Copyright 2015, Canonical Ltd., All Rights Reserved.
367+License: Apache License 2.0
368+ Licensed under the Apache License, Version 2.0 (the "License");
369+ you may not use this file except in compliance with the License.
370+ You may obtain a copy of the License at
371+ .
372+ http://www.apache.org/licenses/LICENSE-2.0
373+ .
374+ Unless required by applicable law or agreed to in writing, software
375+ distributed under the License is distributed on an "AS IS" BASIS,
376+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
377+ See the License for the specific language governing permissions and
378+ limitations under the License.
379
380=== renamed file 'copyright' => 'copyright.moved'
381=== added file 'dist.yaml'
382--- dist.yaml 1970-01-01 00:00:00 +0000
383+++ dist.yaml 2015-08-21 21:51:07 +0000
384@@ -0,0 +1,18 @@
385+# This file contains values that are likely to change per distribution.
386+# The aim is to make it easier to update / extend the charms with
387+# minimal changes to the shared code in charmhelpers.
388+packages:
389+ - 'openjdk-7-jre'
390+groups:
391+ - 'hadoop'
392+users:
393+ flume:
394+ groups: ['hadoop']
395+dirs:
396+ flume:
397+ path: '/usr/lib/flume-twitter'
398+ flume_conf:
399+ path: '/etc/flume-twitter/conf'
400+ flume_logs:
401+ path: '/var/log/flume-twitter'
402+ owner: 'flume'
403
404=== renamed file 'dist.yaml' => 'dist.yaml.moved'
405=== added directory 'hooks'
406=== renamed directory 'hooks' => 'hooks.moved'
407=== added file 'hooks/callbacks.py'
408--- hooks/callbacks.py 1970-01-01 00:00:00 +0000
409+++ hooks/callbacks.py 2015-08-21 21:51:07 +0000
410@@ -0,0 +1,153 @@
411+import os
412+import re
413+import signal
414+from subprocess import Popen, check_output
415+
416+import jujuresources
417+from charmhelpers.core import hookenv
418+from charmhelpers.core import host
419+from charmhelpers.core import unitdata
420+from jujubigdata import utils
421+from jujubigdata.relations import FlumeAgent
422+
423+
424+API_CRED_OPTIONS = [
425+ 'twitter_access_token',
426+ 'twitter_access_token_secret',
427+ 'twitter_consumer_key',
428+ 'twitter_consumer_secret',
429+]
430+
431+
432+# Extended status support
433+# We call update_blocked_status from the "requires" section of our service
434+# block, so be sure to return True. Otherwise, we'll block the "requires"
435+# and never move on to callbacks. The other status update methods are called
436+# from the "callbacks" section and therefore don't need to return True.
437+def update_blocked_status():
438+ if not all(map(hookenv.config().get, API_CRED_OPTIONS)):
439+ hookenv.status_set('blocked', 'Twitter Streaming API credentials must be set')
440+ return True
441+ if unitdata.kv().get('charm.active', False):
442+ return True
443+ if not FlumeAgent().connected_units():
444+ hookenv.status_set('blocked', 'Waiting for relation to apache-flume-hdfs')
445+ elif not FlumeAgent().is_ready():
446+ hookenv.status_set('waiting', 'Waiting for Flume/HDFS to become ready')
447+ return True
448+
449+
450+def update_working_status():
451+ if unitdata.kv().get('charm.active', False):
452+ hookenv.status_set('maintenance', 'Updating configuration')
453+ return
454+ hookenv.status_set('maintenance', 'Setting up Flume/Twitter')
455+
456+
457+def update_active_status():
458+ unitdata.kv().set('charm.active', True)
459+ if not all(map(hookenv.config().get, API_CRED_OPTIONS)):
460+ hookenv.status_set('blocked', 'Twitter Streaming API credentials must be set')
461+ else:
462+ hookenv.status_set('active', 'Ready')
463+
464+
465+# Main Flume Twitter class for callbacks
466+class Flume(object):
467+ def __init__(self, dist_config):
468+ self.dist_config = dist_config
469+ self.resources = {
470+ 'flume': 'flume-%s' % host.cpu_arch(),
471+ }
472+ self.verify_resources = utils.verify_resources(*self.resources.values())
473+
474+ def is_installed(self):
475+ return unitdata.kv().get('flume.installed')
476+
477+ def install(self, force=False):
478+ if not force and self.is_installed():
479+ return
480+ jujuresources.install(self.resources['flume'],
481+ destination=self.dist_config.path('flume'),
482+ skip_top_level=True)
483+ self.dist_config.add_users()
484+ self.dist_config.add_dirs()
485+ self.dist_config.add_packages()
486+ self.setup_flume_config()
487+ self.configure_flume()
488+ unitdata.kv().set('flume.installed', True)
489+
490+ def setup_flume_config(self):
491+ '''
492+ copy the default configuration files to flume_conf property
493+ defined in dist.yaml
494+ '''
495+ default_conf = self.dist_config.path('flume') / 'conf'
496+ flume_conf = self.dist_config.path('flume_conf')
497+ flume_conf.rmtree_p()
498+ default_conf.copytree(flume_conf)
499+ # Now remove the conf included in the tarball and symlink our real conf
500+ default_conf.rmtree_p()
501+ flume_conf.symlink(default_conf)
502+
503+ flume_env = self.dist_config.path('flume_conf') / 'flume-env.sh'
504+ if not flume_env.exists():
505+ (self.dist_config.path('flume_conf') / 'flume-env.sh.template').copy(flume_env)
506+
507+ flume_conf = self.dist_config.path('flume_conf') / 'flume.conf'
508+ if not flume_conf.exists():
509+ (self.dist_config.path('flume_conf') / 'flume-conf.properties.template').copy(flume_conf)
510+
511+ flume_log4j = self.dist_config.path('flume_conf') / 'log4j.properties'
512+ utils.re_edit_in_place(flume_log4j, {
513+ r'^flume.log.dir.*': 'flume.log.dir={}'.format(self.dist_config.path('flume_logs')),
514+ })
515+
516+ def configure_flume(self):
517+ flume_bin = self.dist_config.path('flume') / 'bin'
518+ java_symlink = check_output(["readlink", "-f", "/usr/bin/java"])
519+ java_home = re.sub('/bin/java', '', java_symlink).rstrip()
520+ with utils.environment_edit_in_place('/etc/environment') as env:
521+ if flume_bin not in env['PATH']:
522+ env['PATH'] = ':'.join([env['PATH'], flume_bin])
523+ env['FLUME_CONF_DIR'] = self.dist_config.path('flume_conf')
524+ env['FLUME_CLASSPATH'] = self.dist_config.path('flume') / 'lib'
525+ env['FLUME_HOME'] = self.dist_config.path('flume')
526+ env['JAVA_HOME'] = java_home
527+
528+ def run_bg(self, user, command, *args):
529+ """
530+ Run a command as the given user in the background.
531+
532+ :param str user: User to run flume agent
533+ :param str command: Command to run
534+ :param list args: Additional args to pass to the command
535+ """
536+ parts = [command] + list(args)
537+ quoted = ' '.join("'%s'" % p for p in parts)
538+ e = utils.read_etc_env()
539+ Popen(['su', user, '-c', quoted], env=e)
540+
541+ def restart(self):
542+ # check for a java process with our flume dir in the classpath
543+ if utils.jps(r'-cp .*{}'.format(self.dist_config.path('flume'))):
544+ self.stop()
545+ self.start()
546+
547+ def start(self):
548+ self.run_bg(
549+ 'flume',
550+ self.dist_config.path('flume') / 'bin/flume-ng',
551+ 'agent',
552+ '-c', self.dist_config.path('flume_conf'),
553+ '-f', self.dist_config.path('flume_conf') / 'flume.conf',
554+ '-n', 'a1')
555+
556+ def stop(self):
557+ flume_pids = utils.jps(r'-cp .*{}'.format(self.dist_config.path('flume')))
558+ for pid in flume_pids:
559+ os.kill(int(pid), signal.SIGKILL)
560+
561+ def cleanup(self):
562+ self.dist_config.remove_users()
563+ self.dist_config.remove_dirs()
564
565=== added file 'hooks/common.py'
566--- hooks/common.py 1970-01-01 00:00:00 +0000
567+++ hooks/common.py 2015-08-21 21:51:07 +0000
568@@ -0,0 +1,87 @@
569+#!/usr/bin/env python
570+# Licensed under the Apache License, Version 2.0 (the "License");
571+# you may not use this file except in compliance with the License.
572+# You may obtain a copy of the License at
573+#
574+# http://www.apache.org/licenses/LICENSE-2.0
575+#
576+# Unless required by applicable law or agreed to in writing, software
577+# distributed under the License is distributed on an "AS IS" BASIS,
578+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
579+# See the License for the specific language governing permissions and
580+# limitations under the License.
581+"""
582+Common implementation for all hooks.
583+"""
584+
585+import jujuresources
586+from charmhelpers.core import hookenv
587+from charmhelpers.core import unitdata
588+from charmhelpers.core import charmframework
589+
590+
591+def bootstrap_resources():
592+ """
593+ Install required resources defined in resources.yaml
594+ """
595+ if unitdata.kv().get('charm.bootstrapped', False):
596+ return True
597+ hookenv.status_set('maintenance', 'Installing base resources')
598+ mirror_url = jujuresources.config_get('resources_mirror')
599+ if not jujuresources.fetch(mirror_url=mirror_url):
600+ missing = jujuresources.invalid()
601+ hookenv.status_set('blocked', 'Unable to fetch required resource%s: %s' % (
602+ 's' if len(missing) > 1 else '',
603+ ', '.join(missing),
604+ ))
605+ return False
606+ jujuresources.install(['pathlib', 'jujubigdata'])
607+ unitdata.kv().set('charm.bootstrapped', True)
608+ return True
609+
610+
611+def manage():
612+ if not bootstrap_resources():
613+ # defer until resources are available, since charmhelpers, and thus
614+ # the framework, are required (will require manual intervention)
615+ return
616+
617+ import jujubigdata
618+ import callbacks
619+
620+ flume_reqs = ['packages', 'groups', 'users', 'dirs']
621+ dist_config = jujubigdata.utils.DistConfig(filename='dist.yaml',
622+ required_keys=flume_reqs)
623+ flume = callbacks.Flume(dist_config)
624+ manager = charmframework.Manager([
625+ {
626+ 'name': 'flume',
627+ 'provides': [
628+ ],
629+ 'requires': [
630+ flume.verify_resources,
631+ jujubigdata.relations.FlumeAgent(),
632+ callbacks.update_blocked_status, # not really a requirement, but best way to fit into framework
633+ ],
634+ 'callbacks': [
635+ callbacks.update_working_status,
636+ flume.install,
637+ charmframework.helpers.render_template(
638+ source='flume.conf.j2',
639+ target=flume.dist_config.path('flume_conf') / 'flume.conf',
640+ context={'dist_config': dist_config}
641+ ),
642+ flume.restart,
643+ callbacks.update_active_status,
644+ ],
645+ 'cleanup': [
646+ flume.stop,
647+ flume.cleanup,
648+ ],
649+ },
650+ ])
651+ manager.manage()
652+
653+
654+if __name__ == '__main__':
655+ manage()
656
657=== added file 'hooks/config-changed'
658--- hooks/config-changed 1970-01-01 00:00:00 +0000
659+++ hooks/config-changed 2015-08-21 21:51:07 +0000
660@@ -0,0 +1,15 @@
661+#!/usr/bin/env python
662+# Licensed under the Apache License, Version 2.0 (the "License");
663+# you may not use this file except in compliance with the License.
664+# You may obtain a copy of the License at
665+#
666+# http://www.apache.org/licenses/LICENSE-2.0
667+#
668+# Unless required by applicable law or agreed to in writing, software
669+# distributed under the License is distributed on an "AS IS" BASIS,
670+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
671+# See the License for the specific language governing permissions and
672+# limitations under the License.
673+
674+import common
675+common.manage()
676
677=== added file 'hooks/flume-agent-relation-changed'
678--- hooks/flume-agent-relation-changed 1970-01-01 00:00:00 +0000
679+++ hooks/flume-agent-relation-changed 2015-08-21 21:51:07 +0000
680@@ -0,0 +1,15 @@
681+#!/usr/bin/env python
682+# Licensed under the Apache License, Version 2.0 (the "License");
683+# you may not use this file except in compliance with the License.
684+# You may obtain a copy of the License at
685+#
686+# http://www.apache.org/licenses/LICENSE-2.0
687+#
688+# Unless required by applicable law or agreed to in writing, software
689+# distributed under the License is distributed on an "AS IS" BASIS,
690+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
691+# See the License for the specific language governing permissions and
692+# limitations under the License.
693+
694+import common
695+common.manage()
696
697=== added file 'hooks/install'
698--- hooks/install 1970-01-01 00:00:00 +0000
699+++ hooks/install 2015-08-21 21:51:07 +0000
700@@ -0,0 +1,17 @@
701+#!/usr/bin/python
702+# Licensed under the Apache License, Version 2.0 (the "License");
703+# you may not use this file except in compliance with the License.
704+# You may obtain a copy of the License at
705+#
706+# http://www.apache.org/licenses/LICENSE-2.0
707+#
708+# Unless required by applicable law or agreed to in writing, software
709+# distributed under the License is distributed on an "AS IS" BASIS,
710+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
711+# See the License for the specific language governing permissions and
712+# limitations under the License.
713+import setup
714+setup.pre_install()
715+
716+import common
717+common.manage()
718
719=== added file 'hooks/setup.py'
720--- hooks/setup.py 1970-01-01 00:00:00 +0000
721+++ hooks/setup.py 2015-08-21 21:51:07 +0000
722@@ -0,0 +1,33 @@
723+# Licensed under the Apache License, Version 2.0 (the "License");
724+# you may not use this file except in compliance with the License.
725+# You may obtain a copy of the License at
726+#
727+# http://www.apache.org/licenses/LICENSE-2.0
728+#
729+# Unless required by applicable law or agreed to in writing, software
730+# distributed under the License is distributed on an "AS IS" BASIS,
731+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
732+# See the License for the specific language governing permissions and
733+# limitations under the License.
734+import subprocess
735+from glob import glob
736+
737+
738+def pre_install():
739+ """
740+ Do any setup required before the install hook.
741+ """
742+ install_pip()
743+ install_bundled_resources()
744+
745+
746+def install_pip():
747+ subprocess.check_call(['apt-get', 'install', '-yq', 'python-pip', 'bzr'])
748+
749+
750+def install_bundled_resources():
751+ """
752+ Install the bundled resources libraries.
753+ """
754+ archives = glob('resources/python/*')
755+ subprocess.check_call(['pip', 'install'] + archives)
756
757=== added file 'hooks/start'
758--- hooks/start 1970-01-01 00:00:00 +0000
759+++ hooks/start 2015-08-21 21:51:07 +0000
760@@ -0,0 +1,15 @@
761+#!/usr/bin/env python
762+# Licensed under the Apache License, Version 2.0 (the "License");
763+# you may not use this file except in compliance with the License.
764+# You may obtain a copy of the License at
765+#
766+# http://www.apache.org/licenses/LICENSE-2.0
767+#
768+# Unless required by applicable law or agreed to in writing, software
769+# distributed under the License is distributed on an "AS IS" BASIS,
770+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
771+# See the License for the specific language governing permissions and
772+# limitations under the License.
773+
774+import common
775+common.manage()
776
777=== added file 'hooks/stop'
778--- hooks/stop 1970-01-01 00:00:00 +0000
779+++ hooks/stop 2015-08-21 21:51:07 +0000
780@@ -0,0 +1,15 @@
781+#!/usr/bin/env python
782+# Licensed under the Apache License, Version 2.0 (the "License");
783+# you may not use this file except in compliance with the License.
784+# You may obtain a copy of the License at
785+#
786+# http://www.apache.org/licenses/LICENSE-2.0
787+#
788+# Unless required by applicable law or agreed to in writing, software
789+# distributed under the License is distributed on an "AS IS" BASIS,
790+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
791+# See the License for the specific language governing permissions and
792+# limitations under the License.
793+
794+import common
795+common.manage()
796
797=== added file 'icon.svg'
798--- icon.svg 1970-01-01 00:00:00 +0000
799+++ icon.svg 2015-08-21 21:51:07 +0000
800@@ -0,0 +1,863 @@
801+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
802+<!-- Created with Inkscape (http://www.inkscape.org/) -->
803+
804+<svg
805+ xmlns:dc="http://purl.org/dc/elements/1.1/"
806+ xmlns:cc="http://creativecommons.org/ns#"
807+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
808+ xmlns:svg="http://www.w3.org/2000/svg"
809+ xmlns="http://www.w3.org/2000/svg"
810+ xmlns:xlink="http://www.w3.org/1999/xlink"
811+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
812+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
813+ width="96"
814+ height="96"
815+ id="svg6517"
816+ version="1.1"
817+ inkscape:version="0.48.4 r9939"
818+ sodipodi:docname="icon-template.svg">
819+ <defs
820+ id="defs6519">
821+ <linearGradient
822+ id="Background">
823+ <stop
824+ id="stop4178"
825+ offset="0"
826+ style="stop-color:#b8b8b8;stop-opacity:1" />
827+ <stop
828+ id="stop4180"
829+ offset="1"
830+ style="stop-color:#c9c9c9;stop-opacity:1" />
831+ </linearGradient>
832+ <filter
833+ style="color-interpolation-filters:sRGB;"
834+ inkscape:label="Inner Shadow"
835+ id="filter1121">
836+ <feFlood
837+ flood-opacity="0.59999999999999998"
838+ flood-color="rgb(0,0,0)"
839+ result="flood"
840+ id="feFlood1123" />
841+ <feComposite
842+ in="flood"
843+ in2="SourceGraphic"
844+ operator="out"
845+ result="composite1"
846+ id="feComposite1125" />
847+ <feGaussianBlur
848+ in="composite1"
849+ stdDeviation="1"
850+ result="blur"
851+ id="feGaussianBlur1127" />
852+ <feOffset
853+ dx="0"
854+ dy="2"
855+ result="offset"
856+ id="feOffset1129" />
857+ <feComposite
858+ in="offset"
859+ in2="SourceGraphic"
860+ operator="atop"
861+ result="composite2"
862+ id="feComposite1131" />
863+ </filter>
864+ <filter
865+ style="color-interpolation-filters:sRGB;"
866+ inkscape:label="Drop Shadow"
867+ id="filter950">
868+ <feFlood
869+ flood-opacity="0.25"
870+ flood-color="rgb(0,0,0)"
871+ result="flood"
872+ id="feFlood952" />
873+ <feComposite
874+ in="flood"
875+ in2="SourceGraphic"
876+ operator="in"
877+ result="composite1"
878+ id="feComposite954" />
879+ <feGaussianBlur
880+ in="composite1"
881+ stdDeviation="1"
882+ result="blur"
883+ id="feGaussianBlur956" />
884+ <feOffset
885+ dx="0"
886+ dy="1"
887+ result="offset"
888+ id="feOffset958" />
889+ <feComposite
890+ in="SourceGraphic"
891+ in2="offset"
892+ operator="over"
893+ result="composite2"
894+ id="feComposite960" />
895+ </filter>
896+ <clipPath
897+ clipPathUnits="userSpaceOnUse"
898+ id="clipPath873">
899+ <g
900+ transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)"
901+ id="g875"
902+ inkscape:label="Layer 1"
903+ style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline">
904+ <path
905+ style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"
906+ d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z"
907+ id="path877"
908+ inkscape:connector-curvature="0"
909+ sodipodi:nodetypes="sssssssss" />
910+ </g>
911+ </clipPath>
912+ <filter
913+ inkscape:collect="always"
914+ id="filter891"
915+ inkscape:label="Badge Shadow">
916+ <feGaussianBlur
917+ inkscape:collect="always"
918+ stdDeviation="0.71999962"
919+ id="feGaussianBlur893" />
920+ </filter>
921+ </defs>
922+ <sodipodi:namedview
923+ id="base"
924+ pagecolor="#ffffff"
925+ bordercolor="#666666"
926+ borderopacity="1.0"
927+ inkscape:pageopacity="0.0"
928+ inkscape:pageshadow="2"
929+ inkscape:zoom="3.4719894"
930+ inkscape:cx="18.514671"
931+ inkscape:cy="49.018169"
932+ inkscape:document-units="px"
933+ inkscape:current-layer="layer1"
934+ showgrid="false"
935+ fit-margin-top="0"
936+ fit-margin-left="0"
937+ fit-margin-right="0"
938+ fit-margin-bottom="0"
939+ inkscape:window-width="1366"
940+ inkscape:window-height="744"
941+ inkscape:window-x="0"
942+ inkscape:window-y="24"
943+ inkscape:window-maximized="1"
944+ showborder="true"
945+ showguides="true"
946+ inkscape:guide-bbox="true"
947+ inkscape:showpageshadow="false">
948+ <inkscape:grid
949+ type="xygrid"
950+ id="grid821" />
951+ <sodipodi:guide
952+ orientation="1,0"
953+ position="16,48"
954+ id="guide823" />
955+ <sodipodi:guide
956+ orientation="0,1"
957+ position="64,80"
958+ id="guide825" />
959+ <sodipodi:guide
960+ orientation="1,0"
961+ position="80,40"
962+ id="guide827" />
963+ <sodipodi:guide
964+ orientation="0,1"
965+ position="64,16"
966+ id="guide829" />
967+ </sodipodi:namedview>
968+ <metadata
969+ id="metadata6522">
970+ <rdf:RDF>
971+ <cc:Work
972+ rdf:about="">
973+ <dc:format>image/svg+xml</dc:format>
974+ <dc:type
975+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
976+ <dc:title></dc:title>
977+ </cc:Work>
978+ </rdf:RDF>
979+ </metadata>
980+ <g
981+ inkscape:label="BACKGROUND"
982+ inkscape:groupmode="layer"
983+ id="layer1"
984+ transform="translate(268,-635.29076)"
985+ style="display:inline">
986+ <path
987+ style="fill:#e6e6e6;fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)"
988+ d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z"
989+ id="path6455"
990+ inkscape:connector-curvature="0"
991+ sodipodi:nodetypes="sssssssss" />
992+ <image
993+ y="651.29077"
994+ x="-252"
995+ id="image3185"
996+ xlink:href="data:image/png;base64,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
1056+whezv3RtIEiTZfeqAWF8ajpvCNIop5IpkKqEflsowt7tklxGIAHLypjbd1/786U9r32fSHBVX6QH
1057+58HfELye8M+j2ScZxxNEvtnQrRUtg7lixtSlo7BD573WI+b9g410UVLToBePvE9znCS4zy0724r5
1058+7Gds29oaZg9BujHo+mlttUp1rdaXXijmMQwDy7IYKhfSdWVjvbSfAsAkAJw2huKy0hMBSe4bAzhS
1059+AYJLyVmrSPwn+0wlpVbAhxMqggrUgH07FIenBG1Hx87mstt27f+o57oLK8ce/UIYcKKkRKiEoz/J
1060+KSNnfswxZeJayFmTLqorQeZGIL20RZ/mwCmOcImY0TT3SBtiyXvPLztDOTvzh8V89obgdnJZScbe
1061+WNwvL67QaLQAcF0Xr7FGu14lpztkcDCJJyyk0lglA0DbFQjdIJMvUS4VMAsjjG2ZJGOZl2E8JUHa
1062+k94H0kGctPdaGqS+YRbfIwOQxvUJ3FYqbFevvhqCWjFUggM7FS+cFjgeFEqVysSWHR9z3deeXzn2
1063+6KFeV1MyrjROSxpQ4Rqu2OCK1YD4Jf1uaaAZooQEPdCRZMwFIBDzUqY7KtlhasCAhCKrF6Qy6f+D
1064+5CI7pcAw9F8qFXJvCfNnM5KMvXEMwvHDx/FWp9GdKq7wuYJQCk8qTNMgn036rxS6psjRASCXUbRd
1065+QXVljeYyoNsszy0yuW8/wyPl+K4NQJruiTif4nI4aXJ6NsE5VU/pIacdwEl7uXdslMUgDR9dLiuu
1066+3QHPnvLLHJ7Yuqder/57b89d71458UQjmtdPhu4luGrSBeVf89NSHoDQ6r9C7qk+1qSURGoxSCPW
1067+HnSmEiIGIyQ6LH7LowEJMkTuiughwZsXnao+kJ5fcq4tFnL/VNc1A0DXJHZuY5AePXwKVk9TooFt
1068+6rhK0RUSL3jRWp6g2nZ6ah1VCICsaTBRyJIxdJRok22dYeb5b/Dct55hdaV6UZD2hiOC34dJTpq8
1069+azBI4xJ9wMkIZInCEzljdeFyQOrrrH5Q+viopFyIQb9157432bnizwoZW/3JCCsVzukH+qofBxAv
1070+u5Yp3TReVSCukI6qua6b6ugNRT1cVkje5UTe//e/nnlv1xVf/rkf3D5LSo/y81xY6g7l8/bXS4Xc
1071+DeA79AsFNTDIueu4nD05hVqZIkcrda3ueDQcL2DofuF506CSCzlrP4iCEaUrBC1XULF9obMu83i5
1072+TWzft4fR0WFgMEhD4ITt1hJASnsD/F+owqlkWgDwlN4ecq3kk1XsBXCF/xJqmoEQDhKJhq/He8IJ
1073+6qJFYllDo9kwOXRCsVST6Arc9vrK9Mnnf2T2+S8/oqRv4fdx0TAoJfCfp+b1VZrbhucnTp7qG7eX
1074+Smayk3tBeimD6VLheH38J+CirpDPbx4pPIiSd0XPDfMohW4Y/6qQy10XAiif2zgS/9TRE9i1M1h0
1075++66VMibFjEnXE3RcQUcIWp4g5xnYZm8sgAq4v5+Q0TX0jEG146BpoFQH0V7m8JNnKYzsYOe+PYyN
1076+D6d0sF6dPQRbeC0J0lh3j9NDMRmDO3xvfM4qpIem63heF0/4DKbrtVH44+N4vhqja3rgz/TQNA0N
1077+DSED3VxpaJrmc0kDJjeZvDit0ewqPCFGzbH9PzFxR+Xx2Sf/zE0aSr1xpingKqLZqvQ9V3ApiuN0
1078+X3Jgcyzmk6I+PL2YLhreK/nCU+uHHdebedf3jt4XD4rk3KJzw3Cl+Hg2Y1VQYBiSofJgkJ48fgaW
1079+jzNku2imjvQknuvvBhJHaMU1We+4NB2PjKkzmsskKpgUoSTQ66c3HY+m4+EIgRCKvKWj2yO4uQl2
1080+X7uPkbEKSincThsrm0txxKgvU/2V9IeGol6m6wJ40kVJQcdtopTCkw6ecNE0H3ixihGCQUbATrYl
1081+ehUSYis5HqfO6jz8TJTqKMXbgK/O/M1vq5iL9lr1MYBDoCaDUJKc9uzZswPH76WQueEUKAxeHkKs
1082+T/oVUnHygHn9QSAFWG90vnhw19gH//SrCx/5sTdu+lDYwFw283t2xqoo5UdC5VILSXxqNjucPTUF
1083+tQsUVYtOC3RDx7R08kNZdF2jXe/idNzUfXnL8AHnSRwhyRh6uu1pHSDStXOmQdYIxaik43l0OytU
1084+VJWjT57DymaQ3RqddpMbXvNmRrbuSPVCGqSqD6Qx1/X1wo7bQCqF67URSiTEfiBuE3WOQRrroS8F
1085+pADX7JI8d0pnoQZKkQH+LfCEkLIdTZ+GxlGfCyoB4N6/6kpOofYYCKHBNAikySnQ8C2KOiTSn8Jy
1086+0gbTx//6wnv/6KHzt4f91eq6/wvg4K6xD/7p3y58BODckvPOXM5+daiAZ22B2bO2aXFhhdPPfpNC
1087+7SRF1YjrLSROx8PteKAUuWIGM5N0aihMXcPQfD1tveNS6zjUui5N18MV8YAnQeofS/8FUxJTg4Jp
1088+0HRcZmttRHeFVnWOTrsBUnLkm39LbWkuqnM/SFUCoj7QPOnScRo0OuusNRdoO006bgNPeoRO/KQJ
1089+FZaXBGkI/ORLtxFIe12CKPjemxVGJLjUHaB+IjKMop8P2KTfNF4aHQa3pPfDulLxqFq75RsgF4sZ
1090+DYEY0kWXhjCIi/od+dnHVx4GqLec//lT92154JnTIrrtyNnlj973qvE3DxXzty7XXDYNW5TL9NGh
1091+J59i1JvduEE6lEbyQT0UrfV2EHbmX290PWpdJ6qzCixuTYOxnE3GiLle5NOM/sZpy60uja4bQ076
1092+wJBSYucKXHPHfYxuGo96JgZ9CFSFEA4tp4GUrj9/Hkkkmcrvp4XH4Rr/NEiTXPZSIE2RitOOntZ5
1093+9Pno+mngjlOf//BaiquqHuNqEDdFxdOqUjI7N7fheF0u6aHbCZmYfou6Nxy0OFLq4iBVaZDKgBMF
1094+3fbO147eB3Dj3omPP3NaqPML69+sNjpLSsHBnWMfXG/rNwFYhk4uO1hk6G5jYHrEVaTCc3xpoOka
1095+diGT4sjFjMGWYpaRXIZixkDTYvG11GzT9YQf9xCqKUmABZxNKkXO1CNRnIzJVErRadV58fG/4clH
1096+vs7iwkoEOhkAyPHatLo11lvLuF4HT3hRLyat/36QKtI4i8uNRuFyQRq9g/EzDuwVFELVXam9KPWL
1097+yS1/pEyubE2nx7GsYYxAHLN6JUhrNpopgCY3fthoCjTmDFGjUue9xlYykEWh+JOH59+bzZh3/73X
1098+j71PScmffGXhrbfs2/xFANPUKeQ09mw1yBf8zUx0LXb3Pvd3X2VYT06FqsRfX3YZpk62kMGwDFCK
1099+xlob4Yng+XFdAKodh0bXjax3S/edy37Qt8QVkoJlULbNYLBlyIqZb3QBSdsVkVGhZKxLbi1lqTNE
1100+x55g18H9jE2U6HoNPCkQwiUGXgzi5EsR9HTUb2m9NAZuZDy9FJAm0mJ3F5w5r/M334nuOwa89uj/
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
1161+Oq5HreOglKLa6aJpFpamYWoaZuDvkDKxgYOM3VZuu8HazAnW5s6i6/C82sff+9H/8yAVbova9GPk
1162+Jm4kO7T9ZZUxXim9ZC46CKAhlYuSxHcQDkbjHHBRpWk+SEO/NcmgyytDZlLUE0UKhRX2A2Cj0KSA
1163+Q4Uuh8jtdMlVj4pPPrJ8+w17N/+bkXI2MzFsMzW7jutJLEOP1islpDATI3kqQxkcCVlLp153OH56
1164+FaUUQ3kLISTbxrNMzXaQKCyxTuwXVWR1Dds2MfCXjSilWG12/TxBeF5G19hashMg9dO9Tpu1C8ep
1165+zp0G4KSzhbvf8pYrBtLmwnN01k5R2fv9GFY+Sm+tnGD95P9kaPJ2WnPfftlALRayrK+2GBr2/cIv
1166+F6BhWikHEfSU2qkpBXoQNKSFn1QK45nVd/lxpMGkpwym5HHoJ40qErgctHATA188hmFeobiMuGi0
1167++4lfbjZjvjGTsTJCKGqt2FoOVYDEZiEoFKalYw/nMA0N1RVcOLsW1aHadKm2XByhKBX8WRpduST9
1168+pv5aeD86fzRrBXUOdStf+e+4HivNbgzSoCLLZ56jOj+FlJIWWTbf8kbedd91AzvQbS8j3NbAaxtR
1169+e+kZMlmd6qm/AqBTO8/cUx+mu/Q4w7u/FyOTR8lLlyncFvULT/alv/HufTz20LeorbUvYizFIB3k
1170+U03+bFuRuGHc557S10lDXEQmXHwE2sDnvxzSk34wv5KJiJewIgnREW5/+Cdfmb/94UPtw595ovqR
1171+foMpXiclAxAXcvabAMYrNqsJPTTkolpCfZAKml1/Db2hadTXu7heaJ2DaWiMlmzytkmr46GLNpao
1172+xi9LWG9JAFadrKlF6UmXylrHoeV6/tIJz2H57AtU588ig2CKaWeMH37zjQM7T7gtlp/7L6y88Pt0
1173+auc37OTea8pdwcqV0bUGS89/nNqpT1PecRe54d1RHq+5cXnhs1cOP4BonWLtVPLLeIqt43l+5b1v
1174+4NBXD/Hw556k3XSi8dvQxdhznjzWDSLwKbAhca5iK78Xkv3fK3j5pCdBmZptGuTEjUSz5CfeOPH0
1175+hcXam/dOjnzgscPO2U/+7fLt4Q4aUSMSUSu5rLUfwDL9PU2zltHHRZNxrlsqWUoZnULGwPN8P2to
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
1326+8mIu1DHv6k+hxRV29MaZmygwX7Y4UnYordQ1mnB5WichCuxti9HTGWXbYIoMAqfzFm4AGztjnS9W
1327+7Qdl11ucYHVxTvaAm7ZL3/etie/XZsKmBhl83rI5oG/rZUPlWqCenSjTktIomQ6tLZn+MyULd7H1
1328+6eKDWUrIwg1FIqIqYgSwfn2GwryBoMr4gOV49bKVAMvx6WzRGJ2zyZcsZsomriTg1ydxiOihlakF
1329+cOTYHD88MUPBdjmaXxrSdmSiVJ0AARECGDd8Lu2MUHI8trREOZEziCY28NRzh3j/bRsJAp9Idjvl
1330+8X141hiyGiGS7sYXBUBAPAdhBUHG911EcbnlK7B25pNjG4iyTrLvmmXLfddElJcTbP74t6iRatxL
1331+1a+gRdtCz0DDsgtWVoisgu/6mNUarivhWHnk6OqeAqdmTGaFfrpjKgnH57/vbienCIzYLiBi1A2/
1332+jCLQ686Ri29hoCdJhXCurPe2aFy+LoHleuGEb5Nlpg0bQ1O5OiMS1xQ6YjInyi4/ma81pDWEumlE
1333+k6BNw02qTJsuXT1RfkkV+d+HFhAlBoAD8spZ15b7PpumZfE89h85/Zvrsi2/E48lt6uqRKXqIAkC
1334+PTGFeTNsYe4GYLg+qiwiClB1vJC0YqizarKEq8tYVmFkaKwcTaQyWaPm1GdzhlLNQSmK9LbGqNZs
1335+ZEGgqzfD1HSZeL1xbkSXcWyPH56YWfPhlG2Pj75tB3/0r6Fr6fdv2UyAi3osx7W72rguqSO6Ad6r
1336+42GZje/joxHpvBrf9zBzI+TGDpPq2oikSPhwTrKuJClApiVLdc7GE5an/fnIGLOHUVO9yFoapzrN
1337+3Inv4qISq0tm26rheyZFwyMQwincfS9A8G3UaCuLXgff91+TpIEXYBo1apUaIODZq6eQr9kB//yj
1338+IUq33YtleVy/s40xBWquhy4KmPWEHsXzcU8O8VLJINESh4rNuONzZVuUakrlR69Mk3Q8FuartCU0
1339+XMdnXUeE0myJnOnQkmlB1yErC8w6oUrRFVfZNJhi1PEax8nbHgNRhfaUSloTSSZVgKeke++5p0HQ
1340+RZJ6q0gaxu1lt3hw8/qOkXLV2tPTns7m87mRdDqRdryAtCYTkQU0SaA7phCRIa1KuEHYnTmtSxRs
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
1583+"
1584+ height="63.999981"
1585+ width="64" />
1586+ </g>
1587+ <g
1588+ inkscape:groupmode="layer"
1589+ id="layer3"
1590+ inkscape:label="PLACE YOUR PICTOGRAM HERE"
1591+ style="display:inline" />
1592+ <g
1593+ inkscape:groupmode="layer"
1594+ id="layer2"
1595+ inkscape:label="BADGE"
1596+ style="display:none"
1597+ sodipodi:insensitive="true">
1598+ <g
1599+ style="display:inline"
1600+ transform="translate(-340.00001,-581)"
1601+ id="g4394"
1602+ clip-path="none">
1603+ <g
1604+ id="g855">
1605+ <g
1606+ inkscape:groupmode="maskhelper"
1607+ id="g870"
1608+ clip-path="url(#clipPath873)"
1609+ style="opacity:0.6;filter:url(#filter891)">
1610+ <path
1611+ transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)"
1612+ d="m 264,552.36218 c 0,6.62742 -5.37258,12 -12,12 -6.62742,0 -12,-5.37258 -12,-12 0,-6.62741 5.37258,-12 12,-12 6.62742,0 12,5.37259 12,12 z"
1613+ sodipodi:ry="12"
1614+ sodipodi:rx="12"
1615+ sodipodi:cy="552.36218"
1616+ sodipodi:cx="252"
1617+ id="path844"
1618+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1619+ sodipodi:type="arc" />
1620+ </g>
1621+ <g
1622+ id="g862">
1623+ <path
1624+ sodipodi:type="arc"
1625+ style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1626+ id="path4398"
1627+ sodipodi:cx="252"
1628+ sodipodi:cy="552.36218"
1629+ sodipodi:rx="12"
1630+ sodipodi:ry="12"
1631+ d="m 264,552.36218 c 0,6.62742 -5.37258,12 -12,12 -6.62742,0 -12,-5.37258 -12,-12 0,-6.62741 5.37258,-12 12,-12 6.62742,0 12,5.37259 12,12 z"
1632+ transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" />
1633+ <path
1634+ transform="matrix(1.25,0,0,1.25,33,-100.45273)"
1635+ d="m 264,552.36218 c 0,6.62742 -5.37258,12 -12,12 -6.62742,0 -12,-5.37258 -12,-12 0,-6.62741 5.37258,-12 12,-12 6.62742,0 12,5.37259 12,12 z"
1636+ sodipodi:ry="12"
1637+ sodipodi:rx="12"
1638+ sodipodi:cy="552.36218"
1639+ sodipodi:cx="252"
1640+ id="path4400"
1641+ style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1642+ sodipodi:type="arc" />
1643+ <path
1644+ sodipodi:type="star"
1645+ style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1646+ id="path4459"
1647+ sodipodi:sides="5"
1648+ sodipodi:cx="666.19574"
1649+ sodipodi:cy="589.50385"
1650+ sodipodi:r1="7.2431178"
1651+ sodipodi:r2="4.3458705"
1652+ sodipodi:arg1="1.0471976"
1653+ sodipodi:arg2="1.6755161"
1654+ inkscape:flatsided="false"
1655+ inkscape:rounded="0.1"
1656+ inkscape:randomized="0"
1657+ d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 -0.18379,0.41279 0.0427,4.27917 -0.34859,4.5051 z"
1658+ transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" />
1659+ </g>
1660+ </g>
1661+ </g>
1662+ </g>
1663+</svg>
1664
1665=== renamed file 'icon.svg' => 'icon.svg.moved'
1666=== added file 'metadata.yaml'
1667--- metadata.yaml 1970-01-01 00:00:00 +0000
1668+++ metadata.yaml 2015-08-21 21:51:07 +0000
1669@@ -0,0 +1,10 @@
1670+name: apache-flume-twitter
1671+summary: Ingest tweets with Apache Flume
1672+maintainer: Kevin Monroe <kevin.monroe@canonical.com>
1673+description: |
1674+ Uses a Twitter source, memory channel, and Avro sink in Apache Flume
1675+ to ingest Twitter data.
1676+tags: ["applications", "bigdata", "apache"]
1677+requires:
1678+ flume-agent:
1679+ interface: flume-agent
1680
1681=== renamed file 'metadata.yaml' => 'metadata.yaml.moved'
1682=== added directory 'resources'
1683=== renamed directory 'resources' => 'resources.moved'
1684=== added file 'resources.yaml'
1685--- resources.yaml 1970-01-01 00:00:00 +0000
1686+++ resources.yaml 2015-08-21 21:51:07 +0000
1687@@ -0,0 +1,12 @@
1688+options:
1689+ output_dir: /home/ubuntu/resources
1690+resources:
1691+ pathlib:
1692+ pypi: path.py>=7.0
1693+ jujubigdata:
1694+ pypi: jujubigdata>=4.0.0,<5.0.0
1695+optional_resources:
1696+ flume-x86_64:
1697+ url: https://git.launchpad.net/bigdata-data/plain/apache/x86_64/apache-flume-1.6.0-bin.tar.gz?id=c34a21c939f5fce9ab89b95d65fe2df50e7bbab0
1698+ hash: defd21ad8d2b6f28cc0a16b96f652099
1699+ hash_type: md5
1700
1701=== renamed file 'resources.yaml' => 'resources.yaml.moved'
1702=== added directory 'resources/python'
1703=== added file 'resources/python/PyYAML-3.11.tar.gz'
1704Binary files resources/python/PyYAML-3.11.tar.gz 1970-01-01 00:00:00 +0000 and resources/python/PyYAML-3.11.tar.gz 2015-08-21 21:51:07 +0000 differ
1705=== added file 'resources/python/charmhelpers-0.3.1.tar.gz'
1706Binary files resources/python/charmhelpers-0.3.1.tar.gz 1970-01-01 00:00:00 +0000 and resources/python/charmhelpers-0.3.1.tar.gz 2015-08-21 21:51:07 +0000 differ
1707=== added file 'resources/python/jujuresources-0.2.9.tar.gz'
1708Binary files resources/python/jujuresources-0.2.9.tar.gz 1970-01-01 00:00:00 +0000 and resources/python/jujuresources-0.2.9.tar.gz 2015-08-21 21:51:07 +0000 differ
1709=== added file 'resources/python/pyaml-15.5.7.tar.gz'
1710Binary files resources/python/pyaml-15.5.7.tar.gz 1970-01-01 00:00:00 +0000 and resources/python/pyaml-15.5.7.tar.gz 2015-08-21 21:51:07 +0000 differ
1711=== added file 'resources/python/six-1.9.0-py2.py3-none-any.whl'
1712Binary files resources/python/six-1.9.0-py2.py3-none-any.whl 1970-01-01 00:00:00 +0000 and resources/python/six-1.9.0-py2.py3-none-any.whl 2015-08-21 21:51:07 +0000 differ
1713=== added directory 'templates'
1714=== renamed directory 'templates' => 'templates.moved'
1715=== added file 'templates/flume.conf.j2'
1716--- templates/flume.conf.j2 1970-01-01 00:00:00 +0000
1717+++ templates/flume.conf.j2 2015-08-21 21:51:07 +0000
1718@@ -0,0 +1,31 @@
1719+# list sources, sinks, and channels in the agent
1720+a1.sources = r1
1721+a1.sinks = k1
1722+a1.channels = c1
1723+
1724+# source properties
1725+a1.sources.r1.type = {{ config['twitter_source'] }}
1726+a1.sources.r1.channels = c1
1727+a1.sources.r1.consumerKey = {{ config['twitter_consumer_key'] }}
1728+a1.sources.r1.consumerSecret = {{ config['twitter_consumer_secret'] }}
1729+a1.sources.r1.accessToken = {{ config['twitter_access_token'] }}
1730+a1.sources.r1.accessTokenSecret = {{ config['twitter_access_token_secret'] }}
1731+a1.sources.r1.maxBatchSize = {{ config['twitter_max_batch_size'] }}
1732+a1.sources.r1.maxBatchDurationMillis = {{ config['twitter_max_batch_duration'] }}
1733+
1734+# inject our configured subdir
1735+a1.sources.r1.interceptors = i1
1736+a1.sources.r1.interceptors.i1.type = static
1737+a1.sources.r1.interceptors.i1.key = event_dir
1738+a1.sources.r1.interceptors.i1.value = {{ config['event_dir'] }}
1739+
1740+# channel properties
1741+a1.channels.c1.type = memory
1742+a1.channels.c1.capacity = {{ config['channel_capacity']}}
1743+a1.channels.c1.transactionCapacity = {{ config['channel_transaction_capacity']}}
1744+
1745+# sink properties
1746+a1.sinks.k1.type = {{ any_ready_unit('flume-agent')[1]['protocol'] }}
1747+a1.sinks.k1.channel = c1
1748+a1.sinks.k1.hostname = {{ any_ready_unit('flume-agent')[1]['private-address'] }}
1749+a1.sinks.k1.port = {{ any_ready_unit('flume-agent')[1]['port'] }}
1750
1751=== added directory 'tests'
1752=== renamed directory 'tests' => 'tests.moved'
1753=== added file 'tests/00-setup'
1754--- tests/00-setup 1970-01-01 00:00:00 +0000
1755+++ tests/00-setup 2015-08-21 21:51:07 +0000
1756@@ -0,0 +1,5 @@
1757+#!/bin/bash
1758+
1759+sudo add-apt-repository ppa:juju/stable -y
1760+sudo apt-get update
1761+sudo apt-get install python3 amulet -y
1762
1763=== added directory 'tests/remote'
1764=== added file 'tests/remote/test_dist_config.py'
1765--- tests/remote/test_dist_config.py 1970-01-01 00:00:00 +0000
1766+++ tests/remote/test_dist_config.py 2015-08-21 21:51:07 +0000
1767@@ -0,0 +1,62 @@
1768+#!/usr/bin/env python
1769+
1770+import grp
1771+import pwd
1772+import unittest
1773+
1774+from charmhelpers.contrib import bigdata
1775+
1776+
1777+class TestDistConfig(unittest.TestCase):
1778+ """
1779+ Test that the ``dist.yaml`` settings were applied properly, such as users, groups, and dirs.
1780+
1781+ This is done as a remote test on the deployed unit rather than a regular
1782+ test under ``tests/`` because filling in the ``dist.yaml`` requires Juju
1783+ context (e.g., config).
1784+ """
1785+ @classmethod
1786+ def setUpClass(cls):
1787+ cls.hadoop = bigdata.handlers.apache.HadoopBase()
1788+
1789+ def test_groups(self):
1790+ for name in self.hadoop.groups:
1791+ try:
1792+ grp.getgrnam(name)
1793+ except KeyError:
1794+ self.fail('Group {} is missing'.format(name))
1795+
1796+ def test_users(self):
1797+ for username, details in self.hadoop.users.items():
1798+ try:
1799+ user = pwd.getpwnam(username)
1800+ except KeyError:
1801+ self.fail('User {} is missing'.format(username))
1802+ for groupname in details['groups']:
1803+ try:
1804+ group = grp.getgrnam(groupname)
1805+ except KeyError:
1806+ self.fail('Group {} referenced by user {} does not exist'.format(
1807+ groupname, username))
1808+ if group.gr_gid != user.pw_gid:
1809+ self.assertIn(username, group.gr_mem, 'User {} not in group {}'.format(
1810+ username, groupname))
1811+
1812+ def test_dirs(self):
1813+ for name, details in self.hadoop.managed_dirs.items():
1814+ dirpath = details['path']
1815+ self.assertTrue(dirpath.isdir(), 'Dir {} is missing'.format(name))
1816+ stat = dirpath.stat()
1817+ owner = pwd.getpwuid(stat.st_uid).pw_name
1818+ group = grp.getgrgid(stat.st_gid).gr_name
1819+ perms = stat.st_mode & ~0o40000
1820+ self.assertEqual(owner, details.get('owner', 'root'),
1821+ 'Dir {} ({}) has wrong owner: {}'.format(name, dirpath, owner))
1822+ self.assertEqual(group, details.get('group', 'root'),
1823+ 'Dir {} ({}) has wrong group: {}'.format(name, dirpath, group))
1824+ self.assertEqual(perms, details.get('perms', 0o744),
1825+ 'Dir {} ({}) has wrong perms: 0o{:o}'.format(name, dirpath, perms))
1826+
1827+
1828+if __name__ == '__main__':
1829+ unittest.main()

Subscribers

People subscribed via source and target branches