@@ -22,21 +22,26 @@ import (
22
22
gpb "go.chromium.org/luci/common/proto/gitiles"
23
23
"go.chromium.org/luci/grpc/prpc"
24
24
rdbpb "go.chromium.org/luci/resultdb/proto/v1"
25
+ spb "go.chromium.org/luci/swarming/proto/api_v2"
25
26
"golang.org/x/sync/errgroup"
26
27
"google.golang.org/protobuf/types/known/fieldmaskpb"
27
28
"google.golang.org/protobuf/types/known/timestamppb"
28
29
)
29
30
30
- const resultDBHost = "results.api.cr.dev"
31
- const crBuildBucketHost = "cr-buildbucket.appspot.com"
32
- const gitilesHost = "go.googlesource.com"
31
+ const (
32
+ crBuildBucketHost = "cr-buildbucket.appspot.com"
33
+ gitilesHost = "go.googlesource.com"
34
+ resultDBHost = "results.api.cr.dev"
35
+ swarmingHost = "chromium-swarm.appspot.com"
36
+ )
33
37
34
38
// LUCIClient is a LUCI client.
35
39
type LUCIClient struct {
36
40
HTTPClient * http.Client
37
- GitilesClient gpb.GitilesClient
38
- BuildsClient bbpb.BuildsClient
41
+ BotsClient spb.BotsClient
39
42
BuildersClient bbpb.BuildersClient
43
+ BuildsClient bbpb.BuildsClient
44
+ GitilesClient gpb.GitilesClient
40
45
ResultDBClient rdbpb.ResultDBClient
41
46
42
47
// TraceSteps controls whether to log each step name as it's executed.
@@ -68,8 +73,13 @@ func NewLUCIClient(nProc int) *LUCIClient {
68
73
C : c ,
69
74
Host : resultDBHost ,
70
75
})
76
+ botsClient := spb .NewBotsClient (& prpc.Client {
77
+ C : c ,
78
+ Host : swarmingHost ,
79
+ })
71
80
return & LUCIClient {
72
81
HTTPClient : c ,
82
+ BotsClient : botsClient ,
73
83
GitilesClient : gitilesClient ,
74
84
BuildsClient : buildsClient ,
75
85
BuildersClient : buildersClient ,
@@ -135,6 +145,12 @@ type Failure struct {
135
145
LogText string
136
146
}
137
147
148
+ type Bot struct {
149
+ ID string
150
+ Dead bool
151
+ Quarantined bool
152
+ }
153
+
138
154
// ListCommits fetches the list of commits from Gerrit.
139
155
func (c * LUCIClient ) ListCommits (ctx context.Context , repo , goBranch string , since time.Time ) []Commit {
140
156
if c .TraceSteps {
@@ -666,6 +682,58 @@ func (c *LUCIClient) fetchLogsForBuild(r *BuildResult) {
666
682
}
667
683
}
668
684
685
+ // filter enables the creation of filter functions which can remove bots
686
+ // from the list of returned bots.
687
+ type filter func (bot * spb.BotInfo ) bool
688
+
689
+ // filterOutDarwin filters out darwin machines which no longer exist.
690
+ func filterOutDarwin (bot * spb.BotInfo ) bool {
691
+ return strings .HasPrefix (bot .BotId , "darwin-" )
692
+ }
693
+
694
+ // ListBrokenBots lists bots that are either dead or quarantined. This list is limited
695
+ // to the bots in the shared-workers pool since they are generally the contributor provided
696
+ // bots. Arbitrary filters can be applied to filter out builders.
697
+ func (c * LUCIClient ) ListBrokenBots (ctx context.Context , filters ... filter ) ([]Bot , error ) {
698
+ if c .TraceSteps {
699
+ log .Println ("ListBrokenBots" )
700
+ }
701
+ checkFilters := func (bot * spb.BotInfo ) bool {
702
+ for _ , f := range filters {
703
+ if f (bot ) {
704
+ return true
705
+ }
706
+ }
707
+ return false
708
+ }
709
+ var brokenBots []Bot
710
+ var cursor string
711
+ nextCursor:
712
+ resp , err := c .BotsClient .ListBots (ctx , & spb.BotsRequest {
713
+ Limit : 1000 ,
714
+ Cursor : cursor ,
715
+ Dimensions : []* spb.StringPair {
716
+ & spb.StringPair {Key : "pool" , Value : "luci.golang.shared-workers" },
717
+ },
718
+ })
719
+ if err != nil {
720
+ return nil , err
721
+ }
722
+ for _ , bot := range resp .GetItems () {
723
+ if checkFilters (bot ) {
724
+ continue
725
+ }
726
+ if bot .IsDead || bot .Quarantined {
727
+ brokenBots = append (brokenBots , Bot {ID : bot .BotId , Dead : bot .IsDead , Quarantined : bot .Quarantined })
728
+ }
729
+ }
730
+ if resp .GetCursor () != "" {
731
+ cursor = resp .GetCursor ()
732
+ goto nextCursor
733
+ }
734
+ return brokenBots , nil
735
+ }
736
+
669
737
func fetchURL (url string ) string {
670
738
resp , err := http .Get (url )
671
739
if err != nil {
0 commit comments