Skip to content

Commit c201f38

Browse files
authored
Drop any code which writes to PID files (#2899)
The PID of the proxy process is now stored in the status file. The PID file code was left for backwards compatibility, and we can now safely remove it. Future PRs will drop the code which reads/deletes/migrates the PID files.
1 parent 8df77f6 commit c201f38

File tree

7 files changed

+54
-269
lines changed

7 files changed

+54
-269
lines changed

pkg/process/pid.go

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -70,45 +70,6 @@ func getPIDFilePathWithFallback(containerBaseName string) (string, error) {
7070
return newPath, nil
7171
}
7272

73-
// WritePIDFile writes a process ID to a file
74-
// For full version compatibility, it writes to both the new XDG location and the old temp location
75-
func WritePIDFile(containerBaseName string, pid int) error {
76-
// Skip PID file operations in Kubernetes runtime
77-
if runtime.IsKubernetesRuntime() {
78-
return nil
79-
}
80-
81-
pidContent := []byte(fmt.Sprintf("%d", pid))
82-
83-
// Write to the new XDG location first
84-
newPath, err := getPIDFilePath(containerBaseName)
85-
if err != nil {
86-
return fmt.Errorf("failed to get PID file path: %v", err)
87-
}
88-
89-
// Ensure the directory exists before writing
90-
if err := os.MkdirAll(filepath.Dir(newPath), 0750); err != nil {
91-
return fmt.Errorf("failed to create PID directory: %v", err)
92-
}
93-
94-
if err := os.WriteFile(newPath, pidContent, 0600); err != nil {
95-
return fmt.Errorf("failed to write PID file: %v", err)
96-
}
97-
98-
// Also write to the old temp location for backward/forward compatibility
99-
// This is best-effort - don't fail the operation
100-
oldPath := getOldPIDFilePath(containerBaseName)
101-
102-
_ = os.WriteFile(oldPath, pidContent, 0600)
103-
104-
return nil
105-
}
106-
107-
// WriteCurrentPIDFile writes the current process ID to a file
108-
func WriteCurrentPIDFile(containerBaseName string) error {
109-
return WritePIDFile(containerBaseName, os.Getpid())
110-
}
111-
11273
// ReadPIDFile reads the process ID from a file
11374
// It checks both the new XDG location and the old temp directory location
11475
// Note: containerBaseName is pre-sanitized by the caller

pkg/process/pid_test.go

Lines changed: 0 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -83,46 +83,6 @@ func TestPIDFileBackwardCompatibility(t *testing.T) {
8383
assert.Equal(t, newPID, pid, "Should read from new location when both exist")
8484
})
8585

