Ejemplo n.º 1
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)
			})
		})
}
Ejemplo n.º 2
0
func (as *APIServer) existingPatchRequest(w http.ResponseWriter, r *http.Request) {
	p, err := getPatchFromRequest(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	if !getGlobalLock(r.RemoteAddr, p.Id.String()) {
		as.LoggedError(w, r, http.StatusInternalServerError, ErrLockTimeout)
		return
	}
	defer releaseGlobalLock(r.RemoteAddr, p.Id.String())

	// dispatch to handlers based on specified action
	switch r.FormValue("action") {
	case "update":
		name := r.FormValue("desc")
		err := p.SetDescription(name)
		if err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
		as.WriteJSON(w, http.StatusOK, "patch updated")
	case "finalize":
		if p.Activated == true {
			http.Error(w, "patch is already finalized", http.StatusBadRequest)
			return
		}
		patchedProject, err := validator.GetPatchedProject(p, &as.Settings)
		if err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
		projectYamlBytes, err := yaml.Marshal(patchedProject)
		if err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("error marshalling patched config: %v", err))
			return
		}
		p.PatchedConfig = string(projectYamlBytes)
		_, err = model.FinalizePatch(p, &as.Settings)
		if err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}

		as.WriteJSON(w, http.StatusOK, "patch finalized")
	case "cancel":
		err = model.CancelPatch(p)
		if err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
		as.WriteJSON(w, http.StatusOK, "patch deleted")
	default:
		http.Error(w, fmt.Sprintf("Unrecognized action: %v", r.FormValue("action")), http.StatusBadRequest)
	}
}
Ejemplo n.º 3
0
func (uis *UIServer) schedulePatch(w http.ResponseWriter, r *http.Request) {
	projCtx := MustHaveProjectContext(r)

	if projCtx.Patch == nil {
		http.Error(w, "patch not found", http.StatusNotFound)
		return
	}

	// grab patch again, as the diff  was excluded
	var err error
	projCtx.Patch, err = patch.FindOne(patch.ById(projCtx.Patch.Id))
	if err != nil {
		http.Error(w, fmt.Sprintf("error loading patch: %v", err), http.StatusInternalServerError)
		return
	}

	patchUpdateReq := struct {
		Variants    []string `json:"variants"`
		Tasks       []string `json:"tasks"`
		Description string   `json:"description"`
	}{}

	err = util.ReadJSONInto(r.Body, &patchUpdateReq)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if projCtx.Patch.Version != "" {
		// This patch has already been finalized, just add the new builds and tasks
		if projCtx.Version == nil {
			uis.LoggedError(w, r, http.StatusInternalServerError,
				fmt.Errorf("Couldn't find patch for id %v", projCtx.Patch.Version))
			return
		}

		// First add new tasks to existing builds, if necessary
		if len(patchUpdateReq.Tasks) > 0 {
			err = model.AddNewTasksForPatch(projCtx.Patch, projCtx.Version, patchUpdateReq.Tasks)
			if err != nil {
				uis.LoggedError(w, r, http.StatusInternalServerError,
					fmt.Errorf("Error creating new tasks: `%v` for version `%v`", err, projCtx.Version.Id))
				return
			}
		}

		if len(patchUpdateReq.Variants) > 0 {
			_, err := model.AddNewBuildsForPatch(projCtx.Patch, projCtx.Version, patchUpdateReq.Variants)
			if err != nil {
				uis.LoggedError(w, r, http.StatusInternalServerError,
					fmt.Errorf("Error creating new builds: `%v` for version `%v`", err, projCtx.Version.Id))
				return
			}
		}

		PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Builds and tasks successfully added to patch."))
		uis.WriteJSON(w, http.StatusOK, struct {
			VersionId string `json:"version"`
		}{projCtx.Version.Id})
	} else {
		err = projCtx.Patch.SetVariantsAndTasks(patchUpdateReq.Variants, patchUpdateReq.Tasks)
		if err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError,
				fmt.Errorf("Error setting patch variants and tasks: %v", err))
			return
		}

		if err = projCtx.Patch.SetDescription(patchUpdateReq.Description); err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError,
				fmt.Errorf("Error setting description: %v", err))
			return
		}

		// Unmarshal the project config and set it in the project context
		project := model.Project{}
		if err := yaml.Unmarshal([]byte(projCtx.Patch.PatchedConfig), &project); err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error unmarshaling project config: %v", err))
		}
		projCtx.Project = &project

		ver, err := model.FinalizePatch(projCtx.Patch, &uis.Settings)
		if err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finalizing patch: %v", err))
			return
		}
		PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Patch builds are scheduled."))
		uis.WriteJSON(w, http.StatusOK, struct {
			VersionId string `json:"version"`
		}{ver.Id})
	}
}
Ejemplo n.º 4
0
func (as *APIServer) existingPatchRequest(w http.ResponseWriter, r *http.Request) {
	user := MustHaveUser(r)

	p, err := getPatchFromRequest(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotFound)
		return
	}

	if !getGlobalLock(r.RemoteAddr, p.Id.String()) {
		as.LoggedError(w, r, http.StatusInternalServerError, ErrLockTimeout)
		return
	}
	defer releaseGlobalLock(r.RemoteAddr, p.Id.String())

	var action, desc string
	if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
		action = r.FormValue("action")
	} else {
		data := struct {
			PatchId     string `json:"patch_id"`
			Action      string `json:"action"`
			Description string `json:"description"`
		}{}
		if err := util.ReadJSONInto(r.Body, &data); err != nil {
			as.LoggedError(w, r, http.StatusBadRequest, err)
			return
		}
		action, desc = data.Action, data.Description
	}

	// dispatch to handlers based on specified action
	switch action {
	case "update":
		err := p.SetDescription(desc)
		if err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
		as.WriteJSON(w, http.StatusOK, "patch updated")
	case "finalize":
		if p.Activated == true {
			http.Error(w, "patch is already finalized", http.StatusBadRequest)
			return
		}
		patchedProject, err := validator.GetPatchedProject(p, &as.Settings)
		if err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
		projectYamlBytes, err := yaml.Marshal(patchedProject)
		if err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("error marshalling patched config: %v", err))
			return
		}
		p.PatchedConfig = string(projectYamlBytes)
		_, err = model.FinalizePatch(p, &as.Settings)
		if err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}

		as.WriteJSON(w, http.StatusOK, "patch finalized")
	case "cancel":
		err = model.CancelPatch(p, user.Id)
		if err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
		as.WriteJSON(w, http.StatusOK, "patch deleted")
	default:
		http.Error(w, fmt.Sprintf("Unrecognized action: %v", action), http.StatusBadRequest)
	}
}
Ejemplo n.º 5
0
func (as *APIServer) submitPatch(w http.ResponseWriter, r *http.Request) {
	user := MustHaveUser(r)
	var apiRequest PatchAPIRequest
	var projId, description string
	var finalize bool
	if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
		patchContent := r.FormValue("patch")
		if patchContent == "" {
			as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Error: Patch must not be empty"))
			return
		}
		apiRequest = PatchAPIRequest{
			ProjectFileName: r.FormValue("project"),
			ModuleName:      r.FormValue("module"),
			Githash:         r.FormValue("githash"),
			PatchContent:    r.FormValue("patch"),
			BuildVariants:   strings.Split(r.FormValue("buildvariants"), ","),
		}
		projId = r.FormValue("project")
		description = r.FormValue("desc")
		finalize = strings.ToLower(r.FormValue("finalize")) == "true"
	} else {
		data := struct {
			Description string   `json:"desc"`
			Project     string   `json:"project"`
			Patch       string   `json:"patch"`
			Githash     string   `json:"githash"`
			Variants    string   `json:"buildvariants"`
			Tasks       []string `json:"tasks"`
			Finalize    bool     `json:"finalize"`
		}{}
		if err := util.ReadJSONInto(r.Body, &data); err != nil {
			as.LoggedError(w, r, http.StatusBadRequest, err)
			return
		}
		if len(data.Patch) > patch.SizeLimit {
			as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Patch is too large."))
		}
		if len(data.Patch) == 0 {
			as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Error: Patch must not be empty"))
			return
		}
		description = data.Description
		projId = data.Project
		finalize = data.Finalize

		apiRequest = PatchAPIRequest{
			ProjectFileName: data.Project,
			ModuleName:      r.FormValue("module"),
			Githash:         data.Githash,
			PatchContent:    data.Patch,
			BuildVariants:   strings.Split(data.Variants, ","),
			Tasks:           data.Tasks,
		}
	}

	projectRef, err := model.FindOneProjectRef(projId)
	if err != nil {
		message := fmt.Errorf("Error locating project ref '%v': %v",
			projId, err)
		as.LoggedError(w, r, http.StatusInternalServerError, message)
		return
	}
	project, err := model.FindProject("", projectRef)
	if err != nil {
		message := fmt.Errorf("Error locating project '%v' from '%v': %v",
			projId, as.Settings.ConfigDir, err)
		as.LoggedError(w, r, http.StatusInternalServerError, message)
		return
	}

	if project == nil {
		as.LoggedError(w, r, http.StatusNotFound, fmt.Errorf("project %v not found", projId))
		return
	}

	patchMetadata, err := apiRequest.Validate(as.Settings.Credentials["github"])
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Invalid patch: %v", err))
		return
	}

	if patchMetadata == nil {
		as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("patch metadata is empty"))
		return
	}

	if apiRequest.ModuleName != "" {
		as.WriteJSON(w, http.StatusBadRequest,
			"module not allowed when creating new patches (must be added in a subsequent request)")
		return
	}
	patchProjectRef, err := model.FindOneProjectRef(patchMetadata.Project.Identifier)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Invalid projectRef: %v", err))
		return
	}
	if patchProjectRef == nil {
		as.LoggedError(w, r, http.StatusNotFound, fmt.Errorf("Empty patch project Ref"))
		return
	}

	commitInfo, err := thirdparty.GetCommitEvent(as.Settings.Credentials["github"],
		patchProjectRef.Owner,
		patchProjectRef.Repo,
		apiRequest.Githash)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, err)
		return
	}
	if commitInfo == nil {
		as.WriteJSON(w, http.StatusBadRequest, "That commit doesn't seem to exist.")
		return
	}

	createTime := time.Now()

	// create a new object ID to use as reference for the patch data
	patchFileId := bson.NewObjectId().Hex()

	patchDoc := &patch.Patch{
		Id:            bson.NewObjectId(),
		Description:   description,
		Author:        user.Id,
		Project:       apiRequest.ProjectFileName,
		Githash:       apiRequest.Githash,
		CreateTime:    createTime,
		Status:        evergreen.PatchCreated,
		BuildVariants: apiRequest.BuildVariants,
		Tasks:         apiRequest.Tasks,
		Patches: []patch.ModulePatch{
			patch.ModulePatch{
				ModuleName: "",
				Githash:    apiRequest.Githash,
				PatchSet: patch.PatchSet{
					PatchFileId: patchFileId,
					Summary:     patchMetadata.Summaries,
				},
			},
		},
	}

	// write the patch content into a GridFS file under a new ObjectId.
	err = db.WriteGridFile(patch.GridFSPrefix, patchFileId, strings.NewReader(apiRequest.PatchContent))
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("failed to write patch file to db: %v", err))
		return
	}

	// Get and validate patched config and add it to the patch document
	patchedProject, err := validator.GetPatchedProject(patchDoc, &as.Settings)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("invalid patched config: %v", err))
		return
	}

	projectYamlBytes, err := yaml.Marshal(patchedProject)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("error marshalling patched config: %v", err))
		return
	}

	// set the patch number based on patch author
	patchDoc.PatchNumber, err = user.IncPatchNumber()
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("error computing patch num %v", err))
		return
	}
	patchDoc.PatchedConfig = string(projectYamlBytes)
	patchDoc.ClearPatchData()

	//expand tasks and build variants and include dependencies
	buildVariants := patchDoc.BuildVariants
	if len(patchDoc.BuildVariants) == 1 && patchDoc.BuildVariants[0] == "all" {
		buildVariants = make([]string, 0)
		for _, buildVariant := range patchedProject.BuildVariants {
			if buildVariant.Disabled {
				continue
			}
			buildVariants = append(buildVariants, buildVariant.Name)
		}
	}
	tasks := patchDoc.Tasks
	if len(patchDoc.Tasks) == 1 && patchDoc.Tasks[0] == "all" {
		tasks = make([]string, 0)
		for _, t := range patchedProject.Tasks {
			tasks = append(tasks, t.Name)
		}
	}
	// update variant and tasks to include dependencies
	patchDoc.BuildVariants, patchDoc.Tasks = model.IncludePatchDependencies(
		project, buildVariants, tasks)

	if err = patchDoc.Insert(); err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("error inserting patch: %v", err))
		return
	}

	if finalize {
		if _, err = model.FinalizePatch(patchDoc, &as.Settings); err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
	}

	as.WriteJSON(w, http.StatusCreated, PatchAPIResponse{Patch: patchDoc})
}
Ejemplo n.º 6
0
func (uis *UIServer) schedulePatch(w http.ResponseWriter, r *http.Request) {
	projCtx := MustHaveProjectContext(r)
	if projCtx.Patch == nil {
		http.Error(w, "patch not found", http.StatusNotFound)
		return
	}
	curUser := GetUser(r)
	if !uis.canEditPatch(curUser, projCtx.Patch) {
		http.Error(w, "Not authorized to schedule patch", http.StatusUnauthorized)
		return
	}
	// grab patch again, as the diff  was excluded
	var err error
	projCtx.Patch, err = patch.FindOne(patch.ById(projCtx.Patch.Id))
	if err != nil {
		http.Error(w, fmt.Sprintf("error loading patch: %v", err), http.StatusInternalServerError)
		return
	}

	// Unmarshal the project config and set it in the project context
	project := &model.Project{}
	if err := yaml.Unmarshal([]byte(projCtx.Patch.PatchedConfig), project); err != nil {
		uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error unmarshaling project config: %v", err))
	}
	projCtx.Project = project

	patchUpdateReq := patchVariantsTasksRequest{}

	err = util.ReadJSONInto(r.Body, &patchUpdateReq)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var pairs []model.TVPair
	if len(patchUpdateReq.VariantsTasks) > 0 {
		pairs = model.VariantTasksToTVPairs(patchUpdateReq.VariantsTasks)
	} else {
		for _, v := range patchUpdateReq.Variants {
			for _, t := range patchUpdateReq.Tasks {
				if project.FindTaskForVariant(t, v) != nil {
					pairs = append(pairs, model.TVPair{v, t})
				}
			}
		}
	}

	pairs = model.IncludePatchDependencies(projCtx.Project, pairs)

	if err = model.ValidateTVPairs(projCtx.Project, pairs); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// update the description for both reconfigured and new patches
	if err = projCtx.Patch.SetDescription(patchUpdateReq.Description); err != nil {
		uis.LoggedError(w, r, http.StatusInternalServerError,
			fmt.Errorf("Error setting description: %v", err))
		return
	}

	// update the description for both reconfigured and new patches
	if err = projCtx.Patch.SetVariantsTasks(model.TVPairsToVariantTasks(pairs)); err != nil {
		uis.LoggedError(w, r, http.StatusInternalServerError,
			fmt.Errorf("Error setting description: %v", err))
		return
	}

	if projCtx.Patch.Version != "" {
		projCtx.Patch.Activated = true
		// This patch has already been finalized, just add the new builds and tasks
		if projCtx.Version == nil {
			uis.LoggedError(w, r, http.StatusInternalServerError,
				fmt.Errorf("Couldn't find patch for id %v", projCtx.Patch.Version))
			return
		}

		// First add new tasks to existing builds, if necessary
		err = model.AddNewTasksForPatch(projCtx.Patch, projCtx.Version, projCtx.Project, pairs)
		if err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError,
				fmt.Errorf("Error creating new tasks: `%v` for version `%v`", err, projCtx.Version.Id))
			return
		}

		err := model.AddNewBuildsForPatch(projCtx.Patch, projCtx.Version, projCtx.Project, pairs)
		if err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError,
				fmt.Errorf("Error creating new builds: `%v` for version `%v`", err, projCtx.Version.Id))
			return
		}

		PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Builds and tasks successfully added to patch."))
		uis.WriteJSON(w, http.StatusOK, struct {
			VersionId string `json:"version"`
		}{projCtx.Version.Id})
	} else {
		projCtx.Patch.Activated = true
		err = projCtx.Patch.SetVariantsTasks(model.TVPairsToVariantTasks(pairs))
		if err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError,
				fmt.Errorf("Error setting patch variants and tasks: %v", err))
			return
		}

		ver, err := model.FinalizePatch(projCtx.Patch, &uis.Settings)
		if err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finalizing patch: %v", err))
			return
		}
		PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Patch builds are scheduled."))
		uis.WriteJSON(w, http.StatusOK, struct {
			VersionId string `json:"version"`
		}{ver.Id})
	}
}
Ejemplo n.º 7
0
func ValidateAndFinalize(p *patch.Patch, settings *evergreen.Settings) (*version.Version, error) {
	if p.Version != "" {
		return nil, fmt.Errorf("Patch %v already finalized", p.Version)
	}
	projectRef, err := model.FindOneProjectRef(p.Project)
	if err != nil {
		return nil, err
	}

	gitCommit, err := thirdparty.GetCommitEvent(
		settings.Credentials["github"],
		projectRef.Owner, projectRef.Repo, p.Githash,
	)
	if err != nil {
		return nil, fmt.Errorf("Couldn't fetch commit information: %v", err)
	}
	if gitCommit == nil {
		return nil, fmt.Errorf("Couldn't fetch commit information: git commit" +
			" doesn't exist?")
	}

	// get the remote file at the requested revision
	projectFileURL := thirdparty.GetGithubFileURL(
		projectRef.Owner,
		projectRef.Repo,
		projectRef.RemotePath,
		p.Githash,
	)

	githubFile, err := thirdparty.GetGithubFile(
		settings.Credentials["github"],
		projectFileURL,
	)
	if err != nil {
		return nil, fmt.Errorf("Could not get github file at %v: %v", projectFileURL, err)
	}

	projectFileBytes, err := base64.StdEncoding.DecodeString(githubFile.Content)
	if err != nil {
		return nil, fmt.Errorf("Could not decode github file at %v: %v", projectFileURL, err)
	}

	project := &model.Project{}

	if err = model.LoadProjectInto(projectFileBytes, projectRef.Identifier, project); err != nil {
		return nil, err
	}

	// apply remote configuration patch if needed
	if p.ConfigChanged(projectRef.RemotePath) {
		project, err = model.MakePatchedConfig(p, projectRef.RemotePath, string(projectFileBytes))
		if err != nil {
			return nil, fmt.Errorf("Could not patch remote configuration file: %v", err)
		}

		// overwrite project fields with the project ref to disallow tracking a
		// different project or doing other crazy things via config patches
		if err = angier.TransferByFieldNames(projectRef, project); err != nil {
			return nil, fmt.Errorf("Could not merge project Ref ref into project: %v", err)
		}
		errs := CheckProjectSyntax(project)
		if len(errs) != 0 {
			var message string
			for _, err := range errs {
				message += fmt.Sprintf("\n\t=> %v", err)
			}
			return nil, fmt.Errorf(message)
		}
	} else {
		// overwrite project fields with the project ref to disallow tracking a
		// different project or doing other crazy things via config patches
		if err = angier.TransferByFieldNames(projectRef, project); err != nil {
			return nil, fmt.Errorf("Could not merge project Ref ref into project: %v", err)
		}
	}
	return model.FinalizePatch(p, gitCommit, settings, project)
}
Ejemplo n.º 8
0
// submitPatch creates the Patch document, adds the patched project config to it,
// and saves the patches to GridFS to be retrieved
func (as *APIServer) submitPatch(w http.ResponseWriter, r *http.Request) {
	dbUser := MustHaveUser(r)
	var apiRequest PatchAPIRequest
	var finalize bool
	if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
		patchContent := r.FormValue("patch")
		if patchContent == "" {
			as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Error: Patch must not be empty"))
			return
		}
		apiRequest = PatchAPIRequest{
			ProjectId:     r.FormValue("project"),
			ModuleName:    r.FormValue("module"),
			Githash:       r.FormValue("githash"),
			PatchContent:  r.FormValue("patch"),
			BuildVariants: strings.Split(r.FormValue("buildvariants"), ","),
			Description:   r.FormValue("desc"),
		}
		finalize = strings.ToLower(r.FormValue("finalize")) == "true"
	} else {
		data := struct {
			Description string   `json:"desc"`
			Project     string   `json:"project"`
			Patch       string   `json:"patch"`
			Githash     string   `json:"githash"`
			Variants    string   `json:"buildvariants"`
			Tasks       []string `json:"tasks"`
			Finalize    bool     `json:"finalize"`
		}{}
		if err := util.ReadJSONInto(r.Body, &data); err != nil {
			as.LoggedError(w, r, http.StatusBadRequest, err)
			return
		}
		if len(data.Patch) > patch.SizeLimit {
			as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Patch is too large."))
		}
		finalize = data.Finalize

		apiRequest = PatchAPIRequest{
			ProjectId:     data.Project,
			ModuleName:    r.FormValue("module"),
			Githash:       data.Githash,
			PatchContent:  data.Patch,
			BuildVariants: strings.Split(data.Variants, ","),
			Tasks:         data.Tasks,
			Description:   data.Description,
		}
	}

	project, patchDoc, err := apiRequest.CreatePatch(
		finalize, as.Settings.Credentials["github"], dbUser, &as.Settings)
	if err != nil {
		as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Invalid patch: %v", err))
		return
	}

	//expand tasks and build variants and include dependencies
	if len(patchDoc.BuildVariants) == 1 && patchDoc.BuildVariants[0] == "all" {
		patchDoc.BuildVariants = []string{}
		for _, buildVariant := range project.BuildVariants {
			if buildVariant.Disabled {
				continue
			}
			patchDoc.BuildVariants = append(patchDoc.BuildVariants, buildVariant.Name)
		}
	}

	if len(patchDoc.Tasks) == 1 && patchDoc.Tasks[0] == "all" {
		patchDoc.Tasks = []string{}
		for _, t := range project.Tasks {
			if t.Patchable != nil && !(*t.Patchable) {
				continue
			}
			patchDoc.Tasks = append(patchDoc.Tasks, t.Name)
		}
	}

	var pairs []model.TVPair
	for _, v := range patchDoc.BuildVariants {
		for _, t := range patchDoc.Tasks {
			if project.FindTaskForVariant(t, v) != nil {
				pairs = append(pairs, model.TVPair{v, t})
			}
		}
	}

	// update variant and tasks to include dependencies
	pairs = model.IncludePatchDependencies(project, pairs)

	patchDoc.SyncVariantsTasks(model.TVPairsToVariantTasks(pairs))

	if err = patchDoc.Insert(); err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("error inserting patch: %v", err))
		return
	}

	if finalize {
		if _, err = model.FinalizePatch(patchDoc, &as.Settings); err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
	}

	as.WriteJSON(w, http.StatusCreated, PatchAPIResponse{Patch: patchDoc})
}
Ejemplo n.º 9
0
func (as *APIServer) submitPatch(w http.ResponseWriter, r *http.Request) {
	user := MustHaveUser(r)
	apiRequest := PatchAPIRequest{
		ProjectFileName: r.FormValue("project"),
		ModuleName:      r.FormValue("module"),
		Githash:         r.FormValue("githash"),
		PatchContent:    r.FormValue("patch"),
		BuildVariants:   strings.Split(r.FormValue("buildvariants"), ","),
	}

	description := r.FormValue("desc")
	projId := r.FormValue("project")
	projectRef, err := model.FindOneProjectRef(projId)
	if err != nil {
		message := fmt.Errorf("Error locating project ref '%v': %v",
			projId, err)
		as.LoggedError(w, r, http.StatusInternalServerError, message)
		return
	}
	project, err := model.FindProject("", projectRef)
	if err != nil {
		message := fmt.Errorf("Error locating project '%v' from '%v': %v",
			projId, as.Settings.ConfigDir, err)
		as.LoggedError(w, r, http.StatusInternalServerError, message)
		return
	}

	if project == nil {
		as.LoggedError(w, r, http.StatusNotFound, fmt.Errorf("project %v not found", projId))
		return
	}

	patchMetadata, err := apiRequest.Validate(as.Settings.Credentials[projectRef.RepoKind])
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Invalid patch: %v", err))
		return
	}

	if patchMetadata == nil {
		as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("patch metadata is empty"))
		return
	}

	if apiRequest.ModuleName != "" {
		as.WriteJSON(w, http.StatusBadRequest,
			"module not allowed when creating new patches (must be added in a subsequent request)")
		return
	}
	patchProjectRef, err := model.FindOneProjectRef(patchMetadata.Project.Identifier)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Invalid projectRef: %v", err))
		return
	}
	if patchProjectRef == nil {
		as.LoggedError(w, r, http.StatusNotFound, fmt.Errorf("Empty patch project Ref"))
		return
	}

	commitInfo, err := thirdparty.GetCommitEvent(as.Settings.Credentials[projectRef.RepoKind],
		patchProjectRef.Owner,
		patchProjectRef.Repo,
		apiRequest.Githash)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, err)
		return
	}
	if commitInfo == nil {
		as.WriteJSON(w, http.StatusBadRequest, "That commit doesn't seem to exist.")
		return
	}

	createTime := time.Now()
	patchDoc := &patch.Patch{
		Id:            bson.NewObjectId(),
		Description:   description,
		Author:        user.Id,
		Project:       apiRequest.ProjectFileName,
		Githash:       apiRequest.Githash,
		CreateTime:    createTime,
		Status:        evergreen.PatchCreated,
		BuildVariants: apiRequest.BuildVariants,
		Tasks:         nil,
		Patches: []patch.ModulePatch{
			patch.ModulePatch{
				ModuleName: "",
				Githash:    apiRequest.Githash,
				PatchSet: patch.PatchSet{
					Patch:   apiRequest.PatchContent,
					Summary: patchMetadata.Summaries,
				},
			},
		},
	}

	// set the patch number based on patch author
	patchDoc.PatchNumber, err = user.IncPatchNumber()
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("error computing patch num %v", err))
		return
	}

	// Get and validate patched config and add it to the patch document
	patchedProject, err := validator.GetPatchedProject(patchDoc, &as.Settings)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("invalid patched config: %v", err))
		return
	}
	projectYamlBytes, err := yaml.Marshal(patchedProject)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("error marshalling patched config: %v", err))
		return
	}
	patchDoc.PatchedConfig = string(projectYamlBytes)

	if err = patchDoc.Insert(); err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("error inserting patch: %v", err))
		return
	}

	if strings.ToLower(r.FormValue("finalize")) == "true" {
		if _, err = model.FinalizePatch(patchDoc, &as.Settings); err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}

	}

	as.WriteJSON(w, http.StatusCreated, PatchAPIResponse{Patch: patchDoc})
}
Ejemplo n.º 10
0
func (uis *UIServer) schedulePatch(w http.ResponseWriter, r *http.Request) {
	projCtx := MustHaveProjectContext(r)
	if projCtx.Patch == nil {
		http.Error(w, "patch not found", http.StatusNotFound)
		return
	}

	// grab patch again, as the diff  was excluded
	var err error
	projCtx.Patch, err = patch.FindOne(patch.ById(projCtx.Patch.Id))
	if err != nil {
		http.Error(w, fmt.Sprintf("error loading patch: %v", err), http.StatusInternalServerError)
		return
	}

	// Unmarshal the project config and set it in the project context
	project := &model.Project{}
	if err := yaml.Unmarshal([]byte(projCtx.Patch.PatchedConfig), project); err != nil {
		uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error unmarshaling project config: %v", err))
	}
	projCtx.Project = project

	patchUpdateReq := struct {
		Variants    []string `json:"variants"`
		Tasks       []string `json:"tasks"`
		Description string   `json:"description"`
	}{}

	err = util.ReadJSONInto(r.Body, &patchUpdateReq)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Add all dependencies to patchUpdateReq.Tasks and add their variants to patchUpdateReq.Variants

	// Construct a set of variants to include in patchUpdateReq.Variants
	updateReqVariants := make(map[string]bool)
	for _, variant := range patchUpdateReq.Variants {
		updateReqVariants[variant] = true
	}

	// Construct a set of tasks to include in patchUpdateReq.Tasks
	// Add all dependencies, and add their variants to updateReqVariants
	updateReqTasks := make(map[string]bool)
	for _, v := range patchUpdateReq.Variants {
		for _, t := range projCtx.Project.FindTasksForVariant(v) {
			for _, task := range patchUpdateReq.Tasks {
				if t == task {
					deps, variants, err := getDeps(task, v, projCtx.Project)
					if err != nil {
						if err == TaskNotPatchableError {
							continue
						} else {
							uis.LoggedError(w, r, http.StatusInternalServerError,
								fmt.Errorf("Error getting dependencies for task: %v", err))
							return
						}
					}
					updateReqTasks[task] = true
					for _, dep := range deps {
						updateReqTasks[dep] = true
					}
					for _, variant := range variants {
						updateReqVariants[variant] = true
					}
				}
			}
		}
	}

	// Reset patchUpdateReq.Tasks and patchUpdateReq.Variants
	patchUpdateReq.Tasks = make([]string, 0, len(updateReqTasks))
	for task := range updateReqTasks {
		patchUpdateReq.Tasks = append(patchUpdateReq.Tasks, task)
	}
	patchUpdateReq.Variants = make([]string, 0, len(updateReqVariants))
	for variant := range updateReqVariants {
		patchUpdateReq.Variants = append(patchUpdateReq.Variants, variant)
	}

	if projCtx.Patch.Version != "" {
		// This patch has already been finalized, just add the new builds and tasks
		if projCtx.Version == nil {
			uis.LoggedError(w, r, http.StatusInternalServerError,
				fmt.Errorf("Couldn't find patch for id %v", projCtx.Patch.Version))
			return
		}
		// First add new tasks to existing builds, if necessary
		if len(patchUpdateReq.Tasks) > 0 {
			err = model.AddNewTasksForPatch(projCtx.Patch, projCtx.Version, projCtx.Project, patchUpdateReq.Tasks)
			if err != nil {
				uis.LoggedError(w, r, http.StatusInternalServerError,
					fmt.Errorf("Error creating new tasks: `%v` for version `%v`", err, projCtx.Version.Id))
				return
			}
		}

		if len(patchUpdateReq.Variants) > 0 {
			_, err := model.AddNewBuildsForPatch(projCtx.Patch, projCtx.Version, projCtx.Project, patchUpdateReq.Variants)
			if err != nil {
				uis.LoggedError(w, r, http.StatusInternalServerError,
					fmt.Errorf("Error creating new builds: `%v` for version `%v`", err, projCtx.Version.Id))
				return
			}
		}

		PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Builds and tasks successfully added to patch."))
		uis.WriteJSON(w, http.StatusOK, struct {
			VersionId string `json:"version"`
		}{projCtx.Version.Id})
	} else {
		err = projCtx.Patch.SetVariantsAndTasks(patchUpdateReq.Variants, patchUpdateReq.Tasks)
		if err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError,
				fmt.Errorf("Error setting patch variants and tasks: %v", err))
			return
		}

		if err = projCtx.Patch.SetDescription(patchUpdateReq.Description); err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError,
				fmt.Errorf("Error setting description: %v", err))
			return
		}

		ver, err := model.FinalizePatch(projCtx.Patch, &uis.Settings)
		if err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finalizing patch: %v", err))
			return
		}
		PushFlash(uis.CookieStore, r, w, NewSuccessFlash("Patch builds are scheduled."))
		uis.WriteJSON(w, http.StatusOK, struct {
			VersionId string `json:"version"`
		}{ver.Id})
	}
}