Files
moby/client/system_events.go
Sebastiaan van Stijn 839e46f97c client: remove support for API < v1.22 filter format
The format for filters changed in 93d1dd8036
(docker v1.10 / API v1.22). As part of that implementation, the daemon
would parse the new format, and fall back to parsing the old format if
this failed. This fallback was not based on API version, so any version
of the API released since would continue to accept both the legacy and
curent format.

For the client, the change in format caused a regression when connecting
to an older daemon; a `ToParamWithVersion` utility was introduced in
[docker/engine-api@81388f0] to produce the old format when the client was
connected to a docker v1.9 or older daemon, using an old API version.

Given that any version of docker 1.10 or above would support both formats,
regardless of the API version used, and API v1.22 is no longer supported,
it should be safe to assume we can drop the version-specific format in the
client. Even if the client would be using API v1.22 (or older), the format
would only be necessary for an actual docker v1.9 daemon, which would be
very unlikely, and a daemon that's 9 Years old.

[docker/engine-api@81388f0]: 81388f00dd

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-09-15 21:27:58 +02:00

107 lines
2.2 KiB
Go

package client
import (
"context"
"encoding/json"
"net/url"
"time"
"github.com/moby/moby/api/types/events"
"github.com/moby/moby/api/types/filters"
"github.com/moby/moby/client/internal/timestamp"
)
// EventsListOptions holds parameters to filter events with.
type EventsListOptions struct {
Since string
Until string
Filters filters.Args
}
// Events returns a stream of events in the daemon. It's up to the caller to close the stream
// by cancelling the context. Once the stream has been completely read an [io.EOF] error is
// sent over the error channel. If an error is sent, all processing is stopped. It's up
// to the caller to reopen the stream in the event of an error by reinvoking this method.
func (cli *Client) Events(ctx context.Context, options EventsListOptions) (<-chan events.Message, <-chan error) {
messages := make(chan events.Message)
errs := make(chan error, 1)
started := make(chan struct{})
go func() {
defer close(errs)
query, err := buildEventsQueryParams(options)
if err != nil {
close(started)
errs <- err
return
}
resp, err := cli.get(ctx, "/events", query, nil)
if err != nil {
close(started)
errs <- err
return
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
close(started)
for {
select {
case <-ctx.Done():
errs <- ctx.Err()
return
default:
var event events.Message
if err := decoder.Decode(&event); err != nil {
errs <- err
return
}
select {
case messages <- event:
case <-ctx.Done():
errs <- ctx.Err()
return
}
}
}
}()
<-started
return messages, errs
}
func buildEventsQueryParams(options EventsListOptions) (url.Values, error) {
query := url.Values{}
ref := time.Now()
if options.Since != "" {
ts, err := timestamp.GetTimestamp(options.Since, ref)
if err != nil {
return nil, err
}
query.Set("since", ts)
}
if options.Until != "" {
ts, err := timestamp.GetTimestamp(options.Until, ref)
if err != nil {
return nil, err
}
query.Set("until", ts)
}
if options.Filters.Len() > 0 {
filterJSON, err := filters.ToJSON(options.Filters)
if err != nil {
return nil, err
}
query.Set("filters", filterJSON)
}
return query, nil
}