OLD | NEW |
| (Empty) |
1 /** | |
2 * @license Copyright 2012 Google Inc. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license that can be | |
5 * found in the LICENSE file. | |
6 */ | |
7 | |
8 /** | |
9 * @fileoverview Tools used by the Skia buildbot dashboards, including | |
10 * facilities for retrieving data from the buildbot master. | |
11 */ | |
12 | |
13 "use strict"; | |
14 | |
15 var skiaTools = { | |
16 | |
17 globalVariables: null, | |
18 googlesourceURL: "https://skia.googlesource.com", | |
19 | |
20 masterHostSuffix: "_master_host", | |
21 masterPortSuffix: "_external_port", | |
22 | |
23 | |
24 /** | |
25 * Retrieve the Git log for the Skia repository. | |
26 * | |
27 * @param {string} startHash | |
28 * @param {string} endHash | |
29 * @param {function(Array.<Object>)} callback Call this function with the | |
30 * decoded JSON from the Git log when the data has loaded. | |
31 */ | |
32 gitLog: function(startHash, endHash, callback) { | |
33 var url = this.googlesourceURL + "/skia/+log/" + startHash + ".." + endHash + | |
34 "?format=JSON"; | |
35 try { | |
36 var request = new XMLHttpRequest(); | |
37 } catch (error) { | |
38 alert(error); | |
39 } | |
40 request.open("GET", url, true); | |
41 request.onreadystatechange = function() { | |
42 if (request.readyState != 4) { return; } | |
43 // Remove the first line, which is garbage. | |
44 var responseLines = request.responseText.split('\n'); | |
45 responseLines.splice(0, 1); | |
46 | |
47 callback(JSON.parse(responseLines.join('\n'))["log"]); | |
48 }; | |
49 request.send(); | |
50 }, | |
51 | |
52 /** | |
53 * Object used for managing Git history. | |
54 */ | |
55 GitHistory: function() { | |
56 this.gotRev = {}; | |
57 this.allRevisions = []; | |
58 this.lastFetchedRev = "HEAD"; | |
59 | |
60 /** | |
61 * loadCommits | |
62 * | |
63 * @param {string} oldestCommit Load this commit and all newer commits. | |
64 * @param {function} callback Call this function when finished. | |
65 */ | |
66 this.loadCommits = function(oldestCommit, callback) { | |
67 var thisInstance = this; | |
68 skiaTools.gitLog(oldestCommit + "~1", this.lastFetchedRev, function(newCommi
ts) { | |
69 for (var i = 0; i < newCommits.length; ++i) { | |
70 var commitHash = newCommits[i].commit; | |
71 thisInstance.allRevisions.push(commitHash); | |
72 thisInstance.gotRev[commitHash] = true; | |
73 } | |
74 thisInstance.lastFetchedRev = newCommits[newCommits.length - 1].commit; | |
75 callback(); | |
76 }); | |
77 } | |
78 | |
79 /** | |
80 * getRevList | |
81 * | |
82 * @return {Array.<string>} Array of commit hashes in chronological order. | |
83 */ | |
84 this.getRevList = function() { | |
85 var revList = Array.prototype.slice.call(this.allRevisions); | |
86 revList.reverse(); // Sort newest to oldest. | |
87 return revList; | |
88 } | |
89 | |
90 /** | |
91 * ensureLoaded | |
92 * | |
93 * @param {Array.<string>} commitList Load commits from history until all | |
94 * commits in commitList have been loaded. This will be significantly | |
95 * faster if the list is in chronological order. | |
96 * @param {function} callback Call this function when finished. | |
97 */ | |
98 this.ensureLoaded = function(commitList, callback) { | |
99 var thisInstance = this; | |
100 var areAllLoaded = function() { | |
101 for (var i = 0; i < commitList.length; i++) { | |
102 var commit = commitList[i]; | |
103 if (!thisInstance.gotRev[commit]) { | |
104 thisInstance.loadCommits(commit, areAllLoaded); | |
105 return; | |
106 } | |
107 } | |
108 callback(); | |
109 }; | |
110 areAllLoaded(); | |
111 } | |
112 }, | |
113 | |
114 /** | |
115 * Convenience function for populating a ComboBox or ListBox with a list of | |
116 * items. Existing items will be cleared. | |
117 * | |
118 * @param {string} menuId ID of the menu to populate. | |
119 * @param {Array.<string>} items A list of strings to insert into the menu. | |
120 */ | |
121 populateMenu: function(menuId, items) { | |
122 var menu = document.getElementById(menuId); | |
123 menu.options.length = 0; | |
124 for (var itemIdx = 0; itemIdx < items.length; itemIdx++) { | |
125 var item = items[itemIdx]; | |
126 var newOption = document.createElement("option"); | |
127 newOption.text = item; | |
128 newOption.value = item; | |
129 menu.options.add(newOption); | |
130 } | |
131 }, | |
132 | |
133 /** | |
134 * Load the global_variables.json file. | |
135 * | |
136 * @param {function} callback Call this function when finished. | |
137 */ | |
138 loadGlobalVariables: function(callback) { | |
139 var thisInstance = this; | |
140 var url = this.googlesourceURL + "/buildbot/+/master/site_config/" + | |
141 "global_variables.json?format=TEXT"; | |
142 try { | |
143 var request = new XMLHttpRequest(); | |
144 } catch (error) { | |
145 alert(error); | |
146 } | |
147 request.open("GET", url, true); | |
148 request.onreadystatechange = function() { | |
149 if (request.readyState != 4) { return; } | |
150 thisInstance.globalVariables = JSON.parse(atob(request.responseText)); | |
151 callback(); | |
152 } | |
153 request.send(); | |
154 }, | |
155 | |
156 /** | |
157 * Retrieve the given variable. | |
158 * | |
159 * @param {function(string)} callback Call this function with the value of the | |
160 * requested variable, as defined in the global variables file, or | |
161 * undefined if it is not found. | |
162 */ | |
163 getVariable: function(varName, callback) { | |
164 var thisInstance = this; | |
165 var readGlobalVariable = function() { | |
166 if (!thisInstance.globalVariables[varName]) { | |
167 callback(undefined); | |
168 } | |
169 callback(thisInstance.globalVariables[varName].value); | |
170 }; | |
171 if (!this.globalVariables) { | |
172 this.loadGlobalVariables(readGlobalVariable); | |
173 } else { | |
174 readGlobalVariable(); | |
175 } | |
176 }, | |
177 | |
178 /** | |
179 * Information about a single build. | |
180 */ | |
181 Build: function(builder, number, revision, result, startTime, endTime, steps) { | |
182 this.builder = builder; | |
183 this.number = number; | |
184 this.revision = revision; | |
185 this.result = result; | |
186 this.startTime = startTime; | |
187 this.endTime = endTime; | |
188 this.elapsedTime = endTime - startTime; | |
189 this.steps = steps; | |
190 | |
191 /** | |
192 * getBuilder | |
193 * | |
194 * @return {string} The name of the builder who owns this build. | |
195 */ | |
196 this.getBuilder = function() { return this.builder; } | |
197 | |
198 /** | |
199 * getNumber | |
200 * | |
201 * @return {number} The build number. | |
202 */ | |
203 this.getNumber = function() { return this.number; } | |
204 | |
205 /** | |
206 * getResult | |
207 * | |
208 * @return {number} The result of the build. Will be 0 iff the build | |
209 * succeeded. | |
210 */ | |
211 this.getResult = function() { return this.result; } | |
212 | |
213 /** | |
214 * getRevision | |
215 * | |
216 * @return {number} The revision number of this build. | |
217 */ | |
218 this.getRevision = function() { return this.revision; } | |
219 | |
220 /** | |
221 * getStartTime | |
222 * | |
223 * @return {number} Start time of the build in UNIX seconds from epoch. | |
224 */ | |
225 this.getStartTime = function() { return this.startTime; } | |
226 | |
227 /** | |
228 * getEndTime | |
229 * | |
230 * @return {number} End time of the build in UNIX seconds from epoch. | |
231 */ | |
232 this.getEndTime = function() { return this.endTime; } | |
233 | |
234 /** | |
235 * getElapsedTime | |
236 * | |
237 * @return {number} Elapsed time of the build in seconds. | |
238 */ | |
239 this.getElapsedTime = function() { return this.elapsedTime; } | |
240 | |
241 /** | |
242 * getStartTime | |
243 * | |
244 * @return {Array.<BuildStep>} Information about the steps of this build. | |
245 */ | |
246 this.getSteps = function() { return this.steps; } | |
247 }, | |
248 | |
249 /** | |
250 * Information about a single build step. | |
251 */ | |
252 BuildStep: function(name, elapsedTime, result, stdio) { | |
253 this.name = name; | |
254 this.elapsedTime = elapsedTime; | |
255 this.result = result; | |
256 this.stdio = stdio; | |
257 | |
258 /** | |
259 * getName | |
260 * | |
261 * @return {string} The name of the build step. | |
262 */ | |
263 this.getName = function() { return this.name; } | |
264 | |
265 /** | |
266 * getElapsedTime | |
267 * | |
268 * @return {number} Elapsed time of the build step in seconds. | |
269 */ | |
270 this.getElapsedTime = function() { return this.elapsedTime; } | |
271 | |
272 /** | |
273 * getResult | |
274 * | |
275 * @return {number} The result of the build. Will be 0 iff the build step | |
276 * succeeded. | |
277 */ | |
278 this.getResult = function() { return this.result; } | |
279 | |
280 /** | |
281 * getStdio | |
282 * | |
283 * @return {string} URL to the log output for this build step. | |
284 */ | |
285 this.getStdio = function() { return this.stdio; } | |
286 }, | |
287 | |
288 /** | |
289 * Information about a builder. | |
290 */ | |
291 Builder: function(name, master, basedir, cachedBuilds, category, currentBuilds, | |
292 slaves, state) { | |
293 this.name = name; | |
294 this.master = master; | |
295 this.basedir = basedir; | |
296 // alreadyLoadedBuilds differs from the passed-in cachedBuilds; the passed-in | |
297 // parameter refers to the builds which the build master has cached. This | |
298 // property is a set of builds which have been loaded from the build master | |
299 // and stored here for future use. | |
300 this.alreadyLoadedBuilds = {}; | |
301 this.category = category; | |
302 this.currentBuilds = currentBuilds; | |
303 this.lastBuild = cachedBuilds[cachedBuilds.length - 1]; | |
304 this.slaves = slaves; | |
305 this.state = state; | |
306 | |
307 /** | |
308 * getName | |
309 * | |
310 * @return {string} The name of the builder. | |
311 */ | |
312 this.getName = function() { return this.name; } | |
313 | |
314 /** | |
315 * getBaseDir | |
316 * | |
317 * @return {string} Directory on the build slave machine in which build | |
318 * information is stored. This is typically the same as the builder name. | |
319 */ | |
320 this.getBaseDir = function() { return this.basedir; } | |
321 | |
322 /** | |
323 * getCategory | |
324 * | |
325 * @return {string} Category of this builder. This is the heading under | |
326 * which the builder is placed on the buildbot web page. | |
327 */ | |
328 this.getCategory = function() { return this.category; } | |
329 | |
330 /** | |
331 * getCurrentBuilds | |
332 * | |
333 * @return {Array.<number>} List of currently-running builds for this | |
334 * builder. | |
335 */ | |
336 this.getCurrentBuilds = function() { return this.currentBuilds; } | |
337 | |
338 /** | |
339 * getLastBuild | |
340 * | |
341 * @return {number} The build number of the last completed build for this | |
342 * builder. | |
343 */ | |
344 this.getLastBuild = function() { return this.lastBuild; } | |
345 | |
346 /** | |
347 * getSlaves | |
348 * | |
349 * @return {Array.<string>} List of known build slaves which are capable of | |
350 * running builds for this builder. | |
351 */ | |
352 this.getSlaves = function() { return this.slaves; } | |
353 | |
354 /** | |
355 * getState | |
356 * | |
357 * @return {string} Current status of the builder. Either "building" or | |
358 * "idle." | |
359 */ | |
360 this.getState = function() { return this.state; } | |
361 | |
362 /** | |
363 * Obtain information about a single build from the buildbot master. | |
364 * | |
365 * @param {number} build The number of the build which should be retrieved. | |
366 * @param {boolean} loadUnfinished Whether or not to load data for unfinished | |
367 * builds. | |
368 * @param {boolean} loadUnknownRevs Whether or not to load data for builds | |
369 * which do not have an associated revision number. This occurs when the | |
370 * source checkout step fails. | |
371 * @param {function(string, number, Build|null)} callback Call this function | |
372 * with a builder name, build number, and a Build instance containing | |
373 * information about the requested build when loaded. | |
374 */ | |
375 this.loadDataForBuild = function(build, loadUnfinished, loadUnknownRevs, | |
376 callback) { | |
377 var thisInstance = this; | |
378 var buildURL = "builders/" + this.getName() + "/builds/" + build + "/steps"; | |
379 this.master.loadData(buildURL, function(buildData) { | |
380 // Build step results. | |
381 var SUCCESS = 0; | |
382 var FAILURE = 2; | |
383 var SKIPPED = 3; | |
384 | |
385 var steps = []; | |
386 var result = 0; | |
387 var startTime = 0; | |
388 var endTime = 0; | |
389 var revision = undefined; | |
390 var gotRevisionStr = "got_revision: "; | |
391 for (var step in buildData) { | |
392 var stepData = buildData[step]; | |
393 if (stepData["isStarted"] && !stepData["isFinished"] && | |
394 !loadUnfinished) { | |
395 // If the build isn't finished, ignore it | |
396 callback(thisInstance.getName(), build, null); | |
397 } | |
398 if (!stepData["isStarted"]) { | |
399 continue; | |
400 } | |
401 if (stepData["name"] == "Update") { | |
402 // The buildbot's JSON interface stores results as an array in which | |
403 // the first element is an integer indicating success or failure. | |
404 if (stepData["isStarted"] && stepData["isFinished"] && | |
405 stepData["results"][0] == 0) { | |
406 // The "text" field is an array containing extra information about | |
407 // the build step. In the case of the Update step, its second | |
408 // element is a string indicating the revision obtained for the | |
409 // current build. | |
410 revision = stepData["text"][1].substring(gotRevisionStr.length); | |
411 } else if (!loadUnknownRevs) { | |
412 // If the Update step failed, we can't attach a revision, so we have | |
413 // to ignore this build. | |
414 console.log("Warning: Can't get a revision for build #" + build + | |
415 ". Skipping."); | |
416 callback(thisInstance.getName(), build, null); | |
417 } | |
418 } | |
419 var times = stepData["times"]; | |
420 var stepTime = times[1] - times[0]; | |
421 if (startTime == 0) { | |
422 startTime = times[0]; | |
423 } | |
424 endTime = times[1]; | |
425 var stdout = null; | |
426 try { | |
427 stdout = stepData["logs"][0][1]; | |
428 } catch(e) { | |
429 stdout = "None"; | |
430 } | |
431 | |
432 var buildStep = new skiaTools.BuildStep(stepData["name"], stepTime, | |
433 stepData["results"][0], stdout); | |
434 steps.push(buildStep); | |
435 | |
436 if (buildStep.getResult() != SUCCESS && | |
437 buildStep.getResult() != SKIPPED) { | |
438 result = FAILURE; | |
439 } | |
440 } | |
441 if (revision == undefined) { | |
442 console.log("Warning: could not find a revision for build #" + build); | |
443 } | |
444 | |
445 callback(thisInstance.getName(), build, new skiaTools.Build( | |
446 thisInstance.getName(), build, revision, result, startTime, endTime, | |
447 steps)); | |
448 }); | |
449 } | |
450 | |
451 /** | |
452 * Obtain information about the builds for a single builder. Works backward | |
453 * from the last known build, loading builds until the requested number of | |
454 * builds has been fulfilled or all of the builder's builds have been loaded. | |
455 * | |
456 * @param {number} numBuilds The number of builds to load. | |
457 * @param {function(string, Object)} callback Call this function with the | |
458 * builder name and a dictionary of instances of Build for the builder, | |
459 * indexed by revision. | |
460 */ | |
461 this.loadBuilds = function(numBuilds, callback) { | |
462 var lastBuild = this.getLastBuild(); | |
463 var loading = []; | |
464 var thisInstance = this; | |
465 var doneLoading = function() { | |
466 var data = {}; | |
467 for (var buildNum = lastBuild; buildNum >= lastBuild - numBuilds && buildN
um >= 0; buildNum--) { | |
468 var build = thisInstance.alreadyLoadedBuilds[buildNum]; | |
469 if (build) { | |
470 data[buildNum] = build; | |
471 } | |
472 } | |
473 callback(thisInstance.getName(), data); | |
474 }; | |
475 for (var buildNum = lastBuild; buildNum >= lastBuild - numBuilds && buildNum
>= 0; buildNum--) { | |
476 var build = this.alreadyLoadedBuilds[buildNum]; | |
477 if (!build) { | |
478 loading.push(buildNum); | |
479 this.loadDataForBuild(buildNum, false, false, function(builder, buildNum
, build) { | |
480 loading.splice(loading.indexOf(buildNum), 1); | |
481 thisInstance.alreadyLoadedBuilds[buildNum] = build; | |
482 if (loading.length == 0) { | |
483 doneLoading(); | |
484 } | |
485 }); | |
486 } else { | |
487 data[buildNum] = build; | |
488 if (loading.length == 0) { | |
489 doneLoading(); | |
490 } | |
491 } | |
492 } | |
493 } | |
494 }, | |
495 | |
496 /** | |
497 * Information about a build slave. | |
498 */ | |
499 BuildSlave: function(admin, builders, connected, currentBuilds, host, name, | |
500 master, version) { | |
501 this.admin = admin; | |
502 this.builders = builders; | |
503 this.connected = connected; | |
504 this.currentBuilds = currentBuilds; | |
505 this.host = host; | |
506 this.name = name; | |
507 this.master = master; | |
508 this.version = version; | |
509 | |
510 /** | |
511 * getAdmin | |
512 * | |
513 * @return {string} Usernames of buildbot maintainers. | |
514 */ | |
515 this.getAdmin = function() { return this.admin; } | |
516 | |
517 /** | |
518 * getBuilders | |
519 * | |
520 * @return {Object} Dictionary whose keys are builder names and values are | |
521 * lists of build numbers indicating which builds for which builders this | |
522 * slave has performed. | |
523 */ | |
524 this.getBuilders = function() { return this.builders; } | |
525 | |
526 /** | |
527 * isConnected | |
528 * | |
529 * @return {boolean} Whether or not the build slave is currently connected | |
530 * to the build master. | |
531 */ | |
532 this.isConnected = function() { return this.connected; } | |
533 | |
534 /** | |
535 * getCurrentBuilds | |
536 * | |
537 * @return {Array.<Object>} List of dictionaries containing information about | |
538 * currently-running builds on this slave. | |
539 */ | |
540 this.getCurrentBuilds = function() { return this.currentBuilds; } | |
541 | |
542 /** | |
543 * getHost | |
544 * | |
545 * @return {string} Hostname of this build slave. | |
546 */ | |
547 this.getHost = function() { return this.host; } | |
548 | |
549 /** | |
550 * getName | |
551 * | |
552 * @return {string} The name of this build slave. | |
553 */ | |
554 this.getName = function() { return this.name; } | |
555 | |
556 /** | |
557 * getVersion | |
558 * | |
559 * @return {string} Version of BuildBot which this build slave is running. | |
560 */ | |
561 this.getVersion = function() { return this.version; } | |
562 | |
563 /** | |
564 * Obtain information about recent builds for a build slave. | |
565 * | |
566 * @param {number} rangeMin Builds before this time will not be loaded. | |
567 * @param {number} currentTime The time at which {@code slaveDict} was | |
568 * obtained from the build master. This value is passed in rather than | |
569 * obtaining the current time at the call of this function in case the | |
570 * state of the build slave has changed since {@code slaveDict} was | |
571 * obtained. | |
572 * @param {function(Array.<Build>)} callback Call this function with a list | |
573 * of Build objects when loaded. | |
574 */ | |
575 this.loadBuilds = function(rangeMin, currentTime, callback) { | |
576 var builders = this.getBuilders(); | |
577 var buildList = []; | |
578 var thisInstance = this; | |
579 var getRunningBuilds = function() { | |
580 var runningBuilds = thisInstance.getCurrentBuilds(); | |
581 for (var buildIdx = 0; buildIdx < runningBuilds.length; buildIdx++) { | |
582 var buildData = runningBuilds[buildIdx]; | |
583 buildList.push(new thisInstance.Build(buildData["builderName"], | |
584 buildData["number"], | |
585 -1, 0, buildData["times"][0], | |
586 currentTime + 1, [])); | |
587 } | |
588 buildList.sort(function(a, b) { | |
589 return a.getStartTime() - b.getStartTime(); | |
590 }); | |
591 callback(buildList); | |
592 }; | |
593 | |
594 var loadingBuilders = {}; | |
595 | |
596 var gotBuild = function(builder, buildNum, build) { | |
597 buildList.push(build); | |
598 if (build != null && | |
599 (build.getEndTime() < rangeMin || | |
600 build.getStartTime() < rangeMin || | |
601 build.getNumber() == 0)) { | |
602 delete loadingBuilders[builder]; | |
603 if (Object.keys(loadingBuilders).length == 0) { | |
604 getRunningBuilds(); | |
605 } | |
606 } else { | |
607 var builderObj = loadingBuilders[builder]; | |
608 builderObj.loadDataForBuild(buildNum - 1, true, true, gotBuild); | |
609 } | |
610 } | |
611 | |
612 for (var builder in builders) { | |
613 var builderObj = new skiaTools.Builder(builder, | |
614 this.master, | |
615 null, | |
616 [-1], | |
617 null, | |
618 null, | |
619 null, | |
620 null); | |
621 var builds = builders[builder]; | |
622 if (builds.length > 0) { | |
623 loadingBuilders[builder] = builderObj; | |
624 builderObj.loadDataForBuild(builds[0], true, true, gotBuild); | |
625 } | |
626 } | |
627 } | |
628 }, | |
629 | |
630 loadMasterList: function(callback) { | |
631 var masters = []; | |
632 var thisInstance = this; | |
633 var gotVariables = function() { | |
634 for (var key in thisInstance.globalVariables) { | |
635 var suffixIndex = key.indexOf( | |
636 skiaTools.masterHostSuffix, | |
637 key.length - skiaTools.masterHostSuffix.length); | |
638 if (suffixIndex !== -1) { | |
639 masters.push(key.substring(0, suffixIndex)); | |
640 } | |
641 } | |
642 callback(masters); | |
643 } | |
644 if (!thisInstance.globalVariables) { | |
645 this.loadGlobalVariables(gotVariables); | |
646 } else { | |
647 gotVariables(); | |
648 } | |
649 }, | |
650 | |
651 Master: function(name) { | |
652 this.name = name; | |
653 | |
654 var thisInstance = this; | |
655 skiaTools.getVariable(name + skiaTools.masterHostSuffix, function(host) { | |
656 thisInstance.host = host; | |
657 }); | |
658 skiaTools.getVariable(name + skiaTools.masterPortSuffix, function(port) { | |
659 thisInstance.port = port; | |
660 }); | |
661 | |
662 /** | |
663 * getName | |
664 * | |
665 * @return {string} Name of this build master. | |
666 */ | |
667 this.getName = function() { return this.name; } | |
668 | |
669 /** | |
670 * getHost | |
671 * | |
672 * @return {string} Hostname of this build master. | |
673 */ | |
674 this.getHost = function() { return this.host; } | |
675 | |
676 /** | |
677 * getPort | |
678 * | |
679 * @return {string} Port number of this build master. | |
680 */ | |
681 this.getPort = function() { return this.port; } | |
682 | |
683 /** | |
684 * loadData | |
685 * | |
686 * Sends an {@code XMLHttpRequest} to the buildbot master, parses the JSON in | |
687 * the response, and returns a dictionary. | |
688 * | |
689 * @param {string} subdir Subdirectory of the buildbot master's JSON | |
690 * interface to query. | |
691 * @param {function(Object)} callback Call this function with the decoded | |
692 * JSON data when loaded. | |
693 */ | |
694 this.loadData = function(subdir, callback) { | |
695 try { | |
696 var request = new XMLHttpRequest(); | |
697 } catch (error) { | |
698 alert(error); | |
699 } | |
700 var url = "http://" + this.getHost() + ":" + this.getPort() + "/json/" + | |
701 subdir; | |
702 request.open("GET", url, true); | |
703 request.onreadystatechange = function() { | |
704 if (request.readyState != 4) { return; } | |
705 callback(JSON.parse(request.responseText)); | |
706 } | |
707 request.send(null); | |
708 } | |
709 | |
710 /** | |
711 * Obtain high-level information about known builders. | |
712 * | |
713 * @param {Array.<Builder>} callback Call this function with a list of | |
714 * Builder objects when loaded. | |
715 */ | |
716 this.loadBuilders = function(callback) { | |
717 var thisInstance = this; | |
718 this.loadData("builders", function(buildersData) { | |
719 var builders = []; | |
720 for (var builderName in buildersData) { | |
721 var builderData = buildersData[builderName]; | |
722 var builder = new skiaTools.Builder(builderName, | |
723 thisInstance, | |
724 builderData["basedir"], | |
725 builderData["cachedBuilds"], | |
726 builderData["category"], | |
727 builderData["currentBuilds"], | |
728 builderData["slaves"], | |
729 builderData["state"]); | |
730 builders.push(builder); | |
731 } | |
732 callback(builders); | |
733 }); | |
734 } | |
735 | |
736 /** | |
737 * Obtain high-level information about known build slaves. | |
738 * | |
739 * @param {function(Array.<BuildSlave>)} callback Call this function with a | |
740 * list of BuildSlave objects when loaded. | |
741 */ | |
742 this.loadSlaves = function(callback) { | |
743 var slaves = []; | |
744 var thisInstance = this; | |
745 this.loadData("slaves", function(slavesData) { | |
746 for (var slave in slavesData) { | |
747 var slaveData = slavesData[slave]; | |
748 var currentBuilds = slaveData["currentBuilds"]; | |
749 if (currentBuilds == undefined) { | |
750 currentBuilds = []; | |
751 } | |
752 slaves.push(new skiaTools.BuildSlave(slaveData["admin"], | |
753 slaveData["builders"], | |
754 slaveData["connected"], | |
755 currentBuilds, | |
756 slaveData["host"], | |
757 slaveData["name"], | |
758 thisInstance, | |
759 slaveData["version"])); | |
760 } | |
761 callback(slaves); | |
762 }); | |
763 } | |
764 | |
765 }, | |
766 | |
767 }; | |
OLD | NEW |