fleet/server/datastore/inmem/labels.go
Zachary Wasserman 003ad9b5fa
Delete spec entities by name instead of ID (#1755)
With the UI, deleting by ID made sense. With fleetctl, we now want to delete
by name. Transition only the methods used for spec related entities, as others
will be removed soon.
2018-05-04 11:05:55 -07:00

223 lines
4.7 KiB
Go

package inmem
import (
"errors"
"sort"
"strconv"
"strings"
"time"
"github.com/kolide/fleet/server/kolide"
"github.com/patrickmn/sortutil"
)
func (d *Datastore) NewLabel(label *kolide.Label, opts ...kolide.OptionalArg) (*kolide.Label, error) {
d.mtx.Lock()
defer d.mtx.Unlock()
newLabel := *label
for _, l := range d.labels {
if l.Name == label.Name {
return nil, alreadyExists("Label", l.ID)
}
}
newLabel.ID = d.nextID(label)
d.labels[newLabel.ID] = &newLabel
return &newLabel, nil
}
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{}
for _, lqe := range d.labelQueryExecutions {
if lqe.HostID == hid && lqe.Matches {
if label := d.labels[lqe.LabelID]; label != nil {
resLabels = append(resLabels, *label)
}
}
}
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{}
for _, lqe := range d.labelQueryExecutions {
if lqe.HostID == host.ID && (lqe.UpdatedAt == cutoff || lqe.UpdatedAt.After(cutoff)) {
execedIDs[lqe.LabelID] = true
}
}
queries := map[string]string{}
for _, label := range d.labels {
if (label.Platform == "" || strings.Contains(label.Platform, host.Platform)) && !execedIDs[label.ID] {
queries[strconv.Itoa(int(label.ID))] = label.Query
}
}
return queries, nil
}
func (d *Datastore) RecordLabelQueryExecutions(host *kolide.Host, results map[uint]bool, t time.Time) error {
// Record executions
for labelID, matches := range results {
label, ok := d.labels[labelID]
if !ok {
return notFound("Label").WithID(labelID)
}
updated := false
d.mtx.Lock()
for _, lqe := range d.labelQueryExecutions {
if lqe.LabelID == label.ID && lqe.HostID == host.ID {
// Update existing execution values
lqe.UpdatedAt = t
lqe.Matches = matches
updated = true
break
}
}
if !updated {
// Create new execution
lqe := kolide.LabelQueryExecution{
HostID: host.ID,
LabelID: label.ID,
UpdatedAt: t,
Matches: matches,
}
lqe.ID = d.nextID(lqe)
d.labelQueryExecutions[lqe.ID] = &lqe
}
d.mtx.Unlock()
}
return nil
}
func (d *Datastore) Label(lid uint) (*kolide.Label, error) {
d.mtx.Lock()
label, ok := d.labels[lid]
d.mtx.Unlock()
if !ok {
return nil, errors.New("Label not found")
}
return label, nil
}
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{}
for k := range d.labels {
keys = append(keys, int(k))
}
sort.Ints(keys)
labels := []*kolide.Label{}
for _, k := range keys {
labels = append(labels, d.labels[uint(k)])
}
// Apply ordering
if opt.OrderKey != "" {
var fields = map[string]string{
"id": "ID",
"created_at": "CreatedAt",
"updated_at": "UpdatedAt",
"name": "Name",
}
if err := sortResults(labels, opt, fields); err != nil {
return nil, err
}
}
// Apply limit/offset
low, high := d.getLimitOffsetSliceBounds(opt, len(labels))
labels = labels[low:high]
return labels, nil
}
func (d *Datastore) SearchLabels(query string, omit ...uint) ([]kolide.Label, error) {
omitLookup := map[uint]bool{}
for _, o := range omit {
omitLookup[o] = true
}
var results []kolide.Label
d.mtx.Lock()
defer d.mtx.Unlock()
for _, l := range d.labels {
if len(results) == 10 {
break
}
if (strings.Contains(l.Name, query) || l.Name == "All Hosts") && !omitLookup[l.ID] {
results = append(results, *l)
continue
}
}
sortutil.AscByField(results, "ID")
return results, nil
}
func (d *Datastore) ListHostsInLabel(lid uint) ([]kolide.Host, error) {
var hosts []kolide.Host
d.mtx.Lock()
defer d.mtx.Unlock()
for _, lqe := range d.labelQueryExecutions {
if lqe.LabelID == lid && lqe.Matches {
hosts = append(hosts, *d.hosts[lqe.HostID])
}
}
return hosts, nil
}
func (d *Datastore) ListUniqueHostsInLabels(labels []uint) ([]kolide.Host, error) {
var hosts []kolide.Host
labelSet := map[uint]bool{}
hostSet := map[uint]bool{}
for _, label := range labels {
labelSet[label] = true
}
d.mtx.Lock()
defer d.mtx.Unlock()
for _, lqe := range d.labelQueryExecutions {
if labelSet[lqe.LabelID] && lqe.Matches {
if !hostSet[lqe.HostID] {
hosts = append(hosts, *d.hosts[lqe.HostID])
hostSet[lqe.HostID] = true
}
}
}
return hosts, nil
}
func (d *Datastore) SaveLabel(label *kolide.Label) (*kolide.Label, error) {
panic("inmem is being deprecated")
}