diff --git a/server/datastore/datastore_packs_test.go b/server/datastore/datastore_packs_test.go index 616450ed5c..2e4365e8cc 100644 --- a/server/datastore/datastore_packs_test.go +++ b/server/datastore/datastore_packs_test.go @@ -55,8 +55,6 @@ func testGetHostsInPack(t *testing.T, ds kolide.Datastore) { t.Skip("inmem is deprecated") } - user := test.NewUser(t, ds, "Zach", "zwass", "zwass@kolide.co", true) - mockClock := clock.NewMockClock() p1, err := ds.NewPack(&kolide.Pack{ @@ -64,23 +62,6 @@ func testGetHostsInPack(t *testing.T, ds kolide.Datastore) { }) require.Nil(t, err) - q1, err := ds.NewQuery(&kolide.Query{ - Name: "foo", - Query: "foo", - AuthorID: user.ID, - }) - require.Nil(t, err) - - q2, err := ds.NewQuery(&kolide.Query{ - Name: "bar", - Query: "bar", - AuthorID: user.ID, - }) - require.Nil(t, err) - - test.NewScheduledQuery(t, ds, p1.ID, q1.ID, 60, false, false) - test.NewScheduledQuery(t, ds, p1.ID, q2.ID, 60, false, false) - l1, err := ds.NewLabel(&kolide.Label{ Name: "foo", }) @@ -151,3 +132,90 @@ func testAddLabelToPackTwice(t *testing.T, ds kolide.Datastore) { assert.Nil(t, err) assert.Len(t, labels, 1) } + +func testApplyPackSpecRoundtrip(t *testing.T, ds kolide.Datastore) { + zwass := test.NewUser(t, ds, "Zach", "zwass", "zwass@kolide.co", true) + queries := []*kolide.Query{ + {Name: "foo", Description: "get the foos", Query: "select * from foo"}, + {Name: "bar", Description: "do some bars", Query: "select baz from bar"}, + } + // Zach creates some queries + err := ds.ApplyQueries(zwass.ID, queries) + require.Nil(t, err) + + test.NewLabel(t, ds, "foo", "select * from foo") + test.NewLabel(t, ds, "bar", "select * from bar") + test.NewLabel(t, ds, "bing", "select * from bing") + + boolPtr := func(b bool) *bool { return &b } + uintPtr := func(x uint) *uint { return &x } + stringPtr := func(s string) *string { return &s } + specs := []*kolide.PackSpec{ + &kolide.PackSpec{ + ID: 1, + Name: "test_pack", + Targets: kolide.PackSpecTargets{ + Labels: []string{ + "foo", + "bar", + "bing", + }, + }, + Queries: []kolide.PackSpecQuery{ + kolide.PackSpecQuery{ + QueryName: queries[0].Name, + Description: "test_foo", + Interval: 42, + }, + kolide.PackSpecQuery{ + QueryName: queries[0].Name, + Name: "foo_snapshot", + Interval: 600, + Snapshot: boolPtr(true), + }, + kolide.PackSpecQuery{ + QueryName: queries[1].Name, + Interval: 600, + Removed: boolPtr(false), + Shard: uintPtr(73), + Platform: stringPtr("foobar"), + Version: stringPtr("0.0.0.0.0.1"), + }, + }, + }, + } + + err = ds.ApplyPackSpecs(specs) + require.Nil(t, err) + + gotSpec, err := ds.GetPackSpecs() + + require.Nil(t, err) + assert.Equal(t, specs, gotSpec) +} + +func testApplyPackSpecMissingQueries(t *testing.T, ds kolide.Datastore) { + // Do not define queries mentioned in spec + + specs := []*kolide.PackSpec{ + &kolide.PackSpec{ + ID: 1, + Name: "test_pack", + Targets: kolide.PackSpecTargets{ + Labels: []string{}, + }, + Queries: []kolide.PackSpecQuery{ + kolide.PackSpecQuery{ + QueryName: "bar", + Interval: 600, + }, + }, + }, + } + + // Should error due to unkown query + err := ds.ApplyPackSpecs(specs) + if assert.NotNil(t, err) { + assert.Contains(t, err.Error(), "unknown query 'bar'") + } +} diff --git a/server/datastore/datastore_queries_test.go b/server/datastore/datastore_queries_test.go index 5f9eca7916..a5dcdb4b4e 100644 --- a/server/datastore/datastore_queries_test.go +++ b/server/datastore/datastore_queries_test.go @@ -2,15 +2,76 @@ package datastore import ( "fmt" + "sort" "testing" "github.com/kolide/fleet/server/kolide" "github.com/kolide/fleet/server/test" - "github.com/patrickmn/sortutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func testApplyQueries(t *testing.T, ds kolide.Datastore) { + zwass := test.NewUser(t, ds, "Zach", "zwass", "zwass@kolide.co", true) + groob := test.NewUser(t, ds, "Victor", "groob", "victor@kolide.co", true) + expectedQueries := []*kolide.Query{ + {Name: "foo", Description: "get the foos", Query: "select * from foo"}, + {Name: "bar", Description: "do some bars", Query: "select baz from bar"}, + } + + // Zach creates some queries + err := ds.ApplyQueries(zwass.ID, expectedQueries) + require.Nil(t, err) + + queries, err := ds.ListQueries(kolide.ListOptions{}) + require.Nil(t, err) + require.Len(t, queries, len(expectedQueries)) + for i, q := range queries { + comp := expectedQueries[i] + assert.Equal(t, comp.Name, q.Name) + assert.Equal(t, comp.Description, q.Description) + assert.Equal(t, comp.Query, q.Query) + assert.Equal(t, zwass.ID, q.AuthorID) + } + + // Victor modifies a query (but also pushes the same version of the + // first query) + expectedQueries[1].Query = "not really a valid query ;)" + err = ds.ApplyQueries(groob.ID, expectedQueries) + require.Nil(t, err) + + queries, err = ds.ListQueries(kolide.ListOptions{}) + require.Nil(t, err) + require.Len(t, queries, len(expectedQueries)) + for i, q := range queries { + comp := expectedQueries[i] + assert.Equal(t, comp.Name, q.Name) + assert.Equal(t, comp.Description, q.Description) + assert.Equal(t, comp.Query, q.Query) + assert.Equal(t, groob.ID, q.AuthorID) + } + + // Zach adds a third query (but does not re-apply the others) + expectedQueries = append(expectedQueries, + &kolide.Query{Name: "trouble", Description: "Look out!", Query: "select * from time"}, + ) + err = ds.ApplyQueries(zwass.ID, []*kolide.Query{expectedQueries[2]}) + require.Nil(t, err) + + queries, err = ds.ListQueries(kolide.ListOptions{}) + require.Nil(t, err) + require.Len(t, queries, len(expectedQueries)) + for i, q := range queries { + comp := expectedQueries[i] + assert.Equal(t, comp.Name, q.Name) + assert.Equal(t, comp.Description, q.Description) + assert.Equal(t, comp.Query, q.Query) + } + assert.Equal(t, groob.ID, queries[0].AuthorID) + assert.Equal(t, groob.ID, queries[1].AuthorID) + assert.Equal(t, zwass.ID, queries[2].AuthorID) +} + func testDeleteQuery(t *testing.T, ds kolide.Datastore) { user := test.NewUser(t, ds, "Zach", "zwass", "zwass@kolide.co", true) @@ -137,51 +198,133 @@ func testListQuery(t *testing.T, ds kolide.Datastore) { assert.Equal(t, 10, len(results)) } -func checkPacks(t *testing.T, expected []kolide.Pack, actual []kolide.Pack) { - sortutil.AscByField(expected, "ID") - sortutil.AscByField(actual, "ID") - assert.Equal(t, expected, actual) -} - func testLoadPacksForQueries(t *testing.T, ds kolide.Datastore) { - user := test.NewUser(t, ds, "Zach", "zwass", "zwass@kolide.co", true) - - q1 := test.NewQuery(t, ds, "q1", "select * from time", user.ID, true) - q2 := test.NewQuery(t, ds, "q2", "select * from osquery_info", user.ID, true) - - p1 := test.NewPack(t, ds, "p1") - p2 := test.NewPack(t, ds, "p2") - p3 := test.NewPack(t, ds, "p3") - - var err error - - test.NewScheduledQuery(t, ds, p2.ID, q1.ID, 60, false, false) - - q1, err = ds.Query(q1.ID) + zwass := test.NewUser(t, ds, "Zach", "zwass", "zwass@kolide.co", true) + queries := []*kolide.Query{ + {Name: "q1", Query: "select * from time"}, + {Name: "q2", Query: "select * from osquery_info"}, + } + err := ds.ApplyQueries(zwass.ID, queries) require.Nil(t, err) - q2, err = ds.Query(q2.ID) - require.Nil(t, err) - checkPacks(t, []kolide.Pack{*p2}, q1.Packs) - checkPacks(t, []kolide.Pack{}, q2.Packs) - test.NewScheduledQuery(t, ds, p1.ID, q2.ID, 60, false, false) - test.NewScheduledQuery(t, ds, p3.ID, q2.ID, 60, false, false) + specs := []*kolide.PackSpec{ + &kolide.PackSpec{Name: "p1"}, + &kolide.PackSpec{Name: "p2"}, + &kolide.PackSpec{Name: "p3"}, + } + err = ds.ApplyPackSpecs(specs) + require.Nil(t, err) - q1, err = ds.Query(q1.ID) + q0, exists, err := ds.QueryByName(queries[0].Name) require.Nil(t, err) - q2, err = ds.Query(q2.ID) - require.Nil(t, err) - checkPacks(t, []kolide.Pack{*p2}, q1.Packs) - checkPacks(t, []kolide.Pack{*p1, *p3}, q2.Packs) + require.True(t, exists) + assert.Empty(t, q0.Packs) - test.NewScheduledQuery(t, ds, p3.ID, q1.ID, 60, false, false) + q1, exists, err := ds.QueryByName(queries[1].Name) + require.Nil(t, err) + require.True(t, exists) + assert.Empty(t, q1.Packs) - q1, err = ds.Query(q1.ID) + specs = []*kolide.PackSpec{ + &kolide.PackSpec{ + Name: "p2", + Queries: []kolide.PackSpecQuery{ + kolide.PackSpecQuery{ + QueryName: queries[0].Name, + Interval: 60, + }, + }, + }, + } + err = ds.ApplyPackSpecs(specs) require.Nil(t, err) - q2, err = ds.Query(q2.ID) + + q0, exists, err = ds.QueryByName(queries[0].Name) require.Nil(t, err) - checkPacks(t, []kolide.Pack{*p2, *p3}, q1.Packs) - checkPacks(t, []kolide.Pack{*p1, *p3}, q2.Packs) + require.True(t, exists) + if assert.Len(t, q0.Packs, 1) { + assert.Equal(t, "p2", q0.Packs[0].Name) + } + + q1, exists, err = ds.QueryByName(queries[1].Name) + require.Nil(t, err) + require.True(t, exists) + assert.Empty(t, q1.Packs) + + specs = []*kolide.PackSpec{ + &kolide.PackSpec{ + Name: "p1", + Queries: []kolide.PackSpecQuery{ + kolide.PackSpecQuery{ + QueryName: queries[1].Name, + Interval: 60, + }, + }, + }, + &kolide.PackSpec{ + Name: "p3", + Queries: []kolide.PackSpecQuery{ + kolide.PackSpecQuery{ + QueryName: queries[1].Name, + Interval: 60, + }, + }, + }, + } + err = ds.ApplyPackSpecs(specs) + require.Nil(t, err) + + q0, exists, err = ds.QueryByName(queries[0].Name) + require.Nil(t, err) + require.True(t, exists) + if assert.Len(t, q0.Packs, 1) { + assert.Equal(t, "p2", q0.Packs[0].Name) + } + + q1, exists, err = ds.QueryByName(queries[1].Name) + require.Nil(t, err) + require.True(t, exists) + if assert.Len(t, q1.Packs, 2) { + sort.Slice(q1.Packs, func(i, j int) bool { return q1.Packs[i].Name < q1.Packs[j].Name }) + assert.Equal(t, "p1", q1.Packs[0].Name) + assert.Equal(t, "p3", q1.Packs[1].Name) + } + + specs = []*kolide.PackSpec{ + &kolide.PackSpec{ + Name: "p3", + Queries: []kolide.PackSpecQuery{ + kolide.PackSpecQuery{ + QueryName: queries[0].Name, + Interval: 60, + }, + kolide.PackSpecQuery{ + QueryName: queries[1].Name, + Interval: 60, + }, + }, + }, + } + err = ds.ApplyPackSpecs(specs) + require.Nil(t, err) + + q0, exists, err = ds.QueryByName(queries[0].Name) + require.Nil(t, err) + require.True(t, exists) + if assert.Len(t, q0.Packs, 2) { + sort.Slice(q0.Packs, func(i, j int) bool { return q0.Packs[i].Name < q0.Packs[j].Name }) + assert.Equal(t, "p2", q0.Packs[0].Name) + assert.Equal(t, "p3", q0.Packs[1].Name) + } + + q1, exists, err = ds.QueryByName(queries[1].Name) + require.Nil(t, err) + require.True(t, exists) + if assert.Len(t, q1.Packs, 2) { + sort.Slice(q1.Packs, func(i, j int) bool { return q1.Packs[i].Name < q1.Packs[j].Name }) + assert.Equal(t, "p1", q1.Packs[0].Name) + assert.Equal(t, "p3", q1.Packs[1].Name) + } } func testDuplicateNewQuery(t *testing.T, ds kolide.Datastore) { diff --git a/server/datastore/datastore_scheduled_queries_test.go b/server/datastore/datastore_scheduled_queries_test.go index ea9f6e98ca..d29221dd8e 100644 --- a/server/datastore/datastore_scheduled_queries_test.go +++ b/server/datastore/datastore_scheduled_queries_test.go @@ -9,113 +9,69 @@ import ( "github.com/stretchr/testify/require" ) -func testNewScheduledQuery(t *testing.T, ds kolide.Datastore) { - u1 := test.NewUser(t, ds, "Admin", "admin", "admin@kolide.co", true) - q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) - p1 := test.NewPack(t, ds, "baz") - - query, err := ds.NewScheduledQuery(&kolide.ScheduledQuery{ - PackID: p1.ID, - QueryID: q1.ID, - }) - require.Nil(t, err) - assert.Equal(t, "foo", query.Name) - assert.Equal(t, "select * from time;", query.Query) -} - -func testScheduledQuery(t *testing.T, ds kolide.Datastore) { - u1 := test.NewUser(t, ds, "Admin", "admin", "admin@kolide.co", true) - q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) - p1 := test.NewPack(t, ds, "baz") - sq1 := test.NewScheduledQuery(t, ds, p1.ID, q1.ID, 60, false, false) - - query, err := ds.ScheduledQuery(sq1.ID) - require.Nil(t, err) - assert.Equal(t, uint(60), query.Interval) -} - -func testDeleteScheduledQuery(t *testing.T, ds kolide.Datastore) { - u1 := test.NewUser(t, ds, "Admin", "admin", "admin@kolide.co", true) - q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) - p1 := test.NewPack(t, ds, "baz") - sq1 := test.NewScheduledQuery(t, ds, p1.ID, q1.ID, 60, false, false) - - query, err := ds.ScheduledQuery(sq1.ID) - require.Nil(t, err) - assert.Equal(t, uint(60), query.Interval) - - err = ds.DeleteScheduledQuery(sq1.ID) - require.Nil(t, err) - - _, err = ds.ScheduledQuery(sq1.ID) - require.NotNil(t, err) -} - func testListScheduledQueriesInPack(t *testing.T, ds kolide.Datastore) { - u1 := test.NewUser(t, ds, "Admin", "admin", "admin@kolide.co", true) - q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) - q2 := test.NewQuery(t, ds, "bar", "select * from time;", u1.ID, true) - p1 := test.NewPack(t, ds, "baz") - - test.NewScheduledQuery(t, ds, p1.ID, q1.ID, 60, false, false) - - queries, err := ds.ListScheduledQueriesInPack(p1.ID, kolide.ListOptions{}) - require.Nil(t, err) - require.Len(t, queries, 1) - assert.Equal(t, uint(60), queries[0].Interval) - - test.NewScheduledQuery(t, ds, p1.ID, q2.ID, 60, false, false) - test.NewScheduledQuery(t, ds, p1.ID, q2.ID, 60, true, false) - - queries, err = ds.ListScheduledQueriesInPack(p1.ID, kolide.ListOptions{}) - require.Nil(t, err) - require.Len(t, queries, 3) -} - -func testSaveScheduledQuery(t *testing.T, ds kolide.Datastore) { - u1 := test.NewUser(t, ds, "Admin", "admin", "admin@kolide.co", true) - q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) - p1 := test.NewPack(t, ds, "baz") - sq1 := test.NewScheduledQuery(t, ds, p1.ID, q1.ID, 60, false, false) - - query, err := ds.ScheduledQuery(sq1.ID) - require.Nil(t, err) - assert.Equal(t, uint(60), query.Interval) - - query.Interval = uint(120) - query, err = ds.SaveScheduledQuery(query) - require.Nil(t, err) - assert.Equal(t, uint(120), query.Interval) - - queryVerify, err := ds.ScheduledQuery(sq1.ID) - require.Nil(t, err) - assert.Equal(t, uint(120), queryVerify.Interval) -} - -func testScheduledQueryWithDeletedPack(t *testing.T, ds kolide.Datastore) { - // When a pack is soft-deleted, it should not appear in the list of - // packs associated with a query. - - if ds.Name() == "inmem" { - t.Skip("inmem is being deprecated, test skipped") + zwass := test.NewUser(t, ds, "Zach", "zwass", "zwass@kolide.co", true) + queries := []*kolide.Query{ + {Name: "foo", Description: "get the foos", Query: "select * from foo"}, + {Name: "bar", Description: "do some bars", Query: "select baz from bar"}, } - - user := test.NewUser(t, ds, "Zach", "zwass", "zwass@kolide.co", true) - query := test.NewQuery(t, ds, "q1", "select 1", user.ID, true) - pack := test.NewPack(t, ds, "foobar_pack") - test.NewScheduledQuery(t, ds, pack.ID, query.ID, 60, false, false) - - actual, err := ds.Query(query.ID) + err := ds.ApplyQueries(zwass.ID, queries) require.Nil(t, err) - assert.Equal(t, "q1", actual.Name) - assert.Equal(t, "select 1", actual.Query) - assert.Equal(t, []kolide.Pack{*pack}, actual.Packs) - require.Nil(t, ds.DeletePack(pack.ID)) - - actual, err = ds.Query(query.ID) + specs := []*kolide.PackSpec{ + &kolide.PackSpec{ + Name: "baz", + Targets: kolide.PackSpecTargets{Labels: []string{}}, + Queries: []kolide.PackSpecQuery{ + kolide.PackSpecQuery{ + QueryName: queries[0].Name, + Description: "test_foo", + Interval: 60, + }, + }, + }, + } + err = ds.ApplyPackSpecs(specs) require.Nil(t, err) - assert.Equal(t, "q1", actual.Name) - assert.Equal(t, "select 1", actual.Query) - assert.Empty(t, actual.Packs) + + gotQueries, err := ds.ListScheduledQueriesInPack(1, kolide.ListOptions{}) + require.Nil(t, err) + require.Len(t, gotQueries, 1) + assert.Equal(t, uint(60), gotQueries[0].Interval) + assert.Equal(t, "test_foo", gotQueries[0].Description) + assert.Equal(t, "select * from foo", gotQueries[0].Query) + + boolPtr := func(b bool) *bool { return &b } + specs = []*kolide.PackSpec{ + &kolide.PackSpec{ + Name: "baz", + Targets: kolide.PackSpecTargets{Labels: []string{}}, + Queries: []kolide.PackSpecQuery{ + kolide.PackSpecQuery{ + QueryName: queries[0].Name, + Description: "test_foo", + Interval: 60, + }, + kolide.PackSpecQuery{ + QueryName: queries[1].Name, + Name: "test bar", + Description: "test_bar", + Interval: 60, + }, + kolide.PackSpecQuery{ + QueryName: queries[1].Name, + Name: "test bar snapshot", + Description: "test_bar", + Interval: 60, + Snapshot: boolPtr(true), + }, + }, + }, + } + err = ds.ApplyPackSpecs(specs) + require.Nil(t, err) + + gotQueries, err = ds.ListScheduledQueriesInPack(1, kolide.ListOptions{}) + require.Nil(t, err) + require.Len(t, gotQueries, 3) } diff --git a/server/datastore/datastore_test.go b/server/datastore/datastore_test.go index d243c8f24d..6a44ba190a 100644 --- a/server/datastore/datastore_test.go +++ b/server/datastore/datastore_test.go @@ -52,16 +52,11 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){ testCleanupDistributedQueryCampaigns, testBuiltInLabels, testLoadPacksForQueries, - testScheduledQuery, - testDeleteScheduledQuery, testListScheduledQueriesInPack, - testSaveScheduledQuery, testOptions, - testNewScheduledQuery, testOptionsToConfig, testGetPackByName, testGetQueryByName, - testScheduledQueryWithDeletedPack, testDecorators, testFileIntegrityMonitoring, testYARAStore, @@ -82,4 +77,7 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){ testApplyOsqueryOptions, testApplyOsqueryOptionsNoOverrides, testOsqueryOptionsForHost, + testApplyQueries, + testApplyPackSpecRoundtrip, + testApplyPackSpecMissingQueries, } diff --git a/server/datastore/inmem/inmem.go b/server/datastore/inmem/inmem.go index 499c8df343..73374ac882 100644 --- a/server/datastore/inmem/inmem.go +++ b/server/datastore/inmem/inmem.go @@ -39,8 +39,14 @@ type Datastore struct { yaraSignatureGroups map[uint]*kolide.YARASignatureGroup appConfig *kolide.AppConfig config *config.KolideConfig + + // Embedded interfaces to avoid implementing new methods for (now + // deprecated) inmem. kolide.TargetStore kolide.OsqueryOptionsStore + kolide.QueryStore + kolide.PackStore + kolide.ScheduledQueryStore } func New(config config.KolideConfig) (*Datastore, error) { @@ -243,31 +249,6 @@ func (d *Datastore) createDevPacksAndQueries() error { return err } - _, err = d.NewScheduledQuery(&kolide.ScheduledQuery{ - QueryID: query1.ID, - PackID: pack1.ID, - Interval: 60, - }) - if err != nil { - return err - } - - t := true - _, err = d.NewScheduledQuery(&kolide.ScheduledQuery{ - QueryID: query3.ID, - PackID: pack1.ID, - Interval: 60, - Snapshot: &t, - }) - if err != nil { - return err - } - - _, err = d.NewScheduledQuery(&kolide.ScheduledQuery{ - QueryID: query2.ID, - PackID: pack2.ID, - Interval: 60, - }) return err } diff --git a/server/datastore/inmem/queries.go b/server/datastore/inmem/queries.go index 3c4805e082..7ef23843e4 100644 --- a/server/datastore/inmem/queries.go +++ b/server/datastore/inmem/queries.go @@ -158,7 +158,7 @@ func (d *Datastore) loadPacksForQueries(queries []*kolide.Query) error { for _, q := range queries { q.Packs = make([]kolide.Pack, 0) for _, sq := range d.scheduledQueries { - if sq.QueryID == q.ID { + if sq.QueryName == q.Name { q.Packs = append(q.Packs, *d.packs[sq.PackID]) } } diff --git a/server/datastore/inmem/scheduled_queries.go b/server/datastore/inmem/scheduled_queries.go deleted file mode 100644 index 89f0b9c8c1..0000000000 --- a/server/datastore/inmem/scheduled_queries.go +++ /dev/null @@ -1,113 +0,0 @@ -package inmem - -import ( - "sort" - - "github.com/kolide/fleet/server/kolide" -) - -func (d *Datastore) NewScheduledQuery(sq *kolide.ScheduledQuery, opts ...kolide.OptionalArg) (*kolide.ScheduledQuery, error) { - d.mtx.Lock() - defer d.mtx.Unlock() - - newScheduledQuery := *sq - - newScheduledQuery.ID = d.nextID(newScheduledQuery) - d.scheduledQueries[newScheduledQuery.ID] = &newScheduledQuery - - newScheduledQuery.Query = d.queries[newScheduledQuery.QueryID].Query - newScheduledQuery.Name = d.queries[newScheduledQuery.QueryID].Name - - return &newScheduledQuery, nil -} - -func (d *Datastore) SaveScheduledQuery(sq *kolide.ScheduledQuery) (*kolide.ScheduledQuery, error) { - d.mtx.Lock() - defer d.mtx.Unlock() - - if _, ok := d.scheduledQueries[sq.ID]; !ok { - return nil, notFound("ScheduledQuery").WithID(sq.ID) - } - - d.scheduledQueries[sq.ID] = sq - return sq, nil -} - -func (d *Datastore) DeleteScheduledQuery(id uint) error { - d.mtx.Lock() - defer d.mtx.Unlock() - - if _, ok := d.scheduledQueries[id]; !ok { - return notFound("ScheduledQuery").WithID(id) - } - - delete(d.scheduledQueries, id) - return nil -} - -func (d *Datastore) ScheduledQuery(id uint) (*kolide.ScheduledQuery, error) { - d.mtx.Lock() - defer d.mtx.Unlock() - - sq, ok := d.scheduledQueries[id] - if !ok { - return nil, notFound("ScheduledQuery").WithID(id) - } - - sq.Name = d.queries[sq.QueryID].Name - sq.Query = d.queries[sq.QueryID].Query - - return sq, nil -} - -func (d *Datastore) ListScheduledQueriesInPack(id uint, opt kolide.ListOptions) ([]*kolide.ScheduledQuery, error) { - d.mtx.Lock() - defer d.mtx.Unlock() - - // We need to sort by keys to provide reliable ordering - keys := []int{} - for k, sq := range d.scheduledQueries { - if sq.PackID == id { - keys = append(keys, int(k)) - } - } - - if len(keys) == 0 { - return []*kolide.ScheduledQuery{}, nil - } - - sort.Ints(keys) - - scheduledQueries := []*kolide.ScheduledQuery{} - for _, k := range keys { - q := d.scheduledQueries[uint(k)] - q.Name = d.queries[q.QueryID].Name - q.Query = d.queries[q.QueryID].Query - scheduledQueries = append(scheduledQueries, q) - } - - // Apply ordering - if opt.OrderKey != "" { - var fields = map[string]string{ - "id": "ID", - "created_at": "CreatedAt", - "updated_at": "UpdatedAt", - "name": "Name", - "query": "Query", - "interval": "Interval", - "snapshot": "Snapshot", - "removed": "Removed", - "platform": "Platform", - "version": "Version", - } - if err := sortResults(scheduledQueries, opt, fields); err != nil { - return nil, err - } - } - - // Apply limit/offset - low, high := d.getLimitOffsetSliceBounds(opt, len(scheduledQueries)) - scheduledQueries = scheduledQueries[low:high] - - return scheduledQueries, nil -} diff --git a/server/datastore/mysql/migrations/tables/20171219164727_UpdateScheduledQueryKey.go b/server/datastore/mysql/migrations/tables/20171219164727_UpdateScheduledQueryKey.go new file mode 100644 index 0000000000..88d4da7b8f --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20171219164727_UpdateScheduledQueryKey.go @@ -0,0 +1,74 @@ +package tables + +import ( + "database/sql" + + "github.com/pkg/errors" +) + +func init() { + MigrationClient.AddMigration(Up_20171219164727, Down_20171219164727) +} + +func Up_20171219164727(tx *sql.Tx) error { + // Add query name column + query := ` + ALTER TABLE scheduled_queries + ADD COLUMN query_name varchar(255) + ` + if _, err := tx.Exec(query); err != nil { + return errors.Wrap(err, "adding query_name column") + } + + // Populate query name column via join with query ID + query = ` + UPDATE scheduled_queries + SET query_name = (SELECT name from queries where id = query_id) + ` + if _, err := tx.Exec(query); err != nil { + return errors.Wrap(err, "populating query_name column") + } + + // Add not null constraint + query = ` + ALTER TABLE scheduled_queries + MODIFY query_name varchar(255) NOT NULL + ` + if _, err := tx.Exec(query); err != nil { + return errors.Wrap(err, "adding not null") + } + + // Add foreign key constraint + query = ` + ALTER TABLE scheduled_queries + ADD FOREIGN KEY (query_name) REFERENCES queries (name) + ` + if _, err := tx.Exec(query); err != nil { + return errors.Wrap(err, "adding foreign key to query_name column") + } + + // Add `name` column to scheduled_queries + + query = ` +ALTER TABLE scheduled_queries +ADD COLUMN name varchar(255), +ADD COLUMN description varchar(255) +` + if _, err := tx.Exec(query); err != nil { + return errors.Wrap(err, "adding name to scheduled_queries") + } + + query = ` +ALTER TABLE packs +DROP COLUMN created_by +` + if _, err := tx.Exec(query); err != nil { + return errors.Wrap(err, "removing created_by from packs") + } + + return nil +} + +func Down_20171219164727(tx *sql.Tx) error { + return nil +} diff --git a/server/datastore/mysql/mysql.go b/server/datastore/mysql/mysql.go index 0291027261..9fc2061dec 100644 --- a/server/datastore/mysql/mysql.go +++ b/server/datastore/mysql/mysql.go @@ -282,3 +282,16 @@ func generateMysqlConnectionString(conf config.MysqlConfig) string { return dsn } + +// isForeignKeyError checks if the provided error is a MySQL child foreign key +// error (Error #1452) +func isChildForeignKeyError(err error) bool { + mysqlErr, ok := err.(*mysql.MySQLError) + if !ok { + return false + } + + // https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_no_referenced_row_2 + const ER_NO_REFERENCED_ROW_2 = 1452 + return mysqlErr.Number == ER_NO_REFERENCED_ROW_2 +} diff --git a/server/datastore/mysql/packs.go b/server/datastore/mysql/packs.go index 0580119ff3..228e31642c 100644 --- a/server/datastore/mysql/packs.go +++ b/server/datastore/mysql/packs.go @@ -4,10 +4,186 @@ import ( "database/sql" "fmt" + "github.com/jmoiron/sqlx" "github.com/kolide/fleet/server/kolide" "github.com/pkg/errors" ) +func (d *Datastore) ApplyPackSpecs(specs []*kolide.PackSpec) (err error) { + tx, err := d.db.Beginx() + if err != nil { + return errors.Wrap(err, "begin ApplyPackSpec transaction") + } + + defer func() { + if err != nil { + rbErr := tx.Rollback() + // It seems possible that there might be a case in + // which the error we are dealing with here was thrown + // by the call to tx.Commit(), and the docs suggest + // this call would then result in sql.ErrTxDone. + if rbErr != nil && rbErr != sql.ErrTxDone { + panic(fmt.Sprintf("got err '%s' rolling back after err '%s'", rbErr, err)) + } + } + }() + + for _, spec := range specs { + err = applyPackSpec(tx, spec) + if err != nil { + return errors.Wrapf(err, "applying pack '%s'", spec.Name) + } + } + + err = tx.Commit() + return errors.Wrap(err, "commit transaction") +} + +func applyPackSpec(tx *sqlx.Tx, spec *kolide.PackSpec) error { + // Insert/update pack + query := ` + INSERT INTO packs (name, description, platform) + VALUES (?, ?, ?) + ON DUPLICATE KEY UPDATE + name = VALUES(name), + description = VALUES(description), + platform = VALUES(platform), + deleted = false + ` + if _, err := tx.Exec(query, spec.Name, spec.Description, spec.Platform); err != nil { + return errors.Wrap(err, "insert/update pack") + } + + // Get Pack ID + // This is necessary because MySQL last_insert_id does not return a value + // if no update was made. + var packID uint + query = "SELECT id FROM packs WHERE name = ?" + if err := tx.Get(&packID, query, spec.Name); err != nil { + return errors.Wrap(err, "getting pack ID") + } + + // Delete existing scheduled queries for pack + query = "DELETE FROM scheduled_queries WHERE pack_id = ?" + if _, err := tx.Exec(query, packID); err != nil { + return errors.Wrap(err, "delete existing scheduled queries") + } + + // Insert new scheduled queries for pack + for _, q := range spec.Queries { + query = ` + INSERT INTO scheduled_queries ( + pack_id, query_name, name, description, ` + "`interval`" + `, + snapshot, removed, shard, platform, version + ) + VALUES ( + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ? + ) + ` + _, err := tx.Exec(query, + packID, q.QueryName, q.Name, q.Description, q.Interval, + q.Snapshot, q.Removed, q.Shard, q.Platform, q.Version, + ) + switch { + case isChildForeignKeyError(err): + return errors.Errorf("cannot schedule unknown query '%s'", q.QueryName) + case err != nil: + return errors.Wrapf(err, "adding query %s referencing %s", q.Name, q.QueryName) + } + } + + // Delete existing targets + query = "DELETE FROM pack_targets WHERE pack_id = ?" + if _, err := tx.Exec(query, packID); err != nil { + return errors.Wrap(err, "delete existing targets") + } + + // Insert targets + for _, l := range spec.Targets.Labels { + query = ` + INSERT INTO pack_targets (pack_id, type, target_id) + VALUES (?, ?, (SELECT id FROM labels WHERE name = ?)) + ` + if _, err := tx.Exec(query, packID, kolide.TargetLabel, l); err != nil { + return errors.Wrap(err, "adding label to pack") + } + } + + return nil +} + +func (d *Datastore) GetPackSpecs() (specs []*kolide.PackSpec, err error) { + tx, err := d.db.Beginx() + if err != nil { + return nil, errors.Wrap(err, "begin GetPackSpecs transaction") + } + + defer func() { + if err != nil { + rbErr := tx.Rollback() + // It seems possible that there might be a case in + // which the error we are dealing with here was thrown + // by the call to tx.Commit(), and the docs suggest + // this call would then result in sql.ErrTxDone. + if rbErr != nil && rbErr != sql.ErrTxDone { + panic(fmt.Sprintf("got err '%s' rolling back after err '%s'", rbErr, err)) + } + } + }() + + // Get basic specs + query := "SELECT id, name, description, platform FROM packs" + if err := tx.Select(&specs, query); err != nil { + fmt.Println(err) + return nil, errors.Wrap(err, "get packs") + } + + // Load targets + for _, spec := range specs { + query = ` +SELECT l.name +FROM labels l JOIN pack_targets pt +WHERE pack_id = ? AND pt.type = ? AND pt.target_id = l.id +` + if err := tx.Select(&spec.Targets.Labels, query, spec.ID, kolide.TargetLabel); err != nil { + return nil, errors.Wrap(err, "get pack targets") + } + } + + // query = ` + // INSERT INTO scheduled_queries ( + // pack_id, query_name, name, description, ` + "`interval`" + `, + // snapshot, removed, shard, platform, version + // ) + // VALUES ( + // ?, ?, ?, ?, ?, + // ?, ?, ?, ?, ? + // ) + // ` + + // Load queries + for _, spec := range specs { + query = ` +SELECT +query_name, name, description, ` + "`interval`" + `, +snapshot, removed, shard, platform, version +FROM scheduled_queries +WHERE pack_id = ? +` + if err := tx.Select(&spec.Queries, query, spec.ID); err != nil { + return nil, errors.Wrap(err, "get pack queries") + } + } + + err = tx.Commit() + if err != nil { + return nil, errors.Wrap(err, "commit transaction") + } + + return specs, nil +} + func (d *Datastore) PackByName(name string, opts ...kolide.OptionalArg) (*kolide.Pack, bool, error) { db := d.getTransaction(opts) sqlStatement := ` @@ -40,21 +216,21 @@ func (d *Datastore) NewPack(pack *kolide.Pack, opts ...kolide.OptionalArg) (*kol case nil: query = ` REPLACE INTO packs - ( name, description, platform, created_by, disabled, deleted) - VALUES ( ?, ?, ?, ?, ?, ?) + ( name, description, platform, disabled, deleted) + VALUES ( ?, ?, ?, ?, ?) ` case sql.ErrNoRows: query = ` INSERT INTO packs - ( name, description, platform, created_by, disabled, deleted) - VALUES ( ?, ?, ?, ?, ?, ?) + ( name, description, platform, disabled, deleted) + VALUES ( ?, ?, ?, ?, ?) ` default: return nil, errors.Wrap(err, "check for existing pack") } deleted := false - result, err := db.Exec(query, pack.Name, pack.Description, pack.Platform, pack.CreatedBy, pack.Disabled, deleted) + result, err := db.Exec(query, pack.Name, pack.Description, pack.Platform, pack.Disabled, deleted) if err != nil && isDuplicate(err) { return nil, alreadyExists("Pack", deletedPack.ID) } else if err != nil { diff --git a/server/datastore/mysql/queries.go b/server/datastore/mysql/queries.go index c226a5d11b..32fec1fcd0 100644 --- a/server/datastore/mysql/queries.go +++ b/server/datastore/mysql/queries.go @@ -2,12 +2,65 @@ package mysql import ( "database/sql" + "fmt" "github.com/jmoiron/sqlx" "github.com/kolide/fleet/server/kolide" "github.com/pkg/errors" ) +func (d *Datastore) ApplyQueries(authorID uint, queries []*kolide.Query) (err error) { + tx, err := d.db.Begin() + if err != nil { + return errors.Wrap(err, "begin ApplyQueries transaction") + } + + defer func() { + if err != nil { + rbErr := tx.Rollback() + // It seems possible that there might be a case in + // which the error we are dealing with here was thrown + // by the call to tx.Commit(), and the docs suggest + // this call would then result in sql.ErrTxDone. + if rbErr != nil && rbErr != sql.ErrTxDone { + panic(fmt.Sprintf("got err '%s' rolling back after err '%s'", rbErr, err)) + } + } + }() + + sql := ` + INSERT INTO queries ( + name, + description, + query, + author_id, + saved, + deleted + ) VALUES ( ?, ?, ?, ?, true, false ) + ON DUPLICATE KEY UPDATE + name = VALUES(name), + description = VALUES(description), + query = VALUES(query), + author_id = VALUES(author_id), + saved = VALUES(saved), + deleted = VALUES(deleted) + ` + stmt, err := tx.Prepare(sql) + if err != nil { + return errors.Wrap(err, "prepare ApplyQueries insert") + } + + for _, q := range queries { + _, err := stmt.Exec(q.Name, q.Description, q.Query, authorID) + if err != nil { + return errors.Wrap(err, "exec ApplyQueries insert") + } + } + + err = tx.Commit() + return errors.Wrap(err, "commit ApplyQueries transaction") +} + func (d *Datastore) QueryByName(name string, opts ...kolide.OptionalArg) (*kolide.Query, bool, error) { db := d.getTransaction(opts) sqlStatement := ` @@ -23,6 +76,11 @@ func (d *Datastore) QueryByName(name string, opts ...kolide.OptionalArg) (*kolid } return nil, false, errors.Wrap(err, "selecting query by name") } + + if err := d.loadPacksForQueries([]*kolide.Query{&query}); err != nil { + return nil, false, errors.Wrap(err, "loading packs for query") + } + return &query, true, nil } @@ -175,7 +233,6 @@ func (d *Datastore) ListQueries(opt kolide.ListOptions) ([]*kolide.Query, error) } return results, nil - } // loadPacksForQueries loads the packs associated with the provided queries @@ -185,31 +242,31 @@ func (d *Datastore) loadPacksForQueries(queries []*kolide.Query) error { } sql := ` - SELECT p.*, sq.query_id AS query_id + SELECT p.*, sq.query_name AS query_name FROM packs p JOIN scheduled_queries sq ON p.id = sq.pack_id - WHERE query_id IN (?) + WHERE query_name IN (?) AND NOT p.deleted ` // Used to map the results - id_queries := map[uint]*kolide.Query{} + name_queries := map[string]*kolide.Query{} // Used for the IN clause - ids := []uint{} + names := []string{} for _, q := range queries { q.Packs = make([]kolide.Pack, 0) - ids = append(ids, q.ID) - id_queries[q.ID] = q + names = append(names, q.Name) + name_queries[q.Name] = q } - query, args, err := sqlx.In(sql, ids) + query, args, err := sqlx.In(sql, names) if err != nil { return errors.Wrap(err, "building query in load packs for queries") } rows := []struct { - QueryID uint `db:"query_id"` + QueryName string `db:"query_name"` kolide.Pack }{} @@ -219,7 +276,7 @@ func (d *Datastore) loadPacksForQueries(queries []*kolide.Query) error { } for _, row := range rows { - q := id_queries[row.QueryID] + q := name_queries[row.QueryName] q.Packs = append(q.Packs, row.Pack) } diff --git a/server/datastore/mysql/scheduled_queries.go b/server/datastore/mysql/scheduled_queries.go index 83d00e2d09..5db2eafbe4 100644 --- a/server/datastore/mysql/scheduled_queries.go +++ b/server/datastore/mysql/scheduled_queries.go @@ -1,104 +1,19 @@ package mysql import ( - "database/sql" - "github.com/kolide/fleet/server/kolide" "github.com/pkg/errors" ) -func (d *Datastore) NewScheduledQuery(sq *kolide.ScheduledQuery, opts ...kolide.OptionalArg) (*kolide.ScheduledQuery, error) { - db := d.getTransaction(opts) - query := ` - INSERT INTO scheduled_queries ( - pack_id, - query_id, - snapshot, - removed, - ` + "`interval`" + `, - platform, - version, - shard - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) - ` - result, err := db.Exec(query, sq.PackID, sq.QueryID, sq.Snapshot, sq.Removed, sq.Interval, sq.Platform, sq.Version, sq.Shard) - if err != nil { - return nil, errors.Wrap(err, "inserting scheduled query") - } - - id, _ := result.LastInsertId() - sq.ID = uint(id) - - query = `SELECT query, name FROM queries WHERE id = ? LIMIT 1` - metadata := []struct { - Query string - Name string - }{} - - err = db.Select(&metadata, query, sq.QueryID) - if err != nil && err == sql.ErrNoRows { - return nil, notFound("Query").WithID(sq.QueryID) - } else if err != nil { - return nil, errors.Wrap(err, "select query by ID") - } - - if len(metadata) != 1 { - return nil, errors.Wrap(err, "wrong number of results returned from database") - } - - sq.Query = metadata[0].Query - sq.Name = metadata[0].Name - - return sq, nil -} - -func (d *Datastore) SaveScheduledQuery(sq *kolide.ScheduledQuery) (*kolide.ScheduledQuery, error) { - query := ` - UPDATE scheduled_queries - SET pack_id = ?, query_id = ?, ` + "`interval`" + ` = ?, snapshot = ?, removed = ?, platform = ?, version = ?, shard = ? - WHERE id = ? AND NOT deleted - ` - result, err := d.db.Exec(query, sq.PackID, sq.QueryID, sq.Interval, sq.Snapshot, sq.Removed, sq.Platform, sq.Version, sq.Shard, sq.ID) - if err != nil { - return nil, errors.Wrap(err, "saving a scheduled query") - } - rows, err := result.RowsAffected() - if err != nil { - return nil, errors.Wrap(err, "rows affected saving a scheduled query") - } - if rows == 0 { - return nil, notFound("ScheduledQueries").WithID(sq.ID) - } - return sq, nil -} - -func (d *Datastore) DeleteScheduledQuery(id uint) error { - return d.deleteEntity("scheduled_queries", id) -} - -func (d *Datastore) ScheduledQuery(id uint) (*kolide.ScheduledQuery, error) { - query := ` - SELECT sq.*, q.query, q.name - FROM scheduled_queries sq - JOIN queries q - ON sq.query_id = q.id - WHERE sq.id = ? - AND NOT sq.deleted - ` - sq := &kolide.ScheduledQuery{} - if err := d.db.Get(sq, query, id); err != nil { - return nil, errors.Wrap(err, "selecting a scheduled query") - } - - return sq, nil -} - func (d *Datastore) ListScheduledQueriesInPack(id uint, opts kolide.ListOptions) ([]*kolide.ScheduledQuery, error) { query := ` - SELECT sq.*, q.query, q.name + SELECT + sq.id, sq.pack_id, sq.name, sq.query_name, q.query, + sq.description, sq.interval, sq.snapshot, sq.removed, sq.platform, + sq.version, sq.shard FROM scheduled_queries sq JOIN queries q - ON sq.query_id = q.id + ON sq.query_name = q.name WHERE sq.pack_id = ? AND NOT sq.deleted ` diff --git a/server/kolide/packs.go b/server/kolide/packs.go index c82b9dd608..f0ff107f7f 100644 --- a/server/kolide/packs.go +++ b/server/kolide/packs.go @@ -6,6 +6,9 @@ import ( // PackStore is the datastore interface for managing query packs. type PackStore interface { + ApplyPackSpecs(specs []*PackSpec) error + GetPackSpecs() ([]*PackSpec, error) + // NewPack creates a new pack in the datastore. NewPack(pack *Pack, opts ...OptionalArg) (*Pack, error) @@ -104,7 +107,6 @@ type Pack struct { Name string `json:"name"` Description string `json:"description"` Platform string `json:"platform"` - CreatedBy uint `json:"created_by" db:"created_by"` Disabled bool `json:"disabled"` } @@ -118,6 +120,31 @@ type PackPayload struct { LabelIDs *[]uint `json:"label_ids"` } +type PackSpec struct { + ID uint + Name string `json:"name"` + Description string `json:"description"` + Platform string `json:"platform"` + Targets PackSpecTargets `json:"targets"` + Queries []PackSpecQuery `json:"queries"` +} + +type PackSpecTargets struct { + Labels []string `json:"labels"` +} + +type PackSpecQuery struct { + QueryName string `json:"query" db:"query_name"` + Name string `json:"name"` + Description string `json:"description"` + Interval uint `json:"interval"` + Snapshot *bool `json:"snapshot,omitempty"` + Removed *bool `json:"removed,omitempty"` + Shard *uint `json:"shard,omitempty"` + Platform *string `json:"platform,omitempty"` + Version *string `json:"version,omitempty"` +} + // PackTarget associates a pack with either a host or a label type PackTarget struct { ID uint diff --git a/server/kolide/queries.go b/server/kolide/queries.go index 6de271f68b..d1af52ff9f 100644 --- a/server/kolide/queries.go +++ b/server/kolide/queries.go @@ -2,9 +2,19 @@ package kolide import ( "context" + "strings" + + "github.com/pkg/errors" + + "github.com/ghodss/yaml" ) type QueryStore interface { + // ApplyQueries applies a list of queries (likely from a yaml file) to + // the datastore. Existing queries are updated, and new queries are + // created. + ApplyQueries(authorID uint, queries []*Query) error + // NewQuery creates a new query object in thie datastore. The returned // query should have the ID updated. NewQuery(query *Query, opts ...OptionalArg) (*Query, error) @@ -28,6 +38,12 @@ type QueryStore interface { } type QueryService interface { + // ApplyQuerySpecs applies a list of queries (creating or updating + // them as necessary) + ApplyQuerySpecs(ctx context.Context, specs []*QuerySpec) error + // GetQuerySpecs gets the YAML file representing all the stored queries. + GetQuerySpecs(ctx context.Context) ([]*QuerySpec, error) + // ListQueries returns a list of saved queries. Note only saved queries // should be returned (those that are created for distributed queries // but not saved should not be returned). @@ -64,3 +80,63 @@ type Query struct { // table in the MySQL backend. Packs []Pack `json:"packs" db:"-"` } + +const ( + QueryKind = "Query" +) + +type QueryObject struct { + ObjectMetadata + Spec QuerySpec `json:"spec"` +} + +type QuerySpec struct { + Name string `json:"name"` + Description string `json:"description"` + Query string `json:"query"` +} + +func LoadQueriesFromYaml(yml string) ([]*Query, error) { + queries := []*Query{} + for _, s := range strings.Split(yml, "---") { + s = strings.TrimSpace(s) + if len(s) == 0 { + continue + } + + var q QueryObject + err := yaml.Unmarshal([]byte(s), &q) + if err != nil { + return nil, errors.Wrap(err, "unmarshal yaml") + } + queries = append(queries, + &Query{Name: q.Spec.Name, Description: q.Spec.Description, Query: q.Spec.Query}, + ) + } + + return queries, nil +} + +func WriteQueriesToYaml(queries []*Query) (string, error) { + ymlStrings := []string{} + for _, q := range queries { + qYaml := QueryObject{ + ObjectMetadata: ObjectMetadata{ + ApiVersion: ApiVersion, + Kind: QueryKind, + }, + Spec: QuerySpec{ + Name: q.Name, + Description: q.Description, + Query: q.Query, + }, + } + yml, err := yaml.Marshal(qYaml) + if err != nil { + return "", errors.Wrap(err, "marshal YAML") + } + ymlStrings = append(ymlStrings, string(yml)) + } + + return strings.Join(ymlStrings, "---\n"), nil +} diff --git a/server/kolide/queries_test.go b/server/kolide/queries_test.go new file mode 100644 index 0000000000..4966ec2872 --- /dev/null +++ b/server/kolide/queries_test.go @@ -0,0 +1,104 @@ +package kolide + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLoadQueriesFromYamlStrings(t *testing.T) { + var testCases = []struct { + yaml string + queries []*Query + shouldErr bool + }{ + {"notyaml", []*Query{}, true}, + {"", []*Query{}, false}, + {"---", []*Query{}, false}, + { + ` +--- +apiVersion: k8s.kolide.com/v1alpha1 +kind: OsqueryQuery +spec: + name: osquery_version + description: The version of the Launcher and Osquery process + query: select launcher.version, osquery.version from kolide_launcher_info + support: + launcher: 0.3.0 + osquery: 2.9.0 +--- +apiVersion: k8s.kolide.com/v1alpha1 +kind: OsqueryQuery +spec: + name: osquery_schedule + description: Report performance stats for each file in the query schedule. + query: select name, interval, executions, output_size, wall_time, (user_time +--- + +apiVersion: k8s.kolide.com/v1alpha1 +kind: OsqueryQuery +spec: + name: foobar + description: froobing + query: select fizz from frog + +`, + []*Query{ + &Query{ + Name: "osquery_version", + Description: "The version of the Launcher and Osquery process", + Query: "select launcher.version, osquery.version from kolide_launcher_info", + }, + &Query{ + Name: "osquery_schedule", + Description: "Report performance stats for each file in the query schedule.", + Query: "select name, interval, executions, output_size, wall_time, (user_time", + }, + &Query{ + Name: "foobar", + Description: "froobing", + Query: "select fizz from frog", + }, + }, + false, + }, + } + + for _, tt := range testCases { + t.Run("", func(t *testing.T) { + queries, err := LoadQueriesFromYaml(tt.yaml) + if tt.shouldErr { + require.NotNil(t, err) + } else { + require.Nil(t, err) + assert.Equal(t, tt.queries, queries) + } + }) + } +} + +func TestRoundtripQueriesYaml(t *testing.T) { + var testCases = []struct{ queries []*Query }{ + {[]*Query{{Name: "froob", Description: "bing", Query: "blong"}}}, + { + []*Query{ + {Name: "froob", Description: "bing", Query: "blong"}, + {Name: "mant", Description: "smump", Query: "tmit"}, + {Name: "gorm", Description: "", Query: "blirz"}, + {Name: "blob", Description: "shmoo", Query: "smarle"}, + }, + }, + } + + for _, tt := range testCases { + t.Run("", func(t *testing.T) { + yml, err := WriteQueriesToYaml(tt.queries) + require.Nil(t, err) + queries, err := LoadQueriesFromYaml(yml) + require.Nil(t, err) + assert.Equal(t, tt.queries, queries) + }) + } +} diff --git a/server/kolide/scheduled_queries.go b/server/kolide/scheduled_queries.go index ffd5580a5e..7f44d2f8c6 100644 --- a/server/kolide/scheduled_queries.go +++ b/server/kolide/scheduled_queries.go @@ -5,35 +5,28 @@ import ( ) type ScheduledQueryStore interface { - NewScheduledQuery(sq *ScheduledQuery, opts ...OptionalArg) (*ScheduledQuery, error) - SaveScheduledQuery(sq *ScheduledQuery) (*ScheduledQuery, error) - DeleteScheduledQuery(id uint) error - ScheduledQuery(id uint) (*ScheduledQuery, error) ListScheduledQueriesInPack(id uint, opts ListOptions) ([]*ScheduledQuery, error) } type ScheduledQueryService interface { - GetScheduledQuery(ctx context.Context, id uint) (query *ScheduledQuery, err error) GetScheduledQueriesInPack(ctx context.Context, id uint, opts ListOptions) (queries []*ScheduledQuery, err error) - ScheduleQuery(ctx context.Context, sq *ScheduledQuery) (query *ScheduledQuery, err error) - DeleteScheduledQuery(ctx context.Context, id uint) (err error) - ModifyScheduledQuery(ctx context.Context, id uint, p ScheduledQueryPayload) (query *ScheduledQuery, err error) } type ScheduledQuery struct { UpdateCreateTimestamps DeleteFields - ID uint `json:"id"` - PackID uint `json:"pack_id" db:"pack_id"` - QueryID uint `json:"query_id" db:"query_id"` - Query string `json:"query"` // populated via a join on queries - Name string `json:"name"` // populated via a join on queries - Interval uint `json:"interval"` - Snapshot *bool `json:"snapshot"` - Removed *bool `json:"removed"` - Platform *string `json:"platform"` - Version *string `json:"version"` - Shard *uint `json:"shard"` + ID uint `json:"id"` + PackID uint `json:"pack_id" db:"pack_id"` + Name string `json:"name"` + QueryName string `json:"query_name" db:"query_name"` + Query string `json:"query"` // populated via a join on queries + Description string `json:"description"` + Interval uint `json:"interval"` + Snapshot *bool `json:"snapshot"` + Removed *bool `json:"removed"` + Platform *string `json:"platform"` + Version *string `json:"version"` + Shard *uint `json:"shard"` } type ScheduledQueryPayload struct { diff --git a/server/kolide/service.go b/server/kolide/service.go index a946010890..253d8efcac 100644 --- a/server/kolide/service.go +++ b/server/kolide/service.go @@ -15,7 +15,6 @@ type Service interface { TargetService ScheduledQueryService OptionService - ImportConfigService DecoratorService FileIntegrityMonitoringService } diff --git a/server/mock/datastore.go b/server/mock/datastore.go index 5e5cad4b07..5712b5b671 100644 --- a/server/mock/datastore.go +++ b/server/mock/datastore.go @@ -11,6 +11,7 @@ package mock //go:generate mockimpl -o datastore_fim.go "s *FileIntegrityMonitoringStore" "kolide.FileIntegrityMonitoringStore" //go:generate mockimpl -o datastore_osquery_options.go "s *OsqueryOptionsStore" "kolide.OsqueryOptionsStore" //go:generate mockimpl -o datastore_scheduled_queries.go "s *ScheduledQueryStore" "kolide.ScheduledQueryStore" +//go:generate mockimpl -o datastore_queries.go "s *QueryStore" "kolide.QueryStore" import "github.com/kolide/fleet/server/kolide" @@ -20,7 +21,6 @@ type Store struct { kolide.CampaignStore kolide.SessionStore kolide.PasswordResetStore - kolide.QueryStore kolide.YARAStore kolide.TargetStore ScheduledQueryStore @@ -34,6 +34,7 @@ type Store struct { OptionStore PackStore UserStore + QueryStore } func (m *Store) Drop() error { diff --git a/server/mock/datastore_packs.go b/server/mock/datastore_packs.go index 24bb6c3a43..0248868837 100644 --- a/server/mock/datastore_packs.go +++ b/server/mock/datastore_packs.go @@ -6,6 +6,10 @@ import "github.com/kolide/fleet/server/kolide" var _ kolide.PackStore = (*PackStore)(nil) +type ApplyPackSpecsFunc func(specs []*kolide.PackSpec) error + +type GetPackSpecsFunc func() ([]*kolide.PackSpec, error) + type NewPackFunc func(pack *kolide.Pack, opts ...kolide.OptionalArg) (*kolide.Pack, error) type SavePackFunc func(pack *kolide.Pack) error @@ -33,6 +37,12 @@ type ListHostsInPackFunc func(pid uint, opt kolide.ListOptions) ([]uint, error) type ListExplicitHostsInPackFunc func(pid uint, opt kolide.ListOptions) ([]uint, error) type PackStore struct { + ApplyPackSpecsFunc ApplyPackSpecsFunc + ApplyPackSpecsFuncInvoked bool + + GetPackSpecsFunc GetPackSpecsFunc + GetPackSpecsFuncInvoked bool + NewPackFunc NewPackFunc NewPackFuncInvoked bool @@ -73,6 +83,16 @@ type PackStore struct { ListExplicitHostsInPackFuncInvoked bool } +func (s *PackStore) ApplyPackSpecs(specs []*kolide.PackSpec) error { + s.ApplyPackSpecsFuncInvoked = true + return s.ApplyPackSpecsFunc(specs) +} + +func (s *PackStore) GetPackSpecs() ([]*kolide.PackSpec, error) { + s.GetPackSpecsFuncInvoked = true + return s.GetPackSpecsFunc() +} + func (s *PackStore) NewPack(pack *kolide.Pack, opts ...kolide.OptionalArg) (*kolide.Pack, error) { s.NewPackFuncInvoked = true return s.NewPackFunc(pack, opts...) diff --git a/server/mock/datastore_queries.go b/server/mock/datastore_queries.go new file mode 100644 index 0000000000..b47fff8fea --- /dev/null +++ b/server/mock/datastore_queries.go @@ -0,0 +1,89 @@ +// Automatically generated by mockimpl. DO NOT EDIT! + +package mock + +import "github.com/kolide/fleet/server/kolide" + +var _ kolide.QueryStore = (*QueryStore)(nil) + +type ApplyQueriesFunc func(authorID uint, queries []*kolide.Query) error + +type NewQueryFunc func(query *kolide.Query, opts ...kolide.OptionalArg) (*kolide.Query, error) + +type SaveQueryFunc func(query *kolide.Query) error + +type DeleteQueryFunc func(qid uint) error + +type DeleteQueriesFunc func(ids []uint) (uint, error) + +type QueryFunc func(id uint) (*kolide.Query, error) + +type ListQueriesFunc func(opt kolide.ListOptions) ([]*kolide.Query, error) + +type QueryByNameFunc func(name string, opts ...kolide.OptionalArg) (*kolide.Query, bool, error) + +type QueryStore struct { + ApplyQueriesFunc ApplyQueriesFunc + ApplyQueriesFuncInvoked bool + + NewQueryFunc NewQueryFunc + NewQueryFuncInvoked bool + + SaveQueryFunc SaveQueryFunc + SaveQueryFuncInvoked bool + + DeleteQueryFunc DeleteQueryFunc + DeleteQueryFuncInvoked bool + + DeleteQueriesFunc DeleteQueriesFunc + DeleteQueriesFuncInvoked bool + + QueryFunc QueryFunc + QueryFuncInvoked bool + + ListQueriesFunc ListQueriesFunc + ListQueriesFuncInvoked bool + + QueryByNameFunc QueryByNameFunc + QueryByNameFuncInvoked bool +} + +func (s *QueryStore) ApplyQueries(authorID uint, queries []*kolide.Query) error { + s.ApplyQueriesFuncInvoked = true + return s.ApplyQueriesFunc(authorID, queries) +} + +func (s *QueryStore) NewQuery(query *kolide.Query, opts ...kolide.OptionalArg) (*kolide.Query, error) { + s.NewQueryFuncInvoked = true + return s.NewQueryFunc(query, opts...) +} + +func (s *QueryStore) SaveQuery(query *kolide.Query) error { + s.SaveQueryFuncInvoked = true + return s.SaveQueryFunc(query) +} + +func (s *QueryStore) DeleteQuery(qid uint) error { + s.DeleteQueryFuncInvoked = true + return s.DeleteQueryFunc(qid) +} + +func (s *QueryStore) DeleteQueries(ids []uint) (uint, error) { + s.DeleteQueriesFuncInvoked = true + return s.DeleteQueriesFunc(ids) +} + +func (s *QueryStore) Query(id uint) (*kolide.Query, error) { + s.QueryFuncInvoked = true + return s.QueryFunc(id) +} + +func (s *QueryStore) ListQueries(opt kolide.ListOptions) ([]*kolide.Query, error) { + s.ListQueriesFuncInvoked = true + return s.ListQueriesFunc(opt) +} + +func (s *QueryStore) QueryByName(name string, opts ...kolide.OptionalArg) (*kolide.Query, bool, error) { + s.QueryByNameFuncInvoked = true + return s.QueryByNameFunc(name, opts...) +} diff --git a/server/service/endpoint_import_config.go b/server/service/endpoint_import_config.go deleted file mode 100644 index a15b8df854..0000000000 --- a/server/service/endpoint_import_config.go +++ /dev/null @@ -1,43 +0,0 @@ -package service - -import ( - "context" - - "github.com/go-kit/kit/endpoint" - "github.com/kolide/fleet/server/kolide" -) - -type importRequest struct { - DryRun bool `json:"dry_run"` - // Config contains a JSON osquery config supplied by the end user - Config string `json:"config"` - // ExternalPackConfigs contains a map of external Pack configs keyed by - // Pack name, this includes external packs referenced by the globbing - // feature. Not in the case of globbed packs, we expect the user to - // generate unique pack names since we don't know what they are, these - // names must be included in the GlobPackNames field so that we can - // validate that they've been accounted for. - ExternalPackConfigs map[string]string `json:"external_pack_configs"` - // GlobPackNames list of user generated names for external packs - // referenced by the glob feature, the JSON for the globbed packs - // is stored in ExternalPackConfigs keyed by the GlobPackName - GlobPackNames []string `json:"glob_pack_names"` -} - -type importResponse struct { - Response *kolide.ImportConfigResponse `json:"response,omitempty"` - Err error `json:"error,omitempty"` -} - -func (ir importResponse) error() error { return ir.Err } - -func makeImportConfigEndpoint(svc kolide.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - config := request.(kolide.ImportConfig) - resp, err := svc.ImportConfig(ctx, &config) - if err != nil { - return importResponse{Err: err}, nil - } - return importResponse{Response: resp}, nil - } -} diff --git a/server/service/endpoint_import_config_test.go b/server/service/endpoint_import_config_test.go deleted file mode 100644 index 8c7c1ba1a4..0000000000 --- a/server/service/endpoint_import_config_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package service - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "testing" - - "github.com/kolide/fleet/server/kolide" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func testImportConfigWithGlob(t *testing.T, r *testResource) { - testJSON := ` -{ - "config": "{\"options\":{\"host_identifier\":\"hostname\",\"schedule_splay_percent\":10},\"schedule\":{\"macosx_kextstat\":{\"query\":\"SELECT * FROM kernel_extensions;\",\"interval\":10},\"foobar\":{\"query\":\"SELECT foo, bar, pid FROM foobar_table;\",\"interval\":600}},\"packs\":{\"*\":\"/path/to/glob/*\",\"external_pack\":\"/path/to/external_pack.conf\",\"internal_pack\":{\"discovery\":[\"select pid from processes where name = 'foobar';\",\"select count(*) from users where username like 'www%';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"active_directory\":{\"query\":\"select * from ad_config;\",\"interval\":1200,\"description\":\"Check each user's active directory cached settings.\"}}}},\"decorators\":{\"load\":[\"SELECT version FROM osquery_info\",\"SELECT uuid AS host_uuid FROM system_info\"],\"always\":[\"SELECT user AS username FROM logged_in_users WHERE user <> '' ORDER BY time LIMIT 1;\"],\"interval\":{\"3600\":[\"SELECT total_seconds AS uptime FROM uptime;\"]}},\"glob\":[\"globpack\"],\"yara\":{\"signatures\":{\"sig_group_1\":[\"/Users/wxs/sigs/foo.sig\",\"/Users/wxs/sigs/bar.sig\"],\"sig_group_2\":[\"/Users/wxs/sigs/baz.sig\"]},\"file_paths\":{\"system_binaries\":[\"sig_group_1\"],\"tmp\":[\"sig_group_1\",\"sig_group_2\"]}},\"file_paths\":{\"system_binaries\":[\"/usr/bin/%\",\"/usr/sbin/%\"],\"tmp\":[\"/Users/%/tmp/%%\",\"/tmp/%\"]}}", - "external_pack_configs": { - "external_pack": "{\"discovery\":[\"select pid from processes where name = 'baz';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"something\":{\"query\":\"select * from something;\",\"interval\":1200,\"description\":\"Check something.\"}}}", - "globpack": "{\"discovery\":[\"select pid from processes where name = 'zip';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"something\":{\"query\":\"select * from other;\",\"interval\":1200,\"description\":\"Check other.\"}}}" - }, - "glob_pack_names": ["globpack"] -} -` - buff := bytes.NewBufferString(testJSON) - req, err := http.NewRequest("POST", r.server.URL+"/api/v1/kolide/osquery/config/import", buff) - require.Nil(t, err) - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken)) - client := &http.Client{} - resp, err := client.Do(req) - require.Nil(t, err) - var impResponse importResponse - err = json.NewDecoder(resp.Body).Decode(&impResponse) - require.Nil(t, err) - assert.Equal(t, 4, impResponse.Response.ImportStatusBySection[kolide.PacksSection].ImportCount) -} - -func testImportConfigWithInvalidPlatform(t *testing.T, r *testResource) { - testJSON := ` -{ - "config": "{\"options\":{\"host_identifier\":\"hostname\",\"schedule_splay_percent\":10},\"schedule\":{\"macosx_kextstat\":{\"query\":\"SELECT * FROM kernel_extensions;\",\"interval\":10},\"foobar\":{\"query\":\"SELECT foo, bar, pid FROM foobar_table;\",\"interval\":600}},\"packs\":{\"*\":\"/path/to/glob/*\",\"external_pack\":\"/path/to/external_pack.conf\",\"internal_pack\":{\"discovery\":[\"select pid from processes where name = 'foobar';\",\"select count(*) from users where username like 'www%';\"],\"platform\":\"foo\",\"version\":\"1.5.2\",\"queries\":{\"active_directory\":{\"query\":\"select * from ad_config;\",\"interval\":1200,\"description\":\"Check each user's active directory cached settings.\"}}}},\"decorators\":{\"load\":[\"SELECT version FROM osquery_info\",\"SELECT uuid AS host_uuid FROM system_info\"],\"always\":[\"SELECT user AS username FROM logged_in_users WHERE user <> '' ORDER BY time LIMIT 1;\"],\"interval\":{\"3600\":[\"SELECT total_seconds AS uptime FROM uptime;\"]}},\"glob\":[\"globpack\"],\"yara\":{\"signatures\":{\"sig_group_1\":[\"/Users/wxs/sigs/foo.sig\",\"/Users/wxs/sigs/bar.sig\"],\"sig_group_2\":[\"/Users/wxs/sigs/baz.sig\"]},\"file_paths\":{\"system_binaries\":[\"sig_group_1\"],\"tmp\":[\"sig_group_1\",\"sig_group_2\"]}},\"file_paths\":{\"system_binaries\":[\"/usr/bin/%\",\"/usr/sbin/%\"],\"tmp\":[\"/Users/%/tmp/%%\",\"/tmp/%\"]}}", - "external_pack_configs": { - "external_pack": "{\"discovery\":[\"select pid from processes where name = 'baz';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"something\":{\"query\":\"select * from something;\",\"interval\":1200,\"description\":\"Check something.\"}}}", - "globpack": "{\"discovery\":[\"select pid from processes where name = 'zip';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"something\":{\"query\":\"select * from other;\",\"interval\":1200,\"description\":\"Check other.\"}}}" - }, - "glob_pack_names": ["globpack"] -} -` - buff := bytes.NewBufferString(testJSON) - req, err := http.NewRequest("POST", r.server.URL+"/api/v1/kolide/osquery/config/import", buff) - require.Nil(t, err) - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken)) - client := &http.Client{} - resp, err := client.Do(req) - require.Nil(t, err) - var v mockValidationError - err = json.NewDecoder(resp.Body).Decode(&v) - require.Nil(t, err) - require.Len(t, v.Errors, 1) - assert.Equal(t, "'foo' is not a valid platform", v.Errors[0].Reason) -} - -func testImportConfigWithMissingGlob(t *testing.T, r *testResource) { - testJSON := ` - { - "config": "{\"options\":{\"host_identifier\":\"hostname\",\"schedule_splay_percent\":10},\"schedule\":{\"macosx_kextstat\":{\"query\":\"SELECT * FROM kernel_extensions;\",\"interval\":10},\"foobar\":{\"query\":\"SELECT foo, bar, pid FROM foobar_table;\",\"interval\":600}},\"packs\":{\"*\":\"/path/to/glob/*\",\"external_pack\":\"/path/to/external_pack.conf\",\"internal_pack\":{\"discovery\":[\"select pid from processes where name = 'foobar';\",\"select count(*) from users where username like 'www%';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"active_directory\":{\"query\":\"select * from ad_config;\",\"interval\":1200,\"description\":\"Check each user's active directory cached settings.\"}}}},\"decorators\":{\"load\":[\"SELECT version FROM osquery_info\",\"SELECT uuid AS host_uuid FROM system_info\"],\"always\":[\"SELECT user AS username FROM logged_in_users WHERE user <> '' ORDER BY time LIMIT 1;\"],\"interval\":{\"3600\":[\"SELECT total_seconds AS uptime FROM uptime;\"]}},\"yara\":{\"signatures\":{\"sig_group_1\":[\"/Users/wxs/sigs/foo.sig\",\"/Users/wxs/sigs/bar.sig\"],\"sig_group_2\":[\"/Users/wxs/sigs/baz.sig\"]},\"file_paths\":{\"system_binaries\":[\"sig_group_1\"],\"tmp\":[\"sig_group_1\",\"sig_group_2\"]}},\"file_paths\":{\"system_binaries\":[\"/usr/bin/%\",\"/usr/sbin/%\"],\"tmp\":[\"/Users/%/tmp/%%\",\"/tmp/%\"]}}", - "external_pack_configs": { - "external_pack": "{\"discovery\":[\"select pid from processes where name = 'baz';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"something\":{\"query\":\"select * from something;\",\"interval\":1200,\"description\":\"Check something.\"}}}" - } - } - ` - buff := bytes.NewBufferString(testJSON) - req, err := http.NewRequest("POST", r.server.URL+"/api/v1/kolide/osquery/config/import", buff) - require.Nil(t, err) - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken)) - client := &http.Client{} - resp, err := client.Do(req) - require.Nil(t, err) - var v mockValidationError - err = json.NewDecoder(resp.Body).Decode(&v) - require.Nil(t, err) - require.Len(t, v.Errors, 1) - assert.Equal(t, "missing glob packs", v.Errors[0].Reason) - -} - -func testImportConfigWithIntAsString(t *testing.T, r *testResource) { - - testJSON := ` - { - "config": "{\"options\":{\"host_identifier\":\"hostname\",\"schedule_splay_percent\":10},\"schedule\":{\"macosx_kextstat\":{\"query\":\"SELECT * FROM kernel_extensions;\",\"interval\":\"10\"},\"foobar\":{\"query\":\"SELECT foo, bar, pid FROM foobar_table;\",\"interval\":600}},\"packs\":{\"external_pack\":\"/path/to/external_pack.conf\",\"internal_pack\":{\"discovery\":[\"select pid from processes where name = 'foobar';\",\"select count(*) from users where username like 'www%';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"active_directory\":{\"query\":\"select * from ad_config;\",\"interval\":\"1200\",\"description\":\"Check each user's active directory cached settings.\"}}}},\"decorators\":{\"load\":[\"SELECT version FROM osquery_info\",\"SELECT uuid AS host_uuid FROM system_info\"],\"always\":[\"SELECT user AS username FROM logged_in_users WHERE user <> '' ORDER BY time LIMIT 1;\"],\"interval\":{\"3600\":[\"SELECT total_seconds AS uptime FROM uptime;\"]}},\"yara\":{\"signatures\":{\"sig_group_1\":[\"/Users/wxs/sigs/foo.sig\",\"/Users/wxs/sigs/bar.sig\"],\"sig_group_2\":[\"/Users/wxs/sigs/baz.sig\"]},\"file_paths\":{\"system_binaries\":[\"sig_group_1\"],\"tmp\":[\"sig_group_1\",\"sig_group_2\"]}},\"file_paths\":{\"system_binaries\":[\"/usr/bin/%\",\"/usr/sbin/%\"],\"tmp\":[\"/Users/%/tmp/%%\",\"/tmp/%\"]}}", - "external_pack_configs": { - "external_pack": "{\"discovery\":[\"select pid from processes where name = 'baz';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"something\":{\"query\":\"select * from something;\",\"interval\":1200,\"description\":\"Check something.\"}}}" - } - } - ` - buff := bytes.NewBufferString(testJSON) - req, err := http.NewRequest("POST", r.server.URL+"/api/v1/kolide/osquery/config/import", buff) - require.Nil(t, err) - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken)) - client := &http.Client{} - resp, err := client.Do(req) - require.Nil(t, err) - var impResponse importResponse - err = json.NewDecoder(resp.Body).Decode(&impResponse) - require.Nil(t, err) - assert.Equal(t, 2, impResponse.Response.ImportStatusBySection[kolide.YARASigSection].ImportCount) - assert.Equal(t, 4, impResponse.Response.ImportStatusBySection[kolide.DecoratorsSection].ImportCount) -} - -func testImportConfig(t *testing.T, r *testResource) { - - testJSON := ` - { - "config": "{\"options\":{\"host_identifier\":\"hostname\",\"schedule_splay_percent\":10},\"schedule\":{\"macosx_kextstat\":{\"query\":\"SELECT * FROM kernel_extensions;\",\"interval\":10},\"foobar\":{\"query\":\"SELECT foo, bar, pid FROM foobar_table;\",\"interval\":600}},\"packs\":{\"external_pack\":\"/path/to/external_pack.conf\",\"internal_pack\":{\"discovery\":[\"select pid from processes where name = 'foobar';\",\"select count(*) from users where username like 'www%';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"active_directory\":{\"query\":\"select * from ad_config;\",\"interval\":1200,\"description\":\"Check each user's active directory cached settings.\"}}}},\"decorators\":{\"load\":[\"SELECT version FROM osquery_info\",\"SELECT uuid AS host_uuid FROM system_info\"],\"always\":[\"SELECT user AS username FROM logged_in_users WHERE user <> '' ORDER BY time LIMIT 1;\"],\"interval\":{\"3600\":[\"SELECT total_seconds AS uptime FROM uptime;\"]}},\"yara\":{\"signatures\":{\"sig_group_1\":[\"/Users/wxs/sigs/foo.sig\",\"/Users/wxs/sigs/bar.sig\"],\"sig_group_2\":[\"/Users/wxs/sigs/baz.sig\"]},\"file_paths\":{\"system_binaries\":[\"sig_group_1\"],\"tmp\":[\"sig_group_1\",\"sig_group_2\"]}},\"file_paths\":{\"system_binaries\":[\"/usr/bin/%\",\"/usr/sbin/%\"],\"tmp\":[\"/Users/%/tmp/%%\",\"/tmp/%\"]}}", - "external_pack_configs": { - "external_pack": "{\"discovery\":[\"select pid from processes where name = 'baz';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"something\":{\"query\":\"select * from something;\",\"interval\":1200,\"description\":\"Check something.\"}}}" - } - } - ` - buff := bytes.NewBufferString(testJSON) - req, err := http.NewRequest("POST", r.server.URL+"/api/v1/kolide/osquery/config/import", buff) - require.Nil(t, err) - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken)) - client := &http.Client{} - resp, err := client.Do(req) - require.Nil(t, err) - var impResponse importResponse - err = json.NewDecoder(resp.Body).Decode(&impResponse) - require.Nil(t, err) - assert.Equal(t, 2, impResponse.Response.ImportStatusBySection[kolide.YARASigSection].ImportCount) - assert.Equal(t, 4, impResponse.Response.ImportStatusBySection[kolide.DecoratorsSection].ImportCount) -} - -func testImportConfigMissingExternal(t *testing.T, r *testResource) { - testJSON := ` - { - "config": "{\"options\":{\"host_identifier\":\"hostname\",\"schedule_splay_percent\":10},\"schedule\":{\"macosx_kextstat\":{\"query\":\"SELECT * FROM kernel_extensions;\",\"interval\":10},\"foobar\":{\"query\":\"SELECT foo, bar, pid FROM foobar_table;\",\"interval\":600}},\"packs\":{\"external_pack\":\"/path/to/external_pack.conf\",\"internal_pack\":{\"discovery\":[\"select pid from processes where name = 'foobar';\",\"select count(*) from users where username like 'www%';\"],\"platform\":\"linux\",\"version\":\"1.5.2\",\"queries\":{\"active_directory\":{\"query\":\"select * from ad_config;\",\"interval\":1200,\"description\":\"Check each user's active directory cached settings.\"}}}},\"decorators\":{\"load\":[\"SELECT version FROM osquery_info\",\"SELECT uuid AS host_uuid FROM system_info\"],\"always\":[\"SELECT user AS username FROM logged_in_users WHERE user <> '' ORDER BY time LIMIT 1;\"],\"interval\":{\"3603\":[\"SELECT total_seconds AS uptime FROM uptime;\"]}},\"yara\":{\"signatures\":{\"sig_group_1\":[\"/Users/wxs/sigs/foo.sig\",\"/Users/wxs/sigs/bar.sig\"],\"sig_group_2\":[\"/Users/wxs/sigs/baz.sig\"]},\"file_paths\":{\"system_binaries\":[\"sig_group_1\"],\"tmp\":[\"sig_group_1\",\"sig_group_2\"]}},\"file_paths\":{\"system_binaries\":[\"/usr/bin/%\",\"/usr/sbin/%\"],\"tmp\":[\"/Users/%/tmp/%%\",\"/tmp/%\"]}}" - } - ` - buff := bytes.NewBufferString(testJSON) - req, err := http.NewRequest("POST", r.server.URL+"/api/v1/kolide/osquery/config/import", buff) - require.Nil(t, err) - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.adminToken)) - client := &http.Client{} - resp, err := client.Do(req) - require.Nil(t, err) - - var v mockValidationError - err = json.NewDecoder(resp.Body).Decode(&v) - require.Nil(t, err) - require.Len(t, v.Errors, 2) - assert.Equal(t, "missing content for 'external_pack'", v.Errors[0].Reason) - assert.Equal(t, "interval '3603' must be divisible by 60", v.Errors[1].Reason) - -} diff --git a/server/service/endpoint_scheduled_queries.go b/server/service/endpoint_scheduled_queries.go index 391c6fd8f3..a5b914ebde 100644 --- a/server/service/endpoint_scheduled_queries.go +++ b/server/service/endpoint_scheduled_queries.go @@ -7,42 +7,6 @@ import ( "github.com/kolide/fleet/server/kolide" ) -//////////////////////////////////////////////////////////////////////////////// -// Get Scheduled Query -//////////////////////////////////////////////////////////////////////////////// - -type getScheduledQueryRequest struct { - ID uint -} - -type scheduledQueryResponse struct { - kolide.ScheduledQuery -} - -type getScheduledQueryResponse struct { - Scheduled scheduledQueryResponse `json:"scheduled,omitempty"` - Err error `json:"error,omitempty"` -} - -func (r getScheduledQueryResponse) error() error { return r.Err } - -func makeGetScheduledQueryEndpoint(svc kolide.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(getScheduledQueryRequest) - - sq, err := svc.GetScheduledQuery(ctx, req.ID) - if err != nil { - return getScheduledQueryResponse{Err: err}, nil - } - - return getScheduledQueryResponse{ - Scheduled: scheduledQueryResponse{ - ScheduledQuery: *sq, - }, - }, nil - } -} - //////////////////////////////////////////////////////////////////////////////// // Get Scheduled Queries In Pack //////////////////////////////////////////////////////////////////////////////// @@ -52,6 +16,10 @@ type getScheduledQueriesInPackRequest struct { ListOptions kolide.ListOptions } +type scheduledQueryResponse struct { + kolide.ScheduledQuery +} + type getScheduledQueriesInPackResponse struct { Scheduled []scheduledQueryResponse `json:"scheduled"` Err error `json:"error,omitempty"` @@ -78,106 +46,3 @@ func makeGetScheduledQueriesInPackEndpoint(svc kolide.Service) endpoint.Endpoint return resp, nil } } - -//////////////////////////////////////////////////////////////////////////////// -// Schedule Query -//////////////////////////////////////////////////////////////////////////////// - -type scheduleQueryRequest struct { - PackID uint `json:"pack_id"` - QueryID uint `json:"query_id"` - Interval uint `json:"interval"` - Snapshot *bool `json:"snapshot"` - Removed *bool `json:"removed"` - Platform *string `json:"platform"` - Version *string `json:"version"` - Shard *uint `json:"shard"` -} - -type scheduleQueryResponse struct { - Scheduled scheduledQueryResponse `json:"scheduled"` - Err error `json:"error,omitempty"` -} - -func makeScheduleQueryEndpoint(svc kolide.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(scheduleQueryRequest) - - scheduled, err := svc.ScheduleQuery(ctx, &kolide.ScheduledQuery{ - PackID: req.PackID, - QueryID: req.QueryID, - Interval: req.Interval, - Snapshot: req.Snapshot, - Removed: req.Removed, - Platform: req.Platform, - Version: req.Version, - Shard: req.Shard, - }) - if err != nil { - return scheduleQueryResponse{Err: err}, nil - } - return scheduleQueryResponse{Scheduled: scheduledQueryResponse{ - ScheduledQuery: *scheduled, - }}, nil - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Modify Scheduled Query -//////////////////////////////////////////////////////////////////////////////// - -type modifyScheduledQueryRequest struct { - ID uint - payload kolide.ScheduledQueryPayload -} - -type modifyScheduledQueryResponse struct { - Scheduled scheduledQueryResponse `json:"scheduled,omitempty"` - Err error `json:"error,omitempty"` -} - -func (r modifyScheduledQueryResponse) error() error { return r.Err } - -func makeModifyScheduledQueryEndpoint(svc kolide.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(modifyScheduledQueryRequest) - - sq, err := svc.ModifyScheduledQuery(ctx, req.ID, req.payload) - if err != nil { - return modifyScheduledQueryResponse{Err: err}, nil - } - - return modifyScheduledQueryResponse{ - Scheduled: scheduledQueryResponse{ - ScheduledQuery: *sq, - }, - }, nil - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Delete Scheduled Query -//////////////////////////////////////////////////////////////////////////////// - -type deleteScheduledQueryRequest struct { - ID uint -} - -type deleteScheduledQueryResponse struct { - Err error `json:"error,omitempty"` -} - -func (r deleteScheduledQueryResponse) error() error { return r.Err } - -func makeDeleteScheduledQueryEndpoint(svc kolide.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(deleteScheduledQueryRequest) - - err := svc.DeleteScheduledQuery(ctx, req.ID) - if err != nil { - return deleteScheduledQueryResponse{Err: err}, nil - } - - return deleteScheduledQueryResponse{}, nil - } -} diff --git a/server/service/endpoint_test.go b/server/service/endpoint_test.go index 2c04957c19..9653521c46 100644 --- a/server/service/endpoint_test.go +++ b/server/service/endpoint_test.go @@ -107,11 +107,6 @@ var testFunctions = [...]func(*testing.T, *testResource){ testModifyOptions, testModifyOptionsValidationFail, testOptionNotFound, - testImportConfig, - testImportConfigMissingExternal, - testImportConfigWithMissingGlob, - testImportConfigWithGlob, - testImportConfigWithIntAsString, testAdminUserSetAdmin, testNonAdminUserSetAdmin, testAdminUserSetEnabled, @@ -123,7 +118,6 @@ var testFunctions = [...]func(*testing.T, *testResource){ testNewDecoratorFailValidation, testDeleteDecorator, testModifyDecoratorNoChanges, - testImportConfigWithInvalidPlatform, } func TestEndpoints(t *testing.T) { diff --git a/server/service/handler.go b/server/service/handler.go index 02c5f7867a..08c0f94beb 100644 --- a/server/service/handler.go +++ b/server/service/handler.go @@ -51,11 +51,7 @@ type KolideEndpoints struct { CreatePack endpoint.Endpoint ModifyPack endpoint.Endpoint DeletePack endpoint.Endpoint - ScheduleQuery endpoint.Endpoint GetScheduledQueriesInPack endpoint.Endpoint - GetScheduledQuery endpoint.Endpoint - ModifyScheduledQuery endpoint.Endpoint - DeleteScheduledQuery endpoint.Endpoint EnrollAgent endpoint.Endpoint GetClientConfig endpoint.Endpoint GetDistributedQueries endpoint.Endpoint @@ -78,7 +74,6 @@ type KolideEndpoints struct { GetOptions endpoint.Endpoint ModifyOptions endpoint.Endpoint ResetOptions endpoint.Endpoint - ImportConfig endpoint.Endpoint GetCertificate endpoint.Endpoint ChangeEmail endpoint.Endpoint InitiateSSO endpoint.Endpoint @@ -140,11 +135,7 @@ func MakeKolideServerEndpoints(svc kolide.Service, jwtKey string) KolideEndpoint CreatePack: authenticatedUser(jwtKey, svc, makeCreatePackEndpoint(svc)), ModifyPack: authenticatedUser(jwtKey, svc, makeModifyPackEndpoint(svc)), DeletePack: authenticatedUser(jwtKey, svc, makeDeletePackEndpoint(svc)), - ScheduleQuery: authenticatedUser(jwtKey, svc, makeScheduleQueryEndpoint(svc)), GetScheduledQueriesInPack: authenticatedUser(jwtKey, svc, makeGetScheduledQueriesInPackEndpoint(svc)), - GetScheduledQuery: authenticatedUser(jwtKey, svc, makeGetScheduledQueryEndpoint(svc)), - ModifyScheduledQuery: authenticatedUser(jwtKey, svc, makeModifyScheduledQueryEndpoint(svc)), - DeleteScheduledQuery: authenticatedUser(jwtKey, svc, makeDeleteScheduledQueryEndpoint(svc)), GetHost: authenticatedUser(jwtKey, svc, makeGetHostEndpoint(svc)), ListHosts: authenticatedUser(jwtKey, svc, makeListHostsEndpoint(svc)), GetHostSummary: authenticatedUser(jwtKey, svc, makeGetHostSummaryEndpoint(svc)), @@ -162,7 +153,6 @@ func MakeKolideServerEndpoints(svc kolide.Service, jwtKey string) KolideEndpoint GetOptions: authenticatedUser(jwtKey, svc, mustBeAdmin(makeGetOptionsEndpoint(svc))), ModifyOptions: authenticatedUser(jwtKey, svc, mustBeAdmin(makeModifyOptionsEndpoint(svc))), ResetOptions: authenticatedUser(jwtKey, svc, mustBeAdmin(makeResetOptionsEndpoint(svc))), - ImportConfig: authenticatedUser(jwtKey, svc, makeImportConfigEndpoint(svc)), GetCertificate: authenticatedUser(jwtKey, svc, makeCertificateEndpoint(svc)), ChangeEmail: authenticatedUser(jwtKey, svc, makeChangeEmailEndpoint(svc)), GetFIM: authenticatedUser(jwtKey, svc, makeGetFIMEndpoint(svc)), @@ -214,11 +204,7 @@ type kolideHandlers struct { CreatePack http.Handler ModifyPack http.Handler DeletePack http.Handler - ScheduleQuery http.Handler GetScheduledQueriesInPack http.Handler - GetScheduledQuery http.Handler - ModifyScheduledQuery http.Handler - DeleteScheduledQuery http.Handler EnrollAgent http.Handler GetClientConfig http.Handler GetDistributedQueries http.Handler @@ -241,7 +227,6 @@ type kolideHandlers struct { GetOptions http.Handler ModifyOptions http.Handler ResetOptions http.Handler - ImportConfig http.Handler GetCertificate http.Handler ChangeEmail http.Handler InitiateSSO http.Handler @@ -292,11 +277,7 @@ func makeKolideKitHandlers(e KolideEndpoints, opts []kithttp.ServerOption) *koli CreatePack: newServer(e.CreatePack, decodeCreatePackRequest), ModifyPack: newServer(e.ModifyPack, decodeModifyPackRequest), DeletePack: newServer(e.DeletePack, decodeDeletePackRequest), - ScheduleQuery: newServer(e.ScheduleQuery, decodeScheduleQueryRequest), GetScheduledQueriesInPack: newServer(e.GetScheduledQueriesInPack, decodeGetScheduledQueriesInPackRequest), - GetScheduledQuery: newServer(e.GetScheduledQuery, decodeGetScheduledQueryRequest), - ModifyScheduledQuery: newServer(e.ModifyScheduledQuery, decodeModifyScheduledQueryRequest), - DeleteScheduledQuery: newServer(e.DeleteScheduledQuery, decodeDeleteScheduledQueryRequest), EnrollAgent: newServer(e.EnrollAgent, decodeEnrollAgentRequest), GetClientConfig: newServer(e.GetClientConfig, decodeGetClientConfigRequest), GetDistributedQueries: newServer(e.GetDistributedQueries, decodeGetDistributedQueriesRequest), @@ -319,7 +300,6 @@ func makeKolideKitHandlers(e KolideEndpoints, opts []kithttp.ServerOption) *koli GetOptions: newServer(e.GetOptions, decodeNoParamsRequest), ModifyOptions: newServer(e.ModifyOptions, decodeModifyOptionsRequest), ResetOptions: newServer(e.ResetOptions, decodeNoParamsRequest), - ImportConfig: newServer(e.ImportConfig, decodeImportConfigRequest), GetCertificate: newServer(e.GetCertificate, decodeNoParamsRequest), ChangeEmail: newServer(e.ChangeEmail, decodeChangeEmailRequest), InitiateSSO: newServer(e.InitiateSSO, decodeInitiateSSORequest), @@ -416,10 +396,6 @@ func attachKolideAPIRoutes(r *mux.Router, h *kolideHandlers) { r.Handle("/api/v1/kolide/packs/{id}", h.ModifyPack).Methods("PATCH").Name("modify_pack") r.Handle("/api/v1/kolide/packs/{id}", h.DeletePack).Methods("DELETE").Name("delete_pack") r.Handle("/api/v1/kolide/packs/{id}/scheduled", h.GetScheduledQueriesInPack).Methods("GET").Name("get_scheduled_queries_in_pack") - r.Handle("/api/v1/kolide/schedule", h.ScheduleQuery).Methods("POST").Name("schedule_query") - r.Handle("/api/v1/kolide/schedule/{id}", h.GetScheduledQuery).Methods("GET").Name("get_scheduled_query") - r.Handle("/api/v1/kolide/schedule/{id}", h.ModifyScheduledQuery).Methods("PATCH").Name("modify_scheduled_query") - r.Handle("/api/v1/kolide/schedule/{id}", h.DeleteScheduledQuery).Methods("DELETE").Name("delete_scheduled_query") r.Handle("/api/v1/kolide/labels/{id}", h.GetLabel).Methods("GET").Name("get_label") r.Handle("/api/v1/kolide/labels", h.ListLabels).Methods("GET").Name("list_labels") r.Handle("/api/v1/kolide/labels", h.CreateLabel).Methods("POST").Name("create_label") @@ -445,8 +421,6 @@ func attachKolideAPIRoutes(r *mux.Router, h *kolideHandlers) { r.Handle("/api/v1/kolide/targets", h.SearchTargets).Methods("POST").Name("search_targets") - r.Handle("/api/v1/kolide/osquery/config/import", h.ImportConfig).Methods("POST").Name("import_config") - r.Handle("/api/v1/osquery/enroll", h.EnrollAgent).Methods("POST").Name("enroll_agent") r.Handle("/api/v1/osquery/config", h.GetClientConfig).Methods("POST").Name("get_client_config") r.Handle("/api/v1/osquery/distributed/read", h.GetDistributedQueries).Methods("POST").Name("get_distributed_queries") diff --git a/server/service/handler_test.go b/server/service/handler_test.go index a3f92a6437..e8f923eb11 100644 --- a/server/service/handler_test.go +++ b/server/service/handler_test.go @@ -133,18 +133,6 @@ func TestAPIRoutes(t *testing.T) { verb: "GET", uri: "/api/v1/kolide/packs/1/scheduled", }, - { - verb: "POST", - uri: "/api/v1/kolide/schedule", - }, - { - verb: "DELETE", - uri: "/api/v1/kolide/schedule/1", - }, - { - verb: "PATCH", - uri: "/api/v1/kolide/schedule/1", - }, { verb: "POST", uri: "/api/v1/osquery/enroll", diff --git a/server/service/logging_import_config.go b/server/service/logging_import_config.go deleted file mode 100644 index 33d72dcbaa..0000000000 --- a/server/service/logging_import_config.go +++ /dev/null @@ -1,27 +0,0 @@ -package service - -import ( - "context" - "time" - - "github.com/kolide/fleet/server/kolide" -) - -func (mw loggingMiddleware) ImportConfig(ctx context.Context, cfg *kolide.ImportConfig) (*kolide.ImportConfigResponse, error) { - var ( - resp *kolide.ImportConfigResponse - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "ImportConfig", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - resp, err = mw.Service.ImportConfig(ctx, cfg) - return resp, err - -} diff --git a/server/service/logging_scheduled_queries.go b/server/service/logging_scheduled_queries.go index 64023ba8f4..7d1e7d95c1 100644 --- a/server/service/logging_scheduled_queries.go +++ b/server/service/logging_scheduled_queries.go @@ -7,24 +7,6 @@ import ( "github.com/kolide/fleet/server/kolide" ) -func (mw loggingMiddleware) GetScheduledQuery(ctx context.Context, id uint) (*kolide.ScheduledQuery, error) { - var ( - query *kolide.ScheduledQuery - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "GetScheduledQuery", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - query, err = mw.Service.GetScheduledQuery(ctx, id) - return query, err -} - func (mw loggingMiddleware) GetScheduledQueriesInPack(ctx context.Context, id uint, opts kolide.ListOptions) ([]*kolide.ScheduledQuery, error) { var ( queries []*kolide.ScheduledQuery @@ -42,56 +24,3 @@ func (mw loggingMiddleware) GetScheduledQueriesInPack(ctx context.Context, id ui queries, err = mw.Service.GetScheduledQueriesInPack(ctx, id, opts) return queries, err } - -func (mw loggingMiddleware) ScheduleQuery(ctx context.Context, sq *kolide.ScheduledQuery) (*kolide.ScheduledQuery, error) { - var ( - query *kolide.ScheduledQuery - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "ScheduleQuery", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - query, err = mw.Service.ScheduleQuery(ctx, sq) - return query, err -} - -func (mw loggingMiddleware) DeleteScheduledQuery(ctx context.Context, id uint) error { - var ( - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "DeleteScheduledQuery", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - err = mw.Service.DeleteScheduledQuery(ctx, id) - return err -} - -func (mw loggingMiddleware) ModifyScheduledQuery(ctx context.Context, id uint, p kolide.ScheduledQueryPayload) (*kolide.ScheduledQuery, error) { - var ( - query *kolide.ScheduledQuery - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "ModifyScheduledQuery", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - query, err = mw.Service.ModifyScheduledQuery(ctx, id, p) - return query, err -} diff --git a/server/service/metrics_import_config.go b/server/service/metrics_import_config.go deleted file mode 100644 index 63d5d4ea89..0000000000 --- a/server/service/metrics_import_config.go +++ /dev/null @@ -1,23 +0,0 @@ -package service - -import ( - "context" - "fmt" - "time" - - "github.com/kolide/fleet/server/kolide" -) - -func (mw metricsMiddleware) ImportConfig(ctx context.Context, cfg *kolide.ImportConfig) (*kolide.ImportConfigResponse, error) { - var ( - resp *kolide.ImportConfigResponse - err error - ) - defer func(begin time.Time) { - lvs := []string{"method", "ImportConfig", "error", fmt.Sprint(err != nil)} - mw.requestCount.With(lvs...).Add(1) - mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) - }(time.Now()) - resp, err = mw.Service.ImportConfig(ctx, cfg) - return resp, err -} diff --git a/server/service/service_import_config.go b/server/service/service_import_config.go deleted file mode 100644 index ab62f1fb71..0000000000 --- a/server/service/service_import_config.go +++ /dev/null @@ -1,533 +0,0 @@ -package service - -import ( - "context" - "crypto/md5" - "fmt" - "strconv" - "strings" - - "github.com/kolide/fleet/server/contexts/viewer" - "github.com/kolide/fleet/server/kolide" - "github.com/pkg/errors" -) - -func (svc service) ImportConfig(ctx context.Context, cfg *kolide.ImportConfig) (*kolide.ImportConfigResponse, error) { - resp := &kolide.ImportConfigResponse{ - ImportStatusBySection: make(map[kolide.ImportSection]*kolide.ImportStatus), - } - vc, ok := viewer.FromContext(ctx) - if !ok { - return nil, errors.New("internal error, unable to fetch user") - } - tx, err := svc.ds.Begin() - if err != nil { - return nil, err - } - - if err := svc.importOptions(cfg.Options, resp, tx); err != nil { - svc.rollbackImportConfig(tx, "importOptions") - return nil, errors.Wrap(err, "importOptions failed") - } - if err := svc.importPacks(vc.UserID(), cfg, resp, tx); err != nil { - svc.rollbackImportConfig(tx, "importPacks") - return nil, errors.Wrap(err, "importPacks failed") - } - if err := svc.importScheduledQueries(vc.UserID(), cfg, resp, tx); err != nil { - svc.rollbackImportConfig(tx, "importScheduledQueries") - return nil, errors.Wrap(err, "importScheduledQueries failed") - } - if err := svc.importDecorators(cfg, resp, tx); err != nil { - svc.rollbackImportConfig(tx, "importDecorators") - return nil, errors.Wrap(err, "importDecorators") - } - if err := svc.importFIMSections(cfg, resp, tx); err != nil { - svc.rollbackImportConfig(tx, "importFIMSections") - return nil, errors.Wrap(err, "importFIMSections") - } - if cfg.DryRun { - if err := tx.Rollback(); err != nil { - return nil, errors.Wrap(err, "dry run rollback failed") - } - return resp, nil - } - if err := tx.Commit(); err != nil { - return nil, errors.Wrap(err, "commit failed") - } - return resp, nil -} - -func (svc service) rollbackImportConfig(tx kolide.Transaction, method string) { - if err := tx.Rollback(); err != nil { - svc.logger.Log( - "method", method, - "err", errors.Wrap(err, fmt.Sprintf("db rollback failed in %s", method)), - ) - } -} - -func (svc service) importYARA(cfg *kolide.ImportConfig, resp *kolide.ImportConfigResponse, tx kolide.Transaction) error { - if cfg.YARA != nil { - for sig, paths := range cfg.YARA.Signatures { - ysg := &kolide.YARASignatureGroup{ - SignatureName: sig, - Paths: paths, - } - _, err := svc.ds.NewYARASignatureGroup(ysg, kolide.HasTransaction(tx)) - if _, ok := err.(dbDuplicateError); ok { - resp.Status(kolide.YARAFileSection).SkipCount++ - resp.Status(kolide.YARAFileSection).Warning(kolide.YARADuplicate, "skipped '%s', already exists", sig) - continue - } - if err != nil { - return err - } - resp.Status(kolide.YARASigSection).ImportCount++ - resp.Status(kolide.YARASigSection).Message("imported '%s'", sig) - } - for section, sigs := range cfg.YARA.FilePaths { - for _, sig := range sigs { - err := svc.ds.NewYARAFilePath(section, sig, kolide.HasTransaction(tx)) - if _, ok := err.(dbDuplicateError); ok { - resp.Status(kolide.YARAFileSection).SkipCount++ - resp.Status(kolide.YARAFileSection).Warning(kolide.YARADuplicate, "skipped '%s', already exists", section) - continue - } - if err != nil { - return err - } - resp.Status(kolide.YARAFileSection).ImportCount++ - resp.Status(kolide.YARAFileSection).Message("imported '%s'", section) - } - } - } - return nil -} - -type dbDuplicateError interface { - IsExists() bool -} - -func (svc service) importFIMSections(cfg *kolide.ImportConfig, resp *kolide.ImportConfigResponse, tx kolide.Transaction) error { - if cfg.FileIntegrityMonitoring != nil { - for sectionName, paths := range cfg.FileIntegrityMonitoring { - fp := &kolide.FIMSection{ - SectionName: sectionName, - Description: "imported", - Paths: paths, - } - _, err := svc.ds.NewFIMSection(fp, kolide.HasTransaction(tx)) - if _, ok := err.(dbDuplicateError); ok { - resp.Status(kolide.FilePathsSection).SkipCount++ - resp.Status(kolide.FilePathsSection).Warning(kolide.FIMDuplicate, "skipped '%s', already exists", sectionName) - continue - } - if err != nil { - return err - } - resp.Status(kolide.FilePathsSection).ImportCount++ - resp.Status(kolide.FilePathsSection).Message("imported '%s'", sectionName) - } - } - // this has to happen AFTER fim section, because it requires file paths - return svc.importYARA(cfg, resp, tx) -} - -func (svc service) getExistingDecoratorQueries(tx kolide.Transaction) (map[string]int, error) { - decs, err := svc.ds.ListDecorators(kolide.HasTransaction(tx)) - if err != nil { - return nil, err - } - queryHashes := map[string]int{} - for _, dec := range decs { - hash := fmt.Sprintf("%x", md5.Sum([]byte(dec.Query))) - queryHashes[hash] = 0 - } - return queryHashes, nil -} - -func decoratorExists(query string, queryHashes map[string]int) bool { - hash := fmt.Sprintf("%x", md5.Sum([]byte(query))) - _, exists := queryHashes[hash] - return exists -} - -func (svc service) importDecorators(cfg *kolide.ImportConfig, resp *kolide.ImportConfigResponse, tx kolide.Transaction) error { - if cfg.Decorators != nil { - queryHashes, err := svc.getExistingDecoratorQueries(tx) - if err != nil { - return errors.Wrap(err, "getting existing queries") - } - - for _, query := range cfg.Decorators.Load { - if decoratorExists(query, queryHashes) { - resp.Status(kolide.DecoratorsSection).SkipCount++ - resp.Status(kolide.DecoratorsSection).Warning(kolide.QueryDuplicate, "skipped load '%s'", query) - continue - } - decName, err := uniqueImportName() - if err != nil { - return err - } - decorator := &kolide.Decorator{ - Name: decName, - Query: query, - Type: kolide.DecoratorLoad, - } - _, err = svc.ds.NewDecorator(decorator, kolide.HasTransaction(tx)) - if err != nil { - return err - } - resp.Status(kolide.DecoratorsSection).ImportCount++ - resp.Status(kolide.DecoratorsSection).Warning("imported load '%s'", query) - } - for _, query := range cfg.Decorators.Always { - if decoratorExists(query, queryHashes) { - resp.Status(kolide.DecoratorsSection).SkipCount++ - resp.Status(kolide.DecoratorsSection).Warning(kolide.QueryDuplicate, "skipped always '%s'", query) - continue - } - decName, err := uniqueImportName() - if err != nil { - return err - } - decorator := &kolide.Decorator{ - Name: decName, - Query: query, - Type: kolide.DecoratorAlways, - } - _, err = svc.ds.NewDecorator(decorator, kolide.HasTransaction(tx)) - if err != nil { - return err - } - resp.Status(kolide.DecoratorsSection).ImportCount++ - resp.Status(kolide.DecoratorsSection).Message("imported always '%s'", query) - } - - for key, queries := range cfg.Decorators.Interval { - for _, query := range queries { - if decoratorExists(query, queryHashes) { - resp.Status(kolide.DecoratorsSection).SkipCount++ - resp.Status(kolide.DecoratorsSection).Warning(kolide.QueryDuplicate, "skipped interval '%s'", query) - continue - } - interval, err := strconv.ParseInt(key, 10, 32) - if err != nil { - return err - } - decName, err := uniqueImportName() - if err != nil { - return err - } - decorator := &kolide.Decorator{ - Name: decName, - Query: query, - Type: kolide.DecoratorInterval, - Interval: uint(interval), - } - _, err = svc.ds.NewDecorator(decorator, kolide.HasTransaction(tx)) - if err != nil { - return err - } - resp.Status(kolide.DecoratorsSection).ImportCount++ - resp.Status(kolide.DecoratorsSection).Message("imported interval %d '%s'", interval, query) - } - } - - } - return nil -} - -func (svc service) importScheduledQueries(uid uint, cfg *kolide.ImportConfig, resp *kolide.ImportConfigResponse, tx kolide.Transaction) error { - _, ok, err := svc.ds.PackByName(kolide.ImportPackName, kolide.HasTransaction(tx)) - if ok { - resp.Status(kolide.PacksSection).Warning( - kolide.PackDuplicate, "skipped '%s' already exists", kolide.ImportPackName, - ) - resp.Status(kolide.PacksSection).SkipCount++ - return nil - } - // create import pack to hold imported scheduled queries - pack := &kolide.Pack{ - Name: kolide.ImportPackName, - Description: "holds imported scheduled queries", - CreatedBy: uid, - Disabled: false, - } - pack, err = svc.ds.NewPack(pack, kolide.HasTransaction(tx)) - if err != nil { - return err - } - resp.Status(kolide.PacksSection).ImportCount++ - resp.Status(kolide.PacksSection).Message("created import pack") - - for queryName, queryDetails := range cfg.Schedule { - var query *kolide.Query - query, ok, err = svc.ds.QueryByName(queryName, kolide.HasTransaction(tx)) - // if we find the query check to see if the import query matches the - // query we have, if it doesn't skip it - if ok { - if hashQuery("", query.Query) != hashQuery("", queryDetails.Query) { - resp.Status(kolide.PacksSection).Warning( - kolide.DifferentQuerySameName, - "queries named '%s' have different statements and won't be added to '%s'", - queryName, - pack.Name, - ) - continue - } - resp.Status(kolide.QueriesSection).Warning( - kolide.QueryDuplicate, "skipped '%s' different query of same name already exists", queryName, - ) - resp.Status(kolide.QueriesSection).SkipCount++ - } else { - // if query doesn't exist, create it - query = &kolide.Query{ - Name: queryName, - Description: "imported", - Query: queryDetails.Query, - Saved: true, - AuthorID: uid, - } - query, err = svc.ds.NewQuery(query, kolide.HasTransaction(tx)) - if err != nil { - return err - } - resp.Status(kolide.QueriesSection).ImportCount++ - resp.Status(kolide.QueriesSection).Message( - "imported scheduled query '%s'", query.Name, - ) - } - sq := &kolide.ScheduledQuery{ - PackID: pack.ID, - QueryID: query.ID, - Interval: uint(queryDetails.Interval), - Snapshot: queryDetails.Snapshot, - Removed: queryDetails.Removed, - Platform: queryDetails.Platform, - Version: queryDetails.Version, - Shard: configInt2Ptr(queryDetails.Shard), - } - _, err = svc.ds.NewScheduledQuery(sq, kolide.HasTransaction(tx)) - if err != nil { - return nil - } - resp.Status(kolide.PacksSection).Message( - "added query '%s' to '%s'", query.Name, pack.Name, - ) - } - return nil -} - -func (svc service) importPacks(uid uint, cfg *kolide.ImportConfig, resp *kolide.ImportConfigResponse, tx kolide.Transaction) error { - labelCache := map[string]*kolide.Label{} - packs, err := cfg.CollectPacks() - if err != nil { - return err - } - for packName, packDetails := range packs { - _, ok, err := svc.ds.PackByName(packName, kolide.HasTransaction(tx)) - if err != nil { - return err - } - if ok { - resp.Status(kolide.PacksSection).Warning( - kolide.PackDuplicate, "skipped '%s' already exists", packName, - ) - resp.Status(kolide.PacksSection).SkipCount++ - continue - } - // import new pack - if packDetails.Shard != nil { - resp.Status(kolide.PacksSection).Warning( - kolide.Unsupported, - "shard for pack '%s'", - packName, - ) - } - if packDetails.Version != nil { - resp.Status(kolide.PacksSection).Warning( - kolide.Unsupported, - "version for pack '%s'", - packName, - ) - } - pack := &kolide.Pack{ - Name: packName, - Description: "Imported pack", - Platform: packDetails.Platform, - } - pack, err = svc.ds.NewPack(pack, kolide.HasTransaction(tx)) - if err != nil { - return err - } - err = svc.createLabelsForPack(pack, &packDetails, labelCache, resp, tx) - if err != nil { - return err - } - err = svc.createQueriesForPack(uid, pack, &packDetails, resp, tx) - if err != nil { - return err - } - resp.Status(kolide.PacksSection).ImportCount++ - resp.Status(kolide.PacksSection).Message("imported '%s'", packName) - } - return nil -} - -func hashQuery(platform, query string) string { - s := strings.Replace(query, " ", "", -1) - s = strings.Replace(s, "\t", "", -1) - s = strings.Replace(s, "\n", "", -1) - s = strings.Trim(s, ";") - s = platform + s - return strings.ToLower(s) -} - -func uniqueImportName() (string, error) { - random, err := kolide.RandomText(6) - if err != nil { - return "", err - } - return "import_" + random, nil -} - -func (svc service) createQueriesForPack(uid uint, pack *kolide.Pack, details *kolide.PackDetails, - resp *kolide.ImportConfigResponse, tx kolide.Transaction) error { - for queryName, queryDetails := range details.Queries { - query, ok, err := svc.ds.QueryByName(queryName, kolide.HasTransaction(tx)) - if err != nil { - return err - } - // if the query isn't already in the database, create it - if !ok { - query = &kolide.Query{ - Name: queryName, - Description: "imported", - Query: queryDetails.Query, - Saved: true, - AuthorID: uid, - } - query, err = svc.ds.NewQuery(query, kolide.HasTransaction(tx)) - if err != nil { - return err - } - resp.Status(kolide.QueriesSection).Message( - "created '%s' as part of pack '%s'", queryName, pack.Name, - ) - resp.Status(kolide.QueriesSection).ImportCount++ - } - // associate query with pack - scheduledQuery := &kolide.ScheduledQuery{ - PackID: pack.ID, - QueryID: query.ID, - Interval: uint(queryDetails.Interval), - Platform: queryDetails.Platform, - Snapshot: queryDetails.Snapshot, - Removed: queryDetails.Removed, - Version: queryDetails.Version, - Shard: configInt2Ptr(queryDetails.Shard), - } - _, err = svc.ds.NewScheduledQuery(scheduledQuery, kolide.HasTransaction(tx)) - if err != nil { - return nil - } - resp.Status(kolide.PacksSection).Message("added query '%s'", query.Name) - - } - return nil -} - -// createLabelsForPack Iterates through discover queries, creates a label for -// each query and assigns it to the pack passed as an argument. Once a Label is created we cache -// it for reuse. -func (svc service) createLabelsForPack(pack *kolide.Pack, details *kolide.PackDetails, - cache map[string]*kolide.Label, resp *kolide.ImportConfigResponse, tx kolide.Transaction) error { - for _, query := range details.Discovery { - hash := hashQuery(details.Platform, query) - label, ok := cache[hash] - // add existing label to pack - if ok { - err := svc.ds.AddLabelToPack(label.ID, pack.ID, kolide.HasTransaction(tx)) - if err != nil { - return err - } - resp.Status(kolide.PacksSection).Message( - "added label '%s' to pack '%s'", label.Name, pack.Name, - ) - continue - } - // create new label and add it to pack - labelName, err := uniqueImportName() - if err != nil { - return err - } - label = &kolide.Label{ - Name: labelName, - Query: query, - Description: "imported", - LabelType: kolide.LabelTypeRegular, - Platform: details.Platform, - } - label, err = svc.ds.NewLabel(label, kolide.HasTransaction(tx)) - if err != nil { - return err - } - // hang on to label so we can reuse it for other packs if needed - cache[hash] = label - err = svc.ds.AddLabelToPack(label.ID, pack.ID, kolide.HasTransaction(tx)) - if err != nil { - return err - } - resp.Status(kolide.PacksSection).Message( - "added label '%s' to '%s'", label.Name, pack.Name, - ) - } - return nil -} - -func (svc service) importOptions(opts kolide.OptionNameToValueMap, resp *kolide.ImportConfigResponse, tx kolide.Transaction) error { - var updateOptions []kolide.Option - for optName, optValue := range opts { - opt, err := svc.ds.OptionByName(optName, kolide.HasTransaction(tx)) - if err != nil { - resp.Status(kolide.OptionsSection).Warning( - kolide.OptionUnknown, "skipped '%s' can't find option", optName, - ) - resp.Status(kolide.OptionsSection).SkipCount++ - continue - } - if opt.ReadOnly { - resp.Status(kolide.OptionsSection).Warning( - kolide.OptionReadonly, "skipped '%s' can't change read only option", optName, - ) - resp.Status(kolide.OptionsSection).SkipCount++ - continue - } - if opt.OptionSet() { - resp.Status(kolide.OptionsSection).Warning( - kolide.OptionAlreadySet, "skipped '%s' can't change option that is already set", optName, - ) - resp.Status(kolide.OptionsSection).SkipCount++ - continue - } - opt.SetValue(optValue) - resp.Status(kolide.OptionsSection).Message("set %s value to %v", optName, optValue) - resp.Status(kolide.OptionsSection).ImportCount++ - updateOptions = append(updateOptions, *opt) - } - if len(updateOptions) > 0 { - if err := svc.ds.SaveOptions(updateOptions, kolide.HasTransaction(tx)); err != nil { - return err - } - } - return nil -} - -func configInt2Ptr(ci *kolide.OsQueryConfigInt) *uint { - if ci == nil { - return nil - } - ui := uint(*ci) - return &ui -} diff --git a/server/service/service_import_config_test.go b/server/service/service_import_config_test.go deleted file mode 100644 index 3ee2b23bff..0000000000 --- a/server/service/service_import_config_test.go +++ /dev/null @@ -1,345 +0,0 @@ -package service - -import ( - "testing" - - "github.com/kolide/fleet/server/config" - "github.com/kolide/fleet/server/datastore/inmem" - "github.com/kolide/fleet/server/kolide" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func createServiceMockForImport(t *testing.T) *service { - ds, err := inmem.New(config.TestConfig()) - require.Nil(t, err) - err = ds.MigrateData() - require.Nil(t, err) - return &service{ - ds: ds, - } -} - -func TestHashQuery(t *testing.T) { - q1 := `SELECT * FROM t1 INNER JOIN ON - t1.id = t2.t1id - WHERE t1.name = 'foo' ` - q2 := "SELECT * from t1 INNER JOIN\tON t1.id = t2.t1id WHERE t1.name = 'foo';" - h1 := hashQuery("platform", q1) - h2 := hashQuery("platform", q2) - assert.Equal(t, h1, h2) - q2 = "SELECT * from t1 INNER JOIN\tON t1.id = t2.t1id WHERE t2.name = 'foo';" - h2 = hashQuery("platform", q2) - assert.NotEqual(t, h1, h2) - -} -func TestImportFilePaths(t *testing.T) { - cfg := &kolide.ImportConfig{ - FileIntegrityMonitoring: kolide.FIMCategoryToPaths{ - "files1": []string{ - "path1", - "path2", - }, - "files2": []string{ - "path3", - }, - }, - YARA: &kolide.YARAConfig{ - Signatures: map[string][]string{ - "sig1": []string{ - "path4", - "path5", - }, - "sig2": []string{ - "path6", - }, - }, - FilePaths: map[string][]string{ - "files1": []string{ - "sig1", - "sig2", - }, - "files2": []string{ - "sig1", - }, - }, - }, - } - resp := &kolide.ImportConfigResponse{ - ImportStatusBySection: make(map[kolide.ImportSection]*kolide.ImportStatus), - } - svc := createServiceMockForImport(t) - tx, _ := svc.ds.Begin() - err := svc.importFIMSections(cfg, resp, tx) - require.Nil(t, err) - assert.Equal(t, 2, resp.Status(kolide.FilePathsSection).ImportCount) - sections, err := svc.ds.FIMSections() - require.Nil(t, err) - assert.Len(t, sections, 2) - yara, err := svc.ds.YARASection() - require.Nil(t, err) - assert.Len(t, yara.Signatures, 2) - assert.Len(t, yara.FilePaths, 2) -} - -func TestImportDecorators(t *testing.T) { - cfg := &kolide.ImportConfig{ - Decorators: &kolide.DecoratorConfig{ - Load: []string{ - "select from foo", - "select from bar", - }, - Always: []string{ - "select from always", - }, - Interval: map[string][]string{ - "100": []string{ - "select from 100", - }, - "200": []string{ - "select from 200", - }, - }, - }, - } - resp := &kolide.ImportConfigResponse{ - ImportStatusBySection: make(map[kolide.ImportSection]*kolide.ImportStatus), - } - svc := createServiceMockForImport(t) - tx, _ := svc.ds.Begin() - err := svc.importDecorators(cfg, resp, tx) - require.Nil(t, err) - assert.Equal(t, 5, resp.Status(kolide.DecoratorsSection).ImportCount) - dec, err := svc.ds.ListDecorators() - require.Nil(t, err) - assert.Len(t, dec, 5) -} - -func TestImportScheduledQueries(t *testing.T) { - cfg := &kolide.ImportConfig{ - Schedule: kolide.QueryNameToQueryDetailsMap{ - "q1": kolide.QueryDetails{ - Query: "select pid from processes", - Interval: 60, - Platform: stringPtr("linux"), - }, - "q2": kolide.QueryDetails{ - Query: "select uid from users", - Interval: 120, - Platform: stringPtr("linux"), - Version: stringPtr("1.0"), - }, - "q3": kolide.QueryDetails{ - Query: "select name from os", - Interval: 240, - Platform: stringPtr("linux"), - Snapshot: boolPtr(true), - }, - }, - } - resp := &kolide.ImportConfigResponse{ - ImportStatusBySection: make(map[kolide.ImportSection]*kolide.ImportStatus), - } - svc := createServiceMockForImport(t) - user := &kolide.User{ - Username: "bob", - Password: []byte("secret"), - Email: "bob@something.com", - Admin: false, - AdminForcedPasswordReset: false, - } - user, err := svc.ds.NewUser(user) - require.Nil(t, err) - skipQuery := &kolide.Query{ - Name: "q3", - Query: "select version from os", - Description: "should be skipped", - Saved: true, - AuthorID: user.ID, - } - _, err = svc.ds.NewQuery(skipQuery) - require.Nil(t, err) - noskipQuery := &kolide.Query{ - Name: "q2", - Query: "select uid from users", - Saved: true, - AuthorID: user.ID, - } - _, err = svc.ds.NewQuery(noskipQuery) - require.Nil(t, err) - tx, _ := svc.ds.Begin() - err = svc.importScheduledQueries(user.ID, cfg, resp, tx) - require.Nil(t, err) - _, ok, err := svc.ds.QueryByName("q1") - require.Nil(t, err) - require.True(t, ok) - _, ok, err = svc.ds.QueryByName("q2") - require.Nil(t, err) - require.True(t, ok) - _, ok, err = svc.ds.QueryByName("q3") - require.Nil(t, err) - require.True(t, ok) - -} - -func TestOptionsImportConfig(t *testing.T) { - opts := kolide.OptionNameToValueMap{ - "aws_access_key_id": "foo", - } - resp := &kolide.ImportConfigResponse{ - ImportStatusBySection: make(map[kolide.ImportSection]*kolide.ImportStatus), - } - svc := createServiceMockForImport(t) - tx, _ := svc.ds.Begin() - err := svc.importOptions(opts, resp, tx) - require.Nil(t, err) - status := resp.Status(kolide.OptionsSection) - require.NotNil(t, status) - assert.Equal(t, 1, status.ImportCount) - opt, err := svc.ds.OptionByName("aws_access_key_id") - require.Nil(t, err) - assert.Equal(t, "foo", opt.GetValue()) - require.Len(t, status.Messages, 1) - assert.Equal(t, "set aws_access_key_id value to foo", status.Messages[0]) -} - -func TestOptionsImportConfigWithSkips(t *testing.T) { - opts := kolide.OptionNameToValueMap{ - "aws_access_key_id": "foo", - "aws_secret_access_key": "secret", - // this should be skipped because it's already set - "aws_firehose_period": 100, - // these should be skipped because it's read only - "disable_distributed": false, - "pack_delimiter": "x", - // this should be skipped because it's not an option we know about - "wombat": "not venomous", - } - resp := &kolide.ImportConfigResponse{ - ImportStatusBySection: make(map[kolide.ImportSection]*kolide.ImportStatus), - } - svc := createServiceMockForImport(t) - tx, _ := svc.ds.Begin() - // set option val, it should be skipped - opt, err := svc.ds.OptionByName("aws_firehose_period") - require.Nil(t, err) - opt.SetValue(23) - err = svc.ds.SaveOptions([]kolide.Option{*opt}) - require.Nil(t, err) - err = svc.importOptions(opts, resp, tx) - require.Nil(t, err) - status := resp.Status(kolide.OptionsSection) - require.NotNil(t, status) - assert.Equal(t, 2, status.ImportCount) - assert.Equal(t, 4, status.SkipCount) - assert.Len(t, status.Warnings[kolide.OptionAlreadySet], 1) - assert.Len(t, status.Warnings[kolide.OptionReadonly], 2) - assert.Len(t, status.Warnings[kolide.OptionUnknown], 1) - assert.Len(t, status.Messages, 2) -} - -func TestPacksImportConfig(t *testing.T) { - svc := createServiceMockForImport(t) - tx, _ := svc.ds.Begin() - - p := &kolide.Pack{ - Name: "dup", - } - _, err := svc.ds.NewPack(p) - require.Nil(t, err) - - q1 := kolide.QueryDetails{ - Query: "select * from foo", - Interval: 100, - Removed: boolPtr(false), - Platform: stringPtr("linux"), - Version: stringPtr("1.0"), - } - q2 := kolide.QueryDetails{ - Query: "select * from bar", - Interval: 50, - Removed: boolPtr(false), - Platform: stringPtr("linux"), - Version: stringPtr("1.0"), - } - q3 := kolide.QueryDetails{ - Query: "select * from baz", - Interval: 500, - Removed: boolPtr(false), - Platform: stringPtr("linux"), - Version: stringPtr("1.0"), - } - - importConfig := kolide.ImportConfig{ - Packs: kolide.PackNameMap{ - "ext1": "/home/usr/ext1.json", - "pack1": kolide.PackDetails{ - Queries: kolide.QueryNameToQueryDetailsMap{ - "q1": q1, - "q2": q2, - }, - Discovery: []string{ - "select * from zz", - "select id, xx from yy", - }, - }, - "dup": kolide.PackDetails{ - Queries: kolide.QueryNameToQueryDetailsMap{ - "q1": q1, - "q2": q2, - }, - }, - "*": "/home/usr/packs/*", - }, - ExternalPacks: kolide.PackNameToPackDetails{ - "ext1": kolide.PackDetails{ - Queries: kolide.QueryNameToQueryDetailsMap{ - "q1": q1, - }, - Discovery: []string{ - "select * from zz", - "select a, b, c from processes", - }, - }, - "ext2": kolide.PackDetails{ - Queries: kolide.QueryNameToQueryDetailsMap{ - "q3": q3, - }, - }, - }, - GlobPackNames: []string{"ext2"}, - } - resp := &kolide.ImportConfigResponse{ - ImportStatusBySection: make(map[kolide.ImportSection]*kolide.ImportStatus), - } - user := &kolide.User{ - Username: "bob", - Password: []byte("secret"), - Email: "bob@something.com", - Admin: false, - AdminForcedPasswordReset: false, - } - user, err = svc.ds.NewUser(user) - require.Nil(t, err) - - packs, err := importConfig.CollectPacks() - require.Nil(t, err) - assert.Len(t, packs, 4) - err = svc.importPacks(user.ID, &importConfig, resp, tx) - require.Nil(t, err) - queries, err := svc.ds.ListQueries(kolide.ListOptions{}) - require.Nil(t, err) - assert.Len(t, queries, 3) - pack, ok, err := svc.ds.PackByName("pack1") - require.Nil(t, err) - require.True(t, ok) - sqs, err := svc.ds.ListScheduledQueriesInPack(pack.ID, kolide.ListOptions{}) - require.Nil(t, err) - assert.Len(t, sqs, 2) - labels, err := svc.ds.ListLabels(kolide.ListOptions{}) - require.Nil(t, err) - assert.Len(t, labels, 8) - assert.Equal(t, 3, resp.Status(kolide.PacksSection).ImportCount) - assert.Equal(t, 1, resp.Status(kolide.PacksSection).SkipCount) - assert.Equal(t, 3, resp.Status(kolide.QueriesSection).ImportCount) -} diff --git a/server/service/service_packs.go b/server/service/service_packs.go index e88cc21fba..2c6ad497f7 100644 --- a/server/service/service_packs.go +++ b/server/service/service_packs.go @@ -3,7 +3,6 @@ package service import ( "context" - "github.com/kolide/fleet/server/contexts/viewer" "github.com/kolide/fleet/server/kolide" ) @@ -34,13 +33,6 @@ func (svc service) NewPack(ctx context.Context, p kolide.PackPayload) (*kolide.P pack.Disabled = *p.Disabled } - vc, ok := viewer.FromContext(ctx) - if ok { - if createdBy := vc.UserID(); createdBy != uint(0) { - pack.CreatedBy = createdBy - } - } - _, err := svc.ds.NewPack(&pack) if err != nil { return nil, err diff --git a/server/service/service_queries.go b/server/service/service_queries.go index 7d4fcc389e..a1118d5457 100644 --- a/server/service/service_queries.go +++ b/server/service/service_queries.go @@ -5,8 +5,53 @@ import ( "github.com/kolide/fleet/server/contexts/viewer" "github.com/kolide/fleet/server/kolide" + "github.com/pkg/errors" ) +func queryFromSpec(spec *kolide.QuerySpec) *kolide.Query { + return &kolide.Query{ + Name: spec.Name, + Description: spec.Description, + Query: spec.Query, + } +} + +func specFromQuery(query *kolide.Query) *kolide.QuerySpec { + return &kolide.QuerySpec{ + Name: query.Name, + Description: query.Description, + Query: query.Query, + } +} + +func (svc service) ApplyQuerySpecs(ctx context.Context, specs []*kolide.QuerySpec) error { + vc, ok := viewer.FromContext(ctx) + if !ok { + return errors.New("user must be authenticated to apply queries") + } + + queries := []*kolide.Query{} + for _, spec := range specs { + queries = append(queries, queryFromSpec(spec)) + } + + err := svc.ds.ApplyQueries(vc.UserID(), queries) + return errors.Wrap(err, "applying queries") +} + +func (svc service) GetQuerySpecs(ctx context.Context) ([]*kolide.QuerySpec, error) { + queries, err := svc.ds.ListQueries(kolide.ListOptions{}) + if err != nil { + return nil, errors.Wrap(err, "getting queries") + } + + specs := []*kolide.QuerySpec{} + for _, query := range queries { + specs = append(specs, specFromQuery(query)) + } + return specs, nil +} + func (svc service) ListQueries(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Query, error) { return svc.ds.ListQueries(opt) } diff --git a/server/service/service_scheduled_queries.go b/server/service/service_scheduled_queries.go index ce570699c4..b09c6cd984 100644 --- a/server/service/service_scheduled_queries.go +++ b/server/service/service_scheduled_queries.go @@ -4,62 +4,8 @@ import ( "context" "github.com/kolide/fleet/server/kolide" - "github.com/pkg/errors" ) -func (svc service) GetScheduledQuery(ctx context.Context, id uint) (*kolide.ScheduledQuery, error) { - return svc.ds.ScheduledQuery(id) -} - func (svc service) GetScheduledQueriesInPack(ctx context.Context, id uint, opts kolide.ListOptions) ([]*kolide.ScheduledQuery, error) { return svc.ds.ListScheduledQueriesInPack(id, opts) } - -func (svc service) ScheduleQuery(ctx context.Context, sq *kolide.ScheduledQuery) (*kolide.ScheduledQuery, error) { - return svc.ds.NewScheduledQuery(sq) -} - -func (svc service) ModifyScheduledQuery(ctx context.Context, id uint, p kolide.ScheduledQueryPayload) (*kolide.ScheduledQuery, error) { - sq, err := svc.GetScheduledQuery(ctx, id) - if err != nil { - return nil, errors.Wrap(err, "getting scheduled query to modify") - } - - if p.PackID != nil { - sq.PackID = *p.PackID - } - - if p.QueryID != nil { - sq.QueryID = *p.QueryID - } - - if p.Interval != nil { - sq.Interval = *p.Interval - } - - if p.Snapshot != nil { - sq.Snapshot = p.Snapshot - } - - if p.Removed != nil { - sq.Removed = p.Removed - } - - if p.Platform != nil { - sq.Platform = p.Platform - } - - if p.Version != nil { - sq.Version = p.Version - } - - if p.Shard != nil { - sq.Shard = p.Shard - } - - return svc.ds.SaveScheduledQuery(sq) -} - -func (svc service) DeleteScheduledQuery(ctx context.Context, id uint) error { - return svc.ds.DeleteScheduledQuery(id) -} diff --git a/server/service/service_scheduled_queries_test.go b/server/service/service_scheduled_queries_test.go deleted file mode 100644 index 6a1888c55d..0000000000 --- a/server/service/service_scheduled_queries_test.go +++ /dev/null @@ -1,107 +0,0 @@ -package service - -import ( - "context" - "testing" - - "github.com/kolide/fleet/server/config" - "github.com/kolide/fleet/server/datastore/inmem" - "github.com/kolide/fleet/server/kolide" - "github.com/kolide/fleet/server/test" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGetScheduledQueriesInPack(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - svc, err := newTestService(ds, nil) - assert.Nil(t, err) - ctx := context.Background() - - u1 := test.NewUser(t, ds, "Admin", "admin", "admin@kolide.co", true) - q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) - q2 := test.NewQuery(t, ds, "bar", "select * from time;", u1.ID, true) - p1 := test.NewPack(t, ds, "baz") - sq1 := test.NewScheduledQuery(t, ds, p1.ID, q1.ID, 60, false, false) - - queries, err := svc.GetScheduledQueriesInPack(ctx, p1.ID, kolide.ListOptions{}) - require.Nil(t, err) - require.Len(t, queries, 1) - assert.Equal(t, sq1.ID, queries[0].ID) - - test.NewScheduledQuery(t, ds, p1.ID, q2.ID, 60, false, false) - test.NewScheduledQuery(t, ds, p1.ID, q2.ID, 60, true, false) - - queries, err = svc.GetScheduledQueriesInPack(ctx, p1.ID, kolide.ListOptions{}) - require.Nil(t, err) - require.Len(t, queries, 3) -} - -func TestGetScheduledQuery(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - svc, err := newTestService(ds, nil) - assert.Nil(t, err) - ctx := context.Background() - - u1 := test.NewUser(t, ds, "Admin", "admin", "admin@kolide.co", true) - q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) - p1 := test.NewPack(t, ds, "baz") - sq1 := test.NewScheduledQuery(t, ds, p1.ID, q1.ID, 60, false, false) - - query, err := svc.GetScheduledQuery(ctx, sq1.ID) - require.Nil(t, err) - assert.Equal(t, uint(60), query.Interval) -} - -func TestModifyScheduledQuery(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - svc, err := newTestService(ds, nil) - assert.Nil(t, err) - ctx := context.Background() - - u1 := test.NewUser(t, ds, "Admin", "admin", "admin@kolide.co", true) - q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) - p1 := test.NewPack(t, ds, "baz") - sq1 := test.NewScheduledQuery(t, ds, p1.ID, q1.ID, 60, false, false) - - query, err := svc.GetScheduledQuery(ctx, sq1.ID) - require.Nil(t, err) - assert.Equal(t, uint(60), query.Interval) - - interval := uint(120) - queryPayload := kolide.ScheduledQueryPayload{ - Interval: &interval, - } - query, err = svc.ModifyScheduledQuery(ctx, sq1.ID, queryPayload) - assert.Equal(t, uint(120), query.Interval) - - queryVerify, err := svc.GetScheduledQuery(ctx, sq1.ID) - require.Nil(t, err) - assert.Equal(t, uint(120), queryVerify.Interval) -} - -func TestDeleteScheduledQuery(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - svc, err := newTestService(ds, nil) - assert.Nil(t, err) - ctx := context.Background() - - u1 := test.NewUser(t, ds, "Admin", "admin", "admin@kolide.co", true) - q1 := test.NewQuery(t, ds, "foo", "select * from time;", u1.ID, true) - p1 := test.NewPack(t, ds, "baz") - sq1 := test.NewScheduledQuery(t, ds, p1.ID, q1.ID, 60, false, false) - - query, err := svc.GetScheduledQuery(ctx, sq1.ID) - require.Nil(t, err) - assert.Equal(t, uint(60), query.Interval) - - err = svc.DeleteScheduledQuery(ctx, sq1.ID) - require.Nil(t, err) - - _, err = svc.GetScheduledQuery(ctx, sq1.ID) - require.NotNil(t, err) -} diff --git a/server/service/transport_import_config.go b/server/service/transport_import_config.go deleted file mode 100644 index a0292547ba..0000000000 --- a/server/service/transport_import_config.go +++ /dev/null @@ -1,35 +0,0 @@ -package service - -import ( - "context" - "encoding/json" - "net/http" - - "github.com/kolide/fleet/server/kolide" -) - -func decodeImportConfigRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var req importRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - return nil, err - } - // Unmarshal main config - conf := kolide.ImportConfig{ - DryRun: req.DryRun, - Packs: make(kolide.PackNameMap), - ExternalPacks: make(kolide.PackNameToPackDetails), - } - if err := json.Unmarshal([]byte(req.Config), &conf); err != nil { - return nil, err - } - // Unmarshal external packs - for packName, packConfig := range req.ExternalPackConfigs { - var pack kolide.PackDetails - if err := json.Unmarshal([]byte(packConfig), &pack); err != nil { - return nil, err - } - conf.ExternalPacks[packName] = pack - } - conf.GlobPackNames = req.GlobPackNames - return conf, nil -} diff --git a/server/service/transport_scheduled_queries.go b/server/service/transport_scheduled_queries.go index d3a9c002c5..164fdcda37 100644 --- a/server/service/transport_scheduled_queries.go +++ b/server/service/transport_scheduled_queries.go @@ -2,55 +2,9 @@ package service import ( "context" - "encoding/json" "net/http" ) -func decodeScheduleQueryRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var req scheduleQueryRequest - - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - return nil, err - } - - return req, nil -} - -func decodeModifyScheduledQueryRequest(ctx context.Context, r *http.Request) (interface{}, error) { - id, err := idFromRequest(r, "id") - if err != nil { - return nil, err - } - var req modifyScheduledQueryRequest - - if err := json.NewDecoder(r.Body).Decode(&req.payload); err != nil { - return nil, err - } - - req.ID = id - return req, nil -} - -func decodeDeleteScheduledQueryRequest(ctx context.Context, r *http.Request) (interface{}, error) { - id, err := idFromRequest(r, "id") - if err != nil { - return nil, err - } - var req deleteScheduledQueryRequest - req.ID = id - return req, nil -} - -func decodeGetScheduledQueryRequest(ctx context.Context, r *http.Request) (interface{}, error) { - id, err := idFromRequest(r, "id") - if err != nil { - return nil, err - } - var req getScheduledQueryRequest - req.ID = id - return req, nil -} - func decodeGetScheduledQueriesInPackRequest(ctx context.Context, r *http.Request) (interface{}, error) { id, err := idFromRequest(r, "id") if err != nil { diff --git a/server/service/transport_scheduled_queries_test.go b/server/service/transport_scheduled_queries_test.go index 6e79d74c40..e97d61b02f 100644 --- a/server/service/transport_scheduled_queries_test.go +++ b/server/service/transport_scheduled_queries_test.go @@ -1,7 +1,6 @@ package service import ( - "bytes" "context" "net/http" "net/http/httptest" @@ -9,98 +8,8 @@ import ( "github.com/gorilla/mux" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -func TestDecodeScheduleQueryRequest(t *testing.T) { - router := mux.NewRouter() - router.HandleFunc("/api/v1/kolide/schedule", func(writer http.ResponseWriter, request *http.Request) { - r, err := decodeScheduleQueryRequest(context.Background(), request) - require.Nil(t, err) - - params := r.(scheduleQueryRequest) - assert.Equal(t, uint(5), params.PackID) - assert.Equal(t, uint(1), params.QueryID) - assert.Equal(t, uint(60), params.Interval) - assert.Equal(t, true, *params.Snapshot) - }).Methods("POST") - - var body bytes.Buffer - body.Write([]byte(`{ - "pack_id": 5, - "query_id": 1, - "interval": 60, - "snapshot": true - }`)) - - router.ServeHTTP( - httptest.NewRecorder(), - httptest.NewRequest("POST", "/api/v1/kolide/schedule", &body), - ) -} - -func TestDecodeModifyScheduledQueryRequest(t *testing.T) { - router := mux.NewRouter() - router.HandleFunc("/api/v1/kolide/scheduled/{id}", func(writer http.ResponseWriter, request *http.Request) { - r, err := decodeModifyScheduledQueryRequest(context.Background(), request) - assert.Nil(t, err) - - params := r.(modifyScheduledQueryRequest) - assert.Equal(t, uint(1), params.ID) - assert.Equal(t, uint(5), *params.payload.PackID) - assert.Equal(t, uint(6), *params.payload.QueryID) - assert.Equal(t, true, *params.payload.Removed) - assert.Equal(t, uint(60), *params.payload.Interval) - assert.Equal(t, uint(1), *params.payload.Shard) - }).Methods("PATCH") - - var body bytes.Buffer - body.Write([]byte(`{ - "pack_id": 5, - "query_id": 6, - "removed": true, - "interval": 60, - "shard": 1 - }`)) - - router.ServeHTTP( - httptest.NewRecorder(), - httptest.NewRequest("PATCH", "/api/v1/kolide/scheduled/1", &body), - ) -} - -func TestDecodeDeleteScheduledQueryRequest(t *testing.T) { - router := mux.NewRouter() - router.HandleFunc("/api/v1/kolide/scheduled/{id}", func(writer http.ResponseWriter, request *http.Request) { - r, err := decodeDeleteScheduledQueryRequest(context.Background(), request) - assert.Nil(t, err) - - params := r.(deleteScheduledQueryRequest) - assert.Equal(t, uint(1), params.ID) - }).Methods("DELETE") - - router.ServeHTTP( - httptest.NewRecorder(), - httptest.NewRequest("DELETE", "/api/v1/kolide/scheduled/1", nil), - ) -} - -func TestDecodeGetScheduledQueryRequest(t *testing.T) { - router := mux.NewRouter() - router.HandleFunc("/api/v1/kolide/scheduled/{id}", func(writer http.ResponseWriter, request *http.Request) { - r, err := decodeGetScheduledQueryRequest(context.Background(), request) - assert.Nil(t, err) - - params := r.(getScheduledQueryRequest) - assert.Equal(t, uint(1), params.ID) - }).Methods("GET") - - router.ServeHTTP( - httptest.NewRecorder(), - httptest.NewRequest("GET", "/api/v1/kolide/scheduled/1", nil), - ) -} - func TestDecodeGetScheduledQueriesInPackRequest(t *testing.T) { router := mux.NewRouter() router.HandleFunc("/api/v1/kolide/packs/{id}/scheduled", func(writer http.ResponseWriter, request *http.Request) { diff --git a/server/service/validation_import_config.go b/server/service/validation_import_config.go deleted file mode 100644 index 98c247611e..0000000000 --- a/server/service/validation_import_config.go +++ /dev/null @@ -1,127 +0,0 @@ -package service - -import ( - "context" - "strconv" - - "github.com/kolide/fleet/server/kolide" -) - -func (vm validationMiddleware) ImportConfig(ctx context.Context, cfg *kolide.ImportConfig) (*kolide.ImportConfigResponse, error) { - var invalid invalidArgumentError - vm.validateConfigOptions(cfg, &invalid) - vm.validatePacks(cfg, &invalid) - vm.validateDecorator(cfg, &invalid) - vm.validateYARA(cfg, &invalid) - if invalid.HasErrors() { - return nil, invalid - } - return vm.Service.ImportConfig(ctx, cfg) -} - -func (vm validationMiddleware) validateYARA(cfg *kolide.ImportConfig, argErrs *invalidArgumentError) { - if cfg.YARA != nil { - if cfg.YARA.FilePaths == nil { - argErrs.Append("yara", "missing file_paths") - return - } - if cfg.YARA.Signatures == nil { - argErrs.Append("yara", "missing signatures") - } - for fileSection, sigs := range cfg.YARA.FilePaths { - if cfg.FileIntegrityMonitoring == nil { - argErrs.Append("yara", "missing file paths section") - return - } - if _, ok := cfg.FileIntegrityMonitoring[fileSection]; !ok { - argErrs.Appendf("yara", "missing referenced file_paths section '%s'", fileSection) - } - for _, sig := range sigs { - if _, ok := cfg.YARA.Signatures[sig]; !ok { - argErrs.Appendf( - "yara", - "missing signature '%s' referenced in '%s'", - sig, - fileSection, - ) - } - } - } - } -} - -func (vm validationMiddleware) validateDecorator(cfg *kolide.ImportConfig, argErrs *invalidArgumentError) { - if cfg.Decorators != nil { - for str := range cfg.Decorators.Interval { - val, err := strconv.ParseInt(str, 10, 32) - if err != nil { - argErrs.Appendf("decorators", "interval '%s' must be an integer", str) - continue - } - if val%60 != 0 { - - argErrs.Appendf("decorators", "interval '%d' must be divisible by 60", val) - } - } - } -} - -func (vm validationMiddleware) validateConfigOptions(cfg *kolide.ImportConfig, argErrs *invalidArgumentError) { - if cfg.Options != nil { - for optName, optValue := range cfg.Options { - opt, err := vm.ds.OptionByName(string(optName)) - if err != nil { - // skip validation for an option we don't know about, this will generate - // a warning in the service layer - continue - } - if !opt.SameType(optValue) { - argErrs.Appendf("options", "invalid type for '%s'", optName) - } - } - } -} - -func (vm validationMiddleware) validatePacks(cfg *kolide.ImportConfig, argErrs *invalidArgumentError) { - if cfg.Packs != nil { - for packName, pack := range cfg.Packs { - // if glob packs is defined we expect at least one external pack - if packName == kolide.GlobPacks { - if len(cfg.GlobPackNames) == 0 { - argErrs.Append("external_packs", "missing glob packs") - continue - } - // make sure that each glob pack has JSON content - for _, p := range cfg.GlobPackNames { - if pd, ok := cfg.ExternalPacks[p]; !ok { - argErrs.Appendf("external_packs", "missing content for '%s'", p) - } else { - vm.validatePackContents(p, pd, argErrs) - } - } - continue - } - // if value is a string we expect a file path, in this case, the user has to supply the - // contents of said file which we store in ExternalPacks, if it's not there we need to - // raise an error - switch val := pack.(type) { - case string: - if pd, ok := cfg.ExternalPacks[packName]; !ok { - argErrs.Appendf("external_packs", "missing content for '%s'", packName) - } else { - vm.validatePackContents(packName, pd, argErrs) - } - case kolide.PackDetails: - vm.validatePackContents(packName, val, argErrs) - } - } - } -} - -func (vm validationMiddleware) validatePackContents(packName string, pack kolide.PackDetails, argErrs *invalidArgumentError) { - switch pack.Platform { - case "", "darwin", "freebsd", "windows", "linux", "any", "all": - default: - argErrs.Appendf("pack", "'%s' is not a valid platform", pack.Platform) - } -} diff --git a/server/test/new_objects.go b/server/test/new_objects.go index b08d883d67..2e9a2441fa 100644 --- a/server/test/new_objects.go +++ b/server/test/new_objects.go @@ -128,17 +128,3 @@ func NewUser(t *testing.T, ds kolide.Datastore, name, username, email string, ad return u } - -func NewScheduledQuery(t *testing.T, ds kolide.Datastore, pid, qid, interval uint, snapshot, removed bool) *kolide.ScheduledQuery { - sq, err := ds.NewScheduledQuery(&kolide.ScheduledQuery{ - PackID: pid, - QueryID: qid, - Interval: interval, - Snapshot: &snapshot, - Removed: &removed, - }) - require.Nil(t, err) - require.NotZero(t, sq.ID) - - return sq -}