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) |