Skip to content

Commit

Permalink
Merge pull request #10514 from vrothberg/fix-10507
Browse files Browse the repository at this point in the history
events: support disjunctive filters
  • Loading branch information
openshift-merge-robot authored Jun 2, 2021
2 parents 6df37ab + 37f39ee commit 52dae69
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 21 deletions.
39 changes: 32 additions & 7 deletions libpod/events/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,34 +97,59 @@ func parseFilter(filter string) (string, string, error) {
return filterSplit[0], filterSplit[1], nil
}

func generateEventOptions(filters []string, since, until string) ([]EventFilter, error) {
options := make([]EventFilter, 0, len(filters))
// applyFilters applies the EventFilter slices in sequence. Filters under the
// same key are disjunctive while each key must match (conjuctive).
func applyFilters(event *Event, filterMap map[string][]EventFilter) bool {
for _, filters := range filterMap {
success := false
for _, filter := range filters {
if filter(event) {
success = true
break
}
}
if !success {
return false
}
}
return true
}

// generateEventFilter parses the specified filters into a filter map that can
// later on be used to filter events. Keys are conjunctive, values are
// disjunctive.
func generateEventFilters(filters []string, since, until string) (map[string][]EventFilter, error) {
filterMap := make(map[string][]EventFilter)
for _, filter := range filters {
key, val, err := parseFilter(filter)
if err != nil {
return nil, err
}
funcFilter, err := generateEventFilter(key, val)
filterFunc, err := generateEventFilter(key, val)
if err != nil {
return nil, err
}
options = append(options, funcFilter)
filterSlice := filterMap[key]
filterSlice = append(filterSlice, filterFunc)
filterMap[key] = filterSlice
}

if len(since) > 0 {
timeSince, err := util.ParseInputTime(since)
if err != nil {
return nil, errors.Wrapf(err, "unable to convert since time of %s", since)
}
options = append(options, generateEventSinceOption(timeSince))
filterFunc := generateEventSinceOption(timeSince)
filterMap["since"] = []EventFilter{filterFunc}
}

if len(until) > 0 {
timeUntil, err := util.ParseInputTime(until)
if err != nil {
return nil, errors.Wrapf(err, "unable to convert until time of %s", until)
}
options = append(options, generateEventUntilOption(timeUntil))
filterFunc := generateEventUntilOption(timeUntil)
filterMap["until"] = []EventFilter{filterFunc}
}
return options, nil
return filterMap, nil
}
10 changes: 3 additions & 7 deletions libpod/events/journal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ func (e EventJournalD) Write(ee Event) error {
// Read reads events from the journal and sends qualified events to the event channel
func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error {
defer close(options.EventChannel)
eventOptions, err := generateEventOptions(options.Filters, options.Since, options.Until)
filterMap, err := generateEventFilters(options.Filters, options.Since, options.Until)
if err != nil {
return errors.Wrapf(err, "failed to generate event options")
return errors.Wrapf(err, "failed to parse event filters")
}
var untilTime time.Time
if len(options.Until) > 0 {
Expand Down Expand Up @@ -159,11 +159,7 @@ func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error {
}
continue
}
include := true
for _, filter := range eventOptions {
include = include && filter(newEvent)
}
if include {
if applyFilters(newEvent, filterMap) {
options.EventChannel <- newEvent
}
}
Expand Down
10 changes: 3 additions & 7 deletions libpod/events/logfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ func (e EventLogFile) Write(ee Event) error {
// Reads from the log file
func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error {
defer close(options.EventChannel)
eventOptions, err := generateEventOptions(options.Filters, options.Since, options.Until)
filterMap, err := generateEventFilters(options.Filters, options.Since, options.Until)
if err != nil {
return errors.Wrapf(err, "unable to generate event options")
return errors.Wrapf(err, "failed to parse event filters")
}
t, err := e.getTail(options)
if err != nil {
Expand Down Expand Up @@ -92,11 +92,7 @@ func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error {
default:
return errors.Errorf("event type %s is not valid in %s", event.Type.String(), e.options.LogFilePath)
}
include := true
for _, filter := range eventOptions {
include = include && filter(event)
}
if include && copy {
if copy && applyFilters(event, filterMap) {
options.EventChannel <- event
}
}
Expand Down
29 changes: 29 additions & 0 deletions test/system/090-events.bats
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,32 @@ load helpers
.*image remove $imageID $tag.*" \
"podman events"
}

function _events_disjunctive_filters() {
local backend=$1

# Regression test for #10507: make sure that filters with the same key are
# applied in disjunction.
t0=$(date --iso-8601=seconds)
run_podman $backend run --name foo --rm $IMAGE ls
run_podman $backend run --name bar --rm $IMAGE ls
run_podman $backend events --stream=false --since=$t0 --filter container=foo --filter container=bar --filter event=start
is "$output" ".* container start .* name=foo.*
.* container start .* name=bar.*"
}

@test "events with disjunctive filters - file" {
skip_if_remote "remote does not support --events-backend"
_events_disjunctive_filters --events-backend=file
}

@test "events with disjunctive filters - journald" {
skip_if_remote "remote does not support --events-backend"
_events_disjunctive_filters --events-backend=journald
}

@test "events with disjunctive filters - default" {
# NOTE: the last event for bar doesn't show up reliably.
skip_if_remote "FIXME #10529: remote events lose data"
_events_disjunctive_filters ""
}

0 comments on commit 52dae69

Please sign in to comment.