Skip to content

Commit

Permalink
Trigger step composes a set sourcestamps for the Triggerable scheduler.
Browse files Browse the repository at this point in the history
Scheduler merges this set with its configuration (codebases) and creates new buildset.
  • Loading branch information
hborkhuis committed May 25, 2012
1 parent e7ba48c commit 87ca041
Show file tree
Hide file tree
Showing 6 changed files with 290 additions and 271 deletions.
6 changes: 5 additions & 1 deletion master/buildbot/process/build.py
Expand Up @@ -102,7 +102,11 @@ def getSourceStampSetId(self):
return self.sources[0].sourcestampsetid
else:
return None


def getAllSourceStamps(self):
for s in self.sources:
yield s

def allChanges(self):
for s in self.sources:
for c in s.changes:
Expand Down
42 changes: 7 additions & 35 deletions master/buildbot/schedulers/triggerable.py
Expand Up @@ -30,10 +30,8 @@ def __init__(self, name, builderNames, properties={}, **kwargs):
self._bsc_subscription = None
self.reason = "Triggerable(%s)" % name

def trigger(self, ss_setid, sourcestamps = None, got_revision = None,
set_props=None):
"""Trigger this scheduler with the given sourcestampset ID, optionally
a set of sourcestamps and a dictionary with got_revision entries.
def trigger(self, sourcestamps = None, set_props=None):
"""Trigger this scheduler with the optional given list of sourcestamps
Returns a deferred that will fire when the buildset is finished."""
# properties for this buildset are composed of our own properties,
# potentially overridden by anything from the triggering build
Expand All @@ -45,11 +43,7 @@ def trigger(self, ss_setid, sourcestamps = None, got_revision = None,
# note that this does not use the buildset subscriptions mechanism, as
# the duration of interest to the caller is bounded by the lifetime of
# this process.
if ss_setid or sourcestamps or got_revision:
d = self._addBuildsetForTrigger(self.reason, ss_setid, sourcestamps,
got_revision, props)
else:
d = self.addBuildsetForLatest(reason=self.reason, properties=props)
d = self._addBuildsetForTrigger(self.reason, sourcestamps, props)
def setup_waiter((bsid,brids)):
d = defer.Deferred()
self._waiters[bsid] = (d, brids)
Expand All @@ -74,42 +68,20 @@ def stopService(self):
return base.BaseScheduler.stopService(self)

@defer.inlineCallbacks
def _addBuildsetForTrigger(self, reason, setid, sourcestamps,
got_revision, properties):
def _addBuildsetForTrigger(self, reason, sourcestamps, properties):

def createLookup(input_list, key):
output_dict = {}
for item in input_list:
output_dict[item[key]] = item
return output_dict

if got_revision is None:
got_revision = {}
if sourcestamps is None:
sourcestamps = []

# Create lookup for sourcestamps that exist in database
existing_lookup = {}
if setid:
existing_list = yield self.master.db.sourcestamps.getSourceStamps(setid)
existing_lookup = createLookup(existing_list, 'codebase')

# Create lookup for sourcestamps that where passed
passed_lookup = createLookup(sourcestamps, 'codebase')
sourcestamps = {}

# Define new setid for this set of triggering sourcestamps
new_setid = yield self.master.db.sourcestampsets.addSourceStampSet()

# Merge codebases with the passed setid, sourcestamps and got_revision
# Merge codebases with the passed list of sourcestamps
# This results in a new sourcestamp for each codebase
for codebase in self.codebases:
ss = self.codebases[codebase].copy()
# apply info from setid
ss.update(existing_lookup.get(codebase,{}))
# apply info from got_revision
ss['revision'] = got_revision.get(codebase, ss.get('revision', None))
# apply info from passed sourcestamp
ss.update(passed_lookup.get(codebase,{}))
ss.update(sourcestamps.get(codebase,{}))

# at least repository must be set, this is normaly forced except when
# codebases is not explicitly set in configuration file.
Expand Down
9 changes: 8 additions & 1 deletion master/buildbot/sourcestamp.py
Expand Up @@ -248,12 +248,19 @@ def getText(self):
text.append("[patch]")
return text

def asDict(self):
def asDict(self, includePatch = False):
result = {}
# Constant
result['revision'] = self.revision

# TODO(maruel): Make the patch content a suburl.
result['hasPatch'] = self.patch is not None
if self.patch and includePatch:
result['patch_level'] = self.patch[0]
result['patch_body'] = self.patch[1]
result['patch_author'] = self.patch_info[0]
result['patch_comment'] = self.patch_info[1]

result['branch'] = self.branch
result['changes'] = [c.asDict() for c in getattr(self, 'changes', [])]
result['project'] = self.project
Expand Down
96 changes: 64 additions & 32 deletions master/buildbot/steps/trigger.py
Expand Up @@ -79,64 +79,95 @@ def end(self, result):
self.ended = True
return self.finished(result)

@defer.inlineCallbacks
def start(self):
# Create the properties that are used for the trigger
def createTriggerProperties(self):
properties = self.build.getProperties()

# make a new properties object from a dict rendered by the old
# properties object
props_to_set = Properties()
props_to_set.update(self.set_properties, "Trigger")
trigger_properties = Properties()
trigger_properties.update(self.set_properties, "Trigger")
for p in self.copy_properties:
if p not in properties:
continue
props_to_set.setProperty(p, properties[p],
trigger_properties.setProperty(p, properties[p],
"%s (in triggering build)" % properties.getPropertySource(p))
return trigger_properties

self.running = True

# (is there an easier way to find the BuildMaster?)
# Get all scheduler instances that were configured
# A tuple of (triggerables, invalidnames) is returned
def getSchedulers(self):
all_schedulers = self.build.builder.botmaster.parent.allSchedulers()
all_schedulers = dict([(sch.name, sch) for sch in all_schedulers])
unknown_schedulers = []
invalid_schedulers = []
triggered_schedulers = []

# don't fire any schedulers if we discover an unknown one
for scheduler in self.schedulerNames:
scheduler = scheduler
if all_schedulers.has_key(scheduler):
sch = all_schedulers[scheduler]
if isinstance(sch, Triggerable):
triggered_schedulers.append(scheduler)
triggered_schedulers.append(sch)
else:
unknown_schedulers.append(scheduler)
invalid_schedulers.append(scheduler)
else:
unknown_schedulers.append(scheduler)

if unknown_schedulers:
self.step_status.setText(['no scheduler:'] + unknown_schedulers)
self.end(FAILURE)
return

master = self.build.builder.botmaster.parent # seriously?!
sourceStamps = self.sourceStamps
got = None
if self.alwaysUseLatest:
ss_setid = None
else:
ss_setid = self.build.getSourceStampSetId()
if self.updateSourceStamp:
invalid_schedulers.append(scheduler)

return (triggered_schedulers, invalid_schedulers)

def prepareSourcestampListForTrigger(self):
# start with the sourcestamps from current build
ss_for_trigger = {}
objs_from_build = self.build.getAllSourceStamps()
for ss in objs_from_build:
ss_for_trigger[ss.codebase] = ss.asDict(includePatch = True)
if self.alwaysUseLatest:
# Reset revision so latest version will be requested from vcs
ss_for_trigger[ss.codebase]['revision'] = None

# overrule revision in sourcestamps with got revision
if self.updateSourceStamp and not self.alwaysUseLatest:
properties = self.build.getProperties()
got = properties.getProperty('got_revision')
# be sure property is always a dictionary
if got and not isinstance(got, dict):
got = {'': got}
for codebase in ss_for_trigger:
if codebase in got:
ss_for_trigger[codebase]['revision'] = got[codebase]

# update sourcestamps from build with passed set of fixed sourcestamps
# or add fixed sourcestamp to the dictionary
for ss in self.sourceStamps:
codebase = ss.get('codebase','')
if codebase in ss_for_trigger:
ss_for_trigger[codebase].update(ss)
else:
ss_for_trigger[codebase] = ss

return ss_for_trigger

@defer.inlineCallbacks
def start(self):
# Get all triggerable schedulers and check if there are invalid schedules
(triggered_schedulers, invalid_schedulers) = self.getSchedulers()
if invalid_schedulers:
self.step_status.setText(['not valid scheduler:'] + invalid_schedulers)
self.end(FAILURE)
return

self.running = True

props_to_set = self.createTriggerProperties()

ss_for_trigger = self.prepareSourcestampListForTrigger()

dl = []
for scheduler in triggered_schedulers:
sch = all_schedulers[scheduler]
dl.append(sch.trigger(ss_setid, set_props=props_to_set,
got_revision=got, sourcestamps=sourceStamps))
self.step_status.setText(['triggered'] + triggered_schedulers)
triggered_names = []
for sch in triggered_schedulers:
dl.append(sch.trigger(ss_for_trigger, set_props=props_to_set))
triggered_names.append(sch.name)
self.step_status.setText(['triggered'] + triggered_names)

if self.waitForFinish:
rclist = yield defer.DeferredList(dl, consumeErrors=1)
Expand Down Expand Up @@ -172,6 +203,7 @@ def start(self):
result = SUCCESS

if brids:
master = self.build.builder.botmaster.parent
def add_links(res):
# reverse the dictionary lookup for brid to builder name
brid_to_bn = dict((_brid,_bn) for _bn,_brid in brids.iteritems())
Expand Down

0 comments on commit 87ca041

Please sign in to comment.