mirror of https://github.com/containers/podman.git
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:
parent
6421208e0f
commit
ca1e76ff63
47
API.md
47
API.md
|
@ -45,6 +45,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||||
|
|
||||||
[func GetContainersByContext(all: bool, latest: bool, args: []string) []string](#GetContainersByContext)
|
[func GetContainersByContext(all: bool, latest: bool, args: []string) []string](#GetContainersByContext)
|
||||||
|
|
||||||
|
[func GetEvents(options: EventInput) Event](#GetEvents)
|
||||||
|
|
||||||
[func GetImage(id: string) Image](#GetImage)
|
[func GetImage(id: string) Image](#GetImage)
|
||||||
|
|
||||||
[func GetInfo() PodmanInfo](#GetInfo)
|
[func GetInfo() PodmanInfo](#GetInfo)
|
||||||
|
@ -165,6 +167,10 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||||
|
|
||||||
[type CreateResourceConfig](#CreateResourceConfig)
|
[type CreateResourceConfig](#CreateResourceConfig)
|
||||||
|
|
||||||
|
[type Event](#Event)
|
||||||
|
|
||||||
|
[type EventInput](#EventInput)
|
||||||
|
|
||||||
[type IDMap](#IDMap)
|
[type IDMap](#IDMap)
|
||||||
|
|
||||||
[type IDMappingOptions](#IDMappingOptions)
|
[type IDMappingOptions](#IDMappingOptions)
|
||||||
|
@ -231,8 +237,12 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||||
|
|
||||||
[error RuntimeError](#RuntimeError)
|
[error RuntimeError](#RuntimeError)
|
||||||
|
|
||||||
|
[error StreamEnded](#StreamEnded)
|
||||||
|
|
||||||
[error VolumeNotFound](#VolumeNotFound)
|
[error VolumeNotFound](#VolumeNotFound)
|
||||||
|
|
||||||
|
[error WantsMoreRequired](#WantsMoreRequired)
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
### <a name="BuildImage"></a>func BuildImage
|
### <a name="BuildImage"></a>func BuildImage
|
||||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||||
|
@ -469,6 +479,11 @@ method GetContainersByContext(all: [bool](https://godoc.org/builtin#bool), lates
|
||||||
GetContainersByContext allows you to get a list of container ids depending on all, latest, or a list of
|
GetContainersByContext allows you to get a list of container ids depending on all, latest, or a list of
|
||||||
container names. The definition of latest container means the latest by creation date. In a multi-
|
container names. The definition of latest container means the latest by creation date. In a multi-
|
||||||
user environment, results might differ from what you expect.
|
user environment, results might differ from what you expect.
|
||||||
|
### <a name="GetEvents"></a>func GetEvents
|
||||||
|
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||||
|
|
||||||
|
method GetEvents(options: [EventInput](#EventInput)) [Event](#Event)</div>
|
||||||
|
GetEvents returns known libpod events filtered by the options provided.
|
||||||
### <a name="GetImage"></a>func GetImage
|
### <a name="GetImage"></a>func GetImage
|
||||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||||
|
|
||||||
|
@ -1396,6 +1411,32 @@ pids_limit [int](https://godoc.org/builtin#int)
|
||||||
shm_size [int](https://godoc.org/builtin#int)
|
shm_size [int](https://godoc.org/builtin#int)
|
||||||
|
|
||||||
ulimit [[]string](#[]string)
|
ulimit [[]string](#[]string)
|
||||||
|
### <a name="Event"></a>type Event
|
||||||
|
|
||||||
|
Event describes a libpod struct
|
||||||
|
|
||||||
|
id [string](https://godoc.org/builtin#string)
|
||||||
|
|
||||||
|
image [string](https://godoc.org/builtin#string)
|
||||||
|
|
||||||
|
name [string](https://godoc.org/builtin#string)
|
||||||
|
|
||||||
|
status [string](https://godoc.org/builtin#string)
|
||||||
|
|
||||||
|
time [string](https://godoc.org/builtin#string)
|
||||||
|
|
||||||
|
type [string](https://godoc.org/builtin#string)
|
||||||
|
### <a name="EventInput"></a>type EventInput
|
||||||
|
|
||||||
|
EventInput describes the input to obtain libpod events
|
||||||
|
|
||||||
|
filter [[]string](#[]string)
|
||||||
|
|
||||||
|
since [string](https://godoc.org/builtin#string)
|
||||||
|
|
||||||
|
stream [bool](https://godoc.org/builtin#bool)
|
||||||
|
|
||||||
|
until [string](https://godoc.org/builtin#string)
|
||||||
### <a name="IDMap"></a>type IDMap
|
### <a name="IDMap"></a>type IDMap
|
||||||
|
|
||||||
IDMap is used to describe user name spaces during container creation
|
IDMap is used to describe user name spaces during container creation
|
||||||
|
@ -1752,6 +1793,12 @@ PodNotFound means the pod could not be found by the provided name or ID in local
|
||||||
### <a name="RuntimeError"></a>type RuntimeError
|
### <a name="RuntimeError"></a>type RuntimeError
|
||||||
|
|
||||||
RuntimeErrors generally means a runtime could not be found or gotten.
|
RuntimeErrors generally means a runtime could not be found or gotten.
|
||||||
|
### <a name="StreamEnded"></a>type StreamEnded
|
||||||
|
|
||||||
|
The Podman endpoint has closed because the stream ended.
|
||||||
### <a name="VolumeNotFound"></a>type VolumeNotFound
|
### <a name="VolumeNotFound"></a>type VolumeNotFound
|
||||||
|
|
||||||
VolumeNotFound means the volume could not be found by the name or ID in local storage.
|
VolumeNotFound means the volume could not be found by the name or ID in local storage.
|
||||||
|
### <a name="WantsMoreRequired"></a>type WantsMoreRequired
|
||||||
|
|
||||||
|
The Podman endpoint requires that you use a streaming connection.
|
||||||
|
|
|
@ -53,6 +53,15 @@ type ImagesValues struct {
|
||||||
Sort string
|
Sort string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EventValues struct {
|
||||||
|
PodmanCommand
|
||||||
|
Filter []string
|
||||||
|
Format string
|
||||||
|
Since string
|
||||||
|
Stream bool
|
||||||
|
Until string
|
||||||
|
}
|
||||||
|
|
||||||
type TagValues struct {
|
type TagValues struct {
|
||||||
PodmanCommand
|
PodmanCommand
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
"github.com/containers/libpod/pkg/logs"
|
"github.com/containers/libpod/pkg/logs"
|
||||||
|
"github.com/containers/libpod/pkg/util"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -70,7 +71,7 @@ func logsCmd(c *cliconfig.LogsValues) error {
|
||||||
sinceTime := time.Time{}
|
sinceTime := time.Time{}
|
||||||
if c.Flag("since").Changed {
|
if c.Flag("since").Changed {
|
||||||
// parse time, error out if something is wrong
|
// parse time, error out if something is wrong
|
||||||
since, err := parseInputTime(c.Since)
|
since, err := util.ParseInputTime(c.Since)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "could not parse time: %q", c.Since)
|
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)
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ var (
|
||||||
// implemented.
|
// implemented.
|
||||||
var mainCommands = []*cobra.Command{
|
var mainCommands = []*cobra.Command{
|
||||||
_buildCommand,
|
_buildCommand,
|
||||||
|
_eventsCommand,
|
||||||
_exportCommand,
|
_exportCommand,
|
||||||
_historyCommand,
|
_historyCommand,
|
||||||
&_imagesCommand,
|
&_imagesCommand,
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -435,6 +435,23 @@ type Runlabel(
|
||||||
opts: [string]string
|
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
|
# GetVersion returns version and build information of the podman service
|
||||||
method GetVersion() -> (
|
method GetVersion() -> (
|
||||||
version: string,
|
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.
|
# 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)
|
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.
|
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
|
||||||
error ImageNotFound (id: string, reason: string)
|
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.
|
# RuntimeErrors generally means a runtime could not be found or gotten.
|
||||||
error RuntimeError (reason: string)
|
error RuntimeError (reason: string)
|
||||||
|
|
||||||
|
# The Podman endpoint requires that you use a streaming connection.
|
||||||
|
error WantsMoreRequired (reason: string)
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
| [podman-cp(1)](/docs/podman-cp.1.md) | Copy files/folders between a container and the local filesystem ||
|
| [podman-cp(1)](/docs/podman-cp.1.md) | Copy files/folders between a container and the local filesystem ||
|
||||||
| [podman-create(1)](/docs/podman-create.1.md) | Create a new container ||
|
| [podman-create(1)](/docs/podman-create.1.md) | Create a new container ||
|
||||||
| [podman-diff(1)](/docs/podman-diff.1.md) | Inspect changes on a container or image's filesystem |[](https://asciinema.org/a/FXfWB9CKYFwYM4EfqW3NSZy1G)|
|
| [podman-diff(1)](/docs/podman-diff.1.md) | Inspect changes on a container or image's filesystem |[](https://asciinema.org/a/FXfWB9CKYFwYM4EfqW3NSZy1G)|
|
||||||
|
| [podman-events(1)](/docs/podman-events.1.md) | Monitor Podman events ||
|
||||||
| [podman-exec(1)](/docs/podman-exec.1.md) | Execute a command in a running container
|
| [podman-exec(1)](/docs/podman-exec.1.md) | Execute a command in a running container
|
||||||
| [podman-export(1)](/docs/podman-export.1.md) | Export container's filesystem contents as a tar archive |[](https://asciinema.org/a/913lBIRAg5hK8asyIhhkQVLtV)|
|
| [podman-export(1)](/docs/podman-export.1.md) | Export container's filesystem contents as a tar archive |[](https://asciinema.org/a/913lBIRAg5hK8asyIhhkQVLtV)|
|
||||||
| [podman-generate(1)](/docs/podman-generate.1.md) | Generate structured output based on Podman containers and pods | |
|
| [podman-generate(1)](/docs/podman-generate.1.md) | Generate structured output based on Podman containers and pods | |
|
||||||
|
|
|
@ -2438,6 +2438,22 @@ _podman_play_kube() {
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_podman_events() {
|
||||||
|
local options_with_args="
|
||||||
|
--help
|
||||||
|
--h
|
||||||
|
--filter
|
||||||
|
--format
|
||||||
|
--since
|
||||||
|
--until
|
||||||
|
"
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=($(compgen -W "$options_with_args" -- "$cur"))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
_podman_container_runlabel() {
|
_podman_container_runlabel() {
|
||||||
local options_with_args="
|
local options_with_args="
|
||||||
--authfile
|
--authfile
|
||||||
|
@ -3027,6 +3043,7 @@ _podman_podman() {
|
||||||
cp
|
cp
|
||||||
create
|
create
|
||||||
diff
|
diff
|
||||||
|
events
|
||||||
exec
|
exec
|
||||||
export
|
export
|
||||||
generate
|
generate
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
% podman-events(1)
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
podman\-events- Monitor Podman events
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
**podman events** [*options*]
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
|
||||||
|
Monitor and print events that occur in Podman. Each event will include a timestamp,
|
||||||
|
a type, a status, name (if applicable), and image (if applicable).
|
||||||
|
|
||||||
|
The *container* event type will report the follow statuses:
|
||||||
|
* attach
|
||||||
|
* checkpoint
|
||||||
|
* cleanup
|
||||||
|
* commit
|
||||||
|
* create
|
||||||
|
* exec
|
||||||
|
* export
|
||||||
|
* import
|
||||||
|
* init
|
||||||
|
* kill
|
||||||
|
* mount
|
||||||
|
* pause
|
||||||
|
* prune
|
||||||
|
* remove
|
||||||
|
* restore
|
||||||
|
* start
|
||||||
|
* stop
|
||||||
|
* sync
|
||||||
|
* unmount
|
||||||
|
* unpause
|
||||||
|
* wait
|
||||||
|
|
||||||
|
The *pod* event type will report the follow statuses:
|
||||||
|
* create
|
||||||
|
* kill
|
||||||
|
* pause
|
||||||
|
* remove
|
||||||
|
* start
|
||||||
|
* stop
|
||||||
|
* unpause
|
||||||
|
|
||||||
|
The *image* event type will report the following statuses:
|
||||||
|
* prune
|
||||||
|
* pull
|
||||||
|
* push
|
||||||
|
* remove
|
||||||
|
* save
|
||||||
|
* tag
|
||||||
|
* untag
|
||||||
|
|
||||||
|
The *volume* type will report the following statuses:
|
||||||
|
* create
|
||||||
|
* prune
|
||||||
|
* remove
|
||||||
|
|
||||||
|
|
||||||
|
## OPTIONS
|
||||||
|
|
||||||
|
**--help**
|
||||||
|
|
||||||
|
Print usage statement.
|
||||||
|
|
||||||
|
**--format**
|
||||||
|
|
||||||
|
Format the output using the given Go template. An output value of *json* is not supported.
|
||||||
|
|
||||||
|
|
||||||
|
**--filter**=[]
|
||||||
|
|
||||||
|
Filter events that are displayed. They must be in the format of "filter=value". The following
|
||||||
|
filters are supported:
|
||||||
|
* container=name_or_id
|
||||||
|
* event=event_status (described above)
|
||||||
|
* image=name_or_id
|
||||||
|
* pod=name_or_id
|
||||||
|
* volume=name_or_id
|
||||||
|
* type=event_type (described above)
|
||||||
|
|
||||||
|
In the case where an ID is used, the ID may be in its full or shortened form.
|
||||||
|
|
||||||
|
**--since**=[]
|
||||||
|
|
||||||
|
Show all events created since the given timestamp
|
||||||
|
|
||||||
|
|
||||||
|
**--until**=[]
|
||||||
|
|
||||||
|
Show all events created until the given timestamp
|
||||||
|
|
||||||
|
The *since* and *until* values can be RFC3339Nano time stamps or a Go duration string such as 10m, 5h. If no
|
||||||
|
*since* or *until* values are provided, only new events will be shown.
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
Showing podman events
|
||||||
|
```
|
||||||
|
$ podman events
|
||||||
|
2019-03-02 10:33:42.312377447 -0600 CST container create 34503c192940 (image=docker.io/library/alpine:latest, name=friendly_allen)
|
||||||
|
2019-03-02 10:33:46.958768077 -0600 CST container init 34503c192940 (image=docker.io/library/alpine:latest, name=friendly_allen)
|
||||||
|
2019-03-02 10:33:46.973661968 -0600 CST container start 34503c192940 (image=docker.io/library/alpine:latest, name=friendly_allen)
|
||||||
|
2019-03-02 10:33:50.833761479 -0600 CST container stop 34503c192940 (image=docker.io/library/alpine:latest, name=friendly_allen)
|
||||||
|
2019-03-02 10:33:51.047104966 -0600 CST container cleanup 34503c192940 (image=docker.io/library/alpine:latest, name=friendly_allen)
|
||||||
|
```
|
||||||
|
|
||||||
|
Show only podman create events
|
||||||
|
```
|
||||||
|
$ podman events --filter event=create
|
||||||
|
2019-03-02 10:36:01.375685062 -0600 CST container create 20dc581f6fbf (image=docker.io/library/alpine:latest, name=sharp_morse)
|
||||||
|
2019-03-02 10:36:08.561188337 -0600 CST container create 58e7e002344c (image=k8s.gcr.io/pause:3.1, name=3e701f270d54-infra)
|
||||||
|
2019-03-02 10:36:13.146899437 -0600 CST volume create cad6dc50e087 (image=, name=cad6dc50e0879568e7d656bd004bd343d6035e7fc4024e1711506fe2fd459e6f)
|
||||||
|
2019-03-02 10:36:29.978806894 -0600 CST container create d81e30f1310f (image=docker.io/library/busybox:latest, name=musing_newton)
|
||||||
|
```
|
||||||
|
|
||||||
|
Show only podman pod create events
|
||||||
|
```
|
||||||
|
$ podman events --filter event=create --filter type=pod
|
||||||
|
2019-03-02 10:44:29.601746633 -0600 CST pod create 1df5ebca7b44 (image=, name=confident_hawking)
|
||||||
|
2019-03-02 10:44:42.374637304 -0600 CST pod create ca731231718e (image=, name=webapp)
|
||||||
|
2019-03-02 10:44:47.486759133 -0600 CST pod create 71e807fc3a8e (image=, name=reverent_swanson)
|
||||||
|
```
|
||||||
|
|
||||||
|
Show only podman events created in the last five minutes:
|
||||||
|
```
|
||||||
|
$ sudo podman events --since 5m
|
||||||
|
2019-03-02 10:44:29.598835409 -0600 CST container create b629d10d3831 (image=k8s.gcr.io/pause:3.1, name=1df5ebca7b44-infra)
|
||||||
|
2019-03-02 10:44:29.601746633 -0600 CST pod create 1df5ebca7b44 (image=, name=confident_hawking)
|
||||||
|
2019-03-02 10:44:42.371100253 -0600 CST container create 170a0f457d00 (image=k8s.gcr.io/pause:3.1, name=ca731231718e-infra)
|
||||||
|
2019-03-02 10:44:42.374637304 -0600 CST pod create ca731231718e (image=, name=webapp)
|
||||||
|
```
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
podman(1)
|
||||||
|
|
||||||
|
## HISTORY
|
||||||
|
March 2019, Originally compiled by Brent Baude <bbaude@redhat.com>
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/libpod/libpod/driver"
|
"github.com/containers/libpod/libpod/driver"
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/containers/libpod/pkg/inspect"
|
"github.com/containers/libpod/pkg/inspect"
|
||||||
"github.com/containers/libpod/pkg/lookup"
|
"github.com/containers/libpod/pkg/lookup"
|
||||||
"github.com/containers/storage/pkg/stringid"
|
"github.com/containers/storage/pkg/stringid"
|
||||||
|
@ -88,6 +89,7 @@ func (c *Container) Start(ctx context.Context, recursive bool) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the container
|
// Start the container
|
||||||
|
defer c.newContainerEvent(events.Start)
|
||||||
return c.start()
|
return c.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +127,8 @@ func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams,
|
||||||
}
|
}
|
||||||
close(attachChan)
|
close(attachChan)
|
||||||
}()
|
}()
|
||||||
|
c.newContainerEvent(events.Start)
|
||||||
|
c.newContainerEvent(events.Attach)
|
||||||
return attachChan, nil
|
return attachChan, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +183,7 @@ func (c *Container) StopWithTimeout(timeout uint) error {
|
||||||
c.state.State == ContainerStateExited {
|
c.state.State == ContainerStateExited {
|
||||||
return ErrCtrStopped
|
return ErrCtrStopped
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Stop)
|
||||||
return c.stop(timeout)
|
return c.stop(timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +201,7 @@ func (c *Container) Kill(signal uint) error {
|
||||||
if c.state.State != ContainerStateRunning {
|
if c.state.State != ContainerStateRunning {
|
||||||
return errors.Wrapf(ErrCtrStateInvalid, "can only kill running containers")
|
return errors.Wrapf(ErrCtrStateInvalid, "can only kill running containers")
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Kill)
|
||||||
return c.runtime.ociRuntime.killContainer(c, signal)
|
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
|
// TODO handle this better
|
||||||
return errors.Wrapf(err, "error saving exec sessions %s for container %s", sessionID, c.ID())
|
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())
|
logrus.Debugf("Successfully started exec session %s in container %s", sessionID, c.ID())
|
||||||
|
|
||||||
// Unlock so other processes can use the container
|
// 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 {
|
if err := c.save(); err != nil {
|
||||||
logrus.Errorf("Error removing exec session %s from container %s state: %v", sessionID, c.ID(), err)
|
logrus.Errorf("Error removing exec session %s from container %s state: %v", sessionID, c.ID(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return waitErr
|
return waitErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +392,7 @@ func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan re
|
||||||
c.state.State != ContainerStateExited {
|
c.state.State != ContainerStateExited {
|
||||||
return errors.Wrapf(ErrCtrStateInvalid, "can only attach to created or running containers")
|
return errors.Wrapf(ErrCtrStateInvalid, "can only attach to created or running containers")
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Attach)
|
||||||
return c.attach(streams, keys, resize, false)
|
return c.attach(streams, keys, resize, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,7 +407,7 @@ func (c *Container) Mount() (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Mount)
|
||||||
return c.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())
|
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)
|
return c.unmount(force)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,7 +458,7 @@ func (c *Container) Pause() error {
|
||||||
if c.state.State != ContainerStateRunning {
|
if c.state.State != ContainerStateRunning {
|
||||||
return errors.Wrapf(ErrCtrStateInvalid, "%q is not running, can't pause", c.state.State)
|
return errors.Wrapf(ErrCtrStateInvalid, "%q is not running, can't pause", c.state.State)
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Pause)
|
||||||
return c.pause()
|
return c.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,7 +476,7 @@ func (c *Container) Unpause() error {
|
||||||
if c.state.State != ContainerStatePaused {
|
if c.state.State != ContainerStatePaused {
|
||||||
return errors.Wrapf(ErrCtrStateInvalid, "%q is not paused, can't unpause", c.ID())
|
return errors.Wrapf(ErrCtrStateInvalid, "%q is not paused, can't unpause", c.ID())
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Unpause)
|
||||||
return c.unpause()
|
return c.unpause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,7 +491,7 @@ func (c *Container) Export(path string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Export)
|
||||||
return c.export(path)
|
return c.export(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,7 +545,6 @@ func (c *Container) Inspect(size bool) (*inspect.ContainerInspectData, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error getting graph driver info %q", c.ID())
|
return nil, errors.Wrapf(err, "error getting graph driver info %q", c.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.getContainerInspectData(size, driverData)
|
return c.getContainerInspectData(size, driverData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,6 +576,7 @@ func (c *Container) WaitWithInterval(waitTimeout time.Duration) (int32, error) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
exitCode := c.state.ExitCode
|
exitCode := c.state.ExitCode
|
||||||
|
c.newContainerEvent(events.Wait)
|
||||||
return exitCode, nil
|
return exitCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,7 +600,7 @@ func (c *Container) Cleanup(ctx context.Context) error {
|
||||||
if len(c.state.ExecSessions) != 0 {
|
if len(c.state.ExecSessions) != 0 {
|
||||||
return errors.Wrapf(ErrCtrStateInvalid, "container %s has active exec sessions, refusing to clean up", c.ID())
|
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)
|
return c.cleanup(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,7 +670,7 @@ func (c *Container) Sync() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Sync)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -772,7 +775,6 @@ func (c *Container) Refresh(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -800,7 +802,7 @@ func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointO
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Checkpoint)
|
||||||
return c.checkpoint(ctx, options)
|
return c.checkpoint(ctx, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,6 +817,6 @@ func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOpti
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Restore)
|
||||||
return c.restore(ctx, options)
|
return c.restore(ctx, options)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/containers/buildah"
|
"github.com/containers/buildah"
|
||||||
"github.com/containers/buildah/util"
|
"github.com/containers/buildah/util"
|
||||||
is "github.com/containers/image/storage"
|
is "github.com/containers/image/storage"
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/containers/libpod/libpod/image"
|
"github.com/containers/libpod/libpod/image"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -177,5 +178,6 @@ func (c *Container) Commit(ctx context.Context, destImage string, options Contai
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Commit)
|
||||||
return c.runtime.imageRuntime.NewFromLocal(id)
|
return c.runtime.imageRuntime.NewFromLocal(id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/containers/libpod/pkg/ctime"
|
"github.com/containers/libpod/pkg/ctime"
|
||||||
"github.com/containers/libpod/pkg/hooks"
|
"github.com/containers/libpod/pkg/hooks"
|
||||||
"github.com/containers/libpod/pkg/hooks/exec"
|
"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 {
|
if err := c.save(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer c.newContainerEvent(events.Init)
|
||||||
return c.completeNetworkSetup()
|
return c.completeNetworkSetup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1022,7 +1023,6 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.start()
|
return c.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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})
|
||||||
|
}
|
|
@ -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/image/types"
|
||||||
"github.com/containers/libpod/libpod/common"
|
"github.com/containers/libpod/libpod/common"
|
||||||
"github.com/containers/libpod/libpod/driver"
|
"github.com/containers/libpod/libpod/driver"
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/containers/libpod/pkg/inspect"
|
"github.com/containers/libpod/pkg/inspect"
|
||||||
"github.com/containers/libpod/pkg/registries"
|
"github.com/containers/libpod/pkg/registries"
|
||||||
"github.com/containers/libpod/pkg/util"
|
"github.com/containers/libpod/pkg/util"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/reexec"
|
"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"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/opentracing/opentracing-go"
|
"github.com/opentracing/opentracing-go"
|
||||||
|
@ -64,6 +65,7 @@ type Image struct {
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
store storage.Store
|
store storage.Store
|
||||||
SignaturePolicyPath string
|
SignaturePolicyPath string
|
||||||
|
EventsLogFilePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrRepoTagNotFound is the error returned when the image id given doesn't match a rep tag in store
|
// 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
|
newImage.image = img
|
||||||
newImages = append(newImages, &newImage)
|
newImages = append(newImages, &newImage)
|
||||||
}
|
}
|
||||||
|
ir.newImageEvent(events.LoadFromArchive, "")
|
||||||
return newImages, nil
|
return newImages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,6 +373,7 @@ func (i *Image) Remove(force bool) error {
|
||||||
}
|
}
|
||||||
parent = nextParent
|
parent = nextParent
|
||||||
}
|
}
|
||||||
|
defer i.newImageEvent(events.Remove)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,6 +497,7 @@ func (i *Image) TagImage(tag string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
i.reloadImage()
|
i.reloadImage()
|
||||||
|
defer i.newImageEvent(events.Tag)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,6 +518,7 @@ func (i *Image) UntagImage(tag string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
i.reloadImage()
|
i.reloadImage()
|
||||||
|
defer i.newImageEvent(events.Untag)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,6 +567,7 @@ func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageRefere
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Error copying image to the remote destination")
|
return errors.Wrapf(err, "Error copying image to the remote destination")
|
||||||
}
|
}
|
||||||
|
defer i.newImageEvent(events.Push)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -711,7 +717,6 @@ func (i *Image) History(ctx context.Context) ([]*History, error) {
|
||||||
Comment: oci.History[i].Comment,
|
Comment: oci.History[i].Comment,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return allHistory, nil
|
return allHistory, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -927,7 +932,11 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// 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)
|
return errors.Wrapf(err, "unable to save %q", source)
|
||||||
}
|
}
|
||||||
|
defer i.newImageEvent(events.Save)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1180,3 +1189,26 @@ func (i *Image) GetHealthCheck(ctx context.Context) (*manifest.Schema2HealthConf
|
||||||
}
|
}
|
||||||
return configBlob.ContainerConfig.Healthcheck, nil
|
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
|
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
|
// GetPruneImages returns a slice of images that have no names/unused
|
||||||
func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) {
|
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 {
|
if err := p.Remove(true); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to prune image")
|
return nil, errors.Wrap(err, "failed to prune image")
|
||||||
}
|
}
|
||||||
|
defer p.newImageEvent(events.Prune)
|
||||||
prunedCids = append(prunedCids, p.ID())
|
prunedCids = append(prunedCids, p.ID())
|
||||||
}
|
}
|
||||||
return prunedCids, nil
|
return prunedCids, nil
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/containers/image/transports"
|
"github.com/containers/image/transports"
|
||||||
"github.com/containers/image/transports/alltransports"
|
"github.com/containers/image/transports/alltransports"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/containers/libpod/pkg/registries"
|
"github.com/containers/libpod/pkg/registries"
|
||||||
multierror "github.com/hashicorp/go-multierror"
|
multierror "github.com/hashicorp/go-multierror"
|
||||||
opentracing "github.com/opentracing/opentracing-go"
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
|
@ -273,6 +274,7 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !goal.pullAllPairs {
|
if !goal.pullAllPairs {
|
||||||
|
ir.newImageEvent(events.Pull, "")
|
||||||
return []string{imageInfo.image}, nil
|
return []string{imageInfo.image}, nil
|
||||||
}
|
}
|
||||||
images = append(images, imageInfo.image)
|
images = append(images, imageInfo.image)
|
||||||
|
@ -293,6 +295,9 @@ func (ir *Runtime) doPullImage(ctx context.Context, sc *types.SystemContext, goa
|
||||||
}
|
}
|
||||||
return nil, pullErrors
|
return nil, pullErrors
|
||||||
}
|
}
|
||||||
|
if len(images) > 0 {
|
||||||
|
defer ir.newImageEvent(events.Pull, images[0])
|
||||||
|
}
|
||||||
return images, nil
|
return images, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -286,7 +286,6 @@ func WithTmpDir(dir string) RuntimeOption {
|
||||||
if rt.valid {
|
if rt.valid {
|
||||||
return ErrRuntimeFinalized
|
return ErrRuntimeFinalized
|
||||||
}
|
}
|
||||||
|
|
||||||
rt.config.TmpDir = dir
|
rt.config.TmpDir = dir
|
||||||
rt.configuredFrom.libpodTmpDirSet = true
|
rt.configuredFrom.libpodTmpDirSet = true
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package libpod
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/ulule/deepcopier"
|
"github.com/ulule/deepcopier"
|
||||||
|
@ -58,7 +59,7 @@ func (p *Pod) Start(ctx context.Context) (map[string]error, error) {
|
||||||
if len(ctrErrors) > 0 {
|
if len(ctrErrors) > 0 {
|
||||||
return ctrErrors, errors.Wrapf(ErrCtrExists, "error starting some containers")
|
return ctrErrors, errors.Wrapf(ErrCtrExists, "error starting some containers")
|
||||||
}
|
}
|
||||||
|
defer p.newPodEvent(events.Start)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +139,7 @@ func (p *Pod) StopWithTimeout(ctx context.Context, cleanup bool, timeout int) (m
|
||||||
if len(ctrErrors) > 0 {
|
if len(ctrErrors) > 0 {
|
||||||
return ctrErrors, errors.Wrapf(ErrCtrExists, "error stopping some containers")
|
return ctrErrors, errors.Wrapf(ErrCtrExists, "error stopping some containers")
|
||||||
}
|
}
|
||||||
|
defer p.newPodEvent(events.Stop)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +198,7 @@ func (p *Pod) Pause() (map[string]error, error) {
|
||||||
if len(ctrErrors) > 0 {
|
if len(ctrErrors) > 0 {
|
||||||
return ctrErrors, errors.Wrapf(ErrCtrExists, "error pausing some containers")
|
return ctrErrors, errors.Wrapf(ErrCtrExists, "error pausing some containers")
|
||||||
}
|
}
|
||||||
|
defer p.newPodEvent(events.Pause)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,6 +258,7 @@ func (p *Pod) Unpause() (map[string]error, error) {
|
||||||
return ctrErrors, errors.Wrapf(ErrCtrExists, "error unpausing some containers")
|
return ctrErrors, errors.Wrapf(ErrCtrExists, "error unpausing some containers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer p.newPodEvent(events.Unpause)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +311,8 @@ func (p *Pod) Restart(ctx context.Context) (map[string]error, error) {
|
||||||
if len(ctrErrors) > 0 {
|
if len(ctrErrors) > 0 {
|
||||||
return ctrErrors, errors.Wrapf(ErrCtrExists, "error stopping some containers")
|
return ctrErrors, errors.Wrapf(ErrCtrExists, "error stopping some containers")
|
||||||
}
|
}
|
||||||
|
p.newPodEvent(events.Stop)
|
||||||
|
p.newPodEvent(events.Start)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +370,7 @@ func (p *Pod) Kill(signal uint) (map[string]error, error) {
|
||||||
if len(ctrErrors) > 0 {
|
if len(ctrErrors) > 0 {
|
||||||
return ctrErrors, errors.Wrapf(ErrCtrExists, "error killing some containers")
|
return ctrErrors, errors.Wrapf(ErrCtrExists, "error killing some containers")
|
||||||
}
|
}
|
||||||
|
defer p.newPodEvent(events.Kill)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,6 +223,9 @@ type RuntimeConfig struct {
|
||||||
// NumLocks is the number of locks to make available for containers and
|
// NumLocks is the number of locks to make available for containers and
|
||||||
// pods.
|
// pods.
|
||||||
NumLocks uint32 `toml:"num_locks,omitempty"`
|
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
|
// 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
|
return runtime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,6 +537,7 @@ func NewRuntimeFromConfig(configPath string, options ...RuntimeOption) (runtime
|
||||||
// Make a new runtime based on the given configuration
|
// Make a new runtime based on the given configuration
|
||||||
// Sets up containers/storage, state store, OCI runtime
|
// Sets up containers/storage, state store, OCI runtime
|
||||||
func makeRuntime(runtime *Runtime) (err error) {
|
func makeRuntime(runtime *Runtime) (err error) {
|
||||||
|
runtime.config.EventsLogFilePath = filepath.Join(runtime.config.TmpDir, "events", "events.log")
|
||||||
|
|
||||||
// Backward compatibility for `runtime_path`
|
// Backward compatibility for `runtime_path`
|
||||||
if runtime.config.RuntimePath != nil {
|
if runtime.config.RuntimePath != nil {
|
||||||
|
@ -736,6 +739,9 @@ func makeRuntime(runtime *Runtime) (err error) {
|
||||||
|
|
||||||
// Setting signaturepolicypath
|
// Setting signaturepolicypath
|
||||||
ir.SignaturePolicyPath = runtime.config.SignaturePolicyPath
|
ir.SignaturePolicyPath = runtime.config.SignaturePolicyPath
|
||||||
|
// Set logfile path for events
|
||||||
|
ir.EventsLogFilePath = runtime.config.EventsLogFilePath
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil && store != nil {
|
if err != nil && store != nil {
|
||||||
// Don't forcibly shut down
|
// 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
|
// Make an OCI runtime to perform container operations
|
||||||
ociRuntime, err := newOCIRuntime(runtime.ociRuntimePath,
|
ociRuntime, err := newOCIRuntime(runtime.ociRuntimePath,
|
||||||
runtime.conmonPath, runtime.config.ConmonEnvVars,
|
runtime.conmonPath, runtime.config.ConmonEnvVars,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/containers/libpod/libpod/image"
|
"github.com/containers/libpod/libpod/image"
|
||||||
"github.com/containers/libpod/pkg/rootless"
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
|
@ -228,6 +229,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ctr.newContainerEvent(events.Create)
|
||||||
return ctr, nil
|
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 {
|
func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, removeVolume bool) error {
|
||||||
r.lock.Lock()
|
r.lock.Lock()
|
||||||
defer r.lock.Unlock()
|
defer r.lock.Unlock()
|
||||||
|
|
||||||
return r.removeContainer(ctx, c, force, removeVolume)
|
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
|
return cleanupErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/cgroups"
|
"github.com/containerd/cgroups"
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
@ -121,7 +122,7 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (*Pod,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pod.newPodEvent(events.Create)
|
||||||
return pod, nil
|
return pod, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,6 +308,6 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool)
|
||||||
|
|
||||||
// Mark pod invalid
|
// Mark pod invalid
|
||||||
p.valid = false
|
p.valid = false
|
||||||
|
p.newPodEvent(events.Remove)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,11 @@ package libpod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Contains the public Runtime API for volumes
|
// 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 nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.removeVolume(ctx, v, force)
|
return r.removeVolume(ctx, v, force)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +172,7 @@ func (r *Runtime) PruneVolumes(ctx context.Context) ([]string, []error) {
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
vol.newVolumeEvent(events.Prune)
|
||||||
prunedIDs = append(prunedIDs, vol.Name())
|
prunedIDs = append(prunedIDs, vol.Name())
|
||||||
}
|
}
|
||||||
return prunedIDs, pruneErrors
|
return prunedIDs, pruneErrors
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/containers/storage/pkg/stringid"
|
"github.com/containers/storage/pkg/stringid"
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
"github.com/pkg/errors"
|
"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 {
|
if err := r.state.AddVolume(volume); err != nil {
|
||||||
return nil, errors.Wrapf(err, "error adding volume to state")
|
return nil, errors.Wrapf(err, "error adding volume to state")
|
||||||
}
|
}
|
||||||
|
defer volume.newVolumeEvent(events.Create)
|
||||||
return volume, nil
|
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())
|
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())
|
logrus.Debugf("Removed volume %s", v.Name())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
package adapter
|
package adapter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/containers/buildah"
|
"github.com/containers/buildah"
|
||||||
"github.com/containers/buildah/imagebuildah"
|
"github.com/containers/buildah/imagebuildah"
|
||||||
|
@ -16,7 +18,9 @@ import (
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||||
|
"github.com/containers/libpod/cmd/podman/shared"
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/containers/libpod/libpod/image"
|
"github.com/containers/libpod/libpod/image"
|
||||||
"github.com/containers/libpod/pkg/rootless"
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -377,3 +381,52 @@ func (r *LocalRuntime) JoinOrCreateRootlessPod(pod *Pod) (bool, int, error) {
|
||||||
|
|
||||||
return rootless.BecomeRootInUserNSWithOpts(&opts)
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/buildah/imagebuildah"
|
"github.com/containers/buildah/imagebuildah"
|
||||||
|
@ -18,6 +19,7 @@ import (
|
||||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||||
"github.com/containers/libpod/cmd/podman/varlink"
|
"github.com/containers/libpod/cmd/podman/varlink"
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/events"
|
||||||
"github.com/containers/libpod/libpod/image"
|
"github.com/containers/libpod/libpod/image"
|
||||||
"github.com/containers/libpod/utils"
|
"github.com/containers/libpod/utils"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"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
|
// Nothing to do in the remote case
|
||||||
return true, 0, nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
|
@ -347,3 +348,25 @@ func StorageConfigFile() string {
|
||||||
}
|
}
|
||||||
return storage.DefaultConfigFile
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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{})
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ type PodmanTestIntegration struct {
|
||||||
CgroupManager string
|
CgroupManager string
|
||||||
Host HostOS
|
Host HostOS
|
||||||
Timings []string
|
Timings []string
|
||||||
|
TmpDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
var LockTmpDir string
|
var LockTmpDir string
|
||||||
|
@ -245,6 +246,7 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration {
|
||||||
},
|
},
|
||||||
ConmonBinary: conmonBinary,
|
ConmonBinary: conmonBinary,
|
||||||
CrioRoot: filepath.Join(tempDir, "crio"),
|
CrioRoot: filepath.Join(tempDir, "crio"),
|
||||||
|
TmpDir: tempDir,
|
||||||
CNIConfigDir: CNIConfigDir,
|
CNIConfigDir: CNIConfigDir,
|
||||||
OCIRuntime: ociRuntime,
|
OCIRuntime: ociRuntime,
|
||||||
RunRoot: filepath.Join(tempDir, "crio-run"),
|
RunRoot: filepath.Join(tempDir, "crio-run"),
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
. "github.com/containers/libpod/test/utils"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Podman events", func() {
|
||||||
|
var (
|
||||||
|
tempdir string
|
||||||
|
err error
|
||||||
|
podmanTest *PodmanTestIntegration
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
tempdir, err = CreateTempDirInTempDir()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
podmanTest = PodmanTestCreate(tempdir)
|
||||||
|
podmanTest.RestoreAllArtifacts()
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
podmanTest.Cleanup()
|
||||||
|
f := CurrentGinkgoTestDescription()
|
||||||
|
timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
|
||||||
|
GinkgoWriter.Write([]byte(timedResult))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
// For most, all, of these tests we do not "live" test following a log because it may make a fragile test
|
||||||
|
// system more complex. Instead we run the "events" and then verify that the events are processed correctly.
|
||||||
|
// Perhaps a future version of this test would put events in a go func and send output back over a channel
|
||||||
|
// while events occur.
|
||||||
|
It("podman events", func() {
|
||||||
|
_, ec, _ := podmanTest.RunLsContainer("")
|
||||||
|
Expect(ec).To(Equal(0))
|
||||||
|
result := podmanTest.Podman([]string{"events", "--stream=false"})
|
||||||
|
result.WaitWithDefaultTimeout()
|
||||||
|
Expect(result.ExitCode()).To(BeZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman events with an event filter", func() {
|
||||||
|
SkipIfRemote()
|
||||||
|
_, ec, _ := podmanTest.RunLsContainer("")
|
||||||
|
Expect(ec).To(Equal(0))
|
||||||
|
result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "event=start"})
|
||||||
|
result.WaitWithDefaultTimeout()
|
||||||
|
Expect(result.ExitCode()).To(Equal(0))
|
||||||
|
Expect(len(result.OutputToStringArray())).To(Equal(1))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman events with an event filter and container=cid", func() {
|
||||||
|
SkipIfRemote()
|
||||||
|
_, ec, cid := podmanTest.RunLsContainer("")
|
||||||
|
Expect(ec).To(Equal(0))
|
||||||
|
_, ec2, cid2 := podmanTest.RunLsContainer("")
|
||||||
|
Expect(ec2).To(Equal(0))
|
||||||
|
result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "event=start", "--filter", fmt.Sprintf("container=%s", cid)})
|
||||||
|
result.WaitWithDefaultTimeout()
|
||||||
|
Expect(result.ExitCode()).To(Equal(0))
|
||||||
|
Expect(len(result.OutputToStringArray())).To(Equal(1))
|
||||||
|
Expect(!strings.Contains(result.OutputToString(), cid2))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman events with a type", func() {
|
||||||
|
SkipIfRemote()
|
||||||
|
_, ec, _ := podmanTest.RunLsContainer("")
|
||||||
|
Expect(ec).To(Equal(0))
|
||||||
|
result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "type=pod"})
|
||||||
|
result.WaitWithDefaultTimeout()
|
||||||
|
Expect(result.ExitCode()).To(Equal(0))
|
||||||
|
Expect(len(result.OutputToStringArray())).To(Equal(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman events with a type", func() {
|
||||||
|
SkipIfRemote()
|
||||||
|
setup := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:foobar", ALPINE, "top"})
|
||||||
|
setup.WaitWithDefaultTimeout()
|
||||||
|
stop := podmanTest.Podman([]string{"pod", "stop", "foobar"})
|
||||||
|
stop.WaitWithDefaultTimeout()
|
||||||
|
Expect(stop.ExitCode()).To(Equal(0))
|
||||||
|
Expect(setup.ExitCode()).To(Equal(0))
|
||||||
|
result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "type=pod"})
|
||||||
|
result.WaitWithDefaultTimeout()
|
||||||
|
Expect(result.ExitCode()).To(Equal(0))
|
||||||
|
fmt.Println(result.OutputToStringArray())
|
||||||
|
Expect(len(result.OutputToStringArray())).To(Equal(2))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman events --since", func() {
|
||||||
|
_, ec, _ := podmanTest.RunLsContainer("")
|
||||||
|
Expect(ec).To(Equal(0))
|
||||||
|
result := podmanTest.Podman([]string{"events", "--stream=false", "--since", "1m"})
|
||||||
|
result.WaitWithDefaultTimeout()
|
||||||
|
Expect(result.ExitCode()).To(BeZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman events --until", func() {
|
||||||
|
_, ec, _ := podmanTest.RunLsContainer("")
|
||||||
|
Expect(ec).To(Equal(0))
|
||||||
|
test := podmanTest.Podman([]string{"events", "--help"})
|
||||||
|
test.WaitWithDefaultTimeout()
|
||||||
|
fmt.Println(test.OutputToStringArray())
|
||||||
|
result := podmanTest.Podman([]string{"events", "--stream=false", "--since", "1h"})
|
||||||
|
result.WaitWithDefaultTimeout()
|
||||||
|
Expect(result.ExitCode()).To(BeZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
|
@ -206,8 +206,8 @@ func PodmanTestCreate(tempDir string) *PodmanTestIntegration {
|
||||||
|
|
||||||
//MakeOptions assembles all the podman main options
|
//MakeOptions assembles all the podman main options
|
||||||
func (p *PodmanTestIntegration) makeOptions(args []string) []string {
|
func (p *PodmanTestIntegration) makeOptions(args []string) []string {
|
||||||
podmanOptions := strings.Split(fmt.Sprintf("--root %s --runroot %s --runtime %s --conmon %s --cni-config-dir %s --cgroup-manager %s",
|
podmanOptions := strings.Split(fmt.Sprintf("--root %s --runroot %s --runtime %s --conmon %s --cni-config-dir %s --cgroup-manager %s --tmpdir %s",
|
||||||
p.CrioRoot, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager), " ")
|
p.CrioRoot, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager, p.TmpDir), " ")
|
||||||
if os.Getenv("HOOK_OPTION") != "" {
|
if os.Getenv("HOOK_OPTION") != "" {
|
||||||
podmanOptions = append(podmanOptions, os.Getenv("HOOK_OPTION"))
|
podmanOptions = append(podmanOptions, os.Getenv("HOOK_OPTION"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ There are other equivalents for these tools
|
||||||
| `docker container`|[`podman container`](./docs/podman-container.1.md) |
|
| `docker container`|[`podman container`](./docs/podman-container.1.md) |
|
||||||
| `docker create` | [`podman create`](./docs/podman-create.1.md) |
|
| `docker create` | [`podman create`](./docs/podman-create.1.md) |
|
||||||
| `docker diff` | [`podman diff`](./docs/podman-diff.1.md) |
|
| `docker diff` | [`podman diff`](./docs/podman-diff.1.md) |
|
||||||
|
| `docker events` | [`podman events`](./docs/podman-events.1.md) |
|
||||||
| `docker export` | [`podman export`](./docs/podman-export.1.md) |
|
| `docker export` | [`podman export`](./docs/podman-export.1.md) |
|
||||||
| `docker history` | [`podman history`](./docs/podman-history.1.md) |
|
| `docker history` | [`podman history`](./docs/podman-history.1.md) |
|
||||||
| `docker image` | [`podman image`](./docs/podman-image.1.md) |
|
| `docker image` | [`podman image`](./docs/podman-image.1.md) |
|
||||||
|
@ -89,7 +90,6 @@ Those Docker commands currently do not have equivalents in `podman`:
|
||||||
|
|
||||||
| Missing command | Description|
|
| Missing command | Description|
|
||||||
| :--- | :--- |
|
| :--- | :--- |
|
||||||
| `docker events` ||
|
|
||||||
| `docker network` ||
|
| `docker network` ||
|
||||||
| `docker node` ||
|
| `docker node` ||
|
||||||
| `docker plugin` | podman does not support plugins. We recommend you use alternative OCI Runtimes or OCI Runtime Hooks to alter behavior of podman.|
|
| `docker plugin` | podman does not support plugins. We recommend you use alternative OCI Runtimes or OCI Runtime Hooks to alter behavior of podman.|
|
||||||
|
|
Loading…
Reference in New Issue