summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2019-02-28 14:15:56 -0600
committerbaude <bbaude@redhat.com>2019-03-11 15:08:59 -0500
commitca1e76ff632dec0b0e00e25f26677887ca8cc625 (patch)
treebf5c231a826f4ce15d52a6d4e52e0b11abeb85f0 /cmd
parent6421208e0f6ff1fba58eafdab12e897b5ed12e3b (diff)
downloadpodman-ca1e76ff632dec0b0e00e25f26677887ca8cc625.tar.gz
podman-ca1e76ff632dec0b0e00e25f26677887ca8cc625.tar.bz2
podman-ca1e76ff632dec0b0e00e25f26677887ca8cc625.zip
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>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/cliconfig/config.go9
-rw-r--r--cmd/podman/events.go48
-rw-r--r--cmd/podman/logs.go25
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/shared/events.go115
-rw-r--r--cmd/podman/varlink/io.podman.varlink23
6 files changed, 198 insertions, 23 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index d58964489..ec08eedb5 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -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
}
diff --git a/cmd/podman/events.go b/cmd/podman/events.go
new file mode 100644
index 000000000..dda9a03f9
--- /dev/null
+++ b/cmd/podman/events.go
@@ -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)
+}
diff --git a/cmd/podman/logs.go b/cmd/podman/logs.go
index 9df7281fc..c3416fe57 100644
--- a/cmd/podman/logs.go
+++ b/cmd/podman/logs.go
@@ -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
-}
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index bbeb72397..669860341 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -36,6 +36,7 @@ var (
// implemented.
var mainCommands = []*cobra.Command{
_buildCommand,
+ _eventsCommand,
_exportCommand,
_historyCommand,
&_imagesCommand,
diff --git a/cmd/podman/shared/events.go b/cmd/podman/shared/events.go
new file mode 100644
index 000000000..c62044271
--- /dev/null
+++ b/cmd/podman/shared/events.go
@@ -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
+}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 6109bd290..88ea4adcf 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -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)