Enhance Exec Method with Context-Aware Output Handling and Timeout Verification

- Updated the Exec method to read output with context awareness, allowing for better error handling during command execution.
- Modified the TestExecWithTimeout to ensure it correctly verifies timeout behavior and measures execution duration, improving test reliability.
- Added logic to create parent directories in WriteFile method, ensuring proper file handling within containers.
This commit is contained in:
Max 2026-01-29 19:31:19 +08:00
parent d4bed4f277
commit 55e43a7edd
2 changed files with 51 additions and 18 deletions

View file

@ -319,21 +319,35 @@ func (m *Manager) Exec(ctx context.Context, name string, cmd []string, opts *Exe
}
defer reader.Close()
// Read all output
output, err := io.ReadAll(reader)
if err != nil {
// Read output with context awareness
outputCh := make(chan []byte, 1)
errCh := make(chan error, 1)
go func() {
output, err := io.ReadAll(reader)
if err != nil {
errCh <- err
return
}
outputCh <- output
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case err := <-errCh:
return nil, fmt.Errorf("failed to read output: %w", err)
case output := <-outputCh:
// Parse Docker multiplexed stream
// TODO: Properly demux stdout/stderr from Docker stream
stdout := string(output)
return &ExecResult{
ExitCode: 0,
Stdout: stdout,
Stderr: "",
}, nil
}
// Parse Docker multiplexed stream
// TODO: Properly demux stdout/stderr from Docker stream
stdout := string(output)
return &ExecResult{
ExitCode: 0,
Stdout: stdout,
Stderr: "",
}, nil
}
// Start starts a stopped container
@ -454,6 +468,14 @@ func (m *Manager) WriteFile(ctx context.Context, name, path string, content []by
}
cont := c.(*Container)
// Ensure parent directory exists
dir := filepath.Dir(path)
if dir != "/" && dir != "." {
if _, err := m.Exec(ctx, name, []string{"mkdir", "-p", dir}, nil); err != nil {
return fmt.Errorf("failed to create parent directory: %w", err)
}
}
// Create a tar archive with the file
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
@ -474,7 +496,7 @@ func (m *Manager) WriteFile(ctx context.Context, name, path string, content []by
}
// Copy to container
return m.dockerClient.CopyToContainer(ctx, cont.ID, filepath.Dir(path), &buf, container.CopyToContainerOptions{})
return m.dockerClient.CopyToContainer(ctx, cont.ID, dir, &buf, container.CopyToContainerOptions{})
}
// ReadFile reads content from a file in container

View file

@ -271,15 +271,26 @@ func TestExecWithTimeout(t *testing.T) {
}
defer m.Remove(ctx, container.Name)
// Execute command with very short timeout
// Execute command with short timeout (sleep 10s but timeout after 500ms)
start := time.Now()
_, err = m.Exec(ctx, container.Name, []string{"sleep", "10"}, &ExecOptions{
Timeout: 100 * time.Millisecond,
Timeout: 500 * time.Millisecond,
})
elapsed := time.Since(start)
// Should timeout
// Should timeout with context deadline exceeded
if err == nil {
t.Log("Expected timeout error, but command completed (may be fast system)")
t.Error("Expected timeout error, but command completed without error")
} else if err != context.DeadlineExceeded {
t.Logf("Got error (expected context.DeadlineExceeded): %v", err)
}
// Verify it didn't wait the full 10 seconds
if elapsed > 5*time.Second {
t.Errorf("Timeout took too long: %v (expected < 5s)", elapsed)
}
t.Logf("Timeout completed in %v", elapsed)
}
// TestFileOperations tests filesystem operations