Ticket #56: triggerable.patch

File triggerable.patch, 13.9 KB (added by dustin, 4 years ago)
  • buildbot/scheduler.py

    Sun Jan 13 21:48:39 EST 2008  dustin@zmanda.com
      * triggerable.patch
    diff -rN -u old-trunk/buildbot/scheduler.py new-trunk/buildbot/scheduler.py
    old new  
    681681        from buildbot.status.client import makeRemote 
    682682        return makeRemote(bs.status) 
    683683 
     684class Triggerable(BaseUpstreamScheduler): 
     685    """ 
     686    This scheduler doesn't do anything until it is triggered by 
     687    a Trigger step in a factory. 
     688    """ 
     689 
     690    def __init__(self, name, builderNames): 
     691        BaseUpstreamScheduler.__init__(self, name) 
     692        self.builderNames = builderNames 
     693 
     694    def listBuilderNames(self): 
     695        return self.builderNames 
     696 
     697    def getPendingBuildTimes(self): 
     698        return [] 
     699 
     700    def trigger(self, ss): 
     701        """ 
     702        Trigger this scheduler.  Returns a deferred that will fire when the buildset 
     703        is finished. 
     704        """ 
     705        bs = buildset.BuildSet(self.builderNames, ss) 
     706        d = bs.waitUntilFinished() 
     707        self.submit(bs) 
     708        return d 
  • buildbot/steps/trigger.py

    diff -rN -u old-trunk/buildbot/steps/trigger.py new-trunk/buildbot/steps/trigger.py
    old new  
     1from buildbot.process.buildstep import LoggingBuildStep, SUCCESS, FAILURE, EXCEPTION 
     2from buildbot.steps.shell import WithProperties 
     3from buildbot.sourcestamp import SourceStamp 
     4from buildbot.scheduler import Triggerable 
     5from twisted.internet import defer 
     6from twisted.python import log 
     7import os 
     8 
     9class Trigger(LoggingBuildStep): 
     10    """ 
     11    I trigger a Triggerable.  It's fun. 
     12    """ 
     13    name = "trigger" 
     14 
     15    flunkOnFailure = True 
     16 
     17    def __init__(self, 
     18        schedulers=[], 
     19        updateSourceStamp=False, 
     20        waitForFinish=False, 
     21        **kwargs): 
     22        """ 
     23        Trigger the given schedulers when this step is executed. 
     24 
     25        @var schedulers: list of schedulers' names that should be triggered.  Schedulers 
     26        can be specified using WithProperties, if desired. 
     27 
     28        @var updateSourceStamp: should I update the source stamp to 
     29        an absolute SourceStamp before triggering a new build? 
     30 
     31        @var waitForFinish: should I wait for all of the triggered schedulers to finish 
     32        their builds? 
     33        """ 
     34        assert schedulers, "You must specify a scheduler to trigger" 
     35        self.schedulers = schedulers 
     36        self.updateSourceStamp = updateSourceStamp 
     37        self.waitForFinish = waitForFinish 
     38        self.running = False 
     39        LoggingBuildStep.__init__(self, **kwargs) 
     40        self.addFactoryArguments(schedulers=schedulers, 
     41                                 updateSourceStamp=updateSourceStamp, 
     42                                 waitForFinish=waitForFinish) 
     43 
     44    def interrupt(self, reason): 
     45        if self.running: 
     46            self.step_status.setColor("red") 
     47            self.step_status.setText(["interrupted"]) 
     48 
     49    def start(self): 
     50        self.running = True 
     51        ss = self.build.getSourceStamp() 
     52        if self.updateSourceStamp: 
     53            ss = ss.getAbsoluteSourceStamp(self.build.getProperty('got_revision')) 
     54        # (is there an easier way to find the BuildMaster?) 
     55        all_schedulers = self.build.builder.botmaster.parent.allSchedulers() 
     56        all_schedulers = dict([(sch.name, sch) for sch in all_schedulers]) 
     57        unknown_schedulers = [] 
     58        triggered_schedulers = [] 
     59 
     60        dl = [] 
     61        for scheduler in self.schedulers: 
     62            if isinstance(scheduler, WithProperties): 
     63                scheduler = scheduler.render(self.build) 
     64            if all_schedulers.has_key(scheduler): 
     65                sch = all_schedulers[scheduler] 
     66                if isinstance(sch, Triggerable): 
     67                    dl.append(sch.trigger(ss)) 
     68                    triggered_schedulers.append(scheduler) 
     69                else: 
     70                    unknown_schedulers.append(scheduler) 
     71            else: 
     72                unknown_schedulers.append(scheduler) 
     73 
     74        if unknown_schedulers: 
     75            self.step_status.setColor("red") 
     76            self.step_status.setText(['no scheduler:'] + unknown_schedulers) 
     77            rc = FAILURE 
     78        else: 
     79            rc = SUCCESS 
     80            self.step_status.setText(['triggered'] + triggered_schedulers) 
     81            if self.waitForFinish: 
     82                self.step_status.setColor("yellow") 
     83            else: 
     84                self.step_status.setColor("green") 
     85 
     86        if self.waitForFinish: 
     87            d = defer.DeferredList(dl, consumeErrors=1) 
     88        else: 
     89            d = defer.succeed([]) 
     90 
     91        def cb(rclist): 
     92            rc = SUCCESS 
     93            for was_cb, buildsetstatus in rclist: 
     94                # TODO: make this algo more configurable 
     95                if not was_cb: 
     96                    rc = EXCEPTION 
     97                    break 
     98                if buildsetstatus.getResults() == FAILURE: 
     99                    rc = FAILURE 
     100            return self.finished(rc) 
     101 
     102        def eb(why): 
     103            return self.finished(FAILURE) 
     104 
     105        d.addCallbacks(cb, eb) 
  • buildbot/test/test_run.py

    diff -rN -u old-trunk/buildbot/test/test_run.py new-trunk/buildbot/test/test_run.py
    old new  
    628628        d = self.master.loadConfig(config_4_newbuilder) 
    629629        return d 
    630630 
     631class Triggers(RunMixin, TestFlagMixin, unittest.TestCase): 
     632    config_trigger = config_base + """ 
     633from buildbot.scheduler import Triggerable, Scheduler 
     634from buildbot.steps.trigger import Trigger 
     635from buildbot.steps.dummy import Dummy 
     636from buildbot.test.runutils import SetTestFlagStep 
     637c['schedulers'] = [ 
     638    Scheduler('triggerer', None, 0.1, ['triggerer']), 
     639    Triggerable('triggeree', ['triggeree']) 
     640] 
     641triggerer = factory.BuildFactory([ 
     642    s(SetTestFlagStep, flagname='triggerer_started'), 
     643    s(Trigger, flunkOnFailure=True, @ARGS@), 
     644    s(SetTestFlagStep, flagname='triggerer_finished'), 
     645    ]) 
     646triggeree = factory.BuildFactory([ 
     647    s(SetTestFlagStep, flagname='triggeree_started'), 
     648    s(@DUMMYCLASS@), 
     649    s(SetTestFlagStep, flagname='triggeree_finished'), 
     650    ]) 
     651c['builders'] = [{'name': 'triggerer', 'slavename': 'bot1', 
     652                  'builddir': 'triggerer', 'factory': triggerer}, 
     653                 {'name': 'triggeree', 'slavename': 'bot1', 
     654                  'builddir': 'triggeree', 'factory': triggeree}] 
     655""" 
     656 
     657    def mkConfig(self, args, dummyclass="Dummy"): 
     658        return self.config_trigger.replace("@ARGS@", args).replace("@DUMMYCLASS@", dummyclass) 
     659 
     660    def setupTest(self, args, dummyclass, checkFn): 
     661        self.clearFlags() 
     662        m = self.master 
     663        m.loadConfig(self.mkConfig(args, dummyclass)) 
     664        m.readConfig = True 
     665        m.startService() 
     666 
     667        c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff") 
     668        m.change_svc.addChange(c) 
     669 
     670        d = self.connectSlave(builders=['triggerer', 'triggeree']) 
     671        d.addCallback(self.startTimer, 0.5, checkFn) 
     672        return d 
     673 
     674    def startTimer(self, res, time, next_fn): 
     675        d = defer.Deferred() 
     676        reactor.callLater(time, d.callback, None) 
     677        d.addCallback(next_fn) 
     678        return d 
     679 
     680    def testTriggerBuild(self): 
     681        return self.setupTest("schedulers=['triggeree']", 
     682                "Dummy", 
     683                self._checkTriggerBuild) 
     684 
     685    def _checkTriggerBuild(self, res): 
     686        self.failIfFlagNotSet('triggerer_started') 
     687        self.failIfFlagNotSet('triggeree_started') 
     688        self.failIfFlagSet('triggeree_finished') 
     689        self.failIfFlagNotSet('triggerer_finished') 
     690 
     691    def testTriggerBuildWait(self): 
     692        return self.setupTest("schedulers=['triggeree'], waitForFinish=1", 
     693                "Dummy", 
     694                self._checkTriggerBuildWait) 
     695 
     696    def _checkTriggerBuildWait(self, res): 
     697        self.failIfFlagNotSet('triggerer_started') 
     698        self.failIfFlagNotSet('triggeree_started') 
     699        self.failIfFlagSet('triggeree_finished') 
     700        self.failIfFlagSet('triggerer_finished') 
     701 
    631702# TODO: test everything, from Change submission to Scheduler to Build to 
    632703# Status. Use all the status types. Specifically I want to catch recurrences 
    633704# of the bug where I forgot to make Waterfall inherit from StatusReceiver 
  • docs/buildbot.texinfo

    diff -rN -u old-trunk/docs/buildbot.texinfo new-trunk/docs/buildbot.texinfo
    old new  
    21862186scheduler is triggered. The next section (@pxref{Build Dependencies}) 
    21872187describes this scheduler in more detail. 
    21882188 
     2189@item Triggerable 
     2190This scheduler does nothing until it is triggered by a Trigger 
     2191step in another build.  This facilitates a more general form of 
     2192build dependencies, as described in the next section (@pxref{Build 
     2193Dependencies}). 
     2194 
    21892195@item Periodic 
    21902196This simple scheduler just triggers a build every N seconds. 
    21912197 
     
    22122218@cindex Dependent 
    22132219@cindex Dependencies 
    22142220@slindex buildbot.scheduler.Dependent 
     2221@slindex buildbot.scheduler.Triggerable 
    22152222 
    22162223It is common to wind up with one kind of build which should only be 
    22172224performed if the same source code was successfully handled by some 
     
    22542261@code{Scheduler} @emph{instance}, not a name. This makes it impossible 
    22552262to create circular dependencies in the config file. 
    22562263 
     2264A more general way to coordinate builds is by ``triggering'' schedulers 
     2265from builds.  The Triggerable waits to be triggered by a 
     2266Trigger step in another build.  That step can optionally 
     2267wait for the scheduler's builds to complete.  This provides two 
     2268advantages over Dependent schedulers.  First, the same scheduler 
     2269can be triggered from multiple builds.  Second, the ability to wait 
     2270for a Triggerable's builds to complete provides a form of 
     2271"subroutine call", where one or more builds can "call" a scheduler 
     2272to perform some work for them,  perhaps on other buildslaves. 
     2273 
     2274@example 
     2275from buildbot import scheduler 
     2276from buildbot.steps import trigger 
     2277checkin = scheduler.Scheduler("checkin", None, 5*60, 
     2278                            ["checkin"]) 
     2279nightly = scheduler.Scheduler("nightly", ... 
     2280                            ["nightly"]) 
     2281mktarball = scheduler.Triggerable("mktarball", 
     2282                              ["mktarball"]) 
     2283build = scheduler.Triggerable("build-all-platforms", 
     2284                              ["build-all-platforms"]) 
     2285test = scheduler.Triggerable("distributed-test", 
     2286                              ["distributed-test"]) 
     2287package = scheduler.Triggerable("package-all-platforms", 
     2288                              ["package-all-platforms"]) 
     2289c['schedulers'] = [checkin, nightly, build, test, package] 
     2290 
     2291checkin_factory = factory.BuildFactory() 
     2292f.addStep(trigger.TriggerStep('mktarball', 
     2293    schedulers=['mktarball'], 
     2294    waitForFinish=1) 
     2295f.addStep(trigger.TriggerStep('build', 
     2296    schedulers=['build-all-platforms'], 
     2297    waitForFinish=1) 
     2298f.addStep(trigger.TriggerStep('test', 
     2299    schedulers=['distributed-test'], 
     2300    waitForFinish=1) 
     2301 
     2302nightly_factory = factory.BuildFactory() 
     2303f.addStep(trigger.TriggerStep('mktarball', 
     2304    schedulers=['mktarball'], 
     2305    waitForFinish=1) 
     2306f.addStep(trigger.TriggerStep('build', 
     2307    schedulers=['build-all-platforms'], 
     2308    waitForFinish=1) 
     2309f.addStep(trigger.TriggerStep('package', 
     2310    schedulers=['package-all-platforms'], 
     2311    waitForFinish=1) 
     2312@end example 
    22572313 
    22582314@node Setting the slaveport, Buildslave Specifiers, Listing Change Sources and Schedulers, Configuration 
    22592315@section Setting the slaveport 
     
    36603716* Simple ShellCommand Subclasses::   
    36613717* Python BuildSteps::            
    36623718* Transferring Files::           
     3719* Triggering Schedulers:: 
    36633720* Writing New BuildSteps::       
    36643721@end menu 
    36653722 
     
    45964653 
    45974654@end table 
    45984655 
    4599 @node Python BuildSteps, Transferring Files, Simple ShellCommand Subclasses, Build Steps 
     4656@node Python BuildSteps, Triggering Schedulers, Simple ShellCommand Subclasses, Build Steps 
    46004657@subsection Python BuildSteps 
    46014658 
    46024659Here are some BuildSteps that are specifcally useful for projects 
     
    46724729@end example 
    46734730 
    46744731 
    4675 @node Transferring Files, Writing New BuildSteps, Python BuildSteps, Build Steps 
     4732@node Transferring Files, Triggering Schedulers, Python BuildSteps, Build Steps 
    46764733@subsection Transferring Files 
    46774734 
    46784735@cindex File Transfer 
     
    47614818creation time (@pxref{Buildslave Options}). 
    47624819 
    47634820 
    4764 @node Writing New BuildSteps,  , Transferring Files, Build Steps 
     4821@node Triggering Schedulers, Writing New BuildSteps, Transferring Files, Build Steps 
     4822@subsection Triggering Schedulers 
     4823 
     4824The counterpart to the Triggerable described in section 
     4825@pxref{Build Dependencies} is the Trigger BuildStep. 
     4826 
     4827@example 
     4828from buildbot.steps.trigger import Trigger 
     4829f.addStep(Trigger, 
     4830          schedulers=['build-prep'], 
     4831          waitForFinish=1, 
     4832          updateSourceStamp=1) 
     4833@end example 
     4834 
     4835The @code{schedulers=} argument lists the Triggerables 
     4836that should be triggered when this step is executed.  Note that 
     4837it is possible, but not advisable, to create a cycle where a build 
     4838continually triggers itself, because the schedulers are specified 
     4839by name. 
     4840 
     4841If @code{waitForFinish} is true, then the step will not finish until 
     4842all of the builds from the triggered schedulers have finished.  If this 
     4843argument is not given, then the buildstep succeeds immediately after 
     4844triggering the schedulers. 
     4845 
     4846If @code{updateSourceStamp} is true, then step updates the SourceStamp 
     4847given to the Triggerables to include @code{got_revision} 
     4848(the revision actually used in this build) as @code{revision} (the 
     4849revision to use in the triggered builds).  This is useful to ensure 
     4850that all of the builds use exactly the same SourceStamp, even if 
     4851other Changes have occurred while the build was running. 
     4852 
     4853@node Writing New BuildSteps,  , Triggering Schedulers, Build Steps 
    47654854@subsection Writing New BuildSteps 
    47664855 
    47674856While it is a good idea to keep your build process self-contained in