Add cascade deletes for host software (#1739)

* Add cascade deletes for host software

* Add changes file

* The drop doesn't work on certain mysql

* Fix error message
This commit is contained in:
Tomas Touceda 2021-08-20 14:29:00 -03:00 committed by GitHub
parent c6c63ab12a
commit 605970c441
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 14 deletions

View File

@ -0,0 +1 @@
* MariaDB compatibility fixes: add explicit foreign key constraint and on cascade delete for host_software to allow for hosts with software to be deleted.

View File

@ -96,6 +96,42 @@ func TestSaveHosts(t *testing.T) {
assert.Nil(t, host)
}
func TestDeleteHostWithSoftware(t *testing.T) {
ds := CreateMySQLDS(t)
defer ds.Close()
host, err := ds.NewHost(&fleet.Host{
DetailUpdatedAt: time.Now(),
LabelUpdatedAt: time.Now(),
SeenTime: time.Now(),
NodeKey: "1",
UUID: "1",
Hostname: "foo.local",
PrimaryIP: "192.168.1.1",
PrimaryMac: "30-65-EC-6F-C4-58",
})
require.NoError(t, err)
require.NotNil(t, host)
soft := fleet.HostSoftware{
Modified: true,
Software: []fleet.Software{
{Name: "foo", Version: "0.0.1", Source: "chrome_extensions"},
{Name: "foo", Version: "0.0.3", Source: "chrome_extensions"},
},
}
host.HostSoftware = soft
err = ds.SaveHostSoftware(host)
require.NoError(t, err)
err = ds.DeleteHost(host.ID)
require.NoError(t, err)
host, err = ds.Host(host.ID)
assert.NotNil(t, err)
assert.Nil(t, host)
}
func TestSaveHostPackStats(t *testing.T) {
ds := CreateMySQLDS(t)
defer ds.Close()

View File

@ -12,22 +12,14 @@ func init() {
}
func Up_20210818151827(tx *sql.Tx) error {
rows, err := tx.Query(`SELECT DISTINCT CONSTRAINT_NAME, REFERENCED_TABLE_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = 'scheduled_query_stats' AND CONSTRAINT_NAME <> 'PRIMARY'`)
referencedTables := map[string]struct{}{"hosts": {}, "scheduled_queries": {}}
table := "scheduled_query_stats"
constraints, err := constraintsForTable(tx, table, referencedTables)
if err != nil {
return errors.Wrap(err, "getting fk for scheduled_query_stats")
}
var constraints []string
for rows.Next() {
var constraintName string
var referencedTable string
err := rows.Scan(&constraintName, &referencedTable)
if err != nil {
return errors.Wrap(err, "scanning fk for scheduled_query_stats")
}
if referencedTable == "hosts" || referencedTable == "scheduled_queries" {
constraints = append(constraints, constraintName)
}
return err
}
if len(constraints) == 0 {
return errors.New("Found no constraints in scheduled_query_stats")
}
@ -41,6 +33,27 @@ func Up_20210818151827(tx *sql.Tx) error {
return nil
}
func constraintsForTable(tx *sql.Tx, table string, referencedTables map[string]struct{}) ([]string, error) {
var constraints []string
query := `SELECT DISTINCT CONSTRAINT_NAME, REFERENCED_TABLE_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = ? AND CONSTRAINT_NAME <> 'PRIMARY'`
rows, err := tx.Query(query, table)
if err != nil {
return nil, errors.Wrap(err, "getting fk for scheduled_query_stats")
}
for rows.Next() {
var constraintName string
var referencedTable string
err := rows.Scan(&constraintName, &referencedTable)
if err != nil {
return nil, errors.Wrap(err, "scanning fk for scheduled_query_stats")
}
if _, ok := referencedTables[referencedTable]; ok {
constraints = append(constraints, constraintName)
}
}
return constraints, nil
}
func Down_20210818151827(tx *sql.Tx) error {
return nil
}

View File

@ -0,0 +1,46 @@
package tables
import (
"database/sql"
"fmt"
"strings"
"github.com/pkg/errors"
)
func init() {
MigrationClient.AddMigration(Up_20210819131107, Down_20210819131107)
}
func Up_20210819131107(tx *sql.Tx) error {
referencedTables := map[string]struct{}{"hosts": {}, "software": {}}
table := "host_software"
constraints, err := constraintsForTable(tx, table, referencedTables)
if err != nil {
return err
}
for _, constraint := range constraints {
_, err = tx.Exec(fmt.Sprintf(`ALTER TABLE host_software DROP FOREIGN KEY %s;`, constraint))
if err != nil {
if !strings.Contains(err.Error(), "check that column/key exists") {
return errors.Wrapf(err, "dropping fk %s", constraint)
}
}
}
if _, err := tx.Exec(`
ALTER TABLE host_software
ADD FOREIGN KEY host_software_hosts_fk(host_id) REFERENCES hosts (id) ON DELETE CASCADE,
ADD FOREIGN KEY host_software_software_fk(software_id) REFERENCES software (id) ON DELETE CASCADE
`); err != nil {
return errors.Wrap(err, "add fk on host_software hosts & software")
}
return nil
}
func Down_20210819131107(tx *sql.Tx) error {
return nil
}