Merge pull request #8103 from baude/eventlabels

filter events by labels
This commit is contained in:
OpenShift Merge Robot 2020-10-23 16:21:53 -04:00 committed by GitHub
commit 050dcadf69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 2 deletions

View File

@ -86,6 +86,7 @@ filters are supported:
* container=name_or_id * container=name_or_id
* event=event_status (described above) * event=event_status (described above)
* image=name_or_id * image=name_or_id
* label=key=value
* pod=name_or_id * pod=name_or_id
* volume=name_or_id * volume=name_or_id
* type=event_type (described above) * type=event_type (described above)

View File

@ -26,6 +26,12 @@ func (c *Container) newContainerEvent(status events.Status) {
e.Name = c.Name() e.Name = c.Name()
e.Image = c.config.RootfsImageName e.Image = c.config.RootfsImageName
e.Type = events.Container e.Type = events.Container
e.Details = events.Details{
ID: e.ID,
Attributes: c.Labels(),
}
if err := c.runtime.eventer.Write(e); err != nil { if err := c.runtime.eventer.Write(e); err != nil {
logrus.Errorf("unable to write pod event: %q", err) logrus.Errorf("unable to write pod event: %q", err)
} }

View File

@ -36,6 +36,18 @@ type Event struct {
Time time.Time Time time.Time
// Type of event that occurred // Type of event that occurred
Type Type Type Type
Details
}
// Details describes specifics about certain events, specifically around
// container events
type Details struct {
// ID is the event ID
ID string
// Attributes can be used to describe specifics about the event
// in the case of a container event, labels for example
Attributes map[string]string
} }
// EventerOptions describe options that need to be passed to create // EventerOptions describe options that need to be passed to create

View File

@ -69,7 +69,14 @@ func (e *Event) ToHumanReadable() string {
var humanFormat string var humanFormat string
switch e.Type { switch e.Type {
case Container, Pod: case Container, Pod:
humanFormat = fmt.Sprintf("%s %s %s %s (image=%s, name=%s)", e.Time, e.Type, e.Status, e.ID, e.Image, e.Name) humanFormat = fmt.Sprintf("%s %s %s %s (image=%s, name=%s", e.Time, e.Type, e.Status, e.ID, e.Image, e.Name)
// check if the container has labels and add it to the output
if len(e.Attributes) > 0 {
for k, v := range e.Attributes {
humanFormat += fmt.Sprintf(", %s=%s", k, v)
}
}
humanFormat += ")"
case Image: case Image:
humanFormat = fmt.Sprintf("%s %s %s %s %s", e.Time, e.Type, e.Status, e.ID, e.Name) humanFormat = fmt.Sprintf("%s %s %s %s %s", e.Time, e.Type, e.Status, e.ID, e.Name)
case System: case System:

View File

@ -55,6 +55,24 @@ func generateEventFilter(filter, filterValue string) (func(e *Event) bool, error
return func(e *Event) bool { return func(e *Event) bool {
return string(e.Type) == filterValue return string(e.Type) == filterValue
}, nil }, nil
case "LABEL":
return func(e *Event) bool {
var found bool
// iterate labels and see if we match a key and value
for eventKey, eventValue := range e.Attributes {
filterValueSplit := strings.SplitN(filterValue, "=", 2)
// if the filter isn't right, just return false
if len(filterValueSplit) < 2 {
return false
}
if eventKey == filterValueSplit[0] && eventValue == filterValueSplit[1] {
found = true
break
}
}
return found
}, nil
} }
return nil, errors.Errorf("%s is an invalid filter", filter) return nil, errors.Errorf("%s is an invalid filter", filter)
} }
@ -74,7 +92,7 @@ func generateEventUntilOption(timeUntil time.Time) func(e *Event) bool {
func parseFilter(filter string) (string, string, error) { func parseFilter(filter string) (string, string, error) {
filterSplit := strings.SplitN(filter, "=", 2) filterSplit := strings.SplitN(filter, "=", 2)
if len(filterSplit) == 1 { if len(filterSplit) != 2 {
return "", "", errors.Errorf("%s is an invalid filter", filter) return "", "", errors.Errorf("%s is an invalid filter", filter)
} }
return filterSplit[0], filterSplit[1], nil return filterSplit[0], filterSplit[1], nil

View File

@ -4,6 +4,7 @@ package events
import ( import (
"context" "context"
"encoding/json"
"strconv" "strconv"
"time" "time"
@ -46,6 +47,15 @@ func (e EventJournalD) Write(ee Event) error {
if ee.ContainerExitCode != 0 { if ee.ContainerExitCode != 0 {
m["PODMAN_EXIT_CODE"] = strconv.Itoa(ee.ContainerExitCode) m["PODMAN_EXIT_CODE"] = strconv.Itoa(ee.ContainerExitCode)
} }
// If we have container labels, we need to convert them to a string so they
// can be recorded with the event
if len(ee.Details.Attributes) > 0 {
b, err := json.Marshal(ee.Details.Attributes)
if err != nil {
return err
}
m["PODMAN_LABELS"] = string(b)
}
case Volume: case Volume:
m["PODMAN_NAME"] = ee.Name m["PODMAN_NAME"] = ee.Name
} }
@ -174,6 +184,19 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) { /
newEvent.ContainerExitCode = intCode newEvent.ContainerExitCode = intCode
} }
} }
// we need to check for the presence of labels recorded to a container event
if stringLabels, ok := entry.Fields["PODMAN_LABELS"]; ok && len(stringLabels) > 0 {
labels := make(map[string]string, 0)
if err := json.Unmarshal([]byte(stringLabels), &labels); err != nil {
return nil, err
}
// if we have labels, add them to the event
if len(labels) > 0 {
newEvent.Details = Details{Attributes: labels}
}
}
case Image: case Image:
newEvent.ID = entry.Fields["PODMAN_ID"] newEvent.ID = entry.Fields["PODMAN_ID"]
} }

View File

@ -0,0 +1,14 @@
#!/usr/bin/env bats -*- bats -*-
#
# tests for podman events functionality
#
load helpers
@test "events with a filter by label" {
skip_if_remote "Need to talk to Ed on why this is failing on remote"
rand=$(random_string 30)
run_podman 0 run --label foo=bar --name test-$rand --rm $IMAGE ls
run_podman 0 events --filter type=container --filter container=test-$rand --filter label=foo=bar --filter event=start --stream=false
is "$output" ".*foo=bar" "check for label event on container with label"
}