diff options
author | Brent Baude <bbaude@redhat.com> | 2020-05-26 16:12:38 -0500 |
---|---|---|
committer | Brent Baude <bbaude@redhat.com> | 2020-05-27 12:16:39 -0500 |
commit | 8438fa4fec01142ad9f6943f6e0c429c64d951b7 (patch) | |
tree | 476706a57d94e383728e12486e3933c52baf8147 | |
parent | 89b4683cc4666f789ebc3d21453ef65e37775642 (diff) | |
download | podman-8438fa4fec01142ad9f6943f6e0c429c64d951b7.tar.gz podman-8438fa4fec01142ad9f6943f6e0c429c64d951b7.tar.bz2 podman-8438fa4fec01142ad9f6943f6e0c429c64d951b7.zip |
Add streaming ability to endpoint
Signed-off-by: Brent Baude <bbaude@redhat.com>
-rw-r--r-- | pkg/api/handlers/compat/events.go | 12 | ||||
-rw-r--r-- | pkg/api/server/register_events.go | 5 | ||||
-rw-r--r-- | pkg/bindings/system/system.go | 29 | ||||
-rw-r--r-- | pkg/bindings/test/system_test.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/abi/events.go | 5 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/events.go | 3 | ||||
-rw-r--r-- | test/e2e/common_test.go | 8 | ||||
-rw-r--r-- | test/e2e/events_test.go | 49 |
8 files changed, 72 insertions, 43 deletions
diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index 7ebfb0d1e..577ddd0a1 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -26,7 +26,10 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { Since string `schema:"since"` Until string `schema:"until"` Filters map[string][]string `schema:"filters"` - }{} + Stream bool `schema:"stream"` + }{ + Stream: true, + } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, "Failed to parse parameters", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String())) } @@ -41,9 +44,10 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { if len(query.Since) > 0 || len(query.Until) > 0 { fromStart = true } + eventChannel := make(chan *events.Event) go func() { - readOpts := events.ReadOptions{FromStart: fromStart, Stream: true, Filters: libpodFilters, EventChannel: eventChannel, Since: query.Since, Until: query.Until} + readOpts := events.ReadOptions{FromStart: fromStart, Stream: query.Stream, Filters: libpodFilters, EventChannel: eventChannel, Since: query.Since, Until: query.Until} eventsError = runtime.Events(readOpts) }() if eventsError != nil { @@ -55,7 +59,9 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { // If client disappears we need to stop listening for events go func(done <-chan struct{}) { <-done - close(eventChannel) + if _, ok := <-eventChannel; ok { + close(eventChannel) + } }(r.Context().Done()) // Headers need to be written out before turning Writer() over to json encoder diff --git a/pkg/api/server/register_events.go b/pkg/api/server/register_events.go index e909303da..2b85eb169 100644 --- a/pkg/api/server/register_events.go +++ b/pkg/api/server/register_events.go @@ -58,6 +58,11 @@ func (s *APIServer) registerEventsHandlers(r *mux.Router) error { // type: string // in: query // description: JSON encoded map[string][]string of constraints + // - name: stream + // type: boolean + // in: query + // default: true + // description: when false, do not follow events // responses: // 200: // description: returns a string of json data describing an event diff --git a/pkg/bindings/system/system.go b/pkg/bindings/system/system.go index 5348d0cfb..2cd894228 100644 --- a/pkg/bindings/system/system.go +++ b/pkg/bindings/system/system.go @@ -20,7 +20,7 @@ import ( // Events allows you to monitor libdpod related events like container creation and // removal. The events are then passed to the eventChan provided. The optional cancelChan // can be used to cancel the read of events and close down the HTTP connection. -func Events(ctx context.Context, eventChan chan (entities.Event), cancelChan chan bool, since, until *string, filters map[string][]string) error { +func Events(ctx context.Context, eventChan chan entities.Event, cancelChan chan bool, since, until *string, filters map[string][]string, stream *bool) error { conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -32,6 +32,9 @@ func Events(ctx context.Context, eventChan chan (entities.Event), cancelChan cha if until != nil { params.Set("until", *until) } + if stream != nil { + params.Set("stream", strconv.FormatBool(*stream)) + } if filters != nil { filterString, err := bindings.FiltersToString(filters) if err != nil { @@ -50,18 +53,24 @@ func Events(ctx context.Context, eventChan chan (entities.Event), cancelChan cha logrus.Error(errors.Wrap(err, "unable to close event response body")) }() } + dec := json.NewDecoder(response.Body) - for { - e := entities.Event{} - if err := dec.Decode(&e); err != nil { - if err == io.EOF { - break - } - return errors.Wrap(err, "unable to decode event response") + for err = (error)(nil); err == nil; { + var e = entities.Event{} + err = dec.Decode(&e) + if err == nil { + eventChan <- e } - eventChan <- e } - return nil + close(eventChan) + switch { + case err == nil: + return nil + case errors.Is(err, io.EOF): + return nil + default: + return errors.Wrap(err, "unable to decode event response") + } } // Prune removes all unused system data. diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go index 27ab2f555..dd3778754 100644 --- a/pkg/bindings/test/system_test.go +++ b/pkg/bindings/test/system_test.go @@ -47,13 +47,13 @@ var _ = Describe("Podman system", func() { } }() go func() { - system.Events(bt.conn, eChan, cancelChan, nil, nil, nil) + system.Events(bt.conn, eChan, cancelChan, nil, nil, nil, bindings.PFalse) }() _, err := bt.RunTopContainer(nil, nil, nil) Expect(err).To(BeNil()) cancelChan <- true - Expect(len(messages)).To(BeNumerically("==", 3)) + Expect(len(messages)).To(BeNumerically("==", 5)) }) It("podman system prune - pod,container stopped", func() { diff --git a/pkg/domain/infra/abi/events.go b/pkg/domain/infra/abi/events.go index 20773cdce..7ec9db369 100644 --- a/pkg/domain/infra/abi/events.go +++ b/pkg/domain/infra/abi/events.go @@ -5,12 +5,9 @@ import ( "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/pkg/domain/entities" - "github.com/sirupsen/logrus" ) func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptions) error { readOpts := events.ReadOptions{FromStart: opts.FromStart, Stream: opts.Stream, Filters: opts.Filter, EventChannel: opts.EventChan, Since: opts.Since, Until: opts.Until} - err := ic.Libpod.Events(readOpts) - logrus.Error(err) - return err + return ic.Libpod.Events(readOpts) } diff --git a/pkg/domain/infra/tunnel/events.go b/pkg/domain/infra/tunnel/events.go index 93da3aeb4..6a08a1f85 100644 --- a/pkg/domain/infra/tunnel/events.go +++ b/pkg/domain/infra/tunnel/events.go @@ -25,6 +25,7 @@ func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptio for e := range binChan { opts.EventChan <- entities.ConvertToLibpodEvent(e) } + close(opts.EventChan) }() - return system.Events(ic.ClientCxt, binChan, nil, &opts.Since, &opts.Until, filters) + return system.Events(ic.ClientCxt, binChan, nil, &opts.Since, &opts.Until, filters, &opts.Stream) } diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 80ee83f44..e12edad49 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -21,6 +21,7 @@ import ( "github.com/containers/storage/pkg/reexec" "github.com/containers/storage/pkg/stringid" jsoniter "github.com/json-iterator/go" + "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" @@ -573,3 +574,10 @@ func (p *PodmanTestIntegration) CreateSeccompJson(in []byte) (string, error) { } return jsonFile, nil } + +func SkipIfNotFedora() { + info := GetHostDistributionInfo() + if info.Distribution != "fedora" { + ginkgo.Skip("Test can only run on Fedora") + } +} diff --git a/test/e2e/events_test.go b/test/e2e/events_test.go index 460554b77..8c496872f 100644 --- a/test/e2e/events_test.go +++ b/test/e2e/events_test.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "strings" + "time" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" @@ -24,23 +25,26 @@ var _ = Describe("Podman events", func() { os.Exit(1) } podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() podmanTest.SeedImages() }) AfterEach(func() { podmanTest.Cleanup() f := CurrentGinkgoTestDescription() - timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()) - GinkgoWriter.Write([]byte(timedResult)) - + processTestResult(f) }) // For most, all, of these tests we do not "live" test following a log because it may make a fragile test // system more complex. Instead we run the "events" and then verify that the events are processed correctly. // Perhaps a future version of this test would put events in a go func and send output back over a channel // while events occur. + + // These tests are only known to work on Fedora ATM. Other distributions + // will be skipped. It("podman events", func() { - Skip("need to verify images have correct packages for journald") + SkipIfRootless() + SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false"}) @@ -49,7 +53,8 @@ var _ = Describe("Podman events", func() { }) It("podman events with an event filter", func() { - Skip("need to verify images have correct packages for journald") + SkipIfRootless() + SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "event=start"}) @@ -59,11 +64,14 @@ var _ = Describe("Podman events", func() { }) It("podman events with an event filter and container=cid", func() { - Skip("need to verify images have correct packages for journald") + Skip("Does not work on v2") + SkipIfRootless() + SkipIfNotFedora() _, ec, cid := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) _, ec2, cid2 := podmanTest.RunLsContainer("") Expect(ec2).To(Equal(0)) + time.Sleep(5 * time.Second) result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "event=start", "--filter", fmt.Sprintf("container=%s", cid)}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) @@ -72,7 +80,8 @@ var _ = Describe("Podman events", func() { }) It("podman events with a type and filter container=id", func() { - Skip("need to verify images have correct packages for journald") + SkipIfRootless() + SkipIfNotFedora() _, ec, cid := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "type=pod", "--filter", fmt.Sprintf("container=%s", cid)}) @@ -82,7 +91,8 @@ var _ = Describe("Podman events", func() { }) It("podman events with a type", func() { - Skip("need to verify images have correct packages for journald") + SkipIfRootless() + SkipIfNotFedora() setup := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:foobarpod", ALPINE, "top"}) setup.WaitWithDefaultTimeout() stop := podmanTest.Podman([]string{"pod", "stop", "foobarpod"}) @@ -97,7 +107,8 @@ var _ = Describe("Podman events", func() { }) It("podman events --since", func() { - Skip("need to verify images have correct packages for journald") + SkipIfRootless() + SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--since", "1m"}) @@ -106,7 +117,8 @@ var _ = Describe("Podman events", func() { }) It("podman events --until", func() { - Skip("need to verify images have correct packages for journald") + SkipIfRootless() + SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) test := podmanTest.Podman([]string{"events", "--help"}) @@ -118,37 +130,28 @@ var _ = Describe("Podman events", func() { }) It("podman events format", func() { - Skip(v2remotefail) - info := GetHostDistributionInfo() - if info.Distribution != "fedora" { - Skip("need to verify images have correct packages for journald") - } + SkipIfRootless() + SkipIfNotFedora() _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) test := podmanTest.Podman([]string{"events", "--stream=false", "--format", "json"}) test.WaitWithDefaultTimeout() - fmt.Println(test.OutputToStringArray()) jsonArr := test.OutputToStringArray() Expect(len(jsonArr)).To(Not(BeZero())) eventsMap := make(map[string]string) err := json.Unmarshal([]byte(jsonArr[0]), &eventsMap) - if err != nil { - os.Exit(1) - } + Expect(err).To(BeNil()) _, exist := eventsMap["Status"] Expect(exist).To(BeTrue()) Expect(test.ExitCode()).To(BeZero()) test = podmanTest.Podman([]string{"events", "--stream=false", "--format", "{{json.}}"}) test.WaitWithDefaultTimeout() - fmt.Println(test.OutputToStringArray()) jsonArr = test.OutputToStringArray() Expect(len(jsonArr)).To(Not(BeZero())) eventsMap = make(map[string]string) err = json.Unmarshal([]byte(jsonArr[0]), &eventsMap) - if err != nil { - os.Exit(1) - } + Expect(err).To(BeNil()) _, exist = eventsMap["Status"] Expect(exist).To(BeTrue()) Expect(test.ExitCode()).To(BeZero()) |