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  }