Repository: bigtop
Updated Branches:
  refs/heads/master 4747169cc -> f482fd9fb


BIGTOP-2486: Add Kafka Charm (closes #127)

Signed-off-by: Kevin W Monroe <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/bigtop/repo
Commit: http://git-wip-us.apache.org/repos/asf/bigtop/commit/f482fd9f
Tree: http://git-wip-us.apache.org/repos/asf/bigtop/tree/f482fd9f
Diff: http://git-wip-us.apache.org/repos/asf/bigtop/diff/f482fd9f

Branch: refs/heads/master
Commit: f482fd9fb2a8d9f76bd5ca1c23f28d526516e52a
Parents: 4747169
Author: Konstantinos Tsakalozos <[email protected]>
Authored: Thu Jun 2 16:18:37 2016 +0300
Committer: Kevin W Monroe <[email protected]>
Committed: Sat Oct 8 12:57:34 2016 -0500

----------------------------------------------------------------------
 .../src/charm/kafka/layer-kafka/README.md       | 227 +++++++++++++++++++
 .../src/charm/kafka/layer-kafka/actions.yaml    |  44 ++++
 .../kafka/layer-kafka/actions/create-topic      |  50 ++++
 .../kafka/layer-kafka/actions/kafkautils.py     |  38 ++++
 .../charm/kafka/layer-kafka/actions/list-topics |  42 ++++
 .../charm/kafka/layer-kafka/actions/list-zks    |  36 +++
 .../charm/kafka/layer-kafka/actions/read-topic  |  55 +++++
 .../charm/kafka/layer-kafka/actions/smoke-test  |  64 ++++++
 .../charm/kafka/layer-kafka/actions/write-topic |  54 +++++
 .../src/charm/kafka/layer-kafka/config.yaml     |  10 +
 .../src/charm/kafka/layer-kafka/copyright       |  16 ++
 .../src/charm/kafka/layer-kafka/icon.svg        |  90 ++++++++
 .../src/charm/kafka/layer-kafka/layer.yaml      |  20 ++
 .../lib/charms/layer/bigtop_kafka.py            |  88 +++++++
 .../src/charm/kafka/layer-kafka/metadata.yaml   |  30 +++
 .../charm/kafka/layer-kafka/reactive/kafka.py   |  89 ++++++++
 .../charm/kafka/layer-kafka/tests/01-deploy.py  |  58 +++++
 .../kafka/layer-kafka/tests/02-smoke-test.py    |  59 +++++
 .../layer-kafka/tests/10-config-changed.py      |  98 ++++++++
 .../charm/kafka/layer-kafka/tests/tests.yaml    |   3 +
 20 files changed, 1171 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/README.md
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/README.md 
b/bigtop-packages/src/charm/kafka/layer-kafka/README.md
new file mode 100644
index 0000000..1db627e
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/README.md
@@ -0,0 +1,227 @@
+<!--
+  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
+
+Apache Kafka is an open-source message broker project developed by the Apache
+Software Foundation written in Scala. The project aims to provide a unified,
+high-throughput, low-latency platform for handling real-time data feeds. Learn
+more at [kafka.apache.org](http://kafka.apache.org/).
+
+This charm deploys the Kafka component of the Apache Bigtop platform.
+
+
+# Deploying / Using
+
+A working Juju installation is assumed to be present. If Juju is not yet set
+up, please follow the
+[getting-started](https://jujucharms.com/docs/2.0/getting-started)
+instructions prior to deploying this charm.
+
+Kafka requires the Zookeeper distributed coordination service. Deploy and
+relate them as follows:
+
+    juju deploy kafka
+    juju deploy zookeeper
+    juju add-relation kafka zookeeper
+
+Once deployed, there are a number of actions available in this charm.
+> **Note**: Actions described below assume Juju 2.0 or greater. If using an
+earlier version of Juju, the action syntax is:
+`juju action do kafka/0 <action_name> <action_args>; juju action fetch <id>`.
+
+List the zookeeper servers that our kafka brokers
+are connected to. The following will list `<ip>:<port>` information for each
+zookeeper unit in the environment (e.g.: `10.0.3.221:2181`).
+
+    juju run-action kafka/0 list-zks
+    juju show-action-output <id>  # <-- id from above command
+
+Create a Kafka topic with:
+
+    juju run-action kafka/0 create-topic topic=<topic_name> \
+     partitions=<#> replication=<#>
+    juju show-action-output <id>  # <-- id from above command
+
+List topics with:
+
+    juju run-action kafka/0 list-topics
+    juju show-action-output <id>  # <-- id from above command
+
+Write to a topic with:
+
+    juju run-action kafka/0 write-topic topic=<topic_name> data=<data>
+    juju show-action-output <id>  # <-- id from above command
+
+Read from a topic with:
+
+    juju run-action kafka/0 read-topic topic=<topic_name> partition=<#>
+    juju show-action-output <id>  # <-- id from above command
+
+
+# Verifying
+
+## Status
+Apache Bigtop charms provide extended status reporting to indicate when they
+are ready:
+
+    juju status
+
+This is particularly useful when combined with `watch` to track the on-going
+progress of the deployment:
+
+    watch -n 0.5 juju status
+
+The message column will provide information about a given unit's state.
+This charm is ready for use once the status message indicates that it is
+ready.
+
+## Smoke Test
+This charm provides a `smoke-test` action that can be used to verify the
+application is functioning as expected. The test will verify connectivity
+between Kafka and Zookeeper, and will test creation and listing of Kafka
+topics. Run the action as follows:
+
+    juju run-action slave/0 smoke-test
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju action do kafka/0 smoke-test`.
+
+Watch the progress of the smoke test actions with:
+
+    watch -n 0.5 juju show-action-status
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju action status`.
+
+Eventually, the action should settle to `status: completed`.  If it
+reports `status: failed`, the application is not working as expected. Get
+more information about a specific smoke test with:
+
+    juju show-action-output <action-id>
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju action fetch <action-id>`.
+
+
+# Scaling
+
+Expanding a cluster with many brokers is as easy as adding more Kafka units:
+
+    juju add-unit kafka
+
+After adding additional brokers, topics may be created with
+replication up to the number of ready units. For example, if there are two
+ready units, create a replicated topic as follows:
+
+    juju run-action kafka/0 create-topic topic=my-replicated-topic \
+        partitions=1 replication=2
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju action do kafka/0 create-topic <args>`.
+
+Query the description of the recently created topic:
+
+    juju run --unit kafka/0 'kafka-topics.sh --describe \
+        --topic my-replicated-topic --zookeeper <zookeeperip>:2181'
+
+An expected response should be similar to:
+
+    Topic: my-replicated-topic PartitionCount:1 ReplicationFactor:2 Configs:
+    Topic: my-replicated-topic Partition: 0 Leader: 2 Replicas: 2,0 Isr: 2,0
+
+
+# Connecting External Clients
+
+By default, this charm does not expose Kafka outside of the provider's network.
+To allow external clients to connect to Kafka, first expose the service:
+
+    juju expose kafka
+
+Next, ensure the external client can resolve the short hostname of the kafka
+unit. A simple way to do this is to add an `/etc/hosts` entry on the external
+kafka client machine. Gather the needed info from juju:
+
+    user@juju-client$ juju run --unit kafka/0 'hostname -s'
+    kafka-0
+    user@juju-client$ juju status --format=yaml kafka/0 | grep public-address
+    public-address: 40.784.149.135
+
+Update `/etc/hosts` on the external kafka client:
+
+    user@kafka-client$ echo "40.784.149.135 kafka-0" | sudo tee -a /etc/hosts
+
+The external kafka client should now be able to access Kafka by using
+`kafka-0:9092` as the broker.
+
+
+# Network Interfaces
+
+In some network environments, kafka may need to be restricted to
+listen for incoming connections on a specific network interface
+(e.g.: for security reasons). To do so, configure kafka with either a
+network interface name or a CIDR range specifying a subnet. For example:
+
+    juju config kafka network_interface=eth0
+    juju config kafka network_interface=10.0.2.0/24
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju set-config kafka network_interface=eth0`.
+
+Each kafka machine in the cluster will lookup the IP address of that
+network interface, or find the first network interface with an IP
+address in the specified subnet, and bind kafka to that address.
+
+If a mistake is made and an invalid name for the network interface is
+configured, recover by re-configuring with the correct name and then
+run "juju resolved" on each unit:
+
+    juju config kafka network_interface=eth0
+    juju resolved kafka/0
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju set-config kafka network_interface=eth0;
+juju resolved -r kafka/0`.
+
+To go back to listening on any network interface on the
+machine, simply pass ``0.0.0.0`` to ``network_interface``.
+
+    juju config kafka network_interface=0.0.0.0
+
+
+# Network-Restricted Environments
+
+Charms can be deployed in environments with limited network access. To deploy
+in this environment, configure a Juju model with appropriate
+proxy and/or mirror options. See
+[Configuring Models](https://jujucharms.com/docs/2.0/models-config) for more
+information.
+
+
+# Contact Information
+
+- <[email protected]>
+
+
+# Resources
+
+- [Apache Bigtop](http://bigtop.apache.org/) home page
+- [Apache Bigtop mailing lists](http://bigtop.apache.org/mail-lists.html)
+- [Apache Kafka home page](http://kafka.apache.org/)
+- [Apache Kafka issue tracker](https://issues.apache.org/jira/browse/KAFKA)
+- [Juju Bigtop charms](https://jujucharms.com/q/apache/bigtop)
+- [Juju mailing list](https://lists.ubuntu.com/mailman/listinfo/juju)
+- [Juju community](https://jujucharms.com/community)

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/actions.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/actions.yaml 
b/bigtop-packages/src/charm/kafka/layer-kafka/actions.yaml
new file mode 100644
index 0000000..5db0d58
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/actions.yaml
@@ -0,0 +1,44 @@
+create-topic:
+  description: Create a new Kafka topic
+  params:
+    topic:
+      type: string
+      description: Topic name
+    partitions:
+      type: integer
+      description: Number of partitions for the topic being created
+    replication:
+      type: integer
+      description: Replication factor for each partition in the topic
+  required: [topic, partitions, replication]
+  additionalProperties: false
+list-topics:
+  description: List all Kafka topics
+list-zks:
+  description: List ip:port info for connected Zookeeper servers
+read-topic:
+  description: Consume an existing kafka topic
+  params:
+    topic:
+      type: string
+      description: Topic name
+    partition:
+      type: integer
+      description: Partition to consume
+  required: [topic, partition]
+  additionalProperties: false
+smoke-test:
+  description: >
+    Verify that Kafka is working as expected by listing zookeepers, then
+    creating/listing/deleting a topic
+write-topic:
+  description: Write to a kafka topic
+  params:
+    topic:
+      type: string
+      description: Topic name
+    data:
+      type: string
+      description: Data to write to topic
+  required: [topic, data]
+  additionalProperties: false

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/actions/create-topic
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/actions/create-topic 
b/bigtop-packages/src/charm/kafka/layer-kafka/actions/create-topic
new file mode 100755
index 0000000..b402fe8
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/actions/create-topic
@@ -0,0 +1,50 @@
+#!/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 kafkautils
+import subprocess
+
+from charmhelpers.core import hookenv, host
+from charms.reactive import is_state
+from jujubigdata.utils import run_as
+
+
+if not is_state('kafka.started'):
+    kafkautils.fail('Kafka service not yet ready')
+
+
+# Grab the business
+topic_name = hookenv.action_get('topic')
+topic_partitions = hookenv.action_get('partitions')
+topic_replication = hookenv.action_get('replication')
+
+# Create the topic if kafka is running
+if host.service_available('kafka-server') and 
host.service_running('kafka-server'):
+    zookeepers = kafkautils.get_zookeepers()
+    try:
+        output = run_as('kafka', 'kafka-topics.sh',
+                        '--zookeeper', zookeepers, '--create',
+                        '--topic', topic_name,
+                        '--partitions', topic_partitions,
+                        '--replication-factor', topic_replication,
+                        capture_output=True)
+    except subprocess.CalledProcessError as e:
+        kafkautils.fail('Kafka command failed', e.output)
+    else:
+        hookenv.action_set({'output': output})
+else:
+    kafkautils.fail('Kafka service is not running', 'Please start 
kafka-server')

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/actions/kafkautils.py
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/actions/kafkautils.py 
b/bigtop-packages/src/charm/kafka/layer-kafka/actions/kafkautils.py
new file mode 100644
index 0000000..52e5299
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/actions/kafkautils.py
@@ -0,0 +1,38 @@
+# 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 re
+import sys
+
+from charmhelpers.core import hookenv
+
+
+def fail(msg, output='<No output>'):
+    hookenv.action_set({'output': output})
+    hookenv.action_fail(msg)
+    sys.exit()
+
+
+def get_zookeepers():
+    cfg = '/etc/kafka/conf/server.properties'
+    print(cfg)
+    file = open(cfg, "r")
+
+    for line in file:
+        if re.search('^zookeeper.connect=.*', line):
+            zks = line.split("=")[1].strip('\n')
+            return zks
+
+    return None

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/actions/list-topics
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/actions/list-topics 
b/bigtop-packages/src/charm/kafka/layer-kafka/actions/list-topics
new file mode 100755
index 0000000..0ef3a3f
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/actions/list-topics
@@ -0,0 +1,42 @@
+#!/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 kafkautils
+import subprocess
+
+from charmhelpers.core import hookenv, host
+from charms.reactive import is_state
+from jujubigdata.utils import run_as
+
+
+if not is_state('kafka.started'):
+    kafkautils.fail('Kafka service not yet ready')
+
+
+# List topics if kafka is running
+if host.service_available('kafka-server') and 
host.service_running('kafka-server'):
+    zookeepers = kafkautils.get_zookeepers()
+    try:
+        output = run_as('kafka', '/usr/lib/kafka/bin/kafka-topics.sh',
+                        '--zookeeper', zookeepers, '--list',
+                        capture_output=True)
+    except subprocess.CalledProcessError as e:
+        kafkautils.fail('Kafka command failed', e.output)
+    else:
+        hookenv.action_set({'output': output})
+else:
+    kafkautils.fail('Kafka service is not running')

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/actions/list-zks
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/actions/list-zks 
b/bigtop-packages/src/charm/kafka/layer-kafka/actions/list-zks
new file mode 100755
index 0000000..517d60c
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/actions/list-zks
@@ -0,0 +1,36 @@
+#!/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 kafkautils
+
+from charmhelpers.core import hookenv, host
+from charms.reactive import is_state
+
+
+if not is_state('kafka.started'):
+    kafkautils.fail('Kafka service not yet ready', 'Please deploy kafka and 
required relations')
+
+
+# List zookeepers if kafka is running
+if host.service_available('kafka-server') and 
host.service_running('kafka-server'):
+    zookeepers = kafkautils.get_zookeepers()
+    if zookeepers:
+        hookenv.action_set({'output': zookeepers})
+    else:
+        kafkautils.fail('No zookeeper.connect string found', 'Please relate 
kafka to zookeeper')
+else:
+    kafkautils.fail('Kafka service is not running', 'Please start 
kafka-server')

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/actions/read-topic
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/actions/read-topic 
b/bigtop-packages/src/charm/kafka/layer-kafka/actions/read-topic
new file mode 100755
index 0000000..b385f66
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/actions/read-topic
@@ -0,0 +1,55 @@
+#!/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 sys
+sys.path.append('lib')
+
+import kafkautils
+import subprocess
+
+from charmhelpers.core import hookenv, host
+from charms.layer.apache_bigtop_base import get_layer_opts
+from charms.reactive import is_state
+from jujubigdata import utils
+
+
+if not is_state('kafka.started'):
+    kafkautils.fail('Kafka service not yet ready')
+
+
+# Grab the business
+topic_name = hookenv.action_get('topic')
+topic_partition = hookenv.action_get('partition')
+
+# Read the topic if kafka is running
+if host.service_available('kafka-server') and 
host.service_running('kafka-server'):
+    host = subprocess.check_output(['hostname', '-s']).decode('utf8').strip()
+    port = get_layer_opts().port('kafka')
+    zookeepers = kafkautils.get_zookeepers()
+    try:
+        output = utils.run_as('kafka', 
'/usr/lib/kafka/bin/kafka-simple-consumer-shell.sh',
+                              '--broker-list', '{}:{}'.format(host, port),
+                              '--topic', topic_name,
+                              '--partition', topic_partition,
+                              '--no-wait-at-logend',
+                              capture_output=True)
+    except subprocess.CalledProcessError as e:
+        kafkautils.fail('Kafka command failed', e.output)
+    else:
+        hookenv.action_set({'output': output})
+else:
+    kafkautils.fail('Kafka service is not running')

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/actions/smoke-test
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/actions/smoke-test 
b/bigtop-packages/src/charm/kafka/layer-kafka/actions/smoke-test
new file mode 100755
index 0000000..5c1435a
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/actions/smoke-test
@@ -0,0 +1,64 @@
+#!/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 kafkautils
+import subprocess
+
+from charmhelpers.core import hookenv, host
+from charms.reactive import is_state
+from jujubigdata.utils import run_as
+
+
+if not is_state('kafka.started'):
+    kafkautils.fail('Kafka service not yet ready')
+
+
+# Define smoke test params
+topic_name = "smoketest"
+topic_partitions = 1
+topic_replication = 1
+
+# Smoke only when kafka is running
+if host.service_available('kafka-server') and 
host.service_running('kafka-server'):
+    # List ZKs
+    zookeepers = kafkautils.get_zookeepers()
+    if not zookeepers:
+        kafkautils.fail('No zookeeper.connect string found')
+
+    # Create a topic
+    try:
+        output = run_as('kafka', 'kafka-topics.sh',
+                        '--zookeeper', zookeepers, '--create',
+                        '--topic', topic_name,
+                        '--partitions', topic_partitions,
+                        '--replication-factor', topic_replication,
+                        capture_output=True)
+    except subprocess.CalledProcessError as e:
+        kafkautils.fail('Kafka command failed', e.output)
+
+    # List topics
+    try:
+        output = run_as('kafka', 'kafka-topics.sh',
+                        '--zookeeper', zookeepers, '--list',
+                        capture_output=True)
+    except subprocess.CalledProcessError as e:
+        kafkautils.fail('Kafka command failed', e.output)
+
+    # If we haven't failed yet, we passed
+    hookenv.action_set({'outcome': 'success'})
+else:
+    kafkautils.fail('Kafka service is not running')

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/actions/write-topic
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/actions/write-topic 
b/bigtop-packages/src/charm/kafka/layer-kafka/actions/write-topic
new file mode 100755
index 0000000..b879b65
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/actions/write-topic
@@ -0,0 +1,54 @@
+#!/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 sys
+sys.path.append('lib')
+
+import kafkautils
+import subprocess
+
+from charmhelpers.core import hookenv, host
+from charms.layer.apache_bigtop_base import get_layer_opts
+from charms.reactive import is_state
+from jujubigdata import utils
+
+
+if not is_state('kafka.started'):
+    kafkautils.fail('Kafka service not yet ready')
+
+
+# Grab the business
+topic_name = hookenv.action_get('topic')
+data = hookenv.action_get('data')
+
+# Write to the topic if kafka is running
+if host.service_available('kafka-server') and 
host.service_running('kafka-server'):
+    host = subprocess.check_output(['hostname', '-s']).decode('utf8').strip()
+    port = get_layer_opts().port('kafka')
+    zookeepers = kafkautils.get_zookeepers()
+    try:
+        output = utils.run_as('kafka', 'kafka-console-producer.sh',
+                              '--broker-list', '{}:{}'.format(host, port),
+                              '--topic', topic_name,
+                              capture_output=True,
+                              input=bytes(data, 'UTF-8'))
+    except subprocess.CalledProcessError as e:
+        kafkautils.fail('Kafka command failed', e.output)
+    else:
+        hookenv.action_set({'output': output})
+else:
+    kafkautils.fail('Kafka service is not running')

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/config.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/config.yaml 
b/bigtop-packages/src/charm/kafka/layer-kafka/config.yaml
new file mode 100644
index 0000000..c8e723a
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/config.yaml
@@ -0,0 +1,10 @@
+options:
+  network_interface:
+    default: ""
+    type: string
+    description: |
+      Network interface to bind Kafka to. Defaults to accepting
+      connections on all interfaces. Accepts either the name of an
+      interface (e.g., 'eth0'), or a CIDR range. If the latter, we\'ll
+      bind to the first interface that we find with an IP address in
+      that range.

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/copyright
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/copyright 
b/bigtop-packages/src/charm/kafka/layer-kafka/copyright
new file mode 100644
index 0000000..e900b97
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/copyright
@@ -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.

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/icon.svg
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/icon.svg 
b/bigtop-packages/src/charm/kafka/layer-kafka/icon.svg
new file mode 100644
index 0000000..1564f99
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/icon.svg
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="75pt"
+   height="117pt"
+   viewBox="0 0 75 117"
+   version="1.1"
+   id="svg3201"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="icon.svg">
+  <metadata
+     id="metadata3221">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs3219" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1236"
+     inkscape:window-height="847"
+     id="namedview3217"
+     showgrid="false"
+     inkscape:zoom="4.5641627"
+     inkscape:cx="35.369926"
+     inkscape:cy="80.60281"
+     inkscape:window-x="397"
+     inkscape:window-y="78"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="layer1" />
+  <path
+     d="m 0,0.04322 76.8,0 0,76.8 -76.8,0 0,-76.8 z"
+     id="path3203"
+     style="opacity:0;fill:#fffffe;fill-opacity:0.94977172"
+     inkscape:connector-curvature="0" />
+  <path
+     d="M 13.76256,3.6403482 C 20.992,-0.05524154 33.024,1.9665021 
36.42368,7.4934765 40.37632,12.889169 34.816,19.735528 25.77408,20.917066 c 
0,1.732924 0.01024,3.459282 0,5.185642 4.6592,0.708923 8.87808,2.258051 
12.05248,4.56205 2.21184,-0.892717 4.526079,-1.700102 6.72768,-2.605948 
-0.59392,-3.209847 -0.59392,-6.833231 3.10272,-9.340718 5.77536,-4.719589 
18.51392,-4.476718 23.72608,0.531692 4.751359,4.076308 3.266559,10.450051 
-3.21536,13.403898 -6.03136,3.012923 -15.28832,2.271179 -20.41856,-1.266872 
-2.16064,0.833641 -4.43392,1.549128 -6.52288,2.454975 -0.307201,1.122461 
0.45056,2.251486 0.6144,3.373948 0.70656,1.956103 -0.78848,3.859693 
-0.68608,5.809231 2.11968,0.951795 4.47488,1.68041 6.79936,2.415589 
6.36928,-4.653948 19.445759,-3.918768 24.10496,1.575386 5.12,5.087179 
0.62464,12.668717 -8.52992,14.211281 -6.85056,1.273436 -14.7968,-0.794256 
-17.84832,-4.988717 -1.81248,-2.23836 -1.69984,-4.857437 -0.8704,-7.259898 
-2.29376,-0.951795 -4.67968,-1.805128 -6.97344,-2.756923 -
 3.16416,2.284307 -7.34208,3.885949 -12.01152,4.516102 -0.06144,1.746051 
-0.04096,3.498667 -0.0512,5.251283 4.78208,0.728615 8.98048,2.921025 
10.81344,5.868307 2.75456,4.299487 0.07168,9.872411 -6.51264,12.025436 
-6.2464,2.422154 -15.06304,1.234052 -19.33312,-2.546872 -4.89472,-3.846564 
-3.84,-10.095589 2.17088,-13.213539 1.91488,-1.102768 4.41344,-1.634461 
6.78912,-2.19241 0.03072,-1.68041 0.04096,-3.367384 0.07168,-5.047794 C 
14.98112,50.009169 10.21952,48.597887 7.26016,45.919734 1.44384,41.180451 
2.2528,33.671118 9.20576,29.581682 c 2.78528,-1.93641 6.77888,-2.776616 
10.567679,-3.577436 -0.03072,-1.68041 -0.04096,-3.360821 -0.07168,-5.034667 C 
15.59552,20.181887 11.60192,18.678708 9.5641599,16.164655 5.82656,12.081784 
7.69024,6.3644508 13.76256,3.6403482 z"
+     id="path3205"
+     inkscape:connector-curvature="0"
+     style="fill:#201f1f" />
+  <path
+     d="M 18.95424,7.4869124 C 23.58272,5.3338867 30.53568,8.2614765 
29.85984,11.799528 29.48096,14.924041 23.5008,17.182092 19.2,15.462297 
14.42816,13.926297 14.27456,9.1410662 18.95424,7.4869124 z"
+     id="path3207"
+     inkscape:connector-curvature="0"
+     style="fill:#fffffe" />
+  <path
+     d="m 55.76704,20.844861 c 4.51584,-1.673846 10.91584,0.761436 
10.56768,4.135384 0.235519,3.649642 -7.33184,5.96677 -11.64288,3.557744 
-4.106241,-1.897025 -3.38944,-6.255589 1.0752,-7.693128 z"
+     id="path3209"
+     inkscape:connector-curvature="0"
+     style="fill:#fffffe" />
+  <path
+     d="m 18.78016,32.305784 c 7.311359,-2.303999 16.35328,2.829129 
13.8752,7.75877 -1.44384,4.555487 -11.17184,6.721641 -16.5376,3.472409 
-6.05184,-2.894768 -4.352,-9.570461 2.6624,-11.231179 z"
+     id="path3211"
+     inkscape:connector-curvature="0"
+     style="fill:#fffffe" />
+  <path
+     d="m 54.69184,48.348451 c 4.66944,-2.619077 12.759039,0.347897 
11.601919,4.253538 -0.409599,3.629949 -8.345599,5.218462 -12.103679,2.651898 
-3.2256,-1.772308 -2.8672,-5.303795 0.50176,-6.905436 z"
+     id="path3213"
+     inkscape:connector-curvature="0"
+     style="fill:#fffffe" />
+  <path
+     d="m 20.67456,61.030297 c 5.10976,-1.13559 10.78272,2.566565 
8.82688,5.809231 -1.269761,3.190153 -8.48896,4.483282 -11.84768,1.857641 
-4.06528,-2.19241 -2.03776,-6.872615 3.0208,-7.666872 z"
+     id="path3215"
+     inkscape:connector-curvature="0"
+     style="fill:#fffffe" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer1"
+     inkscape:label="Alpha"
+     style="opacity:1" />
+</svg>

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/layer.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/layer.yaml 
b/bigtop-packages/src/charm/kafka/layer-kafka/layer.yaml
new file mode 100644
index 0000000..daa7f58
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/layer.yaml
@@ -0,0 +1,20 @@
+repo: [email protected]:juju-solutions/layer-apache-bigtop-kafka.git
+includes:
+  - 'layer:apache-bigtop-base'
+  - 'interface:zookeeper'
+  - 'interface:kafka'
+options:
+  apache-bigtop-base:
+    groups:
+      - kafka
+    users:
+      kafka:
+        groups: ['kafka']
+    ports:
+      # Ports that need to be exposed, overridden, or manually specified.
+      # Only expose ports serving a UI or external API (i.e., namenode and
+      # resourcemanager).  Communication among units within the cluster does
+      # not need ports to be explicitly opened.
+      kafka:
+        port: 9092
+        exposed_on: 'kafka'

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/lib/charms/layer/bigtop_kafka.py
----------------------------------------------------------------------
diff --git 
a/bigtop-packages/src/charm/kafka/layer-kafka/lib/charms/layer/bigtop_kafka.py 
b/bigtop-packages/src/charm/kafka/layer-kafka/lib/charms/layer/bigtop_kafka.py
new file mode 100755
index 0000000..c2f1fc4
--- /dev/null
+++ 
b/bigtop-packages/src/charm/kafka/layer-kafka/lib/charms/layer/bigtop_kafka.py
@@ -0,0 +1,88 @@
+# 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 os
+from charmhelpers.core import hookenv
+from charmhelpers.core import host
+from jujubigdata import utils
+from charms.layer.apache_bigtop_base import Bigtop
+from charms import layer
+from subprocess import check_output
+
+
+class Kafka(object):
+    """
+    This class manages Kafka.
+    """
+    def __init__(self):
+        self.dist_config = utils.DistConfig(
+            data=layer.options('apache-bigtop-base'))
+
+    def open_ports(self):
+        for port in self.dist_config.exposed_ports('kafka'):
+            hookenv.open_port(port)
+
+    def close_ports(self):
+        for port in self.dist_config.exposed_ports('kafka'):
+            hookenv.close_port(port)
+
+    def configure_kafka(self, zk_units, network_interface=None):
+        # Get ip:port data from our connected zookeepers
+        zks = []
+        for unit in zk_units:
+            ip = utils.resolve_private_address(unit['host'])
+            zks.append("%s:%s" % (ip, unit['port']))
+        zks.sort()
+        zk_connect = ",".join(zks)
+        service, unit_num = os.environ['JUJU_UNIT_NAME'].split('/', 1)
+        kafka_port = self.dist_config.port('kafka')
+
+        roles = ['kafka-server']
+        override = {
+            'kafka::server::broker_id': unit_num,
+            'kafka::server::port': kafka_port,
+            'kafka::server::zookeeper_connection_string': zk_connect,
+        }
+        if network_interface:
+            ip = Bigtop().get_ip_for_interface(network_interface)
+            override['kafka::server::bind_addr'] = ip
+
+        bigtop = Bigtop()
+        bigtop.render_site_yaml(roles=roles, overrides=override)
+        bigtop.trigger_puppet()
+        self.set_advertise()
+        self.restart()
+
+    def restart(self):
+        self.stop()
+        self.start()
+
+    def start(self):
+        host.service_start('kafka-server')
+
+    def stop(self):
+        host.service_stop('kafka-server')
+
+    def set_advertise(self):
+        short_host = check_output(['hostname', '-s']).decode('utf8').strip()
+
+        # Configure server.properties
+        # NB: We set the advertised.host.name below to our short hostname
+        # to kafka (admin will still have to expose kafka and ensure the
+        # external client can resolve the short hostname to our public ip).
+        kafka_server_conf = '/etc/kafka/conf/server.properties'
+        utils.re_edit_in_place(kafka_server_conf, {
+            r'^#?advertised.host.name=.*': 'advertised.host.name=%s' % 
short_host,
+        })

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/metadata.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/metadata.yaml 
b/bigtop-packages/src/charm/kafka/layer-kafka/metadata.yaml
new file mode 100644
index 0000000..5a11394
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/metadata.yaml
@@ -0,0 +1,30 @@
+name: kafka
+summary: High-performance distributed messaging system from Apache Bigtop
+maintainer: Juju Big Data <[email protected]>
+description: |
+  Fast
+  A single Kafka broker can handle hundreds of megabytes of reads and writes 
per
+  second from thousands of clients.
+
+  Scalable
+  Kafka is designed to allow a single cluster to serve as the central data
+  backbone for a large organization. It can be elastically and transparently
+  expanded without downtime. Data streams are partitioned and spread over a
+  cluster of machines to allow data streams larger than the capability of any
+  single machine and to allow clusters of co-ordinated consumers.
+
+  Durable
+  Messages are persisted on disk and replicated within the cluster to prevent
+  data loss. Each broker can handle terabytes of messages without performance
+  impact.
+
+  Distributed by Design
+  Kafka has a modern cluster-centric design that offers strong durability and
+  fault-tolerance guarantees.
+tags: []
+provides:
+  client:
+    interface: kafka
+requires:
+  zookeeper:
+    interface: zookeeper

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/reactive/kafka.py
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/reactive/kafka.py 
b/bigtop-packages/src/charm/kafka/layer-kafka/reactive/kafka.py
new file mode 100644
index 0000000..6e7d325
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/reactive/kafka.py
@@ -0,0 +1,89 @@
+# 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 charmhelpers.core import hookenv
+from charms.layer.apache_bigtop_base import get_layer_opts
+from charms.layer.bigtop_kafka import Kafka
+from charms.reactive import set_state, remove_state, when, when_not
+from charms.reactive.helpers import data_changed
+
+
+@when('bigtop.available')
+@when_not('zookeeper.joined')
+def waiting_for_zookeeper():
+    hookenv.status_set('blocked', 'waiting for relation to zookeeper')
+
+
+@when('bigtop.available', 'zookeeper.joined')
+@when_not('kafka.started', 'zookeeper.ready')
+def waiting_for_zookeeper_ready(zk):
+    hookenv.status_set('waiting', 'waiting for zookeeper to become ready')
+
+
+@when('bigtop.available', 'zookeeper.ready')
+@when_not('kafka.started')
+def configure_kafka(zk):
+    hookenv.status_set('maintenance', 'setting up kafka')
+    data_changed(  # Prime data changed for network interface
+        'kafka.network_interface', hookenv.config().get('network_interface'))
+    kafka = Kafka()
+    zks = zk.zookeepers()
+    kafka.configure_kafka(zks)
+    kafka.open_ports()
+    set_state('kafka.started')
+    hookenv.status_set('active', 'ready')
+
+
+@when('kafka.started', 'zookeeper.ready')
+def configure_kafka_zookeepers(zk):
+    """Configure ready zookeepers and restart kafka if needed.
+
+    As zks come and go, server.properties will be updated. When that file
+    changes, restart Kafka and set appropriate status messages.
+
+    This method also handles the restart if our network_interface
+    config has changed.
+
+    """
+    zks = zk.zookeepers()
+    network_interface = hookenv.config().get('network_interface')
+    if not data_changed('zookeepers', zks) and not data_changed(
+            'kafka.network_interface', network_interface):
+        return
+
+    hookenv.log('Checking Zookeeper configuration')
+    hookenv.status_set('maintenance', 'updating zookeeper instances')
+    kafka = Kafka()
+    kafka.configure_kafka(zks, network_interface)
+    hookenv.status_set('active', 'ready')
+
+
+@when('kafka.started')
+@when_not('zookeeper.ready')
+def stop_kafka_waiting_for_zookeeper_ready():
+    hookenv.status_set('maintenance', 'zookeeper not ready, stopping kafka')
+    kafka = Kafka()
+    kafka.close_ports()
+    kafka.stop()
+    remove_state('kafka.started')
+    hookenv.status_set('waiting', 'waiting for zookeeper to become ready')
+
+
+@when('client.joined', 'zookeeper.ready')
+def serve_client(client, zookeeper):
+    kafka_port = get_layer_opts().port('kafka')
+    client.send_port(kafka_port)
+    client.send_zookeepers(zookeeper.zookeepers())
+    hookenv.log('Sent Kafka configuration to client')

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/tests/01-deploy.py
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/tests/01-deploy.py 
b/bigtop-packages/src/charm/kafka/layer-kafka/tests/01-deploy.py
new file mode 100755
index 0000000..af11f9c
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/tests/01-deploy.py
@@ -0,0 +1,58 @@
+#!/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 Kafka.
+    """
+    @classmethod
+    def setUpClass(cls):
+        cls.d = amulet.Deployment(series='trusty')
+        cls.d.add('kafka', 'kafka')
+        cls.d.add('openjdk', 'openjdk')
+        cls.d.add('zk', 'zookeeper')
+
+        cls.d.configure('openjdk', {'java-type': 'jdk',
+                                    'java-major': '8'})
+
+        cls.d.relate('kafka:zookeeper', 'zk:zookeeper')
+        cls.d.relate('kafka:java', 'openjdk:java')
+        try:
+            cls.d.relate('zk:java', 'openjdk:java')
+        except ValueError:
+            # No need to related older versions of the zookeeper charm
+            # to java.
+            pass
+
+        cls.d.setup(timeout=900)
+        cls.d.sentry.wait_for_messages({'kafka': 'ready'}, timeout=1800)
+        cls.kafka = cls.d.sentry['kafka'][0]
+
+    def test_deploy(self):
+        """
+        Simple test to make sure the Kafka java process is running.
+        """
+        output, retcode = self.kafka.run("pgrep -a java")
+        assert 'Kafka' in output, "Kafka daemon is not started"
+
+
+if __name__ == '__main__':
+    unittest.main()

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/tests/02-smoke-test.py
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/kafka/layer-kafka/tests/02-smoke-test.py 
b/bigtop-packages/src/charm/kafka/layer-kafka/tests/02-smoke-test.py
new file mode 100755
index 0000000..330cde9
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/tests/02-smoke-test.py
@@ -0,0 +1,59 @@
+#!/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):
+    """
+    Smoke test of Apache Kafka.
+    """
+    @classmethod
+    def setUpClass(cls):
+        cls.d = amulet.Deployment(series='trusty')
+        cls.d.add('kafka', 'kafka')
+        cls.d.add('openjdk', 'openjdk')
+        cls.d.add('zk', 'zookeeper')
+
+        cls.d.configure('openjdk', {'java-type': 'jdk',
+                                    'java-major': '8'})
+
+        cls.d.relate('kafka:zookeeper', 'zk:zookeeper')
+        cls.d.relate('kafka:java', 'openjdk:java')
+        try:
+            cls.d.relate('zk:java', 'openjdk:java')
+        except ValueError:
+            # No need to related older versions of the zookeeper charm
+            # to java.
+            pass
+
+        cls.d.setup(timeout=900)
+        cls.d.sentry.wait_for_messages({'kafka': 'ready'}, timeout=1800)
+        cls.kafka = cls.d.sentry['kafka'][0]
+
+    def test_kafka(self):
+        """
+        Validate Kafka by running the smoke-test action.
+        """
+        smk_uuid = self.kafka.action_do("smoke-test")
+        output = self.d.action_fetch(smk_uuid, full_output=True)
+        assert "completed" in output['status']
+
+
+if __name__ == '__main__':
+    unittest.main()

http://git-wip-us.apache.org/repos/asf/bigtop/blob/f482fd9f/bigtop-packages/src/charm/kafka/layer-kafka/tests/10-config-changed.py
----------------------------------------------------------------------
diff --git 
a/bigtop-packages/src/charm/kafka/layer-kafka/tests/10-config-changed.py 
b/bigtop-packages/src/charm/kafka/layer-kafka/tests/10-config-changed.py
new file mode 100755
index 0000000..dd20c53
--- /dev/null
+++ b/bigtop-packages/src/charm/kafka/layer-kafka/tests/10-config-changed.py
@@ -0,0 +1,98 @@
+#!/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
+import re
+
+
+class TestConfigChanged(unittest.TestCase):
+    """
+    Test to verify that we update network interface bindings successfully.
+
+    """
+    @classmethod
+    def setUpClass(cls):
+        cls.d = amulet.Deployment(series='trusty')
+        cls.d.log.debug("foo!")
+        cls.d.add('kafka', 'kafka')
+        cls.d.add('openjdk', 'openjdk')
+        cls.d.add('zk', 'zookeeper')
+
+        cls.d.configure('openjdk', {'java-type': 'jdk',
+                                    'java-major': '8'})
+
+        cls.d.relate('kafka:zookeeper', 'zk:zookeeper')
+        cls.d.relate('kafka:java', 'openjdk:java')
+        try:
+            cls.d.relate('zk:java', 'openjdk:java')
+        except ValueError:
+            # No need to related older versions of the zookeeper charm
+            # to java.
+            pass
+
+        cls.d.setup(timeout=900)
+        cls.d.sentry.wait_for_messages({'kafka': 'ready'}, timeout=1800)
+        cls.kafka = cls.d.sentry['kafka'][0]
+
+    def test_bind_network_interface(self):
+        """
+        Test to verify that we update network interface bindings successfully.
+
+        """
+        self.d.configure('kafka', {'network_interface': 'eth0'})
+        self.d.sentry.wait_for_messages({'kafka': 'updating zookeeper 
instances'}, timeout=600)
+
+        self.d.sentry.wait_for_messages({'kafka': 'ready'}, timeout=600)
+        ret = self.kafka.run(
+            'grep host.name /etc/kafka/conf/server.properties')[0]
+        # Correct line should start with host.name (no comment hash
+        # mark), followed by an equals sign and something that looks
+        # like an IP address (we aren't too strict about it being a
+        # valid ip address.)
+        matcher = 
re.compile("^host\.name=\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.*")
+
+        self.assertTrue('host.name' in ret)
+        self.assertTrue(matcher.match(ret))
+
+        # Verify that smoke tests still run
+        smk_uuid = self.kafka.action_do("smoke-test")
+        output = self.d.action_fetch(smk_uuid, full_output=True)
+        assert "completed" in output['status']
+
+    def test_reset_network_interface(self):
+        """
+        Verify that we can reset the network interface to 0.
+
+        """
+        self.d.configure('kafka', {'network_interface': '0.0.0.0'})
+        self.d.sentry.wait_for_messages({'kafka': 'updating zookeeper 
instances'}, timeout=600)
+        self.d.sentry.wait_for_messages({'kafka': 'ready'}, timeout=600)
+        ret = self.kafka.run(
+            'grep host.name /etc/kafka/conf/server.properties')[0]
+
+        matcher = re.compile("^host\.name=0\.0\.0\.0.*")
+        self.assertTrue(matcher.match(ret))
+
+        # Verify that smoke tests still run
+        smk_uuid = self.kafka.action_do("smoke-test")
+        output = self.d.action_fetch(smk_uuid, full_output=True)
+        assert "completed" in output['status']
+
+
+if __name__ == '__main__':
+    unittest.main()

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

Reply via email to