diff options
Diffstat (limited to 'cmd')
36 files changed, 346 insertions, 89 deletions
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go index eeedea8c8..7d32c57af 100644 --- a/cmd/podman/attach.go +++ b/cmd/podman/attach.go @@ -33,6 +33,9 @@ func init() { attachCommand.SetUsageTemplate(UsageTemplate()) flags := attachCommand.Flags() flags.StringVar(&attachCommand.DetachKeys, "detach-keys", define.DefaultDetachKeys, "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + // Clear the default, the value specified in the config file should have the + // priority + attachCommand.DetachKeys = "" flags.BoolVar(&attachCommand.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false") flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process") flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") diff --git a/cmd/podman/checkpoint.go b/cmd/podman/checkpoint.go index 22cdb1f39..07db519f8 100644 --- a/cmd/podman/checkpoint.go +++ b/cmd/podman/checkpoint.go @@ -26,7 +26,7 @@ var ( return checkpointCmd(&checkpointCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman container checkpoint --keep ctrID podman container checkpoint --all diff --git a/cmd/podman/cleanup.go b/cmd/podman/cleanup.go index c00654162..a8bc0c116 100644 --- a/cmd/podman/cleanup.go +++ b/cmd/podman/cleanup.go @@ -27,7 +27,7 @@ var ( return cleanupCmd(&cleanupCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman container cleanup --latest podman container cleanup ctrID1 ctrID2 ctrID3 diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index 857c49a48..431d5e087 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -64,6 +64,7 @@ type ImagesValues struct { NoTrunc bool Quiet bool Sort string + History bool } type EventValues struct { @@ -175,12 +176,15 @@ type HistoryValues struct { } type PruneImagesValues struct { PodmanCommand - All bool + All bool + Force bool + Filter []string } type PruneContainersValues struct { PodmanCommand - Force bool + Force bool + Filter []string } type PodPruneValues struct { @@ -367,6 +371,7 @@ type PodRestartValues struct { type PodRmValues struct { PodmanCommand All bool + Ignore bool Force bool Latest bool } @@ -388,6 +393,7 @@ type PodStatsValues struct { type PodStopValues struct { PodmanCommand All bool + Ignore bool Latest bool Timeout uint } @@ -481,11 +487,13 @@ type RestoreValues struct { type RmValues struct { PodmanCommand - All bool - Force bool - Latest bool - Storage bool - Volumes bool + All bool + Force bool + Ignore bool + Latest bool + Storage bool + Volumes bool + CIDFiles []string } type RmiValues struct { @@ -558,9 +566,11 @@ type StatsValues struct { type StopValues struct { PodmanCommand - All bool - Latest bool - Timeout uint + All bool + Ignore bool + Latest bool + Timeout uint + CIDFiles []string } type TopValues struct { @@ -650,6 +660,11 @@ type SystemPruneValues struct { Volume bool } +type SystemResetValues struct { + PodmanCommand + Force bool +} + type SystemRenumberValues struct { PodmanCommand } diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 4db043f31..69365201e 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -39,24 +39,45 @@ func shortID(id string) string { return id } -// checkAllAndLatest checks that --all and --latest are used correctly -func checkAllAndLatest(c *cobra.Command, args []string, ignoreArgLen bool) error { +// checkAllLatestAndCIDFile checks that --all and --latest are used correctly. +// If cidfile is set, also check for the --cidfile flag. +func checkAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error { argLen := len(args) if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { - return errors.New("unable to lookup values for 'latest' or 'all'") + if !cidfile { + return errors.New("unable to lookup values for 'latest' or 'all'") + } else if c.Flags().Lookup("cidfile") == nil { + return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'") + } + } + + specifiedAll, _ := c.Flags().GetBool("all") + specifiedLatest, _ := c.Flags().GetBool("latest") + specifiedCIDFile := false + if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 { + specifiedCIDFile = true } - all, _ := c.Flags().GetBool("all") - latest, _ := c.Flags().GetBool("latest") - if all && latest { + + if specifiedCIDFile && (specifiedAll || specifiedLatest) { + return errors.Errorf("--all, --latest and --cidfile cannot be used together") + } else if specifiedAll && specifiedLatest { return errors.Errorf("--all and --latest cannot be used together") } + if ignoreArgLen { return nil } - if (all || latest) && argLen > 0 { + if (argLen > 0) && (specifiedAll || specifiedLatest) { return errors.Errorf("no arguments are needed with --all or --latest") + } else if cidfile && (argLen > 0) && (specifiedAll || specifiedLatest || specifiedCIDFile) { + return errors.Errorf("no arguments are needed with --all, --latest or --cidfile") } - if argLen < 1 && !all && !latest { + + if specifiedCIDFile { + return nil + } + + if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile { return errors.Errorf("you must provide at least one name or id") } return nil @@ -187,10 +208,14 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "detach", "d", false, "Run container in background and print container ID", ) - createFlags.String( + detachKeys := createFlags.String( "detach-keys", define.DefaultDetachKeys, "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`", ) + // Clear the default, the value specified in the config file should have the + // priority + *detachKeys = "" + createFlags.StringSlice( "device", []string{}, "Add a host device to the container (default [])", diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go index 3d0fef37d..78c50268c 100644 --- a/cmd/podman/containers_prune.go +++ b/cmd/podman/containers_prune.go @@ -1,6 +1,11 @@ package main import ( + "bufio" + "fmt" + "os" + "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/define" @@ -36,9 +41,23 @@ func init() { pruneContainersCommand.SetUsageTemplate(UsageTemplate()) flags := pruneContainersCommand.Flags() flags.BoolVarP(&pruneContainersCommand.Force, "force", "f", false, "Force removal of a running container. The default is false") + flags.StringArrayVar(&pruneContainersCommand.Filter, "filter", []string{}, "Provide filter values (e.g. 'until=<timestamp>')") } func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { + if !c.Force { + reader := bufio.NewReader(os.Stdin) + fmt.Printf(`WARNING! This will remove all stopped containers. +Are you sure you want to continue? [y/N] `) + ans, err := reader.ReadString('\n') + if err != nil { + return errors.Wrapf(err, "error reading input") + } + if strings.ToLower(ans)[0] != 'y' { + return nil + } + } + runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") @@ -49,7 +68,7 @@ func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { if c.GlobalIsSet("max-workers") { maxWorkers = c.GlobalFlags.MaxWorks } - ok, failures, err := runtime.Prune(getContext(), maxWorkers, c.Force) + ok, failures, err := runtime.Prune(getContext(), maxWorkers, c.Force, c.Filter) if err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { if len(c.InputArgs) > 1 { diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index afa701897..8dcec24ce 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -36,6 +36,9 @@ func init() { flags := execCommand.Flags() flags.SetInterspersed(false) flags.StringVar(&execCommand.DetachKeys, "detach-keys", define.DefaultDetachKeys, "Select the key sequence for detaching a container. Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _") + // Clear the default, the value specified in the config file should have the + // priority + execCommand.DetachKeys = "" flags.StringArrayVarP(&execCommand.Env, "env", "e", []string{}, "Set environment variables") flags.BoolVarP(&execCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") flags.BoolVarP(&execCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 7d498517c..6b16272f4 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -32,6 +32,7 @@ type imagesTemplateParams struct { CreatedTime time.Time Size string ReadOnly bool + History string } type imagesJSONParams struct { @@ -42,6 +43,7 @@ type imagesJSONParams struct { Created time.Time `json:"created"` Size *uint64 `json:"size"` ReadOnly bool `json:"readonly"` + History []string `json:"history"` } type imagesOptions struct { @@ -53,6 +55,7 @@ type imagesOptions struct { outputformat string sort string all bool + history bool } // Type declaration and functions for sorting the images output @@ -124,6 +127,7 @@ func imagesInit(command *cliconfig.ImagesValues) { flags.BoolVar(&command.NoTrunc, "no-trunc", false, "Do not truncate output") flags.BoolVarP(&command.Quiet, "quiet", "q", false, "Display only image IDs") flags.StringVar(&command.Sort, "sort", "created", "Sort by created, id, repository, size, or tag") + flags.BoolVarP(&command.History, "history", "", false, "Display the image name history") } @@ -171,6 +175,7 @@ func imagesCmd(c *cliconfig.ImagesValues) error { format: c.Format, sort: c.Sort, all: c.All, + history: c.History, } opts.outputformat = opts.setOutputFormat() @@ -214,6 +219,9 @@ func (i imagesOptions) setOutputFormat() string { format += "{{.Digest}}\t" } format += "{{.ID}}\t{{.Created}}\t{{.Size}}\t" + if i.history { + format += "{{if .History}}{{.History}}{{else}}<none>{{end}}\t" + } return format } @@ -306,6 +314,7 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma Created: units.HumanDuration(time.Since(createdTime)) + " ago", Size: sizeStr, ReadOnly: img.IsReadOnly(), + History: strings.Join(img.NamesHistory(), ", "), } imagesOutput = append(imagesOutput, params) if opts.quiet { // Show only one image ID when quiet @@ -336,6 +345,7 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) Created: img.Created(), Size: size, ReadOnly: img.IsReadOnly(), + History: img.NamesHistory(), } imagesOutput = append(imagesOutput, params) } diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go index 5745edd6b..2b498f83d 100644 --- a/cmd/podman/images_prune.go +++ b/cmd/podman/images_prune.go @@ -1,7 +1,10 @@ package main import ( + "bufio" "fmt" + "os" + "strings" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" @@ -34,9 +37,24 @@ func init() { pruneImagesCommand.SetUsageTemplate(UsageTemplate()) flags := pruneImagesCommand.Flags() flags.BoolVarP(&pruneImagesCommand.All, "all", "a", false, "Remove all unused images, not just dangling ones") + flags.BoolVarP(&pruneImagesCommand.Force, "force", "f", false, "Do not prompt for confirmation") + flags.StringArrayVar(&pruneImagesCommand.Filter, "filter", []string{}, "Provide filter values (e.g. 'label=<key>=<value>')") } func pruneImagesCmd(c *cliconfig.PruneImagesValues) error { + if !c.Force { + reader := bufio.NewReader(os.Stdin) + fmt.Printf(` +WARNING! This will remove all dangling images. +Are you sure you want to continue? [y/N] `) + ans, err := reader.ReadString('\n') + if err != nil { + return errors.Wrapf(err, "error reading input") + } + if strings.ToLower(ans)[0] != 'y' { + return nil + } + } runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") @@ -45,7 +63,7 @@ func pruneImagesCmd(c *cliconfig.PruneImagesValues) error { // Call prune; if any cids are returned, print them and then // return err in case an error also came up - pruneCids, err := runtime.PruneImages(getContext(), c.All) + pruneCids, err := runtime.PruneImages(getContext(), c.All, c.Filter) if len(pruneCids) > 0 { for _, cid := range pruneCids { fmt.Println(cid) diff --git a/cmd/podman/init.go b/cmd/podman/init.go index 3f97824fc..2e0b33828 100644 --- a/cmd/podman/init.go +++ b/cmd/podman/init.go @@ -23,7 +23,7 @@ var ( return initCmd(&initCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman init --latest podman init 3c45ef19d893 diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go index d5056d86d..aba2008ca 100644 --- a/cmd/podman/kill.go +++ b/cmd/podman/kill.go @@ -24,7 +24,7 @@ var ( return killCmd(&killCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman kill mywebserver podman kill 860a4b23 diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index 0484e3cf0..bc46e4652 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -69,7 +69,7 @@ func init() { rootCmd.PersistentFlags().StringArrayVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.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") rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Trace, "trace", false, "Enable opentracing output") } @@ -159,7 +159,7 @@ func setupRootless(cmd *cobra.Command, args []string) error { Remote: remoteclient, } - runtime, err := libpodruntime.GetRuntime(getContext(), &podmanCmd) + runtime, err := libpodruntime.GetRuntimeNoStore(getContext(), &podmanCmd) if err != nil { return errors.Wrapf(err, "could not get runtime") } diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go index b14827592..526a236fd 100644 --- a/cmd/podman/mount.go +++ b/cmd/podman/mount.go @@ -35,7 +35,7 @@ var ( return mountCmd(&mountCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, true) + return checkAllLatestAndCIDFile(cmd, args, true, false) }, } ) diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go index 9bda77471..064946f72 100644 --- a/cmd/podman/pod_kill.go +++ b/cmd/podman/pod_kill.go @@ -28,7 +28,7 @@ var ( return podKillCmd(&podKillCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod kill podID podman pod kill --signal TERM mywebserver diff --git a/cmd/podman/pod_pause.go b/cmd/podman/pod_pause.go index 45e1319ff..320072919 100644 --- a/cmd/podman/pod_pause.go +++ b/cmd/podman/pod_pause.go @@ -25,7 +25,7 @@ var ( return podPauseCmd(&podPauseCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod pause podID1 podID2 podman pod pause --latest diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go index cc090bd6e..cb9f3770f 100644 --- a/cmd/podman/pod_restart.go +++ b/cmd/podman/pod_restart.go @@ -26,7 +26,7 @@ var ( return podRestartCmd(&podRestartCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod restart podID1 podID2 podman pod restart --latest diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go index 82d0eb977..02daf8764 100644 --- a/cmd/podman/pod_rm.go +++ b/cmd/podman/pod_rm.go @@ -12,7 +12,7 @@ import ( var ( podRmCommand cliconfig.PodRmValues - podRmDescription = fmt.Sprintf(`podman rm will remove one or more pods from the host. + podRmDescription = fmt.Sprintf(`podman rm will remove one or more stopped pods and their containers from the host. The pod name or ID can be used. A pod with containers will not be removed without --force. If --force is specified, all containers will be stopped, then removed.`) _podRmCommand = &cobra.Command{ @@ -26,7 +26,7 @@ var ( return podRmCmd(&podRmCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod rm mywebserverpod podman pod rm -f 860a4b23 @@ -41,7 +41,9 @@ func init() { flags := podRmCommand.Flags() flags.BoolVarP(&podRmCommand.All, "all", "a", false, "Remove all running pods") flags.BoolVarP(&podRmCommand.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false") + flags.BoolVarP(&podRmCommand.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") flags.BoolVarP(&podRmCommand.Latest, "latest", "l", false, "Remove the latest pod podman is aware of") + markFlagHiddenForRemoteClient("ignore", flags) markFlagHiddenForRemoteClient("latest", flags) } diff --git a/cmd/podman/pod_start.go b/cmd/podman/pod_start.go index 64c951b43..aa2e09e98 100644 --- a/cmd/podman/pod_start.go +++ b/cmd/podman/pod_start.go @@ -26,7 +26,7 @@ var ( return podStartCmd(&podStartCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod start podID podman pod start --latest diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go index edda99550..7d3951ec4 100644 --- a/cmd/podman/pod_stop.go +++ b/cmd/podman/pod_stop.go @@ -27,7 +27,7 @@ var ( return podStopCmd(&podStopCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod stop mywebserverpod podman pod stop --latest @@ -41,8 +41,10 @@ func init() { podStopCommand.SetUsageTemplate(UsageTemplate()) flags := podStopCommand.Flags() flags.BoolVarP(&podStopCommand.All, "all", "a", false, "Stop all running pods") + flags.BoolVarP(&podStopCommand.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") flags.BoolVarP(&podStopCommand.Latest, "latest", "l", false, "Stop the latest pod podman is aware of") flags.UintVarP(&podStopCommand.Timeout, "timeout", "t", 0, "Seconds to wait for pod stop before killing the container") + markFlagHiddenForRemoteClient("ignore", flags) markFlagHiddenForRemoteClient("latest", flags) } diff --git a/cmd/podman/pod_unpause.go b/cmd/podman/pod_unpause.go index 833434c3f..1f80a7c79 100644 --- a/cmd/podman/pod_unpause.go +++ b/cmd/podman/pod_unpause.go @@ -26,7 +26,7 @@ var ( return podUnpauseCmd(&podUnpauseCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod unpause podID1 podID2 podman pod unpause --all diff --git a/cmd/podman/pods_prune.go b/cmd/podman/pods_prune.go index d40e37bdb..1c5c0bb58 100644 --- a/cmd/podman/pods_prune.go +++ b/cmd/podman/pods_prune.go @@ -17,7 +17,7 @@ var ( _prunePodsCommand = &cobra.Command{ Use: "prune", Args: noSubArgs, - Short: "Remove all stopped pods", + Short: "Remove all stopped pods and their containers", Long: podPruneDescription, RunE: func(cmd *cobra.Command, args []string) error { podPruneCommand.InputArgs = args @@ -32,7 +32,7 @@ func init() { podPruneCommand.SetHelpTemplate(HelpTemplate()) podPruneCommand.SetUsageTemplate(UsageTemplate()) flags := podPruneCommand.Flags() - flags.BoolVarP(&podPruneCommand.Force, "force", "f", false, "Force removal of a running pods. The default is false") + flags.BoolVarP(&podPruneCommand.Force, "force", "f", false, "Force removal of all running pods. The default is false") } func podPruneCmd(c *cliconfig.PodPruneValues) error { diff --git a/cmd/podman/port.go b/cmd/podman/port.go index 4e1f9642c..eef3d4b1d 100644 --- a/cmd/podman/port.go +++ b/cmd/podman/port.go @@ -26,7 +26,7 @@ var ( return portCmd(&portCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, true) + return checkAllLatestAndCIDFile(cmd, args, true, false) }, Example: `podman port --all podman port ctrID 80/tcp diff --git a/cmd/podman/reset.go b/cmd/podman/reset.go new file mode 100644 index 000000000..9d16dc978 --- /dev/null +++ b/cmd/podman/reset.go @@ -0,0 +1,71 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/pkg/adapter" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + systemResetCommand cliconfig.SystemResetValues + systemResetDescription = `Reset podman storage back to default state" + + All containers will be stopped and removed, and all images, volumes and container content will be removed. +` + _systemResetCommand = &cobra.Command{ + Use: "reset", + Args: noSubArgs, + Short: "Reset podman storage", + Long: systemResetDescription, + RunE: func(cmd *cobra.Command, args []string) error { + systemResetCommand.InputArgs = args + systemResetCommand.GlobalFlags = MainGlobalOpts + systemResetCommand.Remote = remoteclient + return systemResetCmd(&systemResetCommand) + }, + } +) + +func init() { + systemResetCommand.Command = _systemResetCommand + flags := systemResetCommand.Flags() + flags.BoolVarP(&systemResetCommand.Force, "force", "f", false, "Do not prompt for confirmation") + + systemResetCommand.SetHelpTemplate(HelpTemplate()) + systemResetCommand.SetUsageTemplate(UsageTemplate()) +} + +func systemResetCmd(c *cliconfig.SystemResetValues) error { + // Prompt for confirmation if --force is not set + if !c.Force { + reader := bufio.NewReader(os.Stdin) + fmt.Print(` +WARNING! This will remove: + - all containers + - all pods + - all images + - all build cache +Are you sure you want to continue? [y/N] `) + ans, err := reader.ReadString('\n') + if err != nil { + return errors.Wrapf(err, "error reading input") + } + if strings.ToLower(ans)[0] != 'y' { + return nil + } + } + + runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "error creating libpod runtime") + } + // No shutdown, since storage will be destroyed when command completes + + return runtime.Reset() +} diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go index c97fb0dc1..996a9f7ce 100644 --- a/cmd/podman/restart.go +++ b/cmd/podman/restart.go @@ -23,7 +23,7 @@ var ( return restartCmd(&restartCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman restart ctrID podman restart --latest diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go index caefadb6d..a8db7fa94 100644 --- a/cmd/podman/restore.go +++ b/cmd/podman/restore.go @@ -26,7 +26,7 @@ var ( return restoreCmd(&restoreCommand, cmd) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, true) + return checkAllLatestAndCIDFile(cmd, args, true, false) }, Example: `podman container restore ctrID podman container restore --latest diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 6329a9d8e..e69565e95 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -25,7 +25,7 @@ var ( return rmCmd(&rmCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman rm imageID podman rm mywebserver myflaskserver 860a4b23 @@ -40,12 +40,16 @@ func init() { rmCommand.SetUsageTemplate(UsageTemplate()) flags := rmCommand.Flags() flags.BoolVarP(&rmCommand.All, "all", "a", false, "Remove all containers") + flags.BoolVarP(&rmCommand.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing") flags.BoolVarP(&rmCommand.Force, "force", "f", false, "Force removal of a running or unusable container. The default is false") flags.BoolVarP(&rmCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&rmCommand.Storage, "storage", false, "Remove container from storage library") flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove anonymous volumes associated with the container") - markFlagHiddenForRemoteClient("storage", flags) + flags.StringArrayVarP(&rmCommand.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") + markFlagHiddenForRemoteClient("ignore", flags) + markFlagHiddenForRemoteClient("cidfile", flags) markFlagHiddenForRemoteClient("latest", flags) + markFlagHiddenForRemoteClient("storage", flags) } // rmCmd removes one or more containers @@ -56,10 +60,10 @@ func rmCmd(c *cliconfig.RmValues) error { } defer runtime.DeferredShutdown(false) - // Storage conflicts with --all/--latest/--volumes + // Storage conflicts with --all/--latest/--volumes/--cidfile/--ignore if c.Storage { - if c.All || c.Latest || c.Volumes { - return errors.Errorf("--storage conflicts with --volumes, --all, and --latest") + if c.All || c.Ignore || c.Latest || c.Volumes || c.CIDFiles != nil { + return errors.Errorf("--storage conflicts with --volumes, --all, --latest, --ignore and --cidfile") } } diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index f49943477..4f2002992 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -17,6 +17,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/timetype" "github.com/containers/libpod/pkg/util" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-units" @@ -195,6 +196,8 @@ func NewBatchContainer(ctr *libpod.Container, opts PsOptions) (PsContainerOutput status = "Paused" case define.ContainerStateCreated.String(), define.ContainerStateConfigured.String(): status = "Created" + case define.ContainerStateRemoving.String(): + status = "Removing" default: status = "Error" } @@ -272,7 +275,8 @@ func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results chan<- PsContai } } -func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { +// GenerateContainerFilterFuncs return ContainerFilter functions based of filter. +func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { switch filter { case "id": return func(c *libpod.Container) bool { @@ -394,6 +398,22 @@ func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) } return hcStatus == filterValue }, nil + case "until": + ts, err := timetype.GetTimestamp(filterValue, time.Now()) + if err != nil { + return nil, err + } + seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0) + if err != nil { + return nil, err + } + until := time.Unix(seconds, nanoseconds) + return func(c *libpod.Container) bool { + if !until.IsZero() && c.CreatedTime().After((until)) { + return true + } + return false + }, nil } return nil, errors.Errorf("%s is an invalid filter", filter) } @@ -411,7 +431,7 @@ func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, m if len(filterSplit) < 2 { return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) } - generatedFunc, err := generateContainerFilterFuncs(filterSplit[0], filterSplit[1], r) + generatedFunc, err := GenerateContainerFilterFuncs(filterSplit[0], filterSplit[1], r) if err != nil { return nil, errors.Wrapf(err, "invalid filter") } diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 2fcec6deb..bb4e9cd12 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -7,6 +7,7 @@ import ( "io" "os" "path/filepath" + goruntime "runtime" "strconv" "strings" "syscall" @@ -88,9 +89,11 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. return nil, nil, err } + overrideOS := c.String("override-os") + overrideArch := c.String("override-arch") dockerRegistryOptions := image.DockerRegistryOptions{ - OSChoice: c.String("override-os"), - ArchitectureChoice: c.String("override-arch"), + OSChoice: overrideOS, + ArchitectureChoice: overrideArch, } newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType) @@ -101,6 +104,15 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. if err != nil { return nil, nil, err } + + if overrideOS == "" && data.Os != goruntime.GOOS { + return nil, nil, errors.Errorf("incompatible image OS %q on %q host", data.Os, goruntime.GOOS) + } + + if overrideArch == "" && data.Architecture != goruntime.GOARCH { + return nil, nil, errors.Errorf("incompatible image architecture %q on %q host", data.Architecture, goruntime.GOARCH) + } + names := newImage.Names() if len(names) > 0 { imageName = names[0] @@ -214,24 +226,24 @@ func configureEntrypoint(c *GenericCLIResults, data *inspect.ImageData) []string return entrypoint } -func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, error) { +func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, string, error) { pod, err := runtime.LookupPod(podName) if err != nil { - return namespaces, err + return namespaces, "", err } podInfraID, err := pod.InfraContainerID() if err != nil { - return namespaces, err + return namespaces, "", err } hasUserns := false if podInfraID != "" { podCtr, err := runtime.GetContainer(podInfraID) if err != nil { - return namespaces, err + return namespaces, "", err } mappings, err := podCtr.IDMappings() if err != nil { - return namespaces, err + return namespaces, "", err } hasUserns = len(mappings.UIDMap) > 0 } @@ -251,7 +263,7 @@ func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[ if (namespaces["uts"] == cc.Pod) || (!c.IsSet("uts") && pod.SharesUTS()) { namespaces["uts"] = fmt.Sprintf("container:%s", podInfraID) } - return namespaces, nil + return namespaces, podInfraID, nil } // Parses CLI options related to container creation into a config which can be @@ -359,6 +371,10 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. if len(podName) < 1 && c.IsSet("pod") { return nil, errors.Errorf("new pod name must be at least one character") } + + // If we are adding a container to a pod, we would like to add an annotation for the infra ID + // so kata containers can share VMs inside the pod + var podInfraID string if c.IsSet("pod") { if strings.HasPrefix(originalPodName, "new:") { // pod does not exist; lets make it @@ -387,7 +403,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. // The container now cannot have port bindings; so we reset the map portBindings = make(map[nat.Port][]nat.PortBinding) } - namespaces, err = configurePod(c, runtime, namespaces, podName) + namespaces, podInfraID, err = configurePod(c, runtime, namespaces, podName) if err != nil { return nil, err } @@ -485,12 +501,26 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. // ANNOTATIONS annotations := make(map[string]string) + // First, add our default annotations - annotations[ann.ContainerType] = "sandbox" annotations[ann.TTY] = "false" if tty { annotations[ann.TTY] = "true" } + + // in the event this container is in a pod, and the pod has an infra container + // we will want to configure it as a type "container" instead defaulting to + // the behavior of a "sandbox" container + // In Kata containers: + // - "sandbox" is the annotation that denotes the container should use its own + // VM, which is the default behavior + // - "container" denotes the container should join the VM of the SandboxID + // (the infra container) + if podInfraID != "" { + annotations[ann.SandboxID] = podInfraID + annotations[ann.ContainerType] = ann.ContainerTypeContainer + } + if data != nil { // Next, add annotations from the image for key, value := range data.Annotations { diff --git a/cmd/podman/start.go b/cmd/podman/start.go index d4b4534bb..a070cd18d 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -35,6 +35,9 @@ func init() { startCommand.SetUsageTemplate(UsageTemplate()) flags := startCommand.Flags() flags.BoolVarP(&startCommand.Attach, "attach", "a", false, "Attach container's STDOUT and STDERR") + // Clear the default, the value specified in the config file should have the + // priority + startCommand.DetachKeys = "" flags.StringVar(&startCommand.DetachKeys, "detach-keys", define.DefaultDetachKeys, "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") flags.BoolVarP(&startCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") flags.BoolVarP(&startCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index e04d8a12b..c62da80df 100644 --- a/cmd/podman/stop.go +++ b/cmd/podman/stop.go @@ -25,7 +25,7 @@ var ( return stopCmd(&stopCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman stop ctrID podman stop --latest @@ -39,10 +39,14 @@ func init() { stopCommand.SetUsageTemplate(UsageTemplate()) flags := stopCommand.Flags() flags.BoolVarP(&stopCommand.All, "all", "a", false, "Stop all running containers") + flags.BoolVarP(&stopCommand.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing") + flags.StringArrayVarP(&stopCommand.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") flags.BoolVarP(&stopCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.UintVar(&stopCommand.Timeout, "time", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") flags.UintVarP(&stopCommand.Timeout, "timeout", "t", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") markFlagHiddenForRemoteClient("latest", flags) + markFlagHiddenForRemoteClient("cidfile", flags) + markFlagHiddenForRemoteClient("ignore", flags) } // stopCmd stops a container or containers diff --git a/cmd/podman/system.go b/cmd/podman/system.go index 80080bf44..921d0c037 100644 --- a/cmd/podman/system.go +++ b/cmd/podman/system.go @@ -19,6 +19,7 @@ var ( ) var systemCommands = []*cobra.Command{ + _systemResetCommand, _infoCommand, _pruneSystemCommand, } diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go index b499d8dd2..74fdcde99 100644 --- a/cmd/podman/system_prune.go +++ b/cmd/podman/system_prune.go @@ -82,7 +82,6 @@ Are you sure you want to continue? [y/N] `, volumeString) fmt.Println("Deleted Pods") pruneValues := cliconfig.PodPruneValues{ PodmanCommand: c.PodmanCommand, - Force: c.Force, } ctx := getContext() ok, failures, lasterr := runtime.PrunePods(ctx, &pruneValues) @@ -93,7 +92,7 @@ Are you sure you want to continue? [y/N] `, volumeString) rmWorkers := shared.Parallelize("rm") fmt.Println("Deleted Containers") - ok, failures, err = runtime.Prune(ctx, rmWorkers, false) + ok, failures, err = runtime.Prune(ctx, rmWorkers, false, []string{}) if err != nil { if lasterr != nil { logrus.Errorf("%q", err) @@ -117,7 +116,8 @@ Are you sure you want to continue? [y/N] `, volumeString) // Call prune; if any cids are returned, print them and then // return err in case an error also came up - pruneCids, err := runtime.PruneImages(ctx, c.All) + // TODO: support for filters in system prune + pruneCids, err := runtime.PruneImages(ctx, c.All, []string{}) if len(pruneCids) > 0 { fmt.Println("Deleted Images") for _, cid := range pruneCids { diff --git a/cmd/podman/system_renumber.go b/cmd/podman/system_renumber.go index 81752a177..4e90a2d8c 100644 --- a/cmd/podman/system_renumber.go +++ b/cmd/podman/system_renumber.go @@ -44,9 +44,7 @@ func renumberCmd(c *cliconfig.SystemRenumberValues) error { if err != nil { return errors.Wrapf(err, "error renumbering locks") } - if err := r.Shutdown(false); err != nil { - return err - } + _ = r.Shutdown(false) return nil } diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go index c3d81d3a8..6ad485c2c 100644 --- a/cmd/podman/umount.go +++ b/cmd/podman/umount.go @@ -28,7 +28,7 @@ var ( return umountCmd(&umountCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman umount ctrID podman umount ctrID1 ctrID2 ctrID3 diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index f9339fccb..a3fd27ed6 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -70,7 +70,8 @@ type Image ( labels: [string]string, isParent: bool, topLayer: string, - readOnly: bool + readOnly: bool, + history: []string ) # ImageHistory describes the returned structure from ImageHistory. @@ -535,6 +536,10 @@ method GetVersion() -> ( remote_api_version: int ) +# Reset resets Podman back to its initial state. +# Removes all Pods, Containers, Images and Volumes +method Reset() -> () + # GetInfo returns a [PodmanInfo](#PodmanInfo) struct that describes podman and its host such as storage stats, # build information of Podman, and system-wide registries. method GetInfo() -> (info: PodmanInfo) @@ -1217,7 +1222,7 @@ method UnmountContainer(name: string, force: bool) -> () # ImagesPrune removes all unused images from the local store. Upon successful pruning, # the IDs of the removed images are returned. -method ImagesPrune(all: bool) -> (pruned: []string) +method ImagesPrune(all: bool, filter: []string) -> (pruned: []string) # This function is not implemented yet. # method ListContainerPorts(name: string) -> (notimplemented: NotImplemented) diff --git a/cmd/podman/version.go b/cmd/podman/version.go index 314b2e266..5907241ff 100644 --- a/cmd/podman/version.go +++ b/cmd/podman/version.go @@ -37,13 +37,40 @@ func init() { flags := versionCommand.Flags() flags.StringVarP(&versionCommand.Format, "format", "f", "", "Change the output format to JSON or a Go template") } +func getRemoteVersion(c *cliconfig.VersionValues) (version define.Version, err error) { + runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + if err != nil { + return version, errors.Wrapf(err, "could not get runtime") + } + defer runtime.DeferredShutdown(false) + + return runtime.GetVersion() +} + +type versionStruct struct { + Client define.Version + Server define.Version +} // versionCmd gets and prints version info for version command func versionCmd(c *cliconfig.VersionValues) error { - clientVersion, err := define.GetVersion() + + var ( + v versionStruct + err error + ) + v.Client, err = define.GetVersion() if err != nil { return errors.Wrapf(err, "unable to determine version") } + if remote { + v.Server, err = getRemoteVersion(c) + if err != nil { + return err + } + } else { + v.Server = v.Client + } versionOutputFormat := c.Format if versionOutputFormat != "" { @@ -53,11 +80,20 @@ func versionCmd(c *cliconfig.VersionValues) error { var out formats.Writer switch versionOutputFormat { case formats.JSONString: - out = formats.JSONStruct{Output: clientVersion} + out = formats.JSONStruct{Output: v} + return out.Out() default: - out = formats.StdoutTemplate{Output: clientVersion, Template: versionOutputFormat} + out = formats.StdoutTemplate{Output: v, Template: versionOutputFormat} + err := out.Out() + if err != nil { + // On Failure, assume user is using older version of podman version --format and check client + out = formats.StdoutTemplate{Output: v.Client, Template: versionOutputFormat} + if err1 := out.Out(); err1 != nil { + return err + } + } } - return out.Out() + return nil } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) defer w.Flush() @@ -66,25 +102,13 @@ func versionCmd(c *cliconfig.VersionValues) error { if _, err := fmt.Fprintf(w, "Client:\n"); err != nil { return err } - } - formatVersion(w, clientVersion) - - if remote { - if _, err := fmt.Fprintf(w, "\nService:\n"); err != nil { - return err - } - - runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) - if err != nil { - return errors.Wrapf(err, "could not get runtime") - } - defer runtime.DeferredShutdown(false) - - serviceVersion, err := runtime.GetVersion() - if err != nil { + formatVersion(w, v.Client) + if _, err := fmt.Fprintf(w, "\nServer:\n"); err != nil { return err } - formatVersion(w, serviceVersion) + formatVersion(w, v.Server) + } else { + formatVersion(w, v.Client) } return nil } |