mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
Target search endpoint (#339)
This commit is contained in:
parent
262a48f8eb
commit
7ebebbb7b1
22 changed files with 984 additions and 34 deletions
|
|
@ -10,5 +10,6 @@ export default {
|
|||
LOGOUT: '/v1/kolide/logout',
|
||||
ME: '/v1/kolide/me',
|
||||
RESET_PASSWORD: '/v1/kolide/reset_password',
|
||||
TARGETS: '/v1/kolide/targets',
|
||||
USERS: '/v1/kolide/users',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ package datastore
|
|||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kolide/kolide-ose/server/kolide"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var enrollTests = []struct {
|
||||
|
|
@ -81,3 +83,62 @@ func testAuthenticateHost(t *testing.T, db kolide.Datastore) {
|
|||
_, err = db.AuthenticateHost("")
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func testSearchHosts(t *testing.T, db kolide.Datastore) {
|
||||
_, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "1",
|
||||
UUID: "1",
|
||||
HostName: "foo.local",
|
||||
PrimaryIP: "192.168.1.10",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "2",
|
||||
UUID: "2",
|
||||
HostName: "bar.local",
|
||||
PrimaryIP: "192.168.1.11",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h3, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "3",
|
||||
UUID: "3",
|
||||
HostName: "foo-bar.local",
|
||||
PrimaryIP: "192.168.1.12",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
hosts, err := db.SearchHosts("foo", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, hosts, 2)
|
||||
|
||||
host, err := db.SearchHosts("foo", []uint{h3.ID})
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, host, 1)
|
||||
assert.Equal(t, "foo.local", host[0].HostName)
|
||||
|
||||
none, err := db.SearchHosts("xxx", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, none, 0)
|
||||
}
|
||||
|
||||
func testSearchHostsLimit(t *testing.T, db kolide.Datastore) {
|
||||
for i := 0; i < 15; i++ {
|
||||
_, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: fmt.Sprintf("%d", i),
|
||||
UUID: fmt.Sprintf("%d", i),
|
||||
HostName: fmt.Sprintf("foo.%d.local", i),
|
||||
PrimaryIP: fmt.Sprintf("192.168.1.%d", i+1),
|
||||
})
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
hosts, err := db.SearchHosts("foo", nil)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, hosts, 10)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kolide/kolide-ose/server/kolide"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func testLabels(t *testing.T, db kolide.Datastore) {
|
||||
|
|
@ -219,3 +221,178 @@ func testManagingLabelsOnPacks(t *testing.T, ds kolide.Datastore) {
|
|||
assert.Nil(t, err)
|
||||
assert.Len(t, labels, 2)
|
||||
}
|
||||
|
||||
func testSearchLabels(t *testing.T, db kolide.Datastore) {
|
||||
_, err := db.NewLabel(&kolide.Label{
|
||||
Name: "foo",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = db.NewLabel(&kolide.Label{
|
||||
Name: "bar",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
l3, err := db.NewLabel(&kolide.Label{
|
||||
Name: "foo-bar",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
labels, err := db.SearchLabels("foo", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, labels, 2)
|
||||
|
||||
label, err := db.SearchLabels("foo", []uint{l3.ID})
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, label, 1)
|
||||
assert.Equal(t, "foo", label[0].Name)
|
||||
|
||||
none, err := db.SearchLabels("xxx", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, none, 0)
|
||||
}
|
||||
|
||||
func testSearchLabelsLimit(t *testing.T, db kolide.Datastore) {
|
||||
for i := 0; i < 15; i++ {
|
||||
_, err := db.NewLabel(&kolide.Label{
|
||||
Name: fmt.Sprintf("foo-%d", i),
|
||||
})
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
labels, err := db.SearchLabels("foo", nil)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, labels, 10)
|
||||
}
|
||||
|
||||
func testListHostsInLabel(t *testing.T, db kolide.Datastore) {
|
||||
h1, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "1",
|
||||
UUID: "1",
|
||||
HostName: "foo.local",
|
||||
PrimaryIP: "192.168.1.10",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h2, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "2",
|
||||
UUID: "2",
|
||||
HostName: "bar.local",
|
||||
PrimaryIP: "192.168.1.11",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h3, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "3",
|
||||
UUID: "3",
|
||||
HostName: "baz.local",
|
||||
PrimaryIP: "192.168.1.12",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
l1, err := db.NewLabel(&kolide.Label{
|
||||
Name: "label foo",
|
||||
QueryID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NotZero(t, l1.ID)
|
||||
l1ID := fmt.Sprintf("%d", l1.ID)
|
||||
|
||||
{
|
||||
|
||||
hosts, err := db.ListHostsInLabel(l1.ID)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, hosts, 0)
|
||||
}
|
||||
|
||||
for _, h := range []*kolide.Host{h1, h2, h3} {
|
||||
err = db.RecordLabelQueryExecutions(h, map[string]bool{l1ID: true}, time.Now())
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
{
|
||||
hosts, err := db.ListHostsInLabel(l1.ID)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, hosts, 3)
|
||||
}
|
||||
}
|
||||
|
||||
func testListUniqueHostsInLabels(t *testing.T, db kolide.Datastore) {
|
||||
h1, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "1",
|
||||
UUID: "1",
|
||||
HostName: "foo.local",
|
||||
PrimaryIP: "192.168.1.10",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h2, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "2",
|
||||
UUID: "2",
|
||||
HostName: "bar.local",
|
||||
PrimaryIP: "192.168.1.11",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h3, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "3",
|
||||
UUID: "3",
|
||||
HostName: "baz.local",
|
||||
PrimaryIP: "192.168.1.12",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h4, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "4",
|
||||
UUID: "4",
|
||||
HostName: "xxx.local",
|
||||
PrimaryIP: "192.168.1.13",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h5, err := db.NewHost(&kolide.Host{
|
||||
DetailUpdateTime: time.Now(),
|
||||
NodeKey: "5",
|
||||
UUID: "5",
|
||||
HostName: "yyy.local",
|
||||
PrimaryIP: "192.168.1.14",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
l1, err := db.NewLabel(&kolide.Label{
|
||||
Name: "label foo",
|
||||
QueryID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NotZero(t, l1.ID)
|
||||
l1ID := fmt.Sprintf("%d", l1.ID)
|
||||
|
||||
l2, err := db.NewLabel(&kolide.Label{
|
||||
Name: "label bar",
|
||||
QueryID: 2,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NotZero(t, l2.ID)
|
||||
l2ID := fmt.Sprintf("%d", l2.ID)
|
||||
|
||||
for _, h := range []*kolide.Host{h1, h2, h3} {
|
||||
err = db.RecordLabelQueryExecutions(h, map[string]bool{l1ID: true}, time.Now())
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
for _, h := range []*kolide.Host{h3, h4, h5} {
|
||||
err = db.RecordLabelQueryExecutions(h, map[string]bool{l2ID: true}, time.Now())
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
hosts, err := db.ListUniqueHostsInLabels([]uint{l1.ID, l2.ID})
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, hosts, 5)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,4 +31,10 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){
|
|||
testSaveUser,
|
||||
testUserByID,
|
||||
testPasswordResetRequests,
|
||||
testSearchHosts,
|
||||
testSearchHostsLimit,
|
||||
testSearchLabels,
|
||||
testSearchLabelsLimit,
|
||||
testListHostsInLabel,
|
||||
testListUniqueHostsInLabels,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,34 @@ func (orm gormDB) Migrate() error {
|
|||
}
|
||||
|
||||
// Have to manually add indexes. Yuck!
|
||||
orm.DB.Model(&kolide.LabelQueryExecution{}).AddUniqueIndex("idx_lqe_label_host", "label_id", "host_id")
|
||||
err := orm.DB.Model(&kolide.LabelQueryExecution{}).AddUniqueIndex("idx_lqe_label_host", "label_id", "host_id").Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
indexes := []interface{}{}
|
||||
err = orm.DB.Raw("SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = 'kolide' AND INDEX_NAME = 'hosts_search';").Scan(&indexes).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(indexes) == 0 {
|
||||
err = orm.DB.Exec("CREATE FULLTEXT INDEX hosts_search ON hosts(host_name, primary_ip);").Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
indexes = []interface{}{}
|
||||
err = orm.DB.Raw("SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = 'kolide' AND INDEX_NAME = 'labels_search';").Scan(&indexes).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(indexes) == 0 {
|
||||
err = orm.DB.Exec("CREATE FULLTEXT INDEX labels_search ON labels(name);").Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,3 +131,28 @@ func (orm gormDB) MarkHostSeen(host *kolide.Host, t time.Time) error {
|
|||
host.UpdatedAt = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (orm gormDB) SearchHosts(query string, omit []uint) ([]kolide.Host, error) {
|
||||
sql := `
|
||||
SELECT *
|
||||
FROM hosts
|
||||
WHERE MATCH(host_name, primary_ip)
|
||||
AGAINST(? IN BOOLEAN MODE)
|
||||
`
|
||||
results := []kolide.Host{}
|
||||
|
||||
var db *gorm.DB
|
||||
if len(omit) > 0 {
|
||||
sql += "AND id NOT IN (?) LIMIT 10;"
|
||||
db = orm.DB.Raw(sql, query+"*", omit)
|
||||
} else {
|
||||
sql += "LIMIT 10;"
|
||||
db = orm.DB.Raw(sql, query+"*")
|
||||
}
|
||||
|
||||
err := db.Scan(&results).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, errors.DatabaseError(err)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,3 +150,69 @@ AND lqe.matches
|
|||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (orm gormDB) SearchLabels(query string, omit []uint) ([]kolide.Label, error) {
|
||||
sql := `
|
||||
SELECT *
|
||||
FROM labels
|
||||
WHERE MATCH(name)
|
||||
AGAINST(? IN BOOLEAN MODE)
|
||||
`
|
||||
results := []kolide.Label{}
|
||||
|
||||
var db *gorm.DB
|
||||
if len(omit) > 0 {
|
||||
sql += "AND id NOT IN (?) LIMIT 10;"
|
||||
db = orm.DB.Raw(sql, query+"*", omit)
|
||||
} else {
|
||||
sql += "LIMIT 10;"
|
||||
db = orm.DB.Raw(sql, query+"*")
|
||||
}
|
||||
|
||||
err := db.Scan(&results).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, errors.DatabaseError(err)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (orm gormDB) ListHostsInLabel(lid uint) ([]kolide.Host, error) {
|
||||
results := []kolide.Host{}
|
||||
err := orm.DB.Raw(`
|
||||
SELECT h.*
|
||||
FROM label_query_executions lqe
|
||||
JOIN hosts h
|
||||
ON lqe.host_id = h.id
|
||||
WHERE lqe.label_id = ?
|
||||
AND lqe.matches = 1;
|
||||
`, lid).Scan(&results).Error
|
||||
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, errors.DatabaseError(err)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (orm gormDB) ListUniqueHostsInLabels(labels []uint) ([]kolide.Host, error) {
|
||||
if labels == nil || len(labels) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
results := []kolide.Host{}
|
||||
err := orm.DB.Raw(`
|
||||
SELECT h.*
|
||||
FROM label_query_executions lqe
|
||||
JOIN hosts h
|
||||
ON lqe.host_id = h.id
|
||||
WHERE lqe.label_id in (?)
|
||||
AND lqe.matches = 1
|
||||
GROUP BY h.id;
|
||||
`, labels).Scan(&results).Error
|
||||
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, errors.DatabaseError(err)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,9 +138,11 @@ func (orm gormDB) RemoveQueryFromPack(query *kolide.Query, pack *kolide.Pack) er
|
|||
|
||||
func (orm gormDB) AddLabelToPack(lid uint, pid uint) error {
|
||||
pt := &kolide.PackTarget{
|
||||
Type: kolide.TargetLabel,
|
||||
PackID: pid,
|
||||
TargetID: lid,
|
||||
PackID: pid,
|
||||
Target: kolide.Target{
|
||||
Type: kolide.TargetLabel,
|
||||
TargetID: lid,
|
||||
},
|
||||
}
|
||||
|
||||
return orm.DB.Create(pt).Error
|
||||
|
|
@ -191,11 +193,5 @@ func (orm gormDB) RemoveLabelFromPack(label *kolide.Label, pack *kolide.Pack) er
|
|||
)
|
||||
}
|
||||
|
||||
pt := &kolide.PackTarget{
|
||||
Type: kolide.TargetLabel,
|
||||
PackID: pack.ID,
|
||||
TargetID: label.ID,
|
||||
}
|
||||
|
||||
return orm.DB.Delete(pt).Error
|
||||
return orm.DB.Where("pack_id = ? AND type = ? AND target_id = ?", pack.ID, kolide.TargetLabel, label.ID).Delete(&kolide.PackTarget{}).Error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package datastore
|
|||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kolide/kolide-ose/server/kolide"
|
||||
|
|
@ -178,3 +179,27 @@ func (orm *inmem) MarkHostSeen(host *kolide.Host, t time.Time) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (orm *inmem) SearchHosts(query string, omit []uint) ([]kolide.Host, error) {
|
||||
omitLookup := map[uint]bool{}
|
||||
for _, o := range omit {
|
||||
omitLookup[o] = true
|
||||
}
|
||||
|
||||
var results []kolide.Host
|
||||
|
||||
orm.mtx.Lock()
|
||||
defer orm.mtx.Unlock()
|
||||
|
||||
for _, h := range orm.hosts {
|
||||
if len(results) == 10 {
|
||||
break
|
||||
}
|
||||
|
||||
if (strings.Contains(h.HostName, query) || strings.Contains(h.PrimaryIP, query)) && !omitLookup[h.ID] {
|
||||
results = append(results, *h)
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kolide/kolide-ose/server/kolide"
|
||||
|
|
@ -193,3 +194,68 @@ func (orm *inmem) SaveLabel(label *kolide.Label) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (orm *inmem) SearchLabels(query string, omit []uint) ([]kolide.Label, error) {
|
||||
omitLookup := map[uint]bool{}
|
||||
for _, o := range omit {
|
||||
omitLookup[o] = true
|
||||
}
|
||||
|
||||
var results []kolide.Label
|
||||
|
||||
orm.mtx.Lock()
|
||||
defer orm.mtx.Unlock()
|
||||
|
||||
for _, l := range orm.labels {
|
||||
if len(results) == 10 {
|
||||
break
|
||||
}
|
||||
|
||||
if strings.Contains(l.Name, query) && !omitLookup[l.ID] {
|
||||
results = append(results, *l)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (orm *inmem) ListHostsInLabel(lid uint) ([]kolide.Host, error) {
|
||||
var hosts []kolide.Host
|
||||
|
||||
orm.mtx.Lock()
|
||||
defer orm.mtx.Unlock()
|
||||
|
||||
for _, lqe := range orm.labelQueryExecutions {
|
||||
if lqe.LabelID == lid && lqe.Matches {
|
||||
hosts = append(hosts, *orm.hosts[lqe.HostID])
|
||||
}
|
||||
}
|
||||
|
||||
return hosts, nil
|
||||
}
|
||||
|
||||
func (orm *inmem) 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
|
||||
}
|
||||
|
||||
orm.mtx.Lock()
|
||||
defer orm.mtx.Unlock()
|
||||
|
||||
for _, lqe := range orm.labelQueryExecutions {
|
||||
if labelSet[lqe.LabelID] && lqe.Matches {
|
||||
if !hostSet[lqe.HostID] {
|
||||
hosts = append(hosts, *orm.hosts[lqe.HostID])
|
||||
hostSet[lqe.HostID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hosts, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,9 +143,11 @@ func (orm *inmem) RemoveQueryFromPack(query *kolide.Query, pack *kolide.Pack) er
|
|||
|
||||
func (orm *inmem) AddLabelToPack(lid uint, pid uint) error {
|
||||
pt := &kolide.PackTarget{
|
||||
Type: kolide.TargetLabel,
|
||||
PackID: pid,
|
||||
TargetID: lid,
|
||||
PackID: pid,
|
||||
Target: kolide.Target{
|
||||
Type: kolide.TargetLabel,
|
||||
TargetID: lid,
|
||||
},
|
||||
}
|
||||
|
||||
orm.mtx.Lock()
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ type HostStore interface {
|
|||
EnrollHost(uuid, hostname, ip, platform string, nodeKeySize int) (*Host, error)
|
||||
AuthenticateHost(nodeKey string) (*Host, error)
|
||||
MarkHostSeen(host *Host, t time.Time) error
|
||||
SearchHosts(query string, omit []uint) ([]Host, error)
|
||||
}
|
||||
|
||||
type HostService interface {
|
||||
|
|
@ -30,7 +31,7 @@ type Host struct {
|
|||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DetailUpdateTime time.Time `json:"detail_updated_at"` // Time that the host details were last updated
|
||||
NodeKey string `json:"-" gorm:"unique_index:idx_host_unique_nodekey"`
|
||||
HostName string `json:"hostname"`
|
||||
HostName string `json:"hostname"` // there is a fulltext index on this field
|
||||
UUID string `json:"uuid" gorm:"unique_index:idx_host_unique_uuid"`
|
||||
Platform string `json:"platform"`
|
||||
OsqueryVersion string `json:"osquery_version"`
|
||||
|
|
@ -38,5 +39,5 @@ type Host struct {
|
|||
Uptime time.Duration `json:"uptime"`
|
||||
PhysicalMemory int `json:"memory" sql:"type:bigint"`
|
||||
PrimaryMAC string `json:"mac"`
|
||||
PrimaryIP string `json:"ip"`
|
||||
PrimaryIP string `json:"ip"` // there is a fulltext index on this field
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ type LabelStore interface {
|
|||
|
||||
// LabelsForHost returns the labels that the given host is in.
|
||||
ListLabelsForHost(hid uint) ([]Label, error)
|
||||
|
||||
ListHostsInLabel(lid uint) ([]Host, error)
|
||||
ListUniqueHostsInLabels(labels []uint) ([]Host, error)
|
||||
|
||||
SearchLabels(query string, omit []uint) ([]Label, error)
|
||||
}
|
||||
|
||||
type LabelService interface {
|
||||
|
|
@ -48,7 +53,7 @@ type Label struct {
|
|||
ID uint `json:"id" gorm:"primary_key"`
|
||||
CreatedAt time.Time `json:"-"`
|
||||
UpdatedAt time.Time `json:"-"`
|
||||
Name string `json:"name" gorm:"not null;unique_index:idx_label_unique_name"`
|
||||
Name string `json:"name" gorm:"not null;unique_index:idx_label_unique_name"` // there is a fulltext index on this field
|
||||
QueryID uint `json:"query_id"`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,16 +59,8 @@ type PackQuery struct {
|
|||
QueryID uint
|
||||
}
|
||||
|
||||
type TargetType int
|
||||
|
||||
const (
|
||||
TargetLabel TargetType = iota
|
||||
TargetHost
|
||||
)
|
||||
|
||||
type PackTarget struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
Type TargetType
|
||||
PackID uint
|
||||
TargetID uint
|
||||
Target
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,4 +11,5 @@ type Service interface {
|
|||
HostService
|
||||
AppConfigService
|
||||
InviteService
|
||||
TargetService
|
||||
}
|
||||
|
|
|
|||
31
server/kolide/target.go
Normal file
31
server/kolide/target.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package kolide
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type TargetSearchResults struct {
|
||||
Hosts []Host
|
||||
Labels []Label
|
||||
}
|
||||
|
||||
type TargetService interface {
|
||||
// SearchTargets will accept a search query, a slice of IDs of hosts to omit,
|
||||
// and a slice of IDs of labels to omit, and it will return a set of targets
|
||||
// (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)
|
||||
}
|
||||
|
||||
type TargetType int
|
||||
|
||||
const (
|
||||
TargetLabel TargetType = iota
|
||||
TargetHost
|
||||
)
|
||||
|
||||
type Target struct {
|
||||
Type TargetType
|
||||
TargetID uint
|
||||
}
|
||||
86
server/service/endpoint_targets.go
Normal file
86
server/service/endpoint_targets.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/kolide/kolide-ose/server/kolide"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Search Targrets
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type searchTargetsRequest struct {
|
||||
Query string `json:"query"`
|
||||
Selected struct {
|
||||
Labels []uint `json:"labels"`
|
||||
Hosts []uint `json:"hosts"`
|
||||
} `json:"selected"`
|
||||
}
|
||||
|
||||
type hostSearchResult struct {
|
||||
hostResponse
|
||||
DisplayText string `json:"display_text"`
|
||||
}
|
||||
|
||||
type labelSearchResult struct {
|
||||
kolide.Label
|
||||
DisplayText string `json:"display_text"`
|
||||
}
|
||||
|
||||
type targetsData struct {
|
||||
Hosts []hostSearchResult `json:"hosts"`
|
||||
Labels []labelSearchResult `json:"labels"`
|
||||
}
|
||||
|
||||
type searchTargetsResponse struct {
|
||||
Targets *targetsData `json:"targets,omitempty"`
|
||||
SelectedTargetsCount uint `json:"selected_targets_count,omitempty"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r searchTargetsResponse) error() error { return r.Err }
|
||||
|
||||
func makeSearchTargetsEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(searchTargetsRequest)
|
||||
|
||||
results, err := svc.SearchTargets(ctx, req.Query, req.Selected.Hosts, req.Selected.Labels)
|
||||
if err != nil {
|
||||
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{},
|
||||
}
|
||||
|
||||
for _, host := range results.Hosts {
|
||||
targets.Hosts = append(targets.Hosts,
|
||||
hostSearchResult{
|
||||
hostResponse{host, svc.HostStatus(ctx, host)},
|
||||
host.HostName,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
for _, label := range results.Labels {
|
||||
targets.Labels = append(targets.Labels,
|
||||
labelSearchResult{
|
||||
label,
|
||||
label.Name,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return searchTargetsResponse{
|
||||
Targets: targets,
|
||||
SelectedTargetsCount: count,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -60,6 +60,7 @@ type KolideEndpoints struct {
|
|||
GetHost endpoint.Endpoint
|
||||
DeleteHost endpoint.Endpoint
|
||||
ListHosts endpoint.Endpoint
|
||||
SearchTargets endpoint.Endpoint
|
||||
}
|
||||
|
||||
// MakeKolideServerEndpoints creates the Kolide API endpoints.
|
||||
|
|
@ -101,6 +102,15 @@ func MakeKolideServerEndpoints(svc kolide.Service, jwtKey string) KolideEndpoint
|
|||
GetHost: authenticatedUser(jwtKey, svc, makeGetHostEndpoint(svc)),
|
||||
ListHosts: authenticatedUser(jwtKey, svc, makeListHostsEndpoint(svc)),
|
||||
DeleteHost: authenticatedUser(jwtKey, svc, makeDeleteHostEndpoint(svc)),
|
||||
GetLabel: authenticatedUser(jwtKey, svc, makeGetLabelEndpoint(svc)),
|
||||
ListLabels: authenticatedUser(jwtKey, svc, makeListLabelsEndpoint(svc)),
|
||||
CreateLabel: authenticatedUser(jwtKey, svc, makeCreateLabelEndpoint(svc)),
|
||||
ModifyLabel: authenticatedUser(jwtKey, svc, makeModifyLabelEndpoint(svc)),
|
||||
DeleteLabel: authenticatedUser(jwtKey, svc, makeDeleteLabelEndpoint(svc)),
|
||||
AddLabelToPack: authenticatedUser(jwtKey, svc, makeAddLabelToPackEndpoint(svc)),
|
||||
GetLabelsForPack: authenticatedUser(jwtKey, svc, makeGetLabelsForPackEndpoint(svc)),
|
||||
DeleteLabelFromPack: authenticatedUser(jwtKey, svc, makeDeleteLabelFromPackEndpoint(svc)),
|
||||
SearchTargets: authenticatedUser(jwtKey, svc, makeSearchTargetsEndpoint(svc)),
|
||||
|
||||
// Osquery endpoints
|
||||
EnrollAgent: makeEnrollAgentEndpoint(svc),
|
||||
|
|
@ -108,14 +118,6 @@ func MakeKolideServerEndpoints(svc kolide.Service, jwtKey string) KolideEndpoint
|
|||
GetDistributedQueries: authenticatedHost(svc, makeGetDistributedQueriesEndpoint(svc)),
|
||||
SubmitDistributedQueryResults: authenticatedHost(svc, makeSubmitDistributedQueryResultsEndpoint(svc)),
|
||||
SubmitLogs: authenticatedHost(svc, makeSubmitLogsEndpoint(svc)),
|
||||
GetLabel: authenticatedUser(jwtKey, svc, makeGetLabelEndpoint(svc)),
|
||||
ListLabels: authenticatedUser(jwtKey, svc, makeListLabelsEndpoint(svc)),
|
||||
CreateLabel: authenticatedUser(jwtKey, svc, makeCreateLabelEndpoint(svc)),
|
||||
ModifyLabel: authenticatedUser(jwtKey, svc, makeModifyLabelEndpoint(svc)),
|
||||
DeleteLabel: authenticatedUser(jwtKey, svc, makeDeleteLabelEndpoint(svc)),
|
||||
AddLabelToPack: authenticatedUser(jwtKey, svc, makeAddLabelToPackEndpoint(svc)),
|
||||
GetLabelsForPack: authenticatedUser(jwtKey, svc, makeGetLabelsForPackEndpoint(svc)),
|
||||
DeleteLabelFromPack: authenticatedUser(jwtKey, svc, makeDeleteLabelFromPackEndpoint(svc)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,6 +169,7 @@ type kolideHandlers struct {
|
|||
GetHost *kithttp.Server
|
||||
DeleteHost *kithttp.Server
|
||||
ListHosts *kithttp.Server
|
||||
SearchTargets *kithttp.Server
|
||||
}
|
||||
|
||||
func makeKolideKitHandlers(ctx context.Context, e KolideEndpoints, opts []kithttp.ServerOption) kolideHandlers {
|
||||
|
|
@ -221,6 +224,7 @@ func makeKolideKitHandlers(ctx context.Context, e KolideEndpoints, opts []kithtt
|
|||
GetHost: newServer(e.GetHost, decodeGetHostRequest),
|
||||
DeleteHost: newServer(e.DeleteHost, decodeDeleteHostRequest),
|
||||
ListHosts: newServer(e.ListHosts, decodeListHostsRequest),
|
||||
SearchTargets: newServer(e.SearchTargets, decodeSearchTargetsRequest),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -296,6 +300,8 @@ func attachKolideAPIRoutes(r *mux.Router, h kolideHandlers) {
|
|||
r.Handle("/api/v1/kolide/hosts/{id}", h.GetHost).Methods("GET")
|
||||
r.Handle("/api/v1/kolide/hosts/{id}", h.DeleteHost).Methods("DELETE")
|
||||
|
||||
r.Handle("/api/v1/kolide/targets", h.SearchTargets).Methods("POST")
|
||||
|
||||
r.Handle("/api/v1/osquery/enroll", h.EnrollAgent).Methods("POST")
|
||||
r.Handle("/api/v1/osquery/config", h.GetClientConfig).Methods("POST")
|
||||
r.Handle("/api/v1/osquery/distributed/read", h.GetDistributedQueries).Methods("POST")
|
||||
|
|
|
|||
45
server/service/service_targets.go
Normal file
45
server/service/service_targets.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/kolide/kolide-ose/server/kolide"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func (svc service) SearchTargets(ctx context.Context, query string, selectedHostIDs []uint, selectedLabelIDs []uint) (*kolide.TargetSearchResults, error) {
|
||||
results := &kolide.TargetSearchResults{}
|
||||
|
||||
hosts, err := svc.ds.SearchHosts(query, selectedHostIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results.Hosts = hosts
|
||||
|
||||
labels, err := svc.ds.SearchLabels(query, selectedLabelIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results.Labels = labels
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (svc service) CountHostsInTargets(ctx context.Context, hosts []uint, labels []uint) (uint, error) {
|
||||
hostsInLabels, err := svc.ds.ListUniqueHostsInLabels(labels)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
hostLookup := map[uint]bool{}
|
||||
|
||||
for _, host := range hosts {
|
||||
hostLookup[host] = true
|
||||
}
|
||||
|
||||
for _, host := range hostsInLabels {
|
||||
if !hostLookup[host.ID] {
|
||||
hostLookup[host.ID] = true
|
||||
}
|
||||
}
|
||||
|
||||
return uint(len(hostLookup)), nil
|
||||
}
|
||||
268
server/service/service_targets_test.go
Normal file
268
server/service/service_targets_test.go
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kolide/kolide-ose/server/datastore"
|
||||
"github.com/kolide/kolide-ose/server/kolide"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestSearchTargets(t *testing.T) {
|
||||
ds, err := datastore.New("inmem", "")
|
||||
require.Nil(t, err)
|
||||
|
||||
svc, err := newTestService(ds)
|
||||
require.Nil(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
h1, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "foo.local",
|
||||
PrimaryIP: "192.168.1.10",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
l1, err := ds.NewLabel(&kolide.Label{
|
||||
Name: "label foo",
|
||||
QueryID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
results, err := svc.SearchTargets(ctx, "foo", nil, nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Len(t, results.Hosts, 1)
|
||||
assert.Equal(t, h1.HostName, results.Hosts[0].HostName)
|
||||
|
||||
require.Len(t, results.Labels, 1)
|
||||
assert.Equal(t, l1.Name, results.Labels[0].Name)
|
||||
}
|
||||
|
||||
func TestCountHostsInTargets(t *testing.T) {
|
||||
ds, err := datastore.New("inmem", "")
|
||||
require.Nil(t, err)
|
||||
|
||||
svc, err := newTestService(ds)
|
||||
require.Nil(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
h1, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "foo.local",
|
||||
PrimaryIP: "192.168.1.10",
|
||||
NodeKey: "1",
|
||||
UUID: "1",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h2, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "bar.local",
|
||||
PrimaryIP: "192.168.1.11",
|
||||
NodeKey: "2",
|
||||
UUID: "2",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h3, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "baz.local",
|
||||
PrimaryIP: "192.168.1.12",
|
||||
NodeKey: "3",
|
||||
UUID: "3",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h4, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "xxx.local",
|
||||
PrimaryIP: "192.168.1.13",
|
||||
NodeKey: "4",
|
||||
UUID: "4",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h5, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "yyy.local",
|
||||
PrimaryIP: "192.168.1.14",
|
||||
NodeKey: "5",
|
||||
UUID: "5",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
l1, err := ds.NewLabel(&kolide.Label{
|
||||
Name: "label foo",
|
||||
QueryID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NotZero(t, l1.ID)
|
||||
l1ID := fmt.Sprintf("%d", l1.ID)
|
||||
|
||||
l2, err := ds.NewLabel(&kolide.Label{
|
||||
Name: "label bar",
|
||||
QueryID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NotZero(t, l2.ID)
|
||||
l2ID := fmt.Sprintf("%d", l2.ID)
|
||||
|
||||
for _, h := range []*kolide.Host{h1, h2, h3} {
|
||||
err = ds.RecordLabelQueryExecutions(h, map[string]bool{l1ID: true}, time.Now())
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
for _, h := range []*kolide.Host{h3, h4, h5} {
|
||||
err = ds.RecordLabelQueryExecutions(h, map[string]bool{l2ID: true}, time.Now())
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
count, err := svc.CountHostsInTargets(ctx, nil, []uint{l1.ID, l2.ID})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, uint(5), count)
|
||||
|
||||
count, err = svc.CountHostsInTargets(ctx, []uint{h1.ID, h2.ID}, []uint{l1.ID, l2.ID})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, uint(5), count)
|
||||
|
||||
count, err = svc.CountHostsInTargets(ctx, []uint{h1.ID, h2.ID}, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, uint(2), count)
|
||||
|
||||
count, err = svc.CountHostsInTargets(ctx, []uint{h1.ID}, []uint{l2.ID})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, uint(4), count)
|
||||
|
||||
count, err = svc.CountHostsInTargets(ctx, nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, uint(0), count)
|
||||
}
|
||||
|
||||
func TestSearchWithOmit(t *testing.T) {
|
||||
ds, err := datastore.New("inmem", "")
|
||||
require.Nil(t, err)
|
||||
|
||||
svc, err := newTestService(ds)
|
||||
require.Nil(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
h1, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "foo.local",
|
||||
PrimaryIP: "192.168.1.10",
|
||||
NodeKey: "1",
|
||||
UUID: "1",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h2, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "foobar.local",
|
||||
PrimaryIP: "192.168.1.11",
|
||||
NodeKey: "2",
|
||||
UUID: "2",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
l1, err := ds.NewLabel(&kolide.Label{
|
||||
Name: "label foo",
|
||||
QueryID: 1,
|
||||
})
|
||||
|
||||
{
|
||||
results, err := svc.SearchTargets(ctx, "foo", nil, nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Len(t, results.Hosts, 2)
|
||||
|
||||
require.Len(t, results.Labels, 1)
|
||||
assert.Equal(t, l1.Name, results.Labels[0].Name)
|
||||
}
|
||||
|
||||
{
|
||||
results, err := svc.SearchTargets(ctx, "foo", []uint{h2.ID}, nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Len(t, results.Hosts, 1)
|
||||
assert.Equal(t, h1.HostName, results.Hosts[0].HostName)
|
||||
|
||||
require.Len(t, results.Labels, 1)
|
||||
assert.Equal(t, l1.Name, results.Labels[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchHostsInLabels(t *testing.T) {
|
||||
ds, err := datastore.New("inmem", "")
|
||||
require.Nil(t, err)
|
||||
|
||||
svc, err := newTestService(ds)
|
||||
require.Nil(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
h1, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "foo.local",
|
||||
PrimaryIP: "192.168.1.10",
|
||||
NodeKey: "1",
|
||||
UUID: "1",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h2, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "bar.local",
|
||||
PrimaryIP: "192.168.1.11",
|
||||
NodeKey: "2",
|
||||
UUID: "2",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
h3, err := ds.NewHost(&kolide.Host{
|
||||
HostName: "baz.local",
|
||||
PrimaryIP: "192.168.1.12",
|
||||
NodeKey: "3",
|
||||
UUID: "3",
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
l1, err := ds.NewLabel(&kolide.Label{
|
||||
Name: "label foo",
|
||||
QueryID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
require.NotZero(t, l1.ID)
|
||||
l1ID := fmt.Sprintf("%d", l1.ID)
|
||||
|
||||
for _, h := range []*kolide.Host{h1, h2, h3} {
|
||||
err = ds.RecordLabelQueryExecutions(h, map[string]bool{l1ID: true}, time.Now())
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
results, err := svc.SearchTargets(ctx, "baz", nil, nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Len(t, results.Hosts, 1)
|
||||
assert.Equal(t, h3.HostName, results.Hosts[0].HostName)
|
||||
}
|
||||
|
||||
func TestSearchResultsLimit(t *testing.T) {
|
||||
ds, err := datastore.New("inmem", "")
|
||||
require.Nil(t, err)
|
||||
|
||||
svc, err := newTestService(ds)
|
||||
require.Nil(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
for i := 0; i < 15; i++ {
|
||||
_, err := ds.NewHost(&kolide.Host{
|
||||
HostName: fmt.Sprintf("foo.%d.local", i),
|
||||
PrimaryIP: fmt.Sprintf("192.168.1.%d", i+1),
|
||||
NodeKey: fmt.Sprintf("%d", i+1),
|
||||
UUID: fmt.Sprintf("%d", i+1),
|
||||
})
|
||||
require.Nil(t, err)
|
||||
}
|
||||
targets, err := svc.SearchTargets(ctx, "foo", nil, nil)
|
||||
require.Nil(t, err)
|
||||
assert.Len(t, targets.Hosts, 10)
|
||||
}
|
||||
17
server/service/transport_targets.go
Normal file
17
server/service/transport_targets.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func decodeSearchTargetsRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
var req searchTargetsRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
46
server/service/transport_targets_test.go
Normal file
46
server/service/transport_targets_test.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestDecodeSearchTargetsRequest(t *testing.T) {
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/v1/kolide/targets", func(writer http.ResponseWriter, request *http.Request) {
|
||||
r, err := decodeSearchTargetsRequest(context.Background(), request)
|
||||
assert.Nil(t, err)
|
||||
|
||||
params := r.(searchTargetsRequest)
|
||||
assert.Equal(t, "bar", params.Query)
|
||||
assert.Len(t, params.Selected.Hosts, 3)
|
||||
assert.Len(t, params.Selected.Labels, 2)
|
||||
}).Methods("POST")
|
||||
var body bytes.Buffer
|
||||
|
||||
body.Write([]byte(`{
|
||||
"query": "bar",
|
||||
"selected": {
|
||||
"hosts": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"labels": [
|
||||
1,
|
||||
2
|
||||
]
|
||||
}
|
||||
}`))
|
||||
|
||||
router.ServeHTTP(
|
||||
httptest.NewRecorder(),
|
||||
httptest.NewRequest("POST", "/api/v1/kolide/targets", &body),
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue