123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- #!/usr/bin/env python
- import subprocess
- import os
- import sys
- from benchmark import framework_test
- from benchmark.utils import gather_tests
- import glob
- import json
- import traceback
- import re
- import logging
- log = logging.getLogger('run-ci')
- import time
- import threading
- # Needed for various imports
- sys.path.append('.')
- sys.path.append('toolset/setup/linux')
- sys.path.append('toolset/benchmark')
- class CIRunnner:
- '''
- Manages running TFB on the Travis Continuous Integration system.
- Makes a best effort to avoid wasting time and resources by running
- useless jobs.
-
- Only verifies the first test in each directory
- '''
-
- def __init__(self, mode, testdir=None):
- '''
- mode = [cisetup|prereq|install|verify] for what we want to do
- testdir = framework directory we are running
- '''
- logging.basicConfig(level=logging.INFO)
- self.directory = testdir
- self.mode = mode
- try:
- is_pull_req = (os.environ['TRAVIS_PULL_REQUEST'] != "false")
- if is_pull_req:
- # See add_commit_range in setup/travis-ci
- self.commit_range = "prbase..prhead"
- else:
- self.commit_range = os.environ['TRAVIS_COMMIT_RANGE']
- except KeyError:
- log.warning("I should only be used for automated integration tests e.g. Travis-CI")
- log.warning("Were you looking for run-tests.py?")
- last_commit = subprocess.check_output("git rev-parse HEAD^", shell=True).rstrip('\n')
- self.commit_range = "%s...HEAD" % last_commit
- #
- # Find the one test from benchmark_config that we are going to run
- #
- tests = gather_tests()
- dirtests = [t for t in tests if t.directory == testdir]
-
- # Travis-CI is linux only
- osvalidtests = [t for t in dirtests if t.os.lower() == "linux"
- and (t.database_os.lower() == "linux" or t.database_os.lower() == "none")]
-
- # Travis-CI only has some supported databases
- validtests = [t for t in osvalidtests if t.database.lower() == "mysql"
- or t.database.lower() == "postgres"
- or t.database.lower() == "mongodb"
- or t.database.lower() == "none"]
- log.info("Found %s tests (%s for linux, %s for linux and mysql) in directory '%s'",
- len(dirtests), len(osvalidtests), len(validtests), testdir)
- if len(validtests) == 0:
- log.critical("Found no test that is possible to run in Travis-CI! Aborting!")
- if len(osvalidtests) != 0:
- log.critical("Note: Found these tests that could run in Travis-CI if more databases were supported")
- log.critical("Note: %s", osvalidtests)
- databases_needed = [t.database for t in osvalidtests]
- databases_needed = list(set(databases_needed))
- log.critical("Note: Here are the needed databases:")
- log.critical("Note: %s", databases_needed)
- sys.exit(1)
- self.names = [t.name for t in validtests]
- log.info("Choosing to use test %s to verify directory %s", self.names, testdir)
- def _should_run(self):
- '''
- Decides if the current framework test should be tested.
- Examines git commits included in the latest push to see if any files relevant to
- this framework were changed.
- If you do rewrite history (e.g. rebase) then it's up to you to ensure that both
- old and new (e.g. old...new) are available in the public repository. For simple
- rebase onto the public master this is not a problem, only more complex rebases
- may have issues
- '''
- # Don't use git diff multiple times, it's mega slow sometimes\
- # Put flag on filesystem so that future calls to run-ci see it too
- if os.path.isfile('.run-ci.should_run'):
- return True
- if os.path.isfile('.run-ci.should_not_run'):
- return False
- def touch(fname):
- open(fname, 'a').close()
- log.info("Using commit range %s", self.commit_range)
- log.info("Running `git diff --name-only %s`" % self.commit_range)
- changes = subprocess.check_output("git diff --name-only %s" % self.commit_range, shell=True)
- log.info(changes)
- # Look for changes to core TFB framework code
- if re.search(r'^toolset/', changes, re.M) is not None:
- log.info("Found changes to core framework code")
- touch('.run-ci.should_run')
- return True
-
- # Look for changes relevant to this test
- if re.search("^%s/" % self.directory, changes, re.M) is None:
- log.info("No changes found for %s", self.directory)
- touch('.run-ci.should_not_run')
- return False
- log.info("Changes found for %s", self.directory)
- touch('.run-ci.should_run')
- return True
- def run(self):
- ''' Do the requested command using TFB '''
- if not self._should_run():
- log.info("Not running %s", self.directory)
- return 0
- if self.mode == 'cisetup':
- self.run_travis_setup()
- return 0
- names = ' '.join(self.names)
- command = 'toolset/run-tests.py '
- if self.mode == 'prereq':
- command = command + "--install server --install-only --test ''"
- elif self.mode == 'install':
- command = command + "--install server --install-only --test %s" % names
- elif self.mode == 'verify':
- command = command + "--mode verify --test %s" % names
- else:
- log.critical('Unknown mode passed')
- return 1
-
- # Run the command
- log.info("Running mode %s with commmand %s", self.mode, command)
- try:
- p = subprocess.Popen(command, shell=True)
- p.wait()
- return p.returncode
- except subprocess.CalledProcessError:
- log.critical("Subprocess Error")
- print traceback.format_exc()
- return 1
- except Exception as err:
- log.critical("Exception from running+wait on subprocess")
- log.error(err.child_traceback)
- return 1
- def run_travis_setup(self):
- log.info("Setting up Travis-CI")
-
- script = '''
- # Needed to download latest MongoDB (use two different approaches)
- sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 || gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 7F0CEB10
- echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
- sudo apt-get update
-
- # MongoDB takes a good 30-45 seconds to turn on, so install it first
- sudo apt-get install mongodb-org
- sudo apt-get install openssh-server
- # Run as travis user (who already has passwordless sudo)
- ssh-keygen -f /home/travis/.ssh/id_rsa -N '' -t rsa
- cat /home/travis/.ssh/id_rsa.pub > /home/travis/.ssh/authorized_keys
- chmod 600 /home/travis/.ssh/authorized_keys
- # =============Setup Databases===========================
- # NOTE: Do not run `--install database` in travis-ci!
- # It changes DB configuration files and will break everything
- # =======================================================
- # Add data to mysql
- mysql -uroot < config/create.sql
- # Setup Postgres
- psql --version
- sudo useradd benchmarkdbuser -p benchmarkdbpass
- sudo -u postgres psql template1 < config/create-postgres-database.sql
- sudo -u benchmarkdbuser psql hello_world < config/create-postgres.sql
- # Setup MongoDB (see install above)
- mongod --version
- until nc -z localhost 27017 ; do echo Waiting for MongoDB; sleep 1; done
- mongo < config/create.js
- '''
- def sh(command):
- log.info("Running `%s`", command)
- subprocess.check_call(command, shell=True)
- for command in script.split('\n'):
- command = command.lstrip()
- if command != "" and command[0] != '#':
- sh(command.lstrip())
- if __name__ == "__main__":
- args = sys.argv[1:]
- usage = '''Usage: toolset/run-ci.py [cisetup|prereq|install|verify] <framework-directory>
-
- run-ci.py selects one test from <framework-directory>/benchark_config, and
- automates a number of calls into run-tests.py specific to the selected test.
- It is guaranteed to always select the same test from the benchark_config, so
- multiple runs with the same <framework-directory> reference the same test.
- The name of the selected test will be printed to standard output.
- cisetup - configure the Travis-CI environment for our test suite
- prereq - trigger standard prerequisite installation
- install - trigger server installation for the selected test_directory
- verify - run a verification on the selected test using `--mode verify`
- run-ci.py expects to be run inside the Travis-CI build environment, and
- will expect environment variables such as $TRAVIS_BUILD'''
- if len(args) != 2:
- print usage
- sys.exit(1)
- mode = args[0]
- testdir = args[1]
- if len(args) == 2 and (mode == "install"
- or mode == "verify"
- or mode == 'prereq'
- or mode == 'cisetup'):
- runner = CIRunnner(mode, testdir)
- else:
- print usage
- sys.exit(1)
-
- retcode = 0
- try:
- retcode = runner.run()
- except KeyError as ke:
- log.warning("Environment key missing, are you running inside Travis-CI?")
- print traceback.format_exc()
- except:
- log.critical("Unknown error")
- print traceback.format_exc()
- finally: # Ensure that logs are printed
-
- # Only print logs if we ran a verify
- if mode != 'verify':
- sys.exit(retcode)
- # Only print logs if we actually did something
- if os.path.isfile('.run-ci.should_not_run'):
- sys.exit(retcode)
- log.error("Running inside Travis-CI, so I will print err and out to console...")
-
- try:
- log.error("Here is ERR:")
- with open("results/ec2/latest/logs/%s/err.txt" % runner.test.name, 'r') as err:
- for line in err:
- log.info(line.rstrip('\n'))
- except IOError:
- log.error("No ERR file found")
- try:
- log.error("Here is OUT:")
- with open("results/ec2/latest/logs/%s/out.txt" % runner.test.name, 'r') as out:
- for line in out:
- log.info(line.rstrip('\n'))
- except IOError:
- log.error("No OUT file found")
- sys.exit(retcode)
|