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  `))