mirror of https://github.com/containers/podman.git
filter events by labels
adding the ability to filter evens by the container labels. this requires that container labels be added to the events data being recorded and subsequently read. Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
parent
51fa8ded9f
commit
c593e49701
|
@ -86,6 +86,7 @@ filters are supported:
|
|||
* container=name_or_id
|
||||
* event=event_status (described above)
|
||||
* image=name_or_id
|
||||
* label=key=value
|
||||
* pod=name_or_id
|
||||
* volume=name_or_id
|
||||
* type=event_type (described above)
|
||||
|
|
|
@ -26,6 +26,12 @@ func (c *Container) newContainerEvent(status events.Status) {
|
|||
e.Name = c.Name()
|
||||
e.Image = c.config.RootfsImageName
|
||||
e.Type = events.Container
|
||||
|
||||
e.Details = events.Details{
|
||||
ID: e.ID,
|
||||
Attributes: c.Labels(),
|
||||
}
|
||||
|
||||
if err := c.runtime.eventer.Write(e); err != nil {
|
||||
logrus.Errorf("unable to write pod event: %q", err)
|
||||
}
|
||||
|
|
|
@ -36,6 +36,18 @@ type Event struct {
|
|||
Time time.Time
|
||||
// Type of event that occurred
|
||||
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
|
||||
|
|
|
@ -69,7 +69,14 @@ func (e *Event) ToHumanReadable() string {
|
|||
var humanFormat string
|
||||
switch e.Type {
|
||||
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:
|
||||
humanFormat = fmt.Sprintf("%s %s %s %s %s", e.Time, e.Type, e.Status, e.ID, e.Name)
|
||||
case System:
|
||||
|
|
|
@ -55,6 +55,24 @@ func generateEventFilter(filter, filterValue string) (func(e *Event) bool, error
|
|||
return func(e *Event) bool {
|
||||
return string(e.Type) == filterValue
|
||||
}, 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)
|
||||
}
|
||||
|
@ -74,7 +92,7 @@ func generateEventUntilOption(timeUntil time.Time) func(e *Event) bool {
|
|||
|
||||
func parseFilter(filter string) (string, string, error) {
|
||||
filterSplit := strings.SplitN(filter, "=", 2)
|
||||
if len(filterSplit) == 1 {
|
||||
if len(filterSplit) != 2 {
|
||||
return "", "", errors.Errorf("%s is an invalid filter", filter)
|
||||
}
|
||||
return filterSplit[0], filterSplit[1], nil
|
||||
|
|
|
@ -4,6 +4,7 @@ package events
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
@ -46,6 +47,15 @@ func (e EventJournalD) Write(ee Event) error {
|
|||
if ee.ContainerExitCode != 0 {
|
||||
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:
|
||||
m["PODMAN_NAME"] = ee.Name
|
||||
}
|
||||
|
@ -174,6 +184,19 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) { /
|
|||
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:
|
||||
newEvent.ID = entry.Fields["PODMAN_ID"]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
Loading…
Reference in New Issue