github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/model/grid/grid.go (about)

     1  package grid
     2  
     3  import (
     4  	"github.com/evergreen-ci/evergreen"
     5  	"github.com/evergreen-ci/evergreen/apimodels"
     6  	"github.com/evergreen-ci/evergreen/db"
     7  	"github.com/evergreen-ci/evergreen/model/build"
     8  	"github.com/evergreen-ci/evergreen/model/task"
     9  	"github.com/evergreen-ci/evergreen/model/version"
    10  	"gopkg.in/mgo.v2/bson"
    11  )
    12  
    13  // CellId represents a unique identifier for each cell on
    14  // the Grid page.
    15  type CellId struct {
    16  	Task    string `bson:"d" json:"task"`
    17  	Variant string `bson:"v" json:"variant"`
    18  }
    19  
    20  // CellHistory holds historical data for each cell on the
    21  // Grid page.
    22  type CellHistory struct {
    23  	Id            string                  `bson:"d" json:"id"`
    24  	Revision      string                  `bson:"v" json:"revision"`
    25  	Status        string                  `bson:"s" json:"status"`
    26  	StatusDetails apimodels.TaskEndDetail `bson:"e" json:"task_end_details"`
    27  }
    28  
    29  // Cell contains information for each cell on the Grid page.
    30  // It includes both the cell's identifier and its history.
    31  type Cell struct {
    32  	Id      CellId        `bson:"_id" json:"cellId"`
    33  	History []CellHistory `bson:"h" json:"history"`
    34  }
    35  
    36  // Grid is a slice of cells.
    37  type Grid []Cell
    38  
    39  type FailureId struct {
    40  	Test string `bson:"t" json:"test"`
    41  	Task string `bson:"d" json:"task"`
    42  }
    43  
    44  // VariantInfo holds information for each variant a test fails on.
    45  type VariantInfo struct {
    46  	Name   string `bson:"n" json:"name"`
    47  	TaskId string `bson:"i" json:"task_id"`
    48  }
    49  
    50  // Failure contains data for a particular test failure - including
    51  // the test's task display name, and the variants its failing on.
    52  type Failure struct {
    53  	Id       FailureId     `bson:"_id" json:"identifier"`
    54  	Variants []VariantInfo `bson:"a" json:"variants"`
    55  }
    56  
    57  // Revision failure contains the revision and a list of the
    58  // task failures that exist on that version.
    59  type RevisionFailure struct {
    60  	Id       string        `bson:"_id" json:"revision"`
    61  	Failures []TaskFailure `bson:"a" json:"failures"`
    62  }
    63  
    64  // TaskFailure has the information needed to be displayed on
    65  // the revision failures tab.
    66  type TaskFailure struct {
    67  	BuildVariant string `bson:"n" json:"variant"`
    68  	TestName     string `bson:"i" json:"test"`
    69  	TaskName     string `bson:"t" json:"task"`
    70  	TaskId       string `bson:"tid" json:"task_id"`
    71  }
    72  
    73  // Failures holds failures.
    74  type Failures []Failure
    75  
    76  // RevisionFailures holds revision failures
    77  type RevisionFailures []RevisionFailure
    78  
    79  // FetchCells returns a Grid of Cells - grouped by variant and display name.
    80  // current is the most recent version and from which to fetch prior Cells
    81  // going back as far as depth versions.
    82  func FetchCells(current version.Version, depth int) (Grid, error) {
    83  	cells := Grid{}
    84  	pipeline := []bson.M{
    85  		// Stage 1: Get all builds from the current version going back
    86  		// as far as depth versions.
    87  		{"$match": bson.M{
    88  			build.RequesterKey: evergreen.RepotrackerVersionRequester,
    89  			build.RevisionOrderNumberKey: bson.M{
    90  				"$lte": current.RevisionOrderNumber,
    91  				"$gte": (current.RevisionOrderNumber - depth),
    92  			},
    93  			build.ProjectKey: current.Identifier,
    94  		}},
    95  		// Stage 2: Sort the builds by the most recently completed.
    96  		{"$sort": bson.M{
    97  			build.RevisionOrderNumberKey: -1,
    98  		}},
    99  		// Stage 3: Project only the relevant fields.
   100  		{"$project": bson.M{
   101  			build.TasksKey:    1,
   102  			build.RevisionKey: 1,
   103  			"v":               "$" + build.BuildVariantKey,
   104  		}},
   105  		// Stage 4: Flatten the task cache for easier grouping.
   106  		{"$unwind": "$tasks"},
   107  		// Stage 5: Rewrite and project out only the relevant fields.
   108  		{"$project": bson.M{
   109  			"_id": 0,
   110  			"v":   1,
   111  			"r":   "$" + build.RevisionKey,
   112  			"d":   "$" + build.TasksKey + "." + build.TaskCacheDisplayNameKey,
   113  			"st":  "$" + build.TasksKey + "." + build.TaskCacheStatusKey,
   114  			"ed":  "$" + build.TasksKey + "." + build.TaskCacheStatusDetailsKey,
   115  			"id":  "$" + build.TasksKey + "." + build.TaskCacheIdKey,
   116  		}},
   117  		// Stage 6: Group the tasks by variant and display name. For each group,
   118  		// add the history - all prior versioned tasks along with their status,
   119  		// id, and revision identifier.
   120  		{"$group": bson.M{
   121  			"_id": bson.M{
   122  				"v": "$v",
   123  				"d": "$d",
   124  			},
   125  			"h": bson.M{
   126  				"$push": bson.M{
   127  					"s": "$st",
   128  					"e": "$ed",
   129  					"d": "$id",
   130  					"v": "$r",
   131  				},
   132  			},
   133  		}},
   134  	}
   135  	return cells, db.Aggregate(build.Collection, pipeline, &cells)
   136  }
   137  
   138  // FetchFailures returns the most recent test failures that have occurred at or
   139  // before the current version - looking back as far as depth versions.
   140  func FetchFailures(current version.Version, depth int) (Failures, error) {
   141  	pipeline := []bson.M{
   142  		// Stage 1: Get the most recent completed tasks - looking back as far as
   143  		// depth versions - on this project.
   144  		{"$match": bson.M{
   145  			task.RevisionOrderNumberKey: bson.M{
   146  				"$lte": current.RevisionOrderNumber,
   147  				"$gte": (current.RevisionOrderNumber - depth),
   148  			},
   149  			task.ProjectKey:   current.Identifier,
   150  			task.RequesterKey: evergreen.RepotrackerVersionRequester,
   151  			task.StatusKey: bson.M{
   152  				"$in": []string{
   153  					evergreen.TaskFailed,
   154  					evergreen.TaskSucceeded,
   155  				},
   156  			},
   157  		}},
   158  		// Stage 2: Sort the tasks by the most recently completed.
   159  		{"$sort": bson.M{
   160  			task.RevisionOrderNumberKey: -1,
   161  		}},
   162  		// Stage 3: Project only relevant fields.
   163  		{"$project": bson.M{
   164  			task.DisplayNameKey:  1,
   165  			task.BuildVariantKey: 1,
   166  			task.TestResultsKey:  1,
   167  			task.IdKey:           1,
   168  		}},
   169  		// Stage 4: Group these tasks by display name and buildvariant -
   170  		// this returns the most recently completed grouped by task display name
   171  		// and by variant. We take only the first test results (adding its task
   172  		// id) for each task/variant group.
   173  		{"$group": bson.M{
   174  			"_id": bson.M{
   175  				"t": "$" + task.DisplayNameKey,
   176  				"v": "$" + task.BuildVariantKey,
   177  			},
   178  			"l": bson.M{
   179  				"$first": "$" + task.TestResultsKey,
   180  			},
   181  			"tid": bson.M{
   182  				"$first": "$" + task.IdKey,
   183  			},
   184  		}},
   185  		// Stage 5: For each group, filter out those task/variant combinations
   186  		// that don't have at least one test failure in them.
   187  		{"$match": bson.M{
   188  			"l." + task.TestResultStatusKey: evergreen.TestFailedStatus,
   189  		}},
   190  		// Stage 6: Rewrite each task/variant combination from the _id into the
   191  		// top-level. Project only the test name and status for all tests, and
   192  		// add a 'status' literal string to each group. This sets up the
   193  		// documents for redacting in next stage.
   194  		{"$project": bson.M{
   195  			"tid": 1,
   196  			"t":   "$_id.t",
   197  			"v":   "$_id.v",
   198  			"l." + task.TestResultStatusKey:   1,
   199  			"l." + task.TestResultTestFileKey: 1,
   200  			"status": bson.M{
   201  				"$literal": evergreen.TestFailedStatus,
   202  			},
   203  		}},
   204  		// Stage 7: While each test result contains at least one failed test,
   205  		// some other tests may have passed. Prune individual tests that did
   206  		// not fail.
   207  		{"$redact": bson.M{
   208  			"$cond": bson.M{
   209  				"if": bson.M{
   210  					"$eq": []string{
   211  						"$status", evergreen.TestFailedStatus,
   212  					},
   213  				},
   214  				"then": "$$DESCEND",
   215  				"else": "$$PRUNE",
   216  			},
   217  		}},
   218  		// Stage 8: We no longer need the status fields so project only fields
   219  		// we want to return.
   220  		{"$project": bson.M{
   221  			"f":   "$l." + task.TestResultTestFileKey,
   222  			"_id": 0,
   223  			"tid": 1,
   224  			"t":   1,
   225  			"v":   1,
   226  		}},
   227  		// Stage 9: Flatten each failing test so we can group them by all the
   228  		// variants on which they are failing.
   229  		{"$unwind": "$f"},
   230  		// Stage 10: Group individual test failure. For each, add the variants
   231  		// it's failing on (and the accompanying task id) and include task's
   232  		// display name.
   233  		{"$group": bson.M{
   234  			"_id": bson.M{
   235  				"t": "$f",
   236  				"d": "$t",
   237  			},
   238  			"a": bson.M{
   239  				"$push": bson.M{
   240  					"n": "$v",
   241  					"i": "$tid",
   242  				},
   243  			},
   244  		}},
   245  	}
   246  	failures := Failures{}
   247  	return failures, db.Aggregate(task.Collection, pipeline, &failures)
   248  }
   249  
   250  // FetchRevisionOrderFailures returns the most recent test failures
   251  // grouped by revision - looking as far back as depth revisions
   252  func FetchRevisionOrderFailures(current version.Version, depth int) (RevisionFailures, error) {
   253  	pipeline := []bson.M{
   254  		// Stage 1: Get the most recent completed tasks - looking back as far as
   255  		// depth versions - on this project.
   256  		{"$match": bson.M{
   257  			task.RevisionOrderNumberKey: bson.M{
   258  				"$lte": current.RevisionOrderNumber,
   259  				"$gte": (current.RevisionOrderNumber - depth),
   260  			},
   261  			task.ProjectKey:   current.Identifier,
   262  			task.RequesterKey: evergreen.RepotrackerVersionRequester,
   263  		}},
   264  		// Stage 2: Project only relevant fields.
   265  		{"$project": bson.M{
   266  			task.DisplayNameKey:  1,
   267  			task.RevisionKey:     1,
   268  			task.BuildVariantKey: 1,
   269  			task.IdKey:           1,
   270  			"l":                  "$" + task.TestResultsKey,
   271  		}},
   272  		// Stage 3: Flatten out the test results
   273  		{"$unwind": "$l"},
   274  		// Stage 4: Take only failed test results
   275  		{"$match": bson.M{
   276  			"l." + task.TestResultStatusKey: evergreen.TestFailedStatus,
   277  		}},
   278  		// Stage 5: Project only relevant fields including just the test file key
   279  		{"$project": bson.M{
   280  			task.RevisionKey:     1,
   281  			task.BuildVariantKey: 1,
   282  			task.DisplayNameKey:  1,
   283  			task.IdKey:           1,
   284  			"f":                  "$l." + task.TestResultTestFileKey,
   285  		}},
   286  		// Stage 6: Group by revision. For each one include the
   287  		// variant name, task name, task id and test name
   288  		{"$group": bson.M{
   289  			"_id": "$" + task.RevisionKey,
   290  			"a": bson.M{
   291  				"$push": bson.M{
   292  					"n":   "$" + task.BuildVariantKey,
   293  					"i":   "$f",
   294  					"t":   "$" + task.DisplayNameKey,
   295  					"tid": "$" + task.IdKey,
   296  				},
   297  			},
   298  		}},
   299  	}
   300  	taskFailures := RevisionFailures{}
   301  	return taskFailures, db.Aggregate(task.Collection, pipeline, &taskFailures)
   302  }