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 <paul.holzinger@web.de>
This commit is contained in:
Paul Holzinger
2020-12-11 17:02:21 +01:00
parent dd954781e6
commit 74fcd9fef3
6 changed files with 68 additions and 10 deletions

View File

@ -216,8 +216,5 @@ func (e EventLogFile) getTail(options ReadOptions) (*tail.Tail, error) {
reopen = false reopen = false
} }
stream := options.Stream 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}) return tail.TailFile(e.options.LogFilePath, tail.Config{ReOpen: reopen, Follow: stream, Location: &seek, Logger: tail.DiscardingLogger, Poll: true})
} }

View File

@ -8,6 +8,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/containers/podman/v2/pkg/util"
"github.com/coreos/go-systemd/v22/journal" "github.com/coreos/go-systemd/v22/journal"
"github.com/coreos/go-systemd/v22/sdjournal" "github.com/coreos/go-systemd/v22/sdjournal"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -72,6 +73,13 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error {
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to generate event options") 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() j, err := sdjournal.NewJournal()
if err != nil { if err != nil {
return err 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") return errors.Wrap(err, "failed to get journal cursor")
} }
if prevCursor == newCursor { if prevCursor == newCursor {
if len(options.Until) > 0 || !options.Stream { if !options.Stream || (len(options.Until) > 0 && time.Now().After(untilTime)) {
break break
} }
_ = j.Wait(sdjournal.IndefiniteWait) t := sdjournal.IndefiniteWait
if len(options.Until) > 0 {
t = time.Until(untilTime)
}
_ = j.Wait(t)
continue continue
} }
prevCursor = newCursor prevCursor = newCursor

View File

@ -4,7 +4,9 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"time"
"github.com/containers/podman/v2/pkg/util"
"github.com/containers/storage" "github.com/containers/storage"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -51,6 +53,16 @@ func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error {
if err != nil { if err != nil {
return err 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) funcDone := make(chan bool)
copy := true copy := true
go func() { go func() {

View File

@ -502,6 +502,7 @@ func WithEventsLogger(logger string) RuntimeOption {
} }
rt.config.Engine.EventsLogger = logger rt.config.Engine.EventsLogger = logger
rt.config.Engine.EventsLogFilePath = filepath.Join(rt.config.Engine.TmpDir, "events", "events.log")
return nil return nil
} }

View File

@ -110,6 +110,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
Until: query.Until, Until: query.Until,
} }
errorChannel <- runtime.Events(r.Context(), readOpts) errorChannel <- runtime.Events(r.Context(), readOpts)
}() }()
var flush = func() {} var flush = func() {}
@ -130,8 +131,8 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
// FIXME StatusOK already sent above cannot send 500 here // FIXME StatusOK already sent above cannot send 500 here
utils.InternalServerError(w, err) utils.InternalServerError(w, err)
return
} }
return
case evt := <-eventChannel: case evt := <-eventChannel:
if evt == nil { if evt == nil {
continue continue

View File

@ -5,9 +5,11 @@ import (
"fmt" "fmt"
"os" "os"
"strings" "strings"
"sync"
"time" "time"
. "github.com/containers/podman/v2/test/utils" . "github.com/containers/podman/v2/test/utils"
"github.com/containers/storage/pkg/stringid"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec" . "github.com/onsi/gomega/gexec"
@ -115,10 +117,7 @@ var _ = Describe("Podman events", func() {
SkipIfNotFedora() SkipIfNotFedora()
_, ec, _ := podmanTest.RunLsContainer("") _, ec, _ := podmanTest.RunLsContainer("")
Expect(ec).To(Equal(0)) Expect(ec).To(Equal(0))
test := podmanTest.Podman([]string{"events", "--help"}) result := podmanTest.Podman([]string{"events", "--stream=false", "--until", "1h"})
test.WaitWithDefaultTimeout()
fmt.Println(test.OutputToStringArray())
result := podmanTest.Podman([]string{"events", "--stream=false", "--since", "1h"})
result.WaitWithDefaultTimeout() result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(BeZero()) Expect(result.ExitCode()).To(BeZero())
}) })
@ -154,4 +153,40 @@ var _ = Describe("Podman events", func() {
Expect(eventsMap).To(HaveKey("Status")) 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()
})
}) })