properly implement pull-error event status

Commit 03f6589f3 added basic support for pull-error event from libimage
but it contains several problems:
1. storing the error as error type prevents it from being unmarshalled,
   thus change it to a string
2. the error was never propagated from the libimage event to the podman
   event struct
3. the error message was not wired into the cli and API

This commit fixes these problems.

Fixes #21458

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Paul Holzinger
2024-03-04 18:36:07 +01:00
parent 3e1d2ab874
commit 9ee96a9569
10 changed files with 57 additions and 24 deletions

View File

@ -1420,10 +1420,10 @@ func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete strin
events.Exited.String(), events.Export.String(), events.Import.String(), events.Init.String(), events.Kill.String(), events.Exited.String(), events.Export.String(), events.Import.String(), events.Init.String(), events.Kill.String(),
events.LoadFromArchive.String(), events.Mount.String(), events.NetworkConnect.String(), events.LoadFromArchive.String(), events.Mount.String(), events.NetworkConnect.String(),
events.NetworkDisconnect.String(), events.Pause.String(), events.Prune.String(), events.Pull.String(), events.NetworkDisconnect.String(), events.Pause.String(), events.Prune.String(), events.Pull.String(),
events.Push.String(), events.Refresh.String(), events.Remove.String(), events.Rename.String(), events.PullError.String(), events.Push.String(), events.Refresh.String(), events.Remove.String(),
events.Renumber.String(), events.Restart.String(), events.Restore.String(), events.Save.String(), events.Rename.String(), events.Renumber.String(), events.Restart.String(), events.Restore.String(),
events.Start.String(), events.Stop.String(), events.Sync.String(), events.Tag.String(), events.Unmount.String(), events.Save.String(), events.Start.String(), events.Stop.String(), events.Sync.String(), events.Tag.String(),
events.Unpause.String(), events.Untag.String(), events.Unmount.String(), events.Unpause.String(), events.Untag.String(),
}, cobra.ShellCompDirectiveNoFileComp }, cobra.ShellCompDirectiveNoFileComp
} }
eventTypes := func(_ string) ([]string, cobra.ShellCompDirective) { eventTypes := func(_ string) ([]string, cobra.ShellCompDirective) {

View File

@ -71,6 +71,8 @@ type Event struct {
Type events.Type Type events.Type
// Health status of the current container // Health status of the current container
HealthStatus string `json:"health_status,omitempty"` HealthStatus string `json:"health_status,omitempty"`
// Error code for certain events involving errors.
Error string `json:",omitempty"`
events.Details events.Details
} }
@ -88,6 +90,7 @@ func newEventFromLibpodEvent(e *events.Event) Event {
HealthStatus: e.HealthStatus, HealthStatus: e.HealthStatus,
Details: e.Details, Details: e.Details,
TimeNano: e.Time.UnixNano(), TimeNano: e.Time.UnixNano(),
Error: e.Error,
} }
} }

View File

@ -61,6 +61,7 @@ The *image* event type reports the following statuses:
* loadFromArchive, * loadFromArchive,
* mount * mount
* pull * pull
* pull-error
* push * push
* remove * remove
* save * save
@ -104,21 +105,22 @@ In the case where an ID is used, the ID may be in its full or shortened form. T
Format the output to JSON Lines or using the given Go template. Format the output to JSON Lines or using the given Go template.
| **Placeholder** | **Description** | | **Placeholder** | **Description** |
|-------------------------|----------------------------------------------- ---| | --------------------- | -------------------------------------------------------------------- |
| .Attributes ... | created_at, _by, labels, and more (map[]) | | .Attributes ... | created_at, _by, labels, and more (map[]) |
| .ContainerExitCode | Exit code (int) | | .ContainerExitCode | Exit code (int) |
| .ContainerInspectData | Payload of the container's inspect | | .ContainerInspectData | Payload of the container's inspect |
| .HealthStatus | Health Status (string) | | .Error | Error message in case the event status is an error (e.g. pull-error) |
| .ID | Container ID (full 64-bit SHA) | | .HealthStatus | Health Status (string) |
| .Image | Name of image being run (string) | | .ID | Container ID (full 64-bit SHA) |
| .Name | Container name (string) | | .Image | Name of image being run (string) |
| .Network | Name of network being used (string) | | .Name | Container name (string) |
| .PodID | ID of pod associated with container, if any | | .Network | Name of network being used (string) |
| .Status | Event status (e.g., create, start, died, ...) | | .PodID | ID of pod associated with container, if any |
| .Time | Event timestamp (string) | | .Status | Event status (e.g., create, start, died, ...) |
| .TimeNano | Event timestamp with nanosecond precision (int64) | | .Time | Event timestamp (string) |
| .Type | Event type (e.g., image, container, pod, ...) | | .TimeNano | Event timestamp with nanosecond precision (int64) |
| .Type | Event type (e.g., image, container, pod, ...) |
#### **--help** #### **--help**

