| // Copyright 2015 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| //go:build linux || darwin | |
| package main | |
| import ( | |
| "bytes" | |
| "encoding/json" | |
| "errors" | |
| "fmt" | |
| "html/template" | |
| "log" | |
| "net/http" | |
| "slices" | |
| "strconv" | |
| "strings" | |
| "golang.org/x/build/internal/buildgo" | |
| "golang.org/x/build/internal/coordinator/pool" | |
| "golang.org/x/build/types" | |
| ) | |
| // handleDoSomeWork adds the last committed CL as work to do. | |
| // | |
| // Only available in dev mode. | |
| func handleDoSomeWork(work chan<- buildgo.BuilderRev) func(w http.ResponseWriter, r *http.Request) { | |
| return func(w http.ResponseWriter, r *http.Request) { | |
| if r.Method == "GET" { | |
| w.Header().Set("Content-Type", "text/html; charset=utf-8") | |
| buf := new(bytes.Buffer) | |
| if err := tmplDoSomeWork.Execute(buf, pool.ReversePool().HostTypes()); err != nil { | |
| http.Error(w, fmt.Sprintf("dosomework: %v", err), http.StatusInternalServerError) | |
| return | |
| } | |
| buf.WriteTo(w) | |
| return | |
| } | |
| if r.Method != "POST" { | |
| http.Error(w, "dosomework only takes GET and POST", http.StatusBadRequest) | |
| return | |
| } | |
| mode := strings.TrimPrefix(r.URL.Path, "/dosomework/") | |
| count, err := strconv.Atoi(r.FormValue("count")) | |
| if err != nil { | |
| count = 1 | |
| } | |
| gceBuildEnv := pool.NewGCEConfiguration().BuildEnv() | |
| // Cap number of jobs that can be scheduled from debug UI. If | |
| // buildEnv.MaxBuilds is zero, there is no cap. | |
| if gceBuildEnv.MaxBuilds > 0 && count > gceBuildEnv.MaxBuilds { | |
| count = gceBuildEnv.MaxBuilds | |
| } | |
| log.Printf("looking for %v work items for %q", count, mode) | |
| w.Header().Set("Content-Type", "text/plain; charset=utf-8") | |
| fmt.Fprintf(w, "looking for work for %s...\n", mode) | |
| if f, ok := w.(http.Flusher); ok { | |
| f.Flush() | |
| } | |
| revs, err := latestBuildableGoRev(count) | |
| if err != nil { | |
| fmt.Fprintf(w, "cannot find revision: %v", err) | |
| return | |
| } | |
| fmt.Fprintf(w, "found work: %v\n", revs) | |
| for _, rev := range revs { | |
| work <- buildgo.BuilderRev{Name: mode, Rev: rev} | |
| } | |
| } | |
| } | |
| // latestBuildableGoRev returns the specified number of most recent buildable | |
| // revisions. If there are not enough buildable revisions available to satisfy | |
| // the specified amount, unbuildable revisions will be used to meet the | |
| // specified count. | |
| func latestBuildableGoRev(count int) ([]string, error) { | |
| var bs types.BuildStatus | |
| var revisions []string | |
| res, err := http.Get("https://build.golang.org/?mode=json") | |
| if err != nil { | |
| return nil, err | |
| } | |
| defer res.Body.Close() | |
| if err := json.NewDecoder(res.Body).Decode(&bs); err != nil { | |
| return nil, err | |
| } | |
| if res.StatusCode != 200 { | |
| return nil, fmt.Errorf("unexpected build.golang.org http status %v", res.Status) | |
| } | |
| // Find first count "ok" revisions | |
| for _, br := range bs.Revisions { | |
| if br.Repo == "go" { | |
| if slices.Contains(br.Results, "ok") { | |
| revisions = append(revisions, br.Revision) | |
| } | |
| } | |
| if len(revisions) == count { | |
| return revisions, nil | |
| } | |
| } | |
| // If there weren't enough "ok" revisions, add enough "not ok" | |
| // revisions to satisfy count. | |
| for _, br := range bs.Revisions { | |
| if br.Repo == "go" { | |
| revisions = append(revisions, br.Revision) | |
| if len(revisions) == count { | |
| return revisions, nil | |
| } | |
| } | |
| } | |
| return nil, errors.New("no revisions on build.golang.org") | |
| } | |
| var tmplDoSomeWork = template.Must(template.New("").Parse(` | |
| <html><head><title>do some work</title></head><body> | |
| <h1>do some work</h1> | |
| {{range .}} | |
| <form action="/dosomework/{{.}}" method="POST"><button>{{.}}</button></form><br\> | |
| {{end}} | |
| <form action="/dosomework/linux-amd64-kube" method="POST"><input type="text" name="count" id="count" value="1"></input><button>linux-amd64-kube</button></form><br\> | |
| </body></html> | |
| `)) |