github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/service/build.go (about)

     1  package service
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"strconv"
     9  	"time"
    10  
    11  	"github.com/evergreen-ci/evergreen"
    12  	"github.com/evergreen-ci/evergreen/model"
    13  	"github.com/evergreen-ci/evergreen/model/build"
    14  	"github.com/evergreen-ci/evergreen/model/task"
    15  	"github.com/evergreen-ci/evergreen/model/user"
    16  	"github.com/evergreen-ci/evergreen/model/version"
    17  	"github.com/evergreen-ci/evergreen/plugin"
    18  	"github.com/evergreen-ci/evergreen/util"
    19  	"github.com/gorilla/mux"
    20  	"github.com/mongodb/grip"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  // getUiTaskCache takes a build object and returns a slice of
    25  // uiTask objects suitable for front-end
    26  func getUiTaskCache(build *build.Build) ([]uiTask, error) {
    27  	tasks, err := task.Find(task.ByBuildId(build.Id))
    28  	if err != nil {
    29  		return nil, errors.WithStack(err)
    30  	}
    31  
    32  	idToTask := make(map[string]task.Task)
    33  	for _, task := range tasks {
    34  		idToTask[task.Id] = task
    35  	}
    36  
    37  	// Insert the tasks in the same order as the task cache
    38  	uiTasks := make([]uiTask, 0, len(build.Tasks))
    39  	for _, taskCache := range build.Tasks {
    40  		taskAsUI := uiTask{Task: idToTask[taskCache.Id]}
    41  		uiTasks = append(uiTasks, taskAsUI)
    42  	}
    43  	return uiTasks, nil
    44  }
    45  
    46  func (uis *UIServer) buildPage(w http.ResponseWriter, r *http.Request) {
    47  	projCtx := MustHaveProjectContext(r)
    48  
    49  	if projCtx.Build == nil {
    50  		uis.LoggedError(w, r, http.StatusNotFound, errors.New("not found"))
    51  		return
    52  	}
    53  
    54  	flashes := PopFlashes(uis.CookieStore, r, w)
    55  
    56  	buildAsUI := &uiBuild{
    57  		Build:       *projCtx.Build,
    58  		CurrentTime: time.Now().UnixNano(),
    59  		Elapsed:     time.Since(projCtx.Build.StartTime),
    60  		Version:     *projCtx.Version,
    61  	}
    62  
    63  	if projCtx.ProjectRef != nil {
    64  		buildAsUI.RepoOwner = projCtx.ProjectRef.Owner
    65  		buildAsUI.Repo = projCtx.ProjectRef.Repo
    66  	}
    67  
    68  	uiTasks, err := getUiTaskCache(projCtx.Build)
    69  	if err != nil {
    70  		uis.LoggedError(w, r, http.StatusInternalServerError, err)
    71  		return
    72  	}
    73  	buildAsUI.Tasks = uiTasks
    74  
    75  	if projCtx.Build.Requester == evergreen.PatchVersionRequester {
    76  		buildOnBaseCommit, err := projCtx.Build.FindBuildOnBaseCommit()
    77  		if err != nil {
    78  			http.Error(w, err.Error(), http.StatusInternalServerError)
    79  			return
    80  		}
    81  		if buildOnBaseCommit == nil {
    82  			grip.Warningln("Could not find build for base commit of patch build:",
    83  				projCtx.Build.Id)
    84  		}
    85  		diffs := model.StatusDiffBuilds(buildOnBaseCommit, projCtx.Build)
    86  
    87  		baseId := ""
    88  		if buildOnBaseCommit != nil {
    89  			baseId = buildOnBaseCommit.Id
    90  		}
    91  		buildAsUI.PatchInfo = &uiPatch{Patch: *projCtx.Patch, BaseBuildId: baseId, StatusDiffs: diffs.Tasks}
    92  	}
    93  
    94  	// set data for plugin data function injection
    95  	pluginContext := projCtx.ToPluginContext(uis.Settings, GetUser(r))
    96  	pluginContent := getPluginDataAndHTML(uis, plugin.BuildPage, pluginContext)
    97  
    98  	uis.WriteHTML(w, http.StatusOK, struct {
    99  		ProjectData   projectContext
   100  		User          *user.DBUser
   101  		Flashes       []interface{}
   102  		Build         *uiBuild
   103  		PluginContent pluginData
   104  	}{projCtx, GetUser(r), flashes, buildAsUI, pluginContent}, "base", "build.html", "base_angular.html", "menu.html")
   105  }
   106  
   107  func (uis *UIServer) modifyBuild(w http.ResponseWriter, r *http.Request) {
   108  	projCtx := MustHaveProjectContext(r)
   109  	user := MustHaveUser(r)
   110  
   111  	if projCtx.Build == nil {
   112  		http.Error(w, "Not found", http.StatusNotFound)
   113  		return
   114  	}
   115  
   116  	body := util.NewRequestReader(r)
   117  	defer body.Close()
   118  	reqBody, err := ioutil.ReadAll(body)
   119  	if err != nil {
   120  		http.Error(w, err.Error(), http.StatusInternalServerError)
   121  		return
   122  	}
   123  
   124  	putParams := struct {
   125  		Action   string   `json:"action"`
   126  		Active   bool     `json:"active"`
   127  		Abort    bool     `json:"abort"`
   128  		Priority string   `json:"priority"`
   129  		TaskIds  []string `json:"taskIds"`
   130  	}{}
   131  	err = json.Unmarshal(reqBody, &putParams)
   132  	if err != nil {
   133  		http.Error(w, err.Error(), http.StatusBadRequest)
   134  		return
   135  	}
   136  
   137  	// determine what action needs to be taken
   138  	switch putParams.Action {
   139  	case "abort":
   140  		if err := model.AbortBuild(projCtx.Build.Id, user.Id); err != nil {
   141  			http.Error(w, fmt.Sprintf("Error aborting build %v", projCtx.Build.Id), http.StatusInternalServerError)
   142  			return
   143  		}
   144  		if err := model.RefreshTasksCache(projCtx.Build.Id); err != nil {
   145  			http.Error(w, fmt.Sprintf("problem refreshing tasks cache %v", projCtx.Build.Id), http.StatusInternalServerError)
   146  			return
   147  		}
   148  	case "set_priority":
   149  		var priority int64
   150  		priority, err = strconv.ParseInt(putParams.Priority, 10, 64)
   151  		if err != nil {
   152  			http.Error(w, "Bad priority value; must be int", http.StatusBadRequest)
   153  			return
   154  		}
   155  		if priority > evergreen.MaxTaskPriority {
   156  			if !uis.isSuperUser(user) {
   157  				http.Error(w, fmt.Sprintf("Insufficient access to set priority %v, can only set prior less than or equal to %v", priority, evergreen.MaxTaskPriority),
   158  					http.StatusBadRequest)
   159  				return
   160  			}
   161  		}
   162  		err = model.SetBuildPriority(projCtx.Build.Id, priority)
   163  		if err != nil {
   164  			http.Error(w, fmt.Sprintf("Error setting priority on build %v", projCtx.Build.Id),
   165  				http.StatusInternalServerError)
   166  			return
   167  		}
   168  	case "set_active":
   169  		err := model.SetBuildActivation(projCtx.Build.Id, putParams.Active, user.Id)
   170  		if err != nil {
   171  			http.Error(w, fmt.Sprintf("Error marking build %v as activated=%v", projCtx.Build.Id, putParams.Active),
   172  				http.StatusInternalServerError)
   173  			return
   174  		}
   175  	case "restart":
   176  		if err := model.RestartBuild(projCtx.Build.Id, putParams.TaskIds, putParams.Abort, user.Id); err != nil {
   177  			http.Error(w, fmt.Sprintf("Error restarting build %v", projCtx.Build.Id), http.StatusInternalServerError)
   178  			return
   179  		}
   180  	default:
   181  		uis.WriteJSON(w, http.StatusBadRequest, "Unrecognized action")
   182  		return
   183  	}
   184  
   185  	// After updating the build, fetch updated version to serve back to client
   186  	projCtx.Build, err = build.FindOne(build.ById(projCtx.Build.Id))
   187  	if err != nil {
   188  		uis.LoggedError(w, r, http.StatusInternalServerError, err)
   189  		return
   190  	}
   191  	updatedBuild := uiBuild{
   192  		Build:       *projCtx.Build,
   193  		CurrentTime: time.Now().UnixNano(),
   194  		Elapsed:     time.Since(projCtx.Build.StartTime),
   195  		RepoOwner:   projCtx.ProjectRef.Owner,
   196  		Repo:        projCtx.ProjectRef.Repo,
   197  		Version:     *projCtx.Version,
   198  	}
   199  
   200  	uiTasks, err := getUiTaskCache(projCtx.Build)
   201  	if err != nil {
   202  		uis.LoggedError(w, r, http.StatusInternalServerError, err)
   203  		return
   204  	}
   205  	updatedBuild.Tasks = uiTasks
   206  	uis.WriteJSON(w, http.StatusOK, updatedBuild)
   207  }
   208  
   209  func (uis *UIServer) buildHistory(w http.ResponseWriter, r *http.Request) {
   210  	buildId := mux.Vars(r)["build_id"]
   211  
   212  	before, err := getIntValue(r, "before", 3)
   213  	if err != nil {
   214  		http.Error(w, fmt.Sprintf("invalid param 'before': %v", r.FormValue("before")), http.StatusBadRequest)
   215  		return
   216  	}
   217  
   218  	after, err := getIntValue(r, "after", 3)
   219  	if err != nil {
   220  		http.Error(w, fmt.Sprintf("invalid param 'after': %v", r.FormValue("after")), http.StatusBadRequest)
   221  		return
   222  	}
   223  
   224  	builds, err := getBuildVariantHistory(buildId, before, after)
   225  	if err != nil {
   226  		http.Error(w, fmt.Sprintf("error getting build history: %v", err), http.StatusInternalServerError)
   227  		return
   228  	}
   229  
   230  	history := &struct {
   231  		Builds      []*uiBuild `json:"builds"`
   232  		LastSuccess *uiBuild   `json:"lastSuccess"`
   233  	}{}
   234  
   235  	history.Builds = make([]*uiBuild, len(builds))
   236  	for i := 0; i < len(builds); i++ {
   237  		v, err := version.FindOne(version.ById(builds[i].Version))
   238  		if err != nil {
   239  			http.Error(w, fmt.Sprintf("error getting version for build %v: %v", builds[i].Id, err), http.StatusInternalServerError)
   240  			return
   241  		}
   242  		if v == nil {
   243  			http.Error(w, fmt.Sprintf("no version found for build %v", builds[i].Id), http.StatusNotFound)
   244  			return
   245  		}
   246  		history.Builds[i] = &uiBuild{
   247  			Build:       builds[i],
   248  			CurrentTime: time.Now().UnixNano(),
   249  			Elapsed:     time.Since(builds[i].StartTime),
   250  			RepoOwner:   v.Owner,
   251  			Repo:        v.Repo,
   252  			Version:     *v,
   253  		}
   254  	}
   255  
   256  	lastSuccess, err := getBuildVariantHistoryLastSuccess(buildId)
   257  	if err == nil && lastSuccess != nil {
   258  		v, err := version.FindOne(version.ById(lastSuccess.Version))
   259  		if err != nil {
   260  			http.Error(
   261  				w, fmt.Sprintf("error getting last successful build version: %v", err),
   262  				http.StatusInternalServerError)
   263  			return
   264  		}
   265  		if v == nil {
   266  			http.Error(w, fmt.Sprintf("no version '%v' found", lastSuccess.Version), http.StatusNotFound)
   267  			return
   268  		}
   269  		history.LastSuccess = &uiBuild{
   270  			Build:       *lastSuccess,
   271  			CurrentTime: time.Now().UnixNano(),
   272  			Elapsed:     time.Since(lastSuccess.StartTime),
   273  			RepoOwner:   v.Owner,
   274  			Repo:        v.Repo,
   275  			Version:     *v,
   276  		}
   277  	}
   278  
   279  	uis.WriteJSON(w, http.StatusOK, history)
   280  }