| OLD | NEW |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """BuildBucketIntegrator integrates Buildbot and Buildbucket.""" | 5 """BuildBucketIntegrator integrates Buildbot and Buildbucket.""" |
| 6 | 6 |
| 7 import datetime | 7 import datetime |
| 8 import itertools | |
| 9 import json | 8 import json |
| 10 import logging | 9 import logging |
| 11 import traceback | |
| 12 | 10 |
| 13 from buildbot.util import deferredLocked | 11 from buildbot.util import deferredLocked |
| 14 from master.buildbucket import common, changestore | 12 from master.buildbucket import changestore |
| 15 from master.buildbucket.common import log | |
| 16 from twisted.internet import defer, reactor | 13 from twisted.internet import defer, reactor |
| 17 from twisted.internet.defer import inlineCallbacks, returnValue | 14 from twisted.internet.defer import inlineCallbacks, returnValue |
| 18 | 15 |
| 19 from . import common | 16 from . import common |
| 20 | 17 |
| 21 # buildbucket API-related constants. | 18 # buildbucket API-related constants. |
| 22 # time enough to schedule and start a build. | 19 # time enough to schedule and start a build. |
| 23 LEASE_DURATION = datetime.timedelta(hours=1) | 20 LEASE_DURATION = datetime.timedelta(hours=1) |
| 24 DEFAULT_HEARTBEAT_INTERVAL = datetime.timedelta(minutes=5) | 21 DEFAULT_HEARTBEAT_INTERVAL = datetime.timedelta(minutes=5) |
| 25 # Maximum integer that max_builds buildbucket parameter can take. | 22 # Maximum integer that max_builds buildbucket parameter can take. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 47 # It is a dict build_id -> lease, where lease is a dict with keys: | 44 # It is a dict build_id -> lease, where lease is a dict with keys: |
| 48 # - key: lease key | 45 # - key: lease key |
| 49 # - build_request: BuildRequestGateway | 46 # - build_request: BuildRequestGateway |
| 50 # - build: if build_started with same build_id was called, | 47 # - build: if build_started with same build_id was called, |
| 51 # the associated build. | 48 # the associated build. |
| 52 # _lease is None on creation, but it gets loaded from the database in | 49 # _lease is None on creation, but it gets loaded from the database in |
| 53 # _ensure_leases_loaded(). | 50 # _ensure_leases_loaded(). |
| 54 | 51 |
| 55 def __init__( | 52 def __init__( |
| 56 self, buckets, build_params_hook=None, max_lease_count=None, | 53 self, buckets, build_params_hook=None, max_lease_count=None, |
| 57 heartbeat_interval=None): | 54 heartbeat_interval=None, change_store_factory=None): |
| 58 """Creates a BuildBucketIntegrator. | 55 """Creates a BuildBucketIntegrator. |
| 59 | 56 |
| 60 Args: | 57 Args: |
| 61 buckets (list of str): poll only builds in any of |buckets|. | 58 buckets (list of str): poll only builds in any of |buckets|. |
| 62 build_params_hook (func): If not None, a callable with arguments | 59 build_params_hook (func): If not None, a callable with arguments |
| 63 (params, build) that can modify the supplied parameters during | 60 (params, build) that can modify the supplied parameters during |
| 64 validation. | 61 validation. |
| 65 | 62 |
| 66 If a ValueError is raised, the build will be marked as an | 63 If a ValueError is raised, the build will be marked as an |
| 67 INVALID_BUILD_DEFINITION failure and the error message will be | 64 INVALID_BUILD_DEFINITION failure and the error message will be |
| 68 propagated to the BuildBucket status. | 65 propagated to the BuildBucket status. |
| 69 max_lease_count (int): maximum number of builds that can be leased at a | 66 max_lease_count (int): maximum number of builds that can be leased at a |
| 70 time. Defaults to the number of connected slaves. | 67 time. Defaults to the number of connected slaves. |
| 71 heartbeat_interval (datetime.timedelta): frequency of build heartbeats. | 68 heartbeat_interval (datetime.timedelta): frequency of build heartbeats. |
| 72 Defaults to 1 minute. | 69 Defaults to 1 minute. |
| 70 change_store_factory: function (BuildbotGateway) => ChangeStore. |
| 73 """ | 71 """ |
| 74 assert buckets, 'Buckets not specified' | 72 assert buckets, 'Buckets not specified' |
| 75 assert max_lease_count is None or isinstance(max_lease_count, int) | 73 assert max_lease_count is None or isinstance(max_lease_count, int) |
| 76 if max_lease_count is not None: | 74 if max_lease_count is not None: |
| 77 assert max_lease_count >= 1 | 75 assert max_lease_count >= 1 |
| 78 self.buckets = buckets[:] | 76 self.buckets = buckets[:] |
| 79 self.build_params_hook = build_params_hook | 77 self.build_params_hook = build_params_hook |
| 80 self.buildbot = None | 78 self.buildbot = None |
| 81 self.buildbucket_service = None | 79 self.buildbucket_service = None |
| 82 self._find_change_cache = None | 80 self._find_change_cache = None |
| 83 self.started = False | 81 self.started = False |
| 84 self.changes = None | 82 self.changes = None |
| 85 self.poll_lock = defer.DeferredLock() | 83 self.poll_lock = defer.DeferredLock() |
| 86 self._max_lease_count = max_lease_count | 84 self._max_lease_count = max_lease_count |
| 87 self._leases = None | 85 self._leases = None |
| 88 self.heartbeat_interval = heartbeat_interval or DEFAULT_HEARTBEAT_INTERVAL | 86 self.heartbeat_interval = heartbeat_interval or DEFAULT_HEARTBEAT_INTERVAL |
| 87 self.change_store_factory = change_store_factory or changestore.ChangeStore |
| 89 | 88 |
| 90 def get_max_lease_count(self): | 89 def get_max_lease_count(self): |
| 91 if self._max_lease_count: | 90 if self._max_lease_count: |
| 92 return self._max_lease_count | 91 return self._max_lease_count |
| 93 if not self.buildbot: | 92 if not self.buildbot: |
| 94 return 0 | 93 return 0 |
| 95 return len(self.buildbot.get_connected_slaves()) | 94 return len(self.buildbot.get_connected_slaves()) |
| 96 | 95 |
| 97 def log(self, message, level=None): | 96 def log(self, message, level=None): |
| 98 common.log(message, level) | 97 common.log(message, level) |
| 99 | 98 |
| 100 def start(self, buildbot, buildbucket_service, change_store_factory=None): | 99 def start(self, buildbot, buildbucket_service): |
| 101 assert not self.started, 'BuildBucketIntegrator is already started' | 100 assert not self.started, 'BuildBucketIntegrator is already started' |
| 102 assert buildbot | 101 assert buildbot |
| 103 assert buildbucket_service | 102 assert buildbucket_service |
| 104 change_store_factory = change_store_factory or changestore.ChangeStore | |
| 105 self.buildbot = buildbot | 103 self.buildbot = buildbot |
| 106 self.buildbucket_service = buildbucket_service | 104 self.buildbucket_service = buildbucket_service |
| 107 self.buildbucket_service.start() | 105 self.buildbucket_service.start() |
| 108 self.changes = change_store_factory(buildbot) | 106 self.changes = self.change_store_factory(buildbot) |
| 109 self.started = True | 107 self.started = True |
| 110 self.log('integrator started') | 108 self.log('integrator started') |
| 111 self._do_until_stopped(self.heartbeat_interval, self.send_heartbeats) | 109 self._do_until_stopped(self.heartbeat_interval, self.send_heartbeats) |
| 112 self._do_until_stopped( | 110 self._do_until_stopped( |
| 113 LEASE_CLEANUP_INTERVAL, self.clean_completed_build_requests) | 111 LEASE_CLEANUP_INTERVAL, self.clean_completed_build_requests) |
| 114 | 112 |
| 115 @inlineCallbacks | 113 @inlineCallbacks |
| 116 def _get_leases_from_db(self): | 114 def _get_leases_from_db(self): |
| 117 """Returns currently held leases from the database. | 115 """Returns currently held leases from the database. |
| 118 | 116 |
| (...skipping 524 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 643 'properties': properties_to_send, | 641 'properties': properties_to_send, |
| 644 }, sort_keys=True), | 642 }, sort_keys=True), |
| 645 } | 643 } |
| 646 if status == 'SUCCESS': | 644 if status == 'SUCCESS': |
| 647 yield self._leased_build_call('succeed', build, body) | 645 yield self._leased_build_call('succeed', build, body) |
| 648 else: | 646 else: |
| 649 body['failure_reason'] = ( | 647 body['failure_reason'] = ( |
| 650 'BUILD_FAILURE' if status == 'FAILURE' | 648 'BUILD_FAILURE' if status == 'FAILURE' |
| 651 else 'INFRA_FAILURE') | 649 else 'INFRA_FAILURE') |
| 652 yield self._leased_build_call('fail', build, body) | 650 yield self._leased_build_call('fail', build, body) |
| OLD | NEW |