Skip to content

Commit

Permalink
Merge branch '9/schedulerdata' into nine
Browse files Browse the repository at this point in the history
  • Loading branch information
djmitche committed Jan 29, 2013
2 parents 5848b46 + a053856 commit 05d0968
Show file tree
Hide file tree
Showing 34 changed files with 1,004 additions and 319 deletions.
16 changes: 4 additions & 12 deletions README.md
Expand Up @@ -96,6 +96,7 @@ For each resource type, we'll need the following (based on "Adding Resource Type

* A resource-type module and class, with unit tests
* Docs for that resource type
* Fake versions of all update methods
* Type validators for the resource type
* New endpoints, with unit tests
* Docs for the endpoints
Expand All @@ -107,25 +108,14 @@ It's safe to leave tasks that have significant prerequisites - particularly the

The outstanding resource types are:

* scheduler (underlying DB API complete)
* buildrequest
* build
* step
* logfile
* buildslave
* changesource

#### Schedulers ####

There is no scheduler resource type yet, although schedulers are in place in the database API so that other tables can refer to a scheduler ID.
Support needs to be added for schedulers on a particular master to "claim" the name for themselves, deactivate themselves if already running on another master, and periodically poll for the opportunity to pick up the role.
This gives us automatic failover for schedulers in a multi-master configuration, and also means that all masters can run with identical configurations, which may make configuration management easier.

When a master becomes inactive (either on its own, or having failed its heartbeat check), its schedulers should be marked inactive by the data API and suitable messages sent.

It should be possible to list the master on which a scheduler is running at e.g., `/scheduler/:schedulerid/master`

#### Buildrequests ####
### Notes on Build Requests ###

Buildrequests include a `builderid` field which will need to be determined from the builders table.
It should probably be an error to schedule a build on a builder that does not exist, as that build will not be executed.
Expand Down Expand Up @@ -164,6 +154,8 @@ Tasks:
* Assign `urn`s to objects, and use those to correlate messages with objects.
* Define the proper means of synchronizing messages and resources for each resource type.
This information should be sufficient to reliably predict resource contents based on only on messages.
* Remove `listBuilderNames` and `getPendingBuildTimes` methods from BaseScheduler
* Add messages to the scheduler resource type, one for each possible change in scheduler status.

## Status Rewrites ##

Expand Down
1 change: 1 addition & 0 deletions master/buildbot/data/connector.py
Expand Up @@ -44,6 +44,7 @@ class DataConnector(service.Service):
'buildbot.data.changes',
'buildbot.data.masters',
'buildbot.data.sourcestamps',
'buildbot.data.schedulers',
]

def __init__(self, master):
Expand Down
8 changes: 8 additions & 0 deletions master/buildbot/data/exceptions.py
Expand Up @@ -13,6 +13,13 @@
#
# Copyright Buildbot Team Members

# copy some exceptions from the DB layer
from buildbot.db.schedulers import SchedulerAlreadyClaimedError

_hush_pyflakes = [
SchedulerAlreadyClaimedError
]

class DataException(Exception):
pass

Expand All @@ -23,6 +30,7 @@ class InvalidPathError(DataException):
class InvalidOptionException(DataException):
"An option was invalid"
pass

class InvalidActionException(DataException):
"Action is not supported"
pass
2 changes: 2 additions & 0 deletions master/buildbot/data/masters.py
Expand Up @@ -116,6 +116,8 @@ def _masterDeactivated(self, masterid, name):
# common code for deactivating a master
yield self.master.data.rtypes.builder._masterDeactivated(
masterid=masterid)
yield self.master.data.rtypes.scheduler._masterDeactivated(
masterid=masterid)
self.produceEvent(
dict(masterid=masterid, name=name, active=False),
'stopped')
97 changes: 97 additions & 0 deletions master/buildbot/data/schedulers.py
@@ -0,0 +1,97 @@
# This file is part of Buildbot. Buildbot is free software: you can
# redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright Buildbot Team Members

from twisted.internet import defer
from buildbot.data import base

class Db2DataMixin(object):

@defer.inlineCallbacks
def db2data(self, dbdict):
master = None
if dbdict['masterid'] is not None:
master = yield self.master.data.get({},
('master', dbdict['masterid']))
data = {
'schedulerid': dbdict['id'],
'name': dbdict['name'],
'master': master,
'link': base.Link(('scheduler', str(dbdict['id']))),
}
defer.returnValue(data)


class SchedulerEndpoint(Db2DataMixin, base.Endpoint):

pathPatterns = [
( 'scheduler', 'i:schedulerid' ),
( 'master', 'i:masterid', 'scheduler', 'i:schedulerid' ),
]

@defer.inlineCallbacks
def get(self, options, kwargs):
dbdict = yield self.master.db.schedulers.getScheduler(
kwargs['schedulerid'])
if 'masterid' in kwargs:
if dbdict['masterid'] != kwargs['masterid']:
return
defer.returnValue((yield self.db2data(dbdict))
if dbdict else None)


class SchedulersEndpoint(Db2DataMixin, base.Endpoint):

pathPatterns = [
( 'scheduler', ),
( 'master', 'i:masterid', 'scheduler', ),
]
rootLinkName = 'schedulers'

@defer.inlineCallbacks
def get(self, options, kwargs):
schedulers = yield self.master.db.schedulers.getSchedulers(
masterid=kwargs.get('masterid'))
schdicts = yield defer.gatherResults(
[ self.db2data(schdict) for schdict in schedulers ],
consumeErrors=True)
defer.returnValue(schdicts)

def startConsuming(self, callback, options, kwargs):
return self.master.mq.startConsuming(callback,
('scheduler', None, None))


class SchedulerResourceType(base.ResourceType):

type = "scheduler"
endpoints = [ SchedulerEndpoint, SchedulersEndpoint ]
keyFields = [ 'schedulerid' ]

@base.updateMethod
def findSchedulerId(self, name):
return self.master.db.schedulers.findSchedulerId(name)

@base.updateMethod
def setSchedulerMaster(self, schedulerid, masterid):
# the db method raises the same exception as documented
return self.master.db.schedulers.setSchedulerMaster(
schedulerid, masterid)

@defer.inlineCallbacks
def _masterDeactivated(self, masterid):
schedulers = yield self.master.db.schedulers.getSchedulers(
masterid=masterid)
for sch in schedulers:
yield self.master.db.schedulers.setSchedulerMaster(sch['id'], None)

0 comments on commit 05d0968

Please sign in to comment.