diff --git a/server/datastore/datastore_scheduled_queries_test.go b/server/datastore/datastore_scheduled_queries_test.go index 39044a5c91..0fe4315729 100644 --- a/server/datastore/datastore_scheduled_queries_test.go +++ b/server/datastore/datastore_scheduled_queries_test.go @@ -117,3 +117,53 @@ func testDeleteScheduledQuery(t *testing.T, ds kolide.Datastore) { _, err = ds.ScheduledQuery(sq1.ID) require.NotNil(t, err) } + +func testCascadingDeletionOfQueries(t *testing.T, ds kolide.Datastore) { + zwass := test.NewUser(t, ds, "Zach", "zwass", "zwass@kolide.co", true) + queries := []*kolide.Query{ + {Name: "foo", Description: "get the foos", Query: "select * from foo"}, + {Name: "bar", Description: "do some bars", Query: "select baz from bar"}, + } + err := ds.ApplyQueries(zwass.ID, queries) + require.Nil(t, err) + + specs := []*kolide.PackSpec{ + &kolide.PackSpec{ + Name: "baz", + Targets: kolide.PackSpecTargets{Labels: []string{}}, + Queries: []kolide.PackSpecQuery{ + kolide.PackSpecQuery{ + QueryName: queries[0].Name, + Description: "test_foo", + Interval: 60, + }, + kolide.PackSpecQuery{ + QueryName: queries[1].Name, + Name: "test bar", + Description: "test_bar", + Interval: 60, + }, + kolide.PackSpecQuery{ + QueryName: queries[1].Name, + Name: "test bar snapshot", + Description: "test_bar", + Interval: 60, + }, + }, + }, + } + err = ds.ApplyPackSpecs(specs) + require.Nil(t, err) + + gotQueries, err := ds.ListScheduledQueriesInPack(1, kolide.ListOptions{}) + require.Nil(t, err) + require.Len(t, gotQueries, 3) + + err = ds.DeleteQuery(queries[1].Name) + require.Nil(t, err) + + gotQueries, err = ds.ListScheduledQueriesInPack(1, kolide.ListOptions{}) + require.Nil(t, err) + require.Len(t, gotQueries, 1) + +} diff --git a/server/datastore/datastore_test.go b/server/datastore/datastore_test.go index 16d52cc590..a7c8d5a95e 100644 --- a/server/datastore/datastore_test.go +++ b/server/datastore/datastore_test.go @@ -60,6 +60,7 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){ testDeleteScheduledQuery, testNewScheduledQuery, testListScheduledQueriesInPack, + testCascadingDeletionOfQueries, testOptions, testOptionsToConfig, testGetPackByName, diff --git a/server/datastore/mysql/migrations/tables/20180620164811_PackAndQueryDeletion.go b/server/datastore/mysql/migrations/tables/20180620164811_PackAndQueryDeletion.go new file mode 100644 index 0000000000..20eec2b6c0 --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20180620164811_PackAndQueryDeletion.go @@ -0,0 +1,61 @@ +package tables + +import ( + "database/sql" + "fmt" + + "github.com/pkg/errors" +) + +func init() { + MigrationClient.AddMigration(Up_20180620164811, Down_20180620164811) +} + +func Up_20180620164811(tx *sql.Tx) error { + // Drop the old foreign key for query name (to be replaced later) + query := ` + ALTER TABLE scheduled_queries + DROP FOREIGN KEY scheduled_queries_ibfk_1 + ` + if _, err := tx.Exec(query); err != nil { + // If the foreign key doesn't exist (or exists under a + // different name), we can just allow it to dupe and move on + // rather than failing and requiring manual intervention. + fmt.Println("Skipped deleting foreign key `scheduled_queries_ibfk_1`: " + err.Error()) + } + + // Delete any scheduled queries where the pack is already deleted + query = ` + DELETE FROM scheduled_queries + WHERE pack_id NOT IN (SELECT id FROM packs) + ` + if _, err := tx.Exec(query); err != nil { + return errors.Wrap(err, "delete dangling scheduled queries") + } + + query = ` + ALTER TABLE scheduled_queries + ADD CONSTRAINT scheduled_queries_query_name + FOREIGN KEY (query_name) REFERENCES queries (name) + ON DELETE CASCADE + ` + if _, err := tx.Exec(query); err != nil { + return errors.Wrap(err, "add foreign key to query name") + } + + query = ` + ALTER TABLE scheduled_queries + ADD CONSTRAINT scheduled_queries_pack_id + FOREIGN KEY (pack_id) REFERENCES packs (id) + ON DELETE CASCADE + ` + if _, err := tx.Exec(query); err != nil { + return errors.Wrap(err, "add foreign key to pack ID") + } + + return nil +} + +func Down_20180620164811(tx *sql.Tx) error { + return nil +}