(t *testing.T)
| 372 | } |
| 373 | |
| 374 | func TestActivateStaleIncomplete(t *testing.T) { |
| 375 | td := t.TempDir() |
| 376 | metadb := filepath.Join(td, "mounts.db") |
| 377 | targetdir := filepath.Join(td, "m") |
| 378 | db, err := bolt.Open(metadb, 0600, nil) |
| 379 | require.NoError(t, err) |
| 380 | ctx := namespaces.WithNamespace(context.Background(), "test") |
| 381 | |
| 382 | // Simulate a stale incomplete activation by directly writing a bucket |
| 383 | // without the "active" sub-bucket (as if the process crashed mid-activation). |
| 384 | // Use mount ID 42 so we can verify the stale target directory gets cleaned up. |
| 385 | var staleMID uint64 = 42 |
| 386 | err = db.Update(func(tx *bolt.Tx) error { |
| 387 | v1bkt, err := tx.CreateBucketIfNotExists([]byte("v1")) |
| 388 | if err != nil { |
| 389 | return err |
| 390 | } |
| 391 | nsbkt, err := v1bkt.CreateBucketIfNotExists([]byte("test")) |
| 392 | if err != nil { |
| 393 | return err |
| 394 | } |
| 395 | mbkt, err := nsbkt.CreateBucketIfNotExists(bucketKeyMounts) |
| 396 | if err != nil { |
| 397 | return err |
| 398 | } |
| 399 | // Create the mount bucket but don't add the "active" sub-bucket |
| 400 | bkt, err := mbkt.CreateBucket([]byte("task1")) |
| 401 | if err != nil { |
| 402 | return err |
| 403 | } |
| 404 | idb, err := encodeID(staleMID) |
| 405 | if err != nil { |
| 406 | return err |
| 407 | } |
| 408 | return bkt.Put(bucketKeyID, idb) |
| 409 | }) |
| 410 | require.NoError(t, err) |
| 411 | |
| 412 | // Create the stale target directory as if the process crashed after |
| 413 | // mkdir but before the second transaction. |
| 414 | staleTarget := filepath.Join(targetdir, fmt.Sprintf("%d", staleMID)) |
| 415 | require.NoError(t, os.MkdirAll(staleTarget, 0700)) |
| 416 | |
| 417 | mountC := new(atomic.Int32) |
| 418 | m, err := NewManager(db, targetdir, WithMountHandler("noop", &noopHandler{mounts: mountC})) |
| 419 | require.NoError(t, err) |
| 420 | t.Cleanup(func() { assert.NoError(t, m.(io.Closer).Close()) }) |
| 421 | |
| 422 | mounts := []mount.Mount{{Type: "noop"}} |
| 423 | |
| 424 | // Activation should succeed by cleaning up the stale entry |
| 425 | ainfo, err := m.Activate(ctx, "task1", mounts) |
| 426 | require.NoError(t, err) |
| 427 | assert.Equal(t, "task1", ainfo.Name) |
| 428 | assert.Equal(t, 1, len(ainfo.Active)) |
| 429 | |
| 430 | // The stale target directory should have been cleaned up |
| 431 | _, err = os.Stat(staleTarget) |
nothing calls this directly
no test coverage detected
searching dependent graphs…