github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/model/host/db.go (about) 1 package host 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/evergreen-ci/evergreen" 8 "github.com/evergreen-ci/evergreen/db" 9 "github.com/evergreen-ci/evergreen/db/bsonutil" 10 "github.com/evergreen-ci/evergreen/model/distro" 11 "github.com/evergreen-ci/evergreen/util" 12 "gopkg.in/mgo.v2" 13 "gopkg.in/mgo.v2/bson" 14 ) 15 16 const ( 17 // Collection is the name of the MongoDB collection that stores hosts. 18 Collection = "hosts" 19 ) 20 21 var ( 22 IdKey = bsonutil.MustHaveTag(Host{}, "Id") 23 DNSKey = bsonutil.MustHaveTag(Host{}, "Host") 24 SecretKey = bsonutil.MustHaveTag(Host{}, "Secret") 25 UserKey = bsonutil.MustHaveTag(Host{}, "User") 26 TagKey = bsonutil.MustHaveTag(Host{}, "Tag") 27 DistroKey = bsonutil.MustHaveTag(Host{}, "Distro") 28 ProviderKey = bsonutil.MustHaveTag(Host{}, "Provider") 29 ProvisionedKey = bsonutil.MustHaveTag(Host{}, "Provisioned") 30 RunningTaskKey = bsonutil.MustHaveTag(Host{}, "RunningTask") 31 PidKey = bsonutil.MustHaveTag(Host{}, "Pid") 32 TaskDispatchTimeKey = bsonutil.MustHaveTag(Host{}, "TaskDispatchTime") 33 CreateTimeKey = bsonutil.MustHaveTag(Host{}, "CreationTime") 34 ExpirationTimeKey = bsonutil.MustHaveTag(Host{}, "ExpirationTime") 35 TerminationTimeKey = bsonutil.MustHaveTag(Host{}, "TerminationTime") 36 LTCTimeKey = bsonutil.MustHaveTag(Host{}, "LastTaskCompletedTime") 37 LTCKey = bsonutil.MustHaveTag(Host{}, "LastTaskCompleted") 38 StatusKey = bsonutil.MustHaveTag(Host{}, "Status") 39 AgentRevisionKey = bsonutil.MustHaveTag(Host{}, "AgentRevision") 40 StartedByKey = bsonutil.MustHaveTag(Host{}, "StartedBy") 41 InstanceTypeKey = bsonutil.MustHaveTag(Host{}, "InstanceType") 42 NotificationsKey = bsonutil.MustHaveTag(Host{}, "Notifications") 43 UserDataKey = bsonutil.MustHaveTag(Host{}, "UserData") 44 LastReachabilityCheckKey = bsonutil.MustHaveTag(Host{}, "LastReachabilityCheck") 45 LastCommunicationTimeKey = bsonutil.MustHaveTag(Host{}, "LastCommunicationTime") 46 UnreachableSinceKey = bsonutil.MustHaveTag(Host{}, "UnreachableSince") 47 ) 48 49 // === Queries === 50 51 // All is a query that returns all hosts 52 var All = db.Query(nil) 53 54 // ByUserWithRunningStatus produces a query that returns all 55 // running hosts for the given user id. 56 func ByUserWithRunningStatus(user string) db.Q { 57 return db.Query( 58 bson.M{ 59 StartedByKey: user, 60 StatusKey: bson.M{"$ne": evergreen.HostTerminated}, 61 }) 62 } 63 64 // IsRunning is a query that returns all hosts that are running 65 // (i.e. status != terminated). 66 var IsRunning = db.Query(bson.M{StatusKey: bson.M{"$ne": evergreen.HostTerminated}}) 67 68 // IsLive is a query that returns all working hosts started by Evergreen 69 var IsLive = db.Query( 70 bson.M{ 71 StartedByKey: evergreen.User, 72 StatusKey: bson.M{"$in": evergreen.UphostStatus}, 73 }, 74 ) 75 76 // ByUserWithUnterminatedStatus produces a query that returns all running hosts 77 // for the given user id. 78 func ByUserWithUnterminatedStatus(user string) db.Q { 79 return db.Query( 80 bson.M{ 81 StartedByKey: user, 82 StatusKey: bson.M{"$ne": evergreen.HostTerminated}, 83 }, 84 ) 85 } 86 87 // IsAvailableAndFree is a query that returns all running 88 // Evergreen hosts without an assigned task. 89 var IsAvailableAndFree = db.Query( 90 bson.M{ 91 RunningTaskKey: bson.M{"$exists": false}, 92 StatusKey: evergreen.HostRunning, 93 StartedByKey: evergreen.User, 94 }, 95 ).Sort([]string{"-" + LTCTimeKey}) 96 97 // ByAvailableForDistro returns all running Evergreen hosts with 98 // no running task of a certain distro Id. 99 func ByAvailableForDistro(d string) db.Q { 100 distroIdKey := fmt.Sprintf("%v.%v", DistroKey, distro.IdKey) 101 return db.Query(bson.M{ 102 distroIdKey: d, 103 RunningTaskKey: bson.M{"$exists": false}, 104 StatusKey: evergreen.HostRunning, 105 StartedByKey: evergreen.User, 106 }).Sort([]string{"-" + LTCTimeKey}) 107 } 108 109 // IsFree is a query that returns all running 110 // Evergreen hosts without an assigned task. 111 var IsFree = db.Query( 112 bson.M{ 113 RunningTaskKey: bson.M{"$exists": false}, 114 StartedByKey: evergreen.User, 115 StatusKey: evergreen.HostRunning, 116 }, 117 ) 118 119 // ByUnprovisionedSince produces a query that returns all hosts 120 // Evergreen never finished setting up that were created before 121 // the given time. 122 func ByUnprovisionedSince(threshold time.Time) db.Q { 123 return db.Query(bson.M{ 124 ProvisionedKey: false, 125 CreateTimeKey: bson.M{"$lte": threshold}, 126 StatusKey: bson.M{"$ne": evergreen.HostTerminated}, 127 StartedByKey: evergreen.User, 128 }) 129 } 130 131 // IsUninitialized is a query that returns all uninitialized Evergreen hosts. 132 var IsUninitialized = db.Query( 133 bson.M{StatusKey: evergreen.HostUninitialized}, 134 ) 135 136 // ByUnproductiveSince produces a query that returns all hosts that 137 // are not doing work and were created before the given time. 138 func ByUnproductiveSince(threshold time.Time) db.Q { 139 return db.Query(bson.M{ 140 RunningTaskKey: bson.M{"$exists": false}, 141 LTCKey: "", 142 CreateTimeKey: bson.M{"$lte": threshold}, 143 StatusKey: bson.M{"$ne": evergreen.HostTerminated}, 144 StartedByKey: evergreen.User, 145 }) 146 } 147 148 // IsRunningAndSpawned is a query that returns all running hosts 149 // spawned by an Evergreen user. 150 var IsRunningAndSpawned = db.Query( 151 bson.M{ 152 StartedByKey: bson.M{"$ne": evergreen.User}, 153 StatusKey: bson.M{"$ne": evergreen.HostTerminated}, 154 }, 155 ) 156 157 // IsRunningTask is a query that returns all running hosts with a running task 158 var IsRunningTask = db.Query( 159 bson.M{ 160 RunningTaskKey: bson.M{"$exists": true}, 161 }, 162 ) 163 164 // IsDecommissioned is a query that returns all hosts without a 165 // running task that are marked for decommissioning. 166 var IsDecommissioned = db.Query( 167 bson.M{ 168 RunningTaskKey: bson.M{"$exists": false}, 169 StatusKey: evergreen.HostDecommissioned}, 170 ) 171 172 // ByDistroId produces a query that returns all working hosts (not terminated and 173 // not quarantined) of the given distro. 174 func ByDistroId(distroId string) db.Q { 175 dId := fmt.Sprintf("%v.%v", DistroKey, distro.IdKey) 176 return db.Query(bson.M{ 177 dId: distroId, 178 StartedByKey: evergreen.User, 179 StatusKey: bson.M{"$in": evergreen.UphostStatus}, 180 }) 181 } 182 183 // ById produces a query that returns a host with the given id. 184 func ById(id string) db.Q { 185 return db.Query(bson.D{{IdKey, id}}) 186 } 187 188 // ByIds produces a query that returns all hosts in the given list of ids. 189 func ByIds(ids []string) db.Q { 190 return db.Query(bson.D{ 191 {IdKey, bson.D{{"$in", ids}}}, 192 }) 193 } 194 195 // ByRunningTaskId returns a host running the task with the given id. 196 func ByRunningTaskId(taskId string) db.Q { 197 return db.Query(bson.D{{RunningTaskKey, taskId}}) 198 } 199 200 // ByDynamicWithinTime is a query that returns all dynamic hosts running between a certain time and another time. 201 func ByDynamicWithinTime(startTime, endTime time.Time) db.Q { 202 return db.Query( 203 bson.M{ 204 "$or": []bson.M{ 205 bson.M{ 206 CreateTimeKey: bson.M{"$lt": endTime}, 207 TerminationTimeKey: bson.M{"$gt": startTime}, 208 ProviderKey: bson.M{"$ne": evergreen.HostTypeStatic}, 209 }, 210 bson.M{ 211 CreateTimeKey: bson.M{"$lt": endTime}, 212 TerminationTimeKey: util.ZeroTime, 213 StatusKey: evergreen.HostRunning, 214 ProviderKey: bson.M{"$ne": evergreen.HostTypeStatic}, 215 }, 216 }, 217 }) 218 } 219 220 var AllStatic = db.Query( 221 bson.M{ 222 ProviderKey: evergreen.HostTypeStatic, 223 }) 224 225 // IsIdle is a query that returns all running Evergreen hosts with no task. 226 var IsIdle = db.Query( 227 bson.M{ 228 RunningTaskKey: bson.M{"$exists": false}, 229 StatusKey: evergreen.HostRunning, 230 StartedByKey: evergreen.User, 231 }, 232 ) 233 234 // IsActive is a query that returns all Evergreen hosts that are working or 235 // capable of being assigned work to do. 236 var IsActive = db.Query( 237 bson.M{ 238 StartedByKey: evergreen.User, 239 StatusKey: bson.M{ 240 "$nin": []string{ 241 evergreen.HostTerminated, evergreen.HostDecommissioned, evergreen.HostInitializing, 242 }, 243 }, 244 }, 245 ) 246 247 // ByNotMonitoredSince produces a query that returns all hosts whose 248 // last reachability check was before the specified threshold, 249 // filtering out user-spawned hosts and hosts currently running tasks. 250 func ByNotMonitoredSince(threshold time.Time) db.Q { 251 return db.Query(bson.M{ 252 "$and": []bson.M{ 253 {RunningTaskKey: bson.M{"$exists": false}}, 254 {StatusKey: bson.M{ 255 "$in": []string{evergreen.HostRunning, evergreen.HostUnreachable}, 256 }}, 257 {StartedByKey: evergreen.User}, 258 {"$or": []bson.M{ 259 {LastReachabilityCheckKey: bson.M{"$lte": threshold}}, 260 {LastReachabilityCheckKey: bson.M{"$exists": false}}, 261 }}, 262 }, 263 }) 264 } 265 266 // ByExpiringBetween produces a query that returns any user-spawned hosts 267 // that will expire between the specified times. 268 func ByExpiringBetween(lowerBound time.Time, upperBound time.Time) db.Q { 269 return db.Query(bson.M{ 270 StartedByKey: bson.M{"$ne": evergreen.User}, 271 StatusKey: bson.M{ 272 "$nin": []string{evergreen.HostTerminated, evergreen.HostQuarantined}, 273 }, 274 ExpirationTimeKey: bson.M{"$gte": lowerBound, "$lte": upperBound}, 275 }) 276 } 277 278 // ByUnreachableBefore produces a query that returns a list of all 279 // hosts that are still unreachable, and have been in that state since before the 280 // given time threshold. 281 func ByUnreachableBefore(threshold time.Time) db.Q { 282 return db.Query(bson.M{ 283 StatusKey: evergreen.HostUnreachable, 284 UnreachableSinceKey: bson.M{"$gt": time.Unix(0, 0), "$lt": threshold}, 285 }) 286 } 287 288 // ByExpiredSicne produces a query that returns any user-spawned hosts 289 // that will expired after the given time. 290 func ByExpiredSince(time time.Time) db.Q { 291 return db.Query(bson.M{ 292 StartedByKey: bson.M{"$ne": evergreen.User}, 293 StatusKey: bson.M{ 294 "$nin": []string{evergreen.HostTerminated, evergreen.HostQuarantined}, 295 }, 296 ExpirationTimeKey: bson.M{"$lte": time}, 297 }) 298 } 299 300 // IsProvisioningFailure is a query that returns all hosts that 301 // failed to provision. 302 var IsProvisioningFailure = db.Query(bson.D{{StatusKey, evergreen.HostProvisionFailed}}) 303 304 // ByRunningWithTimedOutLCT returns hosts that are running and either have no Last Commmunication Time 305 // or have one that exists that is greater than the MaxLTCInterval duration away from the current time. 306 func ByRunningWithTimedOutLCT(currentTime time.Time) db.Q { 307 cutoffTime := currentTime.Add(-MaxLCTInterval) 308 return db.Query(bson.M{ 309 StatusKey: evergreen.HostRunning, 310 StartedByKey: evergreen.User, 311 "$or": []bson.M{ 312 {LastCommunicationTimeKey: util.ZeroTime}, 313 {LastCommunicationTimeKey: bson.M{"$lte": cutoffTime}}, 314 {LastCommunicationTimeKey: bson.M{"$exists": false}}, 315 }, 316 }) 317 } 318 319 // === DB Logic === 320 321 // FindOne gets one Host for the given query. 322 func FindOne(query db.Q) (*Host, error) { 323 host := &Host{} 324 err := db.FindOneQ(Collection, query, host) 325 if err == mgo.ErrNotFound { 326 return nil, nil 327 } 328 return host, err 329 } 330 331 // Find gets all Hosts for the given query. 332 func Find(query db.Q) ([]Host, error) { 333 hosts := []Host{} 334 err := db.FindAllQ(Collection, query, &hosts) 335 return hosts, err 336 } 337 338 // Count returns the number of hosts that satisfy the given query. 339 func Count(query db.Q) (int, error) { 340 return db.CountQ(Collection, query) 341 } 342 343 // UpdateOne updates one host. 344 func UpdateOne(query interface{}, update interface{}) error { 345 return db.Update( 346 Collection, 347 query, 348 update, 349 ) 350 } 351 352 // UpdateAll updates all hosts. 353 func UpdateAll(query interface{}, update interface{}) error { 354 _, err := db.UpdateAll( 355 Collection, 356 query, 357 update, 358 ) 359 return err 360 } 361 362 // UpsertOne upserts a host. 363 func UpsertOne(query interface{}, update interface{}) (*mgo.ChangeInfo, error) { 364 return db.Upsert( 365 Collection, 366 query, 367 update, 368 ) 369 } 370 371 func GetHostsByFromIdWithStatus(id, status string, limit, sortDir int) ([]Host, error) { 372 pipeline := geHostsFromIdWithStatusPipeline(id, status, limit, sortDir) 373 hostRes := []Host{} 374 err := db.Aggregate(Collection, pipeline, &hostRes) 375 if err != nil { 376 return nil, err 377 } 378 return hostRes, nil 379 } 380 381 func geHostsFromIdWithStatusPipeline(id, status string, limit, sortDir int) []bson.M { 382 sortOperator := "$gte" 383 if sortDir < 0 { 384 sortOperator = "$lte" 385 } 386 pipeline := []bson.M{ 387 {"$match": bson.M{IdKey: bson.M{sortOperator: id}}}, 388 } 389 if status != "" { 390 statusMatch := bson.M{ 391 "$match": bson.M{StatusKey: status}, 392 } 393 pipeline = append(pipeline, statusMatch) 394 } 395 if limit > 0 { 396 limitStage := bson.M{ 397 "$limit": limit, 398 } 399 pipeline = append(pipeline, limitStage) 400 } 401 return pipeline 402 }