package system import ( "context" "fmt" "os" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/podman/v5/cmd/podman/common" "github.com/containers/podman/v5/cmd/podman/registry" "github.com/containers/podman/v5/cmd/podman/validate" "github.com/containers/podman/v5/libpod/events" "github.com/containers/podman/v5/pkg/domain/entities" "github.com/spf13/cobra" ) var ( eventsDescription = `Monitor podman system events. By default, streaming mode is used, printing new events as they occur. Previous events can be listed via --since and --until.` eventsCommand = &cobra.Command{ Use: "events [options]", Args: validate.NoArgs, Short: "Show podman system events", Long: eventsDescription, RunE: eventsCmd, ValidArgsFunction: completion.AutocompleteNone, Example: `podman events podman events --filter event=create podman events --format {{.Image}} podman events --since 1h30s`, } systemEventsCommand = &cobra.Command{ Args: eventsCommand.Args, Use: eventsCommand.Use, Short: eventsCommand.Short, Long: eventsCommand.Long, RunE: eventsCommand.RunE, ValidArgsFunction: eventsCommand.ValidArgsFunction, Example: `podman system events`, } ) var ( eventOptions entities.EventsOptions eventFormat string noTrunc bool ) type Event struct { // containerExitCode is for storing the exit code of a container which can // be used for "internal" event notification ContainerExitCode *int `json:",omitempty"` // ID can be for the container, image, volume, etc ID string `json:",omitempty"` // Image used where applicable Image string `json:",omitempty"` // Name where applicable Name string `json:",omitempty"` // Network is the network name in a network event Network string `json:"network,omitempty"` // Status describes the event that occurred Status events.Status // Time the event occurred Time int64 `json:"time,omitempty"` // timeNano the event occurred in nanoseconds TimeNano int64 `json:"timeNano,omitempty"` // Type of event that occurred Type events.Type // Health status of the current container HealthStatus string `json:"health_status,omitempty"` events.Details } func newEventFromLibpodEvent(e *events.Event) Event { return Event{ ContainerExitCode: e.ContainerExitCode, ID: e.ID, Image: e.Image, Name: e.Name, Network: e.Network, Status: e.Status, Time: e.Time.Unix(), Type: e.Type, HealthStatus: e.HealthStatus, Details: e.Details, TimeNano: e.Time.UnixNano(), } } func (e *Event) ToJSONString() (string, error) { b, err := json.Marshal(e) return string(b), err } func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Command: systemEventsCommand, Parent: systemCmd, }) eventsFlags(systemEventsCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ Command: eventsCommand, }) eventsFlags(eventsCommand) } func eventsFlags(cmd *cobra.Command) { flags := cmd.Flags() filterFlagName := "filter" flags.StringArrayVarP(&eventOptions.Filter, filterFlagName, "f", []string{}, "filter output") _ = cmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompleteEventFilter) formatFlagName := "format" flags.StringVar(&eventFormat, formatFlagName, "", "format the output using a Go template") _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&Event{})) flags.BoolVar(&eventOptions.Stream, "stream", true, "stream events and do not exit when returning the last known event") sinceFlagName := "since" flags.StringVar(&eventOptions.Since, sinceFlagName, "", "show all events created since timestamp") _ = cmd.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone) flags.BoolVar(&noTrunc, "no-trunc", true, "do not truncate the output") untilFlagName := "until" flags.StringVar(&eventOptions.Until, untilFlagName, "", "show all events until timestamp") _ = cmd.RegisterFlagCompletionFunc(untilFlagName, completion.AutocompleteNone) } func eventsCmd(cmd *cobra.Command, _ []string) error { if len(eventOptions.Since) > 0 || len(eventOptions.Until) > 0 { eventOptions.FromStart = true } eventChannel := make(chan *events.Event, 1) eventOptions.EventChan = eventChannel errChannel := make(chan error) var ( rpt *report.Formatter doJSON bool ) if cmd.Flags().Changed("format") { doJSON = report.IsJSON(eventFormat) if !doJSON { var err error // Use OriginUnknown so it does not add an extra range since it // will only be called for each single element and not a slice. rpt, err = report.New(os.Stdout, cmd.Name()).Parse(report.OriginUnknown, eventFormat) if err != nil { return err } } } go func() { err := registry.ContainerEngine().Events(context.Background(), eventOptions) errChannel <- err }() for { select { case event, ok := <-eventChannel: if !ok { // channel was closed we can exit return nil } switch { case doJSON: e := newEventFromLibpodEvent(event) jsonStr, err := e.ToJSONString() if err != nil { return err } fmt.Println(jsonStr) case cmd.Flags().Changed("format"): if err := rpt.Execute(newEventFromLibpodEvent(event)); err != nil { return err } default: fmt.Println(event.ToHumanReadable(!noTrunc)) } case err := <-errChannel: // only exit in case of an error, // otherwise keep reading events until the event channel is closed if err != nil { return err } } } }