Skip to content

Commit 88066a8

Browse files
dmitshurgopherbot
authored andcommitted
cmd/coordinator/internal/lucipoll: improve availability and precision
of x/ repo build results When the build.golang.org dashboard front page shows x/ repo results, it shows builds for the very latest x/ repo head commit built at the head release branch (or main branch) of the main Go repo. There are two ways a LUCI build becomes available for the corresponding "latest x/ repo head and latest Go repo release branch" pair of commits: 1. a new Go repo commit invokes a build for each x/ repo main head 2. a new x/ repo commit invokes a build for Go release branch head Some x/ repos are low volume, so most of the time its (latest, latest) pair comes from first type of trigger. Other x/ repos are high volume, and their (latest, latest) pair often comes from the second trigger. Previously, package lucipoll was considering x/ repo LUCI builds that came from the second trigger, and so high volume x/ repos had results visible most of the time. This change adds a loop (over the main branch and two supported release branches) to fetch x/ repo results that were invoked via the Go repo side, so now low volume x/ repos are handled as well. It also reads build output properties and uses them to filter out builds that weren't for the exact (Go repo, x/ repo) commit pair that the build dashboard intends to display. For golang/go#65913. Change-Id: I231e0b1c49bfafb792301dd7dad9a301aca4b924 Reviewed-on: https://go-review.googlesource.com/c/build/+/574558 Auto-Submit: Dmitri Shuralyov <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 6a613cb commit 88066a8

File tree

2 files changed

+159
-16
lines changed

2 files changed

+159
-16
lines changed

