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  }