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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ui
import (
"context"
"net/url"
"time"
buildbucketpb "go.chromium.org/luci/buildbucket/proto"
"go.chromium.org/luci/common/clock"
"go.chromium.org/luci/milo/common/model"
)
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 {
model.Bot
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,
}.Encode(),
}
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:
result.Idle++
case model.Busy:
result.Busy++
case model.Offline:
result.Offline++
}
result.Bots[i] = uiBot
}
return result
}