Ticket #296: idleScheduler-again.diff

File idleScheduler-again.diff, 10.4 kB (added by bhearsum, 7 months ago)

Idle Scheduler, again + docs + tests

  • a/buildbot/scheduler.py

    old new  
    393393        return [] 
    394394 
    395395    def doPeriodicBuild(self): 
    396396        bs = buildset.BuildSet(self.builderNames, 
    397397                               SourceStamp(branch=self.branch), 
    398398                               self.reason, 
    399399                               properties=self.properties) 
    400400        self.submitBuildSet(bs) 
     401 
     402 
     403 
     404class Idle(BaseUpstreamScheduler): 
     405    compare_attrs = ('name', 'builderNames', 'upstream', 'idleBuildTimer', 
     406                     'branch', 'properties') 
     407 
     408    def __init__(self, name, builderNames, upstream, idleBuildTimer, 
     409                 branch=None, properties={}): 
     410        BaseUpstreamScheduler.__init__(self, name, properties) 
     411        self.builderNames = builderNames 
     412        self.branch = branch 
     413        self.idleBuildTimer = idleBuildTimer 
     414        self.reason = ("The Idle scheduler name '%s' triggered this build" 
     415                       % name) 
     416        self.runningBuilds = 0 
     417 
     418        assert isinstance(upstream, (list, tuple)), \ 
     419          "Upstream schedulers in Idle scheduler must be a list" 
     420        assert len(upstream) > 0, \ 
     421          "You must provide at least 1 upstream scheduler to an Idle scheduler" 
     422        self.upstream = upstream 
     423        for s in self.upstream: 
     424            assert interfaces.IUpstreamScheduler.providedBy(s) 
     425            s.subscribeToBuilds(self.stopTimer, start=True, success=False) 
     426            s.subscribeToBuilds(self.restartTimer, failure=True) 
     427            # we can't add ourselves to self.upstream because it will cause 
     428            # BuildMaster.loadConfig_Schedulers to recurse infinitely 
     429            self.subscribeToBuilds(self.stopTimer, start=True, success=False) 
     430            self.subscribeToBuilds(self.restartTimer, failure=True) 
     431 
     432        self.timer = internet.TimerService(self.idleBuildTimer, 
     433                                           self.doPeriodicBuild) 
     434        self.timer.setServiceParent(self) 
     435 
     436    def listBuilderNames(self): 
     437        return self.builderNames 
     438 
     439    def getPendingBuildTimes(self): 
     440        return [] 
     441 
     442    def doPeriodicBuild(self): 
     443        bs = buildset.BuildSet(self.builderNames, 
     444                               SourceStamp(branch=self.branch), 
     445                               self.reason, 
     446                               properties=self.properties) 
     447        self.submitBuildSet(bs) 
     448 
     449    def stopTimer(self, ss): 
     450        self.runningBuilds += 1 
     451        if self.timer._loop.running: 
     452            self.timer._loop.stop() 
     453 
     454    def restartTimer(self, ss): 
     455        self.runningBuilds -= 1 
     456        if self.runningBuilds == 0: 
     457            self.timer._loop.start(self.idleBuildTimer, now=False) 
    401458 
    402459 
    403460 
    404461class Nightly(BaseUpstreamScheduler): 
    405462    """Imitate 'cron' scheduling. This can be used to schedule a nightly 
    406463    build, or one which runs are certain times of the day, week, or month. 
    407464 
    408465    Pass some subset of minute, hour, dayOfMonth, month, and dayOfWeek; each 
  • a/buildbot/test/test_run.py

    old new  
    6262 
    6363from buildbot.scheduler import Scheduler 
    6464c['schedulers'] = [Scheduler('dummy', None, 0.1, ['dummy', 'dummy2'])] 
    6565 
    6666c['builders'].append({'name': 'dummy', 'slavename': 'bot1', 
    6767                      'builddir': 'dummy', 'factory': f2}) 
    6868c['builders'].append({'name': 'dummy2', 'slavename': 'bot1', 
    6969                      'builddir': 'dummy2', 'factory': f2}) 
     70""" 
     71 
     72config_idle = config_base + """ 
     73c['slaves'].append(BuildSlave('bot2', 'sekrit')) 
     74 
     75from buildbot.scheduler import Idle, Scheduler 
     76 
     77f2 = factory.BuildFactory([s(dummy.Dummy, timeout=2)]) 
     78 
     79c['schedulers'] = [] 
     80s = Scheduler('dummy2', None, 0, ['dummy']) 
     81c['schedulers'].append(s) 
     82c['schedulers'].append(Idle('dummy', ['dummy'], [s], 4)) 
     83 
     84c['builders'] = [{'name': 'dummy', 'slavenames': ['bot1', 'bot2'], 
     85                 'builddir': 'dummy', 'factory': f2}] 
    7086""" 
    7187 
    7288config_2 = config_base + """ 
    7389c['builders'] = [{'name': 'dummy', 'slavename': 'bot1', 
    7490                  'builddir': 'dummy1', 'factory': f2}, 
    7591                 {'name': 'testdummy', 'slavename': 'bot1', 
    7692                  'builddir': 'dummy2', 'factory': f2, 'category': 'test'}] 
    7793""" 
     
    209225                            if builder.slaves[0].state == BUILDING ] 
    210226            # assert that only one build is running right now. If the 
    211227            # max_builds= weren't in effect, this would be 2. 
    212228            self.failUnlessEqual(len(building_bs), 1) 
    213229        d.addCallback(_check) 
    214230 
    215231        return d 
    216232 
     233 
     234class Idle(RunMixin, unittest.TestCase): 
     235    def setUp(self): 
     236        RunMixin.setUp(self) 
     237 
     238        self.master.loadConfig(config_idle) 
     239        self.master.readConfig = True 
     240        self.master.startService() 
     241 
     242        return self.connectSlave() 
     243 
     244    def isBuilding(self): 
     245        b = self.master.botmaster.builders['dummy'] 
     246        return len(b.building) 
     247 
     248    def forceBuild(self): 
     249        c = changes.Change("foo", ["blah"], "stuff") 
     250        self.master.change_svc.addChange(c) 
     251 
     252    def testIdleTimerReset(self): 
     253        # idle timer is set to 4, if we wait that long we should have a build 
     254        # going 
     255        self.failUnlessEqual(self.isBuilding(), 1) 
     256        d = defer.Deferred() 
     257        reactor.callLater(2.1, d.callback, None) 
     258        d.addCallback(self._testIdleTimerReset_1) 
     259        return d 
     260 
     261    def _testIdleTimerReset_1(self, res): 
     262        self.failUnlessEqual(self.isBuilding(), 0) 
     263        # now wait until after it is done and force a build 
     264        d = defer.Deferred() 
     265        reactor.callLater(2, d.callback, None) 
     266        d.addCallback(self._testIdleTimerReset_2) 
     267        return d 
     268 
     269    def _testIdleTimerReset_2(self, res): 
     270        self.forceBuild() 
     271        d = defer.Deferred() 
     272        # need extra time here because changes are delayed slightly 
     273        reactor.callLater(4, d.callback, None) 
     274        d.addCallback(self._testIdleTimerReset_3) 
     275        return d 
     276 
     277    def _testIdleTimerReset_3(self, res): 
     278        self.failUnlessEqual(self.isBuilding(), 0) 
     279        d = defer.Deferred() 
     280        reactor.callLater(4, d.callback, None) 
     281        d.addCallback(self._testIdleTimerReset_4) 
     282        return d 
     283 
     284    def _testIdleTimerReset_4(self, res): 
     285        self.failUnlessEqual(self.isBuilding(), 1) 
     286 
     287    def testIdleTimerManyBuilds(self): 
     288        d = self.connectSlave(slavename='bot2') 
     289        d.addCallback(self._testIdleTimerManyBuilds_1) 
     290        return d 
     291 
     292    def _testIdleTimerManyBuilds_1(self, res): 
     293        self.forceBuild() 
     294        self.forceBuild() 
     295        d = defer.Deferred() 
     296        reactor.callLater(1, d.callback, None) 
     297        d.addCallback(self._testIdleTimerManyBuilds_2) 
     298        return d 
     299 
     300    def _testIdleTimerManyBuilds_2(self, res): 
     301        b = self.master.botmaster.builders['dummy'] 
     302        self.failUnlessEqual(self.isBuilding(), 2) 
     303        d = defer.Deferred() 
     304        reactor.callLater(4, d.callback, None) 
     305        d.addCallback(self._testIdleTimerManyBuilds_3) 
     306        return d 
     307 
     308    def _testIdleTimerManyBuilds_3(self, res): 
     309        self.failUnlessEqual(self.isBuilding(), 0) 
    217310 
    218311class Ping(RunMixin, unittest.TestCase): 
    219312    def testPing(self): 
    220313        self.master.loadConfig(config_2) 
    221314        self.master.readConfig = True 
    222315        self.master.startService() 
    223316 
    224317        d = self.connectSlave() 
  • a/docs/buildbot.texinfo

    old new  
    140140* Debug options::                
    141141 
    142142Change Sources and Schedulers 
    143143 
    144144* Scheduler Scheduler::              
    145145* AnyBranchScheduler::           
    146146* Dependent Scheduler::           
    147147* Periodic Scheduler::           
     148* Idle Scheduler:: 
    148149* Nightly Scheduler::           
    149150* Try Schedulers::           
    150151* Triggerable Scheduler::           
    151152 
    152153Buildslave Specifiers 
    153154 
    154155* When Buildslaves Go Missing::   
    155156 
     
    22292230docstrings there are the best source of documentation on the arguments 
    22302231taken by each one. 
    22312232 
    22322233@menu 
    22332234* Scheduler Scheduler::              
    22342235* AnyBranchScheduler::           
    22352236* Dependent Scheduler::           
    22362237* Periodic Scheduler::           
     2238* Idle Scheduler:: 
    22372239* Nightly Scheduler::           
    22382240* Try Schedulers::           
    22392241* Triggerable Scheduler::           
    22402242@end menu 
    22412243 
    22422244@node Scheduler Scheduler 
    22432245@subsection Scheduler Scheduler 
    22442246@slindex buildbot.scheduler.Scheduler 
     
    24322434c['schedulers'] = [nightly] 
    24332435@end example 
    24342436 
    24352437The Scheduler in this example just runs the full solaris build once 
    24362438per day. Note that this Scheduler only lets you control the time 
    24372439between builds, not the absolute time-of-day of each Build, so this 
    24382440could easily wind up a ``daily'' or ``every afternoon'' scheduler 
    24392441depending upon when it was first activated. 
     2442 
     2443@node Idle Scheduler 
     2444@subsection Idle Scheduler 
     2445@slindex buildbot.scheduler.Idle 
     2446 
     2447This scheduler triggers builds after N seconds of idle time. Idle time 
     2448is determined by subscribing to builds from upstream schedulers, and 
     2449builds triggered by this scheduler. 
     2450 
     2451@table @code 
     2452@item name 
     2453 
     2454@item builderNames 
     2455 
     2456@item upstream 
     2457A list of upstream scheduler instances to watch. Whenever a build triggered 
     2458by any of these schedulers is running, the idle timer is stopped. When 
     2459all running builds from these schedulers are completed the timer is 
     2460restarted. 
     2461 
     2462@item idleBuildTimer 
     2463The amount of idle time, in seconds, required before a build is triggered. 
     2464@end table 
     2465 
     2466@example 
     2467from buildbot import scheduler 
     2468s = scheduler.Scheduler(name="onchange", 
     2469                        builderNames=["linux", "osx"], 
     2470                        branch="feature-branch", 
     2471                        treeStableTimer=60*5) 
     2472idle = scheduler.Idle(name="idle", 
     2473                      builderNames=["linux", "osx"], 
     2474                      branch="feature-branch", 
     2475                      idleBuildTimer=60*60*2, # 2 hours 
     2476                      upstream=[s]) 
     2477c['schedulers'] = [s, idle] 
     2478@end example 
     2479 
     2480In this scenario builds will be triggered when the tree changes, as well as 
     2481every 2 hours of inactivity. 
    24402482 
    24412483@node Nightly Scheduler 
    24422484@subsection Nightly Scheduler 
    24432485@slindex buildbot.scheduler.Nightly 
    24442486 
    24452487This is highly configurable periodic build scheduler, which triggers 
    24462488a build at particular times of day, week, month, or year. The 
    24472489configuration syntax is very similar to the well-known @code{crontab}