99 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			99 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import json
							 | 
						||
| 
								 | 
							
								import time
							 | 
						||
| 
								 | 
							
								import logging
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								try:
							 | 
						||
| 
								 | 
							
								    import urllib.request as urllib2
							 | 
						||
| 
								 | 
							
								except ImportError:
							 | 
						||
| 
								 | 
							
								    import urllib2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log = logging.getLogger("travis.leader")
							 | 
						||
| 
								 | 
							
								log.addHandler(logging.StreamHandler())
							 | 
						||
| 
								 | 
							
								log.setLevel(logging.INFO)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TRAVIS_JOB_NUMBER = 'TRAVIS_JOB_NUMBER'
							 | 
						||
| 
								 | 
							
								TRAVIS_BUILD_ID = 'TRAVIS_BUILD_ID'
							 | 
						||
| 
								 | 
							
								POLLING_INTERVAL = 'LEADER_POLLING_INTERVAL'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								build_id = os.getenv(TRAVIS_BUILD_ID)
							 | 
						||
| 
								 | 
							
								polling_interval = int(os.getenv(POLLING_INTERVAL, '5'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#assume, first job is the leader
							 | 
						||
| 
								 | 
							
								is_leader = lambda job_number: job_number.endswith('.1')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if not os.getenv(TRAVIS_JOB_NUMBER):
							 | 
						||
| 
								 | 
							
								    # seems even for builds with only one job, this won't get here
							 | 
						||
| 
								 | 
							
								    log.fatal("Don't use defining leader for build without matrix")
							 | 
						||
| 
								 | 
							
								    exit(1)
							 | 
						||
| 
								 | 
							
								elif is_leader(os.getenv(TRAVIS_JOB_NUMBER)):
							 | 
						||
| 
								 | 
							
								    log.info("This is a leader")
							 | 
						||
| 
								 | 
							
								else:
							 | 
						||
| 
								 | 
							
								    #since python is subprocess, env variables are exported back via file
							 | 
						||
| 
								 | 
							
								    with open(".to_export_back", "w") as export_var:
							 | 
						||
| 
								 | 
							
								        export_var.write("BUILD_MINION=YES")
							 | 
						||
| 
								 | 
							
								    log.info("This is a minion")
							 | 
						||
| 
								 | 
							
								    exit(0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class MatrixElement(object):
							 | 
						||
| 
								 | 
							
								    def __init__(self, json_raw):
							 | 
						||
| 
								 | 
							
								        self.allow_failure = json_raw['allow_failure']
							 | 
						||
| 
								 | 
							
								        self.is_finished = json_raw['finished_at'] is not None
							 | 
						||
| 
								 | 
							
								        self.is_succeeded = json_raw['result'] == 0
							 | 
						||
| 
								 | 
							
								        self.number = json_raw['number']
							 | 
						||
| 
								 | 
							
								        self.is_leader = is_leader(self.number)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def matrix_snapshot():
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    :return: Matrix List
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    response = urllib2.build_opener().open("https://api.travis-ci.org/builds/{0}".format(build_id)).read()
							 | 
						||
| 
								 | 
							
								    raw_json = json.loads(response)
							 | 
						||
| 
								 | 
							
								    matrix_without_leader = [MatrixElement(element) for element in raw_json["matrix"]]
							 | 
						||
| 
								 | 
							
								    return matrix_without_leader
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def wait_others_to_finish():
							 | 
						||
| 
								 | 
							
								    def others_finished():
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Dumps others to finish
							 | 
						||
| 
								 | 
							
								        Leader cannot finish, it is working now
							 | 
						||
| 
								 | 
							
								        :return: tuple(True or False, List of not finished jobs)
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        snapshot = matrix_snapshot()
							 | 
						||
| 
								 | 
							
								        finished = [el.is_finished for el in snapshot if not (el.is_leader or el.allow_failure)]
							 | 
						||
| 
								 | 
							
								        return reduce(lambda a, b: a and b, finished), [el.number for el in snapshot if
							 | 
						||
| 
								 | 
							
								                                                        not el.is_leader and not el.is_finished]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while True:
							 | 
						||
| 
								 | 
							
								        finished, waiting_list = others_finished()
							 | 
						||
| 
								 | 
							
								        if finished: break
							 | 
						||
| 
								 | 
							
								        log.info("Leader waits for minions {0}...".format(waiting_list))  # just in case do not get "silence timeout"
							 | 
						||
| 
								 | 
							
								        time.sleep(polling_interval)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								try:
							 | 
						||
| 
								 | 
							
								    wait_others_to_finish()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    final_snapshot = matrix_snapshot()
							 | 
						||
| 
								 | 
							
								    log.info("Final Results: {0}".format([(e.number, e.is_succeeded, e.allow_failure) for e in final_snapshot]))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    BUILD_AGGREGATE_STATUS = 'BUILD_AGGREGATE_STATUS'
							 | 
						||
| 
								 | 
							
								    others_snapshot = [el for el in final_snapshot if not (el.is_leader or el.allow_failure)]
							 | 
						||
| 
								 | 
							
								    if reduce(lambda a, b: a and b, [e.is_succeeded for e in others_snapshot]):
							 | 
						||
| 
								 | 
							
								        os.environ[BUILD_AGGREGATE_STATUS] = "others_succeeded"
							 | 
						||
| 
								 | 
							
								    elif reduce(lambda a, b: a and b, [not e.is_succeeded for e in others_snapshot]):
							 | 
						||
| 
								 | 
							
								        log.error("Others Failed")
							 | 
						||
| 
								 | 
							
								        os.environ[BUILD_AGGREGATE_STATUS] = "others_failed"
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        log.warn("Others Unknown")
							 | 
						||
| 
								 | 
							
								        os.environ[BUILD_AGGREGATE_STATUS] = "unknown"
							 | 
						||
| 
								 | 
							
								    #since python is subprocess, env variables are exported back via file
							 | 
						||
| 
								 | 
							
								    with open(".to_export_back", "w") as export_var:
							 | 
						||
| 
								 | 
							
								        export_var.write("BUILD_LEADER=YES {0}={1}".format(BUILD_AGGREGATE_STATUS, os.environ[BUILD_AGGREGATE_STATUS]))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								except Exception as e:
							 | 
						||
| 
								 | 
							
								    log.fatal(e)
							 |