diff --git a/server/datastore/datastore_campaigns_test.go b/server/datastore/datastore_campaigns_test.go index 531a5e5950..510603e1ab 100644 --- a/server/datastore/datastore_campaigns_test.go +++ b/server/datastore/datastore_campaigns_test.go @@ -45,8 +45,18 @@ func testDistributedQueryCampaign(t *testing.T, ds kolide.Datastore) { h2 := test.NewHost(t, ds, "bar.local", "192.168.1.11", "2", "2", mockClock.Now().Add(-1*time.Hour)) h3 := test.NewHost(t, ds, "baz.local", "192.168.1.12", "3", "3", mockClock.Now().Add(-13*time.Minute)) - l1 := test.NewLabel(t, ds, "label foo", "query foo") - l2 := test.NewLabel(t, ds, "label bar", "query foo") + l1 := kolide.LabelSpec{ + ID: 1, + Name: "label foo", + Query: "query foo", + } + l2 := kolide.LabelSpec{ + ID: 2, + Name: "label bar", + Query: "query bar", + } + err := ds.ApplyLabelSpecs([]*kolide.LabelSpec{&l1, &l2}) + require.Nil(t, err) checkTargets(t, ds, campaign.ID, []uint{}, []uint{}) diff --git a/server/datastore/datastore_hosts_test.go b/server/datastore/datastore_hosts_test.go index 6d3832b976..be53e82eae 100644 --- a/server/datastore/datastore_hosts_test.go +++ b/server/datastore/datastore_hosts_test.go @@ -411,10 +411,12 @@ func testDistributedQueriesForHost(t *testing.T, ds kolide.Datastore) { assert.Empty(t, queries) // Create a label - l1, err := ds.NewLabel(&kolide.Label{ + l1 := kolide.LabelSpec{ + ID: 1, Name: "label foo", Query: "query1", - }) + } + err = ds.ApplyLabelSpecs([]*kolide.LabelSpec{&l1}) require.Nil(t, err) // Add hosts to label diff --git a/server/datastore/datastore_labels_test.go b/server/datastore/datastore_labels_test.go index 785d7e03c7..ea45286753 100644 --- a/server/datastore/datastore_labels_test.go +++ b/server/datastore/datastore_labels_test.go @@ -39,35 +39,34 @@ func testLabels(t *testing.T, db kolide.Datastore) { assert.Nil(t, err) assert.Empty(t, queries) - newLabels := []kolide.Label{ + newLabels := []*kolide.LabelSpec{ // Note these are intentionally out of order - kolide.Label{ + &kolide.LabelSpec{ + ID: 1, Name: "label3", Query: "query3", Platform: "darwin", }, - kolide.Label{ + &kolide.LabelSpec{ + ID: 2, Name: "label1", Query: "query1", }, - kolide.Label{ + &kolide.LabelSpec{ + ID: 3, Name: "label2", Query: "query2", Platform: "darwin", }, - kolide.Label{ + &kolide.LabelSpec{ + ID: 4, Name: "label4", Query: "query4", Platform: "darwin", }, } - - for _, label := range newLabels { - var newLabel *kolide.Label - newLabel, err = db.NewLabel(&label) - assert.Nil(t, err) - assert.NotZero(t, newLabel.ID) - } + err = db.ApplyLabelSpecs(newLabels) + require.Nil(t, err) expectQueries := map[string]string{ "1": "query3", @@ -141,66 +140,87 @@ func testLabels(t *testing.T, db kolide.Datastore) { } func testManagingLabelsOnPacks(t *testing.T, ds kolide.Datastore) { - monitoringPack := &kolide.Pack{ - Name: "monitoring", + pack := &kolide.PackSpec{ + ID: 1, + Name: "pack1", } - _, err := ds.NewPack(monitoringPack) + err := ds.ApplyPackSpecs([]*kolide.PackSpec{pack}) require.Nil(t, err) - mysqlLabel := &kolide.Label{ + labels, err := ds.ListLabelsForPack(pack.ID) + require.Nil(t, err) + assert.Len(t, labels, 0) + + mysqlLabel := &kolide.LabelSpec{ + ID: 1, Name: "MySQL Monitoring", Query: "select pid from processes where name = 'mysqld';", } - mysqlLabel, err = ds.NewLabel(mysqlLabel) + err = ds.ApplyLabelSpecs([]*kolide.LabelSpec{mysqlLabel}) require.Nil(t, err) - err = ds.AddLabelToPack(mysqlLabel.ID, monitoringPack.ID) + pack.Targets = kolide.PackSpecTargets{ + Labels: []string{ + mysqlLabel.Name, + }, + } + err = ds.ApplyPackSpecs([]*kolide.PackSpec{pack}) require.Nil(t, err) - labels, err := ds.ListLabelsForPack(monitoringPack.ID) + labels, err = ds.ListLabelsForPack(pack.ID) require.Nil(t, err) if assert.Len(t, labels, 1) { assert.Equal(t, "MySQL Monitoring", labels[0].Name) } - osqueryLabel := &kolide.Label{ + osqueryLabel := &kolide.LabelSpec{ + ID: 2, Name: "Osquery Monitoring", Query: "select pid from processes where name = 'osqueryd';", } - osqueryLabel, err = ds.NewLabel(osqueryLabel) + err = ds.ApplyLabelSpecs([]*kolide.LabelSpec{mysqlLabel, osqueryLabel}) require.Nil(t, err) - err = ds.AddLabelToPack(osqueryLabel.ID, monitoringPack.ID) + pack.Targets = kolide.PackSpecTargets{ + Labels: []string{ + mysqlLabel.Name, + osqueryLabel.Name, + }, + } + err = ds.ApplyPackSpecs([]*kolide.PackSpec{pack}) require.Nil(t, err) - labels, err = ds.ListLabelsForPack(monitoringPack.ID) + labels, err = ds.ListLabelsForPack(pack.ID) require.Nil(t, err) assert.Len(t, labels, 2) - } func testSearchLabels(t *testing.T, db kolide.Datastore) { - _, err := db.NewLabel(&kolide.Label{ - Name: "foo", - }) + specs := []*kolide.LabelSpec{ + &kolide.LabelSpec{ + ID: 1, + Name: "foo", + }, + &kolide.LabelSpec{ + ID: 2, + Name: "bar", + }, + &kolide.LabelSpec{ + ID: 3, + Name: "foo-bar", + }, + &kolide.LabelSpec{ + ID: 4, + Name: "All Hosts", + LabelType: kolide.LabelTypeBuiltIn, + }, + } + err := db.ApplyLabelSpecs(specs) require.Nil(t, err) - _, err = db.NewLabel(&kolide.Label{ - Name: "bar", - }) + all, err := db.Label(specs[3].ID) require.Nil(t, err) - - l3, err := db.NewLabel(&kolide.Label{ - Name: "foo-bar", - }) - require.Nil(t, err) - - all, err := db.NewLabel(&kolide.Label{ - Name: "All Hosts", - LabelType: kolide.LabelTypeBuiltIn, - }) - require.Nil(t, err) - all, err = db.Label(all.ID) + l3, err := db.Label(specs[2].ID) require.Nil(t, err) // We once threw errors when the search query was empty. Verify that we @@ -230,15 +250,18 @@ func testSearchLabelsLimit(t *testing.T, db kolide.Datastore) { t.Skip("inmem is being deprecated, test skipped") } - _, err := db.NewLabel(&kolide.Label{ + all := &kolide.LabelSpec{ Name: "All Hosts", LabelType: kolide.LabelTypeBuiltIn, - }) + } + err := db.ApplyLabelSpecs([]*kolide.LabelSpec{all}) + require.Nil(t, err) for i := 0; i < 15; i++ { - _, err := db.NewLabel(&kolide.Label{ - Name: fmt.Sprintf("foo-%d", i), - }) + l := &kolide.LabelSpec{ + Name: fmt.Sprintf("foo%d", i), + } + err := db.ApplyLabelSpecs([]*kolide.LabelSpec{l}) require.Nil(t, err) } @@ -278,12 +301,13 @@ func testListHostsInLabel(t *testing.T, db kolide.Datastore) { }) require.Nil(t, err) - l1, err := db.NewLabel(&kolide.Label{ + l1 := &kolide.LabelSpec{ + ID: 1, Name: "label foo", Query: "query1", - }) + } + err = db.ApplyLabelSpecs([]*kolide.LabelSpec{l1}) require.Nil(t, err) - require.NotZero(t, l1.ID) { @@ -331,19 +355,18 @@ func testListUniqueHostsInLabels(t *testing.T, db kolide.Datastore) { hosts = append(hosts, h) } - l1, err := db.NewLabel(&kolide.Label{ + l1 := kolide.LabelSpec{ + ID: 1, Name: "label foo", Query: "query1", - }) - require.Nil(t, err) - require.NotZero(t, l1.ID) - - l2, err := db.NewLabel(&kolide.Label{ + } + l2 := kolide.LabelSpec{ + ID: 2, Name: "label bar", Query: "query2", - }) + } + err := db.ApplyLabelSpecs([]*kolide.LabelSpec{&l1, &l2}) require.Nil(t, err) - require.NotZero(t, l2.ID) for i := 0; i < 3; i++ { err = db.RecordLabelQueryExecutions(hosts[i], map[uint]bool{l1.ID: true}, time.Now()) @@ -365,63 +388,62 @@ func testListUniqueHostsInLabels(t *testing.T, db kolide.Datastore) { } -func testSaveLabel(t *testing.T, db kolide.Datastore) { +func testChangeLabelDetails(t *testing.T, db kolide.Datastore) { if db.Name() == "inmem" { t.Skip("inmem is being deprecated") } - label := &kolide.Label{ + + label := kolide.LabelSpec{ + ID: 1, Name: "my label", Description: "a label", - Query: "select 1 from processes;", + Query: "select 1 from processes", Platform: "darwin", } - label, err := db.NewLabel(label) + err := db.ApplyLabelSpecs([]*kolide.LabelSpec{&label}) require.Nil(t, err) - label.Name = "changed name" + label.Description = "changed description" - _, err = db.SaveLabel(label) + err = db.ApplyLabelSpecs([]*kolide.LabelSpec{&label}) require.Nil(t, err) + saved, err := db.Label(label.ID) require.Nil(t, err) assert.Equal(t, label.Name, saved.Name) - assert.Equal(t, label.Description, saved.Description) } -func testReplaceDeletedLabel(t *testing.T, db kolide.Datastore) { - if db.Name() == "inmem" { - t.Skip("inmem is being deprecated, test skipped") +func testApplyLabelSpecsRoundtrip(t *testing.T, ds kolide.Datastore) { + expectedSpecs := []*kolide.LabelSpec{ + &kolide.LabelSpec{ + Name: "foo", + Query: "select * from foo", + Description: "foo description", + Platform: "darwin", + }, + &kolide.LabelSpec{ + Name: "bar", + Query: "select * from bar", + }, + &kolide.LabelSpec{ + Name: "bing", + Query: "select * from bing", + }, + &kolide.LabelSpec{ + Name: "All Hosts", + Query: "SELECT 1", + LabelType: kolide.LabelTypeBuiltIn, + }, } - - label := &kolide.Label{ - Name: "my label", - Query: "select 1 from processes;", - } - saved, err := db.NewLabel(label) + err := ds.ApplyLabelSpecs(expectedSpecs) require.Nil(t, err) - - saved, err = db.Label(saved.ID) + specs, err := ds.GetLabelSpecs() require.Nil(t, err) - assert.Equal(t, label.Name, saved.Name) - assert.Equal(t, label.Description, saved.Description) + assert.Equal(t, expectedSpecs, specs) - newLabel := &kolide.Label{ - Name: "my label", - Query: " select * from time", - } - - // Replace should fail when label already exists and isn't soft deleted - _, err = db.NewLabel(newLabel) - require.NotNil(t, err) - - // Now delete label and replace should succeed - err = db.DeleteLabel(label.ID) + // Should be idempotent + err = ds.ApplyLabelSpecs(expectedSpecs) require.Nil(t, err) - - saved, err = db.NewLabel(newLabel) + specs, err = ds.GetLabelSpecs() require.Nil(t, err) - - saved, err = db.Label(saved.ID) - require.Nil(t, err) - assert.Equal(t, newLabel.Name, saved.Name) - assert.Equal(t, newLabel.Description, saved.Description) + assert.Equal(t, expectedSpecs, specs) } diff --git a/server/datastore/datastore_packs_test.go b/server/datastore/datastore_packs_test.go index 2e4365e8cc..1bb3038aed 100644 --- a/server/datastore/datastore_packs_test.go +++ b/server/datastore/datastore_packs_test.go @@ -1,6 +1,7 @@ package datastore import ( + "fmt" "testing" "github.com/WatchBeam/clock" @@ -11,14 +12,10 @@ import ( ) func testDeletePack(t *testing.T, ds kolide.Datastore) { - pack := &kolide.Pack{ - Name: "foo", - } - _, err := ds.NewPack(pack) - assert.Nil(t, err) + pack := test.NewPack(t, ds, "foo") assert.NotEqual(t, uint(0), pack.ID) - pack, err = ds.Pack(pack.ID) + pack, err := ds.Pack(pack.ID) require.Nil(t, err) err = ds.DeletePack(pack.ID) @@ -30,11 +27,7 @@ func testDeletePack(t *testing.T, ds kolide.Datastore) { } func testGetPackByName(t *testing.T, ds kolide.Datastore) { - pack := &kolide.Pack{ - Name: "foo", - } - _, err := ds.NewPack(pack) - assert.Nil(t, err) + pack := test.NewPack(t, ds, "foo") assert.NotEqual(t, uint(0), pack.ID) pack, ok, err := ds.PackByName(pack.Name) @@ -50,28 +43,64 @@ func testGetPackByName(t *testing.T, ds kolide.Datastore) { } -func testGetHostsInPack(t *testing.T, ds kolide.Datastore) { +func testListPacks(t *testing.T, ds kolide.Datastore) { + p1 := &kolide.PackSpec{ + ID: 1, + Name: "foo_pack", + } + p2 := &kolide.PackSpec{ + ID: 2, + Name: "bar_pack", + } + err := ds.ApplyPackSpecs([]*kolide.PackSpec{p1}) + require.Nil(t, err) + + packs, err := ds.ListPacks(kolide.ListOptions{}) + require.Nil(t, err) + assert.Len(t, packs, 1) + + err = ds.ApplyPackSpecs([]*kolide.PackSpec{p1, p2}) + require.Nil(t, err) + + packs, err = ds.ListPacks(kolide.ListOptions{}) + require.Nil(t, err) + assert.Len(t, packs, 2) + + fmt.Println(packs[0], packs[1]) +} + +func testListHostsInPack(t *testing.T, ds kolide.Datastore) { if ds.Name() == "inmem" { t.Skip("inmem is deprecated") } mockClock := clock.NewMockClock() - p1, err := ds.NewPack(&kolide.Pack{ + l1 := kolide.LabelSpec{ + ID: 1, Name: "foo", - }) + } + err := ds.ApplyLabelSpecs([]*kolide.LabelSpec{&l1}) require.Nil(t, err) - l1, err := ds.NewLabel(&kolide.Label{ - Name: "foo", - }) - require.Nil(t, err) - - err = ds.AddLabelToPack(l1.ID, p1.ID) + p1 := &kolide.PackSpec{ + ID: 1, + Name: "foo_pack", + Targets: kolide.PackSpecTargets{ + Labels: []string{ + l1.Name, + }, + }, + } + err = ds.ApplyPackSpecs([]*kolide.PackSpec{p1}) require.Nil(t, err) h1 := test.NewHost(t, ds, "h1.local", "10.10.10.1", "1", "1", mockClock.Now()) + hostsInPack, err := ds.ListHostsInPack(p1.ID, kolide.ListOptions{}) + require.Nil(t, err) + require.Len(t, hostsInPack, 0) + err = ds.RecordLabelQueryExecutions( h1, map[uint]bool{l1.ID: true}, @@ -79,7 +108,7 @@ func testGetHostsInPack(t *testing.T, ds kolide.Datastore) { ) require.Nil(t, err) - hostsInPack, err := ds.ListHostsInPack(p1.ID, kolide.ListOptions{}) + hostsInPack, err = ds.ListHostsInPack(p1.ID, kolide.ListOptions{}) require.Nil(t, err) require.Len(t, hostsInPack, 1) @@ -99,38 +128,29 @@ func testGetHostsInPack(t *testing.T, ds kolide.Datastore) { hostsInPack, err = ds.ListHostsInPack(p1.ID, kolide.ListOptions{}) require.Nil(t, err) require.Len(t, hostsInPack, 2) - - h3 := test.NewHost(t, ds, "h3.local", "10.10.10.3", "3", "3", mockClock.Now()) - - err = ds.AddHostToPack(h3.ID, p1.ID) - require.Nil(t, err) - - hostsInPack, err = ds.ListHostsInPack(p1.ID, kolide.ListOptions{}) - require.Nil(t, err) - require.Len(t, hostsInPack, 3) - - explicitHostsInPack, err = ds.ListExplicitHostsInPack(p1.ID, kolide.ListOptions{}) - require.Nil(t, err) - require.Len(t, explicitHostsInPack, 1) } func testAddLabelToPackTwice(t *testing.T, ds kolide.Datastore) { - l1 := test.NewLabel(t, ds, "l1", "select 1;") - p1 := test.NewPack(t, ds, "p1") + l1 := kolide.LabelSpec{ + ID: 1, + Name: "l1", + Query: "select 1", + } + err := ds.ApplyLabelSpecs([]*kolide.LabelSpec{&l1}) + require.Nil(t, err) - err := ds.AddLabelToPack(l1.ID, p1.ID) - assert.Nil(t, err) - - labels, err := ds.ListLabelsForPack(p1.ID) - assert.Nil(t, err) - assert.Len(t, labels, 1) - - err = ds.AddLabelToPack(l1.ID, p1.ID) - assert.Nil(t, err) - - labels, err = ds.ListLabelsForPack(p1.ID) - assert.Nil(t, err) - assert.Len(t, labels, 1) + p1 := &kolide.PackSpec{ + ID: 1, + Name: "pack1", + Targets: kolide.PackSpecTargets{ + Labels: []string{ + l1.Name, + l1.Name, + }, + }, + } + err = ds.ApplyPackSpecs([]*kolide.PackSpec{p1}) + require.NotNil(t, err) } func testApplyPackSpecRoundtrip(t *testing.T, ds kolide.Datastore) { @@ -143,9 +163,22 @@ func testApplyPackSpecRoundtrip(t *testing.T, ds kolide.Datastore) { 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") + labels := []*kolide.LabelSpec{ + &kolide.LabelSpec{ + Name: "foo", + Query: "select * from foo", + }, + &kolide.LabelSpec{ + Name: "bar", + Query: "select * from bar", + }, + &kolide.LabelSpec{ + Name: "bing", + Query: "select * from bing", + }, + } + err = ds.ApplyLabelSpecs(labels) + require.Nil(t, err) boolPtr := func(b bool) *bool { return &b } uintPtr := func(x uint) *uint { return &x } @@ -196,7 +229,6 @@ func testApplyPackSpecRoundtrip(t *testing.T, ds kolide.Datastore) { func testApplyPackSpecMissingQueries(t *testing.T, ds kolide.Datastore) { // Do not define queries mentioned in spec - specs := []*kolide.PackSpec{ &kolide.PackSpec{ ID: 1, @@ -219,3 +251,172 @@ func testApplyPackSpecMissingQueries(t *testing.T, ds kolide.Datastore) { assert.Contains(t, err.Error(), "unknown query 'bar'") } } + +func testListLabelsForPack(t *testing.T, ds kolide.Datastore) { + labelSpecs := []*kolide.LabelSpec{ + &kolide.LabelSpec{ + Name: "foo", + Query: "select * from foo", + }, + &kolide.LabelSpec{ + Name: "bar", + Query: "select * from bar", + }, + &kolide.LabelSpec{ + Name: "bing", + Query: "select * from bing", + }, + } + err := ds.ApplyLabelSpecs(labelSpecs) + require.Nil(t, err) + + specs := []*kolide.PackSpec{ + &kolide.PackSpec{ + ID: 1, + Name: "test_pack", + Targets: kolide.PackSpecTargets{ + Labels: []string{ + "foo", + "bar", + "bing", + }, + }, + }, + &kolide.PackSpec{ + ID: 2, + Name: "test 2", + Targets: kolide.PackSpecTargets{ + Labels: []string{ + "bing", + }, + }, + }, + &kolide.PackSpec{ + ID: 3, + Name: "test 3", + }, + } + err = ds.ApplyPackSpecs(specs) + require.Nil(t, err) + + labels, err := ds.ListLabelsForPack(specs[0].ID) + require.Nil(t, err) + assert.Len(t, labels, 3) + + labels, err = ds.ListLabelsForPack(specs[1].ID) + require.Nil(t, err) + assert.Len(t, labels, 1) + assert.Equal(t, "bing", labels[0].Name) + + labels, err = ds.ListLabelsForPack(specs[2].ID) + require.Nil(t, err) + assert.Len(t, labels, 0) +} + +func testListPacksForHost(t *testing.T, ds kolide.Datastore) { + if ds.Name() == "inmem" { + t.Skip("inmem is deprecated") + } + + mockClock := clock.NewMockClock() + + l1 := &kolide.LabelSpec{ + ID: 1, + Name: "foo", + } + l2 := &kolide.LabelSpec{ + ID: 2, + Name: "bar", + } + err := ds.ApplyLabelSpecs([]*kolide.LabelSpec{l1, l2}) + require.Nil(t, err) + + p1 := &kolide.PackSpec{ + ID: 1, + Name: "foo_pack", + Targets: kolide.PackSpecTargets{ + Labels: []string{ + l1.Name, + l2.Name, + }, + }, + } + p2 := &kolide.PackSpec{ + ID: 2, + Name: "shmoo_pack", + Targets: kolide.PackSpecTargets{ + Labels: []string{ + l2.Name, + }, + }, + } + err = ds.ApplyPackSpecs([]*kolide.PackSpec{p1, p2}) + require.Nil(t, err) + + h1 := test.NewHost(t, ds, "h1.local", "10.10.10.1", "1", "1", mockClock.Now()) + + packs, err := ds.ListPacksForHost(h1.ID) + require.Nil(t, err) + require.Len(t, packs, 0) + + err = ds.RecordLabelQueryExecutions( + h1, + map[uint]bool{l1.ID: true}, + mockClock.Now(), + ) + require.Nil(t, err) + + packs, err = ds.ListPacksForHost(h1.ID) + require.Nil(t, err) + if assert.Len(t, packs, 1) { + assert.Equal(t, "foo_pack", packs[0].Name) + } + + err = ds.RecordLabelQueryExecutions( + h1, + map[uint]bool{l1.ID: false, l2.ID: true}, + mockClock.Now(), + ) + require.Nil(t, err) + + packs, err = ds.ListPacksForHost(h1.ID) + require.Nil(t, err) + assert.Len(t, packs, 2) + + err = ds.RecordLabelQueryExecutions( + h1, + map[uint]bool{l1.ID: true, l2.ID: true}, + mockClock.Now(), + ) + require.Nil(t, err) + + packs, err = ds.ListPacksForHost(h1.ID) + require.Nil(t, err) + assert.Len(t, packs, 2) + + h2 := test.NewHost(t, ds, "h2.local", "10.10.10.2", "2", "2", mockClock.Now()) + + err = ds.RecordLabelQueryExecutions( + h2, + map[uint]bool{l2.ID: true}, + mockClock.Now(), + ) + require.Nil(t, err) + + packs, err = ds.ListPacksForHost(h1.ID) + require.Nil(t, err) + assert.Len(t, packs, 2) + + err = ds.RecordLabelQueryExecutions( + h1, + map[uint]bool{l2.ID: false}, + mockClock.Now(), + ) + require.Nil(t, err) + + packs, err = ds.ListPacksForHost(h1.ID) + require.Nil(t, err) + if assert.Len(t, packs, 1) { + assert.Equal(t, "foo_pack", packs[0].Name) + } +} diff --git a/server/datastore/datastore_targets_test.go b/server/datastore/datastore_targets_test.go index 5c7a9860ed..4f97b87ff5 100644 --- a/server/datastore/datastore_targets_test.go +++ b/server/datastore/datastore_targets_test.go @@ -43,19 +43,18 @@ func testCountHostsInTargets(t *testing.T, ds kolide.Datastore) { const thirtyDaysAndAMinuteAgo = -1 * (30*24*60 + 1) h6 := initHost(mockClock.Now().Add(thirtyDaysAndAMinuteAgo*time.Minute), 3600, 3600) - l1, err := ds.NewLabel(&kolide.Label{ + l1 := kolide.LabelSpec{ + ID: 1, Name: "label foo", Query: "query foo", - }) - require.Nil(t, err) - require.NotZero(t, l1.ID) - - l2, err := ds.NewLabel(&kolide.Label{ + } + l2 := kolide.LabelSpec{ + ID: 2, Name: "label bar", - Query: "query foo", - }) + Query: "query bar", + } + err := ds.ApplyLabelSpecs([]*kolide.LabelSpec{&l1, &l2}) require.Nil(t, err) - require.NotZero(t, l2.ID) for _, h := range []*kolide.Host{h1, h2, h3, h6} { err = ds.RecordLabelQueryExecutions(h, map[uint]bool{l1.ID: true}, mockClock.Now()) diff --git a/server/datastore/datastore_test.go b/server/datastore/datastore_test.go index 6a44ba190a..b71f3b42cb 100644 --- a/server/datastore/datastore_test.go +++ b/server/datastore/datastore_test.go @@ -47,7 +47,9 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){ testSaveHosts, testDeleteHost, testListHost, - testGetHostsInPack, + testListHostsInPack, + testListPacksForHost, + testListPacks, testDistributedQueryCampaign, testCleanupDistributedQueryCampaigns, testBuiltInLabels, @@ -66,9 +68,8 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){ testDuplicateNewQuery, testIdempotentDeleteHost, testChangeEmail, - testSaveLabel, + testChangeLabelDetails, testFlappingNetworkInterfaces, - testReplaceDeletedLabel, testMigrationStatus, testUnicode, testCountHostsInTargets, @@ -80,4 +81,6 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){ testApplyQueries, testApplyPackSpecRoundtrip, testApplyPackSpecMissingQueries, + testApplyLabelSpecsRoundtrip, + testListLabelsForPack, } diff --git a/server/datastore/datastore_unicode_test.go b/server/datastore/datastore_unicode_test.go index 5b9351cda5..905df448b8 100644 --- a/server/datastore/datastore_unicode_test.go +++ b/server/datastore/datastore_unicode_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/kolide/fleet/server/kolide" + "github.com/kolide/fleet/server/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -14,10 +15,15 @@ func testUnicode(t *testing.T, ds kolide.Datastore) { t.Skip("inmem is being deprecated, test skipped") } - label, err := ds.NewLabel(&kolide.Label{Name: "測試"}) + l1 := kolide.LabelSpec{ + ID: 1, + Name: "測試", + Query: "query foo", + } + err := ds.ApplyLabelSpecs([]*kolide.LabelSpec{&l1}) require.Nil(t, err) - label, err = ds.Label(label.ID) + label, err := ds.Label(l1.ID) require.Nil(t, err) assert.Equal(t, "測試", label.Name) @@ -35,10 +41,10 @@ func testUnicode(t *testing.T, ds kolide.Datastore) { require.Nil(t, err) user, err = ds.User(user.Username) + require.Nil(t, err) assert.Equal(t, "🍱", user.Username) - pack, err := ds.NewPack(&kolide.Pack{Name: "👨🏾‍🚒"}) - require.Nil(t, err) + pack := test.NewPack(t, ds, "👨🏾‍🚒") pack, err = ds.Pack(pack.ID) assert.Equal(t, "👨🏾‍🚒", pack.Name) diff --git a/server/datastore/inmem/inmem.go b/server/datastore/inmem/inmem.go index 73374ac882..be511c7ce6 100644 --- a/server/datastore/inmem/inmem.go +++ b/server/datastore/inmem/inmem.go @@ -47,6 +47,7 @@ type Datastore struct { kolide.QueryStore kolide.PackStore kolide.ScheduledQueryStore + kolide.LabelStore } func New(config config.KolideConfig) (*Datastore, error) { @@ -138,7 +139,7 @@ func (d *Datastore) MigrateData() error { SMTPVerifySSLCerts: true, } - return d.createBuiltinLabels() + return nil } func (m *Datastore) MigrationStatus() (kolide.MigrationStatus, error) { @@ -162,10 +163,6 @@ func (d *Datastore) Initialize() error { return err } - if err := d.createDevLabels(); err != nil { - return err - } - if err := d.createDevOrgInfo(); err != nil { return err } @@ -252,18 +249,6 @@ func (d *Datastore) createDevPacksAndQueries() error { return err } -func (d *Datastore) createBuiltinLabels() error { - for _, label := range appstate.Labels2() { - label := label - _, err := d.NewLabel(&label) - if err != nil { - return err - } - } - - return nil -} - // Bootstrap a few users when using the in-memory database. // Each user's default password will just be their username. func (d *Datastore) createDevUsers() error { @@ -465,43 +450,3 @@ func (d *Datastore) createDevOrgInfo() error { _, err := d.NewAppConfig(devOrgInfo) return err } - -func (d *Datastore) createDevLabels() error { - labels := []kolide.Label{ - { - UpdateCreateTimestamps: kolide.UpdateCreateTimestamps{ - CreateTimestamp: kolide.CreateTimestamp{ - CreatedAt: time.Date(2016, time.October, 27, 8, 31, 16, 0, time.UTC), - }, - UpdateTimestamp: kolide.UpdateTimestamp{ - UpdatedAt: time.Date(2016, time.October, 27, 8, 31, 16, 0, time.UTC), - }, - }, - Name: "dev_label_apache", - Query: "select * from processes where name like '%Apache%'", - }, - { - UpdateCreateTimestamps: kolide.UpdateCreateTimestamps{ - CreateTimestamp: kolide.CreateTimestamp{ - CreatedAt: time.Now().Add(-1 * time.Hour), - }, - UpdateTimestamp: kolide.UpdateTimestamp{ - UpdatedAt: time.Now(), - }, - }, - - Name: "dev_label_darwin", - Query: "select * from osquery_info where platform='darwin'", - }, - } - - for _, label := range labels { - label := label - _, err := d.NewLabel(&label) - if err != nil { - return err - } - } - - return nil -} diff --git a/server/datastore/mysql/labels.go b/server/datastore/mysql/labels.go index 7f39f79231..818d64d81a 100644 --- a/server/datastore/mysql/labels.go +++ b/server/datastore/mysql/labels.go @@ -2,6 +2,7 @@ package mysql import ( "database/sql" + "fmt" "time" "github.com/jmoiron/sqlx" @@ -9,48 +10,66 @@ import ( "github.com/pkg/errors" ) -// NewLabel creates a new kolide.Label -func (d *Datastore) NewLabel(label *kolide.Label, opts ...kolide.OptionalArg) (*kolide.Label, error) { - db := d.getTransaction(opts) - var ( - deletedLabel kolide.Label - query string - ) - err := db.Get(&deletedLabel, - "SELECT * FROM labels WHERE name = ? AND deleted", label.Name) - switch err { - case nil: - query = ` - REPLACE INTO labels ( - name, - description, - query, - platform, - label_type - ) VALUES ( ?, ?, ?, ?, ?) - ` - case sql.ErrNoRows: - query = ` +func (d *Datastore) ApplyLabelSpecs(specs []*kolide.LabelSpec) (err error) { + tx, err := d.db.Beginx() + if err != nil { + return errors.Wrap(err, "begin ApplyLabelSpecs 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 labels ( name, description, query, platform, label_type - ) VALUES ( ?, ?, ?, ?, ?) + ) VALUES ( ?, ?, ?, ?, ? ) + ON DUPLICATE KEY UPDATE + name = VALUES(name), + description = VALUES(description), + query = VALUES(query), + platform = VALUES(platform), + label_type = VALUES(label_type), + deleted = false ` - default: - return nil, errors.Wrap(err, "check for existing label") - } - result, err := db.Exec(query, label.Name, label.Description, label.Query, label.Platform, label.LabelType) + stmt, err := tx.Prepare(sql) if err != nil { - return nil, errors.Wrap(err, "inserting label") + return errors.Wrap(err, "prepare ApplyLabelSpecs insert") } - id, _ := result.LastInsertId() - label.ID = uint(id) - return label, nil + for _, s := range specs { + _, err := stmt.Exec(s.Name, s.Description, s.Query, s.Platform, s.LabelType) + if err != nil { + return errors.Wrap(err, "exec ApplyLabelSpecs insert") + } + } + err = tx.Commit() + return errors.Wrap(err, "commit ApplyLabelSpecs transaction") +} + +func (d *Datastore) GetLabelSpecs() ([]*kolide.LabelSpec, error) { + var specs []*kolide.LabelSpec + // Get basic specs + query := "SELECT name, description, query, platform, label_type FROM labels" + if err := d.db.Select(&specs, query); err != nil { + return nil, errors.Wrap(err, "get labels") + } + + return specs, nil } // DeleteLabel soft deletes a kolide.Label @@ -378,17 +397,3 @@ func (d *Datastore) SearchLabels(query string, omit ...uint) ([]kolide.Label, er return matches, nil } - -func (d *Datastore) SaveLabel(label *kolide.Label) (*kolide.Label, error) { - query := ` - UPDATE labels SET - name = ?, - description = ? - WHERE id = ? - ` - _, err := d.db.Exec(query, label.Name, label.Description, label.ID) - if err != nil { - return nil, errors.Wrap(err, "saving label") - } - return label, nil -} diff --git a/server/datastore/mysql/packs.go b/server/datastore/mysql/packs.go index 228e31642c..10867209b2 100644 --- a/server/datastore/mysql/packs.go +++ b/server/datastore/mysql/packs.go @@ -135,7 +135,6 @@ func (d *Datastore) GetPackSpecs() (specs []*kolide.PackSpec, err error) { // 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") } @@ -151,17 +150,6 @@ WHERE pack_id = ? AND pt.type = ? AND pt.target_id = l.id } } - // 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 = ` @@ -203,67 +191,6 @@ func (d *Datastore) PackByName(name string, opts ...kolide.OptionalArg) (*kolide return &pack, true, nil } -// NewPack creates a new Pack -func (d *Datastore) NewPack(pack *kolide.Pack, opts ...kolide.OptionalArg) (*kolide.Pack, error) { - db := d.getTransaction(opts) - var ( - deletedPack kolide.Pack - query string - ) - err := db.Get(&deletedPack, - "SELECT * FROM packs WHERE name = ? AND deleted", pack.Name) - switch err { - case nil: - query = ` - REPLACE INTO packs - ( name, description, platform, disabled, deleted) - VALUES ( ?, ?, ?, ?, ?) - ` - case sql.ErrNoRows: - query = ` - INSERT INTO packs - ( 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.Disabled, deleted) - if err != nil && isDuplicate(err) { - return nil, alreadyExists("Pack", deletedPack.ID) - } else if err != nil { - return nil, errors.Wrap(err, "creating new pack") - } - - id, _ := result.LastInsertId() - pack.ID = uint(id) - return pack, nil -} - -// SavePack stores changes to pack -func (d *Datastore) SavePack(pack *kolide.Pack) error { - query := ` - UPDATE packs - SET name = ?, platform = ?, disabled = ?, description = ? - WHERE id = ? AND NOT deleted - ` - - results, err := d.db.Exec(query, pack.Name, pack.Platform, pack.Disabled, pack.Description, pack.ID) - if err != nil { - return errors.Wrap(err, "updating pack") - } - rowsAffected, err := results.RowsAffected() - if err != nil { - return errors.Wrap(err, "rows affected updating packs") - } - if rowsAffected == 0 { - return notFound("Pack").WithID(pack.ID) - } - return nil -} - // DeletePack soft deletes a kolide.Pack so that it won't show up in results func (d *Datastore) DeletePack(pid uint) error { return d.deleteEntity("packs", pid) @@ -294,38 +221,6 @@ func (d *Datastore) ListPacks(opt kolide.ListOptions) ([]*kolide.Pack, error) { return packs, nil } -// AddLabelToPack associates a kolide.Label with a kolide.Pack -func (d *Datastore) AddLabelToPack(lid uint, pid uint, opts ...kolide.OptionalArg) error { - db := d.getTransaction(opts) - - query := ` - INSERT INTO pack_targets ( pack_id, type, target_id ) - VALUES ( ?, ?, ? ) - ON DUPLICATE KEY UPDATE id=id - ` - _, err := db.Exec(query, pid, kolide.TargetLabel, lid) - if err != nil { - return errors.Wrap(err, "adding label to pack") - } - - return nil -} - -// AddHostToPack associates a kolide.Host with a kolide.Pack -func (d *Datastore) AddHostToPack(hid, pid uint) error { - query := ` - INSERT INTO pack_targets ( pack_id, type, target_id ) - VALUES ( ?, ?, ? ) - ON DUPLICATE KEY UPDATE id=id - ` - _, err := d.db.Exec(query, pid, kolide.TargetHost, hid) - if err != nil { - return errors.Wrap(err, "adding host to pack") - } - - return nil -} - // ListLabelsForPack will return a list of kolide.Label records associated with kolide.Pack func (d *Datastore) ListLabelsForPack(pid uint) ([]*kolide.Label, error) { query := ` @@ -356,39 +251,26 @@ func (d *Datastore) ListLabelsForPack(pid uint) ([]*kolide.Label, error) { return labels, nil } -// RemoreLabelFromPack will remove the association between a kolide.Label and -// a kolide.Pack -func (d *Datastore) RemoveLabelFromPack(lid, pid uint) error { +func (d *Datastore) ListPacksForHost(hid uint) ([]*kolide.Pack, error) { query := ` - DELETE FROM pack_targets - WHERE target_id = ? AND pack_id = ? AND type = ? + SELECT DISTINCT p.* + FROM packs p + JOIN pack_targets pt + JOIN label_query_executions lqe + ON ( + p.id = pt.pack_id + AND pt.target_id = lqe.label_id + AND pt.type = ? + AND lqe.matches + ) + WHERE lqe.host_id = ? AND NOT p.disabled ` - _, err := d.db.Exec(query, lid, pid, kolide.TargetLabel) - if err == sql.ErrNoRows { - return notFound("PackTarget").WithMessage(fmt.Sprintf("label ID: %d, pack ID: %d", lid, pid)) - } else if err != nil { - return errors.Wrap(err, "removing label from pack") + + packs := []*kolide.Pack{} + if err := d.db.Select(&packs, query, kolide.TargetLabel, hid); err != nil && err != sql.ErrNoRows { + return nil, errors.Wrap(err, "listing hosts in pack") } - - return nil -} - -// RemoveHostFromPack will remove the association between a kolide.Host and a -// kolide.Pack -func (d *Datastore) RemoveHostFromPack(hid, pid uint) error { - query := ` - DELETE FROM pack_targets - WHERE target_id = ? AND pack_id = ? AND type = ? - ` - _, err := d.db.Exec(query, hid, pid, kolide.TargetHost) - if err == sql.ErrNoRows { - return notFound("PackTarget").WithMessage(fmt.Sprintf("host ID: %d, pack ID: %d", hid, pid)) - } else if err != nil { - return errors.Wrap(err, "removing host from pack") - } - - return nil - + return packs, nil } func (d *Datastore) ListHostsInPack(pid uint, opt kolide.ListOptions) ([]uint, error) { diff --git a/server/datastore/mysql/scheduled_queries.go b/server/datastore/mysql/scheduled_queries.go index 5db2eafbe4..15be0a2c5b 100644 --- a/server/datastore/mysql/scheduled_queries.go +++ b/server/datastore/mysql/scheduled_queries.go @@ -8,9 +8,19 @@ import ( func (d *Datastore) ListScheduledQueriesInPack(id uint, opts kolide.ListOptions) ([]*kolide.ScheduledQuery, error) { query := ` 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 + sq.id, + sq.pack_id, + COALESCE(sq.name, q.name) AS name, + sq.query_name, + COALESCE(sq.description, '') AS description, + sq.interval, + sq.snapshot, + sq.removed, + COALESCE(sq.platform, '') AS platform, + sq.version, + sq.shard, + q.query, + q.id AS query_id FROM scheduled_queries sq JOIN queries q ON sq.query_name = q.name diff --git a/server/kolide/labels.go b/server/kolide/labels.go index f6418138f8..ba0be310e9 100644 --- a/server/kolide/labels.go +++ b/server/kolide/labels.go @@ -6,8 +6,13 @@ import ( ) type LabelStore interface { + // ApplyLabelSpes applies a list of LabelSpecs to the datastore, + // creating and updating labels as necessary. + ApplyLabelSpecs(specs []*LabelSpec) error + // GetLabelSpecs returns all of the stored LabelSpecs. + GetLabelSpecs() ([]*LabelSpec, error) + // Label methods - NewLabel(Label *Label, opts ...OptionalArg) (*Label, error) DeleteLabel(lid uint) error Label(lid uint) (*Label, error) ListLabels(opt ListOptions) ([]*Label, error) @@ -38,31 +43,24 @@ type LabelStore interface { ListUniqueHostsInLabels(labels []uint) ([]Host, error) SearchLabels(query string, omit ...uint) ([]Label, error) - - // SaveLabel allows modification of a label's name and/or description - SaveLabel(label *Label) (*Label, error) } type LabelService interface { + // ApplyLabelSpes applies a list of LabelSpecs to the datastore, + // creating and updating labels as necessary. + ApplyLabelSpecs(ctx context.Context, specs []*LabelSpec) error + // GetLabelSpecs returns all of the stored LabelSpecs. + GetLabelSpecs(ctx context.Context) ([]*LabelSpec, error) + ListLabels(ctx context.Context, opt ListOptions) (labels []*Label, err error) GetLabel(ctx context.Context, id uint) (label *Label, err error) - NewLabel(ctx context.Context, p LabelPayload) (label *Label, err error) DeleteLabel(ctx context.Context, id uint) (err error) - // ModifyLabel is used to change editable fields belonging to a Label - ModifyLabel(ctx context.Context, id uint, payload ModifyLabelPayload) (*Label, error) - // HostIDsForLabel returns ids of hosts that belong to the label identified // by lid HostIDsForLabel(lid uint) ([]uint, error) } -// ModifyLabelPayload is used to change editable fields for a Label -type ModifyLabelPayload struct { - Name *string `json:"name"` - Description *string `json:"description"` -} - type LabelPayload struct { Name *string `json:"name"` Query *string `json:"query"` @@ -99,3 +97,12 @@ type LabelQueryExecution struct { LabelID uint HostID uint } + +type LabelSpec struct { + ID uint + Name string `json:"name"` + Description string `json:"description"` + Query string `json:"query"` + Platform string `json:"platform,omitempty"` + LabelType LabelType `json:"label_type" db:"label_type"` +} diff --git a/server/kolide/packs.go b/server/kolide/packs.go index f0ff107f7f..80af2c32a1 100644 --- a/server/kolide/packs.go +++ b/server/kolide/packs.go @@ -6,15 +6,12 @@ import ( // PackStore is the datastore interface for managing query packs. type PackStore interface { + // ApplyPackSpecs applies a list of PackSpecs to the datastore, + // creating and updating packs as necessary. ApplyPackSpecs(specs []*PackSpec) error + // GetPackSpecs returns all of the stored PackSpecs. GetPackSpecs() ([]*PackSpec, error) - // NewPack creates a new pack in the datastore. - NewPack(pack *Pack, opts ...OptionalArg) (*Pack, error) - - // SavePack updates an existing pack in the datastore. - SavePack(pack *Pack) error - // DeletePack deletes a pack record from the datastore. DeletePack(pid uint) error @@ -27,25 +24,14 @@ type PackStore interface { // exists the bool return value is true PackByName(name string, opts ...OptionalArg) (*Pack, bool, error) - // AddLabelToPack adds an existing label to an existing pack, both by ID. - AddLabelToPack(lid, pid uint, opts ...OptionalArg) error - - // RemoveLabelFromPack removes an existing label from it's association with - // an existing pack, both by ID. - RemoveLabelFromPack(lid, pid uint) error - // ListLabelsForPack lists all labels that are associated with a pack. ListLabelsForPack(pid uint) ([]*Label, error) - // AddHostToPack adds an existing host to an existing pack, both by ID. - AddHostToPack(hid uint, pid uint) error + // ListPacksForHost lists the packs that a host should execute. + ListPacksForHost(hid uint) (packs []*Pack, err error) - // RemoveHostFromPack removes an existing host from it's association with - // an existing pack, both by ID. - RemoveHostFromPack(hid uint, pid uint) error - - // ListHostsInPack lists the IDs of all hosts that are associated with a pack, - // both through labels and manual associations. + // ListHostsInPack lists the IDs of all hosts that are associated with a pack + // through labels. ListHostsInPack(pid uint, opt ListOptions) ([]uint, error) // ListExplicitHostsInPack lists the IDs of hosts that have been manually @@ -55,38 +41,24 @@ type PackStore interface { // PackService is the service interface for managing query packs. type PackService interface { + // ApplyPackSpecs applies a list of PackSpecs to the datastore, + // creating and updating packs as necessary. + ApplyPackSpecs(ctx context.Context, specs []*PackSpec) error + // GetPackSpecs returns all of the stored PackSpecs. + GetPackSpecs(ctx context.Context) ([]*PackSpec, error) + // ListPacks lists all packs in the application. ListPacks(ctx context.Context, opt ListOptions) (packs []*Pack, err error) // GetPack retrieves a pack by ID. GetPack(ctx context.Context, id uint) (pack *Pack, err error) - // NewPack creates a new pack in the datastore. - NewPack(ctx context.Context, p PackPayload) (pack *Pack, err error) - - // ModifyPack modifies an existing pack in the datastore. - ModifyPack(ctx context.Context, id uint, p PackPayload) (pack *Pack, err error) - // DeletePack deletes a pack record from the datastore. DeletePack(ctx context.Context, id uint) (err error) - // AddLabelToPack adds an existing label to an existing pack, both by ID. - AddLabelToPack(ctx context.Context, lid, pid uint) (err error) - - // RemoveLabelFromPack removes an existing label from it's association with - // an existing pack, both by ID. - RemoveLabelFromPack(ctx context.Context, lid, pid uint) (err error) - // ListLabelsForPack lists all labels that are associated with a pack. ListLabelsForPack(ctx context.Context, pid uint) (labels []*Label, err error) - // AddHostToPack adds an existing host to an existing pack, both by ID. - AddHostToPack(ctx context.Context, hid, pid uint) (err error) - - // RemoveHostFromPack removes an existing host from it's association with - // an existing pack, both by ID. - RemoveHostFromPack(ctx context.Context, hid, pid uint) (err error) - // ListPacksForHost lists the packs that a host should execute. ListPacksForHost(ctx context.Context, hid uint) (packs []*Pack, err error) diff --git a/server/kolide/scheduled_queries.go b/server/kolide/scheduled_queries.go index 7f44d2f8c6..2e838bdec5 100644 --- a/server/kolide/scheduled_queries.go +++ b/server/kolide/scheduled_queries.go @@ -18,6 +18,7 @@ type ScheduledQuery struct { ID uint `json:"id"` PackID uint `json:"pack_id" db:"pack_id"` Name string `json:"name"` + QueryID uint `json:"query_id" db:"query_id"` QueryName string `json:"query_name" db:"query_name"` Query string `json:"query"` // populated via a join on queries Description string `json:"description"` diff --git a/server/mock/datastore.go b/server/mock/datastore.go index 5712b5b671..28c65d6128 100644 --- a/server/mock/datastore.go +++ b/server/mock/datastore.go @@ -12,17 +12,18 @@ package mock //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" +//go:generate mockimpl -o datastore_campaigns.go "s *CampaignStore" "kolide.CampaignStore" import "github.com/kolide/fleet/server/kolide" var _ kolide.Datastore = (*Store)(nil) type Store struct { - kolide.CampaignStore kolide.SessionStore kolide.PasswordResetStore kolide.YARAStore kolide.TargetStore + CampaignStore ScheduledQueryStore OsqueryOptionsStore FileIntegrityMonitoringStore diff --git a/server/mock/datastore_campaigns.go b/server/mock/datastore_campaigns.go new file mode 100644 index 0000000000..2f83675423 --- /dev/null +++ b/server/mock/datastore_campaigns.go @@ -0,0 +1,83 @@ +// Automatically generated by mockimpl. DO NOT EDIT! + +package mock + +import ( + "time" + + "github.com/kolide/fleet/server/kolide" +) + +var _ kolide.CampaignStore = (*CampaignStore)(nil) + +type NewDistributedQueryCampaignFunc func(camp *kolide.DistributedQueryCampaign) (*kolide.DistributedQueryCampaign, error) + +type DistributedQueryCampaignFunc func(id uint) (*kolide.DistributedQueryCampaign, error) + +type SaveDistributedQueryCampaignFunc func(camp *kolide.DistributedQueryCampaign) error + +type DistributedQueryCampaignTargetIDsFunc func(id uint) (hostIDs []uint, labelIDs []uint, err error) + +type NewDistributedQueryCampaignTargetFunc func(target *kolide.DistributedQueryCampaignTarget) (*kolide.DistributedQueryCampaignTarget, error) + +type NewDistributedQueryExecutionFunc func(exec *kolide.DistributedQueryExecution) (*kolide.DistributedQueryExecution, error) + +type CleanupDistributedQueryCampaignsFunc func(now time.Time) (expired uint, deleted uint, err error) + +type CampaignStore struct { + NewDistributedQueryCampaignFunc NewDistributedQueryCampaignFunc + NewDistributedQueryCampaignFuncInvoked bool + + DistributedQueryCampaignFunc DistributedQueryCampaignFunc + DistributedQueryCampaignFuncInvoked bool + + SaveDistributedQueryCampaignFunc SaveDistributedQueryCampaignFunc + SaveDistributedQueryCampaignFuncInvoked bool + + DistributedQueryCampaignTargetIDsFunc DistributedQueryCampaignTargetIDsFunc + DistributedQueryCampaignTargetIDsFuncInvoked bool + + NewDistributedQueryCampaignTargetFunc NewDistributedQueryCampaignTargetFunc + NewDistributedQueryCampaignTargetFuncInvoked bool + + NewDistributedQueryExecutionFunc NewDistributedQueryExecutionFunc + NewDistributedQueryExecutionFuncInvoked bool + + CleanupDistributedQueryCampaignsFunc CleanupDistributedQueryCampaignsFunc + CleanupDistributedQueryCampaignsFuncInvoked bool +} + +func (s *CampaignStore) NewDistributedQueryCampaign(camp *kolide.DistributedQueryCampaign) (*kolide.DistributedQueryCampaign, error) { + s.NewDistributedQueryCampaignFuncInvoked = true + return s.NewDistributedQueryCampaignFunc(camp) +} + +func (s *CampaignStore) DistributedQueryCampaign(id uint) (*kolide.DistributedQueryCampaign, error) { + s.DistributedQueryCampaignFuncInvoked = true + return s.DistributedQueryCampaignFunc(id) +} + +func (s *CampaignStore) SaveDistributedQueryCampaign(camp *kolide.DistributedQueryCampaign) error { + s.SaveDistributedQueryCampaignFuncInvoked = true + return s.SaveDistributedQueryCampaignFunc(camp) +} + +func (s *CampaignStore) DistributedQueryCampaignTargetIDs(id uint) (hostIDs []uint, labelIDs []uint, err error) { + s.DistributedQueryCampaignTargetIDsFuncInvoked = true + return s.DistributedQueryCampaignTargetIDsFunc(id) +} + +func (s *CampaignStore) NewDistributedQueryCampaignTarget(target *kolide.DistributedQueryCampaignTarget) (*kolide.DistributedQueryCampaignTarget, error) { + s.NewDistributedQueryCampaignTargetFuncInvoked = true + return s.NewDistributedQueryCampaignTargetFunc(target) +} + +func (s *CampaignStore) NewDistributedQueryExecution(exec *kolide.DistributedQueryExecution) (*kolide.DistributedQueryExecution, error) { + s.NewDistributedQueryExecutionFuncInvoked = true + return s.NewDistributedQueryExecutionFunc(exec) +} + +func (s *CampaignStore) CleanupDistributedQueryCampaigns(now time.Time) (expired uint, deleted uint, err error) { + s.CleanupDistributedQueryCampaignsFuncInvoked = true + return s.CleanupDistributedQueryCampaignsFunc(now) +} diff --git a/server/mock/datastore_labels.go b/server/mock/datastore_labels.go index 0711fdb5a5..7fa3506c4c 100644 --- a/server/mock/datastore_labels.go +++ b/server/mock/datastore_labels.go @@ -10,7 +10,9 @@ import ( var _ kolide.LabelStore = (*LabelStore)(nil) -type NewLabelFunc func(Label *kolide.Label, opts ...kolide.OptionalArg) (*kolide.Label, error) +type ApplyLabelSpecsFunc func(specs []*kolide.LabelSpec) error + +type GetLabelSpecsFunc func() ([]*kolide.LabelSpec, error) type DeleteLabelFunc func(lid uint) error @@ -30,11 +32,12 @@ type ListUniqueHostsInLabelsFunc func(labels []uint) ([]kolide.Host, error) type SearchLabelsFunc func(query string, omit ...uint) ([]kolide.Label, error) -type SaveLabelFunc func(label *kolide.Label) (*kolide.Label, error) - type LabelStore struct { - NewLabelFunc NewLabelFunc - NewLabelFuncInvoked bool + ApplyLabelSpecsFunc ApplyLabelSpecsFunc + ApplyLabelSpecsFuncInvoked bool + + GetLabelSpecsFunc GetLabelSpecsFunc + GetLabelSpecsFuncInvoked bool DeleteLabelFunc DeleteLabelFunc DeleteLabelFuncInvoked bool @@ -62,14 +65,16 @@ type LabelStore struct { SearchLabelsFunc SearchLabelsFunc SearchLabelsFuncInvoked bool - - SaveLabelFunc SaveLabelFunc - SaveLabelFuncInvoked bool } -func (s *LabelStore) NewLabel(Label *kolide.Label, opts ...kolide.OptionalArg) (*kolide.Label, error) { - s.NewLabelFuncInvoked = true - return s.NewLabelFunc(Label, opts...) +func (s *LabelStore) ApplyLabelSpecs(specs []*kolide.LabelSpec) error { + s.ApplyLabelSpecsFuncInvoked = true + return s.ApplyLabelSpecsFunc(specs) +} + +func (s *LabelStore) GetLabelSpecs() ([]*kolide.LabelSpec, error) { + s.GetLabelSpecsFuncInvoked = true + return s.GetLabelSpecsFunc() } func (s *LabelStore) DeleteLabel(lid uint) error { @@ -116,8 +121,3 @@ func (s *LabelStore) SearchLabels(query string, omit ...uint) ([]kolide.Label, e s.SearchLabelsFuncInvoked = true return s.SearchLabelsFunc(query, omit...) } - -func (s *LabelStore) SaveLabel(label *kolide.Label) (*kolide.Label, error) { - s.SaveLabelFuncInvoked = true - return s.SaveLabelFunc(label) -} diff --git a/server/mock/datastore_packs.go b/server/mock/datastore_packs.go index 0248868837..bbca1cd2a0 100644 --- a/server/mock/datastore_packs.go +++ b/server/mock/datastore_packs.go @@ -10,10 +10,6 @@ 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 - type DeletePackFunc func(pid uint) error type PackFunc func(pid uint) (*kolide.Pack, error) @@ -22,15 +18,9 @@ type ListPacksFunc func(opt kolide.ListOptions) ([]*kolide.Pack, error) type PackByNameFunc func(name string, opts ...kolide.OptionalArg) (*kolide.Pack, bool, error) -type AddLabelToPackFunc func(lid uint, pid uint, opts ...kolide.OptionalArg) error - -type RemoveLabelFromPackFunc func(lid uint, pid uint) error - type ListLabelsForPackFunc func(pid uint) ([]*kolide.Label, error) -type AddHostToPackFunc func(hid uint, pid uint) error - -type RemoveHostFromPackFunc func(hid uint, pid uint) error +type ListPacksForHostFunc func(hid uint) (packs []*kolide.Pack, err error) type ListHostsInPackFunc func(pid uint, opt kolide.ListOptions) ([]uint, error) @@ -43,12 +33,6 @@ type PackStore struct { GetPackSpecsFunc GetPackSpecsFunc GetPackSpecsFuncInvoked bool - NewPackFunc NewPackFunc - NewPackFuncInvoked bool - - SavePackFunc SavePackFunc - SavePackFuncInvoked bool - DeletePackFunc DeletePackFunc DeletePackFuncInvoked bool @@ -61,20 +45,11 @@ type PackStore struct { PackByNameFunc PackByNameFunc PackByNameFuncInvoked bool - AddLabelToPackFunc AddLabelToPackFunc - AddLabelToPackFuncInvoked bool - - RemoveLabelFromPackFunc RemoveLabelFromPackFunc - RemoveLabelFromPackFuncInvoked bool - ListLabelsForPackFunc ListLabelsForPackFunc ListLabelsForPackFuncInvoked bool - AddHostToPackFunc AddHostToPackFunc - AddHostToPackFuncInvoked bool - - RemoveHostFromPackFunc RemoveHostFromPackFunc - RemoveHostFromPackFuncInvoked bool + ListPacksForHostFunc ListPacksForHostFunc + ListPacksForHostFuncInvoked bool ListHostsInPackFunc ListHostsInPackFunc ListHostsInPackFuncInvoked bool @@ -93,16 +68,6 @@ func (s *PackStore) GetPackSpecs() ([]*kolide.PackSpec, error) { return s.GetPackSpecsFunc() } -func (s *PackStore) NewPack(pack *kolide.Pack, opts ...kolide.OptionalArg) (*kolide.Pack, error) { - s.NewPackFuncInvoked = true - return s.NewPackFunc(pack, opts...) -} - -func (s *PackStore) SavePack(pack *kolide.Pack) error { - s.SavePackFuncInvoked = true - return s.SavePackFunc(pack) -} - func (s *PackStore) DeletePack(pid uint) error { s.DeletePackFuncInvoked = true return s.DeletePackFunc(pid) @@ -123,29 +88,14 @@ func (s *PackStore) PackByName(name string, opts ...kolide.OptionalArg) (*kolide return s.PackByNameFunc(name, opts...) } -func (s *PackStore) AddLabelToPack(lid uint, pid uint, opts ...kolide.OptionalArg) error { - s.AddLabelToPackFuncInvoked = true - return s.AddLabelToPackFunc(lid, pid, opts...) -} - -func (s *PackStore) RemoveLabelFromPack(lid uint, pid uint) error { - s.RemoveLabelFromPackFuncInvoked = true - return s.RemoveLabelFromPackFunc(lid, pid) -} - func (s *PackStore) ListLabelsForPack(pid uint) ([]*kolide.Label, error) { s.ListLabelsForPackFuncInvoked = true return s.ListLabelsForPackFunc(pid) } -func (s *PackStore) AddHostToPack(hid uint, pid uint) error { - s.AddHostToPackFuncInvoked = true - return s.AddHostToPackFunc(hid, pid) -} - -func (s *PackStore) RemoveHostFromPack(hid uint, pid uint) error { - s.RemoveHostFromPackFuncInvoked = true - return s.RemoveHostFromPackFunc(hid, pid) +func (s *PackStore) ListPacksForHost(hid uint) (packs []*kolide.Pack, err error) { + s.ListPacksForHostFuncInvoked = true + return s.ListPacksForHostFunc(hid) } func (s *PackStore) ListHostsInPack(pid uint, opt kolide.ListOptions) ([]uint, error) { diff --git a/server/service/endpoint_labels.go b/server/service/endpoint_labels.go index 5266630b02..7537739dd5 100644 --- a/server/service/endpoint_labels.go +++ b/server/service/endpoint_labels.go @@ -102,39 +102,6 @@ func makeListLabelsEndpoint(svc kolide.Service) endpoint.Endpoint { } } -//////////////////////////////////////////////////////////////////////////////// -// Create Label -//////////////////////////////////////////////////////////////////////////////// - -type createLabelRequest struct { - payload kolide.LabelPayload -} - -type createLabelResponse struct { - Label labelResponse `json:"label"` - Err error `json:"error,omitempty"` -} - -func (r createLabelResponse) error() error { return r.Err } - -func makeCreateLabelEndpoint(svc kolide.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(createLabelRequest) - - label, err := svc.NewLabel(ctx, req.payload) - if err != nil { - return createLabelResponse{Err: err}, nil - } - - labelResp, err := labelResponseForLabel(ctx, svc, label) - if err != nil { - return createLabelResponse{Err: err}, nil - } - - return createLabelResponse{Label: *labelResp}, nil - } -} - //////////////////////////////////////////////////////////////////////////////// // Delete Label //////////////////////////////////////////////////////////////////////////////// @@ -159,36 +126,3 @@ func makeDeleteLabelEndpoint(svc kolide.Service) endpoint.Endpoint { return deleteLabelResponse{}, nil } } - -//////////////////////////////////////////////////////////////////////////////// -// Modify Label -//////////////////////////////////////////////////////////////////////////////// - -type modifyLabelRequest struct { - ID uint - payload kolide.ModifyLabelPayload -} - -type modifyLabelResponse struct { - Label labelResponse `json:"label"` - Err error `json:"error,omitempty"` -} - -func (r modifyLabelResponse) error() error { return r.Err } - -func makeModifyLabelEndpoint(svc kolide.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(modifyLabelRequest) - label, err := svc.ModifyLabel(ctx, req.ID, req.payload) - if err != nil { - return modifyLabelResponse{Err: err}, nil - } - - labelResp, err := labelResponseForLabel(ctx, svc, label) - if err != nil { - return modifyLabelResponse{Err: err}, nil - } - - return modifyLabelResponse{Label: *labelResp}, err - } -} diff --git a/server/service/endpoint_packs.go b/server/service/endpoint_packs.go index 4e722239be..b87090be8b 100644 --- a/server/service/endpoint_packs.go +++ b/server/service/endpoint_packs.go @@ -125,75 +125,6 @@ func makeListPacksEndpoint(svc kolide.Service) endpoint.Endpoint { } } -//////////////////////////////////////////////////////////////////////////////// -// Create Pack -//////////////////////////////////////////////////////////////////////////////// - -type createPackRequest struct { - payload kolide.PackPayload -} - -type createPackResponse struct { - Pack packResponse `json:"pack,omitempty"` - Err error `json:"error,omitempty"` -} - -func (r createPackResponse) error() error { return r.Err } - -func makeCreatePackEndpoint(svc kolide.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(createPackRequest) - pack, err := svc.NewPack(ctx, req.payload) - if err != nil { - return createPackResponse{Err: err}, nil - } - - resp, err := packResponseForPack(ctx, svc, *pack) - if err != nil { - return createPackResponse{Err: err}, nil - } - - return createPackResponse{ - Pack: *resp, - }, nil - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Modify Pack -//////////////////////////////////////////////////////////////////////////////// - -type modifyPackRequest struct { - ID uint - payload kolide.PackPayload -} - -type modifyPackResponse struct { - Pack packResponse `json:"pack,omitempty"` - Err error `json:"error,omitempty"` -} - -func (r modifyPackResponse) error() error { return r.Err } - -func makeModifyPackEndpoint(svc kolide.Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - req := request.(modifyPackRequest) - pack, err := svc.ModifyPack(ctx, req.ID, req.payload) - if err != nil { - return modifyPackResponse{Err: err}, nil - } - - resp, err := packResponseForPack(ctx, svc, *pack) - if err != nil { - return modifyPackResponse{Err: err}, nil - } - - return modifyPackResponse{ - Pack: *resp, - }, nil - } -} - //////////////////////////////////////////////////////////////////////////////// // Delete Pack //////////////////////////////////////////////////////////////////////////////// diff --git a/server/service/handler.go b/server/service/handler.go index 08c0f94beb..8aea2aec70 100644 --- a/server/service/handler.go +++ b/server/service/handler.go @@ -48,8 +48,6 @@ type KolideEndpoints struct { CreateDistributedQueryCampaign endpoint.Endpoint GetPack endpoint.Endpoint ListPacks endpoint.Endpoint - CreatePack endpoint.Endpoint - ModifyPack endpoint.Endpoint DeletePack endpoint.Endpoint GetScheduledQueriesInPack endpoint.Endpoint EnrollAgent endpoint.Endpoint @@ -59,9 +57,7 @@ type KolideEndpoints struct { SubmitLogs endpoint.Endpoint GetLabel endpoint.Endpoint ListLabels endpoint.Endpoint - CreateLabel endpoint.Endpoint DeleteLabel endpoint.Endpoint - ModifyLabel endpoint.Endpoint ListDecorators endpoint.Endpoint NewDecorator endpoint.Endpoint ModifyDecorator endpoint.Endpoint @@ -132,8 +128,6 @@ func MakeKolideServerEndpoints(svc kolide.Service, jwtKey string) KolideEndpoint CreateDistributedQueryCampaign: authenticatedUser(jwtKey, svc, makeCreateDistributedQueryCampaignEndpoint(svc)), GetPack: authenticatedUser(jwtKey, svc, makeGetPackEndpoint(svc)), ListPacks: authenticatedUser(jwtKey, svc, makeListPacksEndpoint(svc)), - CreatePack: authenticatedUser(jwtKey, svc, makeCreatePackEndpoint(svc)), - ModifyPack: authenticatedUser(jwtKey, svc, makeModifyPackEndpoint(svc)), DeletePack: authenticatedUser(jwtKey, svc, makeDeletePackEndpoint(svc)), GetScheduledQueriesInPack: authenticatedUser(jwtKey, svc, makeGetScheduledQueriesInPackEndpoint(svc)), GetHost: authenticatedUser(jwtKey, svc, makeGetHostEndpoint(svc)), @@ -142,9 +136,7 @@ func MakeKolideServerEndpoints(svc kolide.Service, jwtKey string) KolideEndpoint DeleteHost: authenticatedUser(jwtKey, svc, makeDeleteHostEndpoint(svc)), GetLabel: authenticatedUser(jwtKey, svc, makeGetLabelEndpoint(svc)), ListLabels: authenticatedUser(jwtKey, svc, makeListLabelsEndpoint(svc)), - CreateLabel: authenticatedUser(jwtKey, svc, makeCreateLabelEndpoint(svc)), DeleteLabel: authenticatedUser(jwtKey, svc, makeDeleteLabelEndpoint(svc)), - ModifyLabel: authenticatedUser(jwtKey, svc, makeModifyLabelEndpoint(svc)), ListDecorators: authenticatedUser(jwtKey, svc, makeListDecoratorsEndpoint(svc)), NewDecorator: authenticatedUser(jwtKey, svc, makeNewDecoratorEndpoint(svc)), ModifyDecorator: authenticatedUser(jwtKey, svc, makeModifyDecoratorEndpoint(svc)), @@ -201,8 +193,6 @@ type kolideHandlers struct { CreateDistributedQueryCampaign http.Handler GetPack http.Handler ListPacks http.Handler - CreatePack http.Handler - ModifyPack http.Handler DeletePack http.Handler GetScheduledQueriesInPack http.Handler EnrollAgent http.Handler @@ -212,9 +202,7 @@ type kolideHandlers struct { SubmitLogs http.Handler GetLabel http.Handler ListLabels http.Handler - CreateLabel http.Handler DeleteLabel http.Handler - ModifyLabel http.Handler ListDecorators http.Handler NewDecorator http.Handler ModifyDecorator http.Handler @@ -274,8 +262,6 @@ func makeKolideKitHandlers(e KolideEndpoints, opts []kithttp.ServerOption) *koli CreateDistributedQueryCampaign: newServer(e.CreateDistributedQueryCampaign, decodeCreateDistributedQueryCampaignRequest), GetPack: newServer(e.GetPack, decodeGetPackRequest), ListPacks: newServer(e.ListPacks, decodeListPacksRequest), - CreatePack: newServer(e.CreatePack, decodeCreatePackRequest), - ModifyPack: newServer(e.ModifyPack, decodeModifyPackRequest), DeletePack: newServer(e.DeletePack, decodeDeletePackRequest), GetScheduledQueriesInPack: newServer(e.GetScheduledQueriesInPack, decodeGetScheduledQueriesInPackRequest), EnrollAgent: newServer(e.EnrollAgent, decodeEnrollAgentRequest), @@ -285,9 +271,7 @@ func makeKolideKitHandlers(e KolideEndpoints, opts []kithttp.ServerOption) *koli SubmitLogs: newServer(e.SubmitLogs, decodeSubmitLogsRequest), GetLabel: newServer(e.GetLabel, decodeGetLabelRequest), ListLabels: newServer(e.ListLabels, decodeListLabelsRequest), - CreateLabel: newServer(e.CreateLabel, decodeCreateLabelRequest), DeleteLabel: newServer(e.DeleteLabel, decodeDeleteLabelRequest), - ModifyLabel: newServer(e.ModifyLabel, decodeModifyLabelRequest), ListDecorators: newServer(e.ListDecorators, decodeNoParamsRequest), NewDecorator: newServer(e.NewDecorator, decodeNewDecoratorRequest), ModifyDecorator: newServer(e.ModifyDecorator, decodeModifyDecoratorRequest), @@ -392,15 +376,11 @@ func attachKolideAPIRoutes(r *mux.Router, h *kolideHandlers) { r.Handle("/api/v1/kolide/packs/{id}", h.GetPack).Methods("GET").Name("get_pack") r.Handle("/api/v1/kolide/packs", h.ListPacks).Methods("GET").Name("list_packs") - r.Handle("/api/v1/kolide/packs", h.CreatePack).Methods("POST").Name("create_pack") - 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/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") r.Handle("/api/v1/kolide/labels/{id}", h.DeleteLabel).Methods("DELETE").Name("delete_label") - r.Handle("/api/v1/kolide/labels/{id}", h.ModifyLabel).Methods("PATCH").Name("modify_label") r.Handle("/api/v1/kolide/decorators", h.ListDecorators).Methods("GET").Name("list_decorators") r.Handle("/api/v1/kolide/decorators", h.NewDecorator).Methods("POST").Name("create_decorator") diff --git a/server/service/logging_labels.go b/server/service/logging_labels.go index c91f28d505..bed5d189d3 100644 --- a/server/service/logging_labels.go +++ b/server/service/logging_labels.go @@ -7,24 +7,6 @@ import ( "github.com/kolide/fleet/server/kolide" ) -func (mw loggingMiddleware) ModifyLabel(ctx context.Context, id uint, p kolide.ModifyLabelPayload) (*kolide.Label, error) { - var ( - label *kolide.Label - err error - ) - - defer func(begin time.Time) { - mw.logger.Log( - "method", "ModifyLabel", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - label, err = mw.Service.ModifyLabel(ctx, id, p) - return label, err -} - func (mw loggingMiddleware) ListLabels(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Label, error) { var ( labels []*kolide.Label @@ -61,24 +43,6 @@ func (mw loggingMiddleware) GetLabel(ctx context.Context, id uint) (*kolide.Labe return label, err } -func (mw loggingMiddleware) NewLabel(ctx context.Context, p kolide.LabelPayload) (*kolide.Label, error) { - var ( - label *kolide.Label - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "NewLabel", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - label, err = mw.Service.NewLabel(ctx, p) - return label, err -} - func (mw loggingMiddleware) DeleteLabel(ctx context.Context, id uint) error { var ( err error diff --git a/server/service/logging_packs.go b/server/service/logging_packs.go index 46e54e1609..535fe59391 100644 --- a/server/service/logging_packs.go +++ b/server/service/logging_packs.go @@ -43,42 +43,6 @@ func (mw loggingMiddleware) GetPack(ctx context.Context, id uint) (*kolide.Pack, return pack, err } -func (mw loggingMiddleware) NewPack(ctx context.Context, p kolide.PackPayload) (*kolide.Pack, error) { - var ( - pack *kolide.Pack - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "NewPack", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - pack, err = mw.Service.NewPack(ctx, p) - return pack, err -} - -func (mw loggingMiddleware) ModifyPack(ctx context.Context, id uint, p kolide.PackPayload) (*kolide.Pack, error) { - var ( - pack *kolide.Pack - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "ModifyPack", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - pack, err = mw.Service.ModifyPack(ctx, id, p) - return pack, err -} - func (mw loggingMiddleware) DeletePack(ctx context.Context, id uint) error { var ( err error @@ -96,40 +60,6 @@ func (mw loggingMiddleware) DeletePack(ctx context.Context, id uint) error { return err } -func (mw loggingMiddleware) AddLabelToPack(ctx context.Context, lid uint, pid uint) error { - var ( - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "AddLabelToPack", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - err = mw.Service.AddLabelToPack(ctx, lid, pid) - return err -} - -func (mw loggingMiddleware) RemoveLabelFromPack(ctx context.Context, lid uint, pid uint) error { - var ( - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "RemoveLabelFromPack", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - err = mw.Service.RemoveLabelFromPack(ctx, lid, pid) - return err -} - func (mw loggingMiddleware) ListLabelsForPack(ctx context.Context, pid uint) ([]*kolide.Label, error) { var ( labels []*kolide.Label @@ -148,40 +78,6 @@ func (mw loggingMiddleware) ListLabelsForPack(ctx context.Context, pid uint) ([] return labels, err } -func (mw loggingMiddleware) AddHostToPack(ctx context.Context, hid uint, pid uint) error { - var ( - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "AddHostToPack", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - err = mw.Service.AddHostToPack(ctx, hid, pid) - return err -} - -func (mw loggingMiddleware) RemoveHostFromPack(ctx context.Context, hid uint, pid uint) error { - var ( - err error - ) - - defer func(begin time.Time) { - _ = mw.logger.Log( - "method", "RemoveHostFromPack", - "err", err, - "took", time.Since(begin), - ) - }(time.Now()) - - err = mw.Service.RemoveHostFromPack(ctx, hid, pid) - return err -} - func (mw loggingMiddleware) ListPacksForHost(ctx context.Context, hid uint) ([]*kolide.Pack, error) { var ( packs []*kolide.Pack diff --git a/server/service/metrics_labels.go b/server/service/metrics_labels.go deleted file mode 100644 index 6cfe31098b..0000000000 --- a/server/service/metrics_labels.go +++ /dev/null @@ -1,24 +0,0 @@ -package service - -import ( - "context" - "fmt" - "time" - - "github.com/kolide/fleet/server/kolide" -) - -func (mw metricsMiddleware) ModifyLabel(ctx context.Context, id uint, p kolide.ModifyLabelPayload) (*kolide.Label, error) { - var ( - lic *kolide.Label - err error - ) - defer func(begin time.Time) { - lvs := []string{"method", "ModifyLabel", "error", fmt.Sprint(err != nil)} - mw.requestCount.With(lvs...).Add(1) - mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds()) - }(time.Now()) - lic, err = mw.Service.ModifyLabel(ctx, id, p) - return lic, err - -} diff --git a/server/service/service_labels.go b/server/service/service_labels.go index 85c0cd3b78..ca16bc78cf 100644 --- a/server/service/service_labels.go +++ b/server/service/service_labels.go @@ -6,6 +6,14 @@ import ( "github.com/kolide/fleet/server/kolide" ) +func (svc service) ApplyLabelSpecs(ctx context.Context, specs []*kolide.LabelSpec) error { + return svc.ds.ApplyLabelSpecs(specs) +} + +func (svc service) GetLabelSpecs(ctx context.Context) ([]*kolide.LabelSpec, error) { + return svc.ds.GetLabelSpecs() +} + func (svc service) ListLabels(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Label, error) { return svc.ds.ListLabels(opt) } @@ -14,34 +22,6 @@ func (svc service) GetLabel(ctx context.Context, id uint) (*kolide.Label, error) return svc.ds.Label(id) } -func (svc service) NewLabel(ctx context.Context, p kolide.LabelPayload) (*kolide.Label, error) { - label := &kolide.Label{} - - if p.Name == nil { - return nil, newInvalidArgumentError("name", "missing required argument") - } - label.Name = *p.Name - - if p.Query == nil { - return nil, newInvalidArgumentError("query", "missing required argument") - } - label.Query = *p.Query - - if p.Platform != nil { - label.Platform = *p.Platform - } - - if p.Description != nil { - label.Description = *p.Description - } - - label, err := svc.ds.NewLabel(label) - if err != nil { - return nil, err - } - return label, nil -} - func (svc service) DeleteLabel(ctx context.Context, id uint) error { return svc.ds.DeleteLabel(id) } @@ -57,17 +37,3 @@ func (svc service) HostIDsForLabel(lid uint) ([]uint, error) { } return ids, nil } - -func (svc service) ModifyLabel(ctx context.Context, id uint, payload kolide.ModifyLabelPayload) (*kolide.Label, error) { - label, err := svc.ds.Label(id) - if err != nil { - return nil, err - } - if payload.Name != nil { - label.Name = *payload.Name - } - if payload.Description != nil { - label.Description = *payload.Description - } - return svc.ds.SaveLabel(label) -} diff --git a/server/service/service_labels_test.go b/server/service/service_labels_test.go index b99d4d2f74..a25488e909 100644 --- a/server/service/service_labels_test.go +++ b/server/service/service_labels_test.go @@ -7,62 +7,9 @@ import ( "github.com/kolide/fleet/server/config" "github.com/kolide/fleet/server/datastore/inmem" "github.com/kolide/fleet/server/kolide" - "github.com/kolide/fleet/server/mock" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -func TestModifyLabel(t *testing.T) { - ds := new(mock.Store) - ds.LabelFunc = func(id uint) (*kolide.Label, error) { - l := &kolide.Label{ - ID: id, - Name: "name", - Description: "desc", - } - return l, nil - } - ds.SaveLabelFunc = func(l *kolide.Label) (*kolide.Label, error) { - return l, nil - } - svc, err := newTestService(ds, nil) - require.Nil(t, err) - lp := kolide.ModifyLabelPayload{ - Name: stringPtr("new name"), - Description: stringPtr("new desc"), - } - l, err := svc.ModifyLabel(context.Background(), uint(1), lp) - assert.Equal(t, "new name", l.Name) - assert.Equal(t, "new desc", l.Description) - assert.True(t, ds.LabelFuncInvoked) - assert.True(t, ds.SaveLabelFuncInvoked) -} - -func TestListLabels(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() - - labels, err := svc.ListLabels(ctx, kolide.ListOptions{}) - assert.Nil(t, err) - assert.Len(t, labels, 0) - - _, err = ds.NewLabel(&kolide.Label{ - Name: "foo", - Query: "select * from foo;", - }) - assert.Nil(t, err) - - labels, err = svc.ListLabels(ctx, kolide.ListOptions{}) - assert.Nil(t, err) - assert.Len(t, labels, 1) - assert.Equal(t, "foo", labels[0].Name) -} - func TestGetLabel(t *testing.T) { ds, err := inmem.New(config.TestConfig()) assert.Nil(t, err) @@ -85,31 +32,6 @@ func TestGetLabel(t *testing.T) { assert.Equal(t, label.ID, labelVerify.ID) } -func TestNewLabel(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() - - name := "foo" - query := "select * from foo;" - label, err := svc.NewLabel(ctx, kolide.LabelPayload{ - Name: &name, - Query: &query, - }) - assert.NotZero(t, label.ID) - - assert.Nil(t, err) - - labels, err := ds.ListLabels(kolide.ListOptions{}) - assert.Nil(t, err) - assert.Len(t, labels, 1) - assert.Equal(t, "foo", labels[0].Name) -} - func TestDeleteLabel(t *testing.T) { ds, err := inmem.New(config.TestConfig()) assert.Nil(t, err) diff --git a/server/service/service_osquery.go b/server/service/service_osquery.go index 6184689b90..156e16a677 100644 --- a/server/service/service_osquery.go +++ b/server/service/service_osquery.go @@ -136,7 +136,7 @@ func (svc service) GetClientConfig(ctx context.Context) (map[string]interface{}, config["decorators"] = json.RawMessage(decJSON) } - packs, err := svc.ListPacksForHost(ctx, host.ID) + packs, err := svc.ds.ListPacksForHost(host.ID) if err != nil { return nil, osqueryError{message: "database error: " + err.Error()} } diff --git a/server/service/service_osquery_test.go b/server/service/service_osquery_test.go index 4c2082fbaf..bf97fbaec7 100644 --- a/server/service/service_osquery_test.go +++ b/server/service/service_osquery_test.go @@ -219,19 +219,33 @@ func TestHostDetailQueries(t *testing.T) { } } +func TestGetDistributedQueriesMissingHost(t *testing.T) { + svc, err := newTestService(&mock.Store{}, nil) + require.Nil(t, err) + + _, _, err = svc.GetDistributedQueries(context.Background()) + require.NotNil(t, err) + assert.Contains(t, err.Error(), "missing host") +} + func TestLabelQueries(t *testing.T) { - ds, svc, mockClock := setupOsqueryTests(t) - ctx := context.Background() - - _, err := svc.EnrollAgent(ctx, "", "host123") + mockClock := clock.NewMockClock() + ds := new(mock.Store) + svc, err := newTestServiceWithClock(ds, nil, mockClock) require.Nil(t, err) - hosts, err := ds.ListHosts(kolide.ListOptions{}) - require.Nil(t, err) - require.Len(t, hosts, 1) - host := hosts[0] + ds.LabelQueriesForHostFunc = func(host *kolide.Host, cutoff time.Time) (map[string]string, error) { + return map[string]string{}, nil + } + ds.DistributedQueriesForHostFunc = func(host *kolide.Host) (map[uint]string, error) { + return map[uint]string{}, nil + } + ds.SaveHostFunc = func(host *kolide.Host) error { + return nil + } - ctx = hostctx.NewContext(ctx, *host) + host := &kolide.Host{} + ctx := hostctx.NewContext(context.Background(), *host) // With a new host, we should get the detail queries (and accelerate // should be turned on so that we can quickly fill labels) @@ -244,7 +258,6 @@ func TestLabelQueries(t *testing.T) { host.DetailUpdateTime = mockClock.Now().Add(-1 * time.Minute) host.Platform = "darwin" host.HostName = "zwass.local" - ds.SaveHost(host) ctx = hostctx.NewContext(ctx, *host) queries, acc, err = svc.GetDistributedQueries(ctx) @@ -252,32 +265,12 @@ func TestLabelQueries(t *testing.T) { assert.Len(t, queries, 0) assert.Zero(t, acc) - labels := []kolide.Label{ - kolide.Label{ - Name: "label1", - Query: "query1", - Platform: "darwin", - }, - kolide.Label{ - Name: "label2", - Query: "query2", - Platform: "darwin", - }, - kolide.Label{ - Name: "label3", - Query: "query3", - Platform: "darwin,linux", - }, - kolide.Label{ - Name: "label4", - Query: "query4", - Platform: "linux", - }, - } - - for _, label := range labels { - _, err := ds.NewLabel(&label) - assert.Nil(t, err) + ds.LabelQueriesForHostFunc = func(host *kolide.Host, cutoff time.Time) (map[string]string, error) { + return map[string]string{ + "label1": "query1", + "label2": "query2", + "label3": "query3", + }, nil } // Now we should get the label queries @@ -286,6 +279,16 @@ func TestLabelQueries(t *testing.T) { assert.Len(t, queries, 3) assert.Zero(t, acc) + var gotHost *kolide.Host + var gotResults map[uint]bool + var gotTime time.Time + ds.RecordLabelQueryExecutionsFunc = func(host *kolide.Host, results map[uint]bool, t time.Time) error { + gotHost = host + gotResults = results + gotTime = t + return nil + } + // Record a query execution err = svc.SubmitDistributedQueryResults( ctx, @@ -295,32 +298,13 @@ func TestLabelQueries(t *testing.T) { map[string]string{}, ) assert.Nil(t, err) + assert.Equal(t, host, gotHost) + assert.Equal(t, mockClock.Now(), gotTime) + if assert.Len(t, gotResults, 1) { + assert.Equal(t, true, gotResults[1]) + } - // Verify that labels are set appropriately - hostLabels, err := ds.ListLabelsForHost(host.ID) - assert.Len(t, hostLabels, 1) - assert.Equal(t, "label1", hostLabels[0].Name) - - // Now that query should not be returned - queries, acc, err = svc.GetDistributedQueries(ctx) - assert.Nil(t, err) - assert.Len(t, queries, 2) - assert.NotContains(t, queries, "kolide_label_query_1") - assert.Zero(t, acc) - - // Advance the time - mockClock.AddTime(1*time.Hour + 1*time.Minute) - - // Keep the host details fresh - host.DetailUpdateTime = mockClock.Now().Add(-1 * time.Minute) - ds.SaveHost(host) - ctx = hostctx.NewContext(ctx, *host) - - // Now we should get all the label queries again - queries, acc, err = svc.GetDistributedQueries(ctx) - assert.Nil(t, err) - assert.Len(t, queries, 3) - assert.Zero(t, acc) + mockClock.AddTime(1 * time.Second) // Record a query execution err = svc.SubmitDistributedQueryResults( @@ -332,20 +316,11 @@ func TestLabelQueries(t *testing.T) { map[string]string{}, ) assert.Nil(t, err) - - // Now these should no longer show up in the necessary to run queries - queries, acc, err = svc.GetDistributedQueries(ctx) - assert.Nil(t, err) - assert.Len(t, queries, 1) - assert.Zero(t, acc) - - // Verify that labels are set appropriately - hostLabels, err = ds.ListLabelsForHost(host.ID) - assert.Len(t, hostLabels, 2) - expectLabelNames := map[string]bool{"label1": true, "label2": true} - for _, label := range hostLabels { - assert.Contains(t, expectLabelNames, label.Name) - delete(expectLabelNames, label.Name) + assert.Equal(t, host, gotHost) + assert.Equal(t, mockClock.Now(), gotTime) + if assert.Len(t, gotResults, 2) { + assert.Equal(t, true, gotResults[2]) + assert.Equal(t, false, gotResults[3]) } } @@ -354,32 +329,9 @@ func TestGetClientConfig(t *testing.T) { ds.ListDecoratorsFunc = func(opt ...kolide.OptionalArg) ([]*kolide.Decorator, error) { return []*kolide.Decorator{}, nil } - ds.ListPacksFunc = func(opt kolide.ListOptions) ([]*kolide.Pack, error) { + ds.ListPacksForHostFunc = func(hid uint) ([]*kolide.Pack, error) { return []*kolide.Pack{}, nil } - ds.ListLabelsForHostFunc = func(hid uint) ([]kolide.Label, error) { - return []kolide.Label{ - {ID: 1, Name: "foo_label"}, - }, nil - } - ds.ListLabelsForPackFunc = func(pid uint) ([]*kolide.Label, error) { - switch pid { - case 1, 2: - return []*kolide.Label{ - {ID: 1, Name: "foo_label"}, - }, nil - default: - return []*kolide.Label{}, nil - } - } - ds.ListExplicitHostsInPackFunc = func(pid uint, opt kolide.ListOptions) ([]uint, error) { - switch pid { - case 4: - return []uint{1}, nil - default: - return []uint{}, nil - } - } ds.ListScheduledQueriesInPackFunc = func(pid uint, opt kolide.ListOptions) ([]*kolide.ScheduledQuery, error) { tru := true fals := false @@ -411,7 +363,8 @@ func TestGetClientConfig(t *testing.T) { svc, err := newTestService(ds, nil) require.Nil(t, err) - ctx := hostctx.NewContext(context.Background(), kolide.Host{ID: 1}) + ctx1 := hostctx.NewContext(context.Background(), kolide.Host{ID: 1}) + ctx2 := hostctx.NewContext(context.Background(), kolide.Host{ID: 2}) expectedOptions := map[string]interface{}{ "distributed_interval": float64(11), @@ -419,25 +372,36 @@ func TestGetClientConfig(t *testing.T) { } // No packs loaded yet - conf, err := svc.GetClientConfig(ctx) + conf, err := svc.GetClientConfig(ctx1) + require.Nil(t, err) + assert.Equal(t, map[string]interface{}{"options": expectedOptions}, conf) + + conf, err = svc.GetClientConfig(ctx2) require.Nil(t, err) assert.Equal(t, map[string]interface{}{"options": expectedOptions}, conf) // Now add packs - ds.ListPacksFunc = func(opt kolide.ListOptions) ([]*kolide.Pack, error) { - return []*kolide.Pack{ - {ID: 1, Name: "pack_by_label"}, - {ID: 2, Name: "disabled_pack", Disabled: true}, - {ID: 3, Name: "not_matching_pack"}, - {ID: 4, Name: "pack_by_explicit_host"}, - }, nil + ds.ListPacksForHostFunc = func(hid uint) ([]*kolide.Pack, error) { + switch hid { + case 1: + return []*kolide.Pack{ + {ID: 1, Name: "pack_by_label"}, + {ID: 4, Name: "pack_by_other_label"}, + }, nil + + case 2: + return []*kolide.Pack{ + {ID: 1, Name: "pack_by_label"}, + }, nil + } + return []*kolide.Pack{}, nil } - conf, err = svc.GetClientConfig(ctx) + conf, err = svc.GetClientConfig(ctx1) require.Nil(t, err) assert.Equal(t, expectedOptions, conf["options"]) assert.JSONEq(t, `{ - "pack_by_explicit_host": { + "pack_by_other_label": { "queries": { "foobar":{"query":"select 3","interval":20,"shard":42}, "froobing":{"query":"select 'guacamole'","interval":60,"snapshot":true} @@ -451,6 +415,19 @@ func TestGetClientConfig(t *testing.T) { }`, string(conf["packs"].(json.RawMessage)), ) + + conf, err = svc.GetClientConfig(ctx2) + require.Nil(t, err) + assert.Equal(t, expectedOptions, conf["options"]) + assert.JSONEq(t, `{ + "pack_by_label": { + "queries":{ + "time":{"query":"select * from time","interval":30,"removed":false} + } + } + }`, + string(conf["packs"].(json.RawMessage)), + ) } func TestDetailQueriesWithEmptyStrings(t *testing.T) { @@ -785,71 +762,97 @@ func TestDetailQueries(t *testing.T) { assert.Zero(t, acc) } -func TestDistributedQueries(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - require.Nil(t, err) - - _, err = ds.NewAppConfig(&kolide.AppConfig{EnrollSecret: ""}) - require.Nil(t, err) - +func TestNewDistributedQueryCampaign(t *testing.T) { mockClock := clock.NewMockClock() - - rs := pubsub.NewInmemQueryResults() - - svc, err := newTestServiceWithClock(ds, rs, mockClock) + ds := new(mock.Store) + svc, err := newTestServiceWithClock(ds, nil, mockClock) require.Nil(t, err) - ctx := context.Background() + ds.LabelQueriesForHostFunc = func(host *kolide.Host, cutoff time.Time) (map[string]string, error) { + return map[string]string{}, nil + } + ds.DistributedQueriesForHostFunc = func(host *kolide.Host) (map[uint]string, error) { + return map[uint]string{}, nil + } + ds.SaveHostFunc = func(host *kolide.Host) error { + return nil + } + var gotQuery *kolide.Query + ds.NewQueryFunc = func(query *kolide.Query, opts ...kolide.OptionalArg) (*kolide.Query, error) { + gotQuery = query + query.ID = 42 + return query, nil + } + var gotCampaign *kolide.DistributedQueryCampaign + ds.NewDistributedQueryCampaignFunc = func(camp *kolide.DistributedQueryCampaign) (*kolide.DistributedQueryCampaign, error) { + gotCampaign = camp + camp.ID = 21 + return camp, nil + } + var gotTargets []*kolide.DistributedQueryCampaignTarget + ds.NewDistributedQueryCampaignTargetFunc = func(target *kolide.DistributedQueryCampaignTarget) (*kolide.DistributedQueryCampaignTarget, error) { + gotTargets = append(gotTargets, target) + return target, nil + } - nodeKey, err := svc.EnrollAgent(ctx, "", "host123") - require.Nil(t, err) - - host, err := ds.AuthenticateHost(nodeKey) - require.Nil(t, err) - - host.Platform = "centos" - host.HostName = "zwass.local" - require.Nil(t, ds.SaveHost(host)) - - // Create label - n := "foo" - q := "select * from foo;" - label, err := svc.NewLabel(ctx, kolide.LabelPayload{ - Name: &n, - Query: &q, - }) - require.Nil(t, err) - - // Record match with label - ctx = viewer.NewContext(ctx, viewer.Viewer{ + viewerCtx := viewer.NewContext(context.Background(), viewer.Viewer{ User: &kolide.User{ ID: 0, }, }) - err = ds.RecordLabelQueryExecutions(host, map[uint]bool{label.ID: true}, mockClock.Now()) + q := "select year, month, day, hour, minutes, seconds from time" + campaign, err := svc.NewDistributedQueryCampaign(viewerCtx, q, []uint{2}, []uint{1}) require.Nil(t, err) - err = ds.MarkHostSeen(host, mockClock.Now()) - require.Nil(t, err) - ctx = hostctx.NewContext(ctx, *host) + assert.Equal(t, gotQuery.ID, gotCampaign.QueryID) + assert.Equal(t, []*kolide.DistributedQueryCampaignTarget{ + &kolide.DistributedQueryCampaignTarget{ + Type: kolide.TargetHost, + DistributedQueryCampaignID: campaign.ID, + TargetID: 2, + }, + &kolide.DistributedQueryCampaignTarget{ + Type: kolide.TargetLabel, + DistributedQueryCampaignID: campaign.ID, + TargetID: 1, + }, + }, gotTargets, + ) +} - q = "select year, month, day, hour, minutes, seconds from time" - campaign, err := svc.NewDistributedQueryCampaign(ctx, q, []uint{}, []uint{label.ID}) +func TestDistributedQueryResults(t *testing.T) { + mockClock := clock.NewMockClock() + ds := new(mock.Store) + rs := pubsub.NewInmemQueryResults() + svc, err := newTestServiceWithClock(ds, rs, mockClock) require.Nil(t, err) - // Manually set the campaign to running (so that it shows up when - // requesting queries) - campaign.Status = kolide.QueryRunning - err = ds.SaveDistributedQueryCampaign(campaign) - require.Nil(t, err) + campaign := &kolide.DistributedQueryCampaign{ID: 42} - queryKey := fmt.Sprintf("%s%d", hostDistributedQueryPrefix, campaign.ID) + ds.LabelQueriesForHostFunc = func(host *kolide.Host, cutoff time.Time) (map[string]string, error) { + return map[string]string{}, nil + } + ds.SaveHostFunc = func(host *kolide.Host) error { + return nil + } + ds.DistributedQueriesForHostFunc = func(host *kolide.Host) (map[uint]string, error) { + return map[uint]string{campaign.ID: "select * from time"}, nil + } + var gotExecution *kolide.DistributedQueryExecution + ds.NewDistributedQueryExecutionFunc = func(exec *kolide.DistributedQueryExecution) (*kolide.DistributedQueryExecution, error) { + gotExecution = exec + return exec, nil + } + + host := &kolide.Host{ID: 1} + hostCtx := hostctx.NewContext(context.Background(), *host) // Now we should get the active distributed query - queries, acc, err := svc.GetDistributedQueries(ctx) + queries, acc, err := svc.GetDistributedQueries(hostCtx) require.Nil(t, err) assert.Len(t, queries, len(detailQueries)+1) - assert.Equal(t, q, queries[queryKey]) - assert.Zero(t, acc) + queryKey := fmt.Sprintf("%s%d", hostDistributedQueryPrefix, campaign.ID) + assert.Equal(t, "select * from time", queries[queryKey]) + assert.NotZero(t, acc) expectedRows := []map[string]string{ { @@ -866,7 +869,7 @@ func TestDistributedQueries(t *testing.T) { } // TODO use service method - readChan, err := rs.ReadChannel(ctx, *campaign) + readChan, err := rs.ReadChannel(context.Background(), *campaign) require.Nil(t, err) // We need to listen for the result in a separate thread to prevent the @@ -901,17 +904,11 @@ func TestDistributedQueries(t *testing.T) { // this test. time.Sleep(10 * time.Millisecond) - err = svc.SubmitDistributedQueryResults(ctx, results, map[string]string{}) + err = svc.SubmitDistributedQueryResults(hostCtx, results, map[string]string{}) require.Nil(t, err) - - // Now the distributed query should be completed and not returned - queries, acc, err = svc.GetDistributedQueries(ctx) - require.Nil(t, err) - assert.Len(t, queries, len(detailQueries)) - assert.NotContains(t, queries, queryKey) - assert.Zero(t, acc) - - waitComplete.Wait() + assert.Equal(t, campaign.ID, gotExecution.DistributedQueryCampaignID) + assert.Equal(t, host.ID, gotExecution.HostID) + assert.Equal(t, kolide.ExecutionSucceeded, gotExecution.Status) } func TestOrphanedQueryCampaign(t *testing.T) { @@ -983,12 +980,9 @@ func TestUpdateHostIntervals(t *testing.T) { ds.ListDecoratorsFunc = func(opt ...kolide.OptionalArg) ([]*kolide.Decorator, error) { return []*kolide.Decorator{}, nil } - ds.ListPacksFunc = func(opt kolide.ListOptions) ([]*kolide.Pack, error) { + ds.ListPacksForHostFunc = func(hid uint) ([]*kolide.Pack, error) { return []*kolide.Pack{}, nil } - ds.ListLabelsForHostFunc = func(hid uint) ([]kolide.Label, error) { - return []kolide.Label{}, nil - } var testCases = []struct { initHost kolide.Host diff --git a/server/service/service_packs.go b/server/service/service_packs.go index 2c6ad497f7..5f7b36a437 100644 --- a/server/service/service_packs.go +++ b/server/service/service_packs.go @@ -6,6 +6,14 @@ import ( "github.com/kolide/fleet/server/kolide" ) +func (svc service) ApplyPackSpecs(ctx context.Context, specs []*kolide.PackSpec) error { + return svc.ds.ApplyPackSpecs(specs) +} + +func (svc service) GetPackSpecs(ctx context.Context) ([]*kolide.PackSpec, error) { + return svc.ds.GetPackSpecs() +} + func (svc service) ListPacks(ctx context.Context, opt kolide.ListOptions) ([]*kolide.Pack, error) { return svc.ds.ListPacks(opt) } @@ -14,201 +22,14 @@ func (svc service) GetPack(ctx context.Context, id uint) (*kolide.Pack, error) { return svc.ds.Pack(id) } -func (svc service) NewPack(ctx context.Context, p kolide.PackPayload) (*kolide.Pack, error) { - var pack kolide.Pack - - if p.Name != nil { - pack.Name = *p.Name - } - - if p.Description != nil { - pack.Description = *p.Description - } - - if p.Platform != nil { - pack.Platform = *p.Platform - } - - if p.Disabled != nil { - pack.Disabled = *p.Disabled - } - - _, err := svc.ds.NewPack(&pack) - if err != nil { - return nil, err - } - - if p.HostIDs != nil { - for _, hostID := range *p.HostIDs { - err = svc.AddHostToPack(ctx, hostID, pack.ID) - if err != nil { - return nil, err - } - } - } - - if p.LabelIDs != nil { - for _, labelID := range *p.LabelIDs { - err = svc.AddLabelToPack(ctx, labelID, pack.ID) - if err != nil { - return nil, err - } - } - } - - return &pack, nil -} - -func (svc service) ModifyPack(ctx context.Context, id uint, p kolide.PackPayload) (*kolide.Pack, error) { - pack, err := svc.ds.Pack(id) - if err != nil { - return nil, err - } - - if p.Name != nil { - pack.Name = *p.Name - } - - if p.Description != nil { - pack.Description = *p.Description - } - - if p.Platform != nil { - pack.Platform = *p.Platform - } - - if p.Disabled != nil { - pack.Disabled = *p.Disabled - } - - err = svc.ds.SavePack(pack) - if err != nil { - return nil, err - } - - // we must determine what hosts are attached to this pack. then, given - // our new set of host_ids, we will mutate the database to reflect the - // desired state. - if p.HostIDs != nil { - - // first, let's retrieve the total set of hosts - hosts, err := svc.ListHostsInPack(ctx, pack.ID, kolide.ListOptions{}) - if err != nil { - return nil, err - } - - // it will be efficient to create a data structure with constant time - // lookups to determine whether or not a host is already added - existingHosts := map[uint]bool{} - for _, host := range hosts { - existingHosts[host] = true - } - - // we will also make a constant time lookup map for the desired set of - // hosts as well. - desiredHosts := map[uint]bool{} - for _, hostID := range *p.HostIDs { - desiredHosts[hostID] = true - } - - // if the request declares a host ID but the host is not already - // associated with the pack, we add it - for _, hostID := range *p.HostIDs { - if !existingHosts[hostID] { - err = svc.AddHostToPack(ctx, hostID, pack.ID) - if err != nil { - return nil, err - } - } - } - - // if the request does not declare the ID of a host which currently - // exists, we delete the existing relationship - for hostID := range existingHosts { - if !desiredHosts[hostID] { - err = svc.RemoveHostFromPack(ctx, hostID, pack.ID) - if err != nil { - return nil, err - } - } - } - } - - // we must determine what labels are attached to this pack. then, given - // our new set of label_ids, we will mutate the database to reflect the - // desired state. - if p.LabelIDs != nil { - - // first, let's retrieve the total set of labels - labels, err := svc.ListLabelsForPack(ctx, pack.ID) - if err != nil { - return nil, err - } - - // it will be efficient to create a data structure with constant time - // lookups to determine whether or not a label is already added - existingLabels := map[uint]bool{} - for _, label := range labels { - existingLabels[label.ID] = true - } - - // we will also make a constant time lookup map for the desired set of - // labels as well. - desiredLabels := map[uint]bool{} - for _, labelID := range *p.LabelIDs { - desiredLabels[labelID] = true - } - - // if the request declares a label ID but the label is not already - // associated with the pack, we add it - for _, labelID := range *p.LabelIDs { - if !existingLabels[labelID] { - err = svc.AddLabelToPack(ctx, labelID, pack.ID) - if err != nil { - return nil, err - } - } - } - - // if the request does not declare the ID of a label which currently - // exists, we delete the existing relationship - for labelID := range existingLabels { - if !desiredLabels[labelID] { - err = svc.RemoveLabelFromPack(ctx, labelID, pack.ID) - if err != nil { - return nil, err - } - } - } - } - - return pack, err -} - func (svc service) DeletePack(ctx context.Context, id uint) error { return svc.ds.DeletePack(id) } -func (svc service) AddLabelToPack(ctx context.Context, lid, pid uint) error { - return svc.ds.AddLabelToPack(lid, pid) -} - func (svc service) ListLabelsForPack(ctx context.Context, pid uint) ([]*kolide.Label, error) { return svc.ds.ListLabelsForPack(pid) } -func (svc service) RemoveLabelFromPack(ctx context.Context, lid, pid uint) error { - return svc.ds.RemoveLabelFromPack(lid, pid) -} - -func (svc service) AddHostToPack(ctx context.Context, hid, pid uint) error { - return svc.ds.AddHostToPack(hid, pid) -} - -func (svc service) RemoveHostFromPack(ctx context.Context, hid, pid uint) error { - return svc.ds.RemoveHostFromPack(hid, pid) -} - func (svc service) ListHostsInPack(ctx context.Context, pid uint, opt kolide.ListOptions) ([]uint, error) { return svc.ds.ListHostsInPack(pid, opt) } @@ -218,65 +39,5 @@ func (svc service) ListExplicitHostsInPack(ctx context.Context, pid uint, opt ko } func (svc service) ListPacksForHost(ctx context.Context, hid uint) ([]*kolide.Pack, error) { - packs := []*kolide.Pack{} - - // we will need to give some subset of packs to this host based on the - // labels which this host is known to belong to - allPacks, err := svc.ds.ListPacks(kolide.ListOptions{}) - if err != nil { - return nil, err - } - - // pull the labels that this host belongs to - labels, err := svc.ds.ListLabelsForHost(hid) - if err != nil { - return nil, err - } - - // in order to use o(1) array indexing in an o(n) loop vs a o(n^2) double - // for loop iteration, we must create the array which may be indexed below - labelIDs := map[uint]bool{} - for _, label := range labels { - labelIDs[label.ID] = true - } - - for _, pack := range allPacks { - // don't include packs which have been disabled - if pack.Disabled { - continue - } - - // for each pack, we must know what labels have been assigned to that - // pack - labelsForPack, err := svc.ds.ListLabelsForPack(pack.ID) - if err != nil { - return nil, err - } - - // o(n) iteration to determine whether or not a pack is enabled - // in this case, n is len(labelsForPack) - for _, label := range labelsForPack { - if labelIDs[label.ID] { - packs = append(packs, pack) - break - } - } - - // for each pack, we must know what host have been assigned to that pack - hostsForPack, err := svc.ds.ListExplicitHostsInPack(pack.ID, kolide.ListOptions{}) - if err != nil { - return nil, err - } - - // o(n) iteration to determine whether or not a pack is enabled - // in this case, n is len(hostsForPack) - for _, host := range hostsForPack { - if host == hid { - packs = append(packs, pack) - break - } - } - } - - return packs, nil + return svc.ds.ListPacksForHost(hid) } diff --git a/server/service/service_packs_test.go b/server/service/service_packs_test.go index ec5ae1e581..fe6352bbfa 100644 --- a/server/service/service_packs_test.go +++ b/server/service/service_packs_test.go @@ -4,13 +4,10 @@ import ( "context" "testing" - "github.com/WatchBeam/clock" "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 TestListPacks(t *testing.T) { @@ -58,95 +55,6 @@ func TestGetPack(t *testing.T) { assert.Equal(t, pack.ID, packVerify.ID) } -func TestNewPack(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() - - labelName := "label" - labelQuery := "select 1" - label, err := svc.NewLabel(ctx, kolide.LabelPayload{ - Name: &labelName, - Query: &labelQuery, - }) - - packName := "foo" - packLabelIDs := []uint{label.ID} - pack, err := svc.NewPack(ctx, kolide.PackPayload{ - Name: &packName, - LabelIDs: &packLabelIDs, - }) - - assert.Nil(t, err) - - packs, err := ds.ListPacks(kolide.ListOptions{}) - assert.Nil(t, err) - require.Len(t, packs, 1) - assert.Equal(t, pack.ID, packs[0].ID) - - labels, err := ds.ListLabelsForPack(pack.ID) - assert.Nil(t, err) - require.Len(t, labels, 1) - assert.Equal(t, label.ID, labels[0].ID) -} - -func TestModifyPack(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() - - label := &kolide.Label{ - Name: "label", - Query: "select 1", - } - label, err = ds.NewLabel(label) - assert.Nil(t, err) - assert.NotZero(t, label.ID) - - pack := &kolide.Pack{ - Name: "foo", - } - pack, err = ds.NewPack(pack) - assert.Nil(t, err) - assert.NotZero(t, pack.ID) - - newName := "bar" - labelIDs := []uint{label.ID} - packVerify, err := svc.ModifyPack(ctx, pack.ID, kolide.PackPayload{ - Name: &newName, - LabelIDs: &labelIDs, - }) - assert.Nil(t, err) - - assert.Equal(t, pack.ID, packVerify.ID) - assert.Equal(t, "bar", packVerify.Name) - - labels, err := ds.ListLabelsForPack(pack.ID) - assert.Nil(t, err) - require.Len(t, labels, 1) - assert.Equal(t, label.ID, labels[0].ID) - - newLabelIDs := []uint{} - packVerify2, err := svc.ModifyPack(ctx, pack.ID, kolide.PackPayload{ - LabelIDs: &newLabelIDs, - }) - assert.Nil(t, err) - - assert.Equal(t, pack.ID, packVerify2.ID) - - labels, err = ds.ListLabelsForPack(pack.ID) - assert.Nil(t, err) - require.Len(t, labels, 0) -} - func TestDeletePack(t *testing.T) { ds, err := inmem.New(config.TestConfig()) assert.Nil(t, err) @@ -170,38 +78,3 @@ func TestDeletePack(t *testing.T) { assert.Nil(t, err) assert.Len(t, queries, 0) } - -func TestListPacksForHost(t *testing.T) { - ds, err := inmem.New(config.TestConfig()) - assert.Nil(t, err) - - mockClock := clock.NewMockClock() - - svc, err := newTestService(ds, nil) - assert.Nil(t, err) - - ctx := context.Background() - - h1 := test.NewHost(t, ds, "h1", "10.10.10.1", "1", "1", mockClock.Now()) - h2 := test.NewHost(t, ds, "h2", "10.10.10.2", "2", "2", mockClock.Now()) - - p1 := test.NewPack(t, ds, "p1") - p2 := test.NewPack(t, ds, "p2") - - require.Nil(t, svc.AddHostToPack(ctx, h1.ID, p1.ID)) - require.Nil(t, svc.AddHostToPack(ctx, h2.ID, p1.ID)) - - require.Nil(t, svc.AddHostToPack(ctx, h1.ID, p2.ID)) - - { - packs, err := svc.ListPacksForHost(ctx, h1.ID) - require.Nil(t, err) - require.Len(t, packs, 2) - } - { - packs, err := svc.ListPacksForHost(ctx, h2.ID) - require.Nil(t, err) - require.Len(t, packs, 1) - } - -} diff --git a/server/service/transport_labels.go b/server/service/transport_labels.go index c4321d6e9b..a611351a94 100644 --- a/server/service/transport_labels.go +++ b/server/service/transport_labels.go @@ -2,18 +2,9 @@ package service import ( "context" - "encoding/json" "net/http" ) -func decodeCreateLabelRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var req createLabelRequest - if err := json.NewDecoder(r.Body).Decode(&req.payload); err != nil { - return nil, err - } - return req, nil -} - func decodeDeleteLabelRequest(ctx context.Context, r *http.Request) (interface{}, error) { id, err := idFromRequest(r, "id") if err != nil { @@ -41,17 +32,3 @@ func decodeListLabelsRequest(ctx context.Context, r *http.Request) (interface{}, } return listLabelsRequest{ListOptions: opt}, nil } - -func decodeModifyLabelRequest(ctx context.Context, r *http.Request) (interface{}, error) { - id, err := idFromRequest(r, "id") - if err != nil { - return nil, err - } - var resp modifyLabelRequest - err = json.NewDecoder(r.Body).Decode(&resp.payload) - if err != nil { - return nil, err - } - resp.ID = id - return resp, nil -} diff --git a/server/service/transport_labels_test.go b/server/service/transport_labels_test.go index e15b283431..81d29f70b0 100644 --- a/server/service/transport_labels_test.go +++ b/server/service/transport_labels_test.go @@ -1,7 +1,6 @@ package service import ( - "bytes" "context" "net/http" "net/http/httptest" @@ -11,31 +10,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestDecodeCreateLabelRequest(t *testing.T) { - router := mux.NewRouter() - router.HandleFunc("/api/v1/kolide/labels", func(writer http.ResponseWriter, request *http.Request) { - r, err := decodeCreateLabelRequest(context.Background(), request) - assert.Nil(t, err) - - params := r.(createLabelRequest) - assert.Equal(t, "foo", *params.payload.Name) - assert.Equal(t, "select * from foo;", *params.payload.Query) - assert.Equal(t, "darwin", *params.payload.Platform) - }).Methods("POST") - - var body bytes.Buffer - body.Write([]byte(`{ - "name": "foo", - "query": "select * from foo;", - "platform": "darwin" - }`)) - - router.ServeHTTP( - httptest.NewRecorder(), - httptest.NewRequest("POST", "/api/v1/kolide/labels", &body), - ) -} - func TestDecodeDeleteLabelRequest(t *testing.T) { router := mux.NewRouter() router.HandleFunc("/api/v1/kolide/labels/{id}", func(writer http.ResponseWriter, request *http.Request) { diff --git a/server/service/transport_packs.go b/server/service/transport_packs.go index 6ccc63b82e..277522088c 100644 --- a/server/service/transport_packs.go +++ b/server/service/transport_packs.go @@ -2,32 +2,9 @@ package service import ( "context" - "encoding/json" "net/http" ) -func decodeCreatePackRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var req createPackRequest - if err := json.NewDecoder(r.Body).Decode(&req.payload); err != nil { - return nil, err - } - - return req, nil -} - -func decodeModifyPackRequest(ctx context.Context, r *http.Request) (interface{}, error) { - id, err := idFromRequest(r, "id") - if err != nil { - return nil, err - } - var req modifyPackRequest - if err := json.NewDecoder(r.Body).Decode(&req.payload); err != nil { - return nil, err - } - req.ID = id - return req, nil -} - func decodeDeletePackRequest(ctx context.Context, r *http.Request) (interface{}, error) { id, err := idFromRequest(r, "id") if err != nil { diff --git a/server/service/transport_packs_test.go b/server/service/transport_packs_test.go index 0806e4c5bd..abb296233e 100644 --- a/server/service/transport_packs_test.go +++ b/server/service/transport_packs_test.go @@ -1,7 +1,6 @@ package service import ( - "bytes" "context" "net/http" "net/http/httptest" @@ -9,68 +8,8 @@ import ( "github.com/gorilla/mux" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -func TestDecodeCreatePackRequest(t *testing.T) { - router := mux.NewRouter() - router.HandleFunc("/api/v1/kolide/packs", func(writer http.ResponseWriter, request *http.Request) { - r, err := decodeCreatePackRequest(context.Background(), request) - assert.Nil(t, err) - - params := r.(createPackRequest) - assert.Equal(t, "foo", *params.payload.Name) - assert.Equal(t, "bar", *params.payload.Description) - require.NotNil(t, params.payload.HostIDs) - assert.Len(t, *params.payload.HostIDs, 3) - require.NotNil(t, params.payload.LabelIDs) - assert.Len(t, *params.payload.LabelIDs, 2) - }).Methods("POST") - - var body bytes.Buffer - body.Write([]byte(`{ - "name": "foo", - "description": "bar", - "host_ids": [1, 2, 3], - "label_ids": [1, 5] - }`)) - - router.ServeHTTP( - httptest.NewRecorder(), - httptest.NewRequest("POST", "/api/v1/kolide/packs", &body), - ) -} - -func TestDecodeModifyPackRequest(t *testing.T) { - router := mux.NewRouter() - router.HandleFunc("/api/v1/kolide/packs/{id}", func(writer http.ResponseWriter, request *http.Request) { - r, err := decodeModifyPackRequest(context.Background(), request) - assert.Nil(t, err) - - params := r.(modifyPackRequest) - assert.Equal(t, uint(1), params.ID) - assert.Equal(t, "foo", *params.payload.Name) - assert.Equal(t, "bar", *params.payload.Description) - require.NotNil(t, params.payload.HostIDs) - assert.Len(t, *params.payload.HostIDs, 3) - require.NotNil(t, params.payload.LabelIDs) - assert.Len(t, *params.payload.LabelIDs, 2) - }).Methods("PATCH") - - var body bytes.Buffer - body.Write([]byte(`{ - "name": "foo", - "description": "bar", - "host_ids": [1, 2, 3], - "label_ids": [1, 5] - }`)) - - router.ServeHTTP( - httptest.NewRecorder(), - httptest.NewRequest("PATCH", "/api/v1/kolide/packs/1", &body), - ) -} - func TestDecodeDeletePackRequest(t *testing.T) { router := mux.NewRouter() router.HandleFunc("/api/v1/kolide/packs/{id}", func(writer http.ResponseWriter, request *http.Request) { diff --git a/server/test/new_objects.go b/server/test/new_objects.go index 2e9a2441fa..5cc2367f29 100644 --- a/server/test/new_objects.go +++ b/server/test/new_objects.go @@ -25,13 +25,12 @@ func NewQuery(t *testing.T, ds kolide.Datastore, name, q string, authorID uint, } func NewPack(t *testing.T, ds kolide.Datastore, name string) *kolide.Pack { - pack, err := ds.NewPack(&kolide.Pack{ - Name: name, - }) + err := ds.ApplyPackSpecs([]*kolide.PackSpec{&kolide.PackSpec{Name: name}}) require.Nil(t, err) // Loading gives us the timestamps - pack, err = ds.Pack(pack.ID) + pack, ok, err := ds.PackByName(name) + require.True(t, ok) require.Nil(t, err) return pack @@ -104,15 +103,6 @@ func NewHost(t *testing.T, ds kolide.Datastore, name, ip, key, uuid string, now return h } -func NewLabel(t *testing.T, ds kolide.Datastore, name, query string) *kolide.Label { - l, err := ds.NewLabel(&kolide.Label{Name: name, Query: query}) - - require.Nil(t, err) - require.NotZero(t, l.ID) - - return l -} - func NewUser(t *testing.T, ds kolide.Datastore, name, username, email string, admin bool) *kolide.User { u, err := ds.NewUser(&kolide.User{ Password: []byte("garbage"),