Update osquery service methods for distributed queries (#476)

This commit is contained in:
Zachary Wasserman 2016-11-14 10:22:54 -08:00 committed by GitHub
parent b0e856c369
commit 9c38d6d19e
21 changed files with 271 additions and 73 deletions

View file

@ -6,6 +6,7 @@ import (
"github.com/kolide/kolide-ose/server/config" "github.com/kolide/kolide-ose/server/config"
"github.com/kolide/kolide-ose/server/datastore" "github.com/kolide/kolide-ose/server/datastore"
"github.com/kolide/kolide-ose/server/kolide" "github.com/kolide/kolide-ose/server/kolide"
"github.com/kolide/kolide-ose/server/pubsub"
"github.com/kolide/kolide-ose/server/service" "github.com/kolide/kolide-ose/server/service"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -82,7 +83,7 @@ To setup kolide infrastructure, use one of the available commands.
Enabled: &enabled, Enabled: &enabled,
Admin: &isAdmin, Admin: &isAdmin,
} }
svc, err := service.NewService(ds, kitlog.NewNopLogger(), config, nil, clock.C) svc, err := service.NewService(ds, pubsub.NewInmemQueryResults(), kitlog.NewNopLogger(), config, nil, clock.C)
if err != nil { if err != nil {
initFatal(err, "creating service") initFatal(err, "creating service")
} }

View file

