// IncKeyHandler increments the value stored in the given key, and returns it func IncKeyHandler(w http.ResponseWriter, r *http.Request) { key := "" err := util.ReadJSONInto(r.Body, &key) if err != nil { evergreen.Logger.Logf(slogger.ERROR, "Error geting key: %v", err) plugin.WriteJSON(w, http.StatusInternalServerError, err.Error()) return } change := mgo.Change{ Update: bson.M{ "$inc": bson.M{"value": 1}, }, ReturnNew: true, Upsert: true, } keyVal := &KeyVal{} _, err = db.FindAndModify(KeyValCollection, bson.M{"_id": key}, nil, change, keyVal) if err != nil { evergreen.Logger.Logf(slogger.ERROR, "error doing findAndModify: %v", err) plugin.WriteJSON(w, http.StatusInternalServerError, err.Error()) return } plugin.WriteJSON(w, http.StatusOK, keyVal) }
// Execute fetches the expansions from the API server func (incCmd *IncCommand) Execute(pluginLogger plugin.Logger, pluginCom plugin.PluginCommunicator, conf *model.TaskConfig, stop chan bool) error { err := plugin.ExpandValues(incCmd, conf.Expansions) if err != nil { return err } keyVal := &KeyVal{} resp, err := pluginCom.TaskPostJSON(IncRoute, incCmd.Key) if err != nil { return err } if resp == nil { return fmt.Errorf("received nil response from inc API call") } else { defer resp.Body.Close() } if resp.StatusCode != http.StatusOK { return fmt.Errorf("unexpected status code: %v", resp.StatusCode) } err = util.ReadJSONInto(resp.Body, keyVal) if err != nil { return fmt.Errorf("Failed to read JSON reply: %v", err) } conf.Expansions.Put(incCmd.Destination, fmt.Sprintf("%d", keyVal.Value)) return nil }
func (mc *MockCommand) Execute(logger plugin.Logger, pluginCom plugin.PluginCommunicator, conf *model.TaskConfig, stop chan bool) error { resp, err := pluginCom.TaskGetJSON(fmt.Sprintf("blah/%s/%d", mc.Param1, mc.Param2)) if resp != nil { defer resp.Body.Close() } if resp == nil { return fmt.Errorf("Received nil HTTP response from api server") } jsonReply := map[string]string{} err = util.ReadJSONInto(resp.Body, &jsonReply) if err != nil { return err } if resp.StatusCode != http.StatusOK { return fmt.Errorf("Got bad status code from API response: %v, body: %v", resp.StatusCode, jsonReply) } expectedEchoReply := fmt.Sprintf("%v/%v/%v", mc.Param1, mc.Param2, conf.Task.Id) if jsonReply["echo"] != expectedEchoReply { return fmt.Errorf("Wrong echo reply! Wanted %v, got %v", expectedEchoReply, jsonReply["echo"]) } return nil }
// getTasksForLatestVersion sends back the TaskJSON data associated with the latest version. func getTasksForLatestVersion(w http.ResponseWriter, r *http.Request) { name := mux.Vars(r)["name"] var jsonTask TaskJSON projects := []string{} err := util.ReadJSONInto(r.Body, &projects) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } versionData := []VersionData{} for _, project := range projects { err := db.FindOneQ(collection, db.Query(bson.M{NameKey: name, ProjectIdKey: project}).Sort([]string{"-" + RevisionOrderNumberKey}).WithFields(VersionIdKey), &jsonTask) if err != nil { if err != mgo.ErrNotFound { http.Error(w, err.Error(), http.StatusInternalServerError) return } http.Error(w, "{}", http.StatusNotFound) return } if jsonTask.VersionId == "" { http.Error(w, "{}", http.StatusNotFound) } jsonTasks, err := findTasksForVersion(jsonTask.VersionId, name) if jsonTasks == nil { http.Error(w, "{}", http.StatusNotFound) return } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // get the version commit info v, err := version.FindOne(version.ById(jsonTask.VersionId)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if v == nil { http.Error(w, "{}", http.StatusNotFound) return } commitInfo := CommitInfo{ Author: v.Author, Message: v.Message, CreateTime: v.CreateTime, Revision: v.Revision, VersionId: jsonTask.VersionId, } versionData = append(versionData, VersionData{jsonTasks, commitInfo}) } plugin.WriteJSON(w, http.StatusOK, versionData) }
// AttachFiles updates file mappings for a task or build func (as *APIServer) AttachFiles(w http.ResponseWriter, r *http.Request) { task := MustHaveTask(r) evergreen.Logger.Logf(slogger.INFO, "Attaching files to task %v", task.Id) entry := &artifact.Entry{ TaskId: task.Id, TaskDisplayName: task.DisplayName, BuildId: task.BuildId, } err := util.ReadJSONInto(r.Body, &entry.Files) if err != nil { message := fmt.Sprintf("Error reading file definitions for task %v: %v", task.Id, err) evergreen.Logger.Errorf(slogger.ERROR, message) as.WriteJSON(w, http.StatusBadRequest, message) return } fmt.Printf("file entry is %#v\n", entry) if err := entry.Upsert(); err != nil { message := fmt.Sprintf("Error updating artifact file info for task %v: %v", task.Id, err) evergreen.Logger.Errorf(slogger.ERROR, message) as.WriteJSON(w, http.StatusInternalServerError, message) return } as.WriteJSON(w, http.StatusOK, fmt.Sprintf("Artifact files for task %v successfully attached", task.Id)) }
// insertTask creates a TaskJSON document with the data sent in the request body. func insertTask(w http.ResponseWriter, r *http.Request) { t := plugin.GetTask(r) if t == nil { http.Error(w, "task not found", http.StatusNotFound) return } name := mux.Vars(r)["name"] rawData := map[string]interface{}{} err := util.ReadJSONInto(r.Body, &rawData) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } jsonBlob := TaskJSON{ TaskId: t.Id, TaskName: t.DisplayName, Name: name, BuildId: t.BuildId, Variant: t.BuildVariant, ProjectId: t.Project, VersionId: t.Version, CreateTime: t.CreateTime, Revision: t.Revision, RevisionOrderNumber: t.RevisionOrderNumber, Data: rawData, IsPatch: t.Requester == evergreen.PatchVersionRequester, } _, err = db.Upsert(collection, bson.M{TaskIdKey: t.Id, NameKey: name}, jsonBlob) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } plugin.WriteJSON(w, http.StatusOK, "ok") return }
func (as *APIServer) getUserSession(w http.ResponseWriter, r *http.Request) { userCredentials := struct { Username string `json:"username"` Password string `json:"password"` }{} if err := util.ReadJSONInto(r.Body, &userCredentials); err != nil { as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Error reading user credentials: %v", err)) return } userToken, err := as.UserManager.CreateUserToken(userCredentials.Username, userCredentials.Password) if err != nil { as.WriteJSON(w, http.StatusUnauthorized, err.Error()) return } dataOut := struct { User struct { Name string `json:"name"` } `json:"user"` Token string `json:"token"` }{} dataOut.User.Name = userCredentials.Username dataOut.Token = userToken as.WriteJSON(w, http.StatusOK, dataOut) }
//XXX remove this once the transition is complete... //AttachResultsHandler is an API hook for receiving and updating test results func AttachResultsHandler(w http.ResponseWriter, r *http.Request) { task := plugin.GetTask(r) if task == nil { message := "Cannot find task for attach results request" evergreen.Logger.Errorf(slogger.ERROR, message) http.Error(w, message, http.StatusBadRequest) return } results := &model.TestResults{} err := util.ReadJSONInto(r.Body, results) if err != nil { message := fmt.Sprintf("error reading test results: %v", err) evergreen.Logger.Errorf(slogger.ERROR, message) http.Error(w, message, http.StatusBadRequest) return } // set test result of task if err := task.SetResults(results.Results); err != nil { message := fmt.Sprintf("Error calling set results on task %v: %v", task.Id, err) evergreen.Logger.Errorf(slogger.ERROR, message) http.Error(w, message, http.StatusInternalServerError) return } plugin.WriteJSON(w, http.StatusOK, "Test results successfully attached") }
// GetProjectConfig loads the communicator's task's project from the API server. func (h *HTTPCommunicator) GetProjectRef() (*model.ProjectRef, error) { projectRef := &model.ProjectRef{} retriableGet := util.RetriableFunc( func() error { resp, err := h.tryGet("project_ref") if resp != nil { defer resp.Body.Close() } if resp != nil && resp.StatusCode == http.StatusConflict { // Something very wrong, fail now with no retry. return fmt.Errorf("conflict - wrong secret!") } if err != nil { // Some generic error trying to connect - try again return util.RetriableError{err} } if resp == nil { return util.RetriableError{fmt.Errorf("empty response")} } else { err = util.ReadJSONInto(resp.Body, projectRef) if err != nil { return util.RetriableError{err} } return nil } }, ) retryFail, err := util.Retry(retriableGet, h.MaxAttempts, h.RetrySleep) if retryFail { return nil, fmt.Errorf("getting project ref failed after %v tries: %v", h.MaxAttempts, err) } return projectRef, nil }
// AttachFilesHandler updates file mappings for a task or build func AttachFilesHandler(w http.ResponseWriter, r *http.Request) { task := plugin.GetTask(r) evergreen.Logger.Logf(slogger.INFO, "Attaching files to task %v", task.Id) fileEntry := &artifact.Entry{} fileEntry.TaskId = task.Id fileEntry.TaskDisplayName = task.DisplayName fileEntry.BuildId = task.BuildId err := util.ReadJSONInto(r.Body, &fileEntry.Files) if err != nil { message := fmt.Sprintf("error reading file definitions: %v", err) evergreen.Logger.Errorf(slogger.ERROR, message) http.Error(w, message, http.StatusBadRequest) return } if err := fileEntry.Upsert(); err != nil { message := fmt.Sprintf("Error updating artifact file info: %v", err) evergreen.Logger.Errorf(slogger.ERROR, message) http.Error(w, message, http.StatusInternalServerError) return } plugin.WriteJSON(w, http.StatusOK, fmt.Sprintf("Artifact files for task %v successfully attached", task.Id)) }
// Heartbeat sends a heartbeat to the API server. The server can respond with // and "abort" response. This function returns true if the agent should abort. func (h *HTTPCommunicator) Heartbeat() (bool, error) { h.Logger.Logf(slogger.INFO, "Sending heartbeat.") resp, err := h.tryPostJSON("heartbeat", "heartbeat") if resp != nil { defer resp.Body.Close() } if err != nil { h.Logger.Logf(slogger.ERROR, "Error sending heartbeat: %v", err) return false, err } if resp != nil && resp.StatusCode != http.StatusOK { return false, fmt.Errorf("unexpected status code doing heartbeat: %v", resp.StatusCode) } if resp != nil && resp.StatusCode == http.StatusConflict { h.Logger.Logf(slogger.ERROR, "wrong secret (409) sending heartbeat") h.SignalChan <- IncorrectSecret return false, fmt.Errorf("unauthorized - wrong secret") } heartbeatResponse := &apimodels.HeartbeatResponse{} if err = util.ReadJSONInto(resp.Body, heartbeatResponse); err != nil { h.Logger.Logf(slogger.ERROR, "Error unmarshaling heartbeat "+ "response: %v", err) return false, err } return heartbeatResponse.Abort, nil }
// PutPatch submits a new patch for the given project to the API server. If successful, returns // the patch object itself. func (ac *APIClient) PutPatch(incomingPatch patchSubmission) (*patch.Patch, error) { authToken, err := generateTokenParam(ac.User, ac.APIKey) if err != nil { return nil, err } data := url.Values{} data.Set("id_token", authToken) data.Set("desc", incomingPatch.description) data.Set("project", incomingPatch.projectId) data.Set("patch", incomingPatch.patchData) data.Set("githash", incomingPatch.base) if incomingPatch.finalize { data.Set("buildvariants", incomingPatch.variants) data.Set("finalize", "true") } else { data.Set("buildvariants", "all") } resp, err := ac.put("patches/", bytes.NewBufferString(data.Encode()), true) if err != nil { return nil, err } if resp.StatusCode != http.StatusCreated { return nil, NewAPIError(resp) } reply := struct { Patch *patch.Patch `json:"patch"` }{} if err := util.ReadJSONInto(resp.Body, &reply); err != nil { return nil, err } return reply.Patch, nil }
func (jsc *JSONSendCommand) Execute(log plugin.Logger, com plugin.PluginCommunicator, conf *model.TaskConfig, stop chan bool) error { if jsc.File == "" { return fmt.Errorf("'file' param must not be blank") } if jsc.DataName == "" { return fmt.Errorf("'name' param must not be blank") } errChan := make(chan error) go func() { // attempt to open the file fileLoc := filepath.Join(conf.WorkDir, jsc.File) jsonFile, err := os.Open(fileLoc) if err != nil { errChan <- fmt.Errorf("Couldn't open json file: '%v'", err) return } jsonData := map[string]interface{}{} err = util.ReadJSONInto(jsonFile, &jsonData) if err != nil { errChan <- fmt.Errorf("File contained invalid json: %v", err) return } retriablePost := util.RetriableFunc( func() error { log.LogTask(slogger.INFO, "Posting JSON") resp, err := com.TaskPostJSON(fmt.Sprintf("data/%v", jsc.DataName), jsonData) if resp != nil { defer resp.Body.Close() } if err != nil { return util.RetriableError{err} } if resp.StatusCode != http.StatusOK { return util.RetriableError{fmt.Errorf("unexpected status code %v", resp.StatusCode)} } return nil }, ) _, err = util.Retry(retriablePost, 10, 3*time.Second) errChan <- err }() select { case err := <-errChan: if err != nil { log.LogTask(slogger.ERROR, "Sending json data failed: %v", err) } return err case <-stop: log.LogExecution(slogger.INFO, "Received abort signal, stopping.") return nil } }
func (uis *UIServer) modifyHosts(w http.ResponseWriter, r *http.Request) { _ = MustHaveUser(r) opts := &uiParams{} err := util.ReadJSONInto(r.Body, opts) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } hostIds := opts.HostIds if len(hostIds) == 1 && strings.TrimSpace(hostIds[0]) == "" { http.Error(w, "No host ID's found in request", http.StatusBadRequest) return } // fetch all relevant hosts hosts, err := host.Find(host.ByIds(hostIds)) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finding hosts: %v", err)) return } if len(hosts) == 0 { http.Error(w, "No matching hosts found.", http.StatusBadRequest) return } // determine what action needs to be taken switch opts.Action { case "updateStatus": newStatus := opts.Status if !util.SliceContains(validUpdateToStatuses, newStatus) { http.Error(w, fmt.Sprintf("Invalid status: %v", opts.Status), http.StatusBadRequest) return } numHostsUpdated := 0 for _, host := range hosts { err := host.SetStatus(newStatus) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error updating host %v", err)) return } numHostsUpdated += 1 } msg := NewSuccessFlash(fmt.Sprintf("%v host(s) status successfully updated to '%v'", numHostsUpdated, newStatus)) PushFlash(uis.CookieStore, r, w, msg) return default: http.Error(w, fmt.Sprintf("Unrecognized action: %v", opts.Action), http.StatusBadRequest) return } }
func (as *APIServer) StartTask(w http.ResponseWriter, r *http.Request) { task := MustHaveTask(r) if !getGlobalLock(r.RemoteAddr, task.Id) { as.LoggedError(w, r, http.StatusInternalServerError, ErrLockTimeout) return } defer releaseGlobalLock(r.RemoteAddr, task.Id) evergreen.Logger.Logf(slogger.INFO, "Marking task started: %v", task.Id) taskStartInfo := &apimodels.TaskStartRequest{} if err := util.ReadJSONInto(r.Body, taskStartInfo); err != nil { http.Error(w, fmt.Sprintf("Error reading task start request for %v: %v", task.Id, err), http.StatusBadRequest) return } if err := task.MarkStart(); err != nil { message := fmt.Errorf("Error marking task '%v' started: %v", task.Id, err) as.LoggedError(w, r, http.StatusInternalServerError, message) return } h, err := host.FindOne(host.ByRunningTaskId(task.Id)) if err != nil { message := fmt.Errorf("Error finding host running task %v: %v", task.Id, err) as.LoggedError(w, r, http.StatusInternalServerError, message) return } // Fall back to checking host field on task doc if h == nil && len(task.HostId) > 0 { evergreen.Logger.Logf(slogger.DEBUG, "Falling back to host field of task: %v", task.Id) h, err = host.FindOne(host.ById(task.HostId)) if err != nil { as.LoggedError(w, r, http.StatusInternalServerError, err) return } h.SetRunningTask(task.Id, h.AgentRevision, h.TaskDispatchTime) } if h == nil { message := fmt.Errorf("No host found running task %v", task.Id) as.LoggedError(w, r, http.StatusInternalServerError, message) return } if err := h.SetTaskPid(taskStartInfo.Pid); err != nil { message := fmt.Errorf("Error calling set pid on task %v : %v", task.Id, err) as.LoggedError(w, r, http.StatusInternalServerError, message) return } as.WriteJSON(w, http.StatusOK, fmt.Sprintf("Task %v started on host %v", task.Id, h.Id)) }
func (as *APIServer) requestHost(w http.ResponseWriter, r *http.Request) { user := MustHaveUser(r) hostRequest := struct { Distro string `json:"distro"` PublicKey string `json:"public_key"` UserData string `json:"userdata"` }{} err := util.ReadJSONInto(r.Body, &hostRequest) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if hostRequest.Distro == "" { http.Error(w, "distro may not be blank", http.StatusBadRequest) return } if hostRequest.PublicKey == "" { http.Error(w, "public key may not be blank", http.StatusBadRequest) return } opts := spawn.Options{ Distro: hostRequest.Distro, UserName: user.Id, PublicKey: hostRequest.PublicKey, UserData: hostRequest.UserData, } spawner := spawn.New(&as.Settings) err = spawner.Validate(opts) if err != nil { errCode := http.StatusBadRequest if _, ok := err.(spawn.BadOptionsErr); !ok { errCode = http.StatusInternalServerError } as.LoggedError(w, r, errCode, fmt.Errorf("Spawn request failed validation: %v", err)) return } err = spawner.CreateHost(opts, user) if err != nil { evergreen.Logger.Logf(slogger.ERROR, err.Error()) mailErr := notify.TrySendNotificationToUser(opts.UserName, "Spawning failed", err.Error(), notify.ConstructMailer(as.Settings.Notify)) if mailErr != nil { evergreen.Logger.Logf(slogger.ERROR, "Failed to send notification: %v", mailErr) } return } as.WriteJSON(w, http.StatusOK, "") }
// UserMiddleware checks for session tokens on the request, then looks up and attaches a user // for that token if one is found. func UserMiddleware(um auth.UserManager) func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { err := r.ParseForm() if err != nil { http.Error(w, "can't parse form?", http.StatusBadRequest) return } // Note: at this point the "token" is actually a json object in string form, // containing both the username and token. token := r.FormValue("id_token") if len(token) == 0 { next(w, r) return } authData := struct { Name string `json:"auth_user"` Token string `json:"auth_token"` APIKey string `json:"api_key"` }{} if err := util.ReadJSONInto(ioutil.NopCloser(strings.NewReader(token)), &authData); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if len(authData.Token) > 0 { // legacy auth - token lookup authedUser, err := um.GetUserByToken(authData.Token) if err != nil { evergreen.Logger.Logf(slogger.ERROR, "Error getting user: %v", err) } else { // Get the user's full details from the DB or create them if they don't exists dbUser, err := model.GetOrCreateUser(authedUser.Username(), authedUser.DisplayName(), authedUser.Email()) if err != nil { evergreen.Logger.Logf(slogger.ERROR, "Error looking up user %v: %v", authedUser.Username(), err) } else { context.Set(r, apiUserKey, dbUser) } } } else if len(authData.APIKey) > 0 { dbUser, err := user.FindOne(user.ById(authData.Name)) if dbUser != nil && err == nil { if dbUser.APIKey != authData.APIKey { http.Error(w, "Unauthorized - invalid API key", http.StatusUnauthorized) return } context.Set(r, apiUserKey, dbUser) } else { evergreen.Logger.Logf(slogger.ERROR, "Error getting user: %v", err) } } next(w, r) } }
// FetchExpansionVars loads expansions for a communicator's task from the API server. func (h *HTTPCommunicator) FetchExpansionVars() (*apimodels.ExpansionVars, error) { resultVars := &apimodels.ExpansionVars{} retriableGet := util.RetriableFunc( func() error { resp, err := h.tryGet("fetch_vars") if resp != nil { defer resp.Body.Close() } if err != nil { // Some generic error trying to connect - try again h.Logger.Logf(slogger.ERROR, "failed trying to call fetch GET: %v", err) return util.RetriableError{err} } if resp.StatusCode == http.StatusUnauthorized { err = fmt.Errorf("fetching expansions failed: got 'unauthorized' response.") h.Logger.Logf(slogger.ERROR, err.Error()) return err } if resp.StatusCode != http.StatusOK { err = fmt.Errorf("failed trying fetch GET, got bad response code: %v", resp.StatusCode) h.Logger.Logf(slogger.ERROR, err.Error()) return util.RetriableError{err} } if resp == nil { err = fmt.Errorf("empty response fetching expansions") h.Logger.Logf(slogger.ERROR, err.Error()) return util.RetriableError{err} } // got here safely, so all is good - read the results err = util.ReadJSONInto(resp.Body, resultVars) if err != nil { err = fmt.Errorf("failed to read vars from response: %v", err) h.Logger.Logf(slogger.ERROR, err.Error()) return err } return nil }, ) retryFail, err := util.Retry(retriableGet, 10, 1*time.Second) if err != nil { // stop trying to make fetch happen, it's not going to happen if retryFail { h.Logger.Logf(slogger.ERROR, "Fetching vars used up all retries.") } return nil, err } return resultVars, err }
// GetPatches requests a list of the user's patches from the API and returns them as a list func (ac *APIClient) GetPatches(n int) ([]patch.Patch, error) { resp, err := ac.get(fmt.Sprintf("patches/mine?n=%v", n), nil) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, NewAPIError(resp) } patches := []patch.Patch{} if err := util.ReadJSONInto(resp.Body, &patches); err != nil { return nil, err } return patches, nil }
func TestAttachResults(t *testing.T) { resetTasks(t) testConfig := evergreen.TestConfig() cwd := testutil.GetDirectoryOfFile() Convey("With attachResults plugin installed into plugin registry", t, func() { registry := plugin.NewSimpleRegistry() attachPlugin := &AttachPlugin{} err := registry.Register(attachPlugin) testutil.HandleTestingErr(err, t, "Couldn't register plugin: %v") server, err := service.CreateTestServer(testConfig, nil, plugin.APIPlugins, true) testutil.HandleTestingErr(err, t, "Couldn't set up testing server") httpCom := plugintest.TestAgentCommunicator("mocktaskid", "mocktasksecret", server.URL) configFile := filepath.Join(cwd, "testdata", "plugin_attach_results.yml") resultsLoc := filepath.Join(cwd, "testdata", "plugin_attach_results.json") taskConfig, err := plugintest.CreateTestConfig(configFile, t) testutil.HandleTestingErr(err, t, "failed to create test config: %v") taskConfig.WorkDir = "." sliceAppender := &evergreen.SliceAppender{[]*slogger.Log{}} logger := agentutil.NewTestLogger(sliceAppender) Convey("all commands in test project should execute successfully", func() { for _, projTask := range taskConfig.Project.Tasks { So(len(projTask.Commands), ShouldNotEqual, 0) for _, command := range projTask.Commands { pluginCmds, err := registry.GetCommands(command, taskConfig.Project.Functions) testutil.HandleTestingErr(err, t, "Couldn't get plugin command: %v") So(pluginCmds, ShouldNotBeNil) So(err, ShouldBeNil) pluginCom := &comm.TaskJSONCommunicator{pluginCmds[0].Plugin(), httpCom} err = pluginCmds[0].Execute(logger, pluginCom, taskConfig, make(chan bool)) So(err, ShouldBeNil) testTask, err := task.FindOne(task.ById(httpCom.TaskId)) testutil.HandleTestingErr(err, t, "Couldn't find task") So(testTask, ShouldNotBeNil) // ensure test results are exactly as expected // attempt to open the file reportFile, err := os.Open(resultsLoc) testutil.HandleTestingErr(err, t, "Couldn't open report file: '%v'", err) results := &task.TestResults{} err = util.ReadJSONInto(reportFile, results) testutil.HandleTestingErr(err, t, "Couldn't read report file: '%v'", err) testResults := *results So(testTask.TestResults, ShouldResemble, testResults.Results) testutil.HandleTestingErr(err, t, "Couldn't clean up test temp dir") } } }) }) }
// GetPatch fetches the config requests project details from the API server for a given project ID. func (ac *APIClient) GetPatch(patchId string) (*service.RestPatch, error) { resp, err := ac.get(fmt.Sprintf("patches/%v", patchId), nil) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, NewAPIError(resp) } ref := &service.RestPatch{} if err := util.ReadJSONInto(resp.Body, ref); err != nil { return nil, err } return ref, nil }
// GetProjectRef requests project details from the API server for a given project ID. func (ac *APIClient) GetProjectRef(projectId string) (*model.ProjectRef, error) { resp, err := ac.get(fmt.Sprintf("/ref/%s", projectId), nil) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, NewAPIError(resp) } ref := &model.ProjectRef{} if err := util.ReadJSONInto(resp.Body, ref); err != nil { return nil, err } return ref, nil }
func (ac *APIClient) ListProjects() ([]model.ProjectRef, error) { resp, err := ac.get("projects", nil) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, NewAPIError(resp) } projs := []model.ProjectRef{} if err := util.ReadJSONInto(resp.Body, &projs); err != nil { return nil, err } return projs, nil }
func (ac *APIClient) ListTasks(project string) ([]model.ProjectTask, error) { resp, err := ac.get(fmt.Sprintf("tasks/%v", project), nil) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, NewAPIError(resp) } tasks := []model.ProjectTask{} if err := util.ReadJSONInto(resp.Body, &tasks); err != nil { return nil, err } return tasks, nil }
func (ac *APIClient) ListVariants(project string) ([]model.BuildVariant, error) { resp, err := ac.get(fmt.Sprintf("variants/%v", project), nil) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, NewAPIError(resp) } variants := []model.BuildVariant{} if err := util.ReadJSONInto(resp.Body, &variants); err != nil { return nil, err } return variants, nil }
// PutPatch submits a new patch for the given project to the API server. If successful, returns // the patch object itself. func (ac *APIClient) PutPatch(incomingPatch patchSubmission) (*patch.Patch, error) { data := struct { Description string `json:"desc"` Project string `json:"project"` Patch string `json:"patch"` Githash string `json:"githash"` Variants string `json:"buildvariants"` Finalize bool `json:"finalize"` }{ incomingPatch.description, incomingPatch.projectId, incomingPatch.patchData, incomingPatch.base, "all", false, } if incomingPatch.finalize { data.Variants = incomingPatch.variants data.Finalize = true } rPipe, wPipe := io.Pipe() encoder := json.NewEncoder(wPipe) go func() { encoder.Encode(data) wPipe.Close() }() defer rPipe.Close() resp, err := ac.put("patches/", rPipe) if err != nil { return nil, err } if resp.StatusCode != http.StatusCreated { return nil, NewAPIError(resp) } reply := struct { Patch *patch.Patch `json:"patch"` }{} if err := util.ReadJSONInto(resp.Body, &reply); err != nil { return nil, err } return reply.Patch, nil }
// AttachResults attaches the received results to the task in the database. func (as *APIServer) AttachResults(w http.ResponseWriter, r *http.Request) { task := MustHaveTask(r) results := &model.TestResults{} err := util.ReadJSONInto(r.Body, results) if err != nil { as.LoggedError(w, r, http.StatusBadRequest, err) return } // set test result of task if err := task.SetResults(results.Results); err != nil { as.LoggedError(w, r, http.StatusInternalServerError, err) return } as.WriteJSON(w, http.StatusOK, "test results successfully attached") }
// CheckUpdates fetches information about available updates to client binaries from the server. func (ac *APIClient) CheckUpdates() (*evergreen.ClientConfig, error) { resp, err := ac.get("update", nil) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, NewAPIError(resp) } reply := evergreen.ClientConfig{} if err := util.ReadJSONInto(resp.Body, &reply); err != nil { return nil, err } return &reply, nil }
func (uis *UIServer) userSettingsModify(w http.ResponseWriter, r *http.Request) { currentUser := MustHaveUser(r) userSettings := user.UserSettings{} if err := util.ReadJSONInto(r.Body, &userSettings); err != nil { uis.LoggedError(w, r, http.StatusBadRequest, err) return } if err := model.SaveUserSettings(currentUser.Username(), userSettings); err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error saving user settings: %v", err)) return } PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Settings were saved.")) uis.WriteJSON(w, http.StatusOK, "Updated user settings successfully") }
// GetProjectConfig loads the communicator's task's project from the API server. func (h *HTTPCommunicator) GetProjectConfig() (*model.Project, error) { projectConfig := &model.Project{} retriableGet := util.RetriableFunc( func() error { resp, err := h.tryGet("version") if resp != nil { defer resp.Body.Close() } if resp != nil && resp.StatusCode == http.StatusConflict { // Something very wrong, fail now with no retry. return fmt.Errorf("conflict - wrong secret!") } if err != nil { // Some generic error trying to connect - try again return util.RetriableError{err} } if resp == nil { return util.RetriableError{fmt.Errorf("empty response")} } else { v := &version.Version{} err = util.ReadJSONInto(resp.Body, v) if err != nil { h.Logger.Errorf(slogger.ERROR, "unable to read project version response: %v\n", err) return util.RetriableError{fmt.Errorf("unable to read "+ "project version response: %v\n", err)} } err = model.LoadProjectInto([]byte(v.Config), v.Project, projectConfig) if err != nil { h.Logger.Errorf(slogger.ERROR, "unable to unmarshal project config: %v\n", err) return util.RetriableError{fmt.Errorf("unable to "+ "unmarshall project config: %v\n", err)} } return nil } }, ) retryFail, err := util.Retry(retriableGet, h.MaxAttempts, h.RetrySleep) if retryFail { return nil, fmt.Errorf("getting project configuration failed after %v "+ "tries: %v", h.MaxAttempts, err) } return projectConfig, nil }