Skip to content

Commit aab8504

Browse files
committed
cmd/coordinator: add SlowBots support, opt-in to different slow trybots
This parses TRY= comments to opt-in to slower/difference trybots. This needs some docs/UI work yet. Updates golang/go#34501 Change-Id: I13a835520d7ac341bc86139b0a2118235bc83e60 Reviewed-on: https://go-review.googlesource.com/c/build/+/201338 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Bryan C. Mills <[email protected]>
1 parent c53d4ba commit aab8504

File tree

5 files changed

+295
-7
lines changed

5 files changed

+295
-7
lines changed

cmd/coordinator/coordinator.go

Lines changed: 105 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"sync"
4343
"sync/atomic"
4444
"time"
45+
"unicode"
4546

4647
"go4.org/syncutil"
4748
grpc "grpc.go4.org"
@@ -1054,7 +1055,8 @@ func (k *tryKey) ChangeTriple() string {
10541055
type trySet struct {
10551056
// immutable
10561057
tryKey
1057-
tryID string // "T" + 9 random hex
1058+
tryID string // "T" + 9 random hex
1059+
slowBots []*dashboard.BuildConfig // any opt-in slower builders to run in a trybot run
10581060

10591061
// wantedAsOf is guarded by statusMu and is used by
10601062
// findTryWork. It records the last time this tryKey was still
@@ -1105,14 +1107,19 @@ var testingKnobSkipBuilds bool
11051107
func newTrySet(work *apipb.GerritTryWorkItem) *trySet {
11061108
key := tryWorkItemKey(work)
11071109
goBranch := key.Branch // assume same as repo's branch for now
1110+
11081111
builders := dashboard.TryBuildersForProject(key.Project, key.Branch, goBranch)
1112+
slowBots := slowBotsFromComments(work, builders)
1113+
builders = append(builders, slowBots...)
1114+
11091115
log.Printf("Starting new trybot set for %v", key)
11101116
ts := &trySet{
11111117
tryKey: key,
11121118
tryID: "T" + randHex(9),
11131119
trySetState: trySetState{
11141120
builds: make([]*buildStatus, 0, len(builders)),
11151121
},
1122+
slowBots: slowBots,
11161123
}
11171124

11181125
// Defensive check that the input is well-formed.
@@ -1237,7 +1244,11 @@ func (ts *trySet) state() trySetState {
12371244
// notifyStarting runs in its own goroutine and posts to Gerrit that
12381245
// the trybots have started on the user's CL with a link of where to watch.
12391246
func (ts *trySet) notifyStarting() {
1240-
msg := "TryBots beginning. Status page: https://farmer.golang.org/try?commit=" + ts.Commit[:8]
1247+
name := "TryBots"
1248+
if len(ts.slowBots) > 0 {
1249+
name = "SlowBots"
1250+
}
1251+
msg := name + " beginning. Status page: https://farmer.golang.org/try?commit=" + ts.Commit[:8]
12411252

12421253
ctx := context.Background()
12431254
if ci, err := gerritClient.GetChangeDetail(ctx, ts.ChangeTriple()); err == nil {
@@ -1382,20 +1393,38 @@ func (ts *trySet) noteBuildComplete(bs *buildStatus) {
13821393
}
13831394

13841395
if remain == 0 {
1385-
score, msg := 1, "TryBots are happy."
1386-
if numFail > 0 {
1396+
name := "TryBots"
1397+
if len(ts.slowBots) > 0 {
1398+
name = "SlowBots"
1399+
}
1400+
1401+
var buf bytes.Buffer
1402+
var score int
1403+
if numFail == 0 {
1404+
score = 1
1405+
fmt.Fprintf(&buf, "%s are happy.\n", name)
1406+
} else if numFail > 0 {
1407+
score = -1
13871408
ts.mu.Lock()
13881409
errMsg := ts.errMsg.String()
13891410
ts.mu.Unlock()
1390-
score, msg = -1, fmt.Sprintf("%d of %d TryBots failed:\n%s\n"+failureFooter,
1391-
numFail, len(ts.builds), errMsg)
1411+
fmt.Fprintf(&buf, "%d of %d %s failed:\n%s\n"+failureFooter,
1412+
numFail, len(ts.builds), name, errMsg)
1413+
}
1414+
if len(ts.slowBots) > 0 {
1415+
fmt.Fprintf(&buf, "Extra slowbot builds that ran:\n")
1416+
for _, c := range ts.slowBots {
1417+
fmt.Fprintf(&buf, "* %s\n", c.Name)
1418+
}
13921419
}
1420+
// TODO: provide a link in the final report that links to a permanent summary page
1421+
// of which builds ran, and for how long.
13931422
if len(benchResults) > 0 {
13941423
// TODO: restore this functionality
13951424
// msg += fmt.Sprintf("\nBenchmark results are available at:\nhttps://perf.golang.org/search?q=cl:%d+try:%s", ts.ci.ChangeNumber, ts.tryID)
13961425
}
13971426
if err := gerritClient.SetReview(context.Background(), ts.ChangeTriple(), ts.Commit, gerrit.ReviewInput{
1398-
Message: msg,
1427+
Message: buf.String(),
13991428
Labels: map[string]int{
14001429
"TryBot-Result": score,
14011430
},
@@ -1857,12 +1886,25 @@ func (st *buildStatus) build() error {
18571886

18581887
func (st *buildStatus) isTry() bool { return st.trySet != nil }
18591888

1889+
func (st *buildStatus) isSlowBot() bool {
1890+
if st.trySet == nil {
1891+
return false
1892+
}
1893+
for _, conf := range st.trySet.slowBots {
1894+
if st.conf == conf {
1895+
return true
1896+
}
1897+
}
1898+
return false
1899+
}
1900+
18601901
func (st *buildStatus) buildRecord() *types.BuildRecord {
18611902
rec := &types.BuildRecord{
18621903
ID: st.buildID,
18631904
ProcessID: processID,
18641905
StartTime: st.startTime,
18651906
IsTry: st.isTry(),
1907+
IsExtra: st.isSlowBot(),
18661908
GoRev: st.Rev,
18671909
Rev: st.SubRevOrGoRev(),
18681910
Repo: st.RepoOrGo(),
@@ -3634,3 +3676,59 @@ func importPathOfRepo(repo string) string {
36343676
}
36353677
return "golang.org/x/" + repo
36363678
}
3679+
3680+
// slowBotsFromComments looks at the TRY= comments from Gerrit (in
3681+
// work) and returns any addition slow trybot build configuration that
3682+
// should be tested in addition to the ones provided in the existing
3683+
// slice.
3684+
func slowBotsFromComments(work *apipb.GerritTryWorkItem, existing []*dashboard.BuildConfig) (extraBuilders []*dashboard.BuildConfig) {
3685+
tryMsg := latestTryMessage(work) // "aix, darwin, linux-386-387, arm64"
3686+
if tryMsg == "" {
3687+
return nil
3688+
}
3689+
if len(tryMsg) > 1<<10 { // arbitrary sanity
3690+
return nil
3691+
}
3692+
3693+
have := map[string]bool{}
3694+
for _, bc := range existing {
3695+
have[bc.Name] = true
3696+
}
3697+
3698+
tryTerms := strings.FieldsFunc(tryMsg, func(c rune) bool {
3699+
return !unicode.IsLetter(c) && !unicode.IsNumber(c) && c != '-' && c != '_'
3700+
})
3701+
for _, bc := range dashboard.Builders {
3702+
if have[bc.Name] {
3703+
continue
3704+
}
3705+
for _, term := range tryTerms {
3706+
if bc.MatchesSlowBotTerm(term) {
3707+
extraBuilders = append(extraBuilders, bc)
3708+
break
3709+
}
3710+
}
3711+
}
3712+
sort.Slice(extraBuilders, func(i, j int) bool {
3713+
return extraBuilders[i].Name < extraBuilders[j].Name
3714+
})
3715+
return
3716+
}
3717+
3718+
func latestTryMessage(work *apipb.GerritTryWorkItem) string {
3719+
// Prioritize exact version matches first
3720+
for i := len(work.TryMessage) - 1; i >= 0; i-- {
3721+
m := work.TryMessage[i]
3722+
if m.Version == work.Version {
3723+
return m.Message
3724+
}
3725+
}
3726+
// Otherwise the latest message at all
3727+
for i := len(work.TryMessage) - 1; i >= 0; i-- {
3728+
m := work.TryMessage[i]
3729+
if m.Message != "" {
3730+
return m.Message
3731+
}
3732+
}
3733+
return ""
3734+
}

cmd/coordinator/coordinator_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"time"
2020

2121
"golang.org/x/build/buildenv"
22+
"golang.org/x/build/dashboard"
2223
"golang.org/x/build/internal/buildgo"
2324
"golang.org/x/build/maintner/maintnerd/apipb"
2425
)
@@ -211,3 +212,41 @@ func TestBuildersJSON(t *testing.T) {
211212
t.Error(buf.String())
212213
}
213214
}
215+
216+
func mustConf(t *testing.T, name string) *dashboard.BuildConfig {
217+
conf, ok := dashboard.Builders[name]
218+
if !ok {
219+
t.Fatalf("unknown builder %q", name)
220+
}
221+
return conf
222+
}
223+
224+
func TestSlowBotsFromComments(t *testing.T) {
225+
existing := []*dashboard.BuildConfig{mustConf(t, "linux-amd64")}
226+
work := &apipb.GerritTryWorkItem{
227+
Version: 2,
228+
TryMessage: []*apipb.TryVoteMessage{
229+
{
230+
Version: 1,
231+
Message: "ios",
232+
},
233+
{
234+
Version: 2,
235+
Message: "arm64, mac aix ",
236+
},
237+
{
238+
Version: 1,
239+
Message: "aix",
240+
},
241+
},
242+
}
243+
extra := slowBotsFromComments(work, existing)
244+
var got []string
245+
for _, bc := range extra {
246+
got = append(got, bc.Name)
247+
}
248+
want := []string{"aix-ppc64", "darwin-amd64-10_14", "linux-arm64-packet"}
249+
if !reflect.DeepEqual(got, want) {
250+
t.Errorf("mismatch:\n got: %q\nwant: %q\n", got, want)
251+
}
252+
}

dashboard/builders.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,81 @@ import (
1919
"golang.org/x/build/types"
2020
)
2121

22+
// slowBotAliases maps short names from TRY= comments to which builder to run.
23+
//
24+
// TODO: we'll likely expand this, or move it, or change the matching
25+
// syntax entirely. This is a first draft.
26+
var slowBotAliases = map[string]string{
27+
// Known missing builders:
28+
"linux-mips": "",
29+
"linux-mips64": "",
30+
"linux-mipsle": "",
31+
"mips": "",
32+
"mips64": "",
33+
"mipsle": "",
34+
"netbsd-arm64": "",
35+
"openbsd-arm": "",
36+
"openbsd-arm64": "",
37+
"nacl-arm": "",
38+
39+
"386": "linux-386",
40+
"aix": "aix-ppc64",
41+
"amd64": "linux-amd64",
42+
"amd64p32": "nacl-amd64p32",
43+
"android": "android-amd64-emu",
44+
"android-386": "android-386-emu",
45+
"android-amd64": "android-amd64-emu",
46+
"android-arm": "android-arm-corellium",
47+
"android-arm64": "android-arm64-corellium",
48+
"arm": "linux-arm",
49+
"arm64": "linux-arm64-packet",
50+
"arm64p32": "nacl-amd64p32",
51+
"darwin": "darwin-amd64-10_14",
52+
"darwin-386": "darwin-386-10_14",
53+
"darwin-amd64": "darwin-amd64-10_14",
54+
"darwin-arm": "darwin-arm-mg912baios",
55+
"darwin-arm64": "darwin-arm64-corellium",
56+
"dragonfly": "dragonfly-amd64",
57+
"freebsd": "freebsd-amd64-12_0",
58+
"freebsd-386": "freebsd-386-12_0",
59+
"freebsd-amd64": "freebsd-amd64-12_0",
60+
"freebsd-arm": "freebsd-arm-paulzhol",
61+
"illumos": "illumos-amd64",
62+
"ios": "darwin-arm64-corellium",
63+
"js": "js-wasm",
64+
"linux": "linux-amd64",
65+
"linux-arm64": "linux-arm64-packet",
66+
"linux-mips64le": "linux-mips64le-mengzhuo",
67+
"linux-ppc64": "linux-ppc64-buildlet",
68+
"linux-ppc64le": "linux-ppc64le-buildlet",
69+
"linux-s390x": "linux-s390x-ibm",
70+
"mac": "darwin-amd64-10_14",
71+
"macos": "darwin-amd64-10_14",
72+
"mips64le": "linux-mips64le-mengzhuo",
73+
"nacl": "nacl-amd64p32",
74+
"nacl-387": "nacl-386",
75+
"nacl-arm64p32": "nacl-amd64p32",
76+
"netbsd": "netbsd-amd64-8_0",
77+
"netbsd-386": "netbsd-386-8_0",
78+
"netbsd-amd64": "netbsd-amd64-8_0",
79+
"netbsd-arm": "netbsd-arm-bsiegert",
80+
"openbsd": "openbsd-amd64-64",
81+
"openbsd-386": "openbsd-386-64",
82+
"openbsd-amd64": "openbsd-amd64-64",
83+
"plan9": "plan9-386-0intro",
84+
"plan9-386": "plan9-386-0intro",
85+
"plan9-amd64": "plan9-amd64-9front",
86+
"ppc64": "linux-ppc64-buildlet",
87+
"ppc64le": "linux-ppc64le-buildlet",
88+
"s390x": "linux-s390x-ibm",
89+
"solaris": "solaris-amd64-oraclerel",
90+
"solaris-amd64": "solaris-amd64-oraclerel",
91+
"wasm": "js-wasm",
92+
"windows": "windows-amd64-2016",
93+
"windows-386": "windows-386-2008",
94+
"windows-amd64": "windows-amd64-2016",
95+
}
96+
2297
// Builders are the different build configurations.
2398
// The keys are like "darwin-amd64" or "linux-386-387".
2499
// This map should not be modified by other packages.
@@ -787,6 +862,13 @@ func (c *BuildConfig) GOARCH() string {
787862
return arch[:i]
788863
}
789864

865+
// MatchesSlowBotTerm reports whether some provided term from a
866+
// TRY=... comment on a Run-TryBot+1 vote on Gerrit should match this
867+
// build config.
868+
func (c *BuildConfig) MatchesSlowBotTerm(term string) bool {
869+
return term != "" && (term == c.Name || slowBotAliases[term] == c.Name)
870+
}
871+
790872
// FilePathJoin is mostly like filepath.Join (without the cleaning) except
791873
// it uses the path separator of c.GOOS instead of the host system's.
792874
func (c *BuildConfig) FilePathJoin(x ...string) string {

0 commit comments

Comments
 (0)