diff --git a/server/datastore/datastore_hosts_test.go b/server/datastore/datastore_hosts_test.go index 99d16017db..4bbc675bd1 100644 --- a/server/datastore/datastore_hosts_test.go +++ b/server/datastore/datastore_hosts_test.go @@ -625,3 +625,57 @@ func testMarkHostSeen(t *testing.T, ds kolide.Datastore) { assert.WithinDuration(t, anHourAgo, h1Verify.SeenTime, time.Second) } } + +func testFlappingNetworkInterfaces(t *testing.T, ds kolide.Datastore) { + // See https://github.com/kolide/kolide/issues/1278 + host, err := ds.NewHost(&kolide.Host{ + DetailUpdateTime: time.Now(), + SeenTime: time.Now(), + NodeKey: "1", + UUID: "1", + HostName: "foo.local", + }) + require.Nil(t, err) + require.NotNil(t, host) + + host.HostName = "bar.local" + err = ds.SaveHost(host) + require.Nil(t, err) + + host, err = ds.Host(host.ID) + require.Nil(t, err) + assert.Equal(t, "bar.local", host.HostName) + + host.NetworkInterfaces = []*kolide.NetworkInterface{ + &kolide.NetworkInterface{ + HostID: host.ID, + Interface: "en0", + IPAddress: "98.99.100.101", + }, + } + + err = ds.SaveHost(host) + require.Nil(t, err) + + host, err = ds.AuthenticateHost(host.NodeKey) + require.Nil(t, err) + assert.Len(t, host.NetworkInterfaces, 1) + + // Simulate osquery returning the same results for the network + // interfaces (note it's important that we reset this so that the ID is + // 0 before saving) + host.NetworkInterfaces = []*kolide.NetworkInterface{ + &kolide.NetworkInterface{ + HostID: host.ID, + Interface: "en0", + IPAddress: "98.99.100.101", + }, + } + + err = ds.SaveHost(host) + require.Nil(t, err) + + host, err = ds.AuthenticateHost(host.NodeKey) + require.Nil(t, err) + assert.Len(t, host.NetworkInterfaces, 1) +} diff --git a/server/datastore/datastore_test.go b/server/datastore/datastore_test.go index 1472dc782e..5f871f7b77 100644 --- a/server/datastore/datastore_test.go +++ b/server/datastore/datastore_test.go @@ -72,4 +72,5 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){ testChangeEmail, testLicense, testSaveLabel, + testFlappingNetworkInterfaces, } diff --git a/server/datastore/mysql/hosts.go b/server/datastore/mysql/hosts.go index 9d0ebea191..3d172cd6b2 100644 --- a/server/datastore/mysql/hosts.go +++ b/server/datastore/mysql/hosts.go @@ -71,6 +71,8 @@ func removedUnusedNics(tx *sqlx.Tx, host *kolide.Host) error { func updateNicsForHost(tx *sqlx.Tx, host *kolide.Host) ([]*kolide.NetworkInterface, error) { updatedNics := []*kolide.NetworkInterface{} + // id = LAST_INSERT_ID(id) is a fix for the lastinsertid not being set + // properly. See comments in https://goo.gl/cwWRXd. sqlStatement := ` INSERT INTO network_interfaces ( host_id, @@ -92,6 +94,7 @@ func updateNicsForHost(tx *sqlx.Tx, host *kolide.Host) ([]*kolide.NetworkInterfa type ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE + id = LAST_INSERT_ID(id), mac = VALUES(mac), broadcast = VALUES(broadcast), ibytes = VALUES(ibytes), diff --git a/server/datastore/mysql/migrations/tables/20170223094154_AddTimestampsToNetworkInterfaces.go b/server/datastore/mysql/migrations/tables/20170223094154_AddTimestampsToNetworkInterfaces.go new file mode 100644 index 0000000000..53823717de --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20170223094154_AddTimestampsToNetworkInterfaces.go @@ -0,0 +1,25 @@ +package tables + +import "database/sql" + +func init() { + MigrationClient.AddMigration(Up_20170223094154, Down_20170223094154) +} + +func Up_20170223094154(tx *sql.Tx) error { + _, err := tx.Exec( + "ALTER TABLE `network_interfaces` " + + "ADD COLUMN `created_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " + + "ADD COLUMN `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP", + ) + return err +} + +func Down_20170223094154(tx *sql.Tx) error { + _, err := tx.Exec( + "ALTER TABLE `network_interfaces` " + + "DROP COLUMN `created_at`, " + + "DROP COLUMN `updated_at`", + ) + return err +} diff --git a/server/kolide/network_interfaces.go b/server/kolide/network_interfaces.go index a54a534526..63d51cd9f0 100644 --- a/server/kolide/network_interfaces.go +++ b/server/kolide/network_interfaces.go @@ -1,6 +1,7 @@ package kolide type NetworkInterface struct { + UpdateCreateTimestamps ID uint `json:"id"` // HostID foreign key establishes one host to many NetworkInterface relationship HostID uint `json:"-" db:"host_id"` diff --git a/server/service/service_osquery_test.go b/server/service/service_osquery_test.go index c8cac86ff6..d0089c2ef8 100644 --- a/server/service/service_osquery_test.go +++ b/server/service/service_osquery_test.go @@ -225,7 +225,6 @@ func TestHostDetailQueries(t *testing.T) { } func TestLabelQueries(t *testing.T) { - ds, svc, mockClock := setupOsqueryTests(t) ctx := context.Background()