From 3cd841051b69708060c83443eec4d7e4fec4e81b Mon Sep 17 00:00:00 2001 From: Zachary Wasserman Date: Fri, 18 Nov 2016 09:23:44 -0800 Subject: [PATCH] Add online count to target/label responses (#513) Closes #508 --- server/kolide/{target.go => targets.go} | 5 ++- server/service/endpoint_labels.go | 16 +++++---- server/service/endpoint_targets.go | 28 +++++++++------- server/service/service_targets.go | 25 +++++++++----- server/service/service_targets_test.go | 44 ++++++++++++++++++------- 5 files changed, 79 insertions(+), 39 deletions(-) rename server/kolide/{target.go => targets.go} (68%) diff --git a/server/kolide/target.go b/server/kolide/targets.go similarity index 68% rename from server/kolide/target.go rename to server/kolide/targets.go index 1aa53ac79a..4df4903c72 100644 --- a/server/kolide/target.go +++ b/server/kolide/targets.go @@ -15,7 +15,10 @@ type TargetService interface { // (hosts and label) which match the supplied search query. SearchTargets(ctx context.Context, query string, selectedHostIDs []uint, selectedLabelIDs []uint) (*TargetSearchResults, error) - CountHostsInTargets(ctx context.Context, hosts []uint, labels []uint) (uint, error) + // CountHostsInTargets returns the count of hosts in the selected + // targets. The first return uint is the total number of hosts in the + // targets. The second return uint is the total online hosts. + CountHostsInTargets(ctx context.Context, hostIDs []uint, labelIDs []uint) (uint, uint, error) } type TargetType int diff --git a/server/service/endpoint_labels.go b/server/service/endpoint_labels.go index 266fed0a86..d42cb53adf 100644 --- a/server/service/endpoint_labels.go +++ b/server/service/endpoint_labels.go @@ -18,6 +18,7 @@ type labelResponse struct { kolide.Label DisplayText string `json:"display_text"` Count uint `json:"count"` + Online uint `json:"online"` } type getLabelResponse struct { @@ -34,7 +35,7 @@ func makeGetLabelEndpoint(svc kolide.Service) endpoint.Endpoint { if err != nil { return getLabelResponse{Err: err}, nil } - count, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) + total, online, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) if err != nil { return getLabelResponse{Err: err}, nil } @@ -42,7 +43,8 @@ func makeGetLabelEndpoint(svc kolide.Service) endpoint.Endpoint { Label: labelResponse{ *label, label.Name, - count, + total, + online, }, }, nil } @@ -73,7 +75,7 @@ func makeListLabelsEndpoint(svc kolide.Service) endpoint.Endpoint { resp := listLabelsResponse{} for _, label := range labels { - count, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) + total, online, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) if err != nil { return listLabelsResponse{Err: err}, nil } @@ -81,7 +83,8 @@ func makeListLabelsEndpoint(svc kolide.Service) endpoint.Endpoint { labelResponse{ *label, label.Name, - count, + total, + online, }, ) } @@ -111,7 +114,7 @@ func makeCreateLabelEndpoint(svc kolide.Service) endpoint.Endpoint { if err != nil { return createLabelResponse{Err: err}, nil } - count, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) + total, online, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) if err != nil { return createLabelResponse{Err: err}, nil } @@ -119,7 +122,8 @@ func makeCreateLabelEndpoint(svc kolide.Service) endpoint.Endpoint { Label: labelResponse{ *label, label.Name, - count, + total, + online, }, }, nil } diff --git a/server/service/endpoint_targets.go b/server/service/endpoint_targets.go index bdc84a79db..097d89f5ab 100644 --- a/server/service/endpoint_targets.go +++ b/server/service/endpoint_targets.go @@ -27,6 +27,7 @@ type labelSearchResult struct { kolide.Label DisplayText string `json:"display_text"` Count uint `json:"count"` + Online uint `json:"online"` } type targetsData struct { @@ -35,9 +36,10 @@ type targetsData struct { } type searchTargetsResponse struct { - Targets *targetsData `json:"targets,omitempty"` - SelectedTargetsCount uint `json:"selected_targets_count,omitempty"` - Err error `json:"error,omitempty"` + Targets *targetsData `json:"targets,omitempty"` + SelectedTargetsCount uint `json:"selected_targets_count"` + SelectedTargetsOnline uint `json:"selected_targets_online"` + Err error `json:"error,omitempty"` } func (r searchTargetsResponse) error() error { return r.Err } @@ -51,11 +53,6 @@ func makeSearchTargetsEndpoint(svc kolide.Service) endpoint.Endpoint { return searchTargetsResponse{Err: err}, nil } - count, err := svc.CountHostsInTargets(ctx, req.Selected.Hosts, req.Selected.Labels) - if err != nil { - return searchTargetsResponse{Err: err}, nil - } - targets := &targetsData{ Hosts: []hostSearchResult{}, Labels: []labelSearchResult{}, @@ -71,7 +68,7 @@ func makeSearchTargetsEndpoint(svc kolide.Service) endpoint.Endpoint { } for _, label := range results.Labels { - count, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) + total, online, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) if err != nil { return searchTargetsResponse{Err: err}, nil } @@ -79,14 +76,21 @@ func makeSearchTargetsEndpoint(svc kolide.Service) endpoint.Endpoint { labelSearchResult{ label, label.Name, - count, + total, + online, }, ) } + total, online, err := svc.CountHostsInTargets(ctx, req.Selected.Hosts, req.Selected.Labels) + if err != nil { + return searchTargetsResponse{Err: err}, nil + } + return searchTargetsResponse{ - Targets: targets, - SelectedTargetsCount: count, + Targets: targets, + SelectedTargetsCount: total, + SelectedTargetsOnline: online, }, nil } } diff --git a/server/service/service_targets.go b/server/service/service_targets.go index 5b9955f272..69c85c1e09 100644 --- a/server/service/service_targets.go +++ b/server/service/service_targets.go @@ -23,23 +23,30 @@ func (svc service) SearchTargets(ctx context.Context, query string, selectedHost return results, nil } -func (svc service) CountHostsInTargets(ctx context.Context, hosts []uint, labels []uint) (uint, error) { - hostsInLabels, err := svc.ds.ListUniqueHostsInLabels(labels) +func (svc service) CountHostsInTargets(ctx context.Context, hostIDs []uint, labelIDs []uint) (uint, uint, error) { + hosts, err := svc.ds.ListUniqueHostsInLabels(labelIDs) if err != nil { - return 0, err + return 0, 0, err + } + + for _, id := range hostIDs { + h, err := svc.ds.Host(id) + if err != nil { + return 0, 0, err + } + hosts = append(hosts, *h) } hostLookup := map[uint]bool{} - + online := uint(0) for _, host := range hosts { - hostLookup[host] = true - } - - for _, host := range hostsInLabels { if !hostLookup[host.ID] { hostLookup[host.ID] = true + if svc.HostStatus(ctx, host) == "online" { + online++ + } } } - return uint(len(hostLookup)), nil + return uint(len(hostLookup)), online, nil } diff --git a/server/service/service_targets_test.go b/server/service/service_targets_test.go index 2dcd299a85..891f88c4ba 100644 --- a/server/service/service_targets_test.go +++ b/server/service/service_targets_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/WatchBeam/clock" "github.com/kolide/kolide-ose/server/datastore/inmem" "github.com/kolide/kolide-ose/server/kolide" "github.com/stretchr/testify/assert" @@ -47,7 +48,9 @@ func TestCountHostsInTargets(t *testing.T) { ds, err := inmem.New() require.Nil(t, err) - svc, err := newTestService(ds, nil) + mockClock := clock.NewMockClock() + + svc, err := newTestServiceWithClock(ds, nil, mockClock) require.Nil(t, err) ctx := context.Background() @@ -59,6 +62,7 @@ func TestCountHostsInTargets(t *testing.T) { UUID: "1", }) require.Nil(t, err) + require.Nil(t, ds.MarkHostSeen(h1, mockClock.Now())) h2, err := ds.NewHost(&kolide.Host{ HostName: "bar.local", @@ -67,6 +71,8 @@ func TestCountHostsInTargets(t *testing.T) { UUID: "2", }) require.Nil(t, err) + // make this host "offline" + require.Nil(t, ds.MarkHostSeen(h2, mockClock.Now().Add(-1*time.Hour))) h3, err := ds.NewHost(&kolide.Host{ HostName: "baz.local", @@ -75,6 +81,7 @@ func TestCountHostsInTargets(t *testing.T) { UUID: "3", }) require.Nil(t, err) + require.Nil(t, ds.MarkHostSeen(h3, mockClock.Now().Add(-5*time.Minute))) h4, err := ds.NewHost(&kolide.Host{ HostName: "xxx.local", @@ -83,6 +90,7 @@ func TestCountHostsInTargets(t *testing.T) { UUID: "4", }) require.Nil(t, err) + require.Nil(t, ds.MarkHostSeen(h4, mockClock.Now())) h5, err := ds.NewHost(&kolide.Host{ HostName: "yyy.local", @@ -91,6 +99,7 @@ func TestCountHostsInTargets(t *testing.T) { UUID: "5", }) require.Nil(t, err) + require.Nil(t, ds.MarkHostSeen(h5, mockClock.Now())) l1, err := ds.NewLabel(&kolide.Label{ Name: "label foo", @@ -118,25 +127,38 @@ func TestCountHostsInTargets(t *testing.T) { assert.Nil(t, err) } - count, err := svc.CountHostsInTargets(ctx, nil, []uint{l1.ID, l2.ID}) + total, online, err := svc.CountHostsInTargets(ctx, nil, []uint{l1.ID, l2.ID}) assert.Nil(t, err) - assert.Equal(t, uint(5), count) + assert.Equal(t, uint(5), total) + assert.Equal(t, uint(4), online) - count, err = svc.CountHostsInTargets(ctx, []uint{h1.ID, h2.ID}, []uint{l1.ID, l2.ID}) + total, online, err = svc.CountHostsInTargets(ctx, []uint{h1.ID, h2.ID}, []uint{l1.ID, l2.ID}) assert.Nil(t, err) - assert.Equal(t, uint(5), count) + assert.Equal(t, uint(5), total) + assert.Equal(t, uint(4), online) - count, err = svc.CountHostsInTargets(ctx, []uint{h1.ID, h2.ID}, nil) + total, online, err = svc.CountHostsInTargets(ctx, []uint{h1.ID, h2.ID}, nil) assert.Nil(t, err) - assert.Equal(t, uint(2), count) + assert.Equal(t, uint(2), total) + assert.Equal(t, uint(1), online) - count, err = svc.CountHostsInTargets(ctx, []uint{h1.ID}, []uint{l2.ID}) + total, online, err = svc.CountHostsInTargets(ctx, []uint{h1.ID}, []uint{l2.ID}) assert.Nil(t, err) - assert.Equal(t, uint(4), count) + assert.Equal(t, uint(4), total) + assert.Equal(t, uint(4), online) - count, err = svc.CountHostsInTargets(ctx, nil, nil) + total, online, err = svc.CountHostsInTargets(ctx, nil, nil) assert.Nil(t, err) - assert.Equal(t, uint(0), count) + assert.Equal(t, uint(0), total) + assert.Equal(t, uint(0), online) + + // Advance clock so all hosts are offline + mockClock.AddTime(1 * time.Hour) + total, online, err = svc.CountHostsInTargets(ctx, nil, []uint{l1.ID, l2.ID}) + assert.Nil(t, err) + assert.Equal(t, uint(5), total) + assert.Equal(t, uint(0), online) + } func TestSearchWithOmit(t *testing.T) {