From a2fded1b2c15f6887a6a2748f67c9be5ede2dbfe Mon Sep 17 00:00:00 2001 From: John Murphy Date: Tue, 17 Jan 2017 06:57:05 +0800 Subject: [PATCH] Added hosts for each label (#963) --- server/datastore/datastore_labels_test.go | 6 ++ server/datastore/inmem/labels.go | 15 +++-- server/datastore/mysql/labels.go | 12 ++-- server/kolide/labels.go | 3 + server/service/endpoint_labels.go | 70 ++++++++++------------- server/service/service_labels.go | 12 ++++ 6 files changed, 66 insertions(+), 52 deletions(-) diff --git a/server/datastore/datastore_labels_test.go b/server/datastore/datastore_labels_test.go index e1702ed0f2..ea6abfc5b5 100644 --- a/server/datastore/datastore_labels_test.go +++ b/server/datastore/datastore_labels_test.go @@ -176,6 +176,7 @@ func testManagingLabelsOnPacks(t *testing.T, ds kolide.Datastore) { labels, err = ds.ListLabelsForPack(monitoringPack.ID) require.Nil(t, err) assert.Len(t, labels, 2) + } func testSearchLabels(t *testing.T, db kolide.Datastore) { @@ -351,4 +352,9 @@ func testListUniqueHostsInLabels(t *testing.T, db kolide.Datastore) { uniqueHosts, err := db.ListUniqueHostsInLabels([]uint{l1.ID, l2.ID}) assert.Nil(t, err) assert.Equal(t, len(hosts), len(uniqueHosts)) + + labels, err := db.ListLabels(kolide.ListOptions{}) + require.Nil(t, err) + require.Len(t, labels, 2) + } diff --git a/server/datastore/inmem/labels.go b/server/datastore/inmem/labels.go index 1dfa1c50e6..8e51303a7a 100644 --- a/server/datastore/inmem/labels.go +++ b/server/datastore/inmem/labels.go @@ -29,10 +29,11 @@ func (d *Datastore) NewLabel(label *kolide.Label) (*kolide.Label, error) { } func (d *Datastore) ListLabelsForHost(hid uint) ([]kolide.Label, error) { + d.mtx.Lock() + defer d.mtx.Unlock() // First get IDs of label executions for the host resLabels := []kolide.Label{} - d.mtx.Lock() for _, lqe := range d.labelQueryExecutions { if lqe.HostID == hid && lqe.Matches { if label := d.labels[lqe.LabelID]; label != nil { @@ -40,16 +41,16 @@ func (d *Datastore) ListLabelsForHost(hid uint) ([]kolide.Label, error) { } } } - d.mtx.Unlock() return resLabels, nil } func (d *Datastore) LabelQueriesForHost(host *kolide.Host, cutoff time.Time) (map[string]string, error) { + d.mtx.Lock() + defer d.mtx.Unlock() // Get post-cutoff executions for host execedIDs := map[uint]bool{} - d.mtx.Lock() for _, lqe := range d.labelQueryExecutions { if lqe.HostID == host.ID && (lqe.UpdatedAt == cutoff || lqe.UpdatedAt.After(cutoff)) { execedIDs[lqe.LabelID] = true @@ -62,7 +63,6 @@ func (d *Datastore) LabelQueriesForHost(host *kolide.Host, cutoff time.Time) (ma queries[strconv.Itoa(int(label.ID))] = label.Query } } - d.mtx.Unlock() return queries, nil } @@ -141,11 +141,12 @@ func (d *Datastore) Label(lid uint) (*kolide.Label, error) { } func (d *Datastore) ListLabels(opt kolide.ListOptions) ([]*kolide.Label, error) { + d.mtx.Lock() + defer d.mtx.Unlock() // We need to sort by keys to provide reliable ordering keys := []int{} - d.mtx.Lock() - for k, _ := range d.labels { + for k := range d.labels { keys = append(keys, int(k)) } sort.Ints(keys) @@ -154,7 +155,6 @@ func (d *Datastore) ListLabels(opt kolide.ListOptions) ([]*kolide.Label, error) for _, k := range keys { labels = append(labels, d.labels[uint(k)]) } - d.mtx.Unlock() // Apply ordering if opt.OrderKey != "" { @@ -199,7 +199,6 @@ func (d *Datastore) SearchLabels(query string, omit ...uint) ([]kolide.Label, er } sortutil.AscByField(results, "ID") - return results, nil } diff --git a/server/datastore/mysql/labels.go b/server/datastore/mysql/labels.go index 994f874b7b..adc11e316e 100644 --- a/server/datastore/mysql/labels.go +++ b/server/datastore/mysql/labels.go @@ -54,13 +54,17 @@ func (d *Datastore) Label(lid uint) (*kolide.Label, error) { // ListLabels returns all labels limited or sorted by kolide.ListOptions func (d *Datastore) ListLabels(opt kolide.ListOptions) ([]*kolide.Label, error) { - sql := ` + query := ` SELECT * FROM labels WHERE NOT deleted ` - sql = appendListOptionsToSQL(sql, opt) + query = appendListOptionsToSQL(query, opt) labels := []*kolide.Label{} - if err := d.db.Select(&labels, sql); err != nil { + if err := d.db.Select(&labels, query); err != nil { + // it's ok if no labels exist + if err == sql.ErrNoRows { + return labels, nil + } return nil, errors.Wrap(err, "selecting labels") } @@ -150,6 +154,7 @@ func (d *Datastore) ListLabelsForHost(hid uint) ([]kolide.Label, error) { if err != nil { return nil, errors.Wrap(err, "selecting host labels") } + return labels, nil } @@ -274,6 +279,5 @@ func (d *Datastore) SearchLabels(query string, omit ...uint) ([]kolide.Label, er if err != nil { return nil, errors.Wrap(err, "selecting labels for search") } - return matches, nil } diff --git a/server/kolide/labels.go b/server/kolide/labels.go index 75d26a70c4..373da6c0dc 100644 --- a/server/kolide/labels.go +++ b/server/kolide/labels.go @@ -40,6 +40,9 @@ type LabelService interface { GetLabel(ctx context.Context, id uint) (label *Label, err error) NewLabel(ctx context.Context, p LabelPayload) (label *Label, err error) DeleteLabel(ctx context.Context, id uint) (err error) + // HostIDsForLabel returns ids of hosts that belong to the label identified + // by lid + HostIDsForLabel(lid uint) ([]uint, error) } type LabelPayload struct { diff --git a/server/service/endpoint_labels.go b/server/service/endpoint_labels.go index a6d4574e86..920738b236 100644 --- a/server/service/endpoint_labels.go +++ b/server/service/endpoint_labels.go @@ -6,10 +6,6 @@ import ( "golang.org/x/net/context" ) -//////////////////////////////////////////////////////////////////////////////// -// Get Label -//////////////////////////////////////////////////////////////////////////////// - type getLabelRequest struct { ID uint } @@ -21,6 +17,7 @@ type labelResponse struct { Online uint `json:"online"` Offline uint `json:"offline"` MissingInAction uint `json:"missing_in_action"` + HostIDs []uint `json:"host_ids,omitempty"` } type getLabelResponse struct { @@ -30,6 +27,29 @@ type getLabelResponse struct { func (r getLabelResponse) error() error { return r.Err } +func labelResponseForLabel(ctx context.Context, svc kolide.Service, label *kolide.Label) (*labelResponse, error) { + metrics, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) + if err != nil { + return nil, err + } + hosts, err := svc.HostIDsForLabel(label.ID) + if err != nil { + return nil, err + } + return &labelResponse{ + *label, + label.Name, + metrics.TotalHosts, + metrics.OnlineHosts, + metrics.OfflineHosts, + metrics.MissingInActionHosts, + hosts, + }, nil +} + +//////////////////////////////////////////////////////////////////////////////// +// Get Label +//////////////////////////////////////////////////////////////////////////////// func makeGetLabelEndpoint(svc kolide.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(getLabelRequest) @@ -37,20 +57,11 @@ func makeGetLabelEndpoint(svc kolide.Service) endpoint.Endpoint { if err != nil { return getLabelResponse{Err: err}, nil } - metrics, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) + resp, err := labelResponseForLabel(ctx, svc, label) if err != nil { return getLabelResponse{Err: err}, nil } - return getLabelResponse{ - Label: labelResponse{ - *label, - label.Name, - metrics.TotalHosts, - metrics.OnlineHosts, - metrics.OfflineHosts, - metrics.MissingInActionHosts, - }, - }, nil + return getLabelResponse{Label: *resp}, nil } } @@ -79,20 +90,11 @@ func makeListLabelsEndpoint(svc kolide.Service) endpoint.Endpoint { resp := listLabelsResponse{} for _, label := range labels { - metrics, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) + labelResp, err := labelResponseForLabel(ctx, svc, label) if err != nil { return listLabelsResponse{Err: err}, nil } - resp.Labels = append(resp.Labels, - labelResponse{ - *label, - label.Name, - metrics.TotalHosts, - metrics.OnlineHosts, - metrics.OfflineHosts, - metrics.MissingInActionHosts, - }, - ) + resp.Labels = append(resp.Labels, *labelResp) } return resp, nil } @@ -117,23 +119,11 @@ func makeCreateLabelEndpoint(svc kolide.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(createLabelRequest) label, err := svc.NewLabel(ctx, req.payload) + labelResp, err := labelResponseForLabel(ctx, svc, label) if err != nil { return createLabelResponse{Err: err}, nil } - metrics, err := svc.CountHostsInTargets(ctx, nil, []uint{label.ID}) - if err != nil { - return createLabelResponse{Err: err}, nil - } - return createLabelResponse{ - Label: labelResponse{ - *label, - label.Name, - metrics.TotalHosts, - metrics.OnlineHosts, - metrics.OfflineHosts, - metrics.MissingInActionHosts, - }, - }, nil + return createLabelResponse{Label: *labelResp}, nil } } diff --git a/server/service/service_labels.go b/server/service/service_labels.go index 45f1019daa..61cba88abe 100644 --- a/server/service/service_labels.go +++ b/server/service/service_labels.go @@ -44,3 +44,15 @@ func (svc service) NewLabel(ctx context.Context, p kolide.LabelPayload) (*kolide func (svc service) DeleteLabel(ctx context.Context, id uint) error { return svc.ds.DeleteLabel(id) } + +func (svc service) HostIDsForLabel(lid uint) ([]uint, error) { + hosts, err := svc.ds.ListHostsInLabel(lid) + if err != nil { + return nil, err + } + var ids []uint + for _, h := range hosts { + ids = append(ids, h.ID) + } + return ids, nil +}