runtime: Map empty ReadStdout/ReadStderr response to io.EOF

After the kata-agent "drain-after-exit" change, stdout/stderr EOF is
signaled by a successful ReadStdout/ReadStderr reply with empty Data
(len==0), instead of an RPC error. However, runtime-go currently
returns (0, nil) to io.CopyBuffer() when resp.Data is empty, which
violates Go io.Reader semantics and can cause `kubectl exec` to
hang after the command output is already printed.

To avoid exec hang:
In readProcessStream(), map an empty response (len(resp.Data)==0)
into (0, io.EOF). This allows the stdout/stderr copy goroutines to
terminate, closes exitIOch, and unblocks the wait path so exec can
complete normally.

Signed-off-by: Alex Lyn <alex.lyn@antgroup.com>
This commit is contained in:
Alex Lyn
2026-01-23 11:38:30 +08:00
committed by Fabiano Fidêncio
parent ffb8a6a9c3
commit 41e8acbc5e
2 changed files with 17 additions and 6 deletions

View File

@@ -11,6 +11,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"math"
"os"
"path"
@@ -2485,11 +2486,16 @@ func (k *kataAgent) readProcessStream(containerID, processID string, data []byte
ContainerId: containerID,
ExecId: processID,
Len: uint32(len(data))})
if err == nil {
copy(data, resp.Data)
return len(resp.Data), nil
if err != nil {
return 0, err
}
return 0, err
if len(resp.Data) == 0 {
return 0, io.EOF
}
copy(data, resp.Data)
return len(resp.Data), nil
}
func (k *kataAgent) getGuestDetails(ctx context.Context, req *grpc.GuestDetailsRequest) (*grpc.GuestDetailsResponse, error) {

View File

@@ -9,6 +9,7 @@ import (
"bufio"
"context"
"fmt"
"io"
"os"
"path"
"path/filepath"
@@ -176,10 +177,14 @@ func TestKataAgentSendReq(t *testing.T) {
assert.Nil(err)
_, err = k.readProcessStdout(ctx, container, execid, []byte{})
assert.Nil(err)
if err != nil {
assert.ErrorIs(err, io.EOF)
}
_, err = k.readProcessStderr(ctx, container, execid, []byte{})
assert.Nil(err)
if err != nil {
assert.ErrorIs(err, io.EOF)
}
_, err = k.getOOMEvent(ctx)
assert.Nil(err)