Skip to content

Commit bb24c66

Browse files
authored
Change method for setting PID (#1769)
Originally this was combined with setting status. When I went to review the changes I needed to make, I discovered that the changes would be much more simple if it matched the write/remove interface of the PID files.
1 parent 8c8b39f commit bb24c66

File tree

5 files changed

+255
-90
lines changed

5 files changed

+255
-90
lines changed

pkg/workloads/statuses/file_status.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -303,16 +303,47 @@ func (f *fileStatusManager) DeleteWorkloadStatus(ctx context.Context, workloadNa
303303
})
304304
}
305305

306-
// SetWorkloadStatusAndPID sets the status and PID of a workload by its name.
307-
// It otherwise behaves like SetWorkloadStatus.
308-
func (f *fileStatusManager) SetWorkloadStatusAndPID(
309-
ctx context.Context,
310-
workloadName string,
311-
status rt.WorkloadStatus,
312-
contextMsg string,
313-
pid int,
314-
) error {
315-
return f.setWorkloadStatusInternal(ctx, workloadName, status, contextMsg, &pid)
306+
// SetWorkloadPID sets the PID of a workload by its name.
307+
// This method will do nothing if the workload does not exist.
308+
func (f *fileStatusManager) SetWorkloadPID(ctx context.Context, workloadName string, pid int) error {
309+
err := f.withFileLock(ctx, workloadName, func(statusFilePath string) error {
310+
// Check if file exists
311+
if _, err := os.Stat(statusFilePath); os.IsNotExist(err) {
312+
// File doesn't exist, nothing to do
313+
logger.Debugf("workload %s does not exist, skipping PID update", workloadName)
314+
return nil
315+
} else if err != nil {
316+
return fmt.Errorf("failed to check status file for workload %s: %w", workloadName, err)
317+
}
318+
319+
// Read existing file
320+
statusFile, err := f.readStatusFile(statusFilePath)
321+
if err != nil {
322+
return fmt.Errorf("failed to read existing status for workload %s: %w", workloadName, err)
323+
}
324+
325+
// Update only the PID and UpdatedAt timestamp
326+
statusFile.ProcessID = pid
327+
statusFile.UpdatedAt = time.Now()
328+
329+
if err = f.writeStatusFile(statusFilePath, *statusFile); err != nil {
330+
return fmt.Errorf("failed to write updated PID for workload %s: %w", workloadName, err)
331+
}
332+
333+
logger.Debugf("workload %s PID set to %d", workloadName, pid)
334+
return nil
335+
})
336+
337+
if err != nil {
338+
logger.Errorf("error updating workload %s PID: %v", workloadName, err)
339+
}
340+
return err
341+
}
342+
343+
// ResetWorkloadPID resets the PID of a workload to 0.
344+
// This method will do nothing if the workload does not exist.
345+
func (f *fileStatusManager) ResetWorkloadPID(ctx context.Context, workloadName string) error {
346+
return f.SetWorkloadPID(ctx, workloadName, 0)
316347
}
317348

318349
// getStatusFilePath returns the file path for a given workload's status file.

pkg/workloads/statuses/file_status_test.go

Lines changed: 140 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,12 @@ func TestFileStatusManager_SetWorkloadStatus_PreservesPID(t *testing.T) {
371371
manager := &fileStatusManager{baseDir: tempDir}
372372
ctx := context.Background()
373373

374-
// First, create a workload with status and PID using SetWorkloadStatusAndPID
375-
err := manager.SetWorkloadStatusAndPID(ctx, "test-workload", rt.WorkloadStatusStarting, "initializing", 12345)
374+
// First, create a workload with status
375+
err := manager.SetWorkloadStatus(ctx, "test-workload", rt.WorkloadStatusStarting, "initializing")
376+
require.NoError(t, err)
377+
378+
// Then set the PID
379+
err = manager.SetWorkloadPID(ctx, "test-workload", 12345)
376380
require.NoError(t, err)
377381

378382
// Read the file to verify initial state
@@ -1371,36 +1375,22 @@ func TestFileStatusManager_ReadStatusFile_Validation(t *testing.T) {
13711375
}
13721376
}
13731377

1374-
func TestFileStatusManager_SetWorkloadStatusAndPID_Create(t *testing.T) {
1378+
func TestFileStatusManager_SetWorkloadPID_NonExistentWorkload(t *testing.T) {
13751379
t.Parallel()
13761380
tempDir := t.TempDir()
13771381
manager := &fileStatusManager{baseDir: tempDir}
13781382
ctx := context.Background()
13791383

1380-
// Test setting status and PID for new workload (no status file exists)
1381-
err := manager.SetWorkloadStatusAndPID(ctx, "test-workload", rt.WorkloadStatusRunning, "container started", 12345)
1384+
// Test setting PID for non-existent workload (should be a noop)
1385+
err := manager.SetWorkloadPID(ctx, "test-workload", 12345)
13821386
require.NoError(t, err)
13831387

1384-
// Verify file was created
1388+
// Verify no file was created (since it's a noop)
13851389
statusFile := filepath.Join(tempDir, "test-workload.json")
1386-
require.FileExists(t, statusFile)
1387-
1388-
// Verify file contents
1389-
data, err := os.ReadFile(statusFile)
1390-
require.NoError(t, err)
1391-
1392-
var statusFileData workloadStatusFile
1393-
err = json.Unmarshal(data, &statusFileData)
1394-
require.NoError(t, err)
1395-
1396-
assert.Equal(t, rt.WorkloadStatusRunning, statusFileData.Status)
1397-
assert.Equal(t, "container started", statusFileData.StatusContext)
1398-
assert.Equal(t, 12345, statusFileData.ProcessID)
1399-
assert.False(t, statusFileData.CreatedAt.IsZero())
1400-
assert.False(t, statusFileData.UpdatedAt.IsZero())
1390+
require.NoFileExists(t, statusFile)
14011391
}
14021392

1403-
func TestFileStatusManager_SetWorkloadStatusAndPID_Update(t *testing.T) {
1393+
func TestFileStatusManager_SetWorkloadPID_Update(t *testing.T) {
14041394
t.Parallel()
14051395
tempDir := t.TempDir()
14061396
manager := &fileStatusManager{baseDir: tempDir}
@@ -1419,8 +1409,8 @@ func TestFileStatusManager_SetWorkloadStatusAndPID_Update(t *testing.T) {
14191409
err = json.Unmarshal(originalData, &originalStatusFile)
14201410
require.NoError(t, err)
14211411

1422-
// Set the status and PID on existing workload
1423-
err = manager.SetWorkloadStatusAndPID(ctx, "test-workload", rt.WorkloadStatusRunning, "container ready", 67890)
1412+
// Set the PID on existing workload
1413+
err = manager.SetWorkloadPID(ctx, "test-workload", 67890)
14241414
require.NoError(t, err)
14251415

14261416
// Verify file was updated
@@ -1431,25 +1421,29 @@ func TestFileStatusManager_SetWorkloadStatusAndPID_Update(t *testing.T) {
14311421
err = json.Unmarshal(data, &statusFileData)
14321422
require.NoError(t, err)
14331423

1434-
// Verify status, context, and PID were updated while preserving other fields
1435-
assert.Equal(t, rt.WorkloadStatusRunning, statusFileData.Status) // Status updated
1436-
assert.Equal(t, "container ready", statusFileData.StatusContext) // Context updated
1424+
// Verify only PID was updated while preserving other fields
1425+
assert.Equal(t, rt.WorkloadStatusStarting, statusFileData.Status) // Status preserved
1426+
assert.Equal(t, "initializing", statusFileData.StatusContext) // Context preserved
14371427
assert.Equal(t, 67890, statusFileData.ProcessID) // PID updated
14381428
assert.Equal(t, originalStatusFile.CreatedAt, statusFileData.CreatedAt) // CreatedAt preserved
14391429
assert.True(t, statusFileData.UpdatedAt.After(originalStatusFile.UpdatedAt) ||
14401430
statusFileData.UpdatedAt.Equal(originalStatusFile.UpdatedAt)) // UpdatedAt updated
14411431
}
14421432

1443-
func TestFileStatusManager_SetWorkloadStatusAndPID_WithSlashes(t *testing.T) {
1433+
func TestFileStatusManager_SetWorkloadPID_WithSlashes(t *testing.T) {
14441434
t.Parallel()
14451435
tempDir := t.TempDir()
14461436
manager := &fileStatusManager{baseDir: tempDir}
14471437
ctx := context.Background()
14481438

14491439
workloadName := testWorkloadWithSlash
14501440

1451-
// Test setting status and PID for workload name with slashes
1452-
err := manager.SetWorkloadStatusAndPID(ctx, workloadName, rt.WorkloadStatusRunning, "started", 11111)
1441+
// First create the workload
1442+
err := manager.SetWorkloadStatus(ctx, workloadName, rt.WorkloadStatusRunning, "started")
1443+
require.NoError(t, err)
1444+
1445+
// Then set the PID for workload name with slashes
1446+
err = manager.SetWorkloadPID(ctx, workloadName, 11111)
14531447
require.NoError(t, err)
14541448

14551449
// Verify file was created with slashes replaced by dashes
@@ -1469,14 +1463,18 @@ func TestFileStatusManager_SetWorkloadStatusAndPID_WithSlashes(t *testing.T) {
14691463
assert.Equal(t, 11111, statusFileData.ProcessID)
14701464
}
14711465

1472-
func TestFileStatusManager_SetWorkloadStatusAndPID_ZeroPID(t *testing.T) {
1466+
func TestFileStatusManager_SetWorkloadPID_ZeroPID(t *testing.T) {
14731467
t.Parallel()
14741468
tempDir := t.TempDir()
14751469
manager := &fileStatusManager{baseDir: tempDir}
14761470
ctx := context.Background()
14771471

1478-
// Test setting status with PID 0 (which is valid - means no process)
1479-
err := manager.SetWorkloadStatusAndPID(ctx, "test-workload", rt.WorkloadStatusStopped, "container stopped", 0)
1472+
// First create the workload
1473+
err := manager.SetWorkloadStatus(ctx, "test-workload", rt.WorkloadStatusStopped, "container stopped")
1474+
require.NoError(t, err)
1475+
1476+
// Test setting PID 0 (which is valid - means no process)
1477+
err = manager.SetWorkloadPID(ctx, "test-workload", 0)
14801478
require.NoError(t, err)
14811479

14821480
// Verify file was created with PID 0
@@ -1493,7 +1491,7 @@ func TestFileStatusManager_SetWorkloadStatusAndPID_ZeroPID(t *testing.T) {
14931491
assert.Equal(t, 0, statusFileData.ProcessID)
14941492
}
14951493

1496-
func TestFileStatusManager_SetWorkloadStatusAndPID_PreservesCreatedAt(t *testing.T) {
1494+
func TestFileStatusManager_SetWorkloadPID_PreservesCreatedAt(t *testing.T) {
14971495
t.Parallel()
14981496
tempDir := t.TempDir()
14991497
manager := &fileStatusManager{baseDir: tempDir}
@@ -1516,8 +1514,8 @@ func TestFileStatusManager_SetWorkloadStatusAndPID_PreservesCreatedAt(t *testing
15161514
// Wait a bit to ensure timestamps would be different
15171515
time.Sleep(10 * time.Millisecond)
15181516

1519-
// Update using SetWorkloadStatusAndPID
1520-
err = manager.SetWorkloadStatusAndPID(ctx, "test-workload", rt.WorkloadStatusRunning, "now running", 54321)
1517+
// Update using SetWorkloadPID
1518+
err = manager.SetWorkloadPID(ctx, "test-workload", 54321)
15211519
require.NoError(t, err)
15221520

15231521
// Verify CreatedAt is preserved
@@ -1530,49 +1528,49 @@ func TestFileStatusManager_SetWorkloadStatusAndPID_PreservesCreatedAt(t *testing
15301528

15311529
assert.Equal(t, originalCreatedAt, statusFileData.CreatedAt)
15321530
assert.True(t, statusFileData.UpdatedAt.After(originalCreatedAt))
1533-
assert.Equal(t, rt.WorkloadStatusRunning, statusFileData.Status)
1534-
assert.Equal(t, "now running", statusFileData.StatusContext)
1535-
assert.Equal(t, 54321, statusFileData.ProcessID)
1531+
assert.Equal(t, rt.WorkloadStatusStarting, statusFileData.Status) // Status should be preserved
1532+
assert.Equal(t, "initializing", statusFileData.StatusContext) // Context should be preserved
1533+
assert.Equal(t, 54321, statusFileData.ProcessID) // PID should be updated
15361534
}
15371535

1538-
func TestFileStatusManager_SetWorkloadStatusAndPID_ConcurrentAccess(t *testing.T) {
1536+
func TestFileStatusManager_SetWorkloadPID_ConcurrentAccess(t *testing.T) {
15391537
t.Parallel()
15401538
tempDir := t.TempDir()
15411539
manager := &fileStatusManager{baseDir: tempDir}
15421540
ctx := context.Background()
15431541

15441542
// Create initial workload
1545-
err := manager.SetWorkloadStatus(ctx, "concurrent-test", rt.WorkloadStatusStarting, "")
1543+
err := manager.SetWorkloadStatus(ctx, "concurrent-test", rt.WorkloadStatusStarting, "initializing")
15461544
require.NoError(t, err)
15471545

15481546
// Wait a tiny bit to ensure the initial status file is fully written
15491547
time.Sleep(10 * time.Millisecond)
15501548

1551-
// Test concurrent status and PID updates with fewer goroutines to reduce contention
1549+
// Test concurrent PID updates with fewer goroutines to reduce contention
15521550
done := make(chan error, 3)
15531551

15541552
go func() {
1555-
err := manager.SetWorkloadStatusAndPID(ctx, "concurrent-test", rt.WorkloadStatusRunning, "running with PID", 1001)
1553+
err := manager.SetWorkloadPID(ctx, "concurrent-test", 1001)
15561554
done <- err
15571555
}()
15581556

15591557
go func() {
1560-
err := manager.SetWorkloadStatusAndPID(ctx, "concurrent-test", rt.WorkloadStatusStopping, "stopping", 1002)
1558+
err := manager.SetWorkloadPID(ctx, "concurrent-test", 1002)
15611559
done <- err
15621560
}()
15631561

15641562
go func() {
1565-
err := manager.SetWorkloadStatusAndPID(ctx, "concurrent-test", rt.WorkloadStatusStopped, "stopped", 0)
1563+
err := manager.SetWorkloadPID(ctx, "concurrent-test", 1003)
15661564
done <- err
15671565
}()
15681566

15691567
// Wait for all updates to complete and check for errors
15701568
for i := 0; i < 3; i++ {
15711569
select {
15721570
case err := <-done:
1573-
assert.NoError(t, err, "SetWorkloadStatusAndPID should not fail")
1571+
assert.NoError(t, err, "SetWorkloadPID should not fail")
15741572
case <-time.After(5 * time.Second):
1575-
t.Fatal("timeout waiting for concurrent status and PID updates")
1573+
t.Fatal("timeout waiting for concurrent PID updates")
15761574
}
15771575
}
15781576

@@ -1587,14 +1585,100 @@ func TestFileStatusManager_SetWorkloadStatusAndPID_ConcurrentAccess(t *testing.T
15871585
err = json.Unmarshal(data, &statusFileData)
15881586
require.NoError(t, err)
15891587

1590-
// The final state should be one of the expected combinations
1591-
validStates := map[rt.WorkloadStatus]int{
1592-
rt.WorkloadStatusRunning: 1001,
1593-
rt.WorkloadStatusStopping: 1002,
1594-
rt.WorkloadStatusStopped: 0,
1595-
}
1588+
// The status should remain unchanged (starting) since we only updated PIDs
1589+
assert.Equal(t, rt.WorkloadStatusStarting, statusFileData.Status)
1590+
assert.Equal(t, "initializing", statusFileData.StatusContext)
1591+
1592+
// The final PID should be one of the three values we set concurrently
1593+
validPIDs := []int{1001, 1002, 1003}
1594+
assert.Contains(t, validPIDs, statusFileData.ProcessID, "PID should be one of the concurrently set values")
1595+
}
1596+
1597+
func TestFileStatusManager_ResetWorkloadPID_NonExistentWorkload(t *testing.T) {
1598+
t.Parallel()
1599+
tempDir := t.TempDir()
1600+
manager := &fileStatusManager{baseDir: tempDir}
1601+
ctx := context.Background()
1602+
1603+
// Test resetting PID for non-existent workload (should be a noop)
1604+
err := manager.ResetWorkloadPID(ctx, "test-workload")
1605+
require.NoError(t, err)
1606+
1607+
// Verify no file was created (since it's a noop)
1608+
statusFile := filepath.Join(tempDir, "test-workload.json")
1609+
require.NoFileExists(t, statusFile)
1610+
}
1611+
1612+
func TestFileStatusManager_ResetWorkloadPID_ExistingWorkload(t *testing.T) {
1613+
t.Parallel()
1614+
tempDir := t.TempDir()
1615+
manager := &fileStatusManager{baseDir: tempDir}
1616+
ctx := context.Background()
1617+
1618+
// First create a workload with a non-zero PID
1619+
err := manager.SetWorkloadStatus(ctx, "test-workload", rt.WorkloadStatusRunning, "container started")
1620+
require.NoError(t, err)
15961621

1597-
expectedPID, validStatus := validStates[statusFileData.Status]
1598-
assert.True(t, validStatus, "Final status %s should be one of the expected values", statusFileData.Status)
1599-
assert.Equal(t, expectedPID, statusFileData.ProcessID, "PID should match the expected value for status %s", statusFileData.Status)
1622+
err = manager.SetWorkloadPID(ctx, "test-workload", 12345)
1623+
require.NoError(t, err)
1624+
1625+
// Verify the PID is set to 12345
1626+
statusFile := filepath.Join(tempDir, "test-workload.json")
1627+
data, err := os.ReadFile(statusFile)
1628+
require.NoError(t, err)
1629+
1630+
var statusFileData workloadStatusFile
1631+
err = json.Unmarshal(data, &statusFileData)
1632+
require.NoError(t, err)
1633+
assert.Equal(t, 12345, statusFileData.ProcessID)
1634+
1635+
// Now reset the PID
1636+
err = manager.ResetWorkloadPID(ctx, "test-workload")
1637+
require.NoError(t, err)
1638+
1639+
// Verify the PID is now 0 and other fields are preserved
1640+
data, err = os.ReadFile(statusFile)
1641+
require.NoError(t, err)
1642+
1643+
err = json.Unmarshal(data, &statusFileData)
1644+
require.NoError(t, err)
1645+
1646+
assert.Equal(t, 0, statusFileData.ProcessID) // PID should be reset to 0
1647+
assert.Equal(t, rt.WorkloadStatusRunning, statusFileData.Status) // Status should be preserved
1648+
assert.Equal(t, "container started", statusFileData.StatusContext) // Context should be preserved
1649+
}
1650+
1651+
func TestFileStatusManager_ResetWorkloadPID_WithSlashes(t *testing.T) {
1652+
t.Parallel()
1653+
tempDir := t.TempDir()
1654+
manager := &fileStatusManager{baseDir: tempDir}
1655+
ctx := context.Background()
1656+
1657+
workloadName := testWorkloadWithSlash
1658+
1659+
// First create the workload and set a PID
1660+
err := manager.SetWorkloadStatus(ctx, workloadName, rt.WorkloadStatusRunning, "started")
1661+
require.NoError(t, err)
1662+
1663+
err = manager.SetWorkloadPID(ctx, workloadName, 9999)
1664+
require.NoError(t, err)
1665+
1666+
// Reset the PID for workload name with slashes
1667+
err = manager.ResetWorkloadPID(ctx, workloadName)
1668+
require.NoError(t, err)
1669+
1670+
// Verify file exists with slashes replaced by dashes and PID is 0
1671+
statusFile := filepath.Join(tempDir, "test-workload.json")
1672+
require.FileExists(t, statusFile)
1673+
1674+
data, err := os.ReadFile(statusFile)
1675+
require.NoError(t, err)
1676+
1677+
var statusFileData workloadStatusFile
1678+
err = json.Unmarshal(data, &statusFileData)
1679+
require.NoError(t, err)
1680+
1681+
assert.Equal(t, rt.WorkloadStatusRunning, statusFileData.Status)
1682+
assert.Equal(t, "started", statusFileData.StatusContext)
1683+
assert.Equal(t, 0, statusFileData.ProcessID) // PID should be reset to 0
16001684
}

0 commit comments

Comments
 (0)