You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bigtop.apache.org by of...@apache.org on 2016/07/29 17:03:03 UTC

[22/51] [abbrv] bigtop git commit: BIGTOP-2435 Add Juju charms for hadoop component

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/reactive/resourcemanager.py
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/reactive/resourcemanager.py b/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/reactive/resourcemanager.py
new file mode 100644
index 0000000..afca26b
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/reactive/resourcemanager.py
@@ -0,0 +1,225 @@
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+from charms.reactive import is_state, remove_state, set_state, when, when_not
+from charms.layer.apache_bigtop_base import Bigtop, get_layer_opts, get_fqdn
+from charmhelpers.core import hookenv, host
+from jujubigdata import utils
+
+
+###############################################################################
+# Utility methods
+###############################################################################
+def send_early_install_info(remote):
+    """Send clients/slaves enough relation data to start their install.
+
+    If slaves or clients join before the resourcemanager is installed, we can
+    still provide enough info to start their installation. This will help
+    parallelize installation among our cluster.
+
+    Note that slaves can safely install early, but should not start until the
+    'resourcemanager.ready' state is set by the mapred-slave interface.
+    """
+    rm_host = get_fqdn()
+    rm_ipc = get_layer_opts().port('resourcemanager')
+    jh_ipc = get_layer_opts().port('jobhistory')
+    jh_http = get_layer_opts().port('jh_webapp_http')
+
+    remote.send_resourcemanagers([rm_host])
+    remote.send_ports(rm_ipc, jh_http, jh_ipc)
+
+
+###############################################################################
+# Core methods
+###############################################################################
+@when_not('namenode.joined')
+def blocked():
+    hookenv.status_set('blocked', 'missing required namenode relation')
+
+
+@when('bigtop.available', 'namenode.joined')
+@when_not('apache-bigtop-resourcemanager.installed')
+def install_resourcemanager(namenode):
+    """Install if the namenode has sent its FQDN.
+
+    We only need the namenode FQDN to perform the RM install, so poll for
+    namenodes() data whenever we have a namenode relation. This allows us to
+    install asap, even if 'namenode.ready' is not set yet.
+    """
+    if namenode.namenodes():
+        hookenv.status_set('maintenance', 'installing resourcemanager')
+        nn_host = namenode.namenodes()[0]
+        rm_host = get_fqdn()
+        bigtop = Bigtop()
+        hosts = {'namenode': nn_host, 'resourcemanager': rm_host}
+        bigtop.render_site_yaml(hosts=hosts, roles='resourcemanager')
+        bigtop.trigger_puppet()
+
+        # /etc/hosts entries from the KV are not currently used for bigtop,
+        # but a hosts_map attribute is required by some interfaces (eg: mapred-slave)
+        # to signify RM's readiness. Set our RM info in the KV to fulfill this
+        # requirement.
+        utils.initialize_kv_host()
+
+        # Add our ubuntu user to the hadoop and mapred groups.
+        get_layer_opts().add_users()
+
+        set_state('apache-bigtop-resourcemanager.installed')
+        hookenv.status_set('maintenance', 'resourcemanager installed')
+    else:
+        hookenv.status_set('waiting', 'waiting for namenode fqdn')
+
+
+@when('apache-bigtop-resourcemanager.installed', 'namenode.joined')
+@when_not('namenode.ready')
+def send_nn_spec(namenode):
+    """Send our resourcemanager spec so the namenode can become ready."""
+    bigtop = Bigtop()
+    namenode.set_local_spec(bigtop.spec())
+    hookenv.status_set('waiting', 'waiting for namenode to become ready')
+
+
+@when('apache-bigtop-resourcemanager.installed', 'namenode.ready')
+@when_not('apache-bigtop-resourcemanager.started')
+def start_resourcemanager(namenode):
+    hookenv.status_set('maintenance', 'starting resourcemanager')
+    # NB: service should be started by install, but this may be handy in case
+    # we have something that removes the .started state in the future. Also
+    # note we restart here in case we modify conf between install and now.
+    host.service_restart('hadoop-yarn-resourcemanager')
+    host.service_restart('hadoop-mapreduce-historyserver')
+    for port in get_layer_opts().exposed_ports('resourcemanager'):
+        hookenv.open_port(port)
+    set_state('apache-bigtop-resourcemanager.started')
+    hookenv.status_set('active', 'ready')
+
+
+###############################################################################
+# Slave methods
+###############################################################################
+@when('nodemanager.joined')
+@when_not('apache-bigtop-resourcemanager.installed')
+def send_nm_install_info(nodemanager):
+    """Send nodemanagers enough relation data to start their install."""
+    send_early_install_info(nodemanager)
+
+
+@when('apache-bigtop-resourcemanager.started')
+@when('nodemanager.joined')
+def send_nm_all_info(nodemanager):
+    """Send nodemanagers all mapred-slave relation data.
+
+    At this point, the resourcemanager is ready to serve nodemanagers. Send all
+    mapred-slave relation data so that our 'resourcemanager.ready' state becomes set.
+    """
+    bigtop = Bigtop()
+    rm_host = get_fqdn()
+    rm_ipc = get_layer_opts().port('resourcemanager')
+    jh_ipc = get_layer_opts().port('jobhistory')
+    jh_http = get_layer_opts().port('jh_webapp_http')
+
+    nodemanager.send_resourcemanagers([rm_host])
+    nodemanager.send_spec(bigtop.spec())
+    nodemanager.send_ports(rm_ipc, jh_http, jh_ipc)
+
+    # hosts_map and ssh_key are required by the mapred-slave interface to signify
+    # RM's readiness. Send them, even though they are not utilized by bigtop.
+    # NB: update KV hosts with all nodemanagers prior to sending the hosts_map
+    # because mapred-slave gates readiness on a NM's presence in the hosts_map.
+    utils.update_kv_hosts(nodemanager.hosts_map())
+    nodemanager.send_hosts_map(utils.get_kv_hosts())
+    nodemanager.send_ssh_key('invalid')
+
+    # update status with slave count and report ready for hdfs
+    num_slaves = len(nodemanager.nodes())
+    hookenv.status_set('active', 'ready ({count} nodemanager{s})'.format(
+        count=num_slaves,
+        s='s' if num_slaves > 1 else '',
+    ))
+    set_state('apache-bigtop-resourcemanager.ready')
+
+
+@when('apache-bigtop-resourcemanager.started')
+@when('nodemanager.departing')
+def remove_nm(nodemanager):
+    """Handle a departing nodemanager.
+
+    This simply logs a message about a departing nodemanager and removes
+    the entry from our KV hosts_map. The hosts_map is not used by bigtop, but
+    it is required for the 'resourcemanager.ready' state, so we may as well
+    keep it accurate.
+    """
+    slaves_leaving = nodemanager.nodes()  # only returns nodes in "departing" state
+    hookenv.log('Nodemanagers leaving: {}'.format(slaves_leaving))
+    utils.remove_kv_hosts(slaves_leaving)
+    nodemanager.dismiss()
+
+
+@when('apache-bigtop-resourcemanager.started')
+@when_not('nodemanager.joined')
+def wait_for_nm():
+    remove_state('apache-bigtop-resourcemanager.ready')
+    # NB: we're still active since a user may be interested in our web UI
+    # without any NMs, but let them know yarn is caput without a NM relation.
+    hookenv.status_set('active', 'yarn requires a nodemanager relation')
+
+
+###############################################################################
+# Client methods
+###############################################################################
+@when('resourcemanager.clients')
+@when_not('apache-bigtop-resourcemanager.installed')
+def send_client_install_info(client):
+    """Send clients enough relation data to start their install."""
+    send_early_install_info(client)
+
+
+@when('apache-bigtop-resourcemanager.started')
+@when('resourcemanager.clients')
+def send_client_all_info(client):
+    """Send clients (plugin, RM, non-DNs) all dfs relation data.
+
+    At this point, the resourcemanager is ready to serve clients. Send all
+    mapred relation data so that our 'resourcemanager.ready' state becomes set.
+    """
+    bigtop = Bigtop()
+    rm_host = get_fqdn()
+    rm_ipc = get_layer_opts().port('resourcemanager')
+    jh_ipc = get_layer_opts().port('jobhistory')
+    jh_http = get_layer_opts().port('jh_webapp_http')
+
+    client.send_resourcemanagers([rm_host])
+    client.send_spec(bigtop.spec())
+    client.send_ports(rm_ipc, jh_http, jh_ipc)
+
+    # resourcemanager.ready implies we have at least 1 nodemanager, which means
+    # yarn is ready for use. Inform clients of that with send_ready().
+    if is_state('apache-bigtop-resourcemanager.ready'):
+        client.send_ready(True)
+    else:
+        client.send_ready(False)
+
+    # hosts_map is required by the mapred interface to signify
+    # RM's readiness. Send it, even though it is not utilized by bigtop.
+    client.send_hosts_map(utils.get_kv_hosts())
+
+
+###############################################################################
+# Benchmark methods
+###############################################################################
+@when('benchmark.joined')
+def register_benchmarks(benchmark):
+    benchmark.register('mrbench', 'nnbench', 'terasort', 'testdfsio')

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/tests/01-basic-deployment.py
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/tests/01-basic-deployment.py b/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/tests/01-basic-deployment.py
new file mode 100755
index 0000000..65dbbbb
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/tests/01-basic-deployment.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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 unittest
+import amulet
+
+
+class TestDeploy(unittest.TestCase):
+    """
+    Trivial deployment test for Apache Bigtop ResourceManager.
+
+    This charm cannot do anything useful by itself, so integration testing
+    is done in the bundle.
+    """
+
+    def test_deploy(self):
+        self.d = amulet.Deployment(series='trusty')
+        self.d.add('resourcemanager', 'hadoop-resourcemanager')
+        self.d.setup(timeout=900)
+        self.d.sentry.wait(timeout=1800)
+        self.unit = self.d.sentry['resourcemanager'][0]
+
+
+if __name__ == '__main__':
+    unittest.main()

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/tests/tests.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/tests/tests.yaml b/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/tests/tests.yaml
new file mode 100644
index 0000000..3b6ce3e
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/tests/tests.yaml
@@ -0,0 +1,3 @@
+reset: false
+packages:
+  - amulet

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/wheelhouse.txt
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/wheelhouse.txt b/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/wheelhouse.txt
new file mode 100644
index 0000000..183242f
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-resourcemanager/wheelhouse.txt
@@ -0,0 +1 @@
+charms.benchmark>=1.0.0,<2.0.0

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/README.md
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/README.md b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/README.md
new file mode 100644
index 0000000..2580072
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/README.md
@@ -0,0 +1,116 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+## Overview
+
+The Apache Hadoop software library is a framework that allows for the
+distributed processing of large data sets across clusters of computers
+using a simple programming model.
+
+This charm deploys a combined slave node running the NodeManager
+and DataNode components of the Apache Bigtop platform
+to provide YARN compute and HDFS storage resources.
+
+
+## Usage
+
+This charm is intended to be deployed via one of the
+[apache bigtop bundles](https://jujucharms.com/u/bigdata-dev/#bundles).
+For example:
+
+    juju deploy hadoop-processing
+
+> Note: With Juju versions < 2.0, you will need to use [juju-deployer][] to
+deploy the bundle.
+
+This will deploy the Apache Bigtop platform with a workload node
+preconfigured to work with the cluster.
+
+You can also manually load and run map-reduce jobs via the plugin charm
+included in the bundles linked above:
+
+    juju scp my-job.jar plugin/0:
+    juju ssh plugin/0
+    hadoop jar my-job.jar
+
+
+[juju-deployer]: https://pypi.python.org/pypi/juju-deployer/
+
+
+## Status and Smoke Test
+
+Apache Bigtop charms provide extended status reporting to indicate when they
+are ready:
+
+    juju status --format=tabular
+
+This is particularly useful when combined with `watch` to track the on-going
+progress of the deployment:
+
+    watch -n 0.5 juju status --format=tabular
+
+The message for each unit will provide information about that unit's state.
+Once they all indicate that they are ready, you can perform a "smoke test"
+to verify HDFS or YARN services are working as expected. Trigger the
+`smoke-test` action by:
+
+    juju action do namenode/0 smoke-test
+    juju action do resourcemanager/0 smoke-test
+
+After a few seconds or so, you can check the results of the smoke test:
+
+    juju action status
+
+You will see `status: completed` if the smoke test was successful, or
+`status: failed` if it was not.  You can get more information on why it failed
+via:
+
+    juju action fetch <action-id>
+
+
+## Scaling
+
+The slave node is the "workhorse" of the Hadoop environment. To scale your
+cluster performance and storage capabilities, you can simply add more slave
+units.  For example, to add three more units:
+
+    juju add-unit slave -n 3
+
+
+## Deploying in Network-Restricted Environments
+
+Charms can be deployed in environments with limited network access. To deploy
+in this environment, you will need a local mirror to serve required packages.
+
+
+### Mirroring Packages
+
+You can setup a local mirror for apt packages using squid-deb-proxy.
+For instructions on configuring juju to use this, see the
+[Juju Proxy Documentation](https://juju.ubuntu.com/docs/howto-proxies.html).
+
+
+## Contact Information
+
+- <bi...@lists.ubuntu.com>
+
+
+## Hadoop
+
+- [Apache Bigtop](http://bigtop.apache.org/) home page
+- [Apache Bigtop issue tracking](http://bigtop.apache.org/issue-tracking.html)
+- [Apache Bigtop mailing lists](http://bigtop.apache.org/mail-lists.html)
+- [Apache Bigtop charms](https://jujucharms.com/q/apache/bigtop)

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/copyright
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/copyright b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/copyright
new file mode 100644
index 0000000..52de50a
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/copyright
@@ -0,0 +1,16 @@
+Format: http://dep.debian.net/deps/dep5/
+
+Files: *
+Copyright: Copyright 2015, Canonical Ltd., All Rights Reserved, The Apache Software Foundation
+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.

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/layer.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/layer.yaml b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/layer.yaml
new file mode 100644
index 0000000..73c66e6
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/layer.yaml
@@ -0,0 +1,2 @@
+repo: git@github.com:juju-solutions/layer-hadoop-slave.git
+includes: ['layer:hadoop-datanode', 'layer:hadoop-nodemanager']

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/metadata.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/metadata.yaml b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/metadata.yaml
new file mode 100644
index 0000000..f0b6cce
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/metadata.yaml
@@ -0,0 +1,8 @@
+name: hadoop-slave
+summary: Combined slave node (DataNode + NodeManager) for Apache Bigtop.
+description: >
+  Hadoop is a software platform that lets one easily write and
+  run applications that process vast amounts of data.
+
+  This charm manages both the storage node (DataNode) for HDFS and the
+  compute node (NodeManager) for Yarn.

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/reactive/hadoop_status.py
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/reactive/hadoop_status.py b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/reactive/hadoop_status.py
new file mode 100644
index 0000000..1e6d38f
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/reactive/hadoop_status.py
@@ -0,0 +1,55 @@
+# pylint: disable=unused-argument
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+from charms.reactive import when_any, when_none, is_state
+from charmhelpers.core.hookenv import status_set
+
+
+@when_none('namenode.spec.mismatch', 'resourcemanager.spec.mismatch')
+@when_any(
+    'bigtop.available',
+    'apache-bigtop-datanode.pending',
+    'apache-bigtop-nodemanager.pending',
+    'apache-bigtop-datanode.installed',
+    'apache-bigtop-nodemanager.installed',
+    'apache-bigtop-datanode.started',
+    'apache-bigtop-nodemanager.started',
+    'namenode.joined',
+    'namenode.ready',
+    'resourcemanager.joined',
+    'resourcemanager.ready',
+)
+def update_status():
+    hdfs_rel = is_state('namenode.joined')
+    yarn_rel = is_state('resourcemanager.joined')
+    hdfs_ready = is_state('namenode.ready')
+    yarn_ready = is_state('resourcemanager.ready')
+
+    if not (hdfs_rel or yarn_rel):
+        status_set('blocked',
+                   'missing required namenode and/or resourcemanager relation')
+    elif hdfs_rel and not hdfs_ready:
+        status_set('waiting', 'waiting for hdfs to become ready')
+    elif yarn_rel and not yarn_ready:
+        status_set('waiting', 'waiting for yarn to become ready')
+    else:
+        ready = []
+        if hdfs_ready:
+            ready.append('datanode')
+        if yarn_ready:
+            ready.append('nodemanager')
+        status_set('active', 'ready ({})'.format(' & '.join(ready)))

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/tests/01-basic-deployment.py
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/tests/01-basic-deployment.py b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/tests/01-basic-deployment.py
new file mode 100755
index 0000000..e479078
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/tests/01-basic-deployment.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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 unittest
+import amulet
+
+
+class TestDeploy(unittest.TestCase):
+    """
+    Trivial deployment test for Apache Bigtop Slave.
+
+    This charm cannot do anything useful by itself, so integration testing
+    is done in the bundle.
+    """
+
+    def test_deploy(self):
+        self.d = amulet.Deployment(series='trusty')
+        self.d.add('slave', 'hadoop-slave')
+        self.d.setup(timeout=900)
+        self.d.sentry.wait(timeout=1800)
+        self.unit = self.d.sentry['slave'][0]
+
+
+if __name__ == '__main__':
+    unittest.main()

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/tests/tests.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/tests/tests.yaml b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/tests/tests.yaml
new file mode 100644
index 0000000..3b6ce3e
--- /dev/null
+++ b/bigtop-packages/src/charm/hadoop/layer-hadoop-slave/tests/tests.yaml
@@ -0,0 +1,3 @@
+reset: false
+packages:
+  - amulet

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/build.gradle
----------------------------------------------------------------------
diff --git a/build.gradle b/build.gradle
index 6395be8..9af7a43 100644
--- a/build.gradle
+++ b/build.gradle
@@ -113,6 +113,9 @@ rat {
        "bigtop-packages/src/common/*/*.json",
        "bigtop-packages/src/common/**/*.default",
        "bigtop-repos/apt/distributions",
+       /* Juju charm files with rigid structure */
+       "bigtop-packages/src/charm/**/wheelhouse.txt",
+       "bigtop-packages/src/charm/**/*.yaml",
        /* Misc individual files */
        "src/site/resources/bigtop.rdf",
        "src/site/resources/images/bigtop-logo.ai",

http://git-wip-us.apache.org/repos/asf/bigtop/blob/d639645e/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index ab504c3..97098bb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -337,6 +337,9 @@
               <exclude>bigtop-packages/src/common/*/*.json</exclude>
               <exclude>bigtop-packages/src/common/**/*.default</exclude>
               <exclude>bigtop-repos/apt/distributions</exclude>
+              <!-- Juju charm files with rigid structure -->
+              <exclude>bigtop-packages/src/charm/**/wheelhouse.txt</exclude>
+              <exclude>bigtop-packages/src/charm/**/*.yaml</exclude>
               <!-- Miscelaneous individual files -->
               <exclude>src/site/resources/bigtop.rdf</exclude>
               <exclude>src/site/resources/images/bigtop-logo.ai</exclude>