Ticket #99: first_steps_to_99.patch

File first_steps_to_99.patch, 16.6 kB (added by nhemingway, 4 months ago)
  • a/buildbot/interfaces.py

    old new  
    10061006        to a scarcity of build slaves). These upcoming builds can be canceled 
    10071007        through the control object.""" 
    10081008 
     1009    def cancelBuildRequest(self, request_number): 
     1010        """If there is a build request with the given number on the builder's queue, 
     1011        cancel the request""" 
     1012 
    10091013    def getBuild(number): 
    10101014        """Attempt to return an IBuildControl object for the given build. 
    10111015        Returns None if no such object is available. This will only work for 
  • a/buildbot/process/base.py

    old new  
    133133        not yet been started. 
    134134 
    135135        @return: a boolean indicating if the cancel was successful.""" 
     136        log.msg("Cancelling %r" % self) 
    136137        if self.builder: 
    137138            return self.builder.cancelBuildRequest(self) 
    138139        return False 
  • a/buildbot/process/builder.py

    old new  
    333333    def __repr__(self): 
    334334        return "<Builder '%s' at %d>" % (self.name, id(self)) 
    335335 
    336  
    337336    def submitBuildRequest(self, req): 
    338337        req.submittedAt = now() 
    339338        self.buildable.append(req) 
     
    714713            return 
    715714 
    716715        ss = bs.getSourceStamp(absolute=True) 
    717         req = base.BuildRequest(reason, ss, self.original.name) 
     716        # The properties need to be passed again 
     717        req = base.BuildRequest(reason, ss, self.original.name, bs.getProperties()) 
    718718        self.requestBuild(req) 
    719719 
    720720    def getPendingBuilds(self): 
    721721        # return IBuildRequestControl objects 
    722722        raise NotImplementedError 
     723#        """Return a list of L{IBuildRequestControl} objects for this Builder. 
     724#        Each one corresponds to a pending build that has not yet started (due 
     725#        to a scarcity of build slaves). These upcoming builds can be canceled 
     726#        through the control object.""" 
     727#        return self.original.buildable 
    723728 
    724729    def getBuild(self, number): 
    725730        return self.original.getBuild(number) 
     
    736741        d.addCallback(self._gatherPingResults) 
    737742        return d 
    738743 
     744    def cancelBuildRequest(self, request_number): 
     745        # Delete the build request from queue 
     746        for br in self.original.buildable: 
     747            if br.status.getNumber() == request_number: 
     748                br.cancel() 
     749 
    739750    def _gatherPingResults(self, res): 
    740751        for ignored,success in res: 
    741752            if not success: 
  • a/buildbot/process/properties.py

    old new  
    3939 
    4040    def __getitem__(self, name): 
    4141        """Just get the value for this property.""" 
    42         rv = self.properties[name][0] 
    43         return rv 
     42        return self.properties[name][0] 
     43         
     44    def __setitem__(self, key, value): 
     45        self.properties[key] = (value, "Merged") 
    4446 
    4547    def has_key(self, name): 
    4648        return self.properties.has_key(name) 
  • a/buildbot/status/builder.py

    old new  
    614614        self.builderName = builderName 
    615615        self.builds = [] # list of BuildStatus objects 
    616616        self.observers = [] 
     617        self.number = None 
    617618 
    618619    def buildStarted(self, build): 
    619620        self.builds.append(build) 
     
    635636    def unsubscribe(self, observer): 
    636637        self.observers.remove(observer) 
    637638 
     639    def setNumber(self, number): 
     640        self.number = number 
     641    def getNumber(self): 
     642        return self.number 
     643 
    638644 
    639645class BuildStepStatus(styles.Versioned): 
    640646    """ 
     
    13701376        self.nextBuild = None 
    13711377        self.watchers = [] 
    13721378        self.buildCache = [] # TODO: age builds out of the cache 
     1379        self.nextRequestNumber = 0 
    13731380 
    13741381    # persistence 
    13751382 
     
    13901397        del d['basedir'] 
    13911398        del d['status'] 
    13921399        del d['nextBuildNumber'] 
     1400        del d['nextRequestNumber'] 
    13931401        return d 
    13941402 
    13951403    def __setstate__(self, d): 
     
    14011409        self.pendingBuilds = [] 
    14021410        self.watchers = [] 
    14031411        self.slavenames = [] 
     1412        self.nextRequestNumber = 0 
    14041413        # self.basedir must be filled in by our parent 
    14051414        # self.status must be filled in by our parent 
    14061415 
     
    16561665        return s 
    16571666 
    16581667    def addBuildRequest(self, brstatus): 
     1668        req_number = self.nextRequestNumber 
     1669        self.nextRequestNumber += 1 
     1670        brstatus.setNumber(req_number) 
     1671 
     1672        log.msg("Adding build request %u" % req_number) 
    16591673        self.pendingBuilds.append(brstatus) 
    16601674    def removeBuildRequest(self, brstatus): 
    16611675        self.pendingBuilds.remove(brstatus) 
  • a/buildbot/status/web/baseweb.py

    old new  
    2020from buildbot.status.web.slaves import BuildSlavesResource 
    2121from buildbot.status.web.xmlrpc import XMLRPCServer 
    2222from buildbot.status.web.about import AboutBuildbot 
     23from buildbot.status.web.build import QueueStatusResource 
    2324 
    2425# this class contains the status services (WebStatus and the older Waterfall) 
    2526# which can be put in c['status']. It also contains some of the resources 
  • a/buildbot/status/web/build.py

    old new  
    1111from buildbot.status.web.tests import TestsResource 
    1212from buildbot.status.web.step import StepsResource 
    1313from buildbot import version, util 
     14from buildbot.status.web.queue import QueueEntryStatusResource 
    1415 
    1516# /builders/$builder/builds/$buildnum 
    1617class StatusResourceBuild(HtmlResource): 
     
    5657            results = b.getResults() 
    5758            data += "<h2>Results:</h2>\n" 
    5859            text = " ".join(b.getText()) 
    59             data += '<span class="%s">%s</span>\n' % (css_classes[results], 
    60                                                       text) 
     60            if results != None: 
     61                data += '<span class="%s">%s</span>\n' % (css_classes[results], text) 
    6162            if b.getTestResults(): 
    6263                url = req.childLink("tests") 
    6364                data += "<h3><a href=\"%s\">test results</a></h3>\n" % url 
     
    294295                    build_control = None 
    295296                return StatusResourceBuild(build_status, build_control, 
    296297                                           self.builder_control) 
     298            else: 
     299                # Probably a build that was not started yet 
     300                return QueueEntryStatusResource(path, req, num, self.builder_status, self.builder_control) 
     301 
     302        return HtmlResource.getChild(self, path, req) 
     303 
     304# /builders/$builder/queue 
     305class QueueStatusResource(HtmlResource): 
     306    addSlash = True 
     307 
     308    def __init__(self, builder_status, builder_control): 
     309        HtmlResource.__init__(self) 
     310        self.builder_status = builder_status 
     311        self.builder_control = builder_control 
     312 
     313    def getChild(self, path, req): 
     314        try: 
     315            num = int(path) 
     316        except ValueError: 
     317            num = None 
     318        if num is not None: 
     319                return QueueEntryStatusResource(path, req, num, self.builder_status, self.builder_control) 
    297320 
    298321        return HtmlResource.getChild(self, path, req) 
    299322 
  • a/buildbot/status/web/builder.py

    old new  
    99from buildbot.status.web.base import HtmlResource, make_row, \ 
    1010     make_force_build_form, OneLineMixin 
    1111from buildbot.process.base import BuildRequest 
     12from buildbot.process.properties import Properties 
    1213from buildbot.sourcestamp import SourceStamp 
    1314from buildbot import version, util 
    1415 
    15 from buildbot.status.web.build import BuildsResource, StatusResourceBuild 
     16from buildbot.status.web.build import BuildsResource, StatusResourceBuild, QueueStatusResource 
    1617 
    1718# /builders/$builder 
    1819class StatusResourceBuilder(HtmlResource, OneLineMixin): 
     
    5051  <input type="submit" value="Stop Build" /> 
    5152</form>''' % stopURL 
    5253        return data 
     54     
     55    def makeRemoveFromQueueButton(self, req, req_num): 
     56        stopURL = urllib.quote(req.childLink("queue/%d/remove" % req_num)) 
     57        data = ''' 
     58<form action="%s" class="command stopbuild" style="display:inline"> 
     59  <input type="submit" value="Remove Build" /> 
     60</form>''' % stopURL 
     61        return data 
    5362 
    5463    def body(self, req): 
    5564        b = self.builder_status 
     
    7685            data += "</ul>\n" 
    7786        else: 
    7887            data += "<h2>no current builds</h2>\n" 
     88             
     89        # Then a section with the queued build requests 
     90        data += "<h2>Pending Builds:</h2>\n" 
     91        # Retrieve the builders queue 
     92        pendingBuilds = b.getPendingBuilds() 
     93        if pendingBuilds: 
     94            # Determine the build numbers of the queue builds. 
     95            # Sadly we cannot remove the build from the queue with this 
     96            # number as it is not valid yet. 
     97            data += "<ul>\n" 
     98            for buildRequestStatus in pendingBuilds: 
     99                sourceStamp = buildRequestStatus.getSourceStamp() 
     100                req_number = buildRequestStatus.getNumber() 
     101                data += "<li>#%u " % req_number 
     102                data += "Branch: %s " % sourceStamp.branch 
     103                data += "[rev=%s]" % sourceStamp.revision 
     104                data += self.makeRemoveFromQueueButton(req, req_number) 
     105                data += "</li>" 
     106            data += "</ul>\n" 
     107        else: 
     108            data += "No pending builds" 
    79109 
    80110        # Then a section with the last 5 builds, with the most recent build 
    81111        # distinguished from the rest. 
     
    237267            return file 
    238268        if path == "builds": 
    239269            return BuildsResource(self.builder_status, self.builder_control) 
     270        if path == "queue": 
     271            return QueueStatusResource(self.builder_status, self.builder_control) 
    240272 
    241273        return HtmlResource.getChild(self, path, req) 
    242274 
  • /dev/null

    old new  
     1from twisted.web.util import Redirect, DeferredResource 
     2from twisted.python import log 
     3from twisted.internet import defer, reactor 
     4from buildbot.status.web.base import HtmlResource 
     5 
     6# Shows the queue of a Builder 
     7class QueueEntryStatusResource(HtmlResource): 
     8    def __init__(self, path, req, num, builder_status, builder_control): 
     9        HtmlResource.__init__(self) 
     10        self.path = path 
     11        self.req  = req 
     12        self.num  = num 
     13        self.builder_status = builder_status 
     14        self.builder_control = builder_control 
     15         
     16    def body(self, request):     
     17        return "" 
     18     
     19    # Removes a build from a builders queue 
     20    def removeFromQueue(self, req): 
     21        self.builder_control.cancelBuildRequest(self.num) 
     22 
     23        # we're at http://localhost:8080/svn-hello/builds/5/stop?[args] and 
     24        # we want to go to: http://localhost:8080/svn-hello 
     25        r = Redirect("../..") 
     26        d = defer.Deferred() 
     27        reactor.callLater(1, d.callback, r) 
     28        return DeferredResource(d) 
     29         
     30    def getChild(self, path, req): 
     31        if path == "remove": 
     32            return self.removeFromQueue(req) 
     33        else: 
     34            return HtmlResource.getChild(self, path, req) 
  • /dev/null

    old new  
     1# -*- test-case-name: buildbot.test.test_builder -*- 
     2 
     3from twisted.trial import unittest 
     4 
     5from buildbot.process.base import BuildRequest 
     6from buildbot.process.builder import Builder 
     7from buildbot.status.builder import BuilderStatus 
     8from buildbot.sourcestamp import SourceStamp 
     9 
     10from buildbot.interfaces import IBuilderControl 
     11 
     12def makeABuilderWithNRequests(how_many_requests): 
     13    b = QueueTest_MockBuilder( 
     14        setup = { 
     15            'name': "Builder 1", 
     16            'builddir': "/tmp", 
     17            'factory': "NoSuchFactory" 
     18            }, 
     19        how_many_requests = how_many_requests 
     20        ) 
     21 
     22    builder_control = IBuilderControl(b) 
     23    return (builder_control, b) 
     24 
     25class QueueTest_MockBuilder(Builder): 
     26    def __init__(self, setup, how_many_requests): 
     27        Builder.__init__(self, setup, BuilderStatus(self)) 
     28 
     29        for i in range(how_many_requests): 
     30            self.submitBuildRequest(BuildRequest("a reason", SourceStamp())) 
     31 
     32    def maybeStartBuild(self): 
     33        pass 
     34 
     35class Queue(unittest.TestCase): 
     36    def failUnlessPendingBuildsHaveNumbers(self, expected_numbers, builder): 
     37        pending_builds = builder.buildable 
     38        request_numbers = map( lambda req : req.status.getNumber(), pending_builds ) 
     39        self.failUnlessEqual(request_numbers, expected_numbers) 
     40 
     41    def testRemoveOnEmptyQueue(self): 
     42        (builder_control, builder) = makeABuilderWithNRequests(0) 
     43        self.failUnlessPendingBuildsHaveNumbers([], builder) 
     44        builder_control.cancelBuildRequest(2) 
     45        self.failUnlessPendingBuildsHaveNumbers([], builder) 
     46 
     47    def testRemoveOnlyQueueEntry(self): 
     48        (builder_control, builder) = makeABuilderWithNRequests(1) 
     49        self.failUnlessPendingBuildsHaveNumbers([0], builder) 
     50        builder_control.cancelBuildRequest(0) 
     51        self.failUnlessPendingBuildsHaveNumbers([], builder) 
     52 
     53    def testRemoveFirstQueueEntry(self): 
     54        (builder_control, builder) = makeABuilderWithNRequests(3) 
     55        self.failUnlessPendingBuildsHaveNumbers([0,1,2], builder) 
     56        builder_control.cancelBuildRequest(0) 
     57        self.failUnlessPendingBuildsHaveNumbers([1,2], builder) 
     58 
     59    def testRemoveQueueEntryFromMiddle(self): 
     60        (builder_control, builder) = makeABuilderWithNRequests(5) 
     61        self.failUnlessPendingBuildsHaveNumbers([0,1,2,3,4], builder) 
     62        builder_control.cancelBuildRequest(1) 
     63        self.failUnlessPendingBuildsHaveNumbers([0,2,3,4], builder) 
     64 
     65    def testRemoveLastQueueEntry(self): 
     66        (builder_control, builder) = makeABuilderWithNRequests(3) 
     67        self.failUnlessPendingBuildsHaveNumbers([0,1,2], builder) 
     68        builder_control.cancelBuildRequest(2) 
     69        self.failUnlessPendingBuildsHaveNumbers([0,1], builder) 
     70 
     71    def testRemoveUnknownQueueEntry(self): 
     72        (builder_control, builder) = makeABuilderWithNRequests(3) 
     73        self.failUnlessPendingBuildsHaveNumbers([0,1,2], builder) 
     74        builder_control.cancelBuildRequest(5) 
     75        self.failUnlessPendingBuildsHaveNumbers([0,1,2], builder) 
     76 
     77    def testRemoveSeveralInTurn(self): 
     78        (builder_control, builder) = makeABuilderWithNRequests(5) 
     79        self.failUnlessPendingBuildsHaveNumbers([0,1,2,3,4], builder) 
     80        builder_control.cancelBuildRequest(1) 
     81        self.failUnlessPendingBuildsHaveNumbers([0,2,3,4], builder) 
     82        builder_control.cancelBuildRequest(3) 
     83        self.failUnlessPendingBuildsHaveNumbers([0,2,4], builder) 
     84        builder_control.cancelBuildRequest(0) 
     85        self.failUnlessPendingBuildsHaveNumbers([2,4], builder) 
  • a/docs/buildbot.texinfo

    old new  
    67896789 
    67906790@item /builders/$BUILDERNAME 
    67916791 
    6792 This describes the given Builder, and provides buttons to force a build. 
     6792This describes the given Builder, and provides buttons to force a 
     6793build, and manage the builder's queue. 
    67936794 
    67946795@item /builders/$BUILDERNAME/builds/$BUILDNUM 
    67956796