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 }