Repository: bigtop Updated Branches: refs/heads/master cf19b047f -> f4515e8c6
BIGTOP-2575: zk charm test updates (closes #156) 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/f4515e8c Tree: http://git-wip-us.apache.org/repos/asf/bigtop/tree/f4515e8c Diff: http://git-wip-us.apache.org/repos/asf/bigtop/diff/f4515e8c Branch: refs/heads/master Commit: f4515e8c6d27a158449141264be066e0c43b2772 Parents: cf19b04 Author: Kevin W Monroe <[email protected]> Authored: Thu Oct 27 21:55:18 2016 +0000 Committer: Kevin W Monroe <[email protected]> Committed: Wed Nov 16 14:18:00 2016 -0600 ---------------------------------------------------------------------- .../charm/zookeeper/layer-zookeeper/README.md | 4 +- .../zookeeper/layer-zookeeper/actions/restart | 7 +- .../layer-zookeeper/actions/smoke-test | 6 +- .../lib/charms/layer/bigtop_zookeeper.py | 188 +++++++++++++++++++ .../lib/charms/layer/zookeeper.py | 188 ------------------- .../layer-zookeeper/reactive/zookeeper.py | 6 +- .../zookeeper/layer-zookeeper/tests/01-deploy | 74 -------- .../layer-zookeeper/tests/01-deploy-smoke.py | 73 +++++++ .../layer-zookeeper/tests/10-bind-address | 115 ------------ .../layer-zookeeper/tests/10-bind-address.py | 109 +++++++++++ 10 files changed, 384 insertions(+), 386 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bigtop/blob/f4515e8c/bigtop-packages/src/charm/zookeeper/layer-zookeeper/README.md ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/README.md b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/README.md index 69aa650..af9f449 100644 --- a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/README.md +++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/README.md @@ -153,9 +153,9 @@ The Zookeeper nodes will automatically perform a rolling restart to update the Zookeeper quorum without losing any jobs in progress. Once the rolling restart has completed, all of the Zookeeper nodes should report the following status: - ready (n zk nodes) + ready (n units) -(Where 'n' is the total number of Zookeeper nodes in your quorum.) +(Where 'n' is the total number of Zookeeper units in the quorum.) # Integrating http://git-wip-us.apache.org/repos/asf/bigtop/blob/f4515e8c/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/restart ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/restart b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/restart index 4dcf6ae..7bb5136 100755 --- a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/restart +++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/restart @@ -18,14 +18,15 @@ import sys sys.path.append('lib') # Add our helpers to our path. -from charmhelpers.core import hookenv -from charms.layer.zookeeper import Zookeeper -from charms.reactive import is_state +from charmhelpers.core import hookenv # noqa: E402 +from charms.layer.bigtop_zookeeper import Zookeeper # noqa: E402 +from charms.reactive import is_state # noqa: E402 LOG = hookenv.log LOG("starting restart handler.") + def main(): if not is_state('zookeeper.started'): hookenv.action_fail('Cannot restart: Zookeeper has not yet started!') http://git-wip-us.apache.org/repos/asf/bigtop/blob/f4515e8c/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/smoke-test ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/smoke-test b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/smoke-test index 37264cf..6481462 100755 --- a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/smoke-test +++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/smoke-test @@ -18,9 +18,9 @@ import sys sys.path.append('lib') -from charmhelpers.core import hookenv -from charms.layer.apache_bigtop_base import Bigtop -from charms.reactive import is_state +from charmhelpers.core import hookenv # noqa: E402 +from charms.layer.apache_bigtop_base import Bigtop # noqa: E402 +from charms.reactive import is_state # noqa: E402 def fail(msg, output=None): http://git-wip-us.apache.org/repos/asf/bigtop/blob/f4515e8c/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/bigtop_zookeeper.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/bigtop_zookeeper.py b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/bigtop_zookeeper.py new file mode 100644 index 0000000..5f1695c --- /dev/null +++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/bigtop_zookeeper.py @@ -0,0 +1,188 @@ +# 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 subprocess + +from charmhelpers.core import host +from charmhelpers.core.hookenv import (open_port, close_port, log, + unit_private_ip, local_unit, config) +from charms import layer +from charms.layer.apache_bigtop_base import Bigtop +from charms.reactive.relations import RelationBase +from jujubigdata.utils import DistConfig + + +def format_node(unit, node_ip): + ''' + Given a juju unit name and an ip address, return a tuple + containing an id and formatted ip string suitable for passing to + puppet, which will write it out to zoo.cfg. + + ''' + return (unit.split("/")[1], "{ip}:2888:3888".format(ip=node_ip)) + + +class Zookeeper(object): + ''' + Utility class for managing Zookeeper tasks like configuration, start, + stop, and adding and removing nodes. + + ''' + def __init__(self, dist_config=None): + self._dist_config = dist_config or DistConfig( + data=layer.options('apache-bigtop-base')) + + self._roles = ['zookeeper-server', 'zookeeper-client'] + self._hosts = {} + + def is_zk_leader(self): + ''' + Attempt to determine whether this node is the Zookeeper leader. + + Note that Zookeeper tracks leadership independently of juju, + and that this command can fail, depending on the state that + the Zookeeper node is in when we attempt to run it. + + ''' + try: + status = subprocess.check_output( + ["/usr/lib/zookeeper/bin/zkServer.sh", "status"]) + return "leader" in status.decode('utf-8') + except Exception: + log( + "Unable to determine whether this node is the Zookeeper leader.", + level="WARN" + ) + return False + + def read_peers(self): + ''' + Fetch the list of peers available. + + The first item in this list should always be the node that + this code is executing on. + + ''' + # A Zookeeper node likes to be first on the list. + nodes = [(local_unit(), unit_private_ip())] + # Get the list of peers + zkpeer = RelationBase.from_state('zkpeer.joined') + if zkpeer: + nodes.extend(sorted(zkpeer.get_nodes())) + nodes = [format_node(*node) for node in nodes] + return nodes + + def sort_peers(self, zkpeer): + ''' + Return peers, sorted in an order suitable for performing a rolling + restart. + + ''' + peers = self.read_peers() + leader = zkpeer.find_zk_leader() + peers.sort(key=lambda x: x[1] == leader) + + return peers + + @property + def dist_config(self): + ''' + Charm level config. + + ''' + return self._dist_config + + @property + def _override(self): + ''' + Return a dict of keys and values that will override puppet's + defaults. + + ''' + override = { + "hadoop_zookeeper::server::myid": local_unit().split("/")[1], + "hadoop_zookeeper::server::ensemble": self.read_peers() + } + network_interface = config().get('network_interface') + if network_interface: + key = "hadoop_zookeeper::server::client_bind_addr" + override[key] = Bigtop().get_ip_for_interface(network_interface) + + return override + + def install(self, nodes=None): + ''' + Write out the config, then run puppet. + + After this runs, we should have a configured and running service. + + ''' + bigtop = Bigtop() + log("Rendering site yaml ''with overrides: {}".format(self._override)) + bigtop.render_site_yaml(self._hosts, self._roles, self._override) + bigtop.trigger_puppet() + if self.is_zk_leader(): + zkpeer = RelationBase.from_state('zkpeer.joined') + zkpeer.set_zk_leader() + + def start(self): + ''' + Request that our service start. Normally, puppet will handle this + for us. + + ''' + host.service_start('zookeeper-server') + + def stop(self): + ''' + Stop Zookeeper. + + ''' + host.service_stop('zookeeper-server') + + def open_ports(self): + ''' + Expose the ports in the configuration to the outside world. + + ''' + for port in self.dist_config.exposed_ports('zookeeper'): + open_port(port) + + def close_ports(self): + ''' + Close off communication from the outside world. + + ''' + for port in self.dist_config.exposed_ports('zookeeper'): + close_port(port) + + def quorum_check(self): + ''' + Returns a string reporting the node count. Append a message + informing the user if the node count is too low for good quorum, + or is even (meaning that one of the nodes is redundant for + quorum). + + ''' + node_count = len(self.read_peers()) + if node_count == 1: + count_str = "{} unit".format(node_count) + else: + count_str = "{} units".format(node_count) + if node_count < 3: + return " ({}; less than 3 is suboptimal)".format(count_str) + if node_count % 2 == 0: + return " ({}; an even number is suboptimal)".format(count_str) + return "({})".format(count_str) http://git-wip-us.apache.org/repos/asf/bigtop/blob/f4515e8c/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/zookeeper.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/zookeeper.py b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/zookeeper.py deleted file mode 100644 index 4b4932b..0000000 --- a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/zookeeper.py +++ /dev/null @@ -1,188 +0,0 @@ -# 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 subprocess - -from charmhelpers.core import host -from charmhelpers.core.hookenv import (open_port, close_port, log, - unit_private_ip, local_unit, config) -from charms import layer -from charms.layer.apache_bigtop_base import Bigtop -from charms.reactive.relations import RelationBase -from jujubigdata.utils import DistConfig - - -def format_node(unit, node_ip): - ''' - Given a juju unit name and an ip address, return a tuple - containing an id and formatted ip string suitable for passing to - puppet, which will write it out to zoo.cfg. - - ''' - return (unit.split("/")[1], "{ip}:2888:3888".format(ip=node_ip)) - - -class Zookeeper(object): - ''' - Utility class for managing Zookeeper tasks like configuration, start, - stop, and adding and removing nodes. - - ''' - def __init__(self, dist_config=None): - self._dist_config = dist_config or DistConfig( - data=layer.options('apache-bigtop-base')) - - self._roles = ['zookeeper-server', 'zookeeper-client'] - self._hosts = {} - - def is_zk_leader(self): - ''' - Attempt to determine whether this node is the Zookeeper leader. - - Note that Zookeeper tracks leadership independently of juju, - and that this command can fail, depending on the state that - the Zookeeper node is in when we attempt to run it. - - ''' - try: - status = subprocess.check_output( - ["/usr/lib/zookeeper/bin/zkServer.sh", "status"]) - return "leader" in status.decode('utf-8') - except Exception: - log( - "Unable to determine whether this node is the Zookeeper leader.", - level="WARN" - ) - return False - - def read_peers(self): - ''' - Fetch the list of peers available. - - The first item in this list should always be the node that - this code is executing on. - - ''' - # A Zookeeper node likes to be first on the list. - nodes = [(local_unit(), unit_private_ip())] - # Get the list of peers - zkpeer = RelationBase.from_state('zkpeer.joined') - if zkpeer: - nodes.extend(sorted(zkpeer.get_nodes())) - nodes = [format_node(*node) for node in nodes] - return nodes - - def sort_peers(self, zkpeer): - ''' - Return peers, sorted in an order suitable for performing a rolling - restart. - - ''' - peers = self.read_peers() - leader = zkpeer.find_zk_leader() - peers.sort(key=lambda x: x[1] == leader) - - return peers - - @property - def dist_config(self): - ''' - Charm level config. - - ''' - return self._dist_config - - @property - def _override(self): - ''' - Return a dict of keys and values that will override puppet's - defaults. - - ''' - override = { - "hadoop_zookeeper::server::myid": local_unit().split("/")[1], - "hadoop_zookeeper::server::ensemble": self.read_peers() - } - network_interface = config().get('network_interface') - if network_interface: - key = "hadoop_zookeeper::server::client_bind_addr" - override[key] = Bigtop().get_ip_for_interface(network_interface) - - return override - - def install(self, nodes=None): - ''' - Write out the config, then run puppet. - - After this runs, we should have a configured and running service. - - ''' - bigtop = Bigtop() - log("Rendering site yaml ''with overrides: {}".format(self._override)) - bigtop.render_site_yaml(self._hosts, self._roles, self._override) - bigtop.trigger_puppet() - if self.is_zk_leader(): - zkpeer = RelationBase.from_state('zkpeer.joined') - zkpeer.set_zk_leader() - - def start(self): - ''' - Request that our service start. Normally, puppet will handle this - for us. - - ''' - host.service_start('zookeeper-server') - - def stop(self): - ''' - Stop Zookeeper. - - ''' - host.service_stop('zookeeper-server') - - def open_ports(self): - ''' - Expose the ports in the configuration to the outside world. - - ''' - for port in self.dist_config.exposed_ports('zookeeper'): - open_port(port) - - def close_ports(self): - ''' - Close off communication from the outside world. - - ''' - for port in self.dist_config.exposed_ports('zookeeper'): - close_port(port) - - def quorum_check(self): - ''' - Returns a string reporting the node count. Append a message - informing the user if the node count is too low for good quorum, - or is even (meaning that one of the nodes is redundant for - quorum). - - ''' - node_count = len(self.read_peers()) - if node_count == 1: - count_str = "{} zk node".format(node_count) - else: - count_str = "{} zk nodes".format(node_count) - if node_count < 3: - return " ({}; less than 3 nodes is suboptimal)".format(count_str) - if node_count % 2 == 0: - return " ({}; an even number is suboptimal)".format(count_str) - return "({})".format(count_str) http://git-wip-us.apache.org/repos/asf/bigtop/blob/f4515e8c/bigtop-packages/src/charm/zookeeper/layer-zookeeper/reactive/zookeeper.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/reactive/zookeeper.py b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/reactive/zookeeper.py index 825c219..a4c7773 100644 --- a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/reactive/zookeeper.py +++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/reactive/zookeeper.py @@ -16,7 +16,8 @@ import json import time from charmhelpers.core import hookenv -from charms.layer.zookeeper import Zookeeper +from charms.layer.apache_bigtop_base import get_package_version +from charms.layer.bigtop_zookeeper import Zookeeper from charms.leadership import leader_set, leader_get from charms.reactive import set_state, when, when_not, is_state from charms.reactive.helpers import data_changed @@ -44,6 +45,9 @@ def install_zookeeper(): set_state('zookeeper.installed') set_state('zookeeper.started') hookenv.status_set('active', 'ready {}'.format(zookeeper.quorum_check())) + # set app version string for juju status output + zoo_version = get_package_version('zookeeper') or 'unknown' + hookenv.application_version_set(zoo_version) def _restart_zookeeper(msg): http://git-wip-us.apache.org/repos/asf/bigtop/blob/f4515e8c/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy deleted file mode 100755 index 744a71f..0000000 --- a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/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 - -TIMEOUT = 1800 - - -class TestDeploy(unittest.TestCase): - """ - Deployment test for Apache Zookkepper quorum - """ - - @classmethod - def setUpClass(cls): - cls.d = amulet.Deployment(series='xenial') - - cls.d.add('zookeeper', charm='zookeeper', units=3) - - cls.d.setup(timeout=TIMEOUT) - cls.d.sentry.wait(timeout=TIMEOUT) - cls.unit = cls.d.sentry['zookeeper'][0] - - def test_deploy(self): - output, retcode = self.unit.run("pgrep -a java") - assert 'QuorumPeerMain' in output, "zookeeper QuorumPeerMain daemon is not started" - - def test_quorum(self): - ''' - Verify that our peers are talking to each other, and taking on - appropriate roles. - - ''' - self.assertEqual(3, len(self.d.sentry['zookeeper'])) - - # Verify that everything worked. - for unit in self.d.sentry['zookeeper']: - output, _ = unit.run( - "/usr/lib/zookeeper/bin/zkServer.sh status" - ) - # Unit should be a leader or follower - self.assertTrue("leader" in output or "follower" in output) - - def test_smoke(self): - """Validates Zookeeper using the Bigtop 'zookeeper' smoke test.""" - smk_uuids = [] - - for unit in self.d.sentry['zookeeper']: - smk_uuids.append(unit.action_do("smoke-test")) - - for smk_uuid in smk_uuids: - result = self.d.action_fetch(smk_uuid, full_output=True) - # zookeeper smoke-test sets outcome=success on success - if (result['outcome'] != "success"): - error = "Zookeeper smoke-test failed" - amulet.raise_status(amulet.FAIL, msg=error) - -if __name__ == '__main__': - unittest.main() http://git-wip-us.apache.org/repos/asf/bigtop/blob/f4515e8c/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy-smoke.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy-smoke.py b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy-smoke.py new file mode 100755 index 0000000..1ef64d8 --- /dev/null +++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy-smoke.py @@ -0,0 +1,73 @@ +#!/usr/bin/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 amulet +import unittest + +TIMEOUT = 1800 + + +class TestDeploy(unittest.TestCase): + """ + Deployment test for Apache Zookeeper quorum + """ + + @classmethod + def setUpClass(cls): + cls.d = amulet.Deployment(series='xenial') + + cls.d.add('zookeeper', charm='zookeeper', units=3) + + cls.d.setup(timeout=TIMEOUT) + cls.d.sentry.wait_for_messages({'zookeeper': 'ready (3 units)'}, + timeout=TIMEOUT) + cls.unit = cls.d.sentry['zookeeper'][0] + + def test_deploy(self): + """Verify zk quorum is running""" + output, retcode = self.unit.run("pgrep -a java") + assert 'QuorumPeerMain' in output, "Zookeeper QuorumPeerMain daemon is not started" + + def test_quorum(self): + """ + Verify that our peers are talking to each other, and taking on + appropriate roles. + """ + for unit in self.d.sentry['zookeeper']: + output, _ = unit.run( + "/usr/lib/zookeeper/bin/zkServer.sh status" + ) + # Unit should be a leader or follower + self.assertTrue("leader" in output or "follower" in output) + + def test_smoke(self): + """Validates Zookeeper using the Bigtop 'zookeeper' smoke test.""" + smk_uuids = [] + + for unit in self.d.sentry['zookeeper']: + smk_uuids.append(unit.run_action('smoke-test')) + + for smk_uuid in smk_uuids: + # 'zookeeper' smoke takes a while (bigtop tests are slow) + result = self.d.action_fetch(smk_uuid, timeout=1800, full_output=True) + # actions set status=completed on success + if (result['status'] != "completed"): + self.fail('Zookeeper smoke-test failed: %s' % result) + + +if __name__ == '__main__': + unittest.main() http://git-wip-us.apache.org/repos/asf/bigtop/blob/f4515e8c/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address deleted file mode 100755 index 66e42eb..0000000 --- a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/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 re -import amulet -import subprocess - -DEPLOY_TIMEOUT = 1800 -CONFIG_TIMEOUT = 900 - - -class TestBindClientPort(unittest.TestCase): - """ - Test to verify that we can bind to listen for client connections - on a specific interface. - """ - - @classmethod - def setUpClass(cls): - cls.d = amulet.Deployment(series='xenial') - - cls.d.add('zookeeper', charm='zookeeper', units=3) - - cls.d.setup(timeout=CONFIG_TIMEOUT) - cls.d.sentry.wait(timeout=DEPLOY_TIMEOUT) - cls.unit = cls.d.sentry['zookeeper'][0] - - def test_bind_port(self): - """ - Test to verify that we update client port bindings successfully. - - """ - network_interface = None - # Regular expression should handle interfaces in the format - # eth[n], and in the format en[foo] (the "predicatble - # interface names" in v197+ of systemd). - ethernet_interface = re.compile('^e[thn]+.*') - interfaces, _ = self.unit.run( - "ifconfig -a | sed 's/[ \t].*//;/^$/d'") - interfaces = interfaces.split() # Splits on newlines - for interface in interfaces: - if ethernet_interface.match(interface): - network_interface = interface - break - - if network_interface is None: - raise Exception( - "Could not find any interface on the unit that matched my " - "criteria.") - # self.d.configure broken due to change in juju api. TODO: - # switch this out when fixed. - subprocess.check_call( - ['juju', 'config', 'zookeeper', 'network_interface={}'.format( - network_interface)]) - #self.d.configure('zookeeper', {'network_interface': network_interface}) - self.d.sentry.wait_for_messages( - {'zookeeper': 'updating network interface'}, timeout=CONFIG_TIMEOUT) - self.d.sentry.wait_for_messages( - {'zookeeper': 'ready (3 zk nodes)'}, timeout=CONFIG_TIMEOUT) - ret = self.unit.run( - 'grep clientPortAddress /etc/zookeeper/conf/zoo.cfg')[0] - matcher = re.compile( - "^clientPortAddress=\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.*") - - self.assertTrue(matcher.match(ret)) - - # Verify that smoke tests still run - smk_uuid = self.unit.action_do("smoke-test") - result = self.d.action_fetch(smk_uuid, full_output=True) - # zookeeper smoke-test sets outcome=success on success - if (result['outcome'] != "success"): - error = "Zookeeper smoke-test failed" - amulet.raise_status(amulet.FAIL, msg=error) - - @unittest.skip( - 'Broken handling of 0.0.0.0 bindings upstream, in Zookeeper project.') - def test_reset_bindings(self): - """ - Verify that we can reset the client port bindings to 0.0.0.0 - - """ - self.d.configure('zookeeper', {'network_interface': '0.0.0.0'}) - self.d.sentry.wait_for_messages( - {'zookeeper': 'updating network interface'}, timeout=CONFIG_TIMEOUT) - self.d.sentry.wait_for_messages( - {'zookeeper': 'ready (3 zk nodes)'}, timeout=CONFIG_TIMEOUT) - ret = self.unit.run( - 'grep clientPortAddress /etc/zookeeper/conf/zoo.cfg')[0] - - matcher = re.compile("^clientPortAddress=0\.0\.0\.0.*") - self.assertTrue(matcher.match(ret)) - - # Verify that smoke tests still run - smk_uuid = self.unit.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/f4515e8c/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address.py ---------------------------------------------------------------------- diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address.py b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address.py new file mode 100755 index 0000000..8fa2bb7 --- /dev/null +++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address.py @@ -0,0 +1,109 @@ +#!/usr/bin/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 amulet +import re +import time +import unittest + +TIMEOUT = 1800 + + +class TestBindClientPort(unittest.TestCase): + """ + Test to verify that we can bind to listen for client connections + on a specific interface. + """ + + @classmethod + def setUpClass(cls): + cls.d = amulet.Deployment(series='xenial') + + cls.d.add('zk-test', charm='zookeeper') + + cls.d.setup(timeout=TIMEOUT) + cls.d.sentry.wait_for_messages({'zk-test': re.compile('ready')}, + timeout=TIMEOUT) + cls.unit = cls.d.sentry['zk-test'][0] + + def test_bind_port(self): + """ + Verify that we update client port bindings successfully. + """ + network_interface = None + # Regular expression should handle interfaces in the format + # eth[n], and in the format en[foo] (the "predicatble + # interface names" in v197+ of systemd). + ethernet_interface = re.compile('^e[thn]+.*') + interfaces, _ = self.unit.run( + "ifconfig -a | sed 's/[ \t].*//;/^$/d'") + interfaces = interfaces.split() # Splits on newlines + for interface in interfaces: + if ethernet_interface.match(interface): + network_interface = interface + break + + if network_interface is None: + raise Exception( + "Could not find any interface on the unit that matched my " + "criteria.") + self.d.configure('zk-test', {'network_interface': network_interface}) + + # NB: we used to watch for a maintenance status message, but every now + # and then, we'd miss it. Wait 2m to let the config-changed hook settle. + time.sleep(120) + ret = self.unit.run( + 'grep clientPortAddress /etc/zookeeper/conf/zoo.cfg')[0] + + matcher = re.compile( + "^clientPortAddress=\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.*") + self.assertTrue(matcher.match(ret)) + + # Verify that smoke tests still run + smk_uuid = self.unit.run_action('smoke-test') + # 'zookeeper' smoke takes a while (bigtop tests are slow) + result = self.d.action_fetch(smk_uuid, timeout=1800, full_output=True) + # actions set status=completed on success + if (result['status'] != "completed"): + self.fail('Zookeeper smoke-test failed: %s' % result) + + def test_reset_bindings(self): + """ + Verify that we can reset the client port bindings to 0.0.0.0 + """ + self.d.configure('zk-test', {'network_interface': '0.0.0.0'}) + + # NB: we used to watch for a maintenance status message, but every now + # and then, we'd miss it. Wait 2m to let the config-changed hook settle. + time.sleep(120) + ret = self.unit.run( + 'grep clientPortAddress /etc/zookeeper/conf/zoo.cfg')[0] + + matcher = re.compile("^clientPortAddress=0\.0\.0\.0.*") + self.assertTrue(matcher.match(ret)) + + # Verify that smoke tests still run + smk_uuid = self.unit.run_action('smoke-test') + # 'zookeeper' smoke takes a while (bigtop tests are slow) + result = self.d.action_fetch(smk_uuid, timeout=1800, full_output=True) + # actions set status=completed on success + if (result['status'] != "completed"): + self.fail('Zookeeper smoke-test failed: %s' % result) + + +if __name__ == '__main__': + unittest.main()
