//Error is used when there were any errors in building the test func (Response) Error(req *http.Request, args *rpc.BuilderResponse, resp *rpc.None) (err error) { //wrap our error on the way out defer rpc.Wrap(&err) //create the context ctx := httputil.NewContext(req) defer ctx.Close() //get the key of the work item key := bson.ObjectIdHex(args.Key) ops := []txn.Op{{ //make sure we have the given work item C: "Work", Id: key, Assert: bson.M{ "status": entities.WorkStatusProcessing, "attemptlog.0.id": bson.ObjectIdHex(args.ID), "revision": args.WorkRev, }, Update: bson.M{ "$set": bson.M{"status": entities.WorkStatusCompleted}, "$inc": bson.M{"revision": 1}, }, }, { //insert the work result C: "WorkResult", Id: bson.NewObjectId(), Insert: entities.WorkResult{ WorkID: key, Success: false, Revision: args.Revision, RevDate: args.RevDate, When: time.Now(), Error: args.Error, }, }} //run the transaction err = ctx.R.Run(ops, bson.NewObjectId(), nil) if err == txn.ErrAborted { ctx.Infof("Lost the race inserting result.") err = nil } return }
//Remove removes a service from the tracker. func (Tracker) Remove(req *http.Request, args *rpc.RemoveArgs, rep *rpc.None) (err error) { //wrap our error on the way out defer rpc.Wrap(&err) //create our context ctx := httputil.NewContext(req) defer ctx.Close() ctx.Infof("Got a remove request from %s: %+v", req.RemoteAddr, args) //get the key from the argument key := bson.ObjectIdHex(args.Key) //make sure its an entity if !isEntity(args.Kind) { err = rpc.Errorf("kind is not Builder or Runner") return } //remove it from the database err = ctx.DB.C(args.Kind).Remove(bson.M{"_id": key}) return }
//Announce adds the given service into the tracker pool. func (Tracker) Announce(req *http.Request, args *rpc.AnnounceArgs, rep *rpc.AnnounceReply) (err error) { //wrap our error on the way out defer rpc.Wrap(&err) if err = verify(args); err != nil { return } ctx := httputil.NewContext(req) defer ctx.Close() ctx.Infof("Got announce request from %s: %+v", req.RemoteAddr, args) //ping them to make sure we can make valid rpc calls cl := client.New(args.URL, http.DefaultClient, client.JsonCodec) err = cl.Call("Pinger.Ping", nil, new(rpc.None)) if err != nil { ctx.Infof("Failed to Ping the announce.") return } //create the entity var e interface{} //make sure we have a nonzero seed var seed int64 for seed == 0 { seed = rand.Int63() } key := bson.NewObjectId() switch args.Type { case "Builder": e = &Builder{ ID: key, GOOS: args.GOOS, GOARCH: args.GOARCH, URL: args.URL, Seed: seed, } case "Runner": e = &Runner{ ID: key, GOOS: args.GOOS, GOARCH: args.GOARCH, URL: args.URL, Seed: seed, } default: panic("unreachable") } //TODO(zeebo): check if we have the URL already and grab that key to update //save the service in the database if err = ctx.DB.C(args.Type).Insert(e); err != nil { return } //return the hex representation of the key rep.Key = key.Hex() return }
//Post is the rpc method that the Runner uses to give a response about an item. func (Response) Post(req *http.Request, args *rpc.RunnerResponse, resp *rpc.None) (err error) { //wrap our error on the way out defer rpc.Wrap(&err) //create our context ctx := httputil.NewContext(req) defer ctx.Close() //build the keys we need to reference key := bson.ObjectIdHex(args.Key) wkey := bson.NewObjectId() ops := []txn.Op{{ //make sure we have the given work item C: "Work", Id: key, Assert: bson.M{ "status": entities.WorkStatusProcessing, "attemptlog.0.id": bson.ObjectIdHex(args.ID), "revision": args.WorkRev, }, Update: bson.M{ "$set": bson.M{"status": entities.WorkStatusCompleted}, "$inc": bson.M{"revision": 1}, }, }, { //insert the work result C: "WorkResult", Id: wkey, Insert: entities.WorkResult{ WorkID: key, Success: true, Revision: args.Revision, RevDate: args.RevDate, When: time.Now(), }, }} //operations for notifications var nots []txn.Op //store the test results for _, out := range args.Tests { //get the status from the output type and output var status string switch out.Type { case rpc.OutputSuccess: if strings.HasSuffix(out.Output, "\nPASS\n") { status = entities.TestStatusPass } else { status = entities.TestStatusFail } case rpc.OutputWontBuild: status = entities.TestStatusWontBuild case rpc.OutputError: status = entities.TestStatusError default: err = fmt.Errorf("unknown output type: %s", out.Type) return } //add the test result to the operation tid := bson.NewObjectId() ops = append(ops, txn.Op{ C: "TestResult", Id: tid, Insert: entities.TestResult{ WorkResultID: wkey, ImportPath: out.ImportPath, Revision: args.Revision, RevDate: args.RevDate, When: time.Now(), Output: out.Output, Status: status, }, }) //skip if we don't have a notification if out.Config.NotifyOn == "" { continue } //add in the notification nots = append(nots, txn.Op{ C: "Notification", Id: bson.NewObjectId(), Insert: entities.Notification{ Test: tid, Config: out.Config, Status: entities.NotifStatusWaiting, }, }) } //append the notification operations ops = append(ops, nots...) //run the transaction err = ctx.R.Run(ops, bson.NewObjectId(), nil) if err == txn.ErrAborted { ctx.Infof("Lost the race inserting result.") err = nil } //tell it to dispatch notifications if len(nots) > 0 { go http.Get(httputil.Absolute("/notifications/dispatch")) } return }