1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-11-14 11:00:10 +01:00

Merge branch 'build_timeout' into 'master'

Build timeout

See merge request fdroid/fdroidserver!437
This commit is contained in:
Hans-Christoph Steiner 2018-01-22 20:49:01 +00:00
commit 825b8e9683
3 changed files with 59 additions and 21 deletions

6
fdroid
View File

@ -18,6 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys import sys
import os
import logging import logging
import fdroidserver.common import fdroidserver.common
@ -73,7 +74,6 @@ def main():
print_help() print_help()
sys.exit(0) sys.exit(0)
elif command == '--version': elif command == '--version':
import os.path
output = _('no version info found!') output = _('no version info found!')
cmddir = os.path.realpath(os.path.dirname(__file__)) cmddir = os.path.realpath(os.path.dirname(__file__))
moduledir = os.path.realpath(os.path.dirname(fdroidserver.common.__file__) + '/..') moduledir = os.path.realpath(os.path.dirname(fdroidserver.common.__file__) + '/..')
@ -143,7 +143,9 @@ def main():
sys.exit(1) sys.exit(1)
except KeyboardInterrupt: except KeyboardInterrupt:
print('') print('')
sys.exit(1) sys.stdout.flush()
sys.stderr.flush()
os._exit(1)
# These should only be unexpected crashes due to bugs in the code # These should only be unexpected crashes due to bugs in the code
# str(e) often doesn't contain a reason, so just show the backtrace # str(e) often doesn't contain a reason, so just show the backtrace
except Exception as e: except Exception as e:

View File

@ -25,6 +25,7 @@ import re
import resource import resource
import sys import sys
import tarfile import tarfile
import threading
import traceback import traceback
import time import time
import requests import requests
@ -239,9 +240,12 @@ def build_server(app, build, vcs, build_dir, output_dir, log_dir, force):
logging.info("...getting exit status") logging.info("...getting exit status")
returncode = chan.recv_exit_status() returncode = chan.recv_exit_status()
if returncode != 0: if returncode != 0:
raise BuildException( if timeout_event.is_set():
"Build.py failed on server for {0}:{1}".format( message = "Timeout exceeded! Build VM force-stopped for {0}:{1}"
app.id, build.versionName), None if options.verbose else str(output, 'utf-8')) else:
message = "Build.py failed on server for {0}:{1}"
raise BuildException(message.format(app.id, build.versionName),
None if options.verbose else str(output, 'utf-8'))
# Retreive logs... # Retreive logs...
toolsversion_log = common.get_toolsversion_logname(app, build) toolsversion_log = common.get_toolsversion_logname(app, build)
@ -978,6 +982,14 @@ def trybuild(app, build, build_dir, output_dir, log_dir, also_check_dir,
return True return True
def force_halt_build():
"""Halt the currently running Vagrant VM, to be called from a Timer"""
logging.error(_('Force halting build after timeout!'))
timeout_event.set()
vm = vmtools.get_build_vm('builder')
vm.halt()
def parse_commandline(): def parse_commandline():
"""Parse the command line. Returns options, parser.""" """Parse the command line. Returns options, parser."""
@ -1029,6 +1041,7 @@ config = None
buildserverid = None buildserverid = None
fdroidserverid = None fdroidserverid = None
start_timestamp = time.gmtime() start_timestamp = time.gmtime()
timeout_event = threading.Event()
def main(): def main():
@ -1138,15 +1151,23 @@ def main():
# Build applications... # Build applications...
failed_apps = {} failed_apps = {}
build_succeeded = [] build_succeeded = []
max_apps_per_run = 50 # Only build for 12 hours, then stop gracefully
endtime = time.time() + 12 * 60 * 60
max_build_time_reached = False
for appid, app in apps.items(): for appid, app in apps.items():
max_apps_per_run -= 1
if max_apps_per_run < 1:
break
first = True first = True
for build in app.builds: for build in app.builds:
if time.time() > endtime:
max_build_time_reached = True
break
if options.server: # enable watchdog timer
timer = threading.Timer(7200, force_halt_build)
timer.start()
else:
timer = None
wikilog = None wikilog = None
build_starttime = common.get_wiki_timestamp() build_starttime = common.get_wiki_timestamp()
tools_version_log = '' tools_version_log = ''
@ -1287,6 +1308,13 @@ def main():
except Exception as e: except Exception as e:
logging.error("Error while attempting to publish build log: %s" % e) logging.error("Error while attempting to publish build log: %s" % e)
if timer:
timer.cancel() # kill the watchdog timer
if max_build_time_reached:
logging.info("Stopping after global build timeout...")
break
for app in build_succeeded: for app in build_succeeded:
logging.info("success: %s" % (app.id)) logging.info("success: %s" % (app.id))

View File

@ -29,6 +29,9 @@ from .common import FDroidException
from logging import getLogger from logging import getLogger
from fdroidserver import _ from fdroidserver import _
import threading
lock = threading.Lock()
logger = getLogger('fdroidserver-vmtools') logger = getLogger('fdroidserver-vmtools')
@ -175,7 +178,6 @@ class FDroidBuildVm():
This is intended to be a hypervisor independant, fault tolerant This is intended to be a hypervisor independant, fault tolerant
wrapper around the vagrant functions we use. wrapper around the vagrant functions we use.
""" """
def __init__(self, srvdir): def __init__(self, srvdir):
"""Create new server class. """Create new server class.
""" """
@ -191,21 +193,27 @@ class FDroidBuildVm():
self.vgrnt = vagrant.Vagrant(root=srvdir, out_cm=vagrant.stdout_cm, err_cm=vagrant.stdout_cm) self.vgrnt = vagrant.Vagrant(root=srvdir, out_cm=vagrant.stdout_cm, err_cm=vagrant.stdout_cm)
def up(self, provision=True): def up(self, provision=True):
try: global lock
self.vgrnt.up(provision=provision) with lock:
self.srvuuid = self._vagrant_fetch_uuid() try:
except subprocess.CalledProcessError as e: self.vgrnt.up(provision=provision)
raise FDroidBuildVmException("could not bring up vm '%s'" % self.srvname) from e self.srvuuid = self._vagrant_fetch_uuid()
except subprocess.CalledProcessError as e:
raise FDroidBuildVmException("could not bring up vm '%s'" % self.srvname) from e
def suspend(self): def suspend(self):
logger.info('suspending buildserver') global lock
try: with lock:
self.vgrnt.suspend() logger.info('suspending buildserver')
except subprocess.CalledProcessError as e: try:
raise FDroidBuildVmException("could not suspend vm '%s'" % self.srvname) from e self.vgrnt.suspend()
except subprocess.CalledProcessError as e:
raise FDroidBuildVmException("could not suspend vm '%s'" % self.srvname) from e
def halt(self): def halt(self):
self.vgrnt.halt(force=True) global lock
with lock:
self.vgrnt.halt(force=True)
def destroy(self): def destroy(self):
"""Remove every trace of this VM from the system. """Remove every trace of this VM from the system.