Esempio n. 1
0
// getBuildVariantHistory returns a slice of builds that surround a given build.
// As many as 'before' builds (less recent builds) plus as many as 'after' builds
// (more recent builds) are returned.
func getBuildVariantHistory(buildId string, before int, after int) ([]build.Build, error) {
	b, err := build.FindOne(build.ById(buildId))
	if err != nil {
		return nil, err
	}
	if b == nil {
		return nil, fmt.Errorf("no build with id %v", buildId)
	}

	lessRecentBuilds, err := build.Find(
		build.ByBeforeRevision(b.Project, b.BuildVariant, b.RevisionOrderNumber).
			WithFields(build.IdKey, build.TasksKey, build.StatusKey, build.VersionKey, build.ActivatedKey).
			Limit(before))
	if err != nil {
		return nil, err
	}

	moreRecentBuilds, err := build.Find(
		build.ByAfterRevision(b.Project, b.BuildVariant, b.RevisionOrderNumber).
			WithFields(build.IdKey, build.TasksKey, build.StatusKey, build.VersionKey, build.ActivatedKey).
			Limit(after))
	if err != nil {
		return nil, err
	}

	builds := make([]build.Build, 0, len(lessRecentBuilds)+len(moreRecentBuilds))
	for i := len(moreRecentBuilds); i > 0; i-- {
		builds = append(builds, moreRecentBuilds[i-1])
	}
	builds = append(builds, lessRecentBuilds...)
	return builds, nil
}
Esempio n. 2
0
func TestFinalizePatch(t *testing.T) {
	testutil.ConfigureIntegrationTest(t, patchTestConfig, "TestFinalizePatch")

	Convey("With FinalizePatch on a project and commit event generated from GetPatchedProject path",
		t, func() {
			configPatch := resetPatchSetup(t, configFilePath)
			Convey("a patched config should drive version creation", func() {
				project, err := GetPatchedProject(configPatch, patchTestConfig)
				So(err, ShouldBeNil)
				yamlBytes, err := yaml.Marshal(project)
				So(err, ShouldBeNil)
				configPatch.PatchedConfig = string(yamlBytes)
				version, err := model.FinalizePatch(configPatch, patchTestConfig)
				So(err, ShouldBeNil)
				So(version, ShouldNotBeNil)
				// ensure the relevant builds/tasks were created
				builds, err := build.Find(build.All)
				So(err, ShouldBeNil)
				So(len(builds), ShouldEqual, 1)
				So(len(builds[0].Tasks), ShouldEqual, 2)
				tasks, err := task.Find(task.All)
				So(err, ShouldBeNil)
				So(len(tasks), ShouldEqual, 2)
			})

			Convey("a patch that does not include the remote config should not "+
				"drive version creation", func() {
				patchedConfigFile := "fakeInPatchSoNotPatched"
				configPatch := resetPatchSetup(t, patchedConfigFile)
				project, err := GetPatchedProject(configPatch, patchTestConfig)
				So(err, ShouldBeNil)
				yamlBytes, err := yaml.Marshal(project)
				So(err, ShouldBeNil)
				configPatch.PatchedConfig = string(yamlBytes)
				version, err := model.FinalizePatch(configPatch, patchTestConfig)
				So(err, ShouldBeNil)
				So(version, ShouldNotBeNil)
				So(err, ShouldBeNil)
				So(version, ShouldNotBeNil)

				// ensure the relevant builds/tasks were created
				builds, err := build.Find(build.All)
				So(err, ShouldBeNil)
				So(len(builds), ShouldEqual, 1)
				So(len(builds[0].Tasks), ShouldEqual, 1)
				tasks, err := task.Find(task.All)
				So(err, ShouldBeNil)
				So(len(tasks), ShouldEqual, 1)
			})

			Reset(func() {
				db.Clear(distro.Collection)
			})
		})
}
Esempio n. 3
0
// TryMarkPatchBuildFinished attempts to mark a patch as finished if all
// the builds for the patch are finished as well
func TryMarkPatchBuildFinished(b *build.Build, finishTime time.Time) error {
	v, err := version.FindOne(version.ById(b.Version))
	if err != nil {
		return err
	}
	if v == nil {
		return fmt.Errorf("Cannot find version for build %v with version %v", b.Id, b.Version)
	}

	// ensure all builds for this patch are finished as well
	builds, err := build.Find(build.ByIds(v.BuildIds).WithFields(build.StatusKey))
	if err != nil {
		return err
	}

	patchCompleted := true
	status := evergreen.PatchSucceeded
	for _, build := range builds {
		if !build.IsFinished() {
			patchCompleted = false
		}
		if build.Status != evergreen.BuildSucceeded {
			status = evergreen.PatchFailed
		}
	}

	// nothing to do if the patch isn't completed
	if !patchCompleted {
		return nil
	}

	return patch.TryMarkFinished(v.Id, finishTime, status)
}
Esempio n. 4
0
// MarkVersionCompleted updates the status of a completed version to reflect its correct state by
// checking the status of its individual builds.
func MarkVersionCompleted(versionId string, finishTime time.Time) error {
	status := evergreen.VersionSucceeded

	// Find the statuses for all builds in the version so we can figure out the version's status
	builds, err := build.Find(
		build.ByVersion(versionId).WithFields(build.StatusKey),
	)
	if err != nil {
		return err
	}

	for _, b := range builds {
		if !b.IsFinished() {
			return nil
		}
		if b.Status != evergreen.BuildSucceeded {
			status = evergreen.VersionFailed
		}
	}
	return version.UpdateOne(
		bson.M{version.IdKey: versionId},
		bson.M{"$set": bson.M{
			version.FinishTimeKey: finishTime,
			version.StatusKey:     status,
		}},
	)
}
Esempio n. 5
0
func PopulateUIVersion(version *version.Version) (*uiVersion, error) {
	buildIds := version.BuildIds
	dbBuilds, err := build.Find(build.ByIds(buildIds))
	if err != nil {
		return nil, err
	}

	buildsMap := make(map[string]build.Build)
	for _, dbBuild := range dbBuilds {
		buildsMap[dbBuild.Id] = dbBuild
	}

	uiBuilds := make([]uiBuild, len(dbBuilds))
	for buildIdx, buildId := range buildIds {
		build := buildsMap[buildId]
		buildAsUI := uiBuild{Build: build}

		//Use the build's task cache, instead of querying for each individual task.
		uiTasks := make([]uiTask, len(build.Tasks))
		for taskIdx, task := range build.Tasks {
			uiTasks[taskIdx] = uiTask{Task: model.Task{Id: task.Id, Status: task.Status, DisplayName: task.DisplayName}}
		}
		uiTasks = sortUiTasks(uiTasks)

		buildAsUI.Tasks = uiTasks
		uiBuilds[buildIdx] = buildAsUI
	}
	return &uiVersion{Version: (*version), Builds: uiBuilds}, nil
}
Esempio n. 6
0
// Given a patch version and a list of task names, creates a new task with
// the given name for each variant, if applicable.
func AddNewTasksForPatch(p *patch.Patch, patchVersion *version.Version, project *Project,
	taskNames []string) error {
	// create new tasks for all of the added patch tasks
	var newTasks []string
	for _, taskName := range taskNames {
		if !util.SliceContains(p.Tasks, taskName) {
			newTasks = append(newTasks, taskName)
		}
	}

	// add tasks to the patch in the db
	if err := p.AddTasks(taskNames); err != nil {
		return err
	}

	// add new tasks to the build, if they exist
	if len(newTasks) > 0 {
		builds, err := build.Find(build.ByIds(patchVersion.BuildIds))
		if err != nil {
			return err
		}

		for _, b := range builds {
			if _, err = AddTasksToBuild(&b, project, patchVersion, newTasks); err != nil {
				return err
			}
		}
	}
	return nil
}
Esempio n. 7
0
// Returns a JSON response with the status of the specified version
// grouped on the build variants. The keys of the object are the build
// variant name, with each key in the nested object representing a
// particular task.
func (restapi restAPI) getVersionStatusByBuild(versionId string, w http.ResponseWriter, r *http.Request) {
	// Get all of the builds corresponding to this version
	builds, err := build.Find(
		build.ByVersion(versionId).WithFields(build.BuildVariantKey, build.TasksKey),
	)
	if err != nil {
		msg := fmt.Sprintf("Error finding status for version '%v'", versionId)
		evergreen.Logger.Logf(slogger.ERROR, "%v: %v", msg, err)
		restapi.WriteJSON(w, http.StatusInternalServerError, responseError{Message: msg})
		return
	}

	result := versionStatusByBuildContent{
		Id:     versionId,
		Builds: make(map[string]versionStatusByBuild, len(builds)),
	}

	for _, build := range builds {
		statuses := make(versionStatusByBuild, len(build.Tasks))
		for _, task := range build.Tasks {
			status := versionStatus{
				Id:        task.Id,
				Status:    task.Status,
				TimeTaken: task.TimeTaken,
			}
			statuses[task.DisplayName] = status
		}
		result.Builds[build.BuildVariant] = statuses
	}

	restapi.WriteJSON(w, http.StatusOK, result)
	return

}
Esempio n. 8
0
func (uis *UIServer) versionHistory(w http.ResponseWriter, r *http.Request) {
	projCtx := MustHaveProjectContext(r)
	data, err := getVersionHistory(projCtx.Version.Id, 5)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	user := GetUser(r)
	versions := make([]*uiVersion, 0, len(data))

	for _, version := range data {
		// Check whether the project associated with the particular version
		// is accessible to this user. If not, we exclude it from the version
		// history. This is done to hide the existence of the private project.
		if projCtx.ProjectRef.Private && user == nil {
			continue
		}

		versionAsUI := uiVersion{
			Version:   version,
			RepoOwner: projCtx.ProjectRef.Owner,
			Repo:      projCtx.ProjectRef.Repo,
		}
		versions = append(versions, &versionAsUI)

		dbBuilds, err := build.Find(build.ByIds(version.BuildIds))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		uiBuilds := make([]uiBuild, 0, len(projCtx.Version.BuildIds))
		for _, b := range dbBuilds {
			buildAsUI := uiBuild{Build: b}
			uiTasks := make([]uiTask, 0, len(b.Tasks))
			for _, task := range b.Tasks {
				uiTasks = append(uiTasks,
					uiTask{
						Task: model.Task{
							Id:          task.Id,
							Status:      task.Status,
							Activated:   task.Activated,
							DisplayName: task.DisplayName,
						},
					})
				if task.Activated {
					versionAsUI.ActiveTasks++
				}
			}
			uiTasks = sortUiTasks(uiTasks)
			buildAsUI.Tasks = uiTasks
			uiBuilds = append(uiBuilds, buildAsUI)
		}
		versionAsUI.Builds = uiBuilds
	}
	uis.WriteJSON(w, http.StatusOK, versions)
}
Esempio n. 9
0
func (uis *UIServer) versionPage(w http.ResponseWriter, r *http.Request) {
	projCtx := MustHaveProjectContext(r)
	if projCtx.Project == nil || projCtx.Version == nil {
		http.Error(w, "not found", http.StatusNotFound)
		return
	}

	// Set the config to blank to avoid writing it to the UI unnecessarily.
	projCtx.Version.Config = ""

	versionAsUI := uiVersion{
		Version:   *projCtx.Version,
		RepoOwner: projCtx.ProjectRef.Owner,
		Repo:      projCtx.ProjectRef.Repo,
	}

	if projCtx.Patch != nil {
		versionAsUI.PatchInfo = &uiPatch{Patch: *projCtx.Patch}
	}

	dbBuilds, err := build.Find(build.ByIds(projCtx.Version.BuildIds))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	uiBuilds := make([]uiBuild, 0, len(projCtx.Version.BuildIds))
	for _, build := range dbBuilds {
		buildAsUI := uiBuild{Build: build}

		uiTasks := make([]uiTask, 0, len(build.Tasks))
		for _, task := range build.Tasks {
			uiTasks = append(uiTasks,
				uiTask{Task: model.Task{Id: task.Id, Activated: task.Activated,
					Status: task.Status, DisplayName: task.DisplayName}})
			if task.Activated {
				versionAsUI.ActiveTasks++
			}
		}
		uiTasks = sortUiTasks(uiTasks)
		buildAsUI.Tasks = uiTasks
		uiBuilds = append(uiBuilds, buildAsUI)
	}
	versionAsUI.Builds = uiBuilds

	pluginContext := projCtx.ToPluginContext(uis.Settings, GetUser(r))
	pluginContent := getPluginDataAndHTML(uis, plugin.VersionPage, pluginContext)

	flashes := PopFlashes(uis.CookieStore, r, w)
	uis.WriteHTML(w, http.StatusOK, struct {
		ProjectData   projectContext
		User          *user.DBUser
		Flashes       []interface{}
		Version       *uiVersion
		PluginContent pluginData
	}{projCtx, GetUser(r), flashes, &versionAsUI, pluginContent}, "base",
		"version.html", "base_angular.html", "menu.html")
}
Esempio n. 10
0
// This is used to pull recently finished builds
func getRecentlyFinishedBuilds(notificationKey *NotificationKey) (builds []build.Build, err error) {
	if cachedProjectRecords[notificationKey.String()] == nil {
		builds, err = build.Find(build.ByFinishedAfter(lastProjectNotificationTime[notificationKey.Project], notificationKey.Project, notificationKey.NotificationRequester))
		if err != nil {
			return nil, err
		}
		cachedProjectRecords[notificationKey.String()] = builds
		return builds, err
	}
	return cachedProjectRecords[notificationKey.String()].([]build.Build), nil
}
Esempio n. 11
0
// Given a patch version and a list of variant/task pairs, creates the set of new builds that
// do not exist yet out of the set of pairs. No tasks are added for builds which already exist
// (see AddNewTasksForPatch).
func AddNewBuildsForPatch(p *patch.Patch, patchVersion *version.Version, project *Project, pairs TVPairSet) error {
	tt := NewPatchTaskIdTable(project, patchVersion, pairs)

	newBuildIds := make([]string, 0)
	newBuildStatuses := make([]version.BuildStatus, 0)

	existingBuilds, err := build.Find(build.ByVersion(patchVersion.Id).WithFields(build.BuildVariantKey, build.IdKey))
	if err != nil {
		return err
	}
	variantsProcessed := map[string]bool{}
	for _, b := range existingBuilds {
		variantsProcessed[b.BuildVariant] = true
	}

	for _, pair := range pairs {
		if _, ok := variantsProcessed[pair.Variant]; ok { // skip variant that was already processed
			continue
		}
		variantsProcessed[pair.Variant] = true
		// Extract the unique set of task names for the variant we're about to create
		taskNames := pairs.TaskNames(pair.Variant)
		if len(taskNames) == 0 {
			continue
		}
		buildId, err := CreateBuildFromVersion(project, patchVersion, tt, pair.Variant, p.Activated, taskNames)
		evergreen.Logger.Logf(slogger.INFO,
			"Creating build for version %v, buildVariant %v, activated = %v",
			patchVersion.Id, pair.Variant, p.Activated)
		if err != nil {
			return err
		}
		newBuildIds = append(newBuildIds, buildId)
		newBuildStatuses = append(newBuildStatuses,
			version.BuildStatus{
				BuildVariant: pair.Variant,
				BuildId:      buildId,
				Activated:    p.Activated,
			},
		)
	}

	return version.UpdateOne(
		bson.M{version.IdKey: patchVersion.Id},
		bson.M{
			"$push": bson.M{
				version.BuildIdsKey:      bson.M{"$each": newBuildIds},
				version.BuildVariantsKey: bson.M{"$each": newBuildStatuses},
			},
		},
	)
	return nil
}
Esempio n. 12
0
func getTimelineData(projectName, requester string, versionsToSkip, versionsPerPage int) (*timelineData, error) {
	data := &timelineData{}

	// get the total number of versions in the database (used for pagination)
	totalVersions, err := version.Count(version.ByProjectId(projectName))
	if err != nil {
		return nil, err
	}
	data.TotalVersions = totalVersions

	q := version.ByMostRecentForRequester(projectName, requester).WithoutFields(version.ConfigKey).
		Skip(versionsToSkip * versionsPerPage).Limit(versionsPerPage)

	// get the most recent versions, to display in their entirety on the page
	versionsFromDB, err := version.Find(q)
	if err != nil {
		return nil, err
	}

	// create the necessary uiVersion struct for each version
	uiVersions := make([]uiVersion, len(versionsFromDB))
	for versionIdx, version := range versionsFromDB {
		versionAsUI := uiVersion{Version: version}
		uiVersions[versionIdx] = versionAsUI

		buildIds := version.BuildIds
		dbBuilds, err := build.Find(build.ByIds(buildIds))
		if err != nil {
			evergreen.Logger.Errorf(slogger.ERROR, "Ids: %v", buildIds)
		}

		buildsMap := make(map[string]build.Build)
		for _, dbBuild := range dbBuilds {
			buildsMap[dbBuild.Id] = dbBuild
		}

		uiBuilds := make([]uiBuild, len(dbBuilds))
		for buildIdx, buildId := range buildIds {
			build := buildsMap[buildId]
			buildAsUI := uiBuild{Build: build}
			uiBuilds[buildIdx] = buildAsUI
		}
		versionAsUI.Builds = uiBuilds
		uiVersions[versionIdx] = versionAsUI
	}

	data.Versions = uiVersions
	return data, nil
}
Esempio n. 13
0
// getMakespanRatios returns a list of MakespanRatio structs that contain
// the actual and predicted makespans for a certain number of recent builds.
func getMakespanRatios(numberBuilds int) ([]restMakespanStats, error) {
	builds, err := build.Find(build.ByRecentlyFinished(numberBuilds))
	if err != nil {
		return nil, err
	}
	makespanRatios := []restMakespanStats{}
	for _, b := range builds {
		makespanRatios = append(makespanRatios, restMakespanStats{
			BuildId:           b.Id,
			PredictedMakespan: int(b.PredictedMakespan),
			ActualMakespan:    int(b.ActualMakespan),
		})
	}
	return makespanRatios, nil
}
Esempio n. 14
0
// SetVersionActivation updates the "active" state of all builds and tasks associated with a
// version to the given setting. It also updates the task cache for all builds affected.
func SetVersionActivation(versionId string, active bool, caller string) error {
	builds, err := build.Find(
		build.ByVersion(versionId).WithFields(build.IdKey),
	)
	if err != nil {
		return err
	}
	for _, b := range builds {
		err = SetBuildActivation(b.Id, active, caller)
		if err != nil {
			return err
		}
	}
	return nil
}
Esempio n. 15
0
// Helper function to fetch a group of versions and their associated builds.
// Returns the versions themselves, as well as a map of version id -> the
// builds that are a part of the version (unsorted).
func fetchVersionsAndAssociatedBuilds(project *model.Project, skip int, numVersions int) ([]version.Version, map[string][]build.Build, error) {

	// fetch the versions from the db
	versionsFromDB, err := version.Find(version.ByProjectId(project.Identifier).
		WithFields(
			version.RevisionKey,
			version.ErrorsKey,
			version.WarningsKey,
			version.IgnoredKey,
			version.MessageKey,
			version.AuthorKey,
			version.RevisionOrderNumberKey,
			version.CreateTimeKey,
		).Sort([]string{"-" + version.RevisionOrderNumberKey}).Skip(skip).Limit(numVersions))

	if err != nil {
		return nil, nil, fmt.Errorf("error fetching versions from database: %v", err)
	}

	// create a slice of the version ids (used to fetch the builds)
	versionIds := make([]string, 0, len(versionsFromDB))
	for _, v := range versionsFromDB {
		versionIds = append(versionIds, v.Id)
	}

	// fetch all of the builds (with only relevant fields)
	buildsFromDb, err := build.Find(
		build.ByVersions(versionIds).
			WithFields(build.BuildVariantKey, build.TasksKey, build.VersionKey))
	if err != nil {
		return nil, nil, fmt.Errorf("error fetching builds from database: %v", err)
	}

	// group the builds by version
	buildsByVersion := map[string][]build.Build{}
	for _, build := range buildsFromDb {
		buildsByVersion[build.Version] = append(buildsByVersion[build.Version], build)
	}

	return versionsFromDB, buildsByVersion, nil
}
Esempio n. 16
0
// Given a patch version and set of variant/task pairs, creates any tasks that don't exist yet,
// within the set of already existing builds.
func AddNewTasksForPatch(p *patch.Patch, patchVersion *version.Version, project *Project, pairs TVPairSet) error {
	builds, err := build.Find(build.ByIds(patchVersion.BuildIds).WithFields(build.IdKey, build.BuildVariantKey))
	if err != nil {
		return err
	}

	for _, b := range builds {
		// Find the set of task names that already exist for the given build
		tasksInBuild, err := task.Find(task.ByBuildId(b.Id).WithFields(task.DisplayNameKey))
		if err != nil {
			return err
		}
		// build an index to keep track of which tasks already exist
		existingTasksIndex := map[string]bool{}
		for _, t := range tasksInBuild {
			existingTasksIndex[t.DisplayName] = true
		}
		// if the patch is activated, treat the build as activated
		b.Activated = p.Activated

		// build a list of tasks that haven't been created yet for the given variant, but have
		// a record in the TVPairSet indicating that it should exist
		tasksToAdd := []string{}
		for _, taskname := range pairs.TaskNames(b.BuildVariant) {
			if _, ok := existingTasksIndex[taskname]; ok {
				continue
			}
			tasksToAdd = append(tasksToAdd, taskname)
		}
		if len(tasksToAdd) == 0 { // no tasks to add, so we do nothing.
			continue
		}
		// Add the new set of tasks to the build.
		if _, err = AddTasksToBuild(&b, project, patchVersion, tasksToAdd); err != nil {
			return err
		}
	}
	return nil
}
Esempio n. 17
0
func (uis *UIServer) versionPage(w http.ResponseWriter, r *http.Request) {
	projCtx := MustHaveProjectContext(r)
	if projCtx.Project == nil || projCtx.Version == nil {
		http.Error(w, "not found", http.StatusNotFound)
		return
	}

	// Set the config to blank to avoid writing it to the UI unnecessarily.
	projCtx.Version.Config = ""

	versionAsUI := uiVersion{
		Version:   *projCtx.Version,
		RepoOwner: projCtx.ProjectRef.Owner,
		Repo:      projCtx.ProjectRef.Repo,
	}

	dbBuilds, err := build.Find(build.ByIds(projCtx.Version.BuildIds))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	var canEditPatch bool
	currentUser := GetUser(r)
	if projCtx.Patch != nil {
		canEditPatch = uis.canEditPatch(currentUser, projCtx.Patch)
		versionAsUI.PatchInfo = &uiPatch{Patch: *projCtx.Patch}
		// diff builds for each build in the version
		baseBuilds, err := build.Find(build.ByRevision(projCtx.Version.Revision))
		if err != nil {
			http.Error(w,
				fmt.Sprintf("error loading base builds for patch: %v", err),
				http.StatusInternalServerError)
			return
		}
		baseBuildsByVariant := map[string]*build.Build{}
		for i := range baseBuilds {
			baseBuildsByVariant[baseBuilds[i].BuildVariant] = &baseBuilds[i]
		}
		// diff all patch builds with their original build
		diffs := []model.TaskStatusDiff{}
		for i := range dbBuilds {
			diff := model.StatusDiffBuilds(
				baseBuildsByVariant[dbBuilds[i].BuildVariant],
				&dbBuilds[i],
			)
			if diff.Name != "" {
				// append the tasks instead of the build for better usability
				diffs = append(diffs, diff.Tasks...)
			}
		}
		versionAsUI.PatchInfo.StatusDiffs = diffs
	}

	uiBuilds := make([]uiBuild, 0, len(projCtx.Version.BuildIds))
	for _, build := range dbBuilds {
		buildAsUI := uiBuild{Build: build}

		uiTasks := make([]uiTask, 0, len(build.Tasks))
		for _, t := range build.Tasks {
			uiTasks = append(uiTasks,
				uiTask{
					Task: task.Task{
						Id: t.Id, Activated: t.Activated, StartTime: t.StartTime, TimeTaken: t.TimeTaken,
						Status: t.Status, Details: t.StatusDetails, DisplayName: t.DisplayName,
					}})
			if t.Activated {
				versionAsUI.ActiveTasks++
			}
		}
		buildAsUI.Tasks = uiTasks
		uiBuilds = append(uiBuilds, buildAsUI)
	}
	versionAsUI.Builds = uiBuilds

	pluginContext := projCtx.ToPluginContext(uis.Settings, GetUser(r))
	pluginContent := getPluginDataAndHTML(uis, plugin.VersionPage, pluginContext)

	flashes := PopFlashes(uis.CookieStore, r, w)
	uis.WriteHTML(w, http.StatusOK, struct {
		ProjectData   projectContext
		User          *user.DBUser
		Flashes       []interface{}
		Version       *uiVersion
		PluginContent pluginData
		CanEdit       bool
	}{projCtx, currentUser, flashes, &versionAsUI, pluginContent, canEditPatch}, "base",
		"version.html", "base_angular.html", "menu.html")
}
Esempio n. 18
0
func (uis *UIServer) modifyVersion(w http.ResponseWriter, r *http.Request) {
	projCtx := MustHaveProjectContext(r)
	user := MustHaveUser(r)
	if projCtx.Project == nil || projCtx.Version == nil {
		http.Error(w, "not found", http.StatusNotFound)
		return
	}

	jsonMap := struct {
		Action   string   `json:"action"`
		Active   bool     `json:"active"`
		Abort    bool     `json:"abort"`
		Priority int64    `json:"priority"`
		TaskIds  []string `json:"task_ids"`
	}{}
	err := util.ReadJSONInto(r.Body, &jsonMap)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// determine what action needs to be taken
	switch jsonMap.Action {
	case "restart":
		if err = model.RestartVersion(projCtx.Version.Id, jsonMap.TaskIds, jsonMap.Abort, user.Id); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	case "set_active":
		if jsonMap.Abort {
			if err = model.AbortVersion(projCtx.Version.Id); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		}
		if err = model.SetVersionActivation(projCtx.Version.Id, jsonMap.Active, user.Id); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	case "set_priority":
		if jsonMap.Priority > evergreen.MaxTaskPriority {
			if !uis.isSuperUser(user) {
				http.Error(w, fmt.Sprintf("Insufficient access to set priority %v, can only set priority less than or equal to %v", jsonMap.Priority, evergreen.MaxTaskPriority),
					http.StatusBadRequest)
				return
			}
		}
		if err = model.SetVersionPriority(projCtx.Version.Id, jsonMap.Priority); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	default:
		uis.WriteJSON(w, http.StatusBadRequest, fmt.Sprintf("Unrecognized action: %v", jsonMap.Action))
		return
	}

	// After the version has been modified, re-load it from DB and send back the up-to-date view
	// to the client.
	projCtx.Version, err = version.FindOne(version.ById(projCtx.Version.Id))
	if err != nil {
		uis.LoggedError(w, r, http.StatusInternalServerError, err)
		return
	}

	versionAsUI := uiVersion{
		Version:   *projCtx.Version,
		RepoOwner: projCtx.ProjectRef.Owner,
		Repo:      projCtx.ProjectRef.Repo,
	}
	dbBuilds, err := build.Find(build.ByIds(projCtx.Version.BuildIds))
	if err != nil {
		uis.LoggedError(w, r, http.StatusInternalServerError, err)
		return
	}

	uiBuilds := make([]uiBuild, 0, len(projCtx.Version.BuildIds))
	for _, build := range dbBuilds {
		buildAsUI := uiBuild{Build: build}
		uiTasks := make([]uiTask, 0, len(build.Tasks))
		for _, t := range build.Tasks {
			uiTasks = append(uiTasks,
				uiTask{
					Task: task.Task{Id: t.Id, Activated: t.Activated,
						StartTime: t.StartTime, TimeTaken: t.TimeTaken, Status: t.Status,
						Details: t.StatusDetails, DisplayName: t.DisplayName},
				})
			if t.Activated {
				versionAsUI.ActiveTasks++
			}
		}
		buildAsUI.Tasks = uiTasks
		uiBuilds = append(uiBuilds, buildAsUI)
	}
	versionAsUI.Builds = uiBuilds
	uis.WriteJSON(w, http.StatusOK, versionAsUI)
}
Esempio n. 19
0
func TestFinalize(t *testing.T) {
	testutil.ConfigureIntegrationTest(t, patchTestConfig, "TestFinalize")

	Convey("With calling ValidateAndFinalize with a config and remote configuration "+
		"path", t, func() {
		testutil.HandleTestingErr(db.ClearCollections(
			model.ProjectRefCollection,
			patch.Collection,
			version.Collection,
			build.Collection,
			model.TasksCollection),
			t, "Error clearing test collection")

		Convey("a patched config should drive version creation", func() {
			configFilePath := "testing/mci.yml"
			// insert distros to be used
			distros := []distro.Distro{
				distro.Distro{Id: "d1"},
				distro.Distro{Id: "d2"},
			}

			for _, d := range distros {
				So(d.Insert(), ShouldBeNil)
			}

			projectRef := &model.ProjectRef{
				Identifier: patchedProject,
				RemotePath: configFilePath,
				Owner:      patchOwner,
				Repo:       patchRepo,
				Branch:     patchBranch,
			}
			err := projectRef.Insert()
			testutil.HandleTestingErr(err, t, "Couldn't insert test project ref: "+
				"%v", err)
			fileBytes, err := ioutil.ReadFile(patchFile)
			So(err, ShouldBeNil)

			// this patch adds a new task to the existing build
			configPatch := &patch.Patch{
				Id:            "52549c143122",
				Project:       patchedProject,
				BuildVariants: []string{"all"},
				Githash:       patchedRevision,
				Patches: []patch.ModulePatch{
					patch.ModulePatch{
						Githash: "revision",
						PatchSet: patch.PatchSet{
							Patch: fmt.Sprintf(string(fileBytes), configFilePath,
								configFilePath, configFilePath, configFilePath),
							Summary: []thirdparty.Summary{
								thirdparty.Summary{
									Name:      configFilePath,
									Additions: 4,
									Deletions: 80,
								},
								thirdparty.Summary{
									Name:      "random.txt",
									Additions: 6,
									Deletions: 0,
								},
							},
						},
					},
				},
			}
			err = configPatch.Insert()
			testutil.HandleTestingErr(err, t, "Couldn't insert test patch: %v", err)
			version, err := ValidateAndFinalize(configPatch, patchTestConfig)
			So(err, ShouldBeNil)
			So(version, ShouldNotBeNil)
			// ensure the relevant builds/tasks were created
			builds, err := build.Find(build.All)
			So(err, ShouldBeNil)
			So(len(builds), ShouldEqual, 1)
			So(len(builds[0].Tasks), ShouldEqual, 2)
			tasks, err := model.FindAllTasks(bson.M{},
				db.NoProjection,
				db.NoSort,
				db.NoSkip,
				db.NoLimit,
			)
			So(err, ShouldBeNil)
			So(len(tasks), ShouldEqual, 2)
		})

		Convey("a patch that does not include the remote config should not "+
			"drive version creation", func() {
			configFilePath := "testing/mci.yml"
			projectRef := &model.ProjectRef{
				Identifier: patchedProject,
				RemotePath: configFilePath,
				Owner:      patchOwner,
				Repo:       patchRepo,
				Branch:     patchBranch,
			}
			err := projectRef.Insert()
			testutil.HandleTestingErr(err, t, "Couldn't insert test project ref: "+
				"%v", err)
			patchedConfigFile := "fakeInPatchSoNotPatched"
			fileBytes, err := ioutil.ReadFile(patchFile)
			So(err, ShouldBeNil)

			// insert distros to be used
			distros := []distro.Distro{
				distro.Distro{Id: "d1"},
				distro.Distro{Id: "d2"},
			}

			for _, d := range distros {
				So(d.Insert(), ShouldBeNil)
			}

			// this patch adds a new task to the existing build
			configPatch := &patch.Patch{
				Id:            "52549c143122",
				Project:       patchedProject,
				BuildVariants: []string{"all"},
				Githash:       patchedRevision,
				Patches: []patch.ModulePatch{
					patch.ModulePatch{
						Githash: "revision",
						PatchSet: patch.PatchSet{
							Patch: fmt.Sprintf(string(fileBytes), patchedConfigFile,
								patchedConfigFile, patchedConfigFile, patchedConfigFile),
							Summary: []thirdparty.Summary{
								thirdparty.Summary{
									Name:      configFilePath,
									Additions: 4,
									Deletions: 80,
								},
								thirdparty.Summary{
									Name:      patchedProject,
									Additions: 6,
									Deletions: 0,
								},
							},
						},
					},
				},
			}
			err = configPatch.Insert()
			testutil.HandleTestingErr(err, t, "Couldn't insert test patch: %v", err)
			version, err := ValidateAndFinalize(configPatch, patchTestConfig)
			So(err, ShouldBeNil)
			So(version, ShouldNotBeNil)

			// ensure the relevant builds/tasks were created
			builds, err := build.Find(build.All)
			So(err, ShouldBeNil)
			So(len(builds), ShouldEqual, 1)
			So(len(builds[0].Tasks), ShouldEqual, 1)
			tasks, err := model.FindAllTasks(bson.M{},
				db.NoProjection,
				db.NoSort,
				db.NoSkip,
				db.NoLimit,
			)
			So(err, ShouldBeNil)
			So(len(tasks), ShouldEqual, 1)
		})

		Reset(func() {
			db.Clear(distro.Collection)
		})
	})
}
Esempio n. 20
0
// Returns a JSON response of an array with the NumRecentVersions
// most recent versions (sorted on commit order number descending).
func (restapi restAPI) getRecentVersions(w http.ResponseWriter, r *http.Request) {
	projectId := mux.Vars(r)["project_id"]

	versions, err := version.Find(version.ByMostRecentForRequester(projectId, evergreen.RepotrackerVersionRequester).Limit(10))
	if err != nil {
		msg := fmt.Sprintf("Error finding recent versions of project '%v'", projectId)
		evergreen.Logger.Logf(slogger.ERROR, "%v: %v", msg, err)
		restapi.WriteJSON(w, http.StatusInternalServerError, responseError{Message: msg})
		return
	}

	// Create a slice of version ids to find all relevant builds
	versionIds := make([]string, 0, len(versions))

	// Cache the order of versions in a map for lookup by their id
	versionIdx := make(map[string]int, len(versions))

	for i, version := range versions {
		versionIds = append(versionIds, version.Id)
		versionIdx[version.Id] = i
	}

	// Find all builds corresponding the set of version ids
	builds, err := build.Find(
		build.ByVersions(versionIds).
			WithFields(build.BuildVariantKey, build.DisplayNameKey, build.TasksKey, build.VersionKey))
	if err != nil {
		msg := fmt.Sprintf("Error finding recent versions of project '%v'", projectId)
		evergreen.Logger.Logf(slogger.ERROR, "%v: %v", msg, err)
		restapi.WriteJSON(w, http.StatusInternalServerError, responseError{Message: msg})
		return
	}

	result := recentVersionsContent{
		Project:  projectId,
		Versions: make([]versionLessInfo, 0, len(versions)),
	}

	for _, version := range versions {
		versionInfo := versionLessInfo{
			Id:       version.Id,
			Author:   version.Author,
			Revision: version.Revision,
			Message:  version.Message,
			Builds:   make(versionByBuild),
		}

		result.Versions = append(result.Versions, versionInfo)
	}

	for _, build := range builds {
		buildInfo := versionBuildInfo{
			Id:    build.Id,
			Name:  build.DisplayName,
			Tasks: make(versionByBuildByTask, len(build.Tasks)),
		}

		for _, task := range build.Tasks {
			buildInfo.Tasks[task.DisplayName] = versionStatus{
				Id:        task.Id,
				Status:    task.Status,
				TimeTaken: task.TimeTaken,
			}
		}

		versionInfo := result.Versions[versionIdx[build.Version]]
		versionInfo.Builds[build.BuildVariant] = buildInfo
	}

	restapi.WriteJSON(w, http.StatusOK, result)
	return
}
Esempio n. 21
0
// taskTimingJSON sends over the task data for a certain task of a certain build variant
func (uis *UIServer) taskTimingJSON(w http.ResponseWriter, r *http.Request) {
	projCtx := MustHaveProjectContext(r)
	beforeTaskId := r.FormValue("before")

	limit, err := getIntValue(r, "limit", 50)
	if err != nil {
		uis.LoggedError(w, r, http.StatusBadRequest, err)
		return
	}

	buildVariant := mux.Vars(r)["build_variant"]
	taskName := mux.Vars(r)["task_name"]
	request := mux.Vars(r)["request"]

	if projCtx.Project == nil {
		uis.LoggedError(w, r, http.StatusNotFound, fmt.Errorf("not found"))
		return
	}

	bv := projCtx.Project.FindBuildVariant(buildVariant)
	if bv == nil {
		uis.LoggedError(w, r, http.StatusNotFound, fmt.Errorf("build variant %v not found", buildVariant))
		return
	}
	var versionIds []string
	data := UIStats{}

	// if its all tasks find the build
	if taskName == "" || taskName == "All Tasks" {
		// TODO: switch this to be a query on the builds TaskCache
		builds, err := build.Find(build.ByProjectAndVariant(projCtx.Project.Identifier, buildVariant, request).
			WithFields(build.IdKey, build.CreateTimeKey, build.VersionKey,
				build.TimeTakenKey, build.TasksKey, build.FinishTimeKey, build.StartTimeKey).
			Sort([]string{"-" + build.RevisionOrderNumberKey}).
			Limit(limit))
		if err != nil {
			uis.LoggedError(w, r, http.StatusBadRequest, err)
			return
		}
		versionIds = make([]string, 0, len(builds))
		uiBuilds := []*UIBuild{}
		// get the versions for every single task that was returned
		for _, build := range builds {

			// create a UITask
			b := &UIBuild{
				Id:         build.Id,
				CreateTime: build.CreateTime,
				StartTime:  build.StartTime,
				FinishTime: build.FinishTime,
				Version:    build.Version,
				Status:     build.Status,
				TimeTaken:  int64(build.TimeTaken),
				Tasks:      build.Tasks,
			}
			uiBuilds = append(uiBuilds, b)
			versionIds = append(versionIds, b.Version)
		}

		data.Builds = uiBuilds

	} else {
		foundTask := false

		for _, task := range bv.Tasks {
			if task.Name == taskName {
				foundTask = true
				break
			}
		}

		if !foundTask {
			uis.LoggedError(w, r, http.StatusNotFound, fmt.Errorf("no task named '%v'", taskName))
			return
		}

		tasks, err := model.FindCompletedTasksByVariantAndName(projCtx.Project.Identifier,
			buildVariant, taskName, request, limit, beforeTaskId)
		if err != nil {
			uis.LoggedError(w, r, http.StatusNotFound, err)
			return
		}

		uiTasks := []*UITask{}
		versionIds = make([]string, 0, len(tasks))
		// get the versions for every single task that was returned
		for _, task := range tasks {
			// create a UITask
			t := &UITask{
				Id:            task.Id,
				CreateTime:    task.CreateTime,
				DispatchTime:  task.DispatchTime,
				PushTime:      task.PushTime,
				ScheduledTime: task.ScheduledTime,
				StartTime:     task.StartTime,
				FinishTime:    task.FinishTime,
				Version:       task.Version,
				Status:        task.Status,
				Host:          task.HostId,
				Distro:        task.DistroId,
			}
			uiTasks = append(uiTasks, t)
			versionIds = append(versionIds, task.Version)
		}
		data.Tasks = uiTasks
	}

	// Populate the versions field if with commits, otherwise patches field
	if request == evergreen.RepotrackerVersionRequester {
		versions, err := version.Find(version.ByIds(versionIds).
			WithFields(version.IdKey, version.CreateTimeKey, version.MessageKey,
				version.AuthorKey, version.RevisionKey, version.RevisionOrderNumberKey).
			Sort([]string{"-" + version.RevisionOrderNumberKey}))
		if err != nil {
			uis.LoggedError(w, r, http.StatusBadRequest, err)
			return
		}
		data.Versions = versions
	} else {
		// patches
		patches, err := patch.Find(patch.ByVersions(versionIds).
			WithFields(patch.IdKey, patch.CreateTimeKey, patch.DescriptionKey, patch.AuthorKey))
		if err != nil {
			uis.LoggedError(w, r, http.StatusBadRequest, err)
			return
		}
		data.Patches = patches
	}

	uis.WriteJSON(w, http.StatusOK, data)
}