diff --git a/server/datastore/datastore_hosts_test.go b/server/datastore/datastore_hosts_test.go index 68e77c47b0..abb4589d45 100644 --- a/server/datastore/datastore_hosts_test.go +++ b/server/datastore/datastore_hosts_test.go @@ -591,7 +591,7 @@ func testGenerateHostStatusStatistics(t *testing.T, ds kolide.Datastore) { // With an online interval of 60, both the host that checked in a minute ago // as well as the host that checked in 30 seconds ago should both be online - online, offline, mia, new, err = ds.GenerateHostStatusStatistics(mockClock.Now(), 60) + online, offline, mia, new, err = ds.GenerateHostStatusStatistics(mockClock.Now(), 2*time.Minute) assert.Nil(t, err) assert.Equal(t, uint(2), online) assert.Equal(t, uint(1), offline) @@ -599,7 +599,7 @@ func testGenerateHostStatusStatistics(t *testing.T, ds kolide.Datastore) { assert.Equal(t, uint(4), new) // With an online interval of 10, no hosts should be online - online, offline, mia, new, err = ds.GenerateHostStatusStatistics(mockClock.Now(), 10) + online, offline, mia, new, err = ds.GenerateHostStatusStatistics(mockClock.Now(), 20*time.Second) assert.Nil(t, err) assert.Equal(t, uint(0), online) assert.Equal(t, uint(3), offline) @@ -609,7 +609,7 @@ func testGenerateHostStatusStatistics(t *testing.T, ds kolide.Datastore) { // With an online interval of 3600 seconds (60 minutes), the host that checked // in 30 seconds ago, a minute ago, and 60 minutes ago should all appear to be // online - online, offline, mia, new, err = ds.GenerateHostStatusStatistics(mockClock.Now(), 60*60) + online, offline, mia, new, err = ds.GenerateHostStatusStatistics(mockClock.Now(), 2*time.Hour) assert.Nil(t, err) assert.Equal(t, uint(3), online) assert.Equal(t, uint(0), offline) diff --git a/server/datastore/inmem/hosts.go b/server/datastore/inmem/hosts.go index d9336cd307..61a7210f1f 100644 --- a/server/datastore/inmem/hosts.go +++ b/server/datastore/inmem/hosts.go @@ -113,7 +113,7 @@ func (d *Datastore) ListHosts(opt kolide.ListOptions) ([]*kolide.Host, error) { return hosts, nil } -func (d *Datastore) GenerateHostStatusStatistics(now time.Time, onlineInterval uint) (online, offline, mia, new uint, err error) { +func (d *Datastore) GenerateHostStatusStatistics(now time.Time, onlineInterval time.Duration) (online, offline, mia, new uint, err error) { d.mtx.Lock() defer d.mtx.Unlock() @@ -122,7 +122,7 @@ func (d *Datastore) GenerateHostStatusStatistics(now time.Time, onlineInterval u new++ } - status := host.Status(now) + status := host.Status(now, onlineInterval) switch status { case kolide.StatusMIA: mia++ diff --git a/server/datastore/mysql/hosts.go b/server/datastore/mysql/hosts.go index dcf21365ca..0bd0d94dd7 100644 --- a/server/datastore/mysql/hosts.go +++ b/server/datastore/mysql/hosts.go @@ -290,7 +290,7 @@ func (d *Datastore) ListHosts(opt kolide.ListOptions) ([]*kolide.Host, error) { return hosts, nil } -func (d *Datastore) GenerateHostStatusStatistics(now time.Time, onlineInterval uint) (online, offline, mia, new uint, e error) { +func (d *Datastore) GenerateHostStatusStatistics(now time.Time, onlineInterval time.Duration) (online, offline, mia, new uint, e error) { sqlStatement := ` SELECT ( SELECT count(id) @@ -323,7 +323,7 @@ func (d *Datastore) GenerateHostStatusStatistics(now time.Time, onlineInterval u Online uint `db:"online"` New uint `db:"new"` }{} - err := d.db.Get(&counts, sqlStatement, now, 2*onlineInterval, now, now, 2*onlineInterval, now, now) + err := d.db.Get(&counts, sqlStatement, now, onlineInterval.Seconds(), now, now, onlineInterval.Seconds(), now, now) if err != nil && err != sql.ErrNoRows { e = errors.Wrap(err, "generating host statistics") return diff --git a/server/kolide/hosts.go b/server/kolide/hosts.go index 10b9dabe9f..a42f755858 100644 --- a/server/kolide/hosts.go +++ b/server/kolide/hosts.go @@ -22,10 +22,6 @@ const ( // considered new. NewDuration = 24 * time.Hour - // OfflineDuration if a host hasn't been in communition for this - // period it is considered offline. - OfflineDuration = 30 * time.Minute - // OfflineDuration if a host hasn't been in communition for this // period it is considered MIA. MIADuration = 30 * 24 * time.Hour @@ -40,7 +36,7 @@ type HostStore interface { EnrollHost(osqueryHostId string, nodeKeySize int) (*Host, error) AuthenticateHost(nodeKey string) (*Host, error) MarkHostSeen(host *Host, t time.Time) error - GenerateHostStatusStatistics(now time.Time, onlineInterval uint) (online, offline, mia, new uint, err error) + GenerateHostStatusStatistics(now time.Time, onlineInterval time.Duration) (online, offline, mia, new uint, err error) SearchHosts(query string, omit ...uint) ([]*Host, error) // DistributedQueriesForHost retrieves the distributed queries that the // given host should run. The result map is a mapping from campaign ID @@ -147,11 +143,11 @@ func RandomText(keySize int) (string, error) { return base64.StdEncoding.EncodeToString(key), nil } -func (h *Host) Status(now time.Time) string { +func (h *Host) Status(now time.Time, onlineInterval time.Duration) string { switch { case h.SeenTime.Add(MIADuration).Before(now): return StatusMIA - case h.SeenTime.Add(OfflineDuration).Before(now): + case h.SeenTime.Add(onlineInterval).Before(now): return StatusOffline default: return StatusOnline diff --git a/server/kolide/hosts_test.go b/server/kolide/hosts_test.go index 1058d219f5..354d01ac60 100644 --- a/server/kolide/hosts_test.go +++ b/server/kolide/hosts_test.go @@ -50,16 +50,16 @@ func TestHostStatus(t *testing.T) { host := Host{} host.SeenTime = mockClock.Now() - assert.Equal(t, StatusOnline, host.Status(mockClock.Now())) + assert.Equal(t, StatusOnline, host.Status(mockClock.Now(), 60*time.Second)) host.SeenTime = mockClock.Now().Add(-1 * time.Minute) - assert.Equal(t, StatusOnline, host.Status(mockClock.Now())) + assert.Equal(t, StatusOnline, host.Status(mockClock.Now(), 60*time.Second)) host.SeenTime = mockClock.Now().Add(-1 * time.Hour) - assert.Equal(t, StatusOffline, host.Status(mockClock.Now())) + assert.Equal(t, StatusOffline, host.Status(mockClock.Now(), 60*time.Second)) host.SeenTime = mockClock.Now().Add(-35 * (24 * time.Hour)) // 35 days - assert.Equal(t, StatusMIA, host.Status(mockClock.Now())) + assert.Equal(t, StatusMIA, host.Status(mockClock.Now(), 60*time.Second)) } func TestHostIsNew(t *testing.T) { diff --git a/server/kolide/options.go b/server/kolide/options.go index 191e7e987b..6f6eb1ccf7 100644 --- a/server/kolide/options.go +++ b/server/kolide/options.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "strings" + "time" "golang.org/x/net/context" ) @@ -40,7 +41,7 @@ type OptionService interface { // can deduce a minimum amount of time that we should expect to hear from an // osqueryd agent if it is online. This is currently two times the most // frequent check-in interval. - ExpectedCheckinInterval(ctx context.Context) (uint, error) + ExpectedCheckinInterval(ctx context.Context) (time.Duration, error) } const ( diff --git a/server/service/endpoint_hosts.go b/server/service/endpoint_hosts.go index 853fd105f6..daf1d8bd78 100644 --- a/server/service/endpoint_hosts.go +++ b/server/service/endpoint_hosts.go @@ -5,6 +5,7 @@ import ( "github.com/go-kit/kit/endpoint" "github.com/kolide/kolide/server/kolide" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -15,9 +16,13 @@ type hostResponse struct { } func hostResponseForHost(ctx context.Context, svc kolide.Service, host *kolide.Host) (*hostResponse, error) { + onlineInterval, err := svc.ExpectedCheckinInterval(ctx) + if err != nil { + return nil, errors.Wrap(err, "getting expected check-in interval") + } return &hostResponse{ Host: *host, - Status: host.Status(time.Now()), + Status: host.Status(time.Now(), onlineInterval), DisplayText: host.HostName, }, nil } diff --git a/server/service/endpoint_targets.go b/server/service/endpoint_targets.go index 6bba0429d7..9e09b5f36c 100644 --- a/server/service/endpoint_targets.go +++ b/server/service/endpoint_targets.go @@ -5,6 +5,7 @@ import ( "github.com/go-kit/kit/endpoint" "github.com/kolide/kolide/server/kolide" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -64,12 +65,17 @@ func makeSearchTargetsEndpoint(svc kolide.Service) endpoint.Endpoint { Labels: []labelSearchResult{}, } + onlineInterval, err := svc.ExpectedCheckinInterval(ctx) + if err != nil { + return searchTargetsResponse{Err: errors.Wrap(err, "getting expected check-in interval")}, nil + } + for _, host := range results.Hosts { targets.Hosts = append(targets.Hosts, hostSearchResult{ hostResponse{ Host: host, - Status: host.Status(time.Now()), + Status: host.Status(time.Now(), onlineInterval), }, host.HostName, }, diff --git a/server/service/service_options.go b/server/service/service_options.go index 18898fb2d0..7c2f0ae024 100644 --- a/server/service/service_options.go +++ b/server/service/service_options.go @@ -1,11 +1,15 @@ package service import ( + "time" + "github.com/kolide/kolide/server/kolide" "github.com/pkg/errors" "golang.org/x/net/context" ) +const expectedCheckinIntervalMultiplier = 2 + func (svc service) GetOptions(ctx context.Context) ([]kolide.Option, error) { opts, err := svc.ds.ListOptions() if err != nil { @@ -21,7 +25,7 @@ func (svc service) ModifyOptions(ctx context.Context, req kolide.OptionRequest) return req.Options, nil } -func (svc service) ExpectedCheckinInterval(ctx context.Context) (uint, error) { +func (svc service) ExpectedCheckinInterval(ctx context.Context) (time.Duration, error) { interval := uint(0) found := false @@ -71,9 +75,9 @@ func (svc service) ExpectedCheckinInterval(ctx context.Context) (uint, error) { // if we never found any interval options set, the default distributed // interval is 60, so we use that if !found { - return 60, nil + interval = 60 } // return the lowest interval that we found - return interval, nil + return time.Duration(interval) * time.Second * expectedCheckinIntervalMultiplier, nil } diff --git a/server/service/service_options_test.go b/server/service/service_options_test.go index a3afdd7d39..05500bdb5e 100644 --- a/server/service/service_options_test.go +++ b/server/service/service_options_test.go @@ -3,6 +3,7 @@ package service import ( "context" "testing" + "time" "github.com/kolide/kolide/server/config" "github.com/kolide/kolide/server/datastore/inmem" @@ -44,7 +45,7 @@ func TestExpectedCheckinInterval(t *testing.T) { require.Equal(t, 10, int(loggerTlsPeriod)) interval, err := svc.ExpectedCheckinInterval(ctx) require.Nil(t, err) - assert.Equal(t, 10, int(interval)) + assert.Equal(t, 10*time.Second*expectedCheckinIntervalMultiplier, interval) options, err = svc.ModifyOptions(ctx, kolide.OptionRequest{ Options: []kolide.Option{ @@ -69,7 +70,7 @@ func TestExpectedCheckinInterval(t *testing.T) { require.Equal(t, 10, int(loggerTlsPeriod)) interval, err = svc.ExpectedCheckinInterval(ctx) require.Nil(t, err) - assert.Equal(t, 5, int(interval)) + assert.Equal(t, 5*time.Second*expectedCheckinIntervalMultiplier, interval) options, err = svc.ModifyOptions(ctx, kolide.OptionRequest{ Options: []kolide.Option{ @@ -94,5 +95,5 @@ func TestExpectedCheckinInterval(t *testing.T) { require.Equal(t, 1, int(loggerTlsPeriod)) interval, err = svc.ExpectedCheckinInterval(ctx) require.Nil(t, err) - assert.Equal(t, 1, int(interval)) + assert.Equal(t, time.Second*expectedCheckinIntervalMultiplier, interval) } diff --git a/server/service/service_targets.go b/server/service/service_targets.go index ffe1b122b9..1300d7881e 100644 --- a/server/service/service_targets.go +++ b/server/service/service_targets.go @@ -2,6 +2,7 @@ package service import ( "github.com/kolide/kolide/server/kolide" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -44,10 +45,15 @@ func (svc service) CountHostsInTargets(ctx context.Context, hostIDs []uint, labe result := &kolide.TargetMetrics{} + onlineInterval, err := svc.ExpectedCheckinInterval(ctx) + if err != nil { + return nil, errors.Wrap(err, "getting expected check-in interval") + } + for _, host := range hosts { if !hostLookup[host.ID] { hostLookup[host.ID] = true - switch host.Status(svc.clock.Now().UTC()) { + switch host.Status(svc.clock.Now().UTC(), onlineInterval) { case kolide.StatusOnline: result.OnlineHosts++ case kolide.StatusOffline: diff --git a/server/service/service_targets_test.go b/server/service/service_targets_test.go index 511d769924..5d94660db3 100644 --- a/server/service/service_targets_test.go +++ b/server/service/service_targets_test.go @@ -78,7 +78,7 @@ func TestCountHostsInTargets(t *testing.T) { UUID: "3", }) require.Nil(t, err) - require.Nil(t, ds.MarkHostSeen(h3, mockClock.Now().Add(-5*time.Minute))) + require.Nil(t, ds.MarkHostSeen(h3, mockClock.Now().Add(-5*time.Second))) h4, err := ds.NewHost(&kolide.Host{ HostName: "xxx.local",