cmd/coordinator/internal/legacydash/ui.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,7 @@ func addLUCIBuilders(luci lucipoll.Snapshot, builders map[string]bool, xRepos []
795795
repoName := r.Package.Name
796796
for _, b := range luci.Builders {
797797
if b.Repo != repoName || b.GoBranch != goBranch {
798-
// Filter out builders whose repo and Gobranch doesn't match.
798+
// Filter out builders whose repo or Go branch doesn't match.
799799
continue
800800
}
801801
shortGoBranch := "tip"

cmd/coordinator/internal/lucipoll/lucipoll.go

+158-15
Original file line numberDiff line numberDiff line change
@@ -155,41 +155,139 @@ func runOnce(
155155
// Fetch builds for Go repo commits.
156156
for _, c := range dashResp.Commits {
157157
repo, commit := "go", c.Commit
158-
buildList, err := fetchBuildsForCommit(ctx, buildsCl, repo, commit, "id", "builder.builder", "status", "input.gitiles_commit", "input.properties")
158+
buildList, err := fetchBuildsForCommit(ctx, buildsCl, repo, commit, "id", "builder.builder", "status", "input.gitiles_commit")
159159
if err != nil {
160160
return nil, nil, err
161161
}
162162
total += len(buildList)
163163
for _, b := range buildList {
164+
if c := b.GetInput().GetGitilesCommit(); c.Project != repo {
165+
return nil, nil, fmt.Errorf(`internal error: in Go repo commit loop, c.Project is %q but expected it to be "go"`, c.Project)
166+
} else if c.Id != commit {
167+
return nil, nil, fmt.Errorf("internal error: in Go repo commit loop, c.Id is %q but expected it to be %q", c.Id, commit)
168+
}
169+
switch b.GetStatus() {
170+
case bbpb.Status_STARTED, bbpb.Status_SUCCESS, bbpb.Status_FAILURE, bbpb.Status_INFRA_FAILURE:
171+
default:
172+
// Skip builds with other statuses at this time.
173+
// Such builds can be included when the callers deem it useful.
174+
continue
175+
}
164176
builder, ok := builders[b.GetBuilder().GetBuilder()]
165177
if !ok {
166178
// A build that isn't associated with a current builder we're tracking.
167179
// It might've been removed, or has a known issue. Skip this build too.
168180
continue
181+
} else if builder.Repo != "go" {
182+
// Not a Go repo build. Those are handled below, so out of scope here.
183+
continue
169184
}
170-
c := b.GetInput().GetGitilesCommit()
171-
if c.Project != builder.Repo {
172-
// A build that was triggered from a different project than the builder is for.
173-
// If the build hasn't completed, the exact repo commit hasn't been chosen yet.
174-
// For now such builds are not represented in the simple model of this package,
175-
// so skip it.
185+
if builds[repo] == nil {
186+
builds[repo] = make(map[string]map[string]Build)
187+
}
188+
if builds[repo][commit] == nil {
189+
builds[repo][commit] = make(map[string]Build)
190+
}
191+
builds[repo][commit][b.GetBuilder().GetBuilder()] = Build{
192+
ID: b.GetId(),
193+
BuilderName: b.GetBuilder().GetBuilder(),
194+
Status: b.GetStatus(),
195+
}
196+
used++
197+
}
198+
}
199+
// Fetch builds for the single latest commit of each golang.org/x repo,
200+
// ones that were invoked from the Go repository side.
201+
var repoHeads = make(map[string]string) // A repo → head commit ID map.
202+
for _, rh := range dashResp.RepoHeads {
203+
repoHeads[rh.GerritProject] = rh.Commit.Commit
204+
}
205+
for _, r := range dashResp.Releases {
206+
repo, commit := "go", r.GetBranchCommit()
207+
buildList, err := fetchBuildsForCommit(ctx, buildsCl, repo, commit, "id", "builder.builder", "status", "input.gitiles_commit", "output.properties")
208+
if err != nil {
209+
return nil, nil, err
210+
}
211+
total += len(buildList)
212+
for _, b := range buildList {
213+
if c := b.GetInput().GetGitilesCommit(); c.Project != "go" {
214+
return nil, nil, fmt.Errorf(`internal error: in x/ repo loop for builds invoked from the Go repo side, c.Project is %q but expected it to be "go"`, c.Project)
215+
}
216+
switch b.GetStatus() {
217+
case bbpb.Status_STARTED, bbpb.Status_SUCCESS, bbpb.Status_FAILURE, bbpb.Status_INFRA_FAILURE:
218+
default:
219+
// Skip builds with other statuses at this time.
220+
// Such builds can be included when the callers deem it useful.
221+
continue
222+
}
223+
builder, ok := builders[b.GetBuilder().GetBuilder()]
224+
if !ok {
225+
// A build that isn't associated with a current builder we're tracking.
226+
// It might've been removed, or has a known issue. Skip this build too.
227+
continue
228+
} else if builder.Repo == "go" {
229+
// A Go repo build. Those were handled above, so out of scope here.
230+
continue
231+
}
232+
var buildOutputProps struct {
233+
Sources []struct {
234+
GitilesCommit struct {
235+
Project string
236+
Ref string
237+
Id string
238+
}
239+
}
240+
}
241+
if data, err := b.GetOutput().GetProperties().MarshalJSON(); err != nil {
242+
return nil, nil, fmt.Errorf("marshaling build output properties to JSON failed: %v", err)
243+
} else if err := json.Unmarshal(data, &buildOutputProps); err != nil {
244+
return nil, nil, err
245+
}
246+
repoCommit, ok := func() (string, bool) {
247+
for _, s := range buildOutputProps.Sources {
248+
if c := s.GitilesCommit; c.Project == builder.Repo {
249+
if c.Ref != "refs/heads/master" {
250+
panic(fmt.Errorf(`internal error: in x/ repo loop for project %s, c.Ref != "refs/heads/master"`, c.Project))
251+
}
252+
return c.Id, true
253+
}
254+
}
255+
return "", false
256+
}()
257+
if !ok && b.GetStatus() == bbpb.Status_STARTED {
258+
// A started build that hasn't selected the x/ repo commit yet.
259+
// As an approximation, assume it'll pick the latest x/ repo head commit.
260+
repoCommit = repoHeads[builder.Repo]
261+
} else if !ok {
262+
// Repo commit not found in output properties, and it's not a started build.
263+
// As an example, this can happen if a build failed due to an infra failure
264+
// early on, before selecting the x/ repo commit. Skip such builds.
265+
continue
266+
}
267+
if repoCommit != repoHeads[builder.Repo] {
268+
// Skip builds that are not for the x/ repository's head commit.
176269
continue
177270
}
178271
if builds[builder.Repo] == nil {
179272
builds[builder.Repo] = make(map[string]map[string]Build)
180273
}
181-
if builds[builder.Repo][c.Id] == nil {
182-
builds[builder.Repo][c.Id] = make(map[string]Build)
274+
if builds[builder.Repo][repoCommit] == nil {
275+
builds[builder.Repo][repoCommit] = make(map[string]Build)
183276
}
184-
builds[builder.Repo][c.Id][b.GetBuilder().GetBuilder()] = Build{
277+
builds[builder.Repo][repoCommit][b.GetBuilder().GetBuilder()] = Build{
185278
ID: b.GetId(),
186279
BuilderName: b.GetBuilder().GetBuilder(),
187280
Status: b.GetStatus(),
188281
}
189282
used++
190283
}
191284
}
192-
// Fetch builds for a single commit in each golang.org/x repo.
285+
// Fetch builds for the single latest commit of each golang.org/x repo,
286+
// ones that were invoked from the x/ repository side.
287+
var goHeads = make(map[string]string) // A branch → head commit ID map.
288+
for _, r := range dashResp.Releases {
289+
goHeads[r.GetBranchName()] = r.GetBranchCommit()
290+
}
193291
for _, rh := range dashResp.RepoHeads {
194292
if rh.GerritProject == "go" {
195293
continue
@@ -200,25 +298,71 @@ func runOnce(
200298
continue
201299
}
202300
repo, commit := rh.GerritProject, rh.Commit.Commit
203-
buildList, err := fetchBuildsForCommit(ctx, buildsCl, repo, commit, "id", "builder.builder", "status", "input.gitiles_commit", "input.properties")
301+
buildList, err := fetchBuildsForCommit(ctx, buildsCl, repo, commit, "id", "builder.builder", "status", "input.gitiles_commit", "output.properties")
204302
if err != nil {
205303
return nil, nil, err
206304
}
207305
total += len(buildList)
208306
for _, b := range buildList {
307+
switch b.GetStatus() {
308+
case bbpb.Status_STARTED, bbpb.Status_SUCCESS, bbpb.Status_FAILURE, bbpb.Status_INFRA_FAILURE:
309+
default:
310+
// Skip builds with other statuses at this time.
311+
// Such builds can be included when the callers deem it useful.
312+
continue
313+
}
209314
builder, ok := builders[b.GetBuilder().GetBuilder()]
210315
if !ok {
211316
// A build that isn't associated with a current builder we're tracking.
212317
// It might've been removed, or has a known issue. Skip this build too.
213318
continue
214319
}
320+
var buildOutputProps struct {
321+
Sources []struct {
322+
GitilesCommit struct {
323+
Project string
324+
Ref string
325+
Id string
326+
}
327+
}
328+
}
329+
if data, err := b.GetOutput().GetProperties().MarshalJSON(); err != nil {
330+
return nil, nil, fmt.Errorf("marshaling build output properties to JSON failed: %v", err)
331+
} else if err := json.Unmarshal(data, &buildOutputProps); err != nil {
332+
return nil, nil, err
333+
}
334+
goCommit, ok := func() (string, bool) {
335+
for _, s := range buildOutputProps.Sources {
336+
if c := s.GitilesCommit; c.Project == "go" {
337+
if c.Ref != "refs/heads/"+builder.GoBranch {
338+
panic(fmt.Errorf(`internal error: in Go repo loop, c.Ref != "refs/heads/%s"`, builder.GoBranch))
339+
}
340+
return c.Id, true
341+
}
342+
}
343+
return "", false
344+
}()
345+
if !ok && b.GetStatus() == bbpb.Status_STARTED {
346+
// A started build that hasn't selected the Go repo commit yet.
347+
// As an approximation, assume it'll pick the latest Go repo head commit.
348+
goCommit = goHeads[builder.GoBranch]
349+
} else if !ok {
350+
// Repo commit not found in output properties, and it's not a started build.
351+
// As an example, this can happen if a build failed due to an infra failure
352+
// early on, before selecting the Go repo commit. Skip such builds.
353+
continue
354+
}
355+
if goCommit != goHeads[builder.GoBranch] {
356+
// Skip builds that are not for the Go repository's head commit.
357+
continue
358+
}
215359
c := b.GetInput().GetGitilesCommit()
216360
if c.Project != builder.Repo {
217361
// When fetching builds for commits in x/ repos, it's expected
218362
// that build repo will always match builder repo. This isn't
219363
// true for the main Go repo because it triggers builds for x/
220364
// repos. But x/ repo builds don't trigger builds elsewhere.
221-
return nil, nil, fmt.Errorf("internal error: build repo %q doesn't match builder repo %q", b.GetInput().GetGitilesCommit().GetProject(), builder.Repo)
365+
return nil, nil, fmt.Errorf("internal error: build repo %q doesn't match builder repo %q", c.Project, builder.Repo)
222366
}
223367
if builds[builder.Repo] == nil {
224368
builds[builder.Repo] = make(map[string]map[string]Build)
@@ -234,8 +378,7 @@ func runOnce(
234378
used++
235379
}
236380
}
237-
fmt.Printf("runOnce: aggregate GetBuildsForCommit calls fetched %d builds in %v\n", total, time.Since(t0))
238-
fmt.Printf("runOnce: used %d of those %d builds\n", used, total)
381+
log.Printf("lucipoll.runOnce: aggregate GetBuildsForCommit calls fetched %d builds (and used %d of them) in %v\n", total, used, time.Since(t0))
239382

240383
return builders, builds, nil
241384
}

0 commit comments

Comments
 (0)