aboutsummaryrefslogtreecommitdiff
path: root/libpod
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 /libpod
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 'libpod')
-rw-r--r--libpod/container_api.go34
-rw-r--r--libpod/container_commit.go2
-rw-r--r--libpod/container_internal.go4
-rw-r--r--libpod/events.go81
-rw-r--r--libpod/events/events.go264
-rw-r--r--libpod/image/image.go42
-rw-r--r--libpod/image/prune.go6
-rw-r--r--libpod/image/pull.go5
-rw-r--r--libpod/options.go1
-rw-r--r--libpod/pod_api.go13
-rw-r--r--libpod/runtime.go16
-rw-r--r--libpod/runtime_ctr.go4
-rw-r--r--libpod/runtime_pod_linux.go5
-rw-r--r--libpod/runtime_volume.go6
-rw-r--r--libpod/runtime_volume_linux.go5
15 files changed, 450 insertions, 38 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 4a76e1434..3698a15ec 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -10,6 +10,7 @@ import (
"time"
"github.com/containers/libpod/libpod/driver"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/pkg/inspect"
"github.com/containers/libpod/pkg/lookup"
"github.com/containers/storage/pkg/stringid"
@@ -88,6 +89,7 @@ func (c *Container) Start(ctx context.Context, recursive bool) (err error) {
}
// Start the container
+ defer c.newContainerEvent(events.Start)
return c.start()
}
@@ -125,7 +127,8 @@ func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams,
}
close(attachChan)
}()
-
+ c.newContainerEvent(events.Start)
+ c.newContainerEvent(events.Attach)
return attachChan, nil
}
@@ -180,7 +183,7 @@ func (c *Container) StopWithTimeout(timeout uint) error {
c.state.State == ContainerStateExited {
return ErrCtrStopped
}
-
+ defer c.newContainerEvent(events.Stop)
return c.stop(timeout)
}
@@ -198,7 +201,7 @@ func (c *Container) Kill(signal uint) error {
if c.state.State != ContainerStateRunning {
return errors.Wrapf(ErrCtrStateInvalid, "can only kill running containers")
}
-
+ defer c.newContainerEvent(events.Kill)
return c.runtime.ociRuntime.killContainer(c, signal)
}
@@ -321,7 +324,7 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir
// TODO handle this better
return errors.Wrapf(err, "error saving exec sessions %s for container %s", sessionID, c.ID())
}
-
+ c.newContainerEvent(events.Exec)
logrus.Debugf("Successfully started exec session %s in container %s", sessionID, c.ID())
// Unlock so other processes can use the container
@@ -351,7 +354,6 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir
if err := c.save(); err != nil {
logrus.Errorf("Error removing exec session %s from container %s state: %v", sessionID, c.ID(), err)
}
-
return waitErr
}
@@ -390,7 +392,7 @@ func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan re
c.state.State != ContainerStateExited {
return errors.Wrapf(ErrCtrStateInvalid, "can only attach to created or running containers")
}
-
+ defer c.newContainerEvent(events.Attach)
return c.attach(streams, keys, resize, false)
}
@@ -405,7 +407,7 @@ func (c *Container) Mount() (string, error) {
return "", err
}
}
-
+ defer c.newContainerEvent(events.Mount)
return c.mount()
}
@@ -435,6 +437,7 @@ func (c *Container) Unmount(force bool) error {
return errors.Wrapf(ErrInternal, "can't unmount %s last mount, it is still in use", c.ID())
}
}
+ defer c.newContainerEvent(events.Unmount)
return c.unmount(force)
}
@@ -455,7 +458,7 @@ func (c *Container) Pause() error {
if c.state.State != ContainerStateRunning {
return errors.Wrapf(ErrCtrStateInvalid, "%q is not running, can't pause", c.state.State)
}
-
+ defer c.newContainerEvent(events.Pause)
return c.pause()
}
@@ -473,7 +476,7 @@ func (c *Container) Unpause() error {
if c.state.State != ContainerStatePaused {
return errors.Wrapf(ErrCtrStateInvalid, "%q is not paused, can't unpause", c.ID())
}
-
+ defer c.newContainerEvent(events.Unpause)
return c.unpause()
}
@@ -488,7 +491,7 @@ func (c *Container) Export(path string) error {
return err
}
}
-
+ defer c.newContainerEvent(events.Export)
return c.export(path)
}
@@ -542,7 +545,6 @@ func (c *Container) Inspect(size bool) (*inspect.ContainerInspectData, error) {
if err != nil {
return nil, errors.Wrapf(err, "error getting graph driver info %q", c.ID())
}
-
return c.getContainerInspectData(size, driverData)
}
@@ -574,6 +576,7 @@ func (c *Container) WaitWithInterval(waitTimeout time.Duration) (int32, error) {
return 0, err
}
exitCode := c.state.ExitCode
+ c.newContainerEvent(events.Wait)
return exitCode, nil
}
@@ -597,7 +600,7 @@ func (c *Container) Cleanup(ctx context.Context) error {
if len(c.state.ExecSessions) != 0 {
return errors.Wrapf(ErrCtrStateInvalid, "container %s has active exec sessions, refusing to clean up", c.ID())
}
-
+ defer c.newContainerEvent(events.Cleanup)
return c.cleanup(ctx)
}
@@ -667,7 +670,7 @@ func (c *Container) Sync() error {
}
}
}
-
+ defer c.newContainerEvent(events.Sync)
return nil
}
@@ -772,7 +775,6 @@ func (c *Container) Refresh(ctx context.Context) error {
return err
}
}
-
return nil
}
@@ -800,7 +802,7 @@ func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointO
return err
}
}
-
+ defer c.newContainerEvent(events.Checkpoint)
return c.checkpoint(ctx, options)
}
@@ -815,6 +817,6 @@ func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOpti
return err
}
}
-
+ defer c.newContainerEvent(events.Restore)
return c.restore(ctx, options)
}
diff --git a/libpod/container_commit.go b/libpod/container_commit.go
index 5c4fd1a31..0604a550b 100644
--- a/libpod/container_commit.go
+++ b/libpod/container_commit.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/buildah"
"github.com/containers/buildah/util"
is "github.com/containers/image/storage"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -177,5 +178,6 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
if err != nil {
return nil, err
}
+ defer c.newContainerEvent(events.Commit)
return c.runtime.imageRuntime.NewFromLocal(id)
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 00e6786f9..330745314 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -12,6 +12,7 @@ import (
"strings"
"time"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/pkg/ctime"
"github.com/containers/libpod/pkg/hooks"
"github.com/containers/libpod/pkg/hooks/exec"
@@ -824,7 +825,7 @@ func (c *Container) init(ctx context.Context) error {
if err := c.save(); err != nil {
return err
}
-
+ defer c.newContainerEvent(events.Init)
return c.completeNetworkSetup()
}
@@ -1022,7 +1023,6 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e
return err
}
}
-
return c.start()
}
diff --git a/libpod/events.go b/libpod/events.go
new file mode 100644
index 000000000..9806c117b
--- /dev/null
+++ b/libpod/events.go
@@ -0,0 +1,81 @@
+package libpod
+
+import (
+ "github.com/containers/libpod/libpod/events"
+ "github.com/hpcloud/tail"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// newContainerEvent creates a new event based on a container
+func (c *Container) newContainerEvent(status events.Status) {
+ e := events.NewEvent(status)
+ e.ID = c.ID()
+ e.Name = c.Name()
+ e.Image = c.config.RootfsImageName
+ e.Type = events.Container
+ if err := e.Write(c.runtime.config.EventsLogFilePath); err != nil {
+ logrus.Errorf("unable to write event to %s", c.runtime.config.EventsLogFilePath)
+ }
+}
+
+// newPodEvent creates a new event for a libpod pod
+func (p *Pod) newPodEvent(status events.Status) {
+ e := events.NewEvent(status)
+ e.ID = p.ID()
+ e.Name = p.Name()
+ e.Type = events.Pod
+ if err := e.Write(p.runtime.config.EventsLogFilePath); err != nil {
+ logrus.Errorf("unable to write event to %s", p.runtime.config.EventsLogFilePath)
+ }
+}
+
+// newVolumeEvent creates a new event for a libpod volume
+func (v *Volume) newVolumeEvent(status events.Status) {
+ e := events.NewEvent(status)
+ e.Name = v.Name()
+ e.Type = events.Volume
+ if err := e.Write(v.runtime.config.EventsLogFilePath); err != nil {
+ logrus.Errorf("unable to write event to %s", v.runtime.config.EventsLogFilePath)
+ }
+}
+
+// Events is a wrapper function for everyone to begin tailing the events log
+// with options
+func (r *Runtime) Events(fromStart, stream bool, options []events.EventFilter, eventChannel chan *events.Event) error {
+ t, err := r.getTail(fromStart, stream)
+ if err != nil {
+ return err
+ }
+ for line := range t.Lines {
+ event, err := events.NewEventFromString(line.Text)
+ if err != nil {
+ return err
+ }
+ switch event.Type {
+ case events.Image, events.Volume, events.Pod, events.Container:
+ // no-op
+ default:
+ return errors.Errorf("event type %s is not valid in %s", event.Type.String(), r.GetConfig().EventsLogFilePath)
+ }
+ include := true
+ for _, filter := range options {
+ include = include && filter(event)
+ }
+ if include {
+ eventChannel <- event
+ }
+ }
+ close(eventChannel)
+ return nil
+}
+
+func (r *Runtime) getTail(fromStart, stream bool) (*tail.Tail, error) {
+ reopen := true
+ seek := tail.SeekInfo{Offset: 0, Whence: 2}
+ if fromStart || !stream {
+ seek.Whence = 0
+ reopen = false
+ }
+ return tail.TailFile(r.config.EventsLogFilePath, tail.Config{ReOpen: reopen, Follow: stream, Location: &seek})
+}
diff --git a/libpod/events/events.go b/libpod/events/events.go
new file mode 100644
index 000000000..186790500
--- /dev/null
+++ b/libpod/events/events.go
@@ -0,0 +1,264 @@
+package events
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "time"
+
+ "github.com/pkg/errors"
+)
+
+// Event describes the attributes of a libpod event
+type Event struct {
+ // ContainerExitCode is for storing the exit code of a container which can
+ // be used for "internal" event notification
+ ContainerExitCode int
+ // ID can be for the container, image, volume, etc
+ ID string
+ // Image used where applicable
+ Image string
+ // Name where applicable
+ Name string
+ // Status describes the event that occurred
+ Status Status
+ // Time the event occurred
+ Time time.Time
+ // Type of event that occurred
+ Type Type
+}
+
+// Type of event that occurred (container, volume, image, pod, etc)
+type Type string
+
+// Status describes the actual event action (stop, start, create, kill)
+type Status string
+
+const (
+ // If you add or subtract any values to the following lists, make sure you also update
+ // the switch statements below and the enums for EventType or EventStatus in the
+ // varlink description file.
+
+ // Container - event is related to containers
+ Container Type = "container"
+ // Image - event is related to images
+ Image Type = "image"
+ // Pod - event is related to pods
+ Pod Type = "pod"
+ // Volume - event is related to volumes
+ Volume Type = "volume"
+
+ // Attach ...
+ Attach Status = "attach"
+ // Checkpoint ...
+ Checkpoint Status = "checkpoint"
+ // Cleanup ...
+ Cleanup Status = "cleanup"
+ // Commit ...
+ Commit Status = "commit"
+ // Create ...
+ Create Status = "create"
+ // Exec ...
+ Exec Status = "exec"
+ // Export ...
+ Export Status = "export"
+ // History ...
+ History Status = "history"
+ // Import ...
+ Import Status = "import"
+ // Init ...
+ Init Status = "init"
+ // Kill ...
+ Kill Status = "kill"
+ // LoadFromArchive ...
+ LoadFromArchive Status = "status"
+ // Mount ...
+ Mount Status = "mount"
+ // Pause ...
+ Pause Status = "pause"
+ // Prune ...
+ Prune Status = "prune"
+ // Pull ...
+ Pull Status = "pull"
+ // Push ...
+ Push Status = "push"
+ // Remove ...
+ Remove Status = "remove"
+ // Restore ...
+ Restore Status = "restore"
+ // Save ...
+ Save Status = "save"
+ // Start ...
+ Start Status = "start"
+ // Stop ...
+ Stop Status = "stop"
+ // Sync ...
+ Sync Status = "sync"
+ // Tag ...
+ Tag Status = "tag"
+ // Unmount ...
+ Unmount Status = "unmount"
+ // Unpause ...
+ Unpause Status = "unpause"
+ // Untag ...
+ Untag Status = "untag"
+ // Wait ...
+ Wait Status = "wait"
+)
+
+// EventFilter for filtering events
+type EventFilter func(*Event) bool
+
+// NewEvent creates a event struct and populates with
+// the given status and time.
+func NewEvent(status Status) Event {
+ return Event{
+ Status: status,
+ Time: time.Now(),
+ }
+}
+
+// Write will record the event to the given path
+func (e *Event) Write(path string) error {
+ f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0700)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ eventJSONString, err := e.ToJSONString()
+ if err != nil {
+ return err
+ }
+ if _, err := f.WriteString(fmt.Sprintf("%s\n", eventJSONString)); err != nil {
+ return err
+ }
+ return nil
+}
+
+// Recycle checks if the event log has reach a limit and if so
+// renames the current log and starts a new one. The remove bool
+// indicates the old log file should be deleted.
+func (e *Event) Recycle(path string, remove bool) error {
+ return errors.New("not implemented")
+}
+
+// ToJSONString returns the event as a json'ified string
+func (e *Event) ToJSONString() (string, error) {
+ b, err := json.Marshal(e)
+ return string(b), err
+}
+
+// ToHumanReadable returns human readable event as a formatted string
+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)
+ case Image:
+ humanFormat = fmt.Sprintf("%s %s %s %s %s", e.Time, e.Type, e.Status, e.ID, e.Name)
+ case Volume:
+ humanFormat = fmt.Sprintf("%s %s %s %s", e.Time, e.Type, e.Status, e.Name)
+ }
+ return humanFormat
+}
+
+// NewEventFromString takes stringified json and converts
+// it to an event
+func NewEventFromString(event string) (*Event, error) {
+ e := Event{}
+ if err := json.Unmarshal([]byte(event), &e); err != nil {
+ return nil, err
+ }
+ return &e, nil
+
+}
+
+// ToString converts a Type to a string
+func (t Type) String() string {
+ return string(t)
+}
+
+// ToString converts a status to a string
+func (s Status) String() string {
+ return string(s)
+}
+
+// StringToType converts string to an EventType
+func StringToType(name string) (Type, error) {
+ switch name {
+ case Container.String():
+ return Container, nil
+ case Image.String():
+ return Image, nil
+ case Pod.String():
+ return Pod, nil
+ case Volume.String():
+ return Volume, nil
+ }
+ return "", errors.Errorf("unknown event type %s", name)
+}
+
+// StringToStatus converts a string to an Event Status
+// TODO if we add more events, we might consider a go-generator to
+// create the switch statement
+func StringToStatus(name string) (Status, error) {
+ switch name {
+ case Attach.String():
+ return Attach, nil
+ case Checkpoint.String():
+ return Checkpoint, nil
+ case Restore.String():
+ return Restore, nil
+ case Cleanup.String():
+ return Cleanup, nil
+ case Commit.String():
+ return Commit, nil
+ case Create.String():
+ return Create, nil
+ case Exec.String():
+ return Exec, nil
+ case Export.String():
+ return Export, nil
+ case History.String():
+ return History, nil
+ case Import.String():
+ return Import, nil
+ case Init.String():
+ return Init, nil
+ case Kill.String():
+ return Kill, nil
+ case LoadFromArchive.String():
+ return LoadFromArchive, nil
+ case Mount.String():
+ return Mount, nil
+ case Pause.String():
+ return Pause, nil
+ case Prune.String():
+ return Prune, nil
+ case Pull.String():
+ return Pull, nil
+ case Push.String():
+ return Push, nil
+ case Remove.String():
+ return Remove, nil
+ case Save.String():
+ return Save, nil
+ case Start.String():
+ return Start, nil
+ case Stop.String():
+ return Stop, nil
+ case Sync.String():
+ return Sync, nil
+ case Tag.String():
+ return Tag, nil
+ case Unmount.String():
+ return Unmount, nil
+ case Unpause.String():
+ return Unpause, nil
+ case Untag.String():
+ return Untag, nil
+ case Wait.String():
+ return Wait, nil
+ }
+ return "", errors.Errorf("unknown event status %s", name)
+}
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 8c98de3d3..72f07dad1 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -24,12 +24,13 @@ import (
"github.com/containers/image/types"
"github.com/containers/libpod/libpod/common"
"github.com/containers/libpod/libpod/driver"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/pkg/inspect"
"github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/reexec"
- digest "github.com/opencontainers/go-digest"
+ "github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opentracing/opentracing-go"
@@ -64,6 +65,7 @@ type Image struct {
type Runtime struct {
store storage.Store
SignaturePolicyPath string
+ EventsLogFilePath string
}
// ErrRepoTagNotFound is the error returned when the image id given doesn't match a rep tag in store
@@ -195,7 +197,7 @@ func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.Im
newImage.image = img
newImages = append(newImages, &newImage)
}
-
+ ir.newImageEvent(events.LoadFromArchive, "")
return newImages, nil
}
@@ -371,6 +373,7 @@ func (i *Image) Remove(force bool) error {
}
parent = nextParent
}
+ defer i.newImageEvent(events.Remove)
return nil
}
@@ -494,6 +497,7 @@ func (i *Image) TagImage(tag string) error {
return err
}
i.reloadImage()
+ defer i.newImageEvent(events.Tag)
return nil
}
@@ -514,6 +518,7 @@ func (i *Image) UntagImage(tag string) error {
return err
}
i.reloadImage()
+ defer i.newImageEvent(events.Untag)
return nil
}
@@ -562,6 +567,7 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere
if err != nil {
return errors.Wrapf(err, "Error copying image to the remote destination")
}
+ defer i.newImageEvent(events.Push)
return nil
}
@@ -711,7 +717,6 @@ func (i *Image) History(ctx context.Context) ([]*History, error) {
Comment: oci.History[i].Comment,
})
}
-
return allHistory, nil
}
@@ -927,7 +932,11 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io
if err != nil {
return nil, err
}
- return ir.NewFromLocal(reference)
+ newImage, err := ir.NewFromLocal(reference)
+ if err == nil {
+ defer newImage.newImageEvent(events.Import)
+ }
+ return newImage, err
}
// MatchRepoTag takes a string and tries to match it against an
@@ -1148,7 +1157,7 @@ func (i *Image) Save(ctx context.Context, source, format, output string, moreTag
}
return errors.Wrapf(err, "unable to save %q", source)
}
-
+ defer i.newImageEvent(events.Save)
return nil
}
@@ -1180,3 +1189,26 @@ func (i *Image) GetHealthCheck(ctx context.Context) (*manifest.Schema2HealthConf
}
return configBlob.ContainerConfig.Healthcheck, nil
}
+
+// newImageEvent creates a new event based on an image
+func (ir *Runtime) newImageEvent(status events.Status, name string) {
+ e := events.NewEvent(status)
+ e.Type = events.Image
+ e.Name = name
+ if err := e.Write(ir.EventsLogFilePath); err != nil {
+ logrus.Infof("unable to write event to %s", ir.EventsLogFilePath)
+ }
+}
+
+// newImageEvent creates a new event based on an image
+func (i *Image) newImageEvent(status events.Status) {
+ e := events.NewEvent(status)
+ e.ID = i.ID()
+ e.Type = events.Image
+ if len(i.Names()) > 0 {
+ e.Name = i.Names()[0]
+ }
+ if err := e.Write(i.imageruntime.EventsLogFilePath); err != nil {
+ logrus.Infof("unable to write event to %s", i.imageruntime.EventsLogFilePath)
+ }
+}
diff --git a/libpod/image/prune.go b/libpod/image/prune.go
index 8602c222c..5bd3c2c99 100644
--- a/libpod/image/prune.go
+++ b/libpod/image/prune.go
@@ -1,6 +1,9 @@
package image
-import "github.com/pkg/errors"
+import (
+ "github.com/containers/libpod/libpod/events"
+ "github.com/pkg/errors"
+)
// GetPruneImages returns a slice of images that have no names/unused
func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) {
@@ -41,6 +44,7 @@ func (ir *Runtime) PruneImages(all bool) ([]string, error) {
if err := p.Remove(true); err != nil {
return nil, errors.Wrap(err, "failed to prune image")
}
+ defer p.newImageEvent(events.Prune)
prunedCids = append(prunedCids, p.ID())
}
return prunedCids, nil
diff --git a/libpod/image/pull.go b/libpod/image/pull.go
index 607829771..a3b716e65 100644
--- a/libpod/image/pull.go
+++ b/libpod/image/pull.go
@@ -17,6 +17,7 @@ import (
"github.com/containers/image/transports"
"github.com/containers/image/transports/alltransports"
"github.com/containers/image/types"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/pkg/registries"
multierror "github.com/hashicorp/go-multierror"
opentracing "github.com/opentracing/opentracing-go"
@@ -273,6 +274,7 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa
}
} else {
if !goal.pullAllPairs {
+ ir.newImageEvent(events.Pull, "")
return []string{imageInfo.image}, nil
}
images = append(images, imageInfo.image)
@@ -293,6 +295,9 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa
}
return nil, pullErrors
}
+ if len(images) > 0 {
+ defer ir.newImageEvent(events.Pull, images[0])
+ }
return images, nil
}
diff --git a/libpod/options.go b/libpod/options.go
index 64b425c57..1bf3ff9e6 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -286,7 +286,6 @@ func WithTmpDir(dir string) RuntimeOption {
if rt.valid {
return ErrRuntimeFinalized
}
-
rt.config.TmpDir = dir
rt.configuredFrom.libpodTmpDirSet = true
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index cbac2420f..b9a11000e 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -3,6 +3,7 @@ package libpod
import (
"context"
+ "github.com/containers/libpod/libpod/events"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/ulule/deepcopier"
@@ -58,7 +59,7 @@ func (p *Pod) Start(ctx context.Context) (map[string]error, error) {
if len(ctrErrors) > 0 {
return ctrErrors, errors.Wrapf(ErrCtrExists, "error starting some containers")
}
-
+ defer p.newPodEvent(events.Start)
return nil, nil
}
@@ -138,7 +139,7 @@ func (p *Pod) StopWithTimeout(ctx context.Context, cleanup bool, timeout int) (m
if len(ctrErrors) > 0 {
return ctrErrors, errors.Wrapf(ErrCtrExists, "error stopping some containers")
}
-
+ defer p.newPodEvent(events.Stop)
return nil, nil
}
@@ -197,7 +198,7 @@ func (p *Pod) Pause() (map[string]error, error) {
if len(ctrErrors) > 0 {
return ctrErrors, errors.Wrapf(ErrCtrExists, "error pausing some containers")
}
-
+ defer p.newPodEvent(events.Pause)
return nil, nil
}
@@ -257,6 +258,7 @@ func (p *Pod) Unpause() (map[string]error, error) {
return ctrErrors, errors.Wrapf(ErrCtrExists, "error unpausing some containers")
}
+ defer p.newPodEvent(events.Unpause)
return nil, nil
}
@@ -309,7 +311,8 @@ func (p *Pod) Restart(ctx context.Context) (map[string]error, error) {
if len(ctrErrors) > 0 {
return ctrErrors, errors.Wrapf(ErrCtrExists, "error stopping some containers")
}
-
+ p.newPodEvent(events.Stop)
+ p.newPodEvent(events.Start)
return nil, nil
}
@@ -367,7 +370,7 @@ func (p *Pod) Kill(signal uint) (map[string]error, error) {
if len(ctrErrors) > 0 {
return ctrErrors, errors.Wrapf(ErrCtrExists, "error killing some containers")
}
-
+ defer p.newPodEvent(events.Kill)
return nil, nil
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 535b6f41b..fa208a2ca 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -223,6 +223,9 @@ type RuntimeConfig struct {
// NumLocks is the number of locks to make available for containers and
// pods.
NumLocks uint32 `toml:"num_locks,omitempty"`
+
+ // EventsLogFilePath is where the events log is stored.
+ EventsLogFilePath string `toml:-"events_logfile_path"`
}
// runtimeConfiguredFrom is a struct used during early runtime init to help
@@ -459,7 +462,6 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
}
}
}
-
return runtime, nil
}
@@ -535,6 +537,7 @@ func NewRuntimeFromConfig(configPath string, options ...RuntimeOption) (runtime
// Make a new runtime based on the given configuration
// Sets up containers/storage, state store, OCI runtime
func makeRuntime(runtime *Runtime) (err error) {
+ runtime.config.EventsLogFilePath = filepath.Join(runtime.config.TmpDir, "events", "events.log")
// Backward compatibility for `runtime_path`
if runtime.config.RuntimePath != nil {
@@ -736,6 +739,9 @@ func makeRuntime(runtime *Runtime) (err error) {
// Setting signaturepolicypath
ir.SignaturePolicyPath = runtime.config.SignaturePolicyPath
+ // Set logfile path for events
+ ir.EventsLogFilePath = runtime.config.EventsLogFilePath
+
defer func() {
if err != nil && store != nil {
// Don't forcibly shut down
@@ -768,6 +774,14 @@ func makeRuntime(runtime *Runtime) (err error) {
}
}
+ // Create events log dir
+ if err := os.MkdirAll(filepath.Dir(runtime.config.EventsLogFilePath), 0700); err != nil {
+ // The directory is allowed to exist
+ if !os.IsExist(err) {
+ return errors.Wrapf(err, "error creating events dirs %s", filepath.Dir(runtime.config.EventsLogFilePath))
+ }
+ }
+
// Make an OCI runtime to perform container operations
ociRuntime, err := newOCIRuntime(runtime.ociRuntimePath,
runtime.conmonPath, runtime.config.ConmonEnvVars,
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index cfa4f9654..c6f119913 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -10,6 +10,7 @@ import (
"strings"
"time"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
@@ -228,6 +229,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
return nil, err
}
}
+ ctr.newContainerEvent(events.Create)
return ctr, nil
}
@@ -239,7 +241,6 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, removeVolume bool) error {
r.lock.Lock()
defer r.lock.Unlock()
-
return r.removeContainer(ctx, c, force, removeVolume)
}
@@ -430,6 +431,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool,
}
}
+ c.newContainerEvent(events.Remove)
return cleanupErr
}
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index 9063390bd..0011c771a 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -10,6 +10,7 @@ import (
"strings"
"github.com/containerd/cgroups"
+ "github.com/containers/libpod/libpod/events"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -121,7 +122,7 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (*Pod,
return nil, err
}
}
-
+ pod.newPodEvent(events.Create)
return pod, nil
}
@@ -307,6 +308,6 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
// Mark pod invalid
p.valid = false
-
+ p.newPodEvent(events.Remove)
return nil
}
diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go
index 11f37ad4b..68c6c107e 100644
--- a/libpod/runtime_volume.go
+++ b/libpod/runtime_volume.go
@@ -2,9 +2,11 @@ package libpod
import (
"context"
+ "strings"
+
+ "github.com/containers/libpod/libpod/events"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
- "strings"
)
// Contains the public Runtime API for volumes
@@ -34,7 +36,6 @@ func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool) error
return nil
}
}
-
return r.removeVolume(ctx, v, force)
}
@@ -171,6 +172,7 @@ func (r *Runtime) PruneVolumes(ctx context.Context) ([]string, []error) {
}
continue
}
+ vol.newVolumeEvent(events.Prune)
prunedIDs = append(prunedIDs, vol.Name())
}
return prunedIDs, pruneErrors
diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go
index 838c0167a..b51bb8213 100644
--- a/libpod/runtime_volume_linux.go
+++ b/libpod/runtime_volume_linux.go
@@ -8,6 +8,7 @@ import (
"path/filepath"
"strings"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/storage/pkg/stringid"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
@@ -73,7 +74,7 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
if err := r.state.AddVolume(volume); err != nil {
return nil, errors.Wrapf(err, "error adding volume to state")
}
-
+ defer volume.newVolumeEvent(events.Create)
return volume, nil
}
@@ -118,7 +119,7 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error
return errors.Wrapf(err, "error cleaning up volume storage for %q", v.Name())
}
+ defer v.newVolumeEvent(events.Remove)
logrus.Debugf("Removed volume %s", v.Name())
-
return nil
}