blob: 9da4221989c4637b7ae2babbaa234423c798f56a [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package ui
import (
buildbucketpb ""
type BuilderPage struct {
Builder *buildbucketpb.BuilderItem
ScheduledBuilds []*Build
ScheduledBuildsComplete bool
StartedBuilds []*Build
StartedBuildsComplete bool
EndedBuilds []*Build
MachinePool *MachinePool
// Views is a list of links to milo views that reference this builder.
Views []*Link
// PrevPageToken is a token to the previous page.
PrevPageToken string
// NextPageToken is a token to the next page.
NextPageToken string
// HasChanges returns true if at least one ended build has at least one CL.
func (bp *BuilderPage) HasChanges() bool {
for _, b := range bp.EndedBuilds {
if len(b.GetInput().GetGerritChanges()) > 0 {
return true
return false
// Bot wraps a model.Bot to provide a UI specific statuses.
type Bot struct {
Status model.BotStatus
func (b *Bot) Label() *Link {
if b == nil {
return nil
return NewLink(b.Name, b.URL, "bot "+b.Name)
// MachinePool represents the capacity and availability of a builder.
type MachinePool struct {
Total int
Offline int
Idle int
Busy int
Bots []Bot
Dimensions []string
SwarmingHost string
// SwarmingURL returns the swarming bot URL for the machine pool, if available.
func (mp *MachinePool) SwarmingURL() string {
if mp.SwarmingHost == "" {
return ""
u := &url.URL{
Scheme: "https",
Host: mp.SwarmingHost,
Path: "botlist",
RawQuery: url.Values{
"f": mp.Dimensions,
return u.String()
// NewMachinePool calculates stats from a model.Bot and generates a MachinePool.
// This requires a context because setting the UI Status field requires the current time.
func NewMachinePool(c context.Context, botPool *model.BotPool) *MachinePool {
fiveMinAgo := clock.Now(c).Add(-time.Minute * 5)
result := &MachinePool{
Total: len(botPool.Bots),
Bots: make([]Bot, len(botPool.Bots)),
Dimensions: botPool.Descriptor.Dimensions().Format(),
SwarmingHost: botPool.Descriptor.Host(),
for i, bot := range botPool.Bots {
uiBot := Bot{bot, bot.Status} // Wrap the model.Bot
if bot.Status == model.Offline && bot.LastSeen.After(fiveMinAgo) {
// If the bot has been offline for less than 5 minutes, mark it as busy.
uiBot.Status = model.Busy
switch bot.Status {
case model.Idle:
case model.Busy:
case model.Offline:
result.Bots[i] = uiBot
return result