diff --git a/.travis.yml b/.travis.yml index 9e530e9..d369d1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ +env: + - CHRONOSVERSION: 2.3.4 + language: python python: 2.7 sudo: true diff --git a/itests/Dockerfile b/itests/Dockerfile new file mode 100644 index 0000000..3a90cb4 --- /dev/null +++ b/itests/Dockerfile @@ -0,0 +1,11 @@ +FROM ubuntu-debootstrap:14.04 +RUN apt-get update && apt-get -y install sudo lsb-release + +# Setup +ADD ./chronos-version /root/chronos-version +ADD ./install-chronos.sh /root/install-chronos.sh +RUN /root/install-chronos.sh + +EXPOSE 4400 +ADD ./start-chronos.sh /root/start-chronos.sh +CMD /etc/init.d/zookeeper start && /root/start-chronos.sh && sleep infinity diff --git a/itests/chronos-version b/itests/chronos-version new file mode 100644 index 0000000..3249e96 --- /dev/null +++ b/itests/chronos-version @@ -0,0 +1 @@ +CHRONOSVERSION= diff --git a/itests/docker-compose.yml b/itests/docker-compose.yml new file mode 100644 index 0000000..c8a9d63 --- /dev/null +++ b/itests/docker-compose.yml @@ -0,0 +1,5 @@ +--- +chronos: + build: . + ports: + - 4400 diff --git a/itests/environment.py b/itests/environment.py index 4e2b387..8cea4bd 100644 --- a/itests/environment.py +++ b/itests/environment.py @@ -1,5 +1,11 @@ import time +from itest_utils import wait_for_chronos + + +def before_all(context): + wait_for_chronos() + def after_scenario(context, scenario): """If a chronos client object exists in our context, delete any jobs before the next scenario.""" diff --git a/itests/install-chronos.sh b/itests/install-chronos.sh index 0c7f131..4a35fa5 100755 --- a/itests/install-chronos.sh +++ b/itests/install-chronos.sh @@ -1,7 +1,9 @@ #!/bin/bash set -e -CHRONOSVERSION=2.3.4 +# Default version of chronos to test against if not set by the user +[[ -f /root/chronos-version ]] && source /root/chronos-version +CHRONOSVERSION="${CHRONOSVERSION:-2.3.4}" sudo apt-key adv --keyserver keyserver.ubuntu.com --recv E56151BF DISTRO=$(lsb_release -is | tr '[:upper:]' '[:lower:]') diff --git a/itests/itest_utils.py b/itests/itest_utils.py new file mode 100644 index 0000000..5406c01 --- /dev/null +++ b/itests/itest_utils.py @@ -0,0 +1,74 @@ +import errno +from functools import wraps +import os +import signal +import sys +import threading +import time + +import requests +from compose.cli import command + + +class TimeoutError(Exception): + pass + + +def timeout(seconds=10, error_message=os.strerror(errno.ETIME)): + def decorator(func): + def _handle_timeout(signum, frame): + raise TimeoutError(error_message) + + def wrapper(*args, **kwargs): + signal.signal(signal.SIGALRM, _handle_timeout) + signal.alarm(seconds) + try: + result = func(*args, **kwargs) + finally: + signal.alarm(0) + return result + + return wraps(func)(wrapper) + + return decorator + + +@timeout(60) +def wait_for_chronos(): + """Blocks until marathon is up""" + chronos_service = get_chronos_connection_string() + while True: + print 'Connecting to chronos on %s' % chronos_service + try: + response = requests.get('http://%s/' % chronos_service, timeout=2) + except ( + requests.exceptions.ConnectionError, + requests.exceptions.Timeout, + ): + time.sleep(2) + continue + if response.status_code == 200: + print "Chronos is up and running!" + break + + +def get_compose_service(service_name): + """Returns a compose object for the service""" + cmd = command.Command() + project = cmd.get_project(cmd.get_config_path()) + return project.get_service(service_name) + + +def get_chronos_connection_string(): + # only reliable way I can detect travis.. + if '/travis/' in os.environ.get('PATH'): + return 'localhost:4400' + else: + service_port = get_service_internal_port('chronos') + return get_compose_service('chronos').get_container().get_local_port(service_port) + + +def get_service_internal_port(service_name): + """Gets the exposed port for service_name from docker-compose.yml. If there are + multiple ports. It returns the first one.""" + return get_compose_service(service_name).options['ports'][0] diff --git a/itests/steps/chronos_steps.py b/itests/steps/chronos_steps.py index d528ccf..7181771 100644 --- a/itests/steps/chronos_steps.py +++ b/itests/steps/chronos_steps.py @@ -1,14 +1,20 @@ import sys +import chronos from behave import given, when, then -import chronos +from itest_utils import get_chronos_connection_string + +sys.path.append('../') @given('a working chronos instance') def working_chronos(context): + """Adds a working chronos client as context.client for the purposes of + interacting with it in the test.""" if not hasattr(context, 'client'): - context.client = chronos.connect('localhost:4400') + chronos_connection_string = get_chronos_connection_string() + context.client = chronos.connect(chronos_connection_string) @when(u'we create a trivial chronos job') @@ -28,5 +34,5 @@ def create_trivial_chronos_job(context): @then(u'we should be able to see it when we list jobs') def list_chronos_jobs_has_trivial_job(context): jobs = context.client.list() - job_names = [ job['name'] for job in jobs ] + job_names = [job['name'] for job in jobs] assert 'test_chronos_job' in job_names diff --git a/tox.ini b/tox.ini index aa87623..de48564 100644 --- a/tox.ini +++ b/tox.ini @@ -29,8 +29,15 @@ whitelist_externals=/bin/bash skipsdist=True changedir=itests/ deps = + docker-compose==1.3.1 -rrequirements.txt behave mock commands = - behave {posargs} + /bin/bash -c "[[ -n $TRAVIS ]] || echo CHRONOSVERSION=$CHRONOSVERSION > chronos-version" + /bin/bash -c "[[ -n $TRAVIS ]] || docker-compose build" + /bin/bash -c "[[ -n $TRAVIS ]] || docker-compose pull" + /bin/bash -c "[[ -n $TRAVIS ]] || docker-compose up -d" + behave --no-capture --no-capture-stderr {posargs} + /bin/bash -c "[[ -n $TRAVIS ]] || docker-compose stop" + /bin/bash -c "[[ -n $TRAVIS ]] || docker-compose rm --force"