From 74fcd9fef3a31fa94f3487361b5fda1180e8cee2 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 11 Dec 2020 17:02:21 +0100 Subject: [PATCH] podman events allow future time for --until The podman events aren't read until the given timestamp if the timestamp is in the future. It just reads all events until now and exits afterwards. This does not make sense and does not match docker. The correct behavior is to read all events until the given time is reached. This fixes a bug where the wrong event log file path was used when running first time with a new storage location. Fixes #8694 This also fixes the events api endpoint which only exited when an error occurred. Otherwise it just hung after reading all events. Signed-off-by: Paul Holzinger --- libpod/events/events.go | 3 --- libpod/events/journal_linux.go | 16 ++++++++++-- libpod/events/logfile.go | 12 +++++++++ libpod/options.go | 1 + pkg/api/handlers/compat/events.go | 3 ++- test/e2e/events_test.go | 43 ++++++++++++++++++++++++++++--- 6 files changed, 68 insertions(+), 10 deletions(-) diff --git a/libpod/events/events.go b/libpod/events/events.go index 4e7267af3a..aa0401b62e 100644 --- a/libpod/events/events.go +++ b/libpod/events/events.go @@ -216,8 +216,5 @@ func (e EventLogFile) getTail(options ReadOptions) (*tail.Tail, error) { reopen = false } stream := options.Stream - if len(options.Until) > 0 { - stream = false - } return tail.TailFile(e.options.LogFilePath, tail.Config{ReOpen: reopen, Follow: stream, Location: &seek, Logger: tail.DiscardingLogger, Poll: true}) } diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go index 9a514e302f..71c638017a 100644 --- a/libpod/events/journal_linux.go +++ b/libpod/events/journal_linux.go @@ -8,6 +8,7 @@ import ( "strconv" "time" + "github.com/containers/podman/v2/pkg/util" "github.com/coreos/go-systemd/v22/journal" "github.com/coreos/go-systemd/v22/sdjournal" "github.com/pkg/errors" @@ -72,6 +73,13 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { if err != nil { return errors.Wrapf(err, "failed to generate event options") } + var untilTime time.Time + if len(options.Until) > 0 { + untilTime, err = util.ParseInputTime(options.Until) + if err != nil { + return err + } + } j, err := sdjournal.NewJournal() if err != nil { return err @@ -122,10 +130,14 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error { return errors.Wrap(err, "failed to get journal cursor") } if prevCursor == newCursor { - if len(options.Until) > 0 || !options.Stream { + if !options.Stream || (len(options.Until) > 0 && time.Now().After(untilTime)) { break } - _ = j.Wait(sdjournal.IndefiniteWait) + t := sdjournal.IndefiniteWait + if len(options.Until) > 0 { + t = time.Until(untilTime) + } + _ = j.Wait(t) continue } prevCursor = newCursor diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go index 57e38b8151..05ae3ce52f 100644 --- a/libpod/events/logfile.go +++ b/libpod/events/logfile.go @@ -4,7 +4,9 @@ import ( "context" "fmt" "os" + "time" + "github.com/containers/podman/v2/pkg/util" "github.com/containers/storage" "github.com/pkg/errors" ) @@ -51,6 +53,16 @@ func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error { if err != nil { return err } + if len(options.Until) > 0 { + untilTime, err := util.ParseInputTime(options.Until) + if err != nil { + return err + } + go func() { + time.Sleep(time.Until(untilTime)) + t.Stop() + }() + } funcDone := make(chan bool) copy := true go func() { diff --git a/libpod/options.go b/libpod/options.go index bd12c0c341..c2db13560c 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -502,6 +502,7 @@ func WithEventsLogger(logger string) RuntimeOption { } rt.config.Engine.EventsLogger = logger + rt.config.Engine.EventsLogFilePath = filepath.Join(rt.config.Engine.TmpDir, "events", "events.log") return nil } diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index f74491a8fc..c75511a4d4 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -110,6 +110,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { Until: query.Until, } errorChannel <- runtime.Events(r.Context(), readOpts) + }() var flush = func() {} @@ -130,8 +131,8 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { if err != nil { // FIXME StatusOK already sent above cannot send 500 here utils.InternalServerError(w, err) - return } + return case evt := <-eventChannel: if evt == nil { continue diff --git a/test/e2e/events_test.go b/test/e2e/events_test.go index b37bd584e5..0c7a1bd667 100644 --- a/test/e2e/events_test.go +++ b/test/e2e/events_test.go @@ -5,9 +5,11 @@ import ( "fmt" "os" "strings" + "sync" "time" . "github.com/containers/podman/v2/test/utils" + "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -115,10 +117,7 @@ var _ = Describe("Podman events", func() { SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) - test := podmanTest.Podman([]string{"events", "--help"}) - test.WaitWithDefaultTimeout() - fmt.Println(test.OutputToStringArray()) - result := podmanTest.Podman([]string{"events", "--stream=false", "--since", "1h"}) + result := podmanTest.Podman([]string{"events", "--stream=false", "--until", "1h"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(BeZero()) }) @@ -154,4 +153,40 @@ var _ = Describe("Podman events", func() { Expect(eventsMap).To(HaveKey("Status")) }) + + It("podman events --until future", func() { + name1 := stringid.GenerateNonCryptoID() + name2 := stringid.GenerateNonCryptoID() + name3 := stringid.GenerateNonCryptoID() + session := podmanTest.Podman([]string{"create", "--name", name1, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer GinkgoRecover() + defer wg.Done() + + // wait 2 seconds to be sure events is running + time.Sleep(time.Second * 2) + session = podmanTest.Podman([]string{"create", "--name", name2, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"create", "--name", name3, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }() + + // unix timestamp in 10 seconds + until := time.Now().Add(time.Second * 10).Unix() + result := podmanTest.Podman([]string{"events", "--since", "30s", "--until", fmt.Sprint(until)}) + result.Wait(11) + Expect(result.ExitCode()).To(BeZero()) + Expect(result.OutputToString()).To(ContainSubstring(name1)) + Expect(result.OutputToString()).To(ContainSubstring(name2)) + Expect(result.OutputToString()).To(ContainSubstring(name3)) + + wg.Wait() + }) })