bigdata-dev team mailing list archive
-
bigdata-dev team
-
Mailing list archive
-
Message #00220
[Merge] lp:~bigdata-dev/charms/trusty/apache-spark-notebook/trunk into lp:charms/trusty/apache-spark-notebook
Kevin W Monroe has proposed merging lp:~bigdata-dev/charms/trusty/apache-spark-notebook/trunk into lp:charms/trusty/apache-spark-notebook.
Requested reviews:
Kevin W Monroe (kwmonroe)
For more details, see:
https://code.launchpad.net/~bigdata-dev/charms/trusty/apache-spark-notebook/trunk/+merge/268674
--
Your team Juju Big Data Development is subscribed to branch lp:~bigdata-dev/charms/trusty/apache-spark-notebook/trunk.
=== added file 'LICENSE'
--- LICENSE 1970-01-01 00:00:00 +0000
+++ LICENSE 2015-08-20 23:14:47 +0000
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
=== renamed file 'LICENSE' => 'LICENSE.moved'
=== added file 'README.md'
--- README.md 1970-01-01 00:00:00 +0000
+++ README.md 2015-08-20 23:14:47 +0000
@@ -0,0 +1,72 @@
+## Overview
+
+IPython Notebook is a web-based notebook that enables interactive data
+analytics for Spark. The developers of Apache Spark have given thoughtful
+consideration to Python as a language of choice for data analysis. They have
+developed the PySpark API for working with RDDs in Python, and further support
+using the powerful IPythonshell instead of the builtin Python REPL.
+
+The developers of IPython have invested considerable effort in building the
+IPython Notebook, a system inspired by Mathematica that allows you to create
+"executable documents."" IPython Notebooks can integrate formatted text
+(Markdown), executable code (Python), mathematical formulae (LaTeX), and
+graphics/visualizations (matplotlib) into a single document that captures the
+flow of an exploration and can be exported as a formatted report or an
+executable script.
+
+
+## Usage
+
+This is a subordinate charm that requires the `apache-spark` interface. This
+means that you will need to deploy a base Apache Spark cluster to use
+IPython Notebook. An easy way to deploy the recommended environment is to use
+the [apache-hadoop-spark-notebook](https://jujucharms.com/apache-hadoop-spark-notebook)
+bundle. This will deploy the Apache Hadoop platform with an Apache Spark +
+IPython Notebook unit that communicates with the cluster by relating to the
+`apache-hadoop-plugin` subordinate charm:
+
+ juju-quickstart apache-hadoop-spark-notebook
+
+Alternatively, you may manually deploy the recommended environment as follows:
+
+ juju deploy apache-hadoop-hdfs-master hdfs-master
+ juju deploy apache-hadoop-yarn-master yarn-master
+ juju deploy apache-hadoop-compute-slave compute-slave
+ juju deploy apache-hadoop-plugin plugin
+ juju deploy apache-spark spark
+ juju deploy apache-spark-notebook notebook
+
+ juju add-relation yarn-master hdfs-master
+ juju add-relation compute-slave yarn-master
+ juju add-relation compute-slave hdfs-master
+ juju add-relation plugin yarn-master
+ juju add-relation plugin hdfs-master
+ juju add-relation spark plugin
+ juju add-relation notebook spark
+
+Once deployment is complete, expose the notebook service:
+
+ juju expose notebook
+
+You may now access the web interface at
+http://{spark_unit_ip_address}:8880. The ip address can be found by running
+`juju status spark | grep public-address`.
+
+
+## Testing the deployment
+
+From the IPython Notebook web interface, click on the "New Notebook" button.
+In the notebook cell type "sc." followed by the "Tab" key. The Spark API
+completion menu should appear. This verifies the notebook can communicate
+with the Spark unit.
+
+
+## Contact Information
+
+- <bigdata-dev@xxxxxxxxxxxxxxxxxxx>
+
+
+## Help
+
+- [Juju mailing list](https://lists.ubuntu.com/mailman/listinfo/juju)
+- [Juju community](https://jujucharms.com/community)
=== renamed file 'README.md' => 'README.md.moved'
=== added file 'copyright'
--- copyright 1970-01-01 00:00:00 +0000
+++ copyright 2015-08-20 23:14:47 +0000
@@ -0,0 +1,16 @@
+Format: http://dep.debian.net/deps/dep5/
+
+Files: *
+Copyright: Copyright 2015, Canonical Ltd., All Rights Reserved.
+License: Apache License 2.0
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ .
+ http://www.apache.org/licenses/LICENSE-2.0
+ .
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
=== renamed file 'copyright' => 'copyright.moved'
=== added file 'dist.yaml'
--- dist.yaml 1970-01-01 00:00:00 +0000
+++ dist.yaml 2015-08-20 23:14:47 +0000
@@ -0,0 +1,17 @@
+# This file contains values that are likely to change per distribution.
+# The aim is to make it easier to update / extend the charms with
+# minimal changes to the shared code in charmhelpers.
+vendor: 'apache'
+hadoop_version: '2.4.1'
+packages:
+ - 'ipython'
+ - 'ipython-notebook'
+dirs:
+ notebooks:
+ path: '/var/lib/spark-notebook/notebook'
+ owner: 'ubuntu'
+ group: 'hadoop'
+ports:
+ notebook:
+ port: 8880
+ exposed_on: 'notebook'
=== renamed file 'dist.yaml' => 'dist.yaml.moved'
=== added directory 'hooks'
=== renamed directory 'hooks' => 'hooks.moved'
=== added file 'hooks/callbacks.py'
--- hooks/callbacks.py 1970-01-01 00:00:00 +0000
+++ hooks/callbacks.py 2015-08-20 23:14:47 +0000
@@ -0,0 +1,124 @@
+import os
+from path import Path
+from subprocess import check_output
+
+from charmhelpers.core import hookenv
+from charmhelpers.core import unitdata
+from jujubigdata import utils
+from jujubigdata.relations import Spark
+
+
+# Extended status support
+# We call update_blocked_status from the "requires" section of our service
+# block, so be sure to return True. Otherwise, we'll block the "requires"
+# and never move on to callbacks. The other status update methods are called
+# from the "callbacks" section and therefore don't need to return True.
+def update_blocked_status():
+ if unitdata.kv().get('charm.active', False):
+ return True
+ spark = Spark()
+ if not spark.is_ready():
+ hookenv.status_set('waiting', 'Waiting for Spark to become ready')
+ return True
+
+
+def update_working_status():
+ if unitdata.kv().get('charm.active', False):
+ hookenv.status_set('maintenance', 'Updating configuration')
+ return
+ hookenv.status_set('maintenance', 'Setting up Spark Notebook')
+
+
+def update_active_status():
+ unitdata.kv().set('charm.active', True)
+ hookenv.status_set('active', 'Ready')
+
+
+# Main Notebook class for callbacks
+class Notebook(object):
+ def __init__(self, dist_config):
+ self.dist_config = dist_config
+
+ def is_installed(self):
+ return unitdata.kv().get('notebook.installed')
+
+ def install(self, force=False):
+ if not force and self.is_installed():
+ return
+ self.dist_config.add_dirs()
+ self.dist_config.add_packages()
+
+ # Copy our start/stop scripts (preserving attrs) to $HOME
+ start_source = 'scripts/start_notebook.sh'
+ Path(start_source).chmod(0o755)
+ Path(start_source).chown('ubuntu', 'hadoop')
+
+ stop_source = 'scripts/stop_notebook.sh'
+ Path(stop_source).chmod(0o755)
+ Path(stop_source).chown('ubuntu', 'hadoop')
+
+ target = os.environ.get('HOME', '/home/ubuntu')
+ Path(start_source).copy2(target)
+ Path(stop_source).copy2(target)
+
+ # Create an IPython profile
+ utils.run_as("ubuntu", 'ipython', 'profile', 'create', 'pyspark')
+
+ unitdata.kv().set('notebook.installed', True)
+
+ def configure_notebook(self):
+ # profile config created during install
+ ipython_profile = "ipython_notebook_config.py"
+ # find path to ipython_notebook_config.py
+ pPath = "/home/ubuntu/.ipython/profile_pyspark"
+ cmd = "find {} -name {}".format(pPath, ipython_profile)
+ ipython_profile_path = check_output(cmd.split()).strip()
+
+ # update profile with standard opts and configured port
+ utils.re_edit_in_place(ipython_profile_path, {
+ r'.*c.NotebookApp.ip *=.*': 'c.NotebookApp.ip = "*"',
+ r'.*c.NotebookApp.open_browser *=.*': 'c.NotebookApp.open_browser = False',
+ r'.*c.NotebookApp.port *=.*': 'c.NotebookApp.port = {}'.format(self.dist_config.port('notebook')),
+ r'.*c.NotebookManager.notebook_dir *=.*': "c.NotebookManager.notebook_dir = u'{}'".format(self.dist_config.path('notebooks')),
+ })
+
+ spark_home = os.environ.get("SPARK_HOME", '/usr/lib/spark')
+ py4j = "py4j-0.*.zip"
+ cmd = "find {} -name {}".format(spark_home, py4j)
+ # TODO: handle missing py4j
+ py4j_path = check_output(cmd.split()).strip()
+
+ setup_source = 'scripts/00-pyspark-setup.py'
+ Path(setup_source).chmod(0o755)
+ Path(setup_source).chown('ubuntu', 'hadoop')
+ utils.re_edit_in_place(setup_source, {
+ r'py4j *=.*': 'py4j="{}"'.format(py4j_path),
+ })
+ setup_target = '{}/.ipython/profile_pyspark/startup/00-pyspark-setup.py'.format(os.environ.get('HOME', '/home/ubuntu'))
+ Path(setup_source).copy2(setup_target)
+
+ # Our spark charm defaults to yarn-client, so that should
+ # be a safe default here in case MASTER isn't set. Update the env
+ # with our spark mode and py4j location.
+ spark_mode = os.environ.get("MASTER", "yarn-client")
+ with utils.environment_edit_in_place('/etc/environment') as env:
+ env['PYSPARK_DRIVER_PYTHON_OPTS'] = "notebook"
+ env['PYSPARK_SUBMIT_ARGS'] = "--master " + spark_mode
+ env['PYTHONPATH'] = "{}:{}/python".format(py4j_path, os.environ.get("SPARK_HOME", "/usr/lib/spark"))
+
+ def start(self):
+ self.stop()
+ script_path = os.environ.get('HOME', '/home/ubuntu')+'/start_notebook.sh'
+ # TODO: check for executable script; error without it
+ utils.run_as("ubuntu", script_path)
+
+ def stop(self):
+ script_path = os.environ.get('HOME', '/home/ubuntu')+'/stop_notebook.sh'
+ # TODO: check for executable script; error without it
+ utils.run_as("ubuntu", script_path)
+
+ def cleanup(self):
+ ipython_profile = Path("/home/ubuntu/.ipython")
+ ipython_profile.rmtree()
+ with utils.environment_edit_in_place('/etc/environment') as env:
+ env['PYSPARK_DRIVER_PYTHON_OPTS'] = ""
=== added file 'hooks/common.py'
--- hooks/common.py 1970-01-01 00:00:00 +0000
+++ hooks/common.py 2015-08-20 23:14:47 +0000
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Common implementation for all hooks.
+"""
+
+import jujuresources
+from charmhelpers.core import hookenv
+from charmhelpers.core import unitdata
+from charmhelpers.core import charmframework
+
+
+def bootstrap_resources():
+ """
+ Install required resources defined in resources.yaml
+ """
+ if unitdata.kv().get('charm.bootstrapped', False):
+ return True
+ hookenv.status_set('maintenance', 'Installing base resources')
+ mirror_url = jujuresources.config_get('resources_mirror')
+ if not jujuresources.fetch(mirror_url=mirror_url):
+ missing = jujuresources.invalid()
+ hookenv.status_set('blocked', 'Unable to fetch required resource%s: %s' % (
+ 's' if len(missing) > 1 else '',
+ ', '.join(missing),
+ ))
+ return False
+ jujuresources.install(['pathlib', 'jujubigdata'])
+ unitdata.kv().set('charm.bootstrapped', True)
+ return True
+
+
+def manage():
+ if not bootstrap_resources():
+ # defer until resources are available, since charmhelpers, and thus
+ # the framework, are required (will require manual intervention)
+ return
+
+ import jujubigdata
+ import callbacks
+
+ notebook_reqs = ['packages', 'dirs', 'ports']
+ dist_config = jujubigdata.utils.DistConfig(filename='dist.yaml',
+ required_keys=notebook_reqs)
+ notebook = callbacks.Notebook(dist_config)
+ manager = charmframework.Manager([
+ {
+ 'name': 'notebook',
+ 'provides': [],
+ 'requires': [
+ jujubigdata.relations.Spark(),
+ callbacks.update_blocked_status, # not really a requirement, but best way to fit into framework
+ ],
+ 'callbacks': [
+ callbacks.update_working_status,
+ charmframework.helpers.open_ports(dist_config.exposed_ports('notebook')),
+ notebook.install,
+ notebook.configure_notebook,
+ notebook.start,
+ callbacks.update_active_status,
+ ],
+ 'cleanup': [
+ charmframework.helpers.close_ports(dist_config.exposed_ports('notebook')),
+ notebook.stop,
+ notebook.cleanup,
+ ],
+ },
+ ])
+ manager.manage()
+
+
+if __name__ == '__main__':
+ manage()
=== added file 'hooks/config-changed'
--- hooks/config-changed 1970-01-01 00:00:00 +0000
+++ hooks/config-changed 2015-08-20 23:14:47 +0000
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import common
+common.manage()
=== added file 'hooks/install'
--- hooks/install 1970-01-01 00:00:00 +0000
+++ hooks/install 2015-08-20 23:14:47 +0000
@@ -0,0 +1,17 @@
+#!/usr/bin/python
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import setup
+setup.pre_install()
+
+import common
+common.manage()
=== added file 'hooks/setup.py'
--- hooks/setup.py 1970-01-01 00:00:00 +0000
+++ hooks/setup.py 2015-08-20 23:14:47 +0000
@@ -0,0 +1,33 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import subprocess
+from glob import glob
+
+
+def pre_install():
+ """
+ Do any setup required before the install hook.
+ """
+ install_pip()
+ install_bundled_resources()
+
+
+def install_pip():
+ subprocess.check_call(['apt-get', 'install', '-yq', 'python-pip', 'bzr'])
+
+
+def install_bundled_resources():
+ """
+ Install the bundled resources libraries.
+ """
+ archives = glob('resources/python/*')
+ subprocess.check_call(['pip', 'install'] + archives)
=== added file 'hooks/spark-relation-changed'
--- hooks/spark-relation-changed 1970-01-01 00:00:00 +0000
+++ hooks/spark-relation-changed 2015-08-20 23:14:47 +0000
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import common
+common.manage()
=== added file 'hooks/start'
--- hooks/start 1970-01-01 00:00:00 +0000
+++ hooks/start 2015-08-20 23:14:47 +0000
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import common
+common.manage()
=== added file 'hooks/stop'
--- hooks/stop 1970-01-01 00:00:00 +0000
+++ hooks/stop 2015-08-20 23:14:47 +0000
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import common
+common.manage()
=== added file 'icon.svg'
--- icon.svg 1970-01-01 00:00:00 +0000
+++ icon.svg 2015-08-20 23:14:47 +0000
@@ -0,0 +1,662 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="96"
+ height="96"
+ id="svg6517"
+ version="1.1"
+ inkscape:version="0.48.5 r10040"
+ sodipodi:docname="icon.svg">
+ <defs
+ id="defs6519">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#Background"
+ id="linearGradient6461"
+ gradientUnits="userSpaceOnUse"
+ x1="0"
+ y1="970.29498"
+ x2="144"
+ y2="970.29498"
+ gradientTransform="matrix(0,-0.66666669,0.6660448,0,-866.25992,731.29077)" />
+ <linearGradient
+ id="Background">
+ <stop
+ id="stop4178"
+ offset="0"
+ style="stop-color:#b8b8b8;stop-opacity:1" />
+ <stop
+ id="stop4180"
+ offset="1"
+ style="stop-color:#c9c9c9;stop-opacity:1" />
+ </linearGradient>
+ <filter
+ style="color-interpolation-filters:sRGB;"
+ inkscape:label="Inner Shadow"
+ id="filter1121">
+ <feFlood
+ flood-opacity="0.59999999999999998"
+ flood-color="rgb(0,0,0)"
+ result="flood"
+ id="feFlood1123" />
+ <feComposite
+ in="flood"
+ in2="SourceGraphic"
+ operator="out"
+ result="composite1"
+ id="feComposite1125" />
+ <feGaussianBlur
+ in="composite1"
+ stdDeviation="1"
+ result="blur"
+ id="feGaussianBlur1127" />
+ <feOffset
+ dx="0"
+ dy="2"
+ result="offset"
+ id="feOffset1129" />
+ <feComposite
+ in="offset"
+ in2="SourceGraphic"
+ operator="atop"
+ result="composite2"
+ id="feComposite1131" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB;"
+ inkscape:label="Drop Shadow"
+ id="filter950">
+ <feFlood
+ flood-opacity="0.25"
+ flood-color="rgb(0,0,0)"
+ result="flood"
+ id="feFlood952" />
+ <feComposite
+ in="flood"
+ in2="SourceGraphic"
+ operator="in"
+ result="composite1"
+ id="feComposite954" />
+ <feGaussianBlur
+ in="composite1"
+ stdDeviation="1"
+ result="blur"
+ id="feGaussianBlur956" />
+ <feOffset
+ dx="0"
+ dy="1"
+ result="offset"
+ id="feOffset958" />
+ <feComposite
+ in="SourceGraphic"
+ in2="offset"
+ operator="over"
+ result="composite2"
+ id="feComposite960" />
+ </filter>
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath873">
+ <g
+ transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)"
+ id="g875"
+ inkscape:label="Layer 1"
+ style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline">
+ <path
+ style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"
+ 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"
+ id="path877"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="sssssssss" />
+ </g>
+ </clipPath>
+ <filter
+ inkscape:collect="always"
+ id="filter891"
+ inkscape:label="Badge Shadow">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.71999962"
+ id="feGaussianBlur893" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3871"
+ color-interpolation-filters="sRGB">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="2.9338986"
+ id="feGaussianBlur3873" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3913"
+ color-interpolation-filters="sRGB">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.75310748"
+ id="feGaussianBlur3915" />
+ </filter>
+ <linearGradient
+ id="linearGradient3919">
+ <stop
+ style="stop-color:#e4e4e4;stop-opacity:1;"
+ offset="0"
+ id="stop3921" />
+ <stop
+ id="stop3927"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3919"
+ id="linearGradient3579"
+ gradientUnits="userSpaceOnUse"
+ x1="209.00497"
+ y1="-22.631527"
+ x2="200.9668"
+ y2="-2.0393581" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4689-6"
+ id="linearGradient3266"
+ gradientUnits="userSpaceOnUse"
+ x1="323.06018"
+ y1="147.10051"
+ x2="147.68851"
+ y2="293.00339" />
+ <linearGradient
+ id="linearGradient4689-6">
+ <stop
+ style="stop-color:#5a9fd4;stop-opacity:1"
+ offset="0"
+ id="stop4691-3" />
+ <stop
+ style="stop-color:#306998;stop-opacity:1"
+ offset="1"
+ id="stop4693-8" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4689-6"
+ id="linearGradient3256"
+ gradientUnits="userSpaceOnUse"
+ x1="486.50031"
+ y1="184.54053"
+ x2="496.16876"
+ y2="248.36336" />
+ <linearGradient
+ id="linearGradient3596">
+ <stop
+ style="stop-color:#5a9fd4;stop-opacity:1"
+ offset="0"
+ id="stop3598" />
+ <stop
+ style="stop-color:#306998;stop-opacity:1"
+ offset="1"
+ id="stop3600" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4689-6"
+ id="linearGradient3254"
+ gradientUnits="userSpaceOnUse"
+ x1="486.50031"
+ y1="184.54053"
+ x2="496.16876"
+ y2="248.36336" />
+ <linearGradient
+ id="linearGradient3603">
+ <stop
+ style="stop-color:#5a9fd4;stop-opacity:1"
+ offset="0"
+ id="stop3605" />
+ <stop
+ style="stop-color:#306998;stop-opacity:1"
+ offset="1"
+ id="stop3607" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4689-6"
+ id="linearGradient3260"
+ gradientUnits="userSpaceOnUse"
+ x1="485.7803"
+ y1="185.98055"
+ x2="496.88876"
+ y2="249.08336" />
+ <linearGradient
+ id="linearGradient3610">
+ <stop
+ style="stop-color:#5a9fd4;stop-opacity:1"
+ offset="0"
+ id="stop3612" />
+ <stop
+ style="stop-color:#306998;stop-opacity:1"
+ offset="1"
+ id="stop3614" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4689-6"
+ id="linearGradient3258"
+ gradientUnits="userSpaceOnUse"
+ x1="485.7803"
+ y1="185.98055"
+ x2="496.88876"
+ y2="249.08336" />
+ <linearGradient
+ id="linearGradient3617">
+ <stop
+ style="stop-color:#5a9fd4;stop-opacity:1"
+ offset="0"
+ id="stop3619" />
+ <stop
+ style="stop-color:#306998;stop-opacity:1"
+ offset="1"
+ id="stop3621" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4689-6"
+ id="linearGradient3264"
+ gradientUnits="userSpaceOnUse"
+ x1="484.3403"
+ y1="182.38054"
+ x2="495.44876"
+ y2="243.32335" />
+ <linearGradient
+ id="linearGradient3624">
+ <stop
+ style="stop-color:#5a9fd4;stop-opacity:1"
+ offset="0"
+ id="stop3626" />
+ <stop
+ style="stop-color:#306998;stop-opacity:1"
+ offset="1"
+ id="stop3628" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4689-6"
+ id="linearGradient3262"
+ gradientUnits="userSpaceOnUse"
+ x1="484.3403"
+ y1="182.38054"
+ x2="495.44876"
+ y2="243.32335" />
+ <linearGradient
+ id="linearGradient3631">
+ <stop
+ style="stop-color:#5a9fd4;stop-opacity:1"
+ offset="0"
+ id="stop3633" />
+ <stop
+ style="stop-color:#306998;stop-opacity:1"
+ offset="1"
+ id="stop3635" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3919-6">
+ <stop
+ style="stop-color:#e4e4e4;stop-opacity:1;"
+ offset="0"
+ id="stop3921-3" />
+ <stop
+ id="stop3927-0"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1;" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.0745362"
+ inkscape:cx="18.514671"
+ inkscape:cy="78.469374"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1056"
+ inkscape:window-x="2160"
+ inkscape:window-y="340"
+ inkscape:window-maximized="1"
+ showborder="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:showpageshadow="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid821" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="16,48"
+ id="guide823" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="64,80"
+ id="guide825" />
+ <sodipodi:guide
+ orientation="1,0"
+ position="80,40"
+ id="guide827" />
+ <sodipodi:guide
+ orientation="0,1"
+ position="64,16"
+ id="guide829" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata6522">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="BACKGROUND"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(268,-635.29076)"
+ style="display:inline">
+ <path
+ style="fill:url(#linearGradient6461);fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)"
+ 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"
+ id="path6455"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="sssssssss" />
+ <g
+ id="g3712"
+ transform="matrix(0.17744693,0,0,0.13619629,-267.2095,648.28218)">
+ <path
+ transform="matrix(2.2092365,0,0,2.2092365,35.81527,-594.94434)"
+ sodipodi:nodetypes="cccccc"
+ inkscape:connector-curvature="0"
+ id="path3077"
+ d="m 20,279.27442 88.35063,0 c 33.9881,0 77.49135,12.81451 77.49135,29.1526 L 185.84198,492 20,492 z"
+ style="fill:#666666;fill-opacity:0.99033813;stroke:#7d7d7d;stroke-width:1.08476496;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3871)"
+ inkscape:export-filename="/Users/matthiasbussonnier/Desktop/ipython-python.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <g
+ id="g3706">
+ <path
+ sodipodi:nodetypes="cccccc"
+ inkscape:connector-curvature="0"
+ id="path4338"
+ d="m 79.999519,21.999517 194.983301,0 c 61.89823,0 171.01766,39.636924 171.01766,64.410449 l 0,405.590514 -366.000961,0 z"
+ style="fill:#ffffff;fill-opacity:0.99033813;stroke:#7d7d7d;stroke-width:1.99903858;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ inkscape:export-filename="/Users/matthiasbussonnier/Desktop/ipython-python.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <g
+ id="g3936"
+ transform="matrix(2.2092365,0,0,2.2092365,-68.579768,91.189025)">
+ <path
+ style="fill:#ffffff;stroke:#7d7d7d;stroke-width:1.08476496;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3913)"
+ d="m 155.51352,-30.823863 c 14.89296,0 39.4548,5.804798 45.99753,30.22261295 8.5133,-4.59641195 30.96516,-11.88953595 31.36664,-1.19791745"
+ id="path3875"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc" />
+ <path
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path4384"
+ d="m 155.51351,-31.318075 c 14.89296,0 39.4548,5.804798 45.99753,30.2226129 8.5133,-4.596412 30.96516,-11.8895359 31.36664,-1.1979175 0,-18.1489184 -57.91307,-29.0246954 -77.36417,-29.0246954 z"
+ style="fill:url(#linearGradient3579);fill-opacity:1;stroke:#7d7d7d;stroke-width:1.08476496;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ </g>
+ </g>
+ <g
+ transform="matrix(0.15007571,0,0,0.18552765,-259.29043,641.50634)"
+ id="g4227">
+ <g
+ style="fill:#000000"
+ id="g3992"
+ transform="matrix(0.76400311,0,0,0.76400311,36.179593,5.3479839)">
+ <g
+ style="fill:#000000;fill-opacity:1"
+ id="g3994">
+ <g
+ id="text3996"
+ style="font-size:147.90756226px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono">
+ <path
+ id="path4049"
+ style="font-size:147.90756226px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 355.96093,272.06912 -38.13242,0 0,-128.9858 38.13242,0 0,10.76086 -24.98829,0 0,107.39186 24.98829,0 0,10.83308"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="text3998"
+ style="font-size:147.90756226px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono">
+ <path
+ id="path4052"
+ style="font-size:147.90756226px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 439.12013,261.23604 24.98829,0 0,-107.39186 -24.98829,0 0,-10.76086 38.13242,0 0,128.9858 -38.13242,0 0,-10.83308"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="text4000"
+ style="font-size:147.90756226px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono">
+ <path
+ id="path4055"
+ style="font-size:147.90756226px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 514.87478,165.86889 c 6.11462,8e-5 9.17195,3.3463 9.17201,10.03865 -6e-5,6.6925 -3.05739,10.03871 -9.17201,10.03865 -6.1147,6e-5 -9.17203,-3.34615 -9.172,-10.03865 -3e-5,-6.69235 3.0573,-10.03857 9.172,-10.03865 m 0,63.26515 c 6.11462,2e-5 9.17195,3.34623 9.17201,10.03865 -6e-5,6.74058 -3.05739,10.11087 -9.17201,10.11087 -6.1147,0 -9.17203,-3.37029 -9.172,-10.11087 -3e-5,-6.69242 3.0573,-10.03863 9.172,-10.03865"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <g
+ style="fill:#000000"
+ id="g4002">
+ <g
+ id="text4004"
+ style="font-size:204.03166199px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono">
+ <path
+ id="path4058"
+ style="font-size:204.03166199px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 151.39749,272.06912 -77.308871,0 0,-12.25385 29.389331,-1.9925 0,-117.1588 -29.389331,-1.9925 0,-12.25386 77.308871,0 0,12.25386 -29.2897,1.9925 0,117.1588 29.2897,1.9925 0,12.25385"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path4060"
+ style="font-size:204.03166199px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 210.07652,215.38259 0,56.68653 -18.53022,0 0,-145.65151 40.24843,0 c 34.13802,1.5e-4 51.20706,14.21328 51.20716,42.63943 -10e-5,14.54532 -4.61605,25.90254 -13.84785,34.0717 -9.16557,8.16929 -22.51528,12.25391 -40.04918,12.25385 l -19.02834,0 m 0,-15.74072 16.93622,0 c 13.15041,7e-5 22.54834,-2.39092 28.19383,-7.17299 5.71173,-4.78191 8.56764,-12.25376 8.56773,-22.41559 -9e-5,-18.5301 -11.22448,-27.7952 -33.67319,-27.79533 l -20.02459,0 0,57.38391"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="text4006"
+ style="font-size:131.4621582px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono">
+ <path
+ id="path4063"
+ style="font-size:131.4621582px;font-weight:normal;fill:#000000;fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 367.00874,170.0062 12.06781,0 16.81792,41.9806 c 3.50904,8.77272 5.41336,14.97779 5.71295,18.61524 l 0.38515,0 c 0.98421,-4.79287 2.90992,-11.04074 5.77714,-18.74363 l 15.34153,-41.85221 12.13201,0 -30.49049,79.66042 c -2.86722,7.44609 -6.20512,13.03065 -10.01372,16.75373 -3.80867,3.76581 -9.07228,5.64873 -15.79087,5.64876 -3.68027,-3e-5 -7.27493,-0.36378 -10.784,-1.09124 l 0,-9.30762 c 2.6532,0.5135 5.56316,0.77026 8.72991,0.77028 4.10817,-2e-5 7.2963,-0.87729 9.56438,-2.63181 2.31083,-1.75455 4.36493,-4.77151 6.16229,-9.05086 l 3.72305,-9.62857 -29.33506,-71.12309"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ </g>
+ <g
+ transform="matrix(0.76400311,0,0,0.76400311,34.485122,4.1927795)"
+ style="fill:url(#linearGradient3266);fill-opacity:1"
+ id="g3938">
+ <g
+ id="text111"
+ style="font-size:147.90756226px;font-weight:normal;fill:url(#linearGradient3256);fill-rule:evenodd;font-family:Droid Sans Mono">
+ <path
+ id="path4037"
+ style="font-size:147.90756226px;font-weight:normal;fill:url(#linearGradient3254);fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 355.96093,272.06912 -38.13242,0 0,-128.9858 38.13242,0 0,10.76086 -24.98829,0 0,107.39186 24.98829,0 0,10.83308"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="text113"
+ style="font-size:147.90756226px;font-weight:normal;fill:url(#linearGradient3260);fill-rule:evenodd;font-family:Droid Sans Mono">
+ <path
+ id="path4043"
+ style="font-size:147.90756226px;font-weight:normal;fill:url(#linearGradient3258);fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 439.12013,261.23604 24.98829,0 0,-107.39186 -24.98829,0 0,-10.76086 38.13242,0 0,128.9858 -38.13242,0 0,-10.83308"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="text115"
+ style="font-size:147.90756226px;font-weight:normal;fill:url(#linearGradient3264);fill-rule:evenodd;font-family:Droid Sans Mono">
+ <path
+ id="path4046"
+ style="font-size:147.90756226px;font-weight:normal;fill:url(#linearGradient3262);fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 514.87478,165.86889 c 6.11462,8e-5 9.17195,3.3463 9.17201,10.03865 -6e-5,6.6925 -3.05739,10.03871 -9.17201,10.03865 -6.1147,6e-5 -9.17203,-3.34615 -9.172,-10.03865 -3e-5,-6.69235 3.0573,-10.03857 9.172,-10.03865 m 0,63.26515 c 6.11462,2e-5 9.17195,3.34623 9.17201,10.03865 -6e-5,6.74058 -3.05739,10.11087 -9.17201,10.11087 -6.1147,0 -9.17203,-3.37029 -9.172,-10.11087 -3e-5,-6.69242 3.0573,-10.03863 9.172,-10.03865"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ <g
+ transform="matrix(0.76400311,0,0,0.76400311,34.485122,4.1927795)"
+ style="fill:#4d4d4d;fill-opacity:1"
+ id="g3945">
+ <g
+ id="text109"
+ style="font-size:204.03166199px;font-weight:normal;fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;font-family:Droid Sans Mono">
+ <path
+ id="path4032"
+ style="font-size:204.03166199px;font-weight:normal;fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 151.39749,272.06912 -77.308871,0 0,-12.25385 29.389331,-1.9925 0,-117.1588 -29.389331,-1.9925 0,-12.25386 77.308871,0 0,12.25386 -29.2897,1.9925 0,117.1588 29.2897,1.9925 0,12.25385"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path4034"
+ style="font-size:204.03166199px;font-weight:normal;fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 210.07652,215.38259 0,56.68653 -18.53022,0 0,-145.65151 40.24843,0 c 34.13802,1.5e-4 51.20706,14.21328 51.20716,42.63943 -10e-5,14.54532 -4.61605,25.90254 -13.84785,34.0717 -9.16557,8.16929 -22.51528,12.25391 -40.04918,12.25385 l -19.02834,0 m 0,-15.74072 16.93622,0 c 13.15041,7e-5 22.54834,-2.39092 28.19383,-7.17299 5.71173,-4.78191 8.56764,-12.25376 8.56773,-22.41559 -9e-5,-18.5301 -11.22448,-27.7952 -33.67319,-27.79533 l -20.02459,0 0,57.38391"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="text117"
+ style="font-size:131.4621582px;font-weight:normal;fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;font-family:Droid Sans Mono">
+ <path
+ id="path4040"
+ style="font-size:131.4621582px;font-weight:normal;fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;font-family:Droid Sans Mono"
+ d="m 367.00874,170.0062 12.06781,0 16.81792,41.9806 c 3.50904,8.77272 5.41336,14.97779 5.71295,18.61524 l 0.38515,0 c 0.98421,-4.79287 2.90992,-11.04074 5.77714,-18.74363 l 15.34153,-41.85221 12.13201,0 -30.49049,79.66042 c -2.86722,7.44609 -6.20512,13.03065 -10.01372,16.75373 -3.80867,3.76581 -9.07228,5.64873 -15.79087,5.64876 -3.68027,-3e-5 -7.27493,-0.36378 -10.784,-1.09124 l 0,-9.30762 c 2.6532,0.5135 5.56316,0.77026 8.72991,0.77028 4.10817,-2e-5 7.2963,-0.87729 9.56438,-2.63181 2.31083,-1.75455 4.36493,-4.77151 6.16229,-9.05086 l 3.72305,-9.62857 -29.33506,-71.12309"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ </g>
+ <g
+ style="fill:#000000;stroke:none"
+ transform="matrix(0.01117253,0,0,-0.01890653,-248.2999,738.0088)"
+ id="g3904">
+ <path
+ inkscape:connector-curvature="0"
+ d="m 746,2800 c -171,-43 -305,-206 -293,-356 6,-87 39,-141 176,-290 137,-149 151,-168 151,-212 0,-74 -67,-132 -152,-132 -60,0 -105,31 -139,96 l -26,51 -102,-54 c -55,-30 -101,-59 -101,-64 0,-27 64,-130 103,-166 54,-50 123,-73 218,-73 227,0 419,177 422,391 2,97 -31,155 -166,298 -179,188 -197,224 -136,275 69,58 145,42 207,-43 l 28,-39 92,67 c 50,36 91,71 92,78 0,23 -110,131 -156,153 -54,25 -159,34 -218,20 z"
+ id="path3906" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 3700,2700 c -66,-44 -107,-77 -112,-92 -3,-13 -18,-115 -33,-228 -14,-113 -42,-322 -61,-464 -19,-142 -34,-265 -34,-273 0,-10 24,-13 105,-13 l 104,0 6,33 c 8,47 55,407 55,421 0,6 70,-94 155,-222 l 155,-232 125,0 c 69,0 125,2 125,4 0,3 -85,123 -188,268 -103,145 -191,269 -196,276 -5,8 32,54 113,137 67,68 121,130 120,137 0,7 -8,55 -17,107 l -17,93 -170,-188 c -105,-116 -171,-182 -173,-173 -2,9 11,117 28,241 16,123 30,227 30,231 0,13 -16,5 -120,-63 z"
+ id="path3908" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 1467,2485 c -193,-53 -356,-231 -386,-423 -13,-83 -91,-675 -91,-691 0,-7 31,-11 94,-11 l 94,0 11,80 c 6,44 16,116 22,160 6,45 14,79 18,77 92,-50 131,-61 218,-65 112,-5 198,18 288,76 169,109 271,324 236,494 -31,152 -128,261 -270,302 -65,19 -167,19 -234,1 z m 141,-186 c 95,-20 171,-113 172,-210 0,-76 -27,-138 -89,-200 -43,-42 -69,-59 -115,-73 -139,-44 -263,21 -296,156 -33,129 69,284 211,322 68,18 59,18 117,5 z"
+ id="path3910" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 2439,2486 c -177,-51 -339,-216 -378,-386 -37,-162 23,-336 144,-420 72,-49 142,-70 238,-70 75,0 162,17 194,38 9,6 46,232 39,239 -1,2 -21,-11 -44,-28 -132,-97 -296,-68 -363,63 -25,50 -25,148 0,205 101,222 411,238 471,24 14,-50 12,-129 -4,-252 -8,-57 -17,-129 -21,-159 -3,-30 -8,-67 -11,-82 l -6,-28 95,0 95,0 6,28 c 22,111 58,437 53,495 -9,135 -108,269 -235,318 -70,27 -205,35 -273,15 z"
+ id="path3912" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 3243,2464 c -56,-20 -126,-84 -153,-139 -17,-35 -31,-115 -61,-343 -21,-163 -41,-309 -44,-324 l -4,-28 98,0 99,0 6,28 c 3,15 8,47 11,72 3,25 19,148 36,275 37,278 35,275 144,275 l 73,0 6,32 c 3,17 9,62 13,100 l 6,68 -94,-1 c -55,0 -111,-7 -136,-15 z"
+ id="path3914" />
+ </g>
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer3"
+ inkscape:label="PLACE YOUR PICTOGRAM HERE"
+ style="display:inline" />
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="BADGE"
+ style="display:none"
+ sodipodi:insensitive="true">
+ <g
+ style="display:inline"
+ transform="translate(-340.00001,-581)"
+ id="g4394"
+ clip-path="none">
+ <g
+ id="g855">
+ <g
+ inkscape:groupmode="maskhelper"
+ id="g870"
+ clip-path="url(#clipPath873)"
+ style="opacity:0.6;filter:url(#filter891)">
+ <path
+ transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)"
+ d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
+ sodipodi:ry="12"
+ sodipodi:rx="12"
+ sodipodi:cy="552.36218"
+ sodipodi:cx="252"
+ id="path844"
+ 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"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ id="g862">
+ <path
+ sodipodi:type="arc"
+ 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"
+ id="path4398"
+ sodipodi:cx="252"
+ sodipodi:cy="552.36218"
+ sodipodi:rx="12"
+ sodipodi:ry="12"
+ d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
+ transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" />
+ <path
+ transform="matrix(1.25,0,0,1.25,33,-100.45273)"
+ d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
+ sodipodi:ry="12"
+ sodipodi:rx="12"
+ sodipodi:cy="552.36218"
+ sodipodi:cx="252"
+ id="path4400"
+ 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"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:type="star"
+ 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"
+ id="path4459"
+ sodipodi:sides="5"
+ sodipodi:cx="666.19574"
+ sodipodi:cy="589.50385"
+ sodipodi:r1="7.2431178"
+ sodipodi:r2="4.3458705"
+ sodipodi:arg1="1.0471976"
+ sodipodi:arg2="1.6755161"
+ inkscape:flatsided="false"
+ inkscape:rounded="0.1"
+ inkscape:randomized="0"
+ 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 C 669.9821,591.68426 670.20862,595.55064 669.8173,595.77657 Z"
+ transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" />
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
=== renamed file 'icon.svg' => 'icon.svg.moved'
=== added file 'metadata.yaml'
--- metadata.yaml 1970-01-01 00:00:00 +0000
+++ metadata.yaml 2015-08-20 23:14:47 +0000
@@ -0,0 +1,14 @@
+name: apache-spark-notebook
+summary: A web-based IPython Notebook for Apache Spark.
+maintainer: Amir Sanjar <amir.sanjar@xxxxxxxxxxxxx>
+description: |
+ The IPython Notebook is an interactive computational environment, in which
+ you can combine code execution, rich text, mathematics, plots, and rich media.
+ IPython Notebook and Spark’s Python API are a powerful combination for data
+ science.
+tags: ["bigdata", "hadoop", "apache"]
+subordinate: true
+requires:
+ spark:
+ interface: spark
+ scope: container
=== renamed file 'metadata.yaml' => 'metadata.yaml.moved'
=== added directory 'resources'
=== renamed directory 'resources' => 'resources.moved'
=== added file 'resources.yaml'
--- resources.yaml 1970-01-01 00:00:00 +0000
+++ resources.yaml 2015-08-20 23:14:47 +0000
@@ -0,0 +1,7 @@
+options:
+ output_dir: /home/ubuntu/resources
+resources:
+ pathlib:
+ pypi: path.py>=7.0
+ jujubigdata:
+ pypi: jujubigdata>=4.0.0,<5.0.0
=== renamed file 'resources.yaml' => 'resources.yaml.moved'
=== added directory 'resources/python'
=== added file 'resources/python/PyYAML-3.11.tar.gz'
Binary 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-20 23:14:47 +0000 differ
=== added file 'resources/python/charmhelpers-0.3.1.tar.gz'
Binary 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-20 23:14:47 +0000 differ
=== added file 'resources/python/jujuresources-0.2.9.tar.gz'
Binary 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-20 23:14:47 +0000 differ
=== added file 'resources/python/pyaml-15.5.7.tar.gz'
Binary 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-20 23:14:47 +0000 differ
=== added file 'resources/python/six-1.9.0-py2.py3-none-any.whl'
Binary 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-20 23:14:47 +0000 differ
=== added directory 'scripts'
=== renamed directory 'scripts' => 'scripts.moved'
=== added file 'scripts/00-pyspark-setup.py'
--- scripts/00-pyspark-setup.py 1970-01-01 00:00:00 +0000
+++ scripts/00-pyspark-setup.py 2015-08-20 23:14:47 +0000
@@ -0,0 +1,10 @@
+import os
+import sys
+
+py4j = 'python/lib/py4j-0.8.2.1-src.zip'
+spark_home = os.environ.get('SPARK_HOME', None)
+if not spark_home:
+ raise ValueError('SPARK_HOME environment variable is not set')
+sys.path.insert(0, os.path.join(spark_home, 'python'))
+sys.path.insert(0, py4j)
+execfile(os.path.join(spark_home, 'python/pyspark/shell.py'))
=== added file 'scripts/start_notebook.sh'
--- scripts/start_notebook.sh 1970-01-01 00:00:00 +0000
+++ scripts/start_notebook.sh 2015-08-20 23:14:47 +0000
@@ -0,0 +1,2 @@
+#!/bin/bash
+ipython notebook --profile=pyspark >/dev/null 2>&1 &
=== added file 'scripts/stop_notebook.sh'
--- scripts/stop_notebook.sh 1970-01-01 00:00:00 +0000
+++ scripts/stop_notebook.sh 2015-08-20 23:14:47 +0000
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+PID=`pgrep -f 'ipython.*notebook'`
+if [ -n "${PID}" ]; then
+ kill ${PID}
+fi
=== added directory 'tests'
=== renamed directory 'tests' => 'tests.moved'
=== added file 'tests/00-setup'
--- tests/00-setup 1970-01-01 00:00:00 +0000
+++ tests/00-setup 2015-08-20 23:14:47 +0000
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+sudo add-apt-repository ppa:juju/stable -y
+sudo apt-get update
+sudo apt-get install python3 amulet -y
=== added file 'tests/100-deploy-spark-hdfs-yarn'
--- tests/100-deploy-spark-hdfs-yarn 1970-01-01 00:00:00 +0000
+++ tests/100-deploy-spark-hdfs-yarn 2015-08-20 23:14:47 +0000
@@ -0,0 +1,62 @@
+#!/usr/bin/python3
+
+import unittest
+import amulet
+
+
+class TestDeploy(unittest.TestCase):
+ """
+ Deployment test for Apache Spark using HDFS as shared storage and YARN as
+ cluster job manager.
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ cls.d = amulet.Deployment(series='trusty')
+ # Deploy a hadoop cluster
+ cls.d.add('yarn-master', charm='cs:~bigdata-dev/trusty/apache-hadoop-yarn-master')
+ cls.d.add('hdfs-master', charm='cs:~bigdata-dev/trusty/apache-hadoop-hdfs-master')
+ cls.d.add('compute-slave', charm='cs:~bigdata-dev/trusty/apache-hadoop-compute-slave', units=3)
+ cls.d.add('plugin', charm='cs:~bigdata-dev/trusty/apache-hadoop-plugin')
+ cls.d.relate('yarn-master:namenode', 'hdfs-master:namenode')
+ cls.d.relate('compute-slave:nodemanager', 'yarn-master:nodemanager')
+ cls.d.relate('compute-slave:datanode', 'hdfs-master:datanode')
+ cls.d.relate('plugin:resourcemanager', 'yarn-master:resourcemanager')
+ cls.d.relate('plugin:namenode', 'hdfs-master:namenode')
+
+ # Add Spark Service
+ cls.d.add('spark', charm='cs:~bigdata-dev/trusty/apache-spark')
+ cls.d.relate('spark:hadoop-plugin', 'plugin:hadoop-plugin')
+
+ # Add IPythonNotebook
+ cls.d.add('notebook', charm='cs:~bigdata-dev/trusty/apache-spark-notebook')
+ cls.d.relate('notebook:spark', 'spark:spark')
+
+ cls.d.setup(timeout=3600)
+ cls.d.sentry.wait()
+ cls.unit = cls.d.sentry.unit['notebook/0']
+
+###########################################################################
+# Validate that the Spark HistoryServer is running
+###########################################################################
+ def test_spark_status(self):
+ o, c = self.unit.run("pgrep -a java | grep HistoryServer")
+ assert c == 0, "Spark HistoryServer not running"
+
+###########################################################################
+# Validate that the Notebook process is running
+###########################################################################
+ def test_notebook_status(self):
+ o, c = self.unit.run("pgrep -a python | grep notebook")
+ assert c == 0, "IPython Notebook daemon not running"
+
+###########################################################################
+# Validate Spark commandline operation - run SparkPi
+###########################################################################
+ def test_spark_job(self):
+ o, c = self.unit.run("su ubuntu -c '/home/ubuntu/sparkpi.sh'")
+ assert c == 0, "SparkPi test failed: %s" % o
+
+
+if __name__ == '__main__':
+ unittest.main()
=== added directory 'tests/remote'
=== added file 'tests/remote/test_dist_config.py'
--- tests/remote/test_dist_config.py 1970-01-01 00:00:00 +0000
+++ tests/remote/test_dist_config.py 2015-08-20 23:14:47 +0000
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+
+import grp
+import os
+import pwd
+import unittest
+
+from charmhelpers.contrib import bigdata
+
+
+class TestDistConfig(unittest.TestCase):
+ """
+ Test that the ``dist.yaml`` settings were applied properly, such as users, groups, and dirs.
+
+ This is done as a remote test on the deployed unit rather than a regular
+ test under ``tests/`` because filling in the ``dist.yaml`` requires Juju
+ context (e.g., config).
+ """
+ @classmethod
+ def setUpClass(cls):
+ config = None
+ config_dir = os.environ['JUJU_CHARM_DIR']
+ config_file = 'dist.yaml'
+ if os.path.isfile(os.path.join(config_dir, config_file)):
+ config = os.path.join(config_dir, config_file)
+ if not config:
+ raise IOError('Could not find {} in {}'.format(config_file, config_dir))
+ reqs = ['vendor', 'hadoop_version', 'packages', 'groups', 'users',
+ 'dirs', 'ports']
+ cls.dist_config = bigdata.utils.DistConfig(config, reqs)
+
+ def test_groups(self):
+ for name in self.dist_config.groups:
+ try:
+ grp.getgrnam(name)
+ except KeyError:
+ self.fail('Group {} is missing'.format(name))
+
+ def test_users(self):
+ for username, details in self.dist_config.users.items():
+ try:
+ user = pwd.getpwnam(username)
+ except KeyError:
+ self.fail('User {} is missing'.format(username))
+ for groupname in details['groups']:
+ try:
+ group = grp.getgrnam(groupname)
+ except KeyError:
+ self.fail('Group {} referenced by user {} does not exist'.format(
+ groupname, username))
+ if group.gr_gid != user.pw_gid:
+ self.assertIn(username, group.gr_mem, 'User {} not in group {}'.format(
+ username, groupname))
+
+ def test_dirs(self):
+ for name, details in self.dist_config.dirs.items():
+ dirpath = self.dist_config.path(name)
+ self.assertTrue(dirpath.isdir(), 'Dir {} is missing'.format(name))
+ stat = dirpath.stat()
+ owner = pwd.getpwuid(stat.st_uid).pw_name
+ group = grp.getgrgid(stat.st_gid).gr_name
+ perms = stat.st_mode & ~0o40000
+ self.assertEqual(owner, details.get('owner', 'root'),
+ 'Dir {} ({}) has wrong owner: {}'.format(name, dirpath, owner))
+ self.assertEqual(group, details.get('group', 'root'),
+ 'Dir {} ({}) has wrong group: {}'.format(name, dirpath, group))
+ self.assertEqual(perms, details.get('perms', 0o755),
+ 'Dir {} ({}) has wrong perms: 0o{:o}'.format(name, dirpath, perms))
+
+
+if __name__ == '__main__':
+ unittest.main()
=== added file 'tests/tests.yaml'
--- tests/tests.yaml 1970-01-01 00:00:00 +0000
+++ tests/tests.yaml 2015-08-20 23:14:47 +0000
@@ -0,0 +1,10 @@
+# Driver for bundletester: https://github.com/juju-solutions/bundletester
+#
+# It may be useful to alter the defaults during manual testing. For example,
+# set 'reset: false' to reuse existing charms instead of redeploying them.
+
+# Allow bootstrap of current env, default: true
+bootstrap: true
+
+# Use juju-deployer to reset env between test, default: true
+reset: true
Follow ups