diff --git a/lib/instances/delete.go b/lib/instances/delete.go index e1e0bb012..7826803c2 100644 --- a/lib/instances/delete.go +++ b/lib/instances/delete.go @@ -145,13 +145,12 @@ func (m *manager) deleteInstanceWithOptions( m.closeFirecrackerUFFDSession(ctx, stored) // Release before deleting metadata so a failed release can be retried safely. - hadVGPUAssignment := storedVGPUDevicePath(stored) != "" - if hadVGPUAssignment { + if storedVGPUDevicePath(stored) != "" { log.InfoContext(ctx, "destroying vGPU", "instance_id", id, "uuid", stored.GPUMdevUUID) - } - if err := m.releaseStoredVGPUPersisted(ctx, meta); err != nil { - log.ErrorContext(ctx, "failed to destroy vGPU; retaining instance metadata for retry", "instance_id", id, "uuid", stored.GPUMdevUUID, "error", err) - return fmt.Errorf("release vGPU: %w", err) + if err := m.releaseStoredVGPUPersisted(ctx, meta); err != nil { + log.ErrorContext(ctx, "failed to destroy vGPU; retaining instance metadata for retry", "instance_id", id, "uuid", stored.GPUMdevUUID, "error", err) + return fmt.Errorf("release vGPU: %w", err) + } } // 6. Release network allocation diff --git a/lib/instances/lifecycle_noop_test.go b/lib/instances/lifecycle_noop_test.go index cb11eefae..11d1a874f 100644 --- a/lib/instances/lifecycle_noop_test.go +++ b/lib/instances/lifecycle_noop_test.go @@ -150,6 +150,31 @@ func TestLifecycleNoopStandbyWithOptionsStillRejectsStandbyInstance(t *testing.T assertNoLifecycleEvent(t, events) } +func TestDeleteWithoutVGPUDoesNotRewriteMetadata(t *testing.T) { + m, id := newLifecycleNoopManagerWithInstance(t, StateStopped, time.Now().UTC()) + meta, err := m.loadMetadata(id) + require.NoError(t, err) + meta.Devices = []string{"dev-1"} + require.NoError(t, m.saveMetadata(meta)) + before, err := os.Stat(m.paths.InstanceMetadata(id)) + require.NoError(t, err) + + deviceManager := &recordingDeviceManager{ + onMarkDetached: func() { + during, err := os.Stat(m.paths.InstanceMetadata(id)) + require.NoError(t, err) + // saveMetadata replaces the file, requiring free space even for deletion. + assert.True(t, os.SameFile(before, during), "delete without a vGPU must not rewrite metadata") + }, + } + m.deviceManager = deviceManager + + require.NoError(t, m.DeleteInstance(t.Context(), id)) + assert.Equal(t, []string{"dev-1"}, deviceManager.detached) + _, err = m.loadMetadata(id) + require.ErrorIs(t, err, ErrNotFound) +} + func TestDeletePersistsVGPUReleaseBeforeTeardown(t *testing.T) { m, id := newLifecycleNoopManagerWithInstance(t, StateStopped, time.Now().UTC()) var persisted *metadata