Skip to content

add host additional info filters #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 14, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions server/datastore/datastore_hosts_test.go
Original file line number Diff line number Diff line change
@@ -5,9 +5,9 @@ import (
"fmt"
"sort"
"strconv"
"strings"
"testing"
"time"
"strings"

"github.com/WatchBeam/clock"
"github.com/kolide/fleet/server/kolide"
@@ -156,12 +156,49 @@ func testListHosts(t *testing.T, ds kolide.Datastore) {
require.Equal(t, hosts[0].ID, hosts2[0].ID)
}

func testListHostsFilterAdditional(t *testing.T, ds kolide.Datastore) {
h, err := ds.NewHost(&kolide.Host{
DetailUpdateTime: time.Now(),
LabelUpdateTime: time.Now(),
SeenTime: time.Now(),
OsqueryHostID: "foobar",
NodeKey: "nodekey",
UUID: "uuid",
HostName: "foobar.local",
})
require.Nil(t, err)

// Add additional
additional := json.RawMessage(`{"field1": "v1", "field2": "v2"}`)
h.Additional = &additional
err = ds.SaveHost(h)
require.Nil(t, err)

additional = json.RawMessage(`{"field1": "v1", "field2": "v2"}`)
hosts, err := ds.ListHosts(kolide.HostListOptions{})
require.Nil(t, err)
assert.Equal(t, additional, *hosts[0].Additional)

hosts, err = ds.ListHosts(kolide.HostListOptions{AdditionalFilters: []string{"field1", "field2"}})
require.Nil(t, err)
assert.Equal(t, additional, *hosts[0].Additional)

hosts, err = ds.ListHosts(kolide.HostListOptions{})
require.Nil(t, err)
assert.Equal(t, additional, *hosts[0].Additional)

additional = json.RawMessage(`{"field1": "v1", "missing": null}`)
hosts, err = ds.ListHosts(kolide.HostListOptions{AdditionalFilters: []string{"field1", "missing"}})
require.Nil(t, err)
assert.Equal(t, additional, *hosts[0].Additional)
}

func testListHostsStatus(t *testing.T, ds kolide.Datastore) {
for i := 0; i < 10; i++ {
_, err := ds.NewHost(&kolide.Host{
DetailUpdateTime: time.Now(),
LabelUpdateTime: time.Now(),
SeenTime: time.Now().Add(-time.Duration(i) *time.Minute),
SeenTime: time.Now().Add(-time.Duration(i) * time.Minute),
OsqueryHostID: strconv.Itoa(i),
NodeKey: fmt.Sprintf("%d", i),
UUID: fmt.Sprintf("%d", i),
@@ -190,7 +227,6 @@ func testListHostsStatus(t *testing.T, ds kolide.Datastore) {
assert.Equal(t, 10, len(hosts))
}


func testEnrollHost(t *testing.T, ds kolide.Datastore) {
test.AddAllHostsLabel(t, ds)
var hosts []*kolide.Host
1 change: 1 addition & 0 deletions server/datastore/datastore_test.go
Original file line number Diff line number Diff line change
@@ -44,6 +44,7 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){
testSaveHosts,
testDeleteHost,
testListHosts,
testListHostsFilterAdditional,
testListHostsStatus,
testListHostsInPack,
testListPacksForHost,
28 changes: 28 additions & 0 deletions server/datastore/inmem/hosts.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package inmem

import (
"encoding/json"
"errors"
"sort"
"strings"
@@ -103,6 +104,33 @@ func (d *Datastore) ListHosts(opt kolide.HostListOptions) ([]*kolide.Host, error
low, high := d.getLimitOffsetSliceBounds(opt.ListOptions, len(hosts))
hosts = hosts[low:high]

// Filter additional info
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future please feel free not to implement anything in inmem. It's been mostly removed from use and I need to finish tearing it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests, but did you also want me to remove this inmem implementation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for that. I'll be removing all of this soon in any case.

if len(opt.AdditionalFilters) > 0 {
fieldsWanted := map[string]interface{}{}
for _, field := range opt.AdditionalFilters {
fieldsWanted[field] = true
}
for i, host := range hosts {
addInfo := map[string]interface{}{}
if err := json.Unmarshal(*host.Additional, &addInfo); err != nil {
return nil, err
}

for k := range addInfo {
if _, ok := fieldsWanted[k]; !ok {
delete(addInfo, k)
}
}
addInfoJSON := json.RawMessage{}
addInfoJSON, err := json.Marshal(addInfo)
if err != nil {
return nil, err
}
host.Additional = &addInfoJSON
hosts[i] = host
}
}

return hosts, nil
}

61 changes: 58 additions & 3 deletions server/datastore/mysql/hosts.go
Original file line number Diff line number Diff line change
@@ -152,10 +152,65 @@ func (d *Datastore) Host(id uint) (*kolide.Host, error) {
}

func (d *Datastore) ListHosts(opt kolide.HostListOptions) ([]*kolide.Host, error) {
sql := `
SELECT * FROM hosts
`
sql := `SELECT id,
osquery_host_id,
created_at,
updated_at,
detail_update_time,
node_key,
host_name,
uuid,
platform,
osquery_version,
os_version,
build,
platform_like,
code_name,
uptime,
physical_memory,
cpu_type,
cpu_subtype,
cpu_brand,
cpu_physical_cores,
cpu_logical_cores,
hardware_vendor,
hardware_model,
hardware_version,
hardware_serial,
computer_name,
primary_ip_id,
seen_time,
distributed_interval,
logger_tls_period,
config_tls_refresh,
primary_ip,
primary_mac,
label_update_time,
enroll_secret_name,
`

var params []interface{}

// Filter additional info by extracting into a new json object.
if len(opt.AdditionalFilters) > 0 {
sql += `JSON_OBJECT(
`
for _, field := range opt.AdditionalFilters {
sql += fmt.Sprintf(`?, JSON_EXTRACT(additional, ?), `)
params = append(params, field, fmt.Sprintf(`$."%s"`, field))
}
sql = sql[:len(sql)-2]
sql += `
) AS additional
`
} else {
sql += `
additional
`
}

sql += `FROM hosts
`
switch opt.StatusFilter {
case "new":
sql += "WHERE DATE_ADD(created_at, INTERVAL 1 DAY) >= ?"
3 changes: 2 additions & 1 deletion server/kolide/hosts.go
Original file line number Diff line number Diff line change
@@ -87,7 +87,8 @@ type HostService interface {
type HostListOptions struct {
ListOptions

StatusFilter HostStatus
AdditionalFilters []string
StatusFilter HostStatus
}

type Host struct {
6 changes: 6 additions & 0 deletions server/service/transport_hosts.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package service
import (
"context"
"net/http"
"strings"

"github.com/kolide/fleet/server/kolide"
"github.com/pkg/errors"
@@ -48,5 +49,10 @@ func decodeListHostsRequest(ctx context.Context, r *http.Request) (interface{},
if err != nil {
return nil, err
}

additionalInfoFiltersString := r.URL.Query().Get("additional_info_filters")
if additionalInfoFiltersString != "" {
hopt.AdditionalFilters = strings.Split(additionalInfoFiltersString, ",")
}
return listHostsRequest{ListOptions: hopt}, nil
}