1# This file is part of Buildbot. Buildbot is free software: you can 2# redistribute it and/or modify it under the terms of the GNU General Public 3# License as published by the Free Software Foundation, version 2. 4# 5# This program is distributed in the hope that it will be useful, but WITHOUT 6# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 7# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 8# details. 9# 10# You should have received a copy of the GNU General Public License along with 11# this program; if not, write to the Free Software Foundation, Inc., 51 12# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 13# 14# Copyright Buildbot Team Members 15 16 17from twisted.internet import defer 18 19from buildbot.data import base 20from buildbot.data import types 21 22 23class BuilderEndpoint(base.BuildNestingMixin, base.Endpoint): 24 25 isCollection = False 26 pathPatterns = """ 27 /builders/n:builderid 28 /builders/i:buildername 29 /masters/n:masterid/builders/n:builderid 30 """ 31 32 @defer.inlineCallbacks 33 def get(self, resultSpec, kwargs): 34 builderid = yield self.getBuilderId(kwargs) 35 if builderid is None: 36 return None 37 38 bdict = yield self.master.db.builders.getBuilder(builderid) 39 if not bdict: 40 return None 41 if 'masterid' in kwargs: 42 if kwargs['masterid'] not in bdict['masterids']: 43 return None 44 return dict(builderid=builderid, 45 name=bdict['name'], 46 masterids=bdict['masterids'], 47 description=bdict['description'], 48 tags=bdict['tags']) 49 50 51class BuildersEndpoint(base.Endpoint): 52 53 isCollection = True 54 rootLinkName = 'builders' 55 pathPatterns = """ 56 /builders 57 /masters/n:masterid/builders 58 """ 59 60 @defer.inlineCallbacks 61 def get(self, resultSpec, kwargs): 62 bdicts = yield self.master.db.builders.getBuilders( 63 masterid=kwargs.get('masterid', None)) 64 return [dict(builderid=bd['id'], 65 name=bd['name'], 66 masterids=bd['masterids'], 67 description=bd['description'], 68 tags=bd['tags']) 69 for bd in bdicts] 70 71 def get_kwargs_from_graphql(self, parent, resolve_info, args): 72 if parent is not None: 73 return {'masterid': parent['masterid']} 74 return {} 75 76 77class Builder(base.ResourceType): 78 79 name = "builder" 80 plural = "builders" 81 endpoints = [BuilderEndpoint, BuildersEndpoint] 82 keyField = 'builderid' 83 eventPathPatterns = """ 84 /builders/:builderid 85 """ 86 subresources = ["Build", "Forcescheduler", "Scheduler", "Buildrequest"] 87 88 class EntityType(types.Entity): 89 builderid = types.Integer() 90 name = types.Identifier(70) 91 masterids = types.List(of=types.Integer()) 92 description = types.NoneOk(types.String()) 93 tags = types.List(of=types.String()) 94 entityType = EntityType(name, 'Builder') 95 96 @defer.inlineCallbacks 97 def generateEvent(self, _id, event): 98 builder = yield self.master.data.get(('builders', str(_id))) 99 self.produceEvent(builder, event) 100 101 @base.updateMethod 102 def findBuilderId(self, name): 103 return self.master.db.builders.findBuilderId(name) 104 105 @base.updateMethod 106 @defer.inlineCallbacks 107 def updateBuilderInfo(self, builderid, description, tags): 108 ret = yield self.master.db.builders.updateBuilderInfo(builderid, description, tags) 109 yield self.generateEvent(builderid, "update") 110 return ret 111 112 @base.updateMethod 113 @defer.inlineCallbacks 114 def updateBuilderList(self, masterid, builderNames): 115 # get the "current" list of builders for this master, so we know what 116 # changes to make. Race conditions here aren't a great worry, as this 117 # is the only master inserting or deleting these records. 118 builders = yield self.master.db.builders.getBuilders(masterid=masterid) 119 120 # figure out what to remove and remove it 121 builderNames_set = set(builderNames) 122 for bldr in builders: 123 if bldr['name'] not in builderNames_set: 124 builderid = bldr['id'] 125 yield self.master.db.builders.removeBuilderMaster( 126 masterid=masterid, builderid=builderid) 127 self.master.mq.produce(('builders', str(builderid), 'stopped'), 128 dict(builderid=builderid, masterid=masterid, 129 name=bldr['name'])) 130 else: 131 builderNames_set.remove(bldr['name']) 132 133 # now whatever's left in builderNames_set is new 134 for name in builderNames_set: 135 builderid = yield self.master.db.builders.findBuilderId(name) 136 yield self.master.db.builders.addBuilderMaster( 137 masterid=masterid, builderid=builderid) 138 self.master.mq.produce(('builders', str(builderid), 'started'), 139 dict(builderid=builderid, masterid=masterid, name=name)) 140 141 # returns a Deferred that returns None 142 def _masterDeactivated(self, masterid): 143 # called from the masters rtype to indicate that the given master is 144 # deactivated 145 return self.updateBuilderList(masterid, []) 146