return nil
}
-// commitHandler records a new commit. It reads a JSON-encoded Commit value
-// from the request body and creates a new Commit entity.
-// commitHandler also updates the "tip" Tag for each new commit at tip.
+// commitHandler retrieves commit data or records a new commit.
+//
+// For GET requests it returns a Commit value for the specified
+// packagePath and hash.
+//
+// For POST requests it reads a JSON-encoded Commit value from the request
+// body and creates a new Commit entity. It also updates the "tip" Tag for
+// each new commit at tip.
//
// This handler is used by a gobuilder process in -commit mode.
-func commitHandler(w http.ResponseWriter, r *http.Request) {
+func commitHandler(r *http.Request) (interface{}, os.Error) {
+ c := appengine.NewContext(r)
com := new(Commit)
+
+ // TODO(adg): support unauthenticated GET requests to this handler
+ if r.Method == "GET" {
+ com.PackagePath = r.FormValue("packagePath")
+ com.Hash = r.FormValue("hash")
+ if err := datastore.Get(c, com.Key(c), com); err != nil {
+ return nil, err
+ }
+ return com, nil
+ }
+
+ // POST request
defer r.Body.Close()
if err := json.NewDecoder(r.Body).Decode(com); err != nil {
- logErr(w, r, err)
- return
+ return nil, err
}
if err := com.Valid(); err != nil {
- logErr(w, r, err)
- return
+ return nil, err
}
tx := func(c appengine.Context) os.Error {
return addCommit(c, com)
}
- c := appengine.NewContext(r)
- if err := datastore.RunInTransaction(c, tx, nil); err != nil {
- logErr(w, r, err)
- }
+ return nil, datastore.RunInTransaction(c, tx, nil)
}
// addCommit adds the Commit entity to the datastore and updates the tip Tag.
// request body and updates the Tag entity for the Kind of tag provided.
//
// This handler is used by a gobuilder process in -commit mode.
-func tagHandler(w http.ResponseWriter, r *http.Request) {
+func tagHandler(r *http.Request) (interface{}, os.Error) {
t := new(Tag)
defer r.Body.Close()
if err := json.NewDecoder(r.Body).Decode(t); err != nil {
- logErr(w, r, err)
- return
+ return nil, err
}
if err := t.Valid(); err != nil {
- logErr(w, r, err)
- return
+ return nil, err
}
c := appengine.NewContext(r)
- if _, err := datastore.Put(c, t.Key(c), t); err != nil {
- logErr(w, r, err)
- return
- }
+ _, err := datastore.Put(c, t.Key(c), t)
+ return nil, err
}
-// todoHandler returns the string of the hash of the next Commit to be built.
+// todoHandler returns the hash of the next Commit to be built.
// It expects a "builder" query parameter.
//
// By default it scans the first 20 Go Commits in Num-descending order and
// and scans the first 20 Commits in Num-descending order for the specified
// packagePath and returns the first that doesn't have a Result for this builder
// and goHash combination.
-func todoHandler(w http.ResponseWriter, r *http.Request) {
+func todoHandler(r *http.Request) (interface{}, os.Error) {
builder := r.FormValue("builder")
goHash := r.FormValue("goHash")
c := appengine.NewContext(r)
p, err := GetPackage(c, r.FormValue("packagePath"))
if err != nil {
- logErr(w, r, err)
- return
+ return nil, err
}
- q := datastore.NewQuery("Commit").
+ t := datastore.NewQuery("Commit").
Ancestor(p.Key(c)).
Limit(commitsPerPage).
- Order("-Num")
- var nextHash string
- for t := q.Run(c); nextHash == ""; {
+ Order("-Num").
+ Run(c)
+ for {
com := new(Commit)
- if _, err := t.Next(com); err == datastore.Done {
- break
- } else if err != nil {
- logErr(w, r, err)
- return
+ if _, err := t.Next(com); err != nil {
+ if err == datastore.Done {
+ err = nil
+ }
+ return nil, err
}
var hasResult bool
if goHash != "" {
hasResult = com.HasResult(builder)
}
if !hasResult {
- nextHash = com.Hash
+ return com.Hash, nil
}
}
- fmt.Fprint(w, nextHash)
+ panic("unreachable")
}
-// packagesHandler returns a JSON-encoded list of the non-Go Packages
-// monitored by the dashboard.
-func packagesHandler(w http.ResponseWriter, r *http.Request) {
+// packagesHandler returns a list of the non-Go Packages monitored
+// by the dashboard.
+func packagesHandler(r *http.Request) (interface{}, os.Error) {
c := appengine.NewContext(r)
var pkgs []*Package
for t := datastore.NewQuery("Package").Run(c); ; {
if _, err := t.Next(pkg); err == datastore.Done {
break
} else if err != nil {
- logErr(w, r, err)
- return
+ return nil, err
}
if pkg.Path != "" {
pkgs = append(pkgs, pkg)
}
}
- if err := json.NewEncoder(w).Encode(pkgs); err != nil {
- logErr(w, r, err)
- }
+ return pkgs, nil
}
// resultHandler records a build result.
// creates a new Result entity, and updates the relevant Commit entity.
// If the Log field is not empty, resultHandler creates a new Log entity
// and updates the LogHash field before putting the Commit entity.
-func resultHandler(w http.ResponseWriter, r *http.Request) {
+func resultHandler(r *http.Request) (interface{}, os.Error) {
+ c := appengine.NewContext(r)
res := new(Result)
defer r.Body.Close()
if err := json.NewDecoder(r.Body).Decode(res); err != nil {
- logErr(w, r, err)
- return
+ return nil, err
}
if err := res.Valid(); err != nil {
- logErr(w, r, err)
- return
+ return nil, err
}
- c := appengine.NewContext(r)
// store the Log text if supplied
if len(res.Log) > 0 {
hash, err := PutLog(c, res.Log)
if err != nil {
- logErr(w, r, err)
- return
+ return nil, err
}
res.LogHash = hash
}
com := &Commit{PackagePath: res.PackagePath, Hash: res.Hash}
return com.AddResult(c, res)
}
- if err := datastore.RunInTransaction(c, tx, nil); err != nil {
- logErr(w, r, err)
- }
+ return nil, datastore.RunInTransaction(c, tx, nil)
}
func logHandler(w http.ResponseWriter, r *http.Request) {
}
}
+type dashHandler func(*http.Request) (interface{}, os.Error)
+
+type dashResponse struct {
+ Response interface{}
+ Error os.Error
+}
+
// AuthHandler wraps a http.HandlerFunc with a handler that validates the
// supplied key and builder query parameters.
-func AuthHandler(h http.HandlerFunc) http.HandlerFunc {
+func AuthHandler(h dashHandler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Put the URL Query values into r.Form to avoid parsing the
// request body when calling r.FormValue.
}
}
- h(w, r) // Call the original HandlerFunc.
+ // Call the original HandlerFunc and return the response.
+ c := appengine.NewContext(r)
+ resp, err := h(r)
+ if err != nil {
+ c.Errorf("%v", err)
+ }
+ w.Header().Set("Content-Type", "application/json")
+ err = json.NewEncoder(w).Encode(dashResponse{resp, err})
+ if err != nil {
+ c.Criticalf("%v", err)
+ }
}
}
"io"
"json"
"os"
+ "strings"
"url"
)
// logs
{"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false, Log: []byte("test")}, nil},
{"/log/a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", nil, nil, "test"},
- {"/todo", url.Values{"builder": {"linux-386"}}, nil, ""},
+ {"/todo", url.Values{"builder": {"linux-386"}}, nil, nil},
// non-Go repos
{"/commit", nil, &Commit{PackagePath: testPkg, Hash: "1001", ParentHash: "1000"}, nil},
{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1002", GoHash: "0001", OK: true}, nil},
{"/todo", url.Values{"builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, "1001"},
{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1001", GoHash: "0001", OK: true}, nil},
- {"/todo", url.Values{"builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, ""},
+ {"/todo", url.Values{"builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, nil},
{"/todo", url.Values{"builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0002"}}, nil, "1003"},
}
}
}
- failed := false
for i, t := range testRequests {
errorf := func(format string, args ...interface{}) {
fmt.Fprintf(w, "%d %s: ", i, t.path)
fmt.Fprintf(w, format, args...)
fmt.Fprintln(w)
- failed = true
}
var body io.ReadWriter
if t.req != nil {
logErr(w, r, err)
return
}
+ if t.req != nil {
+ req.Method = "POST"
+ }
req.Header = r.Header
rec := httptest.NewRecorder()
http.DefaultServeMux.ServeHTTP(rec, req)
errorf(rec.Body.String())
return
}
+ resp := new(dashResponse)
+ if strings.HasPrefix(t.path, "/log/") {
+ resp.Response = rec.Body.String()
+ } else {
+ err := json.NewDecoder(rec.Body).Decode(resp)
+ if err != nil {
+ errorf("decoding response: %v", err)
+ return
+ }
+ }
if e, ok := t.res.(string); ok {
- g := rec.Body.String()
+ g, ok := resp.Response.(string)
+ if !ok {
+ errorf("Response not string: %T", resp.Response)
+ return
+ }
if g != e {
- errorf("body mismatch: got %q want %q", g, e)
+ errorf("response mismatch: got %q want %q", g, e)
return
}
}
+ if t.res == nil && resp.Response != nil {
+ errorf("response mismatch: got %q expected <nil>",
+ resp.Response)
+ return
+ }
}
- if !failed {
- fmt.Fprint(w, "PASS")
- }
+ fmt.Fprint(w, "PASS")
}
func nukeEntities(c appengine.Context, kinds []string) os.Error {