diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/podmanV2/containers/mount.go | 3 | ||||
-rw-r--r-- | cmd/podmanV2/containers/stop.go | 2 | ||||
-rw-r--r-- | cmd/podmanV2/containers/wait.go | 2 | ||||
-rw-r--r-- | cmd/podmanV2/images/history.go | 9 | ||||
-rw-r--r-- | cmd/podmanV2/images/inspect.go | 3 | ||||
-rw-r--r-- | cmd/podmanV2/images/rm.go | 8 | ||||
-rw-r--r-- | cmd/podmanV2/images/rmi.go | 1 | ||||
-rw-r--r-- | cmd/podmanV2/main.go | 33 | ||||
-rw-r--r-- | cmd/podmanV2/registry/config.go | 59 | ||||
-rw-r--r-- | cmd/podmanV2/registry/registry.go | 38 | ||||
-rw-r--r-- | cmd/podmanV2/registry/remote.go | 2 | ||||
-rw-r--r-- | cmd/podmanV2/root.go | 168 | ||||
-rw-r--r-- | cmd/podmanV2/system/events.go | 104 |
13 files changed, 337 insertions, 95 deletions
diff --git a/cmd/podmanV2/containers/mount.go b/cmd/podmanV2/containers/mount.go index c2f5ae987..4f7b95d98 100644 --- a/cmd/podmanV2/containers/mount.go +++ b/cmd/podmanV2/containers/mount.go @@ -31,6 +31,9 @@ var ( Args: func(cmd *cobra.Command, args []string) error { return parse.CheckAllLatestAndCIDFile(cmd, args, true, false) }, + Annotations: map[string]string{ + registry.RootRequired: "true", + }, } ) diff --git a/cmd/podmanV2/containers/stop.go b/cmd/podmanV2/containers/stop.go index d6f31352f..53ec2934d 100644 --- a/cmd/podmanV2/containers/stop.go +++ b/cmd/podmanV2/containers/stop.go @@ -46,7 +46,7 @@ func init() { flags.StringArrayVarP(&stopOptions.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.UintVarP(&stopTimeout, "time", "t", defaultContainerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container") - if registry.EngineOptions.EngineMode == entities.ABIMode { + if registry.PodmanOptions.EngineMode == entities.ABIMode { _ = flags.MarkHidden("latest") _ = flags.MarkHidden("cidfile") _ = flags.MarkHidden("ignore") diff --git a/cmd/podmanV2/containers/wait.go b/cmd/podmanV2/containers/wait.go index 2171f2073..3d11c581e 100644 --- a/cmd/podmanV2/containers/wait.go +++ b/cmd/podmanV2/containers/wait.go @@ -44,7 +44,7 @@ func init() { flags.DurationVarP(&waitOptions.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion") flags.BoolVarP(&waitOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on") - if registry.EngineOptions.EngineMode == entities.ABIMode { + if registry.PodmanOptions.EngineMode == entities.ABIMode { // TODO: This is the same as V1. We could skip creating the flag altogether in V2... _ = flags.MarkHidden("latest") } diff --git a/cmd/podmanV2/images/history.go b/cmd/podmanV2/images/history.go index 48575b33a..e3bb7a051 100644 --- a/cmd/podmanV2/images/history.go +++ b/cmd/podmanV2/images/history.go @@ -53,7 +53,7 @@ func init() { flags := historyCmd.Flags() flags.StringVar(&opts.format, "format", "", "Change the output to JSON or a Go template") - flags.BoolVarP(&opts.human, "human", "H", false, "Display sizes and dates in human readable format") + flags.BoolVarP(&opts.human, "human", "H", true, "Display sizes and dates in human readable format") flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate the output") flags.BoolVar(&opts.noTrunc, "notruncate", false, "Do not truncate the output") flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Display the numeric IDs only") @@ -79,7 +79,7 @@ func history(cmd *cobra.Command, args []string) error { layers := make([]layer, len(results.Layers)) for i, l := range results.Layers { layers[i].ImageHistoryLayer = l - layers[i].Created = time.Unix(l.Created, 0).Format(time.RFC3339) + layers[i].Created = l.Created.Format(time.RFC3339) } json := jsoniter.ConfigCompatibleWithStandardLibrary enc := json.NewEncoder(os.Stdout) @@ -129,7 +129,10 @@ type historyreporter struct { } func (h historyreporter) Created() string { - return units.HumanDuration(time.Since(time.Unix(h.ImageHistoryLayer.Created, 0))) + " ago" + if opts.human { + return units.HumanDuration(time.Since(h.ImageHistoryLayer.Created)) + " ago" + } + return h.ImageHistoryLayer.Created.Format(time.RFC3339) } func (h historyreporter) Size() string { diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go index d7f6b0ee1..2ee2d86ee 100644 --- a/cmd/podmanV2/images/inspect.go +++ b/cmd/podmanV2/images/inspect.go @@ -67,7 +67,6 @@ func inspect(cmd *cobra.Command, args []string) error { } return nil } - row := inspectFormat(inspectOpts.Format) format := "{{range . }}" + row + "{{end}}" tmpl, err := template.New("inspect").Parse(format) @@ -77,7 +76,7 @@ func inspect(cmd *cobra.Command, args []string) error { w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer func() { _ = w.Flush() }() - err = tmpl.Execute(w, results) + err = tmpl.Execute(w, results.Images) if err != nil { return err } diff --git a/cmd/podmanV2/images/rm.go b/cmd/podmanV2/images/rm.go index bb5880de3..6784182d9 100644 --- a/cmd/podmanV2/images/rm.go +++ b/cmd/podmanV2/images/rm.go @@ -8,6 +8,7 @@ import ( "github.com/containers/libpod/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var ( @@ -33,11 +34,13 @@ func init() { Parent: imageCmd, }) - flags := rmCmd.Flags() + imageRemoveFlagSet(rmCmd.Flags()) +} + +func imageRemoveFlagSet(flags *pflag.FlagSet) { flags.BoolVarP(&imageOpts.All, "all", "a", false, "Remove all images") flags.BoolVarP(&imageOpts.Force, "force", "f", false, "Force Removal of the image") } - func rm(cmd *cobra.Command, args []string) error { if len(args) < 1 && !imageOpts.All { @@ -46,7 +49,6 @@ func rm(cmd *cobra.Command, args []string) error { if len(args) > 0 && imageOpts.All { return errors.Errorf("when using the --all switch, you may not pass any images names or IDs") } - report, err := registry.ImageEngine().Delete(registry.GetContext(), args, imageOpts) if err != nil { switch { diff --git a/cmd/podmanV2/images/rmi.go b/cmd/podmanV2/images/rmi.go index 7f9297bc9..973763966 100644 --- a/cmd/podmanV2/images/rmi.go +++ b/cmd/podmanV2/images/rmi.go @@ -27,4 +27,5 @@ func init() { }) rmiCmd.SetHelpTemplate(registry.HelpTemplate()) rmiCmd.SetUsageTemplate(registry.UsageTemplate()) + imageRemoveFlagSet(rmiCmd.Flags()) } diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go index fe3cd9f16..cfe20d1c1 100644 --- a/cmd/podmanV2/main.go +++ b/cmd/podmanV2/main.go @@ -3,8 +3,6 @@ package main import ( "os" "reflect" - "runtime" - "strings" _ "github.com/containers/libpod/cmd/podmanV2/containers" _ "github.com/containers/libpod/cmd/podmanV2/healthcheck" @@ -14,36 +12,13 @@ import ( "github.com/containers/libpod/cmd/podmanV2/registry" _ "github.com/containers/libpod/cmd/podmanV2/system" _ "github.com/containers/libpod/cmd/podmanV2/volumes" - "github.com/containers/libpod/libpod" - "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/storage/pkg/reexec" - "github.com/sirupsen/logrus" ) func init() { - if err := libpod.SetXdgDirs(); err != nil { - logrus.Errorf(err.Error()) - os.Exit(1) - } - - switch runtime.GOOS { - case "darwin": - fallthrough - case "windows": - registry.EngineOptions.EngineMode = entities.TunnelMode - case "linux": - registry.EngineOptions.EngineMode = entities.ABIMode - default: - logrus.Errorf("%s is not a supported OS", runtime.GOOS) - os.Exit(1) - } - - // TODO: Is there a Cobra way to "peek" at os.Args? - for _, v := range os.Args { - if strings.HasPrefix(v, "--remote") { - registry.EngineOptions.EngineMode = entities.TunnelMode - } - } + // This is the bootstrap configuration, if user gives + // CLI flags parts of this configuration may be overwritten + registry.PodmanOptions = registry.NewPodmanConfig() } func main() { @@ -53,7 +28,7 @@ func main() { return } for _, c := range registry.Commands { - if Contains(registry.EngineOptions.EngineMode, c.Mode) { + if Contains(registry.PodmanOptions.EngineMode, c.Mode) { parent := rootCmd if c.Parent != nil { parent = c.Parent diff --git a/cmd/podmanV2/registry/config.go b/cmd/podmanV2/registry/config.go new file mode 100644 index 000000000..e68009a50 --- /dev/null +++ b/cmd/podmanV2/registry/config.go @@ -0,0 +1,59 @@ +package registry + +import ( + "os" + "runtime" + "strings" + + "github.com/containers/common/pkg/config" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/sirupsen/logrus" +) + +const ( + RootRequired = "RootRequired" +) + +var ( + PodmanOptions entities.PodmanConfig +) + +// NewPodmanConfig creates a PodmanConfig from the environment +func NewPodmanConfig() entities.PodmanConfig { + if err := libpod.SetXdgDirs(); err != nil { + logrus.Errorf(err.Error()) + os.Exit(1) + } + + var mode entities.EngineMode + switch runtime.GOOS { + case "darwin": + fallthrough + case "windows": + mode = entities.TunnelMode + case "linux": + mode = entities.ABIMode + default: + logrus.Errorf("%s is not a supported OS", runtime.GOOS) + os.Exit(1) + } + + // cobra.Execute() may not be called yet, so we peek at os.Args. + for _, v := range os.Args { + // Prefix checking works because of how default EngineMode's + // have been defined. + if strings.HasPrefix(v, "--remote=") { + mode = entities.TunnelMode + } + } + + // FIXME: for rootless, where to get the path + // TODO: + cfg, err := config.NewConfig("") + if err != nil { + logrus.Error("Failed to obtain podman configuration") + os.Exit(1) + } + return entities.PodmanConfig{Config: cfg, EngineMode: mode} +} diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go index 07c2b33ff..5ef6a10d8 100644 --- a/cmd/podmanV2/registry/registry.go +++ b/cmd/podmanV2/registry/registry.go @@ -29,8 +29,9 @@ var ( exitCode = ExecErrorCodeGeneric imageEngine entities.ImageEngine - Commands []CliCommand - EngineOptions entities.EngineOptions + // Commands holds the cobra.Commands to present to the user, including + // parent if not a child of "root" + Commands []CliCommand ) func SetExitCode(code int) { @@ -83,8 +84,8 @@ func ImageEngine() entities.ImageEngine { // NewImageEngine is a wrapper for building an ImageEngine to be used for PreRunE functions func NewImageEngine(cmd *cobra.Command, args []string) (entities.ImageEngine, error) { if imageEngine == nil { - EngineOptions.FlagSet = cmd.Flags() - engine, err := infra.NewImageEngine(EngineOptions) + PodmanOptions.FlagSet = cmd.Flags() + engine, err := infra.NewImageEngine(PodmanOptions) if err != nil { return nil, err } @@ -100,8 +101,8 @@ func ContainerEngine() entities.ContainerEngine { // NewContainerEngine is a wrapper for building an ContainerEngine to be used for PreRunE functions func NewContainerEngine(cmd *cobra.Command, args []string) (entities.ContainerEngine, error) { if containerEngine == nil { - EngineOptions.FlagSet = cmd.Flags() - engine, err := infra.NewContainerEngine(EngineOptions) + PodmanOptions.FlagSet = cmd.Flags() + engine, err := infra.NewContainerEngine(PodmanOptions) if err != nil { return nil, err } @@ -125,24 +126,17 @@ func IdOrLatestArgs(cmd *cobra.Command, args []string) error { return nil } -type podmanContextKey string - -var podmanFactsKey = podmanContextKey("engineOptions") - -func NewOptions(ctx context.Context, facts *entities.EngineOptions) context.Context { - return context.WithValue(ctx, podmanFactsKey, facts) -} - -func Options(cmd *cobra.Command) (*entities.EngineOptions, error) { - if f, ok := cmd.Context().Value(podmanFactsKey).(*entities.EngineOptions); ok { - return f, errors.New("Command Context ") - } - return nil, nil -} - func GetContext() context.Context { if cliCtx == nil { - cliCtx = context.TODO() + cliCtx = context.Background() } return cliCtx } + +type ContextOptionsKey string + +const PodmanOptionsKey ContextOptionsKey = "PodmanOptions" + +func GetContextWithOptions() context.Context { + return context.WithValue(GetContext(), PodmanOptionsKey, PodmanOptions) +} diff --git a/cmd/podmanV2/registry/remote.go b/cmd/podmanV2/registry/remote.go index 32a231ac4..5378701e7 100644 --- a/cmd/podmanV2/registry/remote.go +++ b/cmd/podmanV2/registry/remote.go @@ -5,5 +5,5 @@ import ( ) func IsRemote() bool { - return EngineOptions.EngineMode == entities.TunnelMode + return PodmanOptions.EngineMode == entities.TunnelMode } diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go index 6fc12f57e..0639257ea 100644 --- a/cmd/podmanV2/root.go +++ b/cmd/podmanV2/root.go @@ -1,29 +1,37 @@ package main import ( + "context" "fmt" "log/syslog" "os" "path" + "runtime/pprof" "github.com/containers/libpod/cmd/podmanV2/registry" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/tracing" "github.com/containers/libpod/version" + "github.com/opentracing/opentracing-go" + "github.com/pkg/errors" "github.com/sirupsen/logrus" logrusSyslog "github.com/sirupsen/logrus/hooks/syslog" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var ( rootCmd = &cobra.Command{ - Use: path.Base(os.Args[0]), - Long: "Manage pods, containers and images", - SilenceUsage: true, - SilenceErrors: true, - TraverseChildren: true, - PersistentPreRunE: preRunE, - RunE: registry.SubCommandExists, - Version: version.Version, + Use: path.Base(os.Args[0]), + Long: "Manage pods, containers and images", + SilenceUsage: true, + SilenceErrors: true, + TraverseChildren: true, + PersistentPreRunE: preRunE, + RunE: registry.SubCommandExists, + PersistentPostRunE: postRunE, + Version: version.Version, } logLevels = entities.NewStringSet("debug", "info", "warn", "error", "fatal", "panic") @@ -32,30 +40,73 @@ var ( ) func init() { - // Override default --help information of `--version` global flag} - var dummyVersion bool - // TODO had to disable shorthand -v for version due to -v rm with volume - rootCmd.PersistentFlags().BoolVar(&dummyVersion, "version", false, "Version of Podman") - rootCmd.PersistentFlags().StringVarP(®istry.EngineOptions.Uri, "remote", "r", "", "URL to access Podman service") - rootCmd.PersistentFlags().StringSliceVar(®istry.EngineOptions.Identities, "identity", []string{}, "path to SSH identity file") - rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "error", fmt.Sprintf("Log messages above specified level (%s)", logLevels.String())) - rootCmd.PersistentFlags().BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)") - cobra.OnInitialize( - logging, + rootlessHook, + loggingHook, syslogHook, ) + + rootFlags(registry.PodmanOptions, rootCmd.PersistentFlags()) +} + +func Execute() { + if err := rootCmd.ExecuteContext(registry.GetContextWithOptions()); err != nil { + logrus.Error(err) + } else if registry.GetExitCode() == registry.ExecErrorCodeGeneric { + // The exitCode modified from registry.ExecErrorCodeGeneric, + // indicates an application + // running inside of a container failed, as opposed to the + // podman command failed. Must exit with that exit code + // otherwise command exited correctly. + registry.SetExitCode(0) + } + os.Exit(registry.GetExitCode()) } -func preRunE(cmd *cobra.Command, args []string) error { +func preRunE(cmd *cobra.Command, _ []string) error { + // Update PodmanOptions now that we "know" more + // TODO: pass in path overriding configuration file + registry.PodmanOptions = registry.NewPodmanConfig() + cmd.SetHelpTemplate(registry.HelpTemplate()) cmd.SetUsageTemplate(registry.UsageTemplate()) + + if cmd.Flag("cpu-profile").Changed { + f, err := os.Create(registry.PodmanOptions.CpuProfile) + if err != nil { + return errors.Wrapf(err, "unable to create cpu profiling file %s", + registry.PodmanOptions.CpuProfile) + } + if err := pprof.StartCPUProfile(f); err != nil { + return err + } + } + + if cmd.Flag("trace").Changed { + tracer, closer := tracing.Init("podman") + opentracing.SetGlobalTracer(tracer) + registry.PodmanOptions.SpanCloser = closer + + registry.PodmanOptions.Span = tracer.StartSpan("before-context") + registry.PodmanOptions.SpanCtx = opentracing.ContextWithSpan(context.Background(), registry.PodmanOptions.Span) + } return nil } -func logging() { +func postRunE(cmd *cobra.Command, args []string) error { + if cmd.Flag("cpu-profile").Changed { + pprof.StopCPUProfile() + } + if cmd.Flag("trace").Changed { + registry.PodmanOptions.Span.Finish() + registry.PodmanOptions.SpanCloser.Close() + } + return nil +} + +func loggingHook() { if !logLevels.Contains(logLevel) { - fmt.Fprintf(os.Stderr, "Log Level \"%s\" is not supported, choose from: %s\n", logLevel, logLevels.String()) + logrus.Errorf("Log Level \"%s\" is not supported, choose from: %s", logLevel, logLevels.String()) os.Exit(1) } @@ -83,17 +134,68 @@ func syslogHook() { } } -func Execute() { - o := registry.NewOptions(rootCmd.Context(), ®istry.EngineOptions) - if err := rootCmd.ExecuteContext(o); err != nil { - fmt.Fprintln(os.Stderr, "Error:", err.Error()) - } else if registry.GetExitCode() == registry.ExecErrorCodeGeneric { - // The exitCode modified from registry.ExecErrorCodeGeneric, - // indicates an application - // running inside of a container failed, as opposed to the - // podman command failed. Must exit with that exit code - // otherwise command exited correctly. - registry.SetExitCode(0) +func rootlessHook() { + if rootless.IsRootless() { + logrus.Error("rootless mode is currently not supported. Support will return ASAP.") } - os.Exit(registry.GetExitCode()) + // ce, err := registry.NewContainerEngine(rootCmd, []string{}) + // if err != nil { + // logrus.WithError(err).Fatal("failed to obtain container engine") + // } + // ce.SetupRootLess(rootCmd) +} + +func rootFlags(opts entities.PodmanConfig, flags *pflag.FlagSet) { + // V2 flags + flags.StringVarP(&opts.Uri, "remote", "r", "", "URL to access Podman service") + flags.StringSliceVar(&opts.Identities, "identity", []string{}, "path to SSH identity file") + + // Override default --help information of `--version` global flag + // TODO: restore -v option for version without breaking -v for volumes + var dummyVersion bool + flags.BoolVar(&dummyVersion, "version", false, "Version of Podman") + + cfg := opts.Config + flags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, opts.CGroupUsage) + flags.StringVar(&opts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results") + flags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary") + flags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network") + flags.StringVar(&cfg.Network.NetworkConfigDir, "cni-config-dir", cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks") + flags.StringVar(&cfg.Containers.DefaultMountsFile, "default-mounts-file", cfg.Containers.DefaultMountsFile, "Path to default mounts file") + flags.StringVar(&cfg.Engine.EventsLogger, "events-backend", cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`) + flags.StringSliceVar(&cfg.Engine.HooksDir, "hooks-dir", cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)") + flags.IntVar(&opts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations") + flags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system") + flags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored") + flags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") + flags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") + // -s is deprecated due to conflict with -s on subcommands + flags.StringVar(&opts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") + flags.StringArrayVar(&opts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") + + flags.StringVar(&opts.Engine.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n") + flags.BoolVar(&opts.Trace, "trace", false, "Enable opentracing output (default false)") + + // Override default --help information of `--help` global flag + var dummyHelp bool + flags.BoolVar(&dummyHelp, "help", false, "Help for podman") + flags.StringVar(&logLevel, "log-level", logLevel, fmt.Sprintf("Log messages above specified level (%s)", logLevels.String())) + + // Hide these flags for both ABI and Tunneling + for _, f := range []string{ + "cpu-profile", + "default-mounts-file", + "max-workers", + "trace", + } { + if err := flags.MarkHidden(f); err != nil { + logrus.Warnf("unable to mark %s flag as hidden", f) + } + } + + // Only create these flags for ABI connections + if !registry.IsRemote() { + flags.BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)") + } + } diff --git a/cmd/podmanV2/system/events.go b/cmd/podmanV2/system/events.go new file mode 100644 index 000000000..9fd27e2c1 --- /dev/null +++ b/cmd/podmanV2/system/events.go @@ -0,0 +1,104 @@ +package system + +import ( + "bufio" + "context" + "html/template" + "os" + + "github.com/containers/buildah/pkg/formats" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/libpod/events" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + eventsDescription = "Monitor podman events" + eventsCommand = &cobra.Command{ + Use: "events", + Args: cobra.NoArgs, + Short: "Show podman events", + Long: eventsDescription, + PersistentPreRunE: preRunE, + RunE: eventsCmd, + Example: `podman events + podman events --filter event=create + podman events --since 1h30s`, + } +) + +var ( + eventOptions entities.EventsOptions + eventFormat string +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: eventsCommand, + }) + flags := eventsCommand.Flags() + flags.StringArrayVar(&eventOptions.Filter, "filter", []string{}, "filter output") + flags.StringVar(&eventFormat, "format", "", "format the output using a Go template") + flags.BoolVar(&eventOptions.Stream, "stream", true, "stream new events; for testing only") + flags.StringVar(&eventOptions.Since, "since", "", "show all events created since timestamp") + flags.StringVar(&eventOptions.Until, "until", "", "show all events until timestamp") + _ = flags.MarkHidden("stream") +} + +func eventsCmd(cmd *cobra.Command, args []string) error { + var ( + err error + eventsError error + tmpl *template.Template + ) + if eventFormat != formats.JSONString { + tmpl, err = template.New("events").Parse(eventFormat) + if err != nil { + return err + } + } + if len(eventOptions.Since) > 0 || len(eventOptions.Until) > 0 { + eventOptions.FromStart = true + } + eventChannel := make(chan *events.Event) + eventOptions.EventChan = eventChannel + + go func() { + eventsError = registry.ContainerEngine().Events(context.Background(), eventOptions) + }() + if eventsError != nil { + return eventsError + } + + w := bufio.NewWriter(os.Stdout) + for event := range eventChannel { + switch { + case eventFormat == formats.JSONString: + jsonStr, err := event.ToJSONString() + if err != nil { + return errors.Wrapf(err, "unable to format json") + } + if _, err := w.Write([]byte(jsonStr)); err != nil { + return err + } + case len(eventFormat) > 0: + if err := tmpl.Execute(w, event); err != nil { + return err + } + default: + 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 +} |