golang.org/x/build@v0.0.0-20240506185731-218518f32b70/cmd/coordinator/debug.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build linux || darwin 6 7 package main 8 9 import ( 10 "bytes" 11 "encoding/json" 12 "errors" 13 "fmt" 14 "html/template" 15 "log" 16 "net/http" 17 "strconv" 18 "strings" 19 20 "golang.org/x/build/internal/buildgo" 21 "golang.org/x/build/internal/coordinator/pool" 22 "golang.org/x/build/types" 23 ) 24 25 // handleDoSomeWork adds the last committed CL as work to do. 26 // 27 // Only available in dev mode. 28 func handleDoSomeWork(work chan<- buildgo.BuilderRev) func(w http.ResponseWriter, r *http.Request) { 29 return func(w http.ResponseWriter, r *http.Request) { 30 if r.Method == "GET" { 31 w.Header().Set("Content-Type", "text/html; charset=utf-8") 32 buf := new(bytes.Buffer) 33 if err := tmplDoSomeWork.Execute(buf, pool.ReversePool().HostTypes()); err != nil { 34 http.Error(w, fmt.Sprintf("dosomework: %v", err), http.StatusInternalServerError) 35 return 36 } 37 buf.WriteTo(w) 38 return 39 } 40 if r.Method != "POST" { 41 http.Error(w, "dosomework only takes GET and POST", http.StatusBadRequest) 42 return 43 } 44 45 mode := strings.TrimPrefix(r.URL.Path, "/dosomework/") 46 47 count, err := strconv.Atoi(r.FormValue("count")) 48 if err != nil { 49 count = 1 50 } 51 52 gceBuildEnv := pool.NewGCEConfiguration().BuildEnv() 53 // Cap number of jobs that can be scheduled from debug UI. If 54 // buildEnv.MaxBuilds is zero, there is no cap. 55 if gceBuildEnv.MaxBuilds > 0 && count > gceBuildEnv.MaxBuilds { 56 count = gceBuildEnv.MaxBuilds 57 } 58 log.Printf("looking for %v work items for %q", count, mode) 59 60 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 61 fmt.Fprintf(w, "looking for work for %s...\n", mode) 62 if f, ok := w.(http.Flusher); ok { 63 f.Flush() 64 } 65 revs, err := latestBuildableGoRev(count) 66 if err != nil { 67 fmt.Fprintf(w, "cannot find revision: %v", err) 68 return 69 } 70 fmt.Fprintf(w, "found work: %v\n", revs) 71 for _, rev := range revs { 72 work <- buildgo.BuilderRev{Name: mode, Rev: rev} 73 } 74 } 75 } 76 77 // latestBuildableGoRev returns the specified number of most recent buildable 78 // revisions. If there are not enough buildable revisions available to satisfy 79 // the specified amount, unbuildable revisions will be used to meet the 80 // specified count. 81 func latestBuildableGoRev(count int) ([]string, error) { 82 var bs types.BuildStatus 83 var revisions []string 84 res, err := http.Get("https://build.golang.org/?mode=json") 85 if err != nil { 86 return nil, err 87 } 88 defer res.Body.Close() 89 if err := json.NewDecoder(res.Body).Decode(&bs); err != nil { 90 return nil, err 91 } 92 if res.StatusCode != 200 { 93 return nil, fmt.Errorf("unexpected build.golang.org http status %v", res.Status) 94 } 95 // Find first count "ok" revisions 96 for _, br := range bs.Revisions { 97 if br.Repo == "go" { 98 for _, res := range br.Results { 99 if res == "ok" { 100 revisions = append(revisions, br.Revision) 101 break 102 } 103 } 104 } 105 if len(revisions) == count { 106 return revisions, nil 107 } 108 } 109 110 // If there weren't enough "ok" revisions, add enough "not ok" 111 // revisions to satisfy count. 112 for _, br := range bs.Revisions { 113 if br.Repo == "go" { 114 revisions = append(revisions, br.Revision) 115 if len(revisions) == count { 116 return revisions, nil 117 } 118 } 119 } 120 return nil, errors.New("no revisions on build.golang.org") 121 } 122 123 var tmplDoSomeWork = template.Must(template.New("").Parse(` 124 <html><head><title>do some work</title></head><body> 125 <h1>do some work</h1> 126 {{range .}} 127 <form action="/dosomework/{{.}}" method="POST"><button>{{.}}</button></form><br\> 128 {{end}} 129 <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\> 130 </body></html> 131 `))