Example #1
0
// GetRemoteConfig fetches the contents of a remote github repository's
// configuration data as at a given revision
func (gRepoPoller *GithubRepositoryPoller) GetChangedFiles(commitRevision string) ([]string, error) {
	// get the entire commit, then pull the files from it
	projectRef := gRepoPoller.ProjectRef
	commit, err := thirdparty.GetCommitEvent(
		gRepoPoller.OauthToken,
		projectRef.Owner,
		projectRef.Repo,
		commitRevision,
	)
	if err != nil {
		return nil, fmt.Errorf("error loading commit '%v': %v", commitRevision, err)
	}
	files := []string{}
	for _, f := range commit.Files {
		files = append(files, f.FileName)
	}
	return files, nil
}
Example #2
0
// Finalizes a patch:
// Patches a remote project's configuration file if needed.
// Creates a version for this patch and links it.
// Creates builds based on the version.
func FinalizePatch(p *patch.Patch, settings *evergreen.Settings) (*version.Version, error) {
	// unmarshal the project YAML for storage
	project := &Project{}
	err := yaml.Unmarshal([]byte(p.PatchedConfig), project)
	if err != nil {
		return nil, fmt.Errorf(
			"Error marshalling patched project config from repository revision ā€œ%vā€: %v",
			p.Githash, err)
	}

	projectRef, err := 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?")
	}

	patchVersion := &version.Version{
		Id:            p.Id.Hex(),
		CreateTime:    time.Now(),
		Identifier:    p.Project,
		Revision:      p.Githash,
		Author:        p.Author,
		Message:       p.Description,
		BuildIds:      []string{},
		BuildVariants: []version.BuildStatus{},
		Config:        string(p.PatchedConfig),
		Status:        evergreen.PatchCreated,
		Requester:     evergreen.PatchVersionRequester,
		Branch:        project.Branch,
	}

	var pairs []TVPair
	if len(p.VariantsTasks) > 0 {
		pairs = VariantTasksToTVPairs(p.VariantsTasks)
	} else {
		// handle case where the patch is being finalized but only has the old schema tasks/variants
		// instead of the new one.
		for _, v := range p.BuildVariants {
			for _, t := range p.Tasks {
				if project.FindTaskForVariant(t, v) != nil {
					pairs = append(pairs, TVPair{v, t})
				}
			}
		}
		p.VariantsTasks = TVPairsToVariantTasks(pairs)
	}

	tt := NewPatchTaskIdTable(project, patchVersion, pairs)
	variantsProcessed := map[string]bool{}
	for _, vt := range p.VariantsTasks {
		if _, ok := variantsProcessed[vt.Variant]; ok {
			continue
		}
		buildId, err := CreateBuildFromVersion(project, patchVersion, tt, vt.Variant, true, vt.Tasks)
		if err != nil {
			return nil, err
		}
		patchVersion.BuildIds = append(patchVersion.BuildIds, buildId)
		patchVersion.BuildVariants = append(patchVersion.BuildVariants,
			version.BuildStatus{
				BuildVariant: vt.Variant,
				Activated:    true,
				BuildId:      buildId,
			},
		)
	}

	if err = patchVersion.Insert(); err != nil {
		return nil, err
	}
	if err = p.SetActivated(patchVersion.Id); err != nil {
		return nil, err
	}
	return patchVersion, nil
}
Example #3
0
// Finalizes a patch:
// Patches a remote project's configuration file if needed.
// Creates a version for this patch and links it.
// Creates builds based on the version.
func FinalizePatch(p *patch.Patch, settings *evergreen.Settings) (
	patchVersion *version.Version, err error) {
	// unmarshal the project YAML for storage
	project := &Project{}
	err = yaml.Unmarshal([]byte(p.PatchedConfig), project)
	if err != nil {
		return nil, fmt.Errorf(
			"Error marshalling patched project config from repository revision ā€œ%vā€: %v",
			p.Githash, err)
	}

	projectRef, err := FindOneProjectRef(p.Project)
	if err != nil {
		return
	}

	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?")
	}

	patchVersion = &version.Version{
		Id:            fmt.Sprintf("%v_%v", p.Id.Hex(), 0),
		CreateTime:    time.Now(),
		Identifier:    p.Project,
		Revision:      p.Githash,
		Author:        gitCommit.Commit.Committer.Name,
		AuthorEmail:   gitCommit.Commit.Committer.Email,
		Message:       gitCommit.Commit.Message,
		BuildIds:      []string{},
		BuildVariants: []version.BuildStatus{},
		Config:        string(p.PatchedConfig),
		Status:        evergreen.PatchCreated,
		Requester:     evergreen.PatchVersionRequester,
	}

	//expand tasks and build variants
	buildVariants := p.BuildVariants
	if len(p.BuildVariants) == 1 && p.BuildVariants[0] == "all" {
		buildVariants = make([]string, 0)
		for _, buildVariant := range project.BuildVariants {
			if buildVariant.Disabled {
				continue
			}
			buildVariants = append(buildVariants, buildVariant.Name)
		}
	}
	tasks := p.Tasks
	if len(p.Tasks) == 1 && p.Tasks[0] == "all" {
		tasks = make([]string, 0)
		for _, t := range project.Tasks {
			tasks = append(tasks, t.Name)
		}
	}
	tt := BuildTaskIdTable(project, patchVersion)
	for _, buildvariant := range buildVariants {
		buildId, err := CreateBuildFromVersion(project, patchVersion, tt, buildvariant, true, tasks)
		if err != nil {
			return nil, err
		}
		patchVersion.BuildIds = append(patchVersion.BuildIds, buildId)
		patchVersion.BuildVariants = append(patchVersion.BuildVariants,
			version.BuildStatus{
				BuildVariant: buildvariant,
				Activated:    true,
				BuildId:      buildId,
			},
		)
	}

	if err = patchVersion.Insert(); err != nil {
		return nil, err
	}
	if err = p.SetActivated(patchVersion.Id); err != nil {
		return nil, err
	}
	return patchVersion, nil
}
Example #4
0
// Validate checks an API request to see if it is safe and sane.
// Returns the relevant patch metadata and any errors that occur.
func (pr *PatchAPIRequest) Validate(oauthToken string) (*PatchMetadata, error) {
	var repoOwner, repo string
	var module *model.Module
	projectRef, err := model.FindOneProjectRef(pr.ProjectFileName)
	if err != nil {
		return nil, fmt.Errorf("Could not find project ref %v : %v", pr.ProjectFileName, err)
	}

	repoOwner = projectRef.Owner
	repo = projectRef.Repo

	// validate the project file
	project, err := model.FindProject("", projectRef)
	if err != nil {
		return nil, fmt.Errorf("Could not find project file %v: %v",
			pr.ProjectFileName, err)
	}
	if project == nil {
		return nil, fmt.Errorf("No such project file named %v", pr.ProjectFileName)
	}

	if pr.ModuleName != "" {
		// is there a module? validate it.
		module, err = project.GetModuleByName(pr.ModuleName)
		if err != nil {
			return nil, fmt.Errorf("could not find module %v: %v", pr.ModuleName, err)
		}
		if module == nil {
			return nil, fmt.Errorf("no module named %v", pr.ModuleName)
		}
		repoOwner, repo = module.GetRepoOwnerAndName()
	}

	if len(pr.Githash) != 40 {
		return nil, fmt.Errorf("invalid githash")
	}
	gitCommit, err := thirdparty.GetCommitEvent(oauthToken, repoOwner, repo, pr.Githash)
	if err != nil {
		return nil, fmt.Errorf("could not find base revision %v for project %v: %v",
			pr.Githash, projectRef.Identifier, err)
	}
	if gitCommit == nil {
		return nil, fmt.Errorf("commit hash %v doesn't seem to exist", pr.Githash)
	}

	gitOutput, err := thirdparty.GitApplyNumstat(pr.PatchContent)
	if err != nil {
		return nil, fmt.Errorf("couldn't validate patch: %v", err)
	}
	if gitOutput == nil {
		return nil, fmt.Errorf("couldn't validate patch: git apply --numstat returned empty")
	}

	summaries, err := thirdparty.ParseGitSummary(gitOutput)
	if err != nil {
		return nil, fmt.Errorf("couldn't validate patch: %v", err)
	}

	if len(pr.BuildVariants) == 0 || pr.BuildVariants[0] == "" {
		return nil, fmt.Errorf("no buildvariants specified")
	}

	// verify that this build variant exists
	for _, buildVariant := range pr.BuildVariants {
		if buildVariant == "all" {
			continue
		}
		bv := project.FindBuildVariant(buildVariant)
		if bv == nil {
			return nil, fmt.Errorf("No such buildvariant: %v", buildVariant)
		}
	}
	return &PatchMetadata{pr.Githash, project, module, pr.BuildVariants, summaries}, nil
}
Example #5
0
func (as *APIServer) updatePatchModule(w http.ResponseWriter, r *http.Request) {
	p, err := getPatchFromRequest(r)
	if err != nil {
		as.WriteJSON(w, http.StatusBadRequest, err.Error())
		return
	}
	moduleName := r.FormValue("module")
	patchContent := r.FormValue("patch")
	githash := r.FormValue("githash")

	projectRef, err := model.FindOneProjectRef(p.Project)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error getting project ref with id %v: %v", p.Project, err))
		return
	}
	project, err := model.FindProject("", projectRef)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error getting patch: %v", err))
		return
	}
	if project == nil {
		as.LoggedError(w, r, http.StatusNotFound, fmt.Errorf("can't find project: %v", p.Project))
		return
	}

	module, err := project.GetModuleByName(moduleName)
	if err != nil || module == nil {
		as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("No such module"))
		return
	}

	gitOutput, err := thirdparty.GitApplyNumstat(patchContent)
	if err != nil {
		as.WriteJSON(w, http.StatusBadRequest, fmt.Errorf("Invalid patch: %v", err))
		return
	}
	if gitOutput == nil {
		as.WriteJSON(w, http.StatusBadRequest, fmt.Errorf("Empty diff"))
		return
	}

	summaries, err := thirdparty.ParseGitSummary(gitOutput)
	if err != nil {
		as.WriteJSON(w, http.StatusBadRequest, fmt.Errorf("Can't validate patch: %v", err))
		return
	}

	repoOwner, repo := module.GetRepoOwnerAndName()

	commitInfo, err := thirdparty.GetCommitEvent(as.Settings.Credentials[projectRef.RepoKind], repoOwner, repo, githash)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, err)
		return
	}

	if commitInfo == nil {
		as.WriteJSON(w, http.StatusBadRequest, fmt.Errorf("commit hash doesn't seem to exist"))
		return
	}

	modulePatch := patch.ModulePatch{
		ModuleName: moduleName,
		Githash:    githash,
		PatchSet: patch.PatchSet{
			Patch:   patchContent,
			Summary: summaries, // thirdparty.GetPatchSummary(apiRequest.PatchContent),
		},
	}

	if err = p.UpdateModulePatch(modulePatch); err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, err)
		return
	}

	as.WriteJSON(w, http.StatusOK, "Patch module updated")
	return
}
Example #6
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
	}

	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 = validator.ValidateAndFinalize(patchDoc, &as.Settings); err != nil {
			as.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
	}

	as.WriteJSON(w, http.StatusCreated, PatchAPIResponse{Patch: patchDoc})
}
Example #7
0
func (as *APIServer) updatePatchModule(w http.ResponseWriter, r *http.Request) {
	p, err := getPatchFromRequest(r)
	if err != nil {
		as.WriteJSON(w, http.StatusBadRequest, err.Error())
		return
	}

	var moduleName, patchContent, githash string

	if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
		moduleName, patchContent, githash = r.FormValue("module"), r.FormValue("patch"), r.FormValue("githash")
	} else {
		data := struct {
			Module  string `json:"module"`
			Patch   string `json:"patch"`
			Githash string `json:"githash"`
		}{}
		if err := util.ReadJSONInto(r.Body, &data); err != nil {
			as.LoggedError(w, r, http.StatusBadRequest, err)
			return
		}
		moduleName, patchContent, githash = data.Module, data.Patch, data.Githash
	}

	if len(patchContent) == 0 {
		as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("Error: Patch must not be empty"))
		return
	}

	projectRef, err := model.FindOneProjectRef(p.Project)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error getting project ref with id %v: %v", p.Project, err))
		return
	}
	project, err := model.FindProject("", projectRef)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error getting patch: %v", err))
		return
	}
	if project == nil {
		as.LoggedError(w, r, http.StatusNotFound, fmt.Errorf("can't find project: %v", p.Project))
		return
	}

	module, err := project.GetModuleByName(moduleName)
	if err != nil || module == nil {
		as.LoggedError(w, r, http.StatusBadRequest, fmt.Errorf("No such module", moduleName))
		return
	}

	gitOutput, err := thirdparty.GitApplyNumstat(patchContent)
	if err != nil {
		as.WriteJSON(w, http.StatusBadRequest, fmt.Errorf("Invalid patch: %v", err))
		return
	}
	if gitOutput == nil {
		as.WriteJSON(w, http.StatusBadRequest, fmt.Errorf("Empty diff"))
		return
	}

	summaries, err := thirdparty.ParseGitSummary(gitOutput)
	if err != nil {
		as.WriteJSON(w, http.StatusBadRequest, fmt.Errorf("Can't validate patch: %v", err))
		return
	}

	repoOwner, repo := module.GetRepoOwnerAndName()

	commitInfo, err := thirdparty.GetCommitEvent(as.Settings.Credentials["github"], repoOwner, repo, githash)
	if err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, err)
		return
	}

	if commitInfo == nil {
		as.WriteJSON(w, http.StatusBadRequest, fmt.Errorf("commit hash doesn't seem to exist"))
		return
	}

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

	modulePatch := patch.ModulePatch{
		ModuleName: moduleName,
		Githash:    githash,
		PatchSet: patch.PatchSet{
			PatchFileId: patchFileId,
			Summary:     summaries,
		},
	}

	if err = p.UpdateModulePatch(modulePatch); err != nil {
		as.LoggedError(w, r, http.StatusInternalServerError, err)
		return
	}

	as.WriteJSON(w, http.StatusOK, "Patch module updated")
	return
}
Example #8
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})
}
Example #9
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)
}
Example #10
0
// CreatePatch checks an API request to see if it is safe and sane.
// Returns the relevant patch metadata, the patch document, and any errors that occur.
func (pr *PatchAPIRequest) CreatePatch(finalize bool, oauthToken string,
	dbUser *user.DBUser, settings *evergreen.Settings) (*model.Project, *patch.Patch, error) {
	var repoOwner, repo string
	var module *model.Module

	projectRef, err := model.FindOneProjectRef(pr.ProjectId)
	if err != nil {
		return nil, nil, fmt.Errorf("Could not find project ref %v : %v", pr.ProjectId, err)
	}

	repoOwner = projectRef.Owner
	repo = projectRef.Repo

	if len(pr.Githash) != 40 {
		return nil, nil, fmt.Errorf("invalid githash")
	}

	gitCommit, err := thirdparty.GetCommitEvent(oauthToken, repoOwner, repo, pr.Githash)
	if err != nil {
		return nil, nil, fmt.Errorf("could not find base revision %v for project %v: %v",
			pr.Githash, projectRef.Identifier, err)

	}
	if gitCommit == nil {
		return nil, nil, fmt.Errorf("commit hash %v doesn't seem to exist", pr.Githash)
	}

	summaries, err := getSummaries(pr.PatchContent)
	if err != nil {
		return nil, nil, err
	}

	isEmpty := (pr.PatchContent == "")

	if finalize && (len(pr.BuildVariants) == 0 || pr.BuildVariants[0] == "") {
		return nil, nil, fmt.Errorf("no buildvariants specified")
	}

	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:   pr.Description,
		Author:        dbUser.Id,
		Project:       pr.ProjectId,
		Githash:       pr.Githash,
		CreateTime:    createTime,
		Status:        evergreen.PatchCreated,
		BuildVariants: pr.BuildVariants,
		Tasks:         pr.Tasks,
		IsEmpty:       isEmpty,
		Patches: []patch.ModulePatch{
			{
				ModuleName: "",
				Githash:    pr.Githash,
				PatchSet: patch.PatchSet{
					Patch:       pr.PatchContent,
					PatchFileId: patchFileId,
					Summary:     summaries,
				},
			},
		},
	}

	// Get and validate patched config and add it to the patch document
	project, err := validator.GetPatchedProject(patchDoc, settings)
	if err != nil {
		return nil, nil, fmt.Errorf("invalid patched config: %v", err)
	}

	if pr.ModuleName != "" {
		// is there a module? validate it.
		module, err = project.GetModuleByName(pr.ModuleName)
		if err != nil {
			return nil, nil, fmt.Errorf("could not find module %v: %v", pr.ModuleName, err)
		}
		if module == nil {
			return nil, nil, fmt.Errorf("no module named %v", pr.ModuleName)
		}
	}

	// verify that all variants exists
	for _, buildVariant := range pr.BuildVariants {
		if buildVariant == "all" || buildVariant == "" {
			continue
		}
		bv := project.FindBuildVariant(buildVariant)
		if bv == nil {
			return nil, nil, fmt.Errorf("No such buildvariant: %v", buildVariant)
		}
	}

	// write the patch content into a GridFS file under a new ObjectId after validating.
	err = db.WriteGridFile(patch.GridFSPrefix, patchFileId, strings.NewReader(pr.PatchContent))
	if err != nil {
		return nil, nil, fmt.Errorf("failed to write patch file to db: %v", err)
	}

	// add the project config
	projectYamlBytes, err := yaml.Marshal(project)
	if err != nil {
		return nil, nil, fmt.Errorf("error marshalling patched config: %v", err)
	}

	// set the patch number based on patch author
	patchDoc.PatchNumber, err = dbUser.IncPatchNumber()
	if err != nil {
		return nil, nil, fmt.Errorf("error computing patch num %v", err)
	}
	patchDoc.PatchedConfig = string(projectYamlBytes)

	patchDoc.ClearPatchData()

	return project, patchDoc, nil
}