pkg/domain: return an error instead of nil on remote event parse failure

In remote mode `podman events --format json` could print the literal "null"
instead of an event object. The remote client converts each event received
from the server with ConvertToLibpodEvent, which returned nil when it could
not parse the server result (an unknown status or type, or an invalid
containerExitCode). The tunnel forwarded that nil as an event with no error
set, so the CLI marshalled a nil event and printed "null".

Return a descriptive error from ConvertToLibpodEvent and send it on the event
channel, which the CLI already handles, instead of forwarding a nil event.

Signed-off-by: ROKUMATE <rohitkumawat0110@gmail.com>
This commit is contained in:
ROKUMATE 2026-06-30 18:07:08 +05:30
parent f6afcfaf26
commit 3d9e8fd2d3
3 changed files with 86 additions and 6 deletions

View file

@ -1,6 +1,7 @@
package entities
import (
"fmt"
"strconv"
"time"
@ -12,22 +13,22 @@ import (
type Event = types.Event
// ConvertToLibpodEvent converts an entities event to a libpod one.
func ConvertToLibpodEvent(e Event) *libpodEvents.Event {
func ConvertToLibpodEvent(e Event) (*libpodEvents.Event, error) {
var exitCode int
if ec, ok := e.Actor.Attributes["containerExitCode"]; ok {
var err error
exitCode, err = strconv.Atoi(ec)
if err != nil {
return nil
return nil, fmt.Errorf("parsing containerExitCode %q: %w", ec, err)
}
}
status, err := libpodEvents.StringToStatus(string(e.Action))
if err != nil {
return nil
return nil, err
}
t, err := libpodEvents.StringToType(string(e.Type))
if err != nil {
return nil
return nil, err
}
var (
oomKilled bool
@ -71,7 +72,7 @@ func ConvertToLibpodEvent(e Event) *libpodEvents.Event {
if hasOOM {
newEvent.OOMKilled = &oomKilled
}
return newEvent
return newEvent, nil
}
// ConvertToEntitiesEvent converts a libpod event to an entities one.

View file

@ -0,0 +1,74 @@
package entities
import (
"testing"
"time"
dockerEvents "github.com/moby/moby/api/types/events"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
libpodEvents "go.podman.io/podman/v6/libpod/events"
)
func newTestEvent(typ, action string, attrs map[string]string) Event {
if attrs == nil {
attrs = map[string]string{}
}
return Event{
Message: dockerEvents.Message{
Type: dockerEvents.Type(typ),
Action: dockerEvents.Action(action),
Actor: dockerEvents.Actor{ID: "abc123", Attributes: attrs},
TimeNano: 1700000000000000000,
},
}
}
func TestConvertToLibpodEvent(t *testing.T) {
e := newTestEvent("container", "start", map[string]string{
"image": "alpine",
"name": "c1",
"network": "podman1",
"podId": "pod123",
"containerExitCode": "137",
"custom": "value",
})
event, err := ConvertToLibpodEvent(e)
require.NoError(t, err)
require.NotNil(t, event)
assert.Equal(t, libpodEvents.Container, event.Type)
assert.Equal(t, libpodEvents.Start, event.Status)
assert.Equal(t, "abc123", event.ID)
assert.Equal(t, "alpine", event.Image)
assert.Equal(t, "c1", event.Name)
assert.Equal(t, "podman1", event.Network)
assert.Equal(t, time.Unix(0, 1700000000000000000), event.Time)
require.NotNil(t, event.ContainerExitCode)
assert.Equal(t, 137, *event.ContainerExitCode)
assert.Equal(t, "pod123", event.Details.PodID)
assert.Equal(t, "value", event.Details.Attributes["custom"])
}
// Previously a server result that failed to parse was silently turned into a
// nil event, which surfaced to the user as the literal JSON "null". It must now
// return an error instead. See https://github.com/containers/podman/issues/28325
func TestConvertToLibpodEventReturnsError(t *testing.T) {
tests := []struct {
name string
typ string
action string
attrs map[string]string
}{
{name: "unknown status", typ: "container", action: "bogus-status"},
{name: "empty action", typ: "container", action: ""},
{name: "empty type", typ: "", action: "start"},
{name: "invalid containerExitCode", typ: "container", action: "start", attrs: map[string]string{"containerExitCode": "not-a-number"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event, err := ConvertToLibpodEvent(newTestEvent(tt.typ, tt.action, tt.attrs))
assert.Error(t, err)
assert.Nil(t, event)
})
}
}

View file

@ -24,7 +24,12 @@ func (ic *ContainerEngine) Events(_ context.Context, opts entities.EventsOptions
binChan := make(chan entities.Event)
go func() {
for e := range binChan {
opts.EventChan <- events.ReadResult{Event: entities.ConvertToLibpodEvent(e)}
event, err := entities.ConvertToLibpodEvent(e)
if err != nil {
opts.EventChan <- events.ReadResult{Error: fmt.Errorf("converting event from server: %w", err)}
continue
}
opts.EventChan <- events.ReadResult{Event: event}
}
close(opts.EventChan)
}()