blob: 4506b19cb1243df0790fc00511537ed6dc1c4224 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
""" Repeatedly launch the build master in an infinite loop, updating the source
between launches. This script is intended to be run at boot time. """
import os
import socket
import subprocess
import sys
import time
BUILDBOT_PATH = os.path.realpath(os.path.join(os.path.dirname(__file__),
os.pardir))
sys.path.append(os.path.join(BUILDBOT_PATH, 'site_config'))
sys.path.append(os.path.join(BUILDBOT_PATH, 'third_party', 'chromium_buildbot',
'site_config'))
import config_private
# File where the PID of the running master is stored
PID_FILE = 'twistd.pid'
# Maximum time (in seconds) to wait for PID_FILE to be written after the master
# is launched. If PID_FILE is not written by then, we assume an error occurred.
PID_TIMEOUT = 10.0
def _SyncSources():
""" Run 'gclient sync' on the buildbot sources. """
path_to_gclient = os.path.join(BUILDBOT_PATH, 'third_party', 'depot_tools',
'gclient.py')
cmd = ['python', path_to_gclient, 'sync']
if not subprocess.call(cmd) == 0:
# Don't throw an exception or quit, since we want to keep the master running
print 'WARNING: Failed to update sources.'
def _LaunchMaster():
""" Launch the build master and return its PID. """
# Make sure the master is stopped.
subprocess.call(['make', 'stop'])
# Launch the master
cmd = ['make', 'start']
if not os.environ.get('TESTING_MASTER'):
for master in config_private.Master.valid_masters:
if socket.getfqdn() == master.master_fqdn:
master_name = master.__name__
print 'Using master %s' % master_name
os.environ['TESTING_MASTER'] = master_name
break
else:
print 'Could not find a matching production master. Using default.'
subprocess.call(cmd)
# Wait for the pid file to be written, then use it to obtain the master's pid
pid_file = None
start_time = time.time()
while not pid_file:
try:
pid_file = open(PID_FILE)
except Exception:
if time.time() - start_time > PID_TIMEOUT:
raise Exception('Failed to launch master.')
time.sleep(1)
pid = str(pid_file.read()).rstrip()
pid_file.close()
return pid
# TODO(borenet): Share this code with launch_slaves.py.
def IsRunning(pid):
""" Determine whether a process with the given PID is running.
pid: string; the PID to test. If pid is None, return False.
"""
if pid is None:
return False
if os.name == 'nt':
cmd = ['tasklist', '/FI', '"PID eq %s"' % pid]
output = subprocess.check_output(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
is_running = pid in output
else:
cmd = ['cat', '/proc/%s/stat' % pid]
is_running = subprocess.call(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT) == 0
return is_running
def _BlockUntilFinished(pid):
""" Blocks until the given process has finished.
pid: PID of the process to wait for
"""
while IsRunning(pid):
time.sleep(1)
def _UpdateAndRunMaster():
""" Update the buildbot sources and run the build master, blocking until it
finishes. """
_SyncSources()
pid = _LaunchMaster()
print 'Launched build master with PID: %s' % pid
_BlockUntilFinished(pid)
print 'Master process has finished.'
def main():
""" Alternately sync the buildbot source and launch the build master. """
loop = '--noloop' not in sys.argv
master_path = os.path.join(BUILDBOT_PATH, 'master')
os.chdir(master_path)
_UpdateAndRunMaster()
while loop:
print 'Restarting the build master.'
_UpdateAndRunMaster()
if '__main__' == __name__:
sys.exit(main())