View File

@ -42,7 +42,7 @@ type Event struct {
// Health status of the current container // Health status of the current container
HealthStatus string `json:"health_status,omitempty"` HealthStatus string `json:"health_status,omitempty"`
// Error code for certain events involving errors. // Error code for certain events involving errors.
Error error `json:"error,omitempty"` Error string `json:"error,omitempty"`
Details Details
} }

View File

@ -90,6 +90,9 @@ func (e *Event) ToHumanReadable(truncate bool) string {
humanFormat = fmt.Sprintf("%s %s %s %s (container=%s, name=%s)", e.Time, e.Type, e.Status, id, id, e.Network) humanFormat = fmt.Sprintf("%s %s %s %s (container=%s, name=%s)", e.Time, e.Type, e.Status, id, id, e.Network)
case Image: case Image:
humanFormat = fmt.Sprintf("%s %s %s %s %s", e.Time, e.Type, e.Status, id, e.Name) humanFormat = fmt.Sprintf("%s %s %s %s %s", e.Time, e.Type, e.Status, id, e.Name)
if e.Error != "" {
humanFormat += " " + e.Error
}
case System: case System:
if e.Name != "" { if e.Name != "" {
humanFormat = fmt.Sprintf("%s %s %s %s", e.Time, e.Type, e.Status, e.Name) humanFormat = fmt.Sprintf("%s %s %s %s", e.Time, e.Type, e.Status, e.Name)

View File

@ -43,8 +43,8 @@ func (e EventJournalD) Write(ee Event) error {
case Image: case Image:
m["PODMAN_NAME"] = ee.Name m["PODMAN_NAME"] = ee.Name
m["PODMAN_ID"] = ee.ID m["PODMAN_ID"] = ee.ID
if ee.Error != nil { if ee.Error != "" {
m["ERROR"] = ee.Error.Error() m["ERROR"] = ee.Error
} }
case Container, Pod: case Container, Pod:
m["PODMAN_IMAGE"] = ee.Image m["PODMAN_IMAGE"] = ee.Image
@ -231,8 +231,8 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) {
newEvent.Network = entry.Fields["PODMAN_NETWORK_NAME"] newEvent.Network = entry.Fields["PODMAN_NETWORK_NAME"]
case Image: case Image:
newEvent.ID = entry.Fields["PODMAN_ID"] newEvent.ID = entry.Fields["PODMAN_ID"]
if _, ok := entry.Fields["ERROR"]; ok { if val, ok := entry.Fields["ERROR"]; ok {
newEvent.Error = errors.New(entry.Fields["ERROR"]) newEvent.Error = val
} }
} }
return &newEvent, nil return &newEvent, nil

View File

@ -737,6 +737,9 @@ func (r *Runtime) libimageEvents() {
Time: libimageEvent.Time, Time: libimageEvent.Time,
Type: events.Image, Type: events.Image,
} }
if libimageEvent.Error != nil {
e.Error = libimageEvent.Error.Error()
}
if err := r.eventer.Write(e); err != nil { if err := r.eventer.Write(e); err != nil {
logrus.Errorf("Unable to write image event: %q", err) logrus.Errorf("Unable to write image event: %q", err)
} }

View File

@ -33,11 +33,13 @@ func ConvertToLibpodEvent(e Event) *libpodEvents.Event {
name := e.Actor.Attributes["name"] name := e.Actor.Attributes["name"]
network := e.Actor.Attributes["network"] network := e.Actor.Attributes["network"]
podID := e.Actor.Attributes["podId"] podID := e.Actor.Attributes["podId"]
errorString := e.Actor.Attributes["error"]
details := e.Actor.Attributes details := e.Actor.Attributes
delete(details, "image") delete(details, "image")
delete(details, "name") delete(details, "name")
delete(details, "network") delete(details, "network")
delete(details, "podId") delete(details, "podId")
delete(details, "error")
delete(details, "containerExitCode") delete(details, "containerExitCode")
return &libpodEvents.Event{ return &libpodEvents.Event{
ContainerExitCode: &exitCode, ContainerExitCode: &exitCode,
@ -49,6 +51,7 @@ func ConvertToLibpodEvent(e Event) *libpodEvents.Event {
Time: time.Unix(0, e.TimeNano), Time: time.Unix(0, e.TimeNano),
Type: t, Type: t,
HealthStatus: e.HealthStatus, HealthStatus: e.HealthStatus,
Error: errorString,
Details: libpodEvents.Details{ Details: libpodEvents.Details{
PodID: podID, PodID: podID,
Attributes: details, Attributes: details,
@ -71,6 +74,9 @@ func ConvertToEntitiesEvent(e libpodEvents.Event) *types.Event {
if e.Network != "" { if e.Network != "" {
attributes["network"] = e.Network attributes["network"] = e.Network
} }
if e.Error != "" {
attributes["error"] = e.Error
}
message := dockerEvents.Message{ message := dockerEvents.Message{
// Compatibility with clients that still look for deprecated API elements // Compatibility with clients that still look for deprecated API elements
Status: e.Status.String(), Status: e.Status.String(),

View File

@ -343,4 +343,14 @@ t GET libpod/images/no-alias-for-sure/resolve 200 \
t GET libpod/images/noCAPITALcharAllowed/resolve 400 \ t GET libpod/images/noCAPITALcharAllowed/resolve 400 \
.cause="repository name must be lowercase" .cause="repository name must be lowercase"
START=$(date +%s.%N)
# test pull-error API response
podman pull --retry 0 localhost:5000/idonotexist || true
t GET "libpod/events?stream=false&since=$START" 200 \
.status=pull-error \
.Action=pull-error \
.Actor.Attributes.name="localhost:5000/idonotexist" \
.Actor.Attributes.error~".*connection refused"
# vim: filetype=sh # vim: filetype=sh

View File

@ -4,6 +4,7 @@
# #
load helpers load helpers
load helpers.network
# bats test_tags=distro-integration # bats test_tags=distro-integration
@test "events with a filter by label" { @test "events with a filter by label" {
@ -57,12 +58,15 @@ load helpers
t0=$(date --iso-8601=seconds) t0=$(date --iso-8601=seconds)
tag=registry.com/$(random_string 10 | tr A-Z a-z) tag=registry.com/$(random_string 10 | tr A-Z a-z)
bogus_image="localhost:$(random_free_port)/bogus"
# Force using the file backend since the journal backend is eating events # Force using the file backend since the journal backend is eating events
# (see containers/podman/pull/10219#issuecomment-842325032). # (see containers/podman/pull/10219#issuecomment-842325032).
run_podman --events-backend=file push $IMAGE dir:$pushedDir run_podman --events-backend=file push $IMAGE dir:$pushedDir
run_podman --events-backend=file save $IMAGE -o $tarball run_podman --events-backend=file save $IMAGE -o $tarball
run_podman --events-backend=file load -i $tarball run_podman --events-backend=file load -i $tarball
run_podman --events-backend=file pull docker-archive:$tarball run_podman --events-backend=file pull docker-archive:$tarball
run_podman 125 --events-backend=file pull --retry 0 $bogus_image
run_podman --events-backend=file tag $IMAGE $tag run_podman --events-backend=file tag $IMAGE $tag
run_podman --events-backend=file untag $IMAGE $tag run_podman --events-backend=file untag $IMAGE $tag
run_podman --events-backend=file tag $IMAGE $tag run_podman --events-backend=file tag $IMAGE $tag
@ -74,6 +78,7 @@ load helpers
.*image save $imageID $tarball .*image save $imageID $tarball
.*image loadfromarchive $imageID $tarball .*image loadfromarchive $imageID $tarball
.*image pull $imageID docker-archive:$tarball .*image pull $imageID docker-archive:$tarball
.*image pull-error $bogus_image .*pinging container registry localhost.*connection refused
.*image tag $imageID $tag .*image tag $imageID $tag
.*image untag $imageID $tag:latest .*image untag $imageID $tag:latest
.*image tag $imageID $tag .*image tag $imageID $tag
@ -87,6 +92,7 @@ load helpers
"save--$tarball" "save--$tarball"
"loadfromarchive--$tarball" "loadfromarchive--$tarball"
"pull--docker-archive:$tarball" "pull--docker-archive:$tarball"
"pull-error--$bogus_image"
"tag--$tag" "tag--$tag"
"untag--$tag:latest" "untag--$tag:latest"
"tag--$tag" "tag--$tag"