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