mirror of
https://github.com/containers/podman.git
synced 2025-12-12 01:38:04 +08:00
Add event logging to libpod, even display to podman
In lipod, we now log major events that occurr. These events can be displayed using the `podman events` command. Each event contains: * Type (container, image, volume, pod...) * Status (create, rm, stop, kill, ....) * Timestamp in RFC3339Nano format * Name (if applicable) * Image (if applicable) The format of the event and the varlink endpoint are to not be considered stable until cockpit has done its enablement. Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
@@ -53,6 +53,15 @@ type ImagesValues struct {
|
||||
Sort string
|
||||
}
|
||||
|
||||
type EventValues struct {
|
||||
PodmanCommand
|
||||
Filter []string
|
||||
Format string
|
||||
Since string
|
||||
Stream bool
|
||||
Until string
|
||||
}
|
||||
|
||||
type TagValues struct {
|
||||
PodmanCommand
|
||||
}
|
||||
|
||||
48
cmd/podman/events.go
Normal file
48
cmd/podman/events.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||
"github.com/containers/libpod/pkg/adapter"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
eventsCommand cliconfig.EventValues
|
||||
eventsDescription = "Monitor podman events"
|
||||
_eventsCommand = &cobra.Command{
|
||||
Use: "events [flags]",
|
||||
Short: "show podman events",
|
||||
Long: eventsDescription,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
eventsCommand.InputArgs = args
|
||||
eventsCommand.GlobalFlags = MainGlobalOpts
|
||||
return eventsCmd(&eventsCommand)
|
||||
},
|
||||
Example: `podman events
|
||||
podman events --filter event=create
|
||||
podman events --since 1h30s`,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
eventsCommand.Command = _eventsCommand
|
||||
eventsCommand.SetUsageTemplate(UsageTemplate())
|
||||
flags := eventsCommand.Flags()
|
||||
flags.StringArrayVar(&eventsCommand.Filter, "filter", []string{}, "filter output")
|
||||
flags.StringVar(&eventsCommand.Format, "format", "", "format the output using a Go template")
|
||||
flags.BoolVar(&eventsCommand.Stream, "stream", true, "stream new events; for testing only")
|
||||
flags.StringVar(&eventsCommand.Since, "since", "", "show all events created since timestamp")
|
||||
flags.StringVar(&eventsCommand.Until, "until", "", "show all events until timestamp")
|
||||
flags.MarkHidden("stream")
|
||||
}
|
||||
|
||||
func eventsCmd(c *cliconfig.EventValues) error {
|
||||
runtime, err := adapter.GetRuntime(&c.PodmanCommand)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating libpod runtime")
|
||||
}
|
||||
defer runtime.Shutdown(false)
|
||||
|
||||
return runtime.Events(c)
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/logs"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -70,7 +71,7 @@ func logsCmd(c *cliconfig.LogsValues) error {
|
||||
sinceTime := time.Time{}
|
||||
if c.Flag("since").Changed {
|
||||
// parse time, error out if something is wrong
|
||||
since, err := parseInputTime(c.Since)
|
||||
since, err := util.ParseInputTime(c.Since)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not parse time: %q", c.Since)
|
||||
}
|
||||
@@ -112,25 +113,3 @@ func logsCmd(c *cliconfig.LogsValues) error {
|
||||
}
|
||||
return logs.ReadLogs(logPath, ctr, opts)
|
||||
}
|
||||
|
||||
// parseInputTime takes the users input and to determine if it is valid and
|
||||
// returns a time format and error. The input is compared to known time formats
|
||||
// or a duration which implies no-duration
|
||||
func parseInputTime(inputTime string) (time.Time, error) {
|
||||
timeFormats := []string{time.RFC3339Nano, time.RFC3339, "2006-01-02T15:04:05", "2006-01-02T15:04:05.999999999",
|
||||
"2006-01-02Z07:00", "2006-01-02"}
|
||||
// iterate the supported time formats
|
||||
for _, tf := range timeFormats {
|
||||
t, err := time.Parse(tf, inputTime)
|
||||
if err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
|
||||
// input might be a duration
|
||||
duration, err := time.ParseDuration(inputTime)
|
||||
if err != nil {
|
||||
return time.Time{}, errors.Errorf("unable to interpret time value")
|
||||
}
|
||||
return time.Now().Add(-duration), nil
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ var (
|
||||
// implemented.
|
||||
var mainCommands = []*cobra.Command{
|
||||
_buildCommand,
|
||||
_eventsCommand,
|
||||
_exportCommand,
|
||||
_historyCommand,
|
||||
&_imagesCommand,
|
||||
|
||||
115
cmd/podman/shared/events.go
Normal file
115
cmd/podman/shared/events.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/libpod/libpod/events"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func generateEventFilter(filter, filterValue string) (func(e *events.Event) bool, error) {
|
||||
switch strings.ToUpper(filter) {
|
||||
case "CONTAINER":
|
||||
return func(e *events.Event) bool {
|
||||
if e.Type != events.Container {
|
||||
return false
|
||||
}
|
||||
if e.Name == filterValue {
|
||||
return true
|
||||
}
|
||||
return strings.HasPrefix(e.ID, filterValue)
|
||||
}, nil
|
||||
case "EVENT", "STATUS":
|
||||
return func(e *events.Event) bool {
|
||||
return fmt.Sprintf("%s", e.Status) == filterValue
|
||||
}, nil
|
||||
case "IMAGE":
|
||||
return func(e *events.Event) bool {
|
||||
if e.Type != events.Image {
|
||||
return false
|
||||
}
|
||||
if e.Name == filterValue {
|
||||
return true
|
||||
}
|
||||
return strings.HasPrefix(e.ID, filterValue)
|
||||
}, nil
|
||||
case "POD":
|
||||
return func(e *events.Event) bool {
|
||||
if e.Type != events.Pod {
|
||||
return false
|
||||
}
|
||||
if e.Name == filterValue {
|
||||
return true
|
||||
}
|
||||
return strings.HasPrefix(e.ID, filterValue)
|
||||
}, nil
|
||||
case "VOLUME":
|
||||
return func(e *events.Event) bool {
|
||||
if e.Type != events.Volume {
|
||||
return false
|
||||
}
|
||||
return strings.HasPrefix(e.ID, filterValue)
|
||||
}, nil
|
||||
case "TYPE":
|
||||
return func(e *events.Event) bool {
|
||||
return fmt.Sprintf("%s", e.Type) == filterValue
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.Errorf("%s is an invalid filter", filter)
|
||||
}
|
||||
|
||||
func generateEventSinceOption(timeSince time.Time) func(e *events.Event) bool {
|
||||
return func(e *events.Event) bool {
|
||||
return e.Time.After(timeSince)
|
||||
}
|
||||
}
|
||||
|
||||
func generateEventUntilOption(timeUntil time.Time) func(e *events.Event) bool {
|
||||
return func(e *events.Event) bool {
|
||||
return e.Time.Before(timeUntil)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func parseFilter(filter string) (string, string, error) {
|
||||
filterSplit := strings.Split(filter, "=")
|
||||
if len(filterSplit) != 2 {
|
||||
return "", "", errors.Errorf("%s is an invalid filter", filter)
|
||||
}
|
||||
return filterSplit[0], filterSplit[1], nil
|
||||
}
|
||||
|
||||
func GenerateEventOptions(filters []string, since, until string) ([]events.EventFilter, error) {
|
||||
var options []events.EventFilter
|
||||
for _, filter := range filters {
|
||||
key, val, err := parseFilter(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
funcFilter, err := generateEventFilter(key, val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options = append(options, funcFilter)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
@@ -435,6 +435,23 @@ type Runlabel(
|
||||
opts: [string]string
|
||||
)
|
||||
|
||||
# Event describes a libpod struct
|
||||
type Event(
|
||||
# TODO: make status and type a enum at some point?
|
||||
# id is the container, volume, pod, image ID
|
||||
id: string,
|
||||
# image is the image name where applicable
|
||||
image: string,
|
||||
# name is the name of the pod, container, image
|
||||
name: string,
|
||||
# status describes the event that happened (i.e. create, remove, ...)
|
||||
status: string,
|
||||
# time the event happened
|
||||
time: string,
|
||||
# type describes object the event happened with (image, container...)
|
||||
type: string
|
||||
)
|
||||
|
||||
# GetVersion returns version and build information of the podman service
|
||||
method GetVersion() -> (
|
||||
version: string,
|
||||
@@ -1123,6 +1140,9 @@ method GetPodsByContext(all: bool, latest: bool, args: []string) -> (pods: []str
|
||||
# LoadImage allows you to load an image into local storage from a tarball.
|
||||
method LoadImage(name: string, inputFile: string, quiet: bool, deleteFile: bool) -> (reply: MoreResponse)
|
||||
|
||||
# GetEvents returns known libpod events filtered by the options provided.
|
||||
method GetEvents(filter: []string, since: string, stream: bool, until: string) -> (events: Event)
|
||||
|
||||
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
|
||||
error ImageNotFound (id: string, reason: string)
|
||||
|
||||
@@ -1152,3 +1172,6 @@ error ErrorOccurred (reason: string)
|
||||
|
||||
# RuntimeErrors generally means a runtime could not be found or gotten.
|
||||
error RuntimeError (reason: string)
|
||||
|
||||
# The Podman endpoint requires that you use a streaming connection.
|
||||
error WantsMoreRequired (reason: string)
|
||||
|
||||
Reference in New Issue
Block a user