Merge pull request #10514 from vrothberg/fix-10507

events: support disjunctive filters
This commit is contained in:
OpenShift Merge Robot 2021-06-02 10:37:58 -04:00 committed by GitHub
commit 52dae693da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 21 deletions

View File

@ -97,18 +97,41 @@ 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 {
@ -116,7 +139,8 @@ func generateEventOptions(filters []string, since, until string) ([]EventFilter,
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 {
@ -124,7 +148,8 @@ func generateEventOptions(filters []string, since, until string) ([]EventFilter,
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
}

View File

@ -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 {
@ -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
}
}

View File

@ -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 {
@ -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
}
}

View File

@ -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 ""
}