Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions components/execd/pkg/runtime/command_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package runtime
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"path/filepath"
Expand Down Expand Up @@ -60,20 +61,32 @@ func (c *Controller) storeCommandKernel(sessionID string, kernel *commandKernel)
}

// stdLogDescriptor creates temporary files for capturing command output.
// It ensures the temp directory exists before opening files, so that commands
// continue to work even after the /tmp directory has been removed and recreated.
func (c *Controller) stdLogDescriptor(session string) (io.WriteCloser, io.WriteCloser, error) {
logDir := os.TempDir()
if err := os.MkdirAll(logDir, 0o755); err != nil {
return nil, nil, fmt.Errorf("failed to create temp dir %s: %w", logDir, err)
}

stdout, err := os.OpenFile(c.stdoutFileName(session), os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)
if err != nil {
return nil, nil, err
}
stderr, err := os.OpenFile(c.stderrFileName(session), os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)
if err != nil {
stdout.Close()
return nil, nil, err
}

return stdout, stderr, nil
}

func (c *Controller) combinedOutputDescriptor(session string) (io.WriteCloser, error) {
logDir := os.TempDir()
if err := os.MkdirAll(logDir, 0o755); err != nil {
return nil, fmt.Errorf("failed to create temp dir %s: %w", logDir, err)
}
return os.OpenFile(c.combinedOutputFileName(session), os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)
}

Expand Down
57 changes: 57 additions & 0 deletions components/execd/pkg/runtime/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,60 @@ func TestRunCommand_Error(t *testing.T) {
t.Fatalf("unexpected error payload: %+v", gotErr)
}
}

// TestStdLogDescriptor_AutoCreatesTempDir verifies that stdLogDescriptor
// recreates the temp directory when it has been deleted, rather than failing.
// Regression test for https://github.com/alibaba/OpenSandbox/issues/400.
func TestStdLogDescriptor_AutoCreatesTempDir(t *testing.T) {
if goruntime.GOOS == "windows" {
t.Skip("TMPDIR env var has no effect on Windows")
}

// Point os.TempDir() at a path that does not yet exist.
missingDir := filepath.Join(t.TempDir(), "deleted_tmp")
t.Setenv("TMPDIR", missingDir)

c := NewController("", "")
stdout, stderr, err := c.stdLogDescriptor("test-session")
if err != nil {
t.Fatalf("stdLogDescriptor failed with missing temp dir: %v", err)
}
stdout.Close()
stderr.Close()

// The directory must have been created.
info, err := os.Stat(missingDir)
if err != nil {
t.Fatalf("expected temp dir to be created, stat error: %v", err)
}
if !info.IsDir() {
t.Fatalf("expected %s to be a directory", missingDir)
}
}

// TestCombinedOutputDescriptor_AutoCreatesTempDir verifies that
// combinedOutputDescriptor also recreates the temp directory when missing.
// Regression test for https://github.com/alibaba/OpenSandbox/issues/400.
func TestCombinedOutputDescriptor_AutoCreatesTempDir(t *testing.T) {
if goruntime.GOOS == "windows" {
t.Skip("TMPDIR env var has no effect on Windows")
}

missingDir := filepath.Join(t.TempDir(), "deleted_tmp")
t.Setenv("TMPDIR", missingDir)

c := NewController("", "")
f, err := c.combinedOutputDescriptor("test-session")
if err != nil {
t.Fatalf("combinedOutputDescriptor failed with missing temp dir: %v", err)
}
f.Close()

info, err := os.Stat(missingDir)
if err != nil {
t.Fatalf("expected temp dir to be created, stat error: %v", err)
}
if !info.IsDir() {
t.Fatalf("expected %s to be a directory", missingDir)
}
}
Loading