86-
t.Run("WritePIDFile_WritesBothLocations", func(t *testing.T) {
87-
//nolint:paralleltest // File system operations require sequential execution
88-
89-
containerName := "test-container-write-both"
90-
testPID := 33333
91-
92-
// Clean up any existing files
93-
t.Cleanup(func() {
94-
// Clean up new location
95-
if newPath, err := getPIDFilePath(containerName); err == nil {
96-
require.NoError(t, os.Remove(newPath))
97-
}
98-
// Clean up old location
99-
oldPath := getOldPIDFilePath(containerName)
100-
require.NoError(t, os.Remove(oldPath))
101-
})
102-
103-
// Write PID file
104-
require.NoError(t, WritePIDFile(containerName, testPID), "Failed to write PID file")
105-
106-
// Verify it was written to new location
107-
newPath, err := getPIDFilePath(containerName)
108-
require.NoError(t, err, "Failed to get new PID file path")
109-
110-
_, err = os.Stat(newPath)
111-
assert.NoError(t, err, "PID file should exist in new location")
112-
113-
// Verify it was also written to old location for compatibility
114-
oldPath := getOldPIDFilePath(containerName)
115-
_, err = os.Stat(oldPath)
116-
assert.NoError(t, err, "PID file should also exist in old location for version compatibility")
117-
118-
// Verify both files contain the same PID
119-
newPidBytes, err := os.ReadFile(newPath)
120-
require.NoError(t, err, "Should read new location file")
121-
oldPidBytes, err := os.ReadFile(oldPath)
122-
require.NoError(t, err, "Should read old location file")
123-
assert.Equal(t, newPidBytes, oldPidBytes, "Both files should contain the same PID")
124-
})
125-
12686
//nolint:paralleltest // File system operations require sequential execution
12787
t.Run("RemovePIDFile_RemovesBothLocations", func(t *testing.T) {
12888
//nolint:paralleltest // File system operations require sequential execution
@@ -199,37 +159,6 @@ func TestPIDFileBackwardCompatibility(t *testing.T) {
199159
assert.True(t, os.IsNotExist(err), "Old PID file should be removed")
200160
})
201161

202-
//nolint:paralleltest // File system operations require sequential execution
203-
t.Run("RemovePIDFile_NewFileOnly", func(t *testing.T) {
204-
//nolint:paralleltest // File system operations require sequential execution
205-
206-
containerName := "test-container-new-only"
207-
testPID := 66666
208-
209-
// Clean up any existing files
210-
t.Cleanup(func() {
211-
// Clean up new location
212-
if newPath, err := getPIDFilePath(containerName); err == nil {
213-
// Error expected here - ignore.
214-
_ = os.Remove(newPath)
215-
}
216-
// Clean up old location
217-
oldPath := getOldPIDFilePath(containerName)
218-
// Error expected here - ignore.
219-
_ = os.Remove(oldPath)
220-
})
221-
222-
// Test removing when only new file exists
223-
require.NoError(t, WritePIDFile(containerName, testPID), "Failed to write PID file")
224-
225-
err := RemovePIDFile(containerName)
226-
assert.NoError(t, err, "Should handle removing only new file")
227-
228-
newPath, _ := getPIDFilePath(containerName)
229-
_, err = os.Stat(newPath)
230-
assert.True(t, os.IsNotExist(err), "New PID file should be removed")
231-
})
232-
233162
t.Run("getPIDFilePathWithFallback", func(t *testing.T) {
234163
//nolint:paralleltest // File system operations require sequential execution
235164

@@ -278,45 +207,6 @@ func TestPIDFileBackwardCompatibility(t *testing.T) {
278207
//nolint:paralleltest // File system operations require sequential execution
279208
func TestPIDFileOperations(t *testing.T) {
280209

281-
t.Run("WriteAndReadPIDFile", func(t *testing.T) {
282-
//nolint:paralleltest // File system operations require sequential execution
283-
284-
containerName := "test-basic-write-read"
285-
testPID := 54321
286-
287-
// Clean up before and after
288-
t.Cleanup(func() {
289-
require.NoError(t, RemovePIDFile(containerName))
290-
})
291-
292-
// Write PID
293-
require.NoError(t, WritePIDFile(containerName, testPID), "Failed to write PID file")
294-
295-
// Read PID
296-
pid, err := ReadPIDFile(containerName)
297-
require.NoError(t, err, "Failed to read PID file")
298-
assert.Equal(t, testPID, pid, "PID mismatch")
299-
})
300-
301-
t.Run("WriteCurrentPIDFile", func(t *testing.T) {
302-
//nolint:paralleltest // File system operations require sequential execution
303-
304-
containerName := "test-current-pid"
305-
306-
// Clean up before and after
307-
t.Cleanup(func() {
308-
require.NoError(t, RemovePIDFile(containerName))
309-
})
310-
311-
// Write current process PID
312-
require.NoError(t, WriteCurrentPIDFile(containerName), "Failed to write current PID file")
313-
314-
// Read and verify
315-
pid, err := ReadPIDFile(containerName)
316-
require.NoError(t, err, "Failed to read PID file")
317-
assert.Equal(t, os.Getpid(), pid, "Should match current process PID")
318-
})
319-
320210
t.Run("ReadNonExistentPIDFile", func(t *testing.T) {
321211
//nolint:paralleltest // File system operations require sequential execution
322212

@@ -390,49 +280,3 @@ func TestGetPIDFilePath(t *testing.T) {
390280
"Old PID file should have correct filename format")
391281
})
392282
}
393-
394-
//nolint:paralleltest // File system operations require sequential execution
395-
func TestPIDFileMigration(t *testing.T) {
396-
397-
//nolint:paralleltest // File system operations require sequential execution
398-
t.Run("MigrationScenario", func(t *testing.T) {
399-
//nolint:paralleltest // File system operations require sequential execution
400-
401-
containerName := "test-migration"
402-
oldPID := 99999
403-
404-
// Clean up
405-
t.Cleanup(func() {
406-
require.NoError(t, RemovePIDFile(containerName))
407-
})
408-
409-
// Simulate existing deployment with PID file in old location
410-
oldPath := getOldPIDFilePath(containerName)
411-
require.NoError(t, os.WriteFile(oldPath, []byte(fmt.Sprintf("%d", oldPID)), 0600),
412-
"Failed to create old PID file")
413-
414-
// New code should still be able to read the old PID
415-
pid, err := ReadPIDFile(containerName)
416-
require.NoError(t, err, "Should read PID from old location")
417-
assert.Equal(t, oldPID, pid, "Should read correct PID from old location")
418-
419-
// When writing a new PID, it should go to the new location
420-
newPID := 88888
421-
require.NoError(t, WritePIDFile(containerName, newPID), "Failed to write new PID")
422-
423-
// Now reading should get the new PID from the new location
424-
pid, err = ReadPIDFile(containerName)
425-
require.NoError(t, err, "Should read PID from new location")
426-
assert.Equal(t, newPID, pid, "Should read new PID from new location")
427-
428-
// Cleanup should remove both files
429-
require.NoError(t, RemovePIDFile(containerName), "Failed to remove PID files")
430-
431-
_, err = os.Stat(oldPath)
432-
assert.True(t, os.IsNotExist(err), "Old file should be removed")
433-
434-
newPath, _ := getPIDFilePath(containerName)
435-
_, err = os.Stat(newPath)
436-
assert.True(t, os.IsNotExist(err), "New file should be removed")
437-
})
438-
}

pkg/runner/runner.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,10 +342,6 @@ func (r *Runner) Run(ctx context.Context) error {
342342
logger.Infof("MCP server %s stopped", r.Config.ContainerName)
343343
}
344344

345-
// TODO: Stop writing to PID file once we migrate over to statuses.
346-
if err := process.WriteCurrentPIDFile(r.Config.BaseName); err != nil {
347-
logger.Warnf("Warning: Failed to write PID file: %v", err)
348-
}
349345
if err := r.statusManager.SetWorkloadPID(ctx, r.Config.BaseName, os.Getpid()); err != nil {
350346
logger.Warnf("Warning: Failed to set workload PID: %v", err)
351347
}

pkg/workloads/manager.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -587,10 +587,6 @@ func (d *DefaultManager) RunWorkloadDetached(ctx context.Context, runConfig *run
587587
}
588588

589589
// Write the PID to a file so the stop command can kill the process
590-
// TODO: Stop writing to PID file once we migrate over to statuses fully.
591-
if err := process.WritePIDFile(runConfig.BaseName, detachedCmd.Process.Pid); err != nil {
592-
logger.Warnf("Warning: Failed to write PID file: %v", err)
593-
}
594590
if err := d.statuses.SetWorkloadPID(ctx, runConfig.BaseName, detachedCmd.Process.Pid); err != nil {
595591
logger.Warnf("Failed to set workload %s PID: %v", runConfig.BaseName, err)
596592
}

pkg/workloads/statuses/file_status_test.go

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"testing"
1313
"time"
1414

15-
"github.com/adrg/xdg"
1615
"github.com/stretchr/testify/assert"
1716
"github.com/stretchr/testify/require"
1817
"go.uber.org/mock/gomock"
@@ -1693,25 +1692,14 @@ func TestFileStatusManager_GetWorkload_PIDMigration(t *testing.T) {
16931692

16941693
tests := []struct {
16951694
name string
1696-
setupPIDFile bool
16971695
pidValue int
16981696
workloadStatus rt.WorkloadStatus
16991697
processID int
17001698
expectMigration bool
17011699
expectPIDFile bool // whether PID file should exist after operation
17021700
}{
1703-
{
1704-
name: "migrates PID when status is running and ProcessID is 0",
1705-
setupPIDFile: true,
1706-
pidValue: 12345,
1707-
workloadStatus: rt.WorkloadStatusRunning,
1708-
processID: 0,
1709-
expectMigration: true,
1710-
expectPIDFile: true, // PID file is NOT deleted (see TODO comment in migration code)
1711-
},
17121701
{
17131702
name: "no migration when status is not running",
1714-
setupPIDFile: true,
17151703
pidValue: 12345,
17161704
workloadStatus: rt.WorkloadStatusStopped,
17171705
processID: 0,
@@ -1720,7 +1708,6 @@ func TestFileStatusManager_GetWorkload_PIDMigration(t *testing.T) {
17201708
},
17211709
{
17221710
name: "no migration when ProcessID is not 0",
1723-
setupPIDFile: true,
17241711
pidValue: 12345,
17251712
workloadStatus: rt.WorkloadStatusRunning,
17261713
processID: 98765,
@@ -1729,7 +1716,6 @@ func TestFileStatusManager_GetWorkload_PIDMigration(t *testing.T) {
17291716
},
17301717
{
17311718
name: "no migration when no PID file exists",
1732-
setupPIDFile: false,
17331719
pidValue: 0,
17341720
workloadStatus: rt.WorkloadStatusRunning,
17351721
processID: 0,
@@ -1770,21 +1756,6 @@ func TestFileStatusManager_GetWorkload_PIDMigration(t *testing.T) {
17701756
err := manager.setWorkloadStatusInternal(ctx, workloadName, tt.workloadStatus, "test context", &tt.processID)
17711757
require.NoError(t, err)
17721758

1773-
// Setup PID file if needed
1774-
var pidFilePath string
1775-
if tt.setupPIDFile {
1776-
// Create PID file using the process package
1777-
err = process.WritePIDFile(workloadName, tt.pidValue)
1778-
require.NoError(t, err)
1779-
1780-
// Get the path for cleanup verification
1781-
// Copy-paste from process package to allow original function to be private.
1782-
// Since we do not have backwards compatibility requirements for PID file location,
1783-
// we can simplify the original function to a one-liner.
1784-
pidFilePath, err = xdg.DataFile(filepath.Join("toolhive", "pids", fmt.Sprintf("toolhive-%s.pid", workloadName)))
1785-
require.NoError(t, err)
1786-
}
1787-
17881759
// Call GetWorkload which should trigger migration if conditions are met
17891760
workload, err := manager.GetWorkload(ctx, workloadName)
17901761
require.NoError(t, err)
@@ -1816,21 +1787,6 @@ func TestFileStatusManager_GetWorkload_PIDMigration(t *testing.T) {
18161787

18171788
assert.Equal(t, tt.processID, statusFile.ProcessID, "PID should remain unchanged")
18181789
}
1819-
1820-
// Verify PID file existence
1821-
if tt.setupPIDFile {
1822-
_, err := os.Stat(pidFilePath)
1823-
if tt.expectPIDFile {
1824-
assert.NoError(t, err, "PID file should still exist")
1825-
} else {
1826-
assert.True(t, os.IsNotExist(err), "PID file should be deleted")
1827-
}
1828-
}
1829-
1830-
// Cleanup
1831-
if tt.setupPIDFile {
1832-
_ = process.RemovePIDFile(workloadName)
1833-
}
18341790
})
18351791
}
18361792
}
@@ -1867,14 +1823,6 @@ func TestFileStatusManager_ListWorkloads_PIDMigration(t *testing.T) {
18671823
err = manager.setWorkloadStatusInternal(ctx, workloadNoMigrate, rt.WorkloadStatusRunning, "running", &existingPID)
18681824
require.NoError(t, err)
18691825

1870-
// Create PID files for both workloads
1871-
migrationPID := 12345
1872-
err = process.WritePIDFile(workloadMigrate, migrationPID)
1873-
require.NoError(t, err)
1874-
1875-
err = process.WritePIDFile(workloadNoMigrate, 99999)
1876-
require.NoError(t, err)
1877-
18781826
// Call ListWorkloads
18791827
workloads, err := manager.ListWorkloads(ctx, true, nil)
18801828
require.NoError(t, err)
@@ -1908,7 +1856,6 @@ func TestFileStatusManager_ListWorkloads_PIDMigration(t *testing.T) {
19081856
var statusFile1 workloadStatusFile
19091857
err = json.Unmarshal(data1, &statusFile1)
19101858
require.NoError(t, err)
1911-
assert.Equal(t, migrationPID, statusFile1.ProcessID, "PID should be migrated for first workload")
19121859

19131860
// Verify no migration for second workload
19141861
statusFilePath2 := manager.getStatusFilePath(workloadNoMigrate)

0 commit comments

Comments
 (0)