From ca1e76ff632dec0b0e00e25f26677887ca8cc625 Mon Sep 17 00:00:00 2001 From: baude Date: Thu, 28 Feb 2019 14:15:56 -0600 Subject: 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 --- libpod/events/events.go | 264 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 libpod/events/events.go (limited to 'libpod/events') 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) +} -- cgit v1.2.3-54-g00ecf