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:
parent
d4bed4f277
commit
55e43a7edd
2 changed files with 51 additions and 18 deletions
|
|
@ -319,12 +319,25 @@ func (m *Manager) Exec(ctx context.Context, name string, cmd []string, opts *Exe
|
||||||
}
|
}
|
||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
|
|
||||||
// Read all output
|
// Read output with context awareness
|
||||||
|
outputCh := make(chan []byte, 1)
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
output, err := io.ReadAll(reader)
|
output, err := io.ReadAll(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read output: %w", err)
|
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
|
// Parse Docker multiplexed stream
|
||||||
// TODO: Properly demux stdout/stderr from Docker stream
|
// TODO: Properly demux stdout/stderr from Docker stream
|
||||||
stdout := string(output)
|
stdout := string(output)
|
||||||
|
|
@ -334,6 +347,7 @@ func (m *Manager) Exec(ctx context.Context, name string, cmd []string, opts *Exe
|
||||||
Stdout: stdout,
|
Stdout: stdout,
|
||||||
Stderr: "",
|
Stderr: "",
|
||||||
}, nil
|
}, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts a stopped container
|
// Start starts a stopped container
|
||||||
|
|
@ -454,6 +468,14 @@ func (m *Manager) WriteFile(ctx context.Context, name, path string, content []by
|
||||||
}
|
}
|
||||||
cont := c.(*Container)
|
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
|
// Create a tar archive with the file
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
tw := tar.NewWriter(&buf)
|
tw := tar.NewWriter(&buf)
|
||||||
|
|
@ -474,7 +496,7 @@ func (m *Manager) WriteFile(ctx context.Context, name, path string, content []by
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy to container
|
// 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
|
// ReadFile reads content from a file in container
|
||||||
|
|
|
||||||
|
|
@ -271,15 +271,26 @@ func TestExecWithTimeout(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer m.Remove(ctx, container.Name)
|
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{
|
_, 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 {
|
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
|
// TestFileOperations tests filesystem operations
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue