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 }