Manager: refuse to delete workers without foreign key constraints

As a safety measure, refuse to delete Workers from the Manager's database
when foreign key constraints are disabled.

In the long term, the underlying problem should be solved. This is a stop-
gap measure to ensure database consistency.
This commit is contained in:
Sybren A. Stüvel 2024-04-12 10:45:51 +02:00
parent 6c28db780f
commit 3974770f36
2 changed files with 33 additions and 0 deletions

@ -88,6 +88,15 @@ func (db *DB) FetchWorker(ctx context.Context, uuid string) (*Worker, error) {
} }
func (db *DB) DeleteWorker(ctx context.Context, uuid string) error { func (db *DB) DeleteWorker(ctx context.Context, uuid string) error {
// As a safety measure, refuse to delete unless foreign key constraints are active.
fkEnabled, err := db.areForeignKeysEnabled()
if err != nil {
return fmt.Errorf("checking whether foreign keys are enabled: %w", err)
}
if !fkEnabled {
return ErrDeletingWithoutFK
}
tx := db.gormDB.WithContext(ctx). tx := db.gormDB.WithContext(ctx).
Where("uuid = ?", uuid). Where("uuid = ?", uuid).
Delete(&Worker{}) Delete(&Worker{})

@ -314,6 +314,30 @@ func TestDeleteWorker(t *testing.T) {
} }
} }
func TestDeleteWorkerNoForeignKeys(t *testing.T) {
ctx, cancel, db := persistenceTestFixtures(t, 1*time.Second)
defer cancel()
// Create a Worker to delete.
w1 := Worker{
UUID: "fd97a35b-a5bd-44b4-ac2b-64c193ca877d",
Name: "Worker 1",
Status: api.WorkerStatusAwake,
}
require.NoError(t, db.CreateWorker(ctx, &w1))
// Try deleting with foreign key constraints disabled.
require.NoError(t, db.pragmaForeignKeys(false))
require.ErrorIs(t, ErrDeletingWithoutFK, db.DeleteWorker(ctx, w1.UUID))
// The worker should still exist.
{
fetchedWorker, err := db.FetchWorker(ctx, w1.UUID)
require.NoError(t, err)
assert.Equal(t, w1.UUID, fetchedWorker.UUID)
}
}
func TestDeleteWorkerWithTagAssigned(t *testing.T) { func TestDeleteWorkerWithTagAssigned(t *testing.T) {
f := workerTestFixtures(t, 1*time.Second) f := workerTestFixtures(t, 1*time.Second)
defer f.done() defer f.done()