mirror of
https://github.com/containers/podman.git
synced 2025-10-16 02:32:55 +08:00

Do not ignore ErrUnexpectedEOF from DemuxHeader(), if we fail to parse the header there must have been a clear protocal error between client and server which should be reported and not silently ignored. I wonder ig this might explain why we have missing remote exec/attach output without any error, it is possible we are eating some internal errors due this. Commit ba8eba83ef added the ErrUnexpectedEOF check but without any explanation why that would be needed. The tests from that commit pass without it locally but not in CI. With some debugging best I found the issue is actually a test bug. The channel is not consumed until it is closed which means the main test exists before the log reading goroutine is done. And if the main test exists the first step it does is to kill the podman service which then can trigger the ErrUnexpectedEOF server on the still open http connection and thus the test case failed there. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
71 lines
1.6 KiB
Go
71 lines
1.6 KiB
Go
package containers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/containers/podman/v5/pkg/bindings"
|
|
)
|
|
|
|
// Logs obtains a container's logs given the options provided. The logs are then sent to the
|
|
// stdout|stderr channels as strings.
|
|
func Logs(ctx context.Context, nameOrID string, options *LogOptions, stdoutChan, stderrChan chan string) error {
|
|
if options == nil {
|
|
options = new(LogOptions)
|
|
}
|
|
conn, err := bindings.GetClient(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
params, err := options.ToParams()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// The API requires either stdout|stderr be used. If neither are specified, we specify stdout
|
|
if options.Stdout == nil && options.Stderr == nil {
|
|
params.Set("stdout", strconv.FormatBool(true))
|
|
}
|
|
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/containers/%s/logs", params, nil, nameOrID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer response.Body.Close()
|
|
|
|
// if not success handle and return possible error message
|
|
if !response.IsSuccess() && !response.IsInformational() {
|
|
return response.Process(nil)
|
|
}
|
|
|
|
buffer := make([]byte, 1024)
|
|
for {
|
|
fd, l, err := DemuxHeader(response.Body, buffer)
|
|
if err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
frame, err := DemuxFrame(response.Body, buffer, l)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch fd {
|
|
case 0:
|
|
stdoutChan <- string(frame)
|
|
case 1:
|
|
stdoutChan <- string(frame)
|
|
case 2:
|
|
stderrChan <- string(frame)
|
|
case 3:
|
|
return errors.New("from service in stream: " + string(frame))
|
|
default:
|
|
return fmt.Errorf("unrecognized input header: %d", fd)
|
|
}
|
|
}
|
|
}
|