summaryrefslogtreecommitdiff
path: root/pkg
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 /pkg
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 'pkg')
-rw-r--r--pkg/adapter/runtime.go53
-rw-r--r--pkg/adapter/runtime_remote.go68
-rw-r--r--pkg/util/utils.go23
-rw-r--r--pkg/varlinkapi/events.go56
4 files changed, 200 insertions, 0 deletions
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index 482b6119a..a0951f677 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -3,11 +3,13 @@
package adapter
import (
+ "bufio"
"context"
"io"
"io/ioutil"
"os"
"strconv"
+ "text/template"
"github.com/containers/buildah"
"github.com/containers/buildah/imagebuildah"
@@ -16,7 +18,9 @@ import (
"github.com/containers/image/types"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/rootless"
"github.com/pkg/errors"
@@ -377,3 +381,52 @@ func (r *LocalRuntime) JoinOrCreateRootlessPod(pod *Pod) (bool, int, error) {
return rootless.BecomeRootInUserNSWithOpts(&opts)
}
+
+// Events is a wrapper to libpod to obtain libpod/podman events
+func (r *LocalRuntime) Events(c *cliconfig.EventValues) error {
+ var (
+ fromStart bool
+ eventsError error
+ )
+ options, err := shared.GenerateEventOptions(c.Filter, c.Since, c.Until)
+ if err != nil {
+ return errors.Wrapf(err, "unable to generate event options")
+ }
+ tmpl, err := template.New("events").Parse(c.Format)
+ if err != nil {
+ return err
+ }
+ if len(c.Since) > 0 || len(c.Until) > 0 {
+ fromStart = true
+ }
+ eventChannel := make(chan *events.Event)
+ go func() {
+ eventsError = r.Runtime.Events(fromStart, c.Stream, options, eventChannel)
+ }()
+
+ if eventsError != nil {
+ return eventsError
+ }
+ if err != nil {
+ return errors.Wrapf(err, "unable to tail the events log")
+ }
+ w := bufio.NewWriter(os.Stdout)
+ for event := range eventChannel {
+ if len(c.Format) > 0 {
+ if err := tmpl.Execute(w, event); err != nil {
+ return err
+ }
+ } else {
+ if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil {
+ return err
+ }
+ }
+ if _, err := w.Write([]byte("\n")); err != nil {
+ return err
+ }
+ if err := w.Flush(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index 9ca4e245f..01f774dbd 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -10,6 +10,7 @@ import (
"io/ioutil"
"os"
"strings"
+ "text/template"
"time"
"github.com/containers/buildah/imagebuildah"
@@ -18,6 +19,7 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/utils"
"github.com/containers/storage/pkg/archive"
@@ -758,3 +760,69 @@ func (r *LocalRuntime) JoinOrCreateRootlessPod(pod *Pod) (bool, int, error) {
// Nothing to do in the remote case
return true, 0, nil
}
+
+// Events monitors libpod/podman events over a varlink connection
+func (r *LocalRuntime) Events(c *cliconfig.EventValues) error {
+ reply, err := iopodman.GetEvents().Send(r.Conn, uint64(varlink.More), c.Filter, c.Since, c.Stream, c.Until)
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain events")
+ }
+
+ w := bufio.NewWriter(os.Stdout)
+ tmpl, err := template.New("events").Parse(c.Format)
+ if err != nil {
+ return err
+ }
+
+ for {
+ returnedEvent, flags, err := reply()
+ if err != nil {
+ // When the error handling is back into podman, we can flip this to a better way to check
+ // for problems. For now, this works.
+ return err
+ }
+ if returnedEvent.Time == "" && returnedEvent.Status == "" && returnedEvent.Type == "" {
+ // We got a blank event return, signals end of stream in certain cases
+ break
+ }
+ eTime, err := time.Parse(time.RFC3339Nano, returnedEvent.Time)
+ if err != nil {
+ return errors.Wrapf(err, "unable to parse time of event %s", returnedEvent.Time)
+ }
+ eType, err := events.StringToType(returnedEvent.Type)
+ if err != nil {
+ return err
+ }
+ eStatus, err := events.StringToStatus(returnedEvent.Status)
+ if err != nil {
+ return err
+ }
+ event := events.Event{
+ ID: returnedEvent.Id,
+ Image: returnedEvent.Image,
+ Name: returnedEvent.Name,
+ Status: eStatus,
+ Time: eTime,
+ Type: eType,
+ }
+ if len(c.Format) > 0 {
+ if err := tmpl.Execute(w, event); err != nil {
+ return err
+ }
+ } else {
+ if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil {
+ return err
+ }
+ }
+ if _, err := w.Write([]byte("\n")); err != nil {
+ return err
+ }
+ if err := w.Flush(); err != nil {
+ return err
+ }
+ if flags&varlink.Continues == 0 {
+ break
+ }
+ }
+ return nil
+}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index a4576191b..d7e1ddd38 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -7,6 +7,7 @@ import (
"path/filepath"
"strings"
"syscall"
+ "time"
"github.com/BurntSushi/toml"
"github.com/containers/image/types"
@@ -347,3 +348,25 @@ func StorageConfigFile() string {
}
return storage.DefaultConfigFile
}
+
+// 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/pkg/varlinkapi/events.go b/pkg/varlinkapi/events.go
new file mode 100644
index 000000000..d3fe3d65f
--- /dev/null
+++ b/pkg/varlinkapi/events.go
@@ -0,0 +1,56 @@
+package varlinkapi
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod/events"
+)
+
+// GetEvents is a remote endpoint to get events from the event log
+func (i *LibpodAPI) GetEvents(call iopodman.VarlinkCall, filter []string, since string, stream bool, until string) error {
+ var (
+ fromStart bool
+ eventsError error
+ event *events.Event
+ )
+ if call.WantsMore() {
+ call.Continues = true
+ }
+ filters, err := shared.GenerateEventOptions(filter, since, until)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ if len(since) > 0 || len(until) > 0 {
+ fromStart = true
+ }
+ eventChannel := make(chan *events.Event)
+ go func() {
+ eventsError = i.Runtime.Events(fromStart, stream, filters, eventChannel)
+ }()
+ if eventsError != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ for {
+ event = <-eventChannel
+ if event == nil {
+ call.Continues = false
+ break
+ }
+ call.ReplyGetEvents(iopodman.Event{
+ Id: event.ID,
+ Image: event.Image,
+ Name: event.Name,
+ Status: fmt.Sprintf("%s", event.Status),
+ Time: event.Time.Format(time.RFC3339Nano),
+ Type: fmt.Sprintf("%s", event.Type),
+ })
+ if !call.Continues {
+ // For a one-shot on events, we break out here
+ break
+ }
+ }
+ return call.ReplyGetEvents(iopodman.Event{})
+}