dashboard/coordinator: use new dashboard JSON interface to find work
This uses the new JSON interface to the build dashboard (from golang.org/cl/2290) to find all work, and re-enables the OpenBSD builder[*], and can do multiple things at a time. Andrew and I just watched it fire up 8 OpenBSD VMs at once to catch up. [*] The OpenBSD builder was disabled because it would only report results for the main repo, not subrepos, and the old build.golang.org/todo interface didn't understand that was possible. Now the steps are considered separate. Update golang/go#8642 (OpenBSD) Update golang/go#9492 (builds in VMs) Change-Id: Ic6c2f73ee3da218dd54ef1a33f3afc97046ea3cc Reviewed-on: https://go-review.googlesource.com/2282 Reviewed-by: Andrew Gerrand <adg@golang.org>
This commit is contained in:
parent
58c8b8a738
commit
ac848a9536
|
@ -35,6 +35,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/oauth2/google"
|
"golang.org/x/oauth2/google"
|
||||||
|
"golang.org/x/tools/dashboard/types"
|
||||||
"google.golang.org/api/compute/v1"
|
"google.golang.org/api/compute/v1"
|
||||||
"google.golang.org/cloud/compute/metadata"
|
"google.golang.org/cloud/compute/metadata"
|
||||||
)
|
)
|
||||||
|
@ -193,7 +194,7 @@ func main() {
|
||||||
addBuilder(buildConfig{name: "linux-amd64-clang", image: "gobuilders/linux-x86-clang"})
|
addBuilder(buildConfig{name: "linux-amd64-clang", image: "gobuilders/linux-x86-clang"})
|
||||||
|
|
||||||
// VMs:
|
// VMs:
|
||||||
// addBuilder(buildConfig{name: "openbsd-amd64-gce56", vmImage: "openbsd-amd64-56"})
|
addBuilder(buildConfig{name: "openbsd-amd64-gce56", vmImage: "openbsd-amd64-56"})
|
||||||
// addBuilder(buildConfig{name: "plan9-386-gce", vmImage: "plan9-386"})
|
// addBuilder(buildConfig{name: "plan9-386-gce", vmImage: "plan9-386"})
|
||||||
|
|
||||||
addWatcher(watchConfig{repo: "https://go.googlesource.com/go", dash: "https://build.golang.org/"})
|
addWatcher(watchConfig{repo: "https://go.googlesource.com/go", dash: "https://build.golang.org/"})
|
||||||
|
@ -232,9 +233,8 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
workc := make(chan builderRev)
|
workc := make(chan builderRev)
|
||||||
for name, builder := range builders {
|
go findWorkLoop(workc)
|
||||||
go findWorkLoop(name, builder.dashURL, workc)
|
// TODO(cmang): gccgo will need its own findWorkLoop
|
||||||
}
|
|
||||||
|
|
||||||
ticker := time.NewTicker(1 * time.Minute)
|
ticker := time.NewTicker(1 * time.Minute)
|
||||||
for {
|
for {
|
||||||
|
@ -266,6 +266,13 @@ func numCurrentBuilds() int {
|
||||||
return len(status)
|
return len(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isBuilding(work builderRev) bool {
|
||||||
|
statusMu.Lock()
|
||||||
|
defer statusMu.Unlock()
|
||||||
|
_, building := status[work]
|
||||||
|
return building
|
||||||
|
}
|
||||||
|
|
||||||
// mayBuildRev reports whether the build type & revision should be started.
|
// mayBuildRev reports whether the build type & revision should be started.
|
||||||
// It returns true if it's not already building, and there is capacity.
|
// It returns true if it's not already building, and there is capacity.
|
||||||
func mayBuildRev(work builderRev) bool {
|
func mayBuildRev(work builderRev) bool {
|
||||||
|
@ -395,42 +402,80 @@ func handleLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
// BUILDERKEY scrubbing into the Write method.
|
// BUILDERKEY scrubbing into the Write method.
|
||||||
}
|
}
|
||||||
|
|
||||||
func findWorkLoop(builderName, dashURL string, work chan<- builderRev) {
|
// findWorkLoop polls http://build.golang.org/?mode=json looking for new work
|
||||||
// TODO: make this better
|
// for the main dashboard. It does not support gccgo.
|
||||||
|
// TODO(bradfitz): it also currently does not support subrepos.
|
||||||
|
func findWorkLoop(work chan<- builderRev) {
|
||||||
|
ticker := time.NewTicker(15 * time.Second)
|
||||||
for {
|
for {
|
||||||
rev, err := findWork(builderName, dashURL)
|
if err := findWork(work); err != nil {
|
||||||
if err != nil {
|
log.Printf("failed to find new work: %v", err)
|
||||||
log.Printf("Finding work for %s: %v", builderName, err)
|
|
||||||
} else if rev != "" {
|
|
||||||
work <- builderRev{builderName, rev}
|
|
||||||
}
|
}
|
||||||
time.Sleep(60 * time.Second)
|
<-ticker.C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func findWork(builderName, dashURL string) (rev string, err error) {
|
func findWork(work chan<- builderRev) error {
|
||||||
var jres struct {
|
var bs types.BuildStatus
|
||||||
Response struct {
|
res, err := http.Get("https://build.golang.org/?mode=json")
|
||||||
Kind string
|
if err != nil {
|
||||||
Data struct {
|
return err
|
||||||
Hash string
|
}
|
||||||
PerfResults []string
|
defer res.Body.Close()
|
||||||
|
if err := json.NewDecoder(res.Body).Decode(&bs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("unexpected http status %v", res.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
knownToDashboard := map[string]bool{} // keys are builder
|
||||||
|
for _, b := range bs.Builders {
|
||||||
|
knownToDashboard[b] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var goRevisions []string
|
||||||
|
for _, br := range bs.Revisions {
|
||||||
|
if br.Repo == "go" {
|
||||||
|
goRevisions = append(goRevisions, br.Revision)
|
||||||
|
} else {
|
||||||
|
// TODO(bradfitz): support these: golang.org/issue/9506
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(br.Results) != len(bs.Builders) {
|
||||||
|
return errors.New("bogus JSON response from dashboard: results is too long.")
|
||||||
|
}
|
||||||
|
for i, res := range br.Results {
|
||||||
|
if res != "" {
|
||||||
|
// It's either "ok" or a failure URL.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
builder := bs.Builders[i]
|
||||||
|
if _, ok := builders[builder]; !ok {
|
||||||
|
// Not managed by the coordinator.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
br := builderRev{bs.Builders[i], br.Revision}
|
||||||
|
if !isBuilding(br) {
|
||||||
|
work <- br
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res, err := http.Get(dashURL + "/todo?builder=" + builderName + "&kind=build-go-commit")
|
|
||||||
if err != nil {
|
// And to bootstrap new builders, see if we have any builders
|
||||||
return
|
// that the dashboard doesn't know about.
|
||||||
|
for b := range builders {
|
||||||
|
if knownToDashboard[b] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, rev := range goRevisions {
|
||||||
|
br := builderRev{b, rev}
|
||||||
|
if !isBuilding(br) {
|
||||||
|
work <- br
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
return nil
|
||||||
if res.StatusCode != 200 {
|
|
||||||
return "", fmt.Errorf("unexpected http status %d", res.StatusCode)
|
|
||||||
}
|
|
||||||
err = json.NewDecoder(res.Body).Decode(&jres)
|
|
||||||
if jres.Response.Kind == "build-go-commit" {
|
|
||||||
rev = jres.Response.Data.Hash
|
|
||||||
}
|
|
||||||
return rev, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// builderRev is a build configuration type and a revision.
|
// builderRev is a build configuration type and a revision.
|
||||||
|
@ -554,7 +599,7 @@ func addWatcher(c watchConfig) {
|
||||||
func condUpdateImage(img string) error {
|
func condUpdateImage(img string) error {
|
||||||
ii := images[img]
|
ii := images[img]
|
||||||
if ii == nil {
|
if ii == nil {
|
||||||
log.Fatalf("Image %q not described.", img)
|
return fmt.Errorf("image %q doesn't exist", img)
|
||||||
}
|
}
|
||||||
ii.mu.Lock()
|
ii.mu.Lock()
|
||||||
defer ii.mu.Unlock()
|
defer ii.mu.Unlock()
|
||||||
|
@ -758,7 +803,9 @@ func startBuildingInVM(conf buildConfig, rev string) (*buildStatus, error) {
|
||||||
// take minutes for it to come up, and then even more time to do the build.
|
// take minutes for it to come up, and then even more time to do the build.
|
||||||
go func() {
|
go func() {
|
||||||
err := watchVM(st)
|
err := watchVM(st)
|
||||||
deleteVM(projectZone, st.instName)
|
if st.hasEvent("instance_created") {
|
||||||
|
deleteVM(projectZone, st.instName)
|
||||||
|
}
|
||||||
st.setDone(err == nil)
|
st.setDone(err == nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(st, "\n\nError: %v\n", err)
|
fmt.Fprintf(st, "\n\nError: %v\n", err)
|
||||||
|
@ -960,6 +1007,17 @@ func (st *buildStatus) logEventTime(event string) {
|
||||||
st.events = append(st.events, eventAndTime{event, time.Now()})
|
st.events = append(st.events, eventAndTime{event, time.Now()})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (st *buildStatus) hasEvent(event string) bool {
|
||||||
|
st.mu.Lock()
|
||||||
|
defer st.mu.Unlock()
|
||||||
|
for _, e := range st.events {
|
||||||
|
if e.evt == event {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// htmlStatusLine returns the HTML to show within the <pre> block on
|
// htmlStatusLine returns the HTML to show within the <pre> block on
|
||||||
// the main page's list of active builds.
|
// the main page's list of active builds.
|
||||||
func (st *buildStatus) htmlStatusLine() string {
|
func (st *buildStatus) htmlStatusLine() string {
|
||||||
|
|
Loading…
Reference in New Issue