github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/dashboard/builder/http.go (about)

     1  // Copyright 2011 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  package main
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"log"
    14  	"net/http"
    15  	"net/url"
    16  	"time"
    17  )
    18  
    19  const builderVersion = 1 // keep in sync with dashboard/app/build/handler.go
    20  
    21  type obj map[string]interface{}
    22  
    23  // dash runs the given method and command on the dashboard.
    24  // If args is non-nil it is encoded as the URL query string.
    25  // If req is non-nil it is JSON-encoded and passed as the body of the HTTP POST.
    26  // If resp is non-nil the server's response is decoded into the value pointed
    27  // to by resp (resp must be a pointer).
    28  func dash(meth, cmd string, args url.Values, req, resp interface{}) error {
    29  	argsCopy := url.Values{"version": {fmt.Sprint(builderVersion)}}
    30  	for k, v := range args {
    31  		if k == "version" {
    32  			panic(`dash: reserved args key: "version"`)
    33  		}
    34  		argsCopy[k] = v
    35  	}
    36  	var r *http.Response
    37  	var err error
    38  	if *verbose {
    39  		log.Println("dash <-", meth, cmd, argsCopy, req)
    40  	}
    41  	cmd = *dashboard + "/" + cmd + "?" + argsCopy.Encode()
    42  	switch meth {
    43  	case "GET":
    44  		if req != nil {
    45  			log.Panicf("%s to %s with req", meth, cmd)
    46  		}
    47  		r, err = http.Get(cmd)
    48  	case "POST":
    49  		var body io.Reader
    50  		if req != nil {
    51  			b, err := json.Marshal(req)
    52  			if err != nil {
    53  				return err
    54  			}
    55  			body = bytes.NewBuffer(b)
    56  		}
    57  		r, err = http.Post(cmd, "text/json", body)
    58  	default:
    59  		log.Panicf("%s: invalid method %q", cmd, meth)
    60  		panic("invalid method: " + meth)
    61  	}
    62  	if err != nil {
    63  		return err
    64  	}
    65  	defer r.Body.Close()
    66  	if r.StatusCode != http.StatusOK {
    67  		return fmt.Errorf("bad http response: %v", r.Status)
    68  	}
    69  	body := new(bytes.Buffer)
    70  	if _, err := body.ReadFrom(r.Body); err != nil {
    71  		return err
    72  	}
    73  
    74  	// Read JSON-encoded Response into provided resp
    75  	// and return an error if present.
    76  	var result = struct {
    77  		Response interface{}
    78  		Error    string
    79  	}{
    80  		// Put the provided resp in here as it can be a pointer to
    81  		// some value we should unmarshal into.
    82  		Response: resp,
    83  	}
    84  	if err = json.Unmarshal(body.Bytes(), &result); err != nil {
    85  		log.Printf("json unmarshal %#q: %s\n", body.Bytes(), err)
    86  		return err
    87  	}
    88  	if *verbose {
    89  		log.Println("dash ->", result)
    90  	}
    91  	if result.Error != "" {
    92  		return errors.New(result.Error)
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  // todo returns the next hash to build or benchmark.
    99  func (b *Builder) todo(kinds []string, pkg, goHash string) (kind, rev string, benchs []string, err error) {
   100  	args := url.Values{
   101  		"builder":     {b.name},
   102  		"packagePath": {pkg},
   103  		"goHash":      {goHash},
   104  	}
   105  	for _, k := range kinds {
   106  		args.Add("kind", k)
   107  	}
   108  	var resp *struct {
   109  		Kind string
   110  		Data struct {
   111  			Hash        string
   112  			PerfResults []string
   113  		}
   114  	}
   115  	if err = dash("GET", "todo", args, nil, &resp); err != nil {
   116  		return
   117  	}
   118  	if resp == nil {
   119  		return
   120  	}
   121  	if *verbose {
   122  		fmt.Printf("dash resp: %+v\n", *resp)
   123  	}
   124  	for _, k := range kinds {
   125  		if k == resp.Kind {
   126  			return resp.Kind, resp.Data.Hash, resp.Data.PerfResults, nil
   127  		}
   128  	}
   129  	err = fmt.Errorf("expecting Kinds %q, got %q", kinds, resp.Kind)
   130  	return
   131  }
   132  
   133  // recordResult sends build results to the dashboard
   134  func (b *Builder) recordResult(ok bool, pkg, hash, goHash, buildLog string, runTime time.Duration) error {
   135  	if !*report {
   136  		return nil
   137  	}
   138  	req := obj{
   139  		"Builder":     b.name,
   140  		"PackagePath": pkg,
   141  		"Hash":        hash,
   142  		"GoHash":      goHash,
   143  		"OK":          ok,
   144  		"Log":         buildLog,
   145  		"RunTime":     runTime,
   146  	}
   147  	args := url.Values{"key": {b.key}, "builder": {b.name}}
   148  	return dash("POST", "result", args, req, nil)
   149  }
   150  
   151  // Result of running a single benchmark on a single commit.
   152  type PerfResult struct {
   153  	Builder   string
   154  	Benchmark string
   155  	Hash      string
   156  	OK        bool
   157  	Metrics   []PerfMetric
   158  	Artifacts []PerfArtifact
   159  }
   160  
   161  type PerfMetric struct {
   162  	Type string
   163  	Val  uint64
   164  }
   165  
   166  type PerfArtifact struct {
   167  	Type string
   168  	Body string
   169  }
   170  
   171  // recordPerfResult sends benchmarking results to the dashboard
   172  func (b *Builder) recordPerfResult(req *PerfResult) error {
   173  	if !*report {
   174  		return nil
   175  	}
   176  	req.Builder = b.name
   177  	args := url.Values{"key": {b.key}, "builder": {b.name}}
   178  	return dash("POST", "perf-result", args, req, nil)
   179  }
   180  
   181  func postCommit(key, pkg string, l *HgLog) error {
   182  	if !*report {
   183  		return nil
   184  	}
   185  	t, err := time.Parse(time.RFC3339, l.Date)
   186  	if err != nil {
   187  		return fmt.Errorf("parsing %q: %v", l.Date, t)
   188  	}
   189  	return dash("POST", "commit", url.Values{"key": {key}}, obj{
   190  		"PackagePath":       pkg,
   191  		"Hash":              l.Hash,
   192  		"ParentHash":        l.Parent,
   193  		"Time":              t.Format(time.RFC3339),
   194  		"User":              l.Author,
   195  		"Desc":              l.Desc,
   196  		"NeedsBenchmarking": l.bench,
   197  	}, nil)
   198  }
   199  
   200  func dashboardCommit(pkg, hash string) bool {
   201  	err := dash("GET", "commit", url.Values{
   202  		"packagePath": {pkg},
   203  		"hash":        {hash},
   204  	}, nil, nil)
   205  	return err == nil
   206  }
   207  
   208  func dashboardPackages(kind string) []string {
   209  	args := url.Values{"kind": []string{kind}}
   210  	var resp []struct {
   211  		Path string
   212  	}
   213  	if err := dash("GET", "packages", args, nil, &resp); err != nil {
   214  		log.Println("dashboardPackages:", err)
   215  		return nil
   216  	}
   217  	if *verbose {
   218  		fmt.Printf("dash resp: %+v\n", resp)
   219  	}
   220  	var pkgs []string
   221  	for _, r := range resp {
   222  		pkgs = append(pkgs, r.Path)
   223  	}
   224  	return pkgs
   225  }