github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/service/api_status.go (about) 1 package service 2 3 import ( 4 "fmt" 5 "net/http" 6 "strconv" 7 "time" 8 9 "github.com/evergreen-ci/evergreen/apimodels" 10 "github.com/evergreen-ci/evergreen/model" 11 "github.com/evergreen-ci/evergreen/util" 12 "github.com/gorilla/mux" 13 ) 14 15 const ( 16 apiStatusSuccess = "SUCCESS" 17 apiStatusError = "ERROR" 18 ) 19 20 // taskAssignmentResp holds the status, errors and four separate lists of task and host ids 21 // this is so that when addressing inconsistencies we can differentiate between the states of 22 // the tasks and hosts. 23 // Status is either SUCCESS or ERROR. 24 // Errors is a list of all the errors that exist. 25 // TaskIds are a list of the tasks that have errors associated with them. 26 // TaskHostIds are a list of the hosts that exist that are said to be running on tasks with errors. 27 // HostIds are a list of hosts that have errors associated with them. 28 // HostRunningTasks are list of the tasks that are said to be running on inconsistent hosts. 29 type taskAssignmentResp struct { 30 Status string `json:"status"` 31 Errors []string `json:"errors"` 32 TaskIds []string `json:"tasks"` 33 TaskHostIds []string `json:"task_host_ids"` 34 HostIds []string `json:"hosts"` 35 HostRunningTasks []string `json:"host_running_tasks"` 36 } 37 38 type stuckHostResp struct { 39 Status string `json:"status"` 40 Errors []string `json:"errors"` 41 TaskIds []string `json:"tasks"` 42 HostIds []string `json:"hosts"` 43 } 44 45 // consistentTaskAssignment returns any disparities between tasks' and hosts's views 46 // of their mapping between each other. JSON responses take the form of 47 // {status: “ERROR/SUCCESS”, errors:[error strings], tasks:[ids], hosts:[ids]} 48 func (as *APIServer) consistentTaskAssignment(w http.ResponseWriter, r *http.Request) { 49 disparities, err := model.AuditHostTaskConsistency() 50 if err != nil { 51 as.LoggedError(w, r, http.StatusInternalServerError, err) 52 return 53 } 54 resp := taskAssignmentResp{Status: apiStatusSuccess} 55 if len(disparities) > 0 { 56 resp.Status = apiStatusError 57 for _, d := range disparities { 58 resp.Errors = append(resp.Errors, d.Error()) 59 if d.Task != "" { 60 resp.TaskIds = append(resp.TaskIds, d.Task) 61 } 62 if d.HostTaskCache != "" { 63 resp.HostRunningTasks = append(resp.HostRunningTasks, d.HostTaskCache) 64 } 65 if d.Host != "" { 66 resp.HostIds = append(resp.HostIds, d.Host) 67 } 68 if d.TaskHostCache != "" { 69 resp.TaskHostIds = append(resp.TaskHostIds, d.TaskHostCache) 70 } 71 } 72 // dedupe id slices before returning, for simplicity 73 resp.TaskIds = util.UniqueStrings(resp.TaskIds) 74 resp.HostIds = util.UniqueStrings(resp.HostIds) 75 resp.HostRunningTasks = util.UniqueStrings(resp.HostRunningTasks) 76 resp.TaskHostIds = util.UniqueStrings(resp.TaskHostIds) 77 } 78 as.WriteJSON(w, http.StatusOK, resp) 79 } 80 81 // Returns a list of all processes with runtime entries, i.e. all processes being tracked. 82 func (as *APIServer) listRuntimes(w http.ResponseWriter, r *http.Request) { 83 runtimes, err := model.FindEveryProcessRuntime() 84 if err != nil { 85 as.LoggedError(w, r, http.StatusInternalServerError, err) 86 return 87 } 88 as.WriteJSON(w, http.StatusOK, runtimes) 89 } 90 91 // Given a timeout cutoff in seconds, returns a JSON response with a SUCCESS flag 92 // if all processes have run within the cutoff, or ERROR and a list of late processes 93 // if one or more processes last finished before the timeout cutoff. DevOps tools 94 // should be able to do a regex for "SUCCESS" or "ERROR" to check for timeouts. 95 func (as *APIServer) lateRuntimes(w http.ResponseWriter, r *http.Request) { 96 vars := mux.Vars(r) 97 timeAsString := vars["seconds"] 98 if len(timeAsString) == 0 { 99 http.Error(w, "Must supply an amount in seconds with timeout query", http.StatusBadRequest) 100 return 101 } 102 timeInSeconds, err := strconv.Atoi(timeAsString) 103 if err != nil { 104 http.Error(w, fmt.Sprintf("Invalid time param: %v", timeAsString), http.StatusBadRequest) 105 return 106 } 107 cutoff := time.Now().Add(time.Duration(-1*timeInSeconds) * time.Second) 108 runtimes, err := model.FindAllLateProcessRuntimes(cutoff) 109 if err != nil { 110 as.LoggedError(w, r, http.StatusInternalServerError, err) 111 return 112 } 113 114 timeoutResponse := apimodels.ProcessTimeoutResponse{} 115 timeoutResponse.LateProcesses = &runtimes 116 if len(runtimes) > 0 { 117 timeoutResponse.Status = apiStatusError 118 } else { 119 timeoutResponse.Status = apiStatusSuccess 120 } 121 as.WriteJSON(w, http.StatusOK, timeoutResponse) 122 } 123 124 func (as *APIServer) getTaskQueueSizes(w http.ResponseWriter, r *http.Request) { 125 126 distroNames := make(map[string]int) 127 taskQueues, err := model.FindAllTaskQueues() 128 if err != nil { 129 as.LoggedError(w, r, http.StatusInternalServerError, err) 130 return 131 } 132 for _, queue := range taskQueues { 133 distroNames[queue.Distro] = queue.Length() 134 } 135 taskQueueResponse := struct { 136 Distros map[string]int 137 }{distroNames} 138 139 as.WriteJSON(w, http.StatusOK, taskQueueResponse) 140 } 141 142 // getTaskQueueSize returns a JSON response with a SUCCESS flag if all task queues have a size 143 // less than the size indicated. If a distro's task queue has size greater than or equal to the size given, 144 // there will be an ERROR flag along with a map of the distro name to the size of the task queue. 145 // If the size is 0 or the size is not sent, the JSON response will be SUCCESS with a list of all distros and their 146 // task queue sizes. 147 func (as *APIServer) checkTaskQueueSize(w http.ResponseWriter, r *http.Request) { 148 size, err := util.GetIntValue(r, "size", 0) 149 if err != nil { 150 as.LoggedError(w, r, http.StatusInternalServerError, err) 151 return 152 } 153 distro := r.FormValue("distro") 154 155 distroNames := make(map[string]int) 156 status := apiStatusSuccess 157 158 if distro != "" { 159 taskQueue, err := model.FindTaskQueueForDistro(distro) 160 if err != nil { 161 as.LoggedError(w, r, http.StatusBadRequest, err) 162 return 163 } 164 if taskQueue.Length() >= size { 165 distroNames[distro] = taskQueue.Length() 166 status = apiStatusError 167 } 168 } else { 169 taskQueues, err := model.FindAllTaskQueues() 170 if err != nil { 171 as.LoggedError(w, r, http.StatusInternalServerError, err) 172 return 173 } 174 for _, queue := range taskQueues { 175 if queue.Length() >= size { 176 distroNames[queue.Distro] = queue.Length() 177 status = apiStatusError 178 } 179 } 180 } 181 growthResponse := struct { 182 Status string 183 Distros map[string]int 184 }{status, distroNames} 185 186 as.WriteJSON(w, http.StatusOK, growthResponse) 187 } 188 189 // getStuckHosts returns hosts that have tasks running that are completed 190 func (as *APIServer) getStuckHosts(w http.ResponseWriter, r *http.Request) { 191 stuckHosts, err := model.CheckStuckHosts() 192 if err != nil { 193 as.LoggedError(w, r, http.StatusInternalServerError, err) 194 return 195 } 196 errors := []string{} 197 hosts := []string{} 198 tasks := []string{} 199 for _, disparity := range stuckHosts { 200 errors = append(errors, disparity.Error()) 201 hosts = append(hosts, disparity.Host) 202 tasks = append(tasks, disparity.RunningTask) 203 } 204 status := apiStatusSuccess 205 if len(stuckHosts) > 0 { 206 status = apiStatusError 207 } 208 209 as.WriteJSON(w, http.StatusOK, stuckHostResp{ 210 Status: status, 211 Errors: errors, 212 HostIds: hosts, 213 TaskIds: tasks, 214 }) 215 }