From ec0668ac3e171ac9713f757cea34fe8946e548a6 Mon Sep 17 00:00:00 2001 From: Chee Hau Lim Date: Fri, 12 May 2023 02:39:53 +0200 Subject: [PATCH] libpod/events: Update event time format and add timeNano - Use unix timestamp for event time - Add a timeNano field for event time in nanoseconds Fixes: #14993 Signed-off-by: Chee Hau Lim --- cmd/podman/machine/machine.go | 4 +++- docs/source/markdown/podman-events.1.md | 29 +++++++++++++------------ libpod/events/config.go | 5 +++-- libpod/events/events.go | 18 ++++++++------- libpod/events/filters.go | 4 ++-- libpod/events/journal_linux.go | 5 +++-- libpod/events/logfile.go | 2 +- libpod/runtime.go | 11 +++++----- pkg/domain/entities/events.go | 8 +++---- test/e2e/events_test.go | 23 ++++++++++++++++++-- 10 files changed, 68 insertions(+), 41 deletions(-) diff --git a/cmd/podman/machine/machine.go b/cmd/podman/machine/machine.go index 7c13f24c58..d14dfd3f4c 100644 --- a/cmd/podman/machine/machine.go +++ b/cmd/podman/machine/machine.go @@ -147,8 +147,10 @@ func eventSockDir() (string, error) { func newMachineEvent(status events.Status, event events.Event) { openEventSock.Do(initMachineEvents) + now := time.Now() + event.Time = now.Unix() + event.TimeNano = now.UnixNano() event.Status = status - event.Time = time.Now() event.Type = events.Machine payload, err := json.Marshal(event) diff --git a/docs/source/markdown/podman-events.1.md b/docs/source/markdown/podman-events.1.md index 5de46724b3..0b9f072e1a 100644 --- a/docs/source/markdown/podman-events.1.md +++ b/docs/source/markdown/podman-events.1.md @@ -101,20 +101,21 @@ 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. -| **Placeholder** | **Description** | -|-----------------------|-----------------------------------------------| -| .Attributes | created_at, _by, labels, and more (map[]) | -| .ContainerExitCode | Exit code (int) | -| .ContainerInspectData | Payload of the container's inspect | -| .HealthStatus | Health Status (string) | -| .ID | Container ID (full 64-bit SHA) | -| .Image | Name of image being run (string) | -| .Name | Container name (string) | -| .Network | Name of network being used (string) | -| .PodID | ID of pod associated with container, if any | -| .Status | Event status (e.g., create, start, died, ...) | -| .Time | Event timestamp (string) | -| .Type | Event type (e.g., image, container, pod, ...) | +| **Placeholder** | **Description** | +|-----------------------|---------------------------------------------------| +| .Attributes | created_at, _by, labels, and more (map[]) | +| .ContainerExitCode | Exit code (int) | +| .ContainerInspectData | Payload of the container's inspect | +| .HealthStatus | Health Status (string) | +| .ID | Container ID (full 64-bit SHA) | +| .Image | Name of image being run (string) | +| .Name | Container name (string) | +| .Network | Name of network being used (string) | +| .PodID | ID of pod associated with container, if any | +| .Status | Event status (e.g., create, start, died, ...) | +| .Time | Event timestamp (int64) | +| .TimeNano | Event timestamp with nanosecond precision (int64) | +| .Type | Event type (e.g., image, container, pod, ...) | #### **--help** diff --git a/libpod/events/config.go b/libpod/events/config.go index 058b219a78..ee28847118 100644 --- a/libpod/events/config.go +++ b/libpod/events/config.go @@ -3,7 +3,6 @@ package events import ( "context" "errors" - "time" ) // EventerType ... @@ -36,7 +35,9 @@ type Event struct { // Status describes the event that occurred Status Status // Time the event occurred - Time time.Time + Time int64 `json:"time,omitempty"` + // TimeNano the event occurred in nanoseconds + TimeNano int64 `json:"timeNano,omitempty"` // Type of event that occurred Type Type // Health status of the current container diff --git a/libpod/events/events.go b/libpod/events/events.go index 2105a3b89f..2e562ee125 100644 --- a/libpod/events/events.go +++ b/libpod/events/events.go @@ -48,9 +48,11 @@ func IsValidEventer(eventer string) bool { // NewEvent creates an event struct and populates with // the given status and time. func NewEvent(status Status) Event { + now := time.Now() return Event{ - Status: status, - Time: time.Now(), + Status: status, + Time: now.Unix(), + TimeNano: now.UnixNano(), } } @@ -76,7 +78,7 @@ func (e *Event) ToHumanReadable(truncate bool) string { } switch e.Type { case Container, Pod: - humanFormat = fmt.Sprintf("%s %s %s %s (image=%s, name=%s", e.Time, e.Type, e.Status, id, e.Image, e.Name) + humanFormat = fmt.Sprintf("%s %s %s %s (image=%s, name=%s", time.Unix(0, e.TimeNano), e.Type, e.Status, id, e.Image, e.Name) if e.PodID != "" { humanFormat += fmt.Sprintf(", pod_id=%s", e.PodID) } @@ -91,17 +93,17 @@ func (e *Event) ToHumanReadable(truncate bool) string { } humanFormat += ")" case Network: - 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)", time.Unix(0, e.TimeNano), e.Type, e.Status, id, id, e.Network) 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", time.Unix(0, e.TimeNano), e.Type, e.Status, id, e.Name) case System: 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", time.Unix(0, e.TimeNano), e.Type, e.Status, e.Name) } else { - humanFormat = fmt.Sprintf("%s %s %s", e.Time, e.Type, e.Status) + humanFormat = fmt.Sprintf("%s %s %s", time.Unix(0, e.TimeNano), e.Type, e.Status) } case Volume, Machine: - humanFormat = fmt.Sprintf("%s %s %s %s", e.Time, e.Type, e.Status, e.Name) + humanFormat = fmt.Sprintf("%s %s %s %s", time.Unix(0, e.TimeNano), e.Type, e.Status, e.Name) } return humanFormat } diff --git a/libpod/events/filters.go b/libpod/events/filters.go index 9057c9b79c..d3bb7c9c93 100644 --- a/libpod/events/filters.go +++ b/libpod/events/filters.go @@ -82,13 +82,13 @@ func generateEventFilter(filter, filterValue string) (func(e *Event) bool, error func generateEventSinceOption(timeSince time.Time) func(e *Event) bool { return func(e *Event) bool { - return e.Time.After(timeSince) + return time.Unix(0, e.TimeNano).After(timeSince) } } func generateEventUntilOption(timeUntil time.Time) func(e *Event) bool { return func(e *Event) bool { - return e.Time.Before(timeUntil) + return time.Unix(0, e.TimeNano).Before(timeUntil) } } diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go index 0f472b8d89..9f530bb939 100644 --- a/libpod/events/journal_linux.go +++ b/libpod/events/journal_linux.go @@ -37,7 +37,7 @@ func (e EventJournalD) Write(ee Event) error { m["SYSLOG_IDENTIFIER"] = "podman" m["PODMAN_EVENT"] = ee.Status.String() m["PODMAN_TYPE"] = ee.Type.String() - m["PODMAN_TIME"] = ee.Time.Format(time.RFC3339Nano) + m["PODMAN_TIME"] = time.Unix(0, ee.TimeNano).Format(time.RFC3339Nano) // Add specialized information based on the podman type switch ee.Type { @@ -180,7 +180,8 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) { return nil, err } newEvent.Type = eventType - newEvent.Time = eventTime + newEvent.Time = eventTime.Unix() + newEvent.TimeNano = eventTime.UnixNano() newEvent.Status = eventStatus newEvent.Name = entry.Fields["PODMAN_NAME"] diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go index 17c93e9609..9db3bc896b 100644 --- a/libpod/events/logfile.go +++ b/libpod/events/logfile.go @@ -178,7 +178,7 @@ func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error { if err != nil { return err } - if begin && event.Time.After(readTime) { + if begin && time.Unix(0, event.TimeNano).After(readTime) { // If the rotation event happened _after_ we // started reading, we need to ignore/skip // subsequent event until the end of the diff --git a/libpod/runtime.go b/libpod/runtime.go index 98956a5e16..bcebd7b488 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -738,11 +738,12 @@ func (r *Runtime) libimageEvents() { for len(eventChannel) > 0 { libimageEvent := <-eventChannel e := events.Event{ - ID: libimageEvent.ID, - Name: libimageEvent.Name, - Status: toLibpodEventStatus(libimageEvent), - Time: libimageEvent.Time, - Type: events.Image, + ID: libimageEvent.ID, + Name: libimageEvent.Name, + Status: toLibpodEventStatus(libimageEvent), + Time: libimageEvent.Time.Unix(), + TimeNano: libimageEvent.Time.UnixNano(), + Type: events.Image, } if err := r.eventer.Write(e); err != nil { logrus.Errorf("Unable to write image event: %q", err) diff --git a/pkg/domain/entities/events.go b/pkg/domain/entities/events.go index 34a6fe0489..ebd7094a8e 100644 --- a/pkg/domain/entities/events.go +++ b/pkg/domain/entities/events.go @@ -2,7 +2,6 @@ package entities import ( "strconv" - "time" libpodEvents "github.com/containers/podman/v4/libpod/events" dockerEvents "github.com/docker/docker/api/types/events" @@ -44,7 +43,8 @@ func ConvertToLibpodEvent(e Event) *libpodEvents.Event { Image: image, Name: name, Status: status, - Time: time.Unix(0, e.TimeNano), + Time: e.Time, + TimeNano: e.TimeNano, Type: t, HealthStatus: e.HealthStatus, Details: libpodEvents.Details{ @@ -76,8 +76,8 @@ func ConvertToEntitiesEvent(e libpodEvents.Event) *Event { Attributes: attributes, }, Scope: "local", - Time: e.Time.Unix(), - TimeNano: e.Time.UnixNano(), + Time: e.Time, + TimeNano: e.TimeNano, } return &Event{ message, diff --git a/test/e2e/events_test.go b/test/e2e/events_test.go index b30fcc57e1..854b288f07 100644 --- a/test/e2e/events_test.go +++ b/test/e2e/events_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "strconv" "sync" "time" @@ -120,7 +121,10 @@ var _ = Describe("Podman events", func() { }) It("podman events format", func() { - _, ec, _ := podmanTest.RunLsContainer("") + start := time.Now() + ctrName := "testCtr" + _, ec, _ := podmanTest.RunLsContainer(ctrName) + end := time.Now() Expect(ec).To(Equal(0)) test := podmanTest.Podman([]string{"events", "--stream=false", "--format", "json"}) @@ -134,7 +138,13 @@ var _ = Describe("Podman events", func() { err := json.Unmarshal([]byte(jsonArr[0]), &event) Expect(err).ToNot(HaveOccurred()) - test = podmanTest.Podman([]string{"events", "--stream=false", "--format", "{{json.}}"}) + test = podmanTest.Podman([]string{ + "events", + "--stream=false", + "--since", strconv.FormatInt(start.Unix(), 10), + "--filter", fmt.Sprintf("container=%s", ctrName), + "--format", "{{json.}}", + }) test.WaitWithDefaultTimeout() Expect(test).To(Exit(0)) @@ -145,6 +155,15 @@ var _ = Describe("Podman events", func() { err = json.Unmarshal([]byte(jsonArr[0]), &event) Expect(err).ToNot(HaveOccurred()) + Expect(event.Time).To(BeNumerically(">=", start.Unix())) + Expect(event.Time).To(BeNumerically("<=", end.Unix())) + Expect(event.TimeNano).To(BeNumerically(">=", start.UnixNano())) + Expect(event.TimeNano).To(BeNumerically("<=", end.UnixNano())) + Expect(time.Unix(0, event.TimeNano).Unix()).To(BeEquivalentTo(event.Time)) + + date := time.Unix(0, event.TimeNano).Format("2006-01-02") + Expect(event.ToHumanReadable(false)).To(HavePrefix(date)) + test = podmanTest.Podman([]string{"events", "--stream=false", "--filter=type=container", "--format", "ID: {{.ID}}"}) test.WaitWithDefaultTimeout() Expect(test).To(Exit(0))