mirror of
https://github.com/containers/podman.git
synced 2025-06-21 17:38:12 +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:
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
81
libpod/events.go
Normal file
81
libpod/events.go
Normal file
@ -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})
|
||||
}
|
264
libpod/events/events.go
Normal file
264
libpod/events/events.go
Normal file
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -286,7 +286,6 @@ func WithTmpDir(dir string) RuntimeOption {
|
||||
if rt.valid {
|
||||
return ErrRuntimeFinalized
|
||||
}
|
||||
|
||||
rt.config.TmpDir = dir
|
||||
rt.configuredFrom.libpodTmpDirSet = true
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user