| Index: dashboard/builder/http.go |
| =================================================================== |
| new file mode 100644 |
| --- /dev/null |
| +++ b/dashboard/builder/http.go |
| @@ -0,0 +1,167 @@ |
| +// Copyright 2011 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. |
| + |
| +package main |
| + |
| +import ( |
| + "bytes" |
| + "encoding/json" |
| + "errors" |
| + "fmt" |
| + "io" |
| + "log" |
| + "net/http" |
| + "net/url" |
| + "time" |
| +) |
| + |
| +type obj map[string]interface{} |
| + |
| +// dash runs the given method and command on the dashboard. |
| +// If args is non-nil it is encoded as the URL query string. |
| +// If req is non-nil it is JSON-encoded and passed as the body of the HTTP POST. |
| +// If resp is non-nil the server's response is decoded into the value pointed |
| +// to by resp (resp must be a pointer). |
| +func dash(meth, cmd string, args url.Values, req, resp interface{}) error { |
| + var r *http.Response |
| + var err error |
| + if *verbose { |
| + log.Println("dash", meth, cmd, args, req) |
| + } |
| + cmd = "http://" + *dashboard + "/" + cmd |
| + if len(args) > 0 { |
| + cmd += "?" + args.Encode() |
| + } |
| + switch meth { |
| + case "GET": |
| + if req != nil { |
| + log.Panicf("%s to %s with req", meth, cmd) |
| + } |
| + r, err = http.Get(cmd) |
| + case "POST": |
| + var body io.Reader |
| + if req != nil { |
| + b, err := json.Marshal(req) |
| + if err != nil { |
| + return err |
| + } |
| + body = bytes.NewBuffer(b) |
| + } |
| + r, err = http.Post(cmd, "text/json", body) |
| + default: |
| + log.Panicf("%s: invalid method %q", cmd, meth) |
| + panic("invalid method: " + meth) |
| + } |
| + if err != nil { |
| + return err |
| + } |
| + defer r.Body.Close() |
| + if r.StatusCode != http.StatusOK { |
| + return fmt.Errorf("bad http response: %v", r.Status) |
| + } |
| + body := new(bytes.Buffer) |
| + if _, err := body.ReadFrom(r.Body); err != nil { |
| + return err |
| + } |
| + |
| + // Read JSON-encoded Response into provided resp |
| + // and return an error if present. |
| + var result = struct { |
| + Response interface{} |
| + Error string |
| + }{ |
| + // Put the provided resp in here as it can be a pointer to |
| + // some value we should unmarshal into. |
| + Response: resp, |
| + } |
| + if err = json.Unmarshal(body.Bytes(), &result); err != nil { |
| + log.Printf("json unmarshal %#q: %s\n", body.Bytes(), err) |
| + return err |
| + } |
| + if result.Error != "" { |
| + return errors.New(result.Error) |
| + } |
| + |
| + return nil |
| +} |
| + |
| +// todo returns the next hash to build. |
| +func (b *Builder) todo(kind, pkg, goHash string) (rev string, err error) { |
| + args := url.Values{ |
| + "kind": {kind}, |
| + "builder": {b.name}, |
| + "packagePath": {pkg}, |
| + "goHash": {goHash}, |
| + } |
| + var resp *struct { |
| + Kind string |
| + Data struct { |
| + Hash string |
| + } |
| + } |
| + if err = dash("GET", "todo", args, nil, &resp); err != nil { |
| + return "", err |
| + } |
| + if resp == nil { |
| + return "", nil |
| + } |
| + if kind != resp.Kind { |
| + return "", fmt.Errorf("expecting Kind %q, got %q", kind, resp.Kind) |
| + } |
| + return resp.Data.Hash, nil |
| +} |
| + |
| +// recordResult sends build results to the dashboard |
| +func (b *Builder) recordResult(ok bool, pkg, hash, goHash, buildLog string, runTime time.Duration) error { |
| + req := obj{ |
| + "Builder": b.name, |
| + "PackagePath": pkg, |
| + "Hash": hash, |
| + "GoHash": goHash, |
| + "OK": ok, |
| + "Log": buildLog, |
| + "RunTime": runTime, |
| + } |
| + args := url.Values{"key": {b.key}, "builder": {b.name}} |
| + return dash("POST", "result", args, req, nil) |
| +} |
| + |
| +func postCommit(key, pkg string, l *HgLog) error { |
| + t, err := time.Parse(time.RFC3339, l.Date) |
| + if err != nil { |
| + return fmt.Errorf("parsing %q: %v", l.Date, t) |
| + } |
| + return dash("POST", "commit", url.Values{"key": {key}}, obj{ |
| + "PackagePath": pkg, |
| + "Hash": l.Hash, |
| + "ParentHash": l.Parent, |
| + "Time": t.Format(time.RFC3339), |
| + "User": l.Author, |
| + "Desc": l.Desc, |
| + }, nil) |
| +} |
| + |
| +func dashboardCommit(pkg, hash string) bool { |
| + err := dash("GET", "commit", url.Values{ |
| + "packagePath": {pkg}, |
| + "hash": {hash}, |
| + }, nil, nil) |
| + return err == nil |
| +} |
| + |
| +func dashboardPackages(kind string) []string { |
| + args := url.Values{"kind": []string{kind}} |
| + var resp []struct { |
| + Path string |
| + } |
| + if err := dash("GET", "packages", args, nil, &resp); err != nil { |
| + log.Println("dashboardPackages:", err) |
| + return nil |
| + } |
| + var pkgs []string |
| + for _, r := range resp { |
| + pkgs = append(pkgs, r.Path) |
| + } |
| + return pkgs |
| +} |