github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/model/patch/patch.go (about) 1 package patch 2 3 import ( 4 "io/ioutil" 5 "time" 6 7 "github.com/evergreen-ci/evergreen" 8 "github.com/evergreen-ci/evergreen/db" 9 "gopkg.in/mgo.v2" 10 "gopkg.in/mgo.v2/bson" 11 ) 12 13 // Hard limit on patch size. 14 const SizeLimit = 1024 * 1024 * 100 15 16 // VariantTasks contains the variant ID and the set of tasks to be scheduled for that variant 17 type VariantTasks struct { 18 Variant string 19 Tasks []string 20 } 21 22 // Stores all details related to a patch request 23 type Patch struct { 24 Id bson.ObjectId `bson:"_id,omitempty"` 25 Description string `bson:"desc"` 26 Project string `bson:"branch"` 27 Githash string `bson:"githash"` 28 PatchNumber int `bson:"patch_number"` 29 Author string `bson:"author"` 30 Version string `bson:"version"` 31 Status string `bson:"status"` 32 CreateTime time.Time `bson:"create_time"` 33 StartTime time.Time `bson:"start_time"` 34 FinishTime time.Time `bson:"finish_time"` 35 BuildVariants []string `bson:"build_variants"` 36 Tasks []string `bson:"tasks"` 37 VariantsTasks []VariantTasks `bson:"variants_tasks"` 38 Patches []ModulePatch `bson:"patches"` 39 Activated bool `bson:"activated"` 40 PatchedConfig string `bson:"patched_config"` 41 } 42 43 // this stores request details for a patch 44 type ModulePatch struct { 45 ModuleName string `bson:"name"` 46 Githash string `bson:"githash"` 47 PatchSet PatchSet `bson:"patch_set"` 48 } 49 50 // this stores information about the actual patch 51 type PatchSet struct { 52 Patch string `bson:"patch,omitempty"` 53 PatchFileId string `bson:"patch_file_id,omitempty"` 54 Summary []Summary `bson:"summary"` 55 } 56 57 // this stores summary patch information 58 type Summary struct { 59 Name string `bson:"filename"` 60 Additions int `bson:"additions"` 61 Deletions int `bson:"deletions"` 62 } 63 64 func (p *Patch) SetDescription(desc string) error { 65 p.Description = desc 66 return UpdateOne( 67 bson.M{IdKey: p.Id}, 68 bson.M{ 69 "$set": bson.M{ 70 DescriptionKey: desc, 71 }, 72 }, 73 ) 74 } 75 76 // ClearPatchData removes any inline patch data stored in this patch object for patches that have 77 // an associated id in gridfs, so that it can be stored properly. 78 func (p *Patch) ClearPatchData() { 79 for i, patchPart := range p.Patches { 80 // If the patch isn't stored externally, no need to do anything. 81 if patchPart.PatchSet.PatchFileId != "" { 82 p.Patches[i].PatchSet.Patch = "" 83 } 84 } 85 } 86 87 // FetchPatchFiles dereferences externally-stored patch diffs by fetching them from gridfs 88 // and placing their contents into the patch object. 89 func (p *Patch) FetchPatchFiles() error { 90 for i, patchPart := range p.Patches { 91 // If the patch isn't stored externally, no need to do anything. 92 if patchPart.PatchSet.PatchFileId == "" { 93 continue 94 } 95 96 file, err := db.GetGridFile(GridFSPrefix, patchPart.PatchSet.PatchFileId) 97 if err != nil { 98 return err 99 } 100 defer file.Close() 101 raw, err := ioutil.ReadAll(file) 102 if err != nil { 103 return err 104 } 105 p.Patches[i].PatchSet.Patch = string(raw) 106 } 107 return nil 108 } 109 110 // SyncVariantsTasks updates the patch's Tasks and BuildVariants fields to match with the set 111 // in the given list of VariantTasks. This is to ensure schema backwards compatibility for T shaped 112 // patches. This mutates the patch in memory but does not update it in the database; for that, use 113 // SetVariantsTasks. 114 func (p *Patch) SyncVariantsTasks(variantsTasks []VariantTasks) { 115 taskSet := map[string]bool{} 116 variantSet := map[string]bool{} 117 118 // TODO after fully switching over to new schema, remove support for standalone 119 // Variants and Tasks field 120 for _, v := range variantsTasks { 121 variantSet[v.Variant] = true 122 for _, t := range v.Tasks { 123 taskSet[t] = true 124 } 125 } 126 tasks := []string{} 127 variants := []string{} 128 for k := range variantSet { 129 variants = append(variants, k) 130 } 131 132 for k := range taskSet { 133 tasks = append(tasks, k) 134 } 135 136 p.VariantsTasks = variantsTasks 137 p.Tasks = tasks 138 p.BuildVariants = variants 139 } 140 141 // Updates the variant/tasks pairs in the database. 142 // Also updates the Tasks and Variants fields to maintain backwards compatibility between 143 // the old and new fields. 144 func (p *Patch) SetVariantsTasks(variantsTasks []VariantTasks) error { 145 p.SyncVariantsTasks(variantsTasks) 146 return UpdateOne( 147 bson.M{IdKey: p.Id}, 148 bson.M{ 149 "$set": bson.M{ 150 VariantsTasksKey: variantsTasks, 151 BuildVariantsKey: p.BuildVariants, 152 TasksKey: p.Tasks, 153 }, 154 }, 155 ) 156 } 157 158 // AddBuildVariants adds more buildvarints to a patch document. 159 // This is meant to be used after initial patch creation. 160 func (p *Patch) AddBuildVariants(bvs []string) error { 161 change := mgo.Change{ 162 Update: bson.M{ 163 "$addToSet": bson.M{BuildVariantsKey: bson.M{"$each": bvs}}, 164 }, 165 ReturnNew: true, 166 } 167 _, err := db.FindAndModify(Collection, bson.M{IdKey: p.Id}, nil, change, p) 168 return err 169 } 170 171 // AddTasks adds more tasks to a patch document. 172 // This is meant to be used after initial patch creation, to reconfigure the patch. 173 func (p *Patch) AddTasks(tasks []string) error { 174 change := mgo.Change{ 175 Update: bson.M{ 176 "$addToSet": bson.M{TasksKey: bson.M{"$each": tasks}}, 177 }, 178 ReturnNew: true, 179 } 180 _, err := db.FindAndModify(Collection, bson.M{IdKey: p.Id}, nil, change, p) 181 return err 182 } 183 184 // TryMarkStarted attempts to mark a patch as started if it 185 // isn't already marked as such 186 func TryMarkStarted(versionId string, startTime time.Time) error { 187 filter := bson.M{ 188 VersionKey: versionId, 189 StatusKey: evergreen.PatchCreated, 190 } 191 update := bson.M{ 192 "$set": bson.M{ 193 StartTimeKey: startTime, 194 StatusKey: evergreen.PatchStarted, 195 }, 196 } 197 err := UpdateOne(filter, update) 198 if err == mgo.ErrNotFound { 199 return nil 200 } 201 return err 202 } 203 204 // TryMarkFinished attempts to mark a patch of a given version as finished. 205 func TryMarkFinished(versionId string, finishTime time.Time, status string) error { 206 filter := bson.M{VersionKey: versionId} 207 update := bson.M{ 208 "$set": bson.M{ 209 FinishTimeKey: finishTime, 210 StatusKey: status, 211 }, 212 } 213 return UpdateOne(filter, update) 214 } 215 216 // Insert inserts the patch into the db, returning any errors that occur 217 func (p *Patch) Insert() error { 218 return db.Insert(Collection, p) 219 } 220 221 // ConfigChanged looks through the parts of the patch and returns true if the 222 // passed in remotePath is in the the name of the changed files that are part 223 // of the patch 224 func (p *Patch) ConfigChanged(remotePath string) bool { 225 for _, patchPart := range p.Patches { 226 if patchPart.ModuleName == "" { 227 for _, summary := range patchPart.PatchSet.Summary { 228 if summary.Name == remotePath { 229 return true 230 } 231 } 232 return false 233 } 234 } 235 return false 236 } 237 238 // SetActivated sets the patch to activated in the db 239 func (p *Patch) SetActivated(versionId string) error { 240 p.Version = versionId 241 p.Activated = true 242 return UpdateOne( 243 bson.M{IdKey: p.Id}, 244 bson.M{ 245 "$set": bson.M{ 246 ActivatedKey: true, 247 VersionKey: versionId, 248 }, 249 }, 250 ) 251 252 } 253 254 // Add or update a module within a patch. 255 func (p *Patch) UpdateModulePatch(modulePatch ModulePatch) error { 256 // check that a patch for this module exists 257 query := bson.M{ 258 IdKey: p.Id, 259 PatchesKey + "." + ModulePatchNameKey: modulePatch.ModuleName, 260 } 261 update := bson.M{ 262 "$set": bson.M{PatchesKey + ".$": modulePatch}, 263 } 264 result, err := UpdateAll(query, update) 265 if err != nil { 266 return err 267 } 268 269 // The patch already existed in the array, and it's been updated. 270 if result.Updated > 0 { 271 return nil 272 } 273 274 //it wasn't in the array, we need to add it. 275 query = bson.M{IdKey: p.Id} 276 update = bson.M{ 277 "$push": bson.M{PatchesKey: modulePatch}, 278 } 279 return UpdateOne(query, update) 280 } 281 282 // RemoveModulePatch removes a module that's part of a patch request 283 func (p *Patch) RemoveModulePatch(moduleName string) error { 284 // check that a patch for this module exists 285 query := bson.M{ 286 IdKey: p.Id, 287 } 288 update := bson.M{ 289 "$pull": bson.M{ 290 PatchesKey: bson.M{ModulePatchNameKey: moduleName}, 291 }, 292 } 293 return UpdateOne(query, update) 294 }