-
Notifications
You must be signed in to change notification settings - Fork 129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DEVPROD-6266 Fix duplication for --repeat-patch #7892
Changes from 6 commits
5339148
611bb4e
0836d09
0f1410c
e4ed569
cc76faf
dae6a50
15b7658
1ea5ff7
9e1ab61
bb8910b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -606,7 +606,6 @@ func (j *patchIntentProcessor) createGitHubMergeSubscription(ctx context.Context | |
} | ||
|
||
func (j *patchIntentProcessor) buildTasksAndVariants(patchDoc *patch.Patch, project *model.Project) error { | ||
var previousPatchStatus string | ||
var err error | ||
var reuseDef bool | ||
reusePatchId, failedOnly := j.intent.RepeatFailedTasksAndVariants() | ||
|
@@ -615,13 +614,14 @@ func (j *patchIntentProcessor) buildTasksAndVariants(patchDoc *patch.Patch, proj | |
} | ||
|
||
if reuseDef || failedOnly { | ||
previousPatchStatus, err = j.setToPreviousPatchDefinition(patchDoc, project, reusePatchId, failedOnly) | ||
err = j.setToPreviousPatchDefinition(patchDoc, project, reusePatchId, failedOnly) | ||
if err != nil { | ||
return err | ||
} | ||
if j.IntentType == patch.GithubIntentType { | ||
patchDoc.GithubPatchData.RepeatPatchIdNextPatch = reusePatchId | ||
} | ||
return nil | ||
} | ||
|
||
// Verify that all variants exists | ||
|
@@ -648,109 +648,149 @@ func (j *patchIntentProcessor) buildTasksAndVariants(patchDoc *patch.Patch, proj | |
} | ||
} | ||
|
||
// If the user only wants failed tasks but the previous patch has no failed tasks, there is nothing to build | ||
skipForFailed := failedOnly && previousPatchStatus != evergreen.VersionFailed | ||
|
||
if len(patchDoc.VariantsTasks) == 0 && !skipForFailed { | ||
if len(patchDoc.VariantsTasks) == 0 { | ||
project.BuildProjectTVPairs(patchDoc, j.intent.GetAlias()) | ||
} | ||
return nil | ||
} | ||
|
||
func setTasksToPreviousFailed(patchDoc, previousPatch *patch.Patch, project *model.Project) error { | ||
var failedTasks []string | ||
for _, vt := range previousPatch.VariantsTasks { | ||
tasks, err := getPreviousFailedTasksAndDisplayTasks(project, vt, previousPatch.Version) | ||
if err != nil { | ||
return err | ||
// setToFilteredTasks sets the tasks/variants to a previous patch's activated tasks (filtered on failures if requested) | ||
// and adds dependencies and task group tasks as needed. | ||
func setToFilteredTasks(patchDoc, reusePatch *patch.Patch, project *model.Project, failedOnly bool) error { | ||
activatedTasks, err := filterToActiveForReuse(reusePatch) | ||
if err != nil { | ||
return errors.Wrap(err, "filtering to activated tasks") | ||
} | ||
|
||
activatedTasksDisplayNames := []string{} | ||
failedTaskDisplayNames := []string{} | ||
failedTasks := []task.Task{} | ||
for _, t := range activatedTasks { | ||
activatedTasksDisplayNames = append(activatedTasksDisplayNames, t.DisplayName) | ||
if failedOnly && evergreen.IsFailedTaskStatus(t.Status) { | ||
failedTasks = append(failedTasks, t) | ||
failedTaskDisplayNames = append(failedTaskDisplayNames, t.DisplayName) | ||
} | ||
} | ||
filteredTasks := activatedTasksDisplayNames | ||
if failedOnly { | ||
filteredTasks = failedTaskDisplayNames | ||
} | ||
|
||
filteredVariantTasks := []patch.VariantTasks{} | ||
for _, vt := range reusePatch.VariantsTasks { | ||
// Limit it to tasks that are failed or who have failed tasks depending on them. | ||
// We only need to add dependencies and task group tasks for failed tasks because otherwise | ||
// we can rely on them being there from the previous patch. | ||
if failedOnly { | ||
failedPlusNeeded, err := addTasksNeededByFailedForReuse(failedTasks, failedTaskDisplayNames, project, vt) | ||
if err != nil { | ||
return errors.Wrap(err, "getting dependencies and task groups for activated tasks") | ||
} | ||
filteredTasks = append(filteredTasks, failedPlusNeeded...) | ||
} | ||
|
||
variantTask := vt | ||
ablack12 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
variantTask.Tasks = utility.StringSliceIntersection(filteredTasks, vt.Tasks) | ||
|
||
// only add build variants and variant tasks if there are tasks in them that are being reused | ||
if len(variantTask.Tasks) != 0 || len(variantTask.DisplayTasks) != 0 { | ||
ablack12 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
filteredVariantTasks = append(filteredVariantTasks, variantTask) | ||
patchDoc.BuildVariants = append(patchDoc.BuildVariants, vt.Variant) | ||
} | ||
failedTasks = append(failedTasks, tasks...) | ||
|
||
} | ||
|
||
patchDoc.Tasks = failedTasks | ||
patchDoc.Tasks = filteredTasks | ||
patchDoc.VariantsTasks = filteredVariantTasks | ||
|
||
return nil | ||
} | ||
|
||
func filterToActiveForReuse(reusePatch *patch.Patch) ([]task.Task, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be moved to task_db.go (doesn't need to reference reuse specifically, it's really just byActivatedTasksWithoutDisplay or something) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moving it creates an import cycle. It's specific enough to this use case that I think it's okay here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this comment got missed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I forgot to hit enter to submit all of my comments, sorry There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that's only because you're passing in the whole patch -- you can change this to just pass in the ID and then it should be fine There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (may want to do that anyway) |
||
query := db.Query(bson.M{ | ||
task.VersionKey: reusePatch.Version, | ||
task.ActivatedKey: true, | ||
task.DisplayOnlyKey: bson.M{"$ne": true}, | ||
}) | ||
activatedTasks, err := task.FindAll(query) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "getting previous patch tasks") | ||
} | ||
|
||
return activatedTasks, nil | ||
|
||
} | ||
|
||
// addTasksNeededByFailedForReuse adds tasks that failed tasks need to run including dependencies and tasks from single host task groups. | ||
func addTasksNeededByFailedForReuse(failedTasks []task.Task, failedTaskDisplayNames []string, project *model.Project, vt patch.VariantTasks) ([]string, error) { | ||
// only add tasks if they are in the current project definition | ||
tasksInProjectVariant := project.FindTasksForVariant(vt.Variant) | ||
tasksToAdd := []string{} | ||
// add dependencies of failed tasks | ||
failedTaskDependencies, err := task.GetRecursiveDependenciesUp(failedTasks, nil) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "getting dependencies for activated tasks") | ||
} | ||
for _, t := range failedTaskDependencies { | ||
if utility.StringSliceContains(tasksInProjectVariant, t.DisplayName) && !utility.StringSliceContains(failedTaskDisplayNames, t.DisplayName) { | ||
tasksToAdd = append(tasksToAdd, t.DisplayName) | ||
} | ||
} | ||
|
||
for _, failedTask := range failedTasks { | ||
// Schedule all tasks in a single host task group because they may need to execute together to order to succeed. | ||
if utility.StringSliceContains(tasksInProjectVariant, failedTask.TaskGroup) && failedTask.TaskGroup != "" && failedTask.IsPartOfSingleHostTaskGroup() { | ||
taskGroup := project.FindTaskGroup(failedTask.TaskGroup) | ||
for _, t := range taskGroup.Tasks { | ||
if !utility.StringSliceContains(failedTaskDisplayNames, t) { | ||
tasksToAdd = append(tasksToAdd, t) | ||
} | ||
} | ||
} | ||
} | ||
|
||
return tasksToAdd, nil | ||
|
||
} | ||
|
||
// setToPreviousPatchDefinition sets the tasks/variants based on a previous patch. | ||
// If failedOnly is set, we only use the tasks/variants that failed. | ||
// If patchId isn't set, we just use the most recent patch for the project. | ||
func (j *patchIntentProcessor) setToPreviousPatchDefinition(patchDoc *patch.Patch, | ||
project *model.Project, patchId string, failedOnly bool) (string, error) { | ||
project *model.Project, patchId string, failedOnly bool) error { | ||
var reusePatch *patch.Patch | ||
var err error | ||
if patchId == "" { | ||
reusePatch, err = patch.FindOne(patch.MostRecentPatchByUserAndProject(j.user.Username(), project.Identifier)) | ||
if err != nil { | ||
return "", errors.Wrap(err, "querying for most recent patch") | ||
return errors.Wrap(err, "querying for most recent patch") | ||
} | ||
if reusePatch == nil { | ||
return "", errors.Errorf("no previous patch available") | ||
return errors.Errorf("no previous patch available") | ||
} | ||
} else { | ||
reusePatch, err = patch.FindOneId(patchId) | ||
if err != nil { | ||
return "", errors.Wrapf(err, "querying for patch '%s'", patchId) | ||
return errors.Wrapf(err, "querying for patch '%s'", patchId) | ||
} | ||
if reusePatch == nil { | ||
return "", errors.Errorf("patch '%s' not found", patchId) | ||
return errors.Errorf("patch '%s' not found", patchId) | ||
} | ||
} | ||
|
||
patchDoc.BuildVariants = reusePatch.BuildVariants | ||
if failedOnly { | ||
if err = setTasksToPreviousFailed(patchDoc, reusePatch, project); err != nil { | ||
return "", errors.Wrap(err, "settings tasks to previous failed") | ||
} | ||
} else if j.IntentType == patch.GithubIntentType { | ||
if j.IntentType == patch.GithubIntentType { | ||
patchDoc.Tasks = reusePatch.Tasks | ||
} else { | ||
// Only add activated tasks from previous patch | ||
query := db.Query(bson.M{ | ||
task.VersionKey: reusePatch.Version, | ||
task.DisplayNameKey: bson.M{"$in": reusePatch.Tasks}, | ||
task.ActivatedKey: true, | ||
task.DisplayOnlyKey: bson.M{"$ne": true}, | ||
}).WithFields(task.DisplayNameKey) | ||
allActivatedTasks, err := task.FindAll(query) | ||
if err != nil { | ||
return "", errors.Wrap(err, "getting previous patch tasks") | ||
} | ||
activatedTasks := []string{} | ||
for _, t := range allActivatedTasks { | ||
activatedTasks = append(activatedTasks, t.DisplayName) | ||
} | ||
patchDoc.Tasks = utility.StringSliceIntersection(activatedTasks, reusePatch.Tasks) | ||
patchDoc.BuildVariants = reusePatch.BuildVariants | ||
patchDoc.VariantsTasks = reusePatch.VariantsTasks | ||
return nil | ||
} | ||
|
||
return reusePatch.Status, nil | ||
} | ||
|
||
func getPreviousFailedTasksAndDisplayTasks(project *model.Project, vt patch.VariantTasks, version string) ([]string, error) { | ||
tasksInProjectVariant := project.FindTasksForVariant(vt.Variant) | ||
failedTasks, err := task.FindAll(db.Query(task.FailedTasksByVersionAndBV(version, vt.Variant))) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "finding failed tasks in build variant '%s' from previous patch '%s'", vt.Variant, version) | ||
if err = setToFilteredTasks(patchDoc, reusePatch, project, failedOnly); err != nil { | ||
return errors.Wrapf(err, "filtering tasks for '%s'", patchId) | ||
} | ||
// Verify that the task group or task is in the current project definition and in the previous run. | ||
allFailedTasks := []string{} | ||
for _, failedTask := range failedTasks { | ||
if utility.StringSliceContains(vt.Tasks, failedTask.DisplayName) { | ||
if failedTask.TaskGroup != "" && | ||
utility.StringSliceContains(tasksInProjectVariant, failedTask.TaskGroup) { | ||
// Schedule all tasks in a single host task group because they may need to execute together to order to succeed. | ||
if failedTask.IsPartOfSingleHostTaskGroup() { | ||
taskGroup := project.FindTaskGroup(failedTask.TaskGroup) | ||
allFailedTasks = append(allFailedTasks, taskGroup.Tasks...) | ||
} else { | ||
allFailedTasks = append(allFailedTasks, failedTask.DisplayName) | ||
} | ||
} else if !failedTask.DisplayOnly && | ||
utility.StringSliceContains(tasksInProjectVariant, failedTask.DisplayName) { | ||
allFailedTasks = append(allFailedTasks, failedTask.DisplayName) | ||
} | ||
} | ||
} | ||
return allFailedTasks, nil | ||
|
||
return nil | ||
} | ||
|
||
func ProcessTriggerAliases(ctx context.Context, p *patch.Patch, projectRef *model.ProjectRef, env evergreen.Environment, aliasNames []string) error { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we change "needed" language to dependency language? This is more consistent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It needs both dependencies and tasks from single host task groups
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. Perhaps "addDependenciesAndTaskGroups" then? I think right now the function includes details that really aren't relevant to the usage (that only failed tasks are passed in, and that its called by the reuse function).
Also, should we change the error message to say "dependencies and task groups"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the first half of this message got missed -- I'd still suggest changing the function title and variables to be more specific about what's being done rather than why its being called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so strange, I did the change but it was somehow lost. My apologies, pushed it now.