@ -17,6 +17,7 @@ import (
"github.com/kolide/kolide-ose/server/datastore" "github.com/kolide/kolide-ose/server/datastore"
"github.com/kolide/kolide-ose/server/kolide" "github.com/kolide/kolide-ose/server/kolide"
"github.com/kolide/kolide-ose/server/mail" "github.com/kolide/kolide-ose/server/mail"
"github.com/kolide/kolide-ose/server/pubsub"
"github.com/kolide/kolide-ose/server/service" "github.com/kolide/kolide-ose/server/service"
"github.com/kolide/kolide-ose/server/version" "github.com/kolide/kolide-ose/server/version"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
@ -84,7 +85,7 @@ the way that the kolide server works.
} }
} }
svc, err := service.NewService(ds, logger, config, mailService, clock.C) svc, err := service.NewService(ds, pubsub.NewInmemQueryResults(), logger, config, mailService, clock.C)
if err != nil { if err != nil {
initFatal(err, "initializing service") initFatal(err, "initializing service")
} }
@ -334,7 +335,7 @@ func createDevLabels(ds kolide.Datastore, config config.KolideConfig) {
CreatedAt: time.Date(2016, time.October, 27, 8, 31, 16, 0, time.UTC), CreatedAt: time.Date(2016, time.October, 27, 8, 31, 16, 0, time.UTC),
UpdatedAt: time.Date(2016, time.October, 27, 8, 31, 16, 0, time.UTC), UpdatedAt: time.Date(2016, time.October, 27, 8, 31, 16, 0, time.UTC),
Name: "dev_label_apache", Name: "dev_label_apache",
Query: "select * from processes where nae like '%Apache%'", Query: "select * from processes where name like '%Apache%'",
}, },
{ {
CreatedAt: time.Now().Add(-1 * time.Hour), CreatedAt: time.Now().Add(-1 * time.Hour),

View file

@ -1,7 +1,6 @@
package kolide package kolide
import ( import (
"encoding/json"
"time" "time"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -101,9 +100,9 @@ const (
) )
type DistributedQueryResult struct { type DistributedQueryResult struct {
DistributedQueryCampaignID uint `json:"distributed_query_execution_id"` DistributedQueryCampaignID uint `json:"distributed_query_execution_id"`
Host Host `json:"host"` Host Host `json:"host"`
ResultJSON json.RawMessage `json:"result_json"` Rows []map[string]string `json:"rows"`
} }
type DistributedQueryExecution struct { type DistributedQueryExecution struct {

View file

@ -40,7 +40,12 @@ func (im *inmemQueryResults) WriteResult(result kolide.DistributedQueryResult) e
return errors.New("no subscribers for channel") return errors.New("no subscribers for channel")
} }
channel <- result select {
case channel <- result:
// intentionally do nothing
default:
return errors.New("no subscribers for channel")
}
return nil return nil
} }

View file

@ -1,7 +1,6 @@
package pubsub package pubsub
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
@ -96,7 +95,7 @@ func testQueryResultsStoreErrors(t *testing.T, store kolide.QueryResultStore) {
err := store.WriteResult( err := store.WriteResult(
kolide.DistributedQueryResult{ kolide.DistributedQueryResult{
DistributedQueryCampaignID: 1, DistributedQueryCampaignID: 1,
ResultJSON: json.RawMessage(`{"bing":"fds"}`), Rows: []map[string]string{{"bing": "fds"}},
Host: kolide.Host{ Host: kolide.Host{
ID: 4, ID: 4,
UpdatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(),
@ -118,7 +117,7 @@ func testQueryResultsStore(t *testing.T, store kolide.QueryResultStore) {
expected1 := []kolide.DistributedQueryResult{ expected1 := []kolide.DistributedQueryResult{
kolide.DistributedQueryResult{ kolide.DistributedQueryResult{
DistributedQueryCampaignID: 1, DistributedQueryCampaignID: 1,
ResultJSON: json.RawMessage(`{"foo":"bar"}`), Rows: []map[string]string{{"foo": "bar"}},
Host: kolide.Host{ Host: kolide.Host{
ID: 1, ID: 1,
// Note these times need to be set to avoid // Note these times need to be set to avoid
@ -130,7 +129,7 @@ func testQueryResultsStore(t *testing.T, store kolide.QueryResultStore) {
}, },
kolide.DistributedQueryResult{ kolide.DistributedQueryResult{
DistributedQueryCampaignID: 1, DistributedQueryCampaignID: 1,
ResultJSON: json.RawMessage(`{"whoo":"wahh"}`), Rows: []map[string]string{{"whoo": "wahh"}},
Host: kolide.Host{ Host: kolide.Host{
ID: 3, ID: 3,
UpdatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(),
@ -139,7 +138,7 @@ func testQueryResultsStore(t *testing.T, store kolide.QueryResultStore) {
}, },
kolide.DistributedQueryResult{ kolide.DistributedQueryResult{
DistributedQueryCampaignID: 1, DistributedQueryCampaignID: 1,
ResultJSON: json.RawMessage(`{"bing":"fds"}`), Rows: []map[string]string{{"bing": "fds"}},
Host: kolide.Host{ Host: kolide.Host{
ID: 4, ID: 4,
UpdatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(),
@ -157,7 +156,7 @@ func testQueryResultsStore(t *testing.T, store kolide.QueryResultStore) {
expected2 := []kolide.DistributedQueryResult{ expected2 := []kolide.DistributedQueryResult{
kolide.DistributedQueryResult{ kolide.DistributedQueryResult{
DistributedQueryCampaignID: 2, DistributedQueryCampaignID: 2,
ResultJSON: json.RawMessage(`{"tim":"tom"}`), Rows: []map[string]string{{"tim": "tom"}},
Host: kolide.Host{ Host: kolide.Host{
ID: 1, ID: 1,
UpdatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(),
@ -166,7 +165,7 @@ func testQueryResultsStore(t *testing.T, store kolide.QueryResultStore) {
}, },
kolide.DistributedQueryResult{ kolide.DistributedQueryResult{
DistributedQueryCampaignID: 2, DistributedQueryCampaignID: 2,
ResultJSON: json.RawMessage(`{"slim":"slam"}`), Rows: []map[string]string{{"slim": "slam"}},
Host: kolide.Host{ Host: kolide.Host{
ID: 3, ID: 3,
UpdatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(),

View file

@ -186,7 +186,7 @@ func TestGetNodeKey(t *testing.T) {
func TestAuthenticatedHost(t *testing.T) { func TestAuthenticatedHost(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
require.Nil(t, err) require.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
require.Nil(t, err) require.Nil(t, err)
endpoint := authenticatedHost( endpoint := authenticatedHost(

View file

@ -15,7 +15,7 @@ func TestAPIRoutes(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()

View file

@ -24,7 +24,7 @@ import (
func TestLogin(t *testing.T) { func TestLogin(t *testing.T) {
ds, _ := datastore.New("inmem", "") ds, _ := datastore.New("inmem", "")
svc, _ := newTestService(ds) svc, _ := newTestService(ds, nil)
users := createTestUsers(t, ds) users := createTestUsers(t, ds)
logger := kitlog.NewLogfmtLogger(os.Stdout) logger := kitlog.NewLogfmtLogger(os.Stdout)

View file

@ -13,7 +13,7 @@ import (
) )
// NewService creates a new service from the config struct // NewService creates a new service from the config struct
func NewService(ds kolide.Datastore, logger kitlog.Logger, kolideConfig config.KolideConfig, mailService kolide.MailService, c clock.Clock) (kolide.Service, error) { func NewService(ds kolide.Datastore, resultStore kolide.QueryResultStore, logger kitlog.Logger, kolideConfig config.KolideConfig, mailService kolide.MailService, c clock.Clock) (kolide.Service, error) {
var svc kolide.Service var svc kolide.Service
logFile := func(path string) io.Writer { logFile := func(path string) io.Writer {
@ -26,10 +26,11 @@ func NewService(ds kolide.Datastore, logger kitlog.Logger, kolideConfig config.K
} }
svc = service{ svc = service{
ds: ds, ds: ds,
logger: logger, resultStore: resultStore,
config: kolideConfig, logger: logger,
clock: c, config: kolideConfig,
clock: c,
osqueryStatusLogWriter: logFile(kolideConfig.Osquery.StatusLogFile), osqueryStatusLogWriter: logFile(kolideConfig.Osquery.StatusLogFile),
osqueryResultLogWriter: logFile(kolideConfig.Osquery.ResultLogFile), osqueryResultLogWriter: logFile(kolideConfig.Osquery.ResultLogFile),
@ -40,10 +41,11 @@ func NewService(ds kolide.Datastore, logger kitlog.Logger, kolideConfig config.K
} }
type service struct { type service struct {
ds kolide.Datastore ds kolide.Datastore
logger kitlog.Logger resultStore kolide.QueryResultStore
config config.KolideConfig logger kitlog.Logger
clock clock.Clock config config.KolideConfig
clock clock.Clock
osqueryStatusLogWriter io.Writer osqueryStatusLogWriter io.Writer
osqueryResultLogWriter io.Writer osqueryResultLogWriter io.Writer

View file

@ -13,7 +13,7 @@ import (
func TestCreateAppConfig(t *testing.T) { func TestCreateAppConfig(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
require.Nil(t, err) require.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
require.Nil(t, err) require.Nil(t, err)
var appConfigTests = []struct { var appConfigTests = []struct {
configPayload kolide.AppConfigPayload configPayload kolide.AppConfigPayload

View file

@ -13,7 +13,7 @@ func TestListHosts(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -36,7 +36,7 @@ func TestGetHost(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -57,7 +57,7 @@ func TestDeleteHost(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()

View file

@ -13,7 +13,7 @@ func TestListLabels(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -38,7 +38,7 @@ func TestGetLabel(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -60,7 +60,7 @@ func TestNewLabel(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -85,7 +85,7 @@ func TestDeleteLabel(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()

View file

@ -158,6 +158,10 @@ const hostLabelQueryPrefix = "kolide_label_query_"
// provided as a detail query. // provided as a detail query.
const hostDetailQueryPrefix = "kolide_detail_query_" const hostDetailQueryPrefix = "kolide_detail_query_"
// hostDistributedQueryPrefix is appended before the query name when a query is
// run from a distributed query campaign
const hostDistributedQueryPrefix = "kolide_distributed_query_"
// detailQueries defines the detail queries that should be run on the host, as // detailQueries defines the detail queries that should be run on the host, as
// well as how the results of those queries should be ingested into the // well as how the results of those queries should be ingested into the
// kolide.Host data model. This map should not be modified at runtime. // kolide.Host data model. This map should not be modified at runtime.
@ -295,7 +299,14 @@ func (svc service) GetDistributedQueries(ctx context.Context) (map[string]string
queries[hostLabelQueryPrefix+name] = query queries[hostLabelQueryPrefix+name] = query
} }
// TODO: retrieve the active distributed queries for this host distributedQueries, err := svc.ds.DistributedQueriesForHost(&host)
if err != nil {
return nil, osqueryError{message: "retrieving query campaigns: " + err.Error()}
}
for id, query := range distributedQueries {
queries[hostDistributedQueryPrefix+strconv.Itoa(int(id))] = query
}
return queries, nil return queries, nil
} }
@ -327,6 +338,43 @@ func (svc service) ingestLabelQuery(host kolide.Host, query string, rows []map[s
return nil return nil
} }
// ingestDistributedQuery takes the results of a distributed query and modifies the
// provided kolide.Host appropriately.
func (svc service) ingestDistributedQuery(host kolide.Host, name string, rows []map[string]string) error {
trimmedQuery := strings.TrimPrefix(name, hostDistributedQueryPrefix)
campaignID, err := strconv.Atoi(trimmedQuery)
if err != nil {
return osqueryError{message: "unable to parse campaign ID: " + trimmedQuery}
}
// Write the results to the pubsub store
res := kolide.DistributedQueryResult{
DistributedQueryCampaignID: uint(campaignID),
Host: host,
Rows: rows,
}
err = svc.resultStore.WriteResult(res)
if err != nil {
return osqueryError{message: "writing results: " + err.Error()}
}
// Record execution of the query
exec := kolide.DistributedQueryExecution{
HostID: host.ID,
DistributedQueryCampaignID: uint(campaignID),
Status: kolide.ExecutionSucceeded,
}
_, err = svc.ds.NewDistributedQueryExecution(exec)
if err != nil {
return osqueryError{message: "recording execution: " + err.Error()}
}
return nil
}
func (svc service) SubmitDistributedQueryResults(ctx context.Context, results kolide.OsqueryDistributedQueryResults) error { func (svc service) SubmitDistributedQueryResults(ctx context.Context, results kolide.OsqueryDistributedQueryResults) error {
host, ok := hostctx.FromContext(ctx) host, ok := hostctx.FromContext(ctx)
if !ok { if !ok {
@ -347,8 +395,11 @@ func (svc service) SubmitDistributedQueryResults(ctx context.Context, results ko
case strings.HasPrefix(query, hostLabelQueryPrefix): case strings.HasPrefix(query, hostLabelQueryPrefix):
err = svc.ingestLabelQuery(host, query, rows, labelResults) err = svc.ingestLabelQuery(host, query, rows, labelResults)
case strings.HasPrefix(query, hostDistributedQueryPrefix):
err = svc.ingestDistributedQuery(host, query, rows)
default: default:
// TODO ingest regular distributed query results err = osqueryError{message: "unknown query prefix: " + query}
} }
if err != nil { if err != nil {

View file

@ -4,7 +4,9 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strconv"
"strings" "strings"
"sync"
"testing" "testing"
"time" "time"
@ -14,6 +16,7 @@ import (
hostctx "github.com/kolide/kolide-ose/server/contexts/host" hostctx "github.com/kolide/kolide-ose/server/contexts/host"
"github.com/kolide/kolide-ose/server/datastore" "github.com/kolide/kolide-ose/server/datastore"
"github.com/kolide/kolide-ose/server/kolide" "github.com/kolide/kolide-ose/server/kolide"
"github.com/kolide/kolide-ose/server/pubsub"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -22,7 +25,7 @@ func TestEnrollAgent(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -44,7 +47,7 @@ func TestEnrollAgentIncorrectEnrollSecret(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -68,7 +71,7 @@ func TestSubmitStatusLogs(t *testing.T) {
mockClock := clock.NewMockClock() mockClock := clock.NewMockClock()
svc, err := newTestServiceWithClock(ds, mockClock) svc, err := newTestServiceWithClock(ds, nil, mockClock)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -140,7 +143,7 @@ func TestSubmitResultLogs(t *testing.T) {
mockClock := clock.NewMockClock() mockClock := clock.NewMockClock()
svc, err := newTestServiceWithClock(ds, mockClock) svc, err := newTestServiceWithClock(ds, nil, mockClock)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -241,7 +244,7 @@ func TestLabelQueries(t *testing.T) {
mockClock := clock.NewMockClock() mockClock := clock.NewMockClock()
svc, err := newTestServiceWithClock(ds, mockClock) svc, err := newTestServiceWithClock(ds, nil, mockClock)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -368,7 +371,7 @@ func TestGetClientConfig(t *testing.T) {
mockClock := clock.NewMockClock() mockClock := clock.NewMockClock()
svc, err := newTestServiceWithClock(ds, mockClock) svc, err := newTestServiceWithClock(ds, nil, mockClock)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -449,7 +452,7 @@ func TestDetailQueries(t *testing.T) {
mockClock := clock.NewMockClock() mockClock := clock.NewMockClock()
svc, err := newTestServiceWithClock(ds, mockClock) svc, err := newTestServiceWithClock(ds, nil, mockClock)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -584,3 +587,140 @@ func TestDetailQueries(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.Len(t, queries, len(detailQueries)) assert.Len(t, queries, len(detailQueries))
} }
func TestDistributedQueries(t *testing.T) {
ds, err := datastore.New("inmem", "")
require.Nil(t, err)
mockClock := clock.NewMockClock()
rs := pubsub.NewInmemQueryResults()
svc, err := newTestServiceWithClock(ds, rs, mockClock)
require.Nil(t, err)
ctx := context.Background()
nodeKey, err := svc.EnrollAgent(ctx, "", "host123")
require.Nil(t, err)
host, err := ds.AuthenticateHost(nodeKey)
require.Nil(t, err)
ctx = hostctx.NewContext(ctx, *host)
// Create label
n := "foo"
q := "select * from foo;"
label, err := svc.NewLabel(ctx, kolide.LabelPayload{
Name: &n,
Query: &q,
})
require.Nil(t, err)
labelId := strconv.Itoa(int(label.ID))
// Record match with label
err = ds.RecordLabelQueryExecutions(host, map[string]bool{labelId: true}, mockClock.Now())
require.Nil(t, err)
// Create query
n = "time"
q = "select year, month, day, hour, minutes, seconds from time"
query, err := svc.NewQuery(ctx, kolide.QueryPayload{
Name: &n,
Query: &q,
})
require.Nil(t, err)
// Create query campaign
c1 := kolide.DistributedQueryCampaign{
QueryID: query.ID,
Status: kolide.QueryRunning,
}
// TODO use service method
c1, err = ds.NewDistributedQueryCampaign(c1)
require.Nil(t, err)
// Add a target to the campaign (targeting the matching label)
target := kolide.DistributedQueryCampaignTarget{
Type: kolide.TargetLabel,
DistributedQueryCampaignID: c1.ID,
TargetID: label.ID,
}
// TODO use service method
target, err = ds.NewDistributedQueryCampaignTarget(target)
require.Nil(t, err)
queryKey := fmt.Sprintf("%s%d", hostDistributedQueryPrefix, c1.ID)
// Now we should get the active distributed query
queries, err := svc.GetDistributedQueries(ctx)
require.Nil(t, err)
assert.Len(t, queries, len(detailQueries)+1)
assert.Equal(t, q, queries[queryKey])
expectedRows := []map[string]string{
{
"year": "2016",
"month": "11",
"day": "11",
"hour": "6",
"minutes": "12",
"seconds": "10",
},
}
results := map[string][]map[string]string{
queryKey: expectedRows,
}
// Submit results (should error because no one is listening)
err = svc.SubmitDistributedQueryResults(ctx, results)
assert.NotNil(t, err)
// TODO use service method
readChan, err := rs.ReadChannel(ctx, c1)
require.Nil(t, err)
// We need to listen for the result in a separate thread to prevent the
// write to the result channel from failing
var waitSetup, waitComplete sync.WaitGroup
waitSetup.Add(1)
waitComplete.Add(1)
go func() {
waitSetup.Done()
select {
case val := <-readChan:
if res, ok := val.(kolide.DistributedQueryResult); ok {
assert.Equal(t, c1.ID, res.DistributedQueryCampaignID)
assert.Equal(t, expectedRows, res.Rows)
assert.Equal(t, *host, res.Host)
} else {
t.Error("Wrong result type")
}
assert.NotNil(t, val)
case <-time.After(1 * time.Second):
t.Error("No result received")
}
waitComplete.Done()
}()
waitSetup.Wait()
// Sleep a short time to ensure that the above goroutine is blocking on
// the channel read (the waitSetup.Wait() is not necessarily sufficient
// if there is a context switch immediately after waitSetup.Done() is
// called). This should be a small price to pay to prevent flakiness in
// this test.
time.Sleep(10 * time.Millisecond)
err = svc.SubmitDistributedQueryResults(ctx, results)
require.Nil(t, err)
// Now the distributed query should be completed and not returned
queries, err = svc.GetDistributedQueries(ctx)
require.Nil(t, err)
assert.Len(t, queries, len(detailQueries))
assert.NotContains(t, queries, queryKey)
waitComplete.Wait()
}

View file

@ -13,7 +13,7 @@ func TestListPacks(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -36,7 +36,7 @@ func TestGetPack(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -58,7 +58,7 @@ func TestNewPack(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -79,7 +79,7 @@ func TestModifyPack(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -105,7 +105,7 @@ func TestDeletePack(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -130,7 +130,7 @@ func TestAddQueryToPack(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -166,7 +166,7 @@ func TestGetQueriesInPack(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -198,7 +198,7 @@ func TestRemoveQueryFromPack(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()

View file

@ -13,7 +13,7 @@ func TestListQueries(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -37,7 +37,7 @@ func TestGetQuery(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -60,7 +60,7 @@ func TestNewQuery(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -83,7 +83,7 @@ func TestModifyQuery(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -110,7 +110,7 @@ func TestDeleteQuery(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
ctx := context.Background() ctx := context.Background()

View file

@ -16,7 +16,7 @@ const bcryptCost = 6
func TestAuthenticate(t *testing.T) { func TestAuthenticate(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
require.Nil(t, err) require.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
require.Nil(t, err) require.Nil(t, err)
users := createTestUsers(t, ds) users := createTestUsers(t, ds)

View file

@ -16,7 +16,7 @@ func TestSearchTargets(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
require.Nil(t, err) require.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
require.Nil(t, err) require.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -47,7 +47,7 @@ func TestCountHostsInTargets(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
require.Nil(t, err) require.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
require.Nil(t, err) require.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -143,7 +143,7 @@ func TestSearchWithOmit(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
require.Nil(t, err) require.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
require.Nil(t, err) require.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -195,7 +195,7 @@ func TestSearchHostsInLabels(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
require.Nil(t, err) require.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
require.Nil(t, err) require.Nil(t, err)
ctx := context.Background() ctx := context.Background()
@ -248,7 +248,7 @@ func TestSearchResultsLimit(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
require.Nil(t, err) require.Nil(t, err)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
require.Nil(t, err) require.Nil(t, err)
ctx := context.Background() ctx := context.Background()

View file

@ -19,7 +19,7 @@ func TestAuthenticatedUser(t *testing.T) {
ds, err := datastore.New("inmem", "") ds, err := datastore.New("inmem", "")
assert.Nil(t, err) assert.Nil(t, err)
createTestUsers(t, ds) createTestUsers(t, ds)
svc, err := newTestService(ds) svc, err := newTestService(ds, nil)
assert.Nil(t, err) assert.Nil(t, err)
admin1, err := ds.User("admin1") admin1, err := ds.User("admin1")
assert.Nil(t, err) assert.Nil(t, err)
@ -113,7 +113,7 @@ func TestRequestPasswordReset(t *testing.T) {
func TestCreateUser(t *testing.T) { func TestCreateUser(t *testing.T) {
ds, _ := datastore.New("inmem", "") ds, _ := datastore.New("inmem", "")
svc, _ := newTestService(ds) svc, _ := newTestService(ds, nil)
invites := setupInvites(t, ds, []string{"admin2@example.com"}) invites := setupInvites(t, ds, []string{"admin2@example.com"})
ctx := context.Background() ctx := context.Background()
@ -236,7 +236,7 @@ func setupInvites(t *testing.T, ds kolide.Datastore, emails []string) map[string
func TestChangeUserPassword(t *testing.T) { func TestChangeUserPassword(t *testing.T) {
ds, _ := datastore.New("inmem", "") ds, _ := datastore.New("inmem", "")
svc, _ := newTestService(ds) svc, _ := newTestService(ds, nil)
createTestUsers(t, ds) createTestUsers(t, ds)
var passwordChangeTests = []struct { var passwordChangeTests = []struct {
token string token string

View file

@ -10,12 +10,12 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func newTestService(ds kolide.Datastore) (kolide.Service, error) { func newTestService(ds kolide.Datastore, rs kolide.QueryResultStore) (kolide.Service, error) {
return NewService(ds, kitlog.NewNopLogger(), config.TestConfig(), nil, clock.C) return NewService(ds, rs, kitlog.NewNopLogger(), config.TestConfig(), nil, clock.C)
} }
func newTestServiceWithClock(ds kolide.Datastore, c clock.Clock) (kolide.Service, error) { func newTestServiceWithClock(ds kolide.Datastore, rs kolide.QueryResultStore, c clock.Clock) (kolide.Service, error) {
return NewService(ds, kitlog.NewNopLogger(), config.TestConfig(), nil, c) return NewService(ds, rs, kitlog.NewNopLogger(), config.TestConfig(), nil, c)
} }
func createTestUsers(t *testing.T, ds kolide.Datastore) map[string]kolide.User { func createTestUsers(t *testing.T, ds kolide.Datastore) map[string]kolide.User {

View file

@ -2,13 +2,13 @@
"options": { "options": {
"disable_distributed": "false", "disable_distributed": "false",
"distributed_plugin": "tls", "distributed_plugin": "tls",
"distributed_interval": 30, "distributed_interval": 10,
"distributed_tls_max_attempts": 3, "distributed_tls_max_attempts": 3,
"distributed_tls_read_endpoint": "/api/v1/osquery/distributed/read", "distributed_tls_read_endpoint": "/api/v1/osquery/distributed/read",
"distributed_tls_write_endpoint": "/api/v1/osquery/distributed/write", "distributed_tls_write_endpoint": "/api/v1/osquery/distributed/write",
"logger_plugin": "tls", "logger_plugin": "tls",
"logger_tls_endpoint": "/api/v1/osquery/log", "logger_tls_endpoint": "/api/v1/osquery/log",
"logger_tls_period": 5 "logger_tls_period": 60
}, },
"schedule": { "schedule": {
@ -17,7 +17,7 @@
// The exact query to run. // The exact query to run.
"query": "SELECT hostname, cpu_brand, physical_memory FROM system_info;", "query": "SELECT hostname, cpu_brand, physical_memory FROM system_info;",
// The interval in seconds to run this query, not an exact interval. // The interval in seconds to run this query, not an exact interval.
"interval": 5 "interval": 60
} }
}, },