diff options
Diffstat (limited to 'misc/dashboard/builder/http.go')
-rw-r--r-- | misc/dashboard/builder/http.go | 193 |
1 files changed, 122 insertions, 71 deletions
diff --git a/misc/dashboard/builder/http.go b/misc/dashboard/builder/http.go index abef8faa4..b25b417e1 100644 --- a/misc/dashboard/builder/http.go +++ b/misc/dashboard/builder/http.go @@ -6,98 +6,129 @@ package main import ( "bytes" + "encoding/json" + "errors" "fmt" - "http" - "json" + "io" "log" - "os" - "strconv" - "url" + "net/http" + "net/url" + "time" ) -type param map[string]string +type obj map[string]interface{} // dash runs the given method and command on the dashboard. -// If args is not nil, it is the query or post parameters. -// If resp is not nil, dash unmarshals the body as JSON into resp. -func dash(meth, cmd string, resp interface{}, args param) os.Error { +// 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 os.Error + var err error if *verbose { - log.Println("dash", cmd, args) + log.Println("dash", meth, cmd, args, req) } cmd = "http://" + *dashboard + "/" + cmd - vals := make(url.Values) - for k, v := range args { - vals.Add(k, v) + if len(args) > 0 { + cmd += "?" + args.Encode() } switch meth { case "GET": - if q := vals.Encode(); q != "" { - cmd += "?" + q + if req != nil { + log.Panicf("%s to %s with req", meth, cmd) } r, err = http.Get(cmd) case "POST": - r, err = http.PostForm(cmd, vals) + 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: - return fmt.Errorf("unknown method %q", meth) + log.Panicf("%s: invalid method %q", cmd, meth) + panic("invalid method: " + meth) } if err != nil { return err } + defer r.Body.Close() - var buf bytes.Buffer - buf.ReadFrom(r.Body) - if resp != nil { - if err = json.Unmarshal(buf.Bytes(), resp); err != nil { - log.Printf("json unmarshal %#q: %s\n", buf.Bytes(), err) - return err - } + body := new(bytes.Buffer) + if _, err := body.ReadFrom(r.Body); err != nil { + return err } - return nil -} -func dashStatus(meth, cmd string, args param) os.Error { - var resp struct { - Status string - Error string + // 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, } - err := dash(meth, cmd, &resp, args) - if err != nil { + if err = json.Unmarshal(body.Bytes(), &result); err != nil { + log.Printf("json unmarshal %#q: %s\n", body.Bytes(), err) return err } - if resp.Status != "OK" { - return os.NewError("/build: " + resp.Error) + if result.Error != "" { + return errors.New(result.Error) } + return nil } // todo returns the next hash to build. -func (b *Builder) todo() (rev string, err os.Error) { - var resp []struct { - Hash string +func (b *Builder) todo(kind, pkg, goHash string) (rev string, err error) { + args := url.Values{ + "kind": {kind}, + "builder": {b.name}, + "packagePath": {pkg}, + "goHash": {goHash}, } - if err = dash("GET", "todo", &resp, param{"builder": b.name}); err != nil { - return + var resp *struct { + Kind string + Data struct { + Hash string + } } - if len(resp) > 0 { - rev = resp[0].Hash + if err = dash("GET", "todo", args, nil, &resp); err != nil { + return "", err } - return + 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(buildLog string, hash string) os.Error { - return dash("POST", "build", nil, param{ - "builder": b.name, - "key": b.key, - "node": hash, - "log": buildLog, - }) +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) } // packages fetches a list of package paths from the dashboard -func packages() (pkgs []string, err os.Error) { +func packages() (pkgs []string, err error) { + return nil, nil + /* TODO(adg): un-stub this once the new package builder design is done var resp struct { Packages []struct { Path string @@ -111,38 +142,58 @@ func packages() (pkgs []string, err os.Error) { pkgs = append(pkgs, p.Path) } return + */ } -// updatePackage sends package build results and info dashboard -func (b *Builder) updatePackage(pkg string, ok bool, buildLog, info string) os.Error { +// updatePackage sends package build results and info to the dashboard +func (b *Builder) updatePackage(pkg string, ok bool, buildLog, info string) error { + return nil + /* TODO(adg): un-stub this once the new package builder design is done return dash("POST", "package", nil, param{ "builder": b.name, "key": b.key, "path": pkg, - "ok": strconv.Btoa(ok), + "ok": strconv.FormatBool(ok), "log": buildLog, "info": info, }) + */ } -// postCommit informs the dashboard of a new commit -func postCommit(key string, l *HgLog) os.Error { - return dashStatus("POST", "commit", param{ - "key": key, - "node": l.Hash, - "date": l.Date, - "user": l.Author, - "parent": l.Parent, - "desc": l.Desc, - }) +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.Unix() * 1e6, // in microseconds, yuck! + "User": l.Author, + "Desc": l.Desc, + }, nil) } -// dashboardCommit returns true if the dashboard knows about hash. -func dashboardCommit(hash string) bool { - err := dashStatus("GET", "commit", param{"node": hash}) - if err != nil { - log.Printf("check %s: %s", hash, err) - return false +func dashboardCommit(pkg, hash string) bool { + err := dash("GET", "commit", url.Values{ + "packagePath": {pkg}, + "hash": {hash}, + }, nil, nil) + return err == nil +} + +func dashboardPackages() []string { + var resp []struct { + Path string + } + if err := dash("GET", "packages", nil, nil, &resp); err != nil { + log.Println("dashboardPackages:", err) + return nil + } + var pkgs []string + for _, r := range resp { + pkgs = append(pkgs, r.Path) } - return true + return pkgs } |