diff options
37 files changed, 432 insertions, 134 deletions
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 780b68333..541b2e05d 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -480,11 +480,12 @@ type RestoreValues struct { type RmValues struct { PodmanCommand - All bool - Force bool - Latest bool - Storage bool - Volumes bool + All bool + Force bool + Latest bool + Storage bool + Volumes bool + CIDFiles []string } type RmiValues struct { @@ -557,9 +558,10 @@ type StatsValues struct { type StopValues struct { PodmanCommand - All bool - Latest bool - Timeout uint + All bool + Latest bool + Timeout uint + CIDFiles []string } type TopValues struct { diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 4db043f31..442823d2d 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 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/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..86d6d2f27 100644 --- a/cmd/podman/pod_rm.go +++ b/cmd/podman/pod_rm.go @@ -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 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..579e4f1d3 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 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/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/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..b3bc8e1b9 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 @@ -44,8 +44,10 @@ func init() { 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") + flags.StringArrayVarP(&rmCommand.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") markFlagHiddenForRemoteClient("storage", flags) markFlagHiddenForRemoteClient("latest", flags) + markFlagHiddenForRemoteClient("cidfile", flags) } // rmCmd removes one or more containers @@ -58,8 +60,8 @@ func rmCmd(c *cliconfig.RmValues) error { // Storage conflicts with --all/--latest/--volumes if c.Storage { - if c.All || c.Latest || c.Volumes { - return errors.Errorf("--storage conflicts with --volumes, --all, and --latest") + if c.All || c.Latest || c.Volumes || c.CIDFiles != nil { + return errors.Errorf("--storage conflicts with --volumes, --all, --latest and --cidfile") } } diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index e04d8a12b..acecb298e 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 @@ -42,7 +42,9 @@ func init() { 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") + flags.StringArrayVarP(&stopCommand.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") markFlagHiddenForRemoteClient("latest", flags) + markFlagHiddenForRemoteClient("cidfile", flags) } // stopCmd stops a container or containers 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/completions/bash/podman b/completions/bash/podman index d8034ad11..98950799e 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2150,6 +2150,7 @@ _podman_rm() { local boolean_options=" --all -a + --cidfile --force -f --help @@ -2426,6 +2427,7 @@ _podman_stop() { local boolean_options=" --all -a + --cidfile -h --help --latest diff --git a/docs/source/markdown/podman-rm.1.md b/docs/source/markdown/podman-rm.1.md index c1663129c..74831fef6 100644 --- a/docs/source/markdown/podman-rm.1.md +++ b/docs/source/markdown/podman-rm.1.md @@ -18,6 +18,10 @@ Running or unusable containers will not be removed without the `-f` option. Remove all containers. Can be used in conjunction with -f as well. +**--cidfile** + +Read container ID from the specified file and remove the container. Can be specified multiple times. + **--force**, **-f** Force the removal of running and paused containers. Forcing a container removal also @@ -50,11 +54,17 @@ Remove a container by its name *mywebserver* ``` podman rm mywebserver ``` + Remove several containers by name and container id. ``` podman rm mywebserver myflaskserver 860a4b23 ``` +Remove several containers reading their IDs from files. +``` +podman rm --cidfile ./cidfile-1 --cidfile /home/user/cidfile-2 +``` + Forcibly remove a container by container ID. ``` podman rm -f 860a4b23 diff --git a/docs/source/markdown/podman-stop.1.md b/docs/source/markdown/podman-stop.1.md index b5ea670b0..5e8056e92 100644 --- a/docs/source/markdown/podman-stop.1.md +++ b/docs/source/markdown/podman-stop.1.md @@ -21,6 +21,10 @@ container and also via command line when creating the container. Stop all running containers. This does not include paused containers. +**--cidfile** + +Read container ID from the specified file and remove the container. Can be specified multiple times. + **--latest**, **-l** Instead of providing the container name or ID, use the last created container. If you use methods other than Podman @@ -40,6 +44,10 @@ podman stop 860a4b235279 podman stop mywebserver 860a4b235279 +podman stop --cidfile /home/user/cidfile-1 + +podman stop --cidfile /home/user/cidfile-1 --cidfile ./cidfile-2 + podman stop --timeout 2 860a4b235279 podman stop -a diff --git a/libpod/config/config.go b/libpod/config/config.go index 5b4b57f3a..f1fa70fbc 100644 --- a/libpod/config/config.go +++ b/libpod/config/config.go @@ -469,6 +469,9 @@ func NewConfig(userConfigPath string) (*Config, error) { if defaultConfig, err := defaultConfigFromMemory(); err != nil { return nil, errors.Wrapf(err, "error generating default config from memory") } else { + // Check if we need to switch to cgroupfs and logger=file on rootless. + defaultConfig.checkCgroupsAndLogger() + if err := config.mergeConfig(defaultConfig); err != nil { return nil, errors.Wrapf(err, "error merging default config from memory") } @@ -487,9 +490,6 @@ func NewConfig(userConfigPath string) (*Config, error) { return nil, errors.Wrapf(define.ErrInvalidArg, "volume path must be an absolute path - instead got %q", config.VolumePath) } - // Check if we need to switch to cgroupfs on rootless. - config.checkCgroupsAndAdjustConfig() - return config, nil } @@ -524,11 +524,13 @@ func systemConfigs() ([]string, error) { return configs, nil } -// checkCgroupsAndAdjustConfig checks if we're running rootless with the systemd +// checkCgroupsAndLogger checks if we're running rootless with the systemd // cgroup manager. In case the user session isn't available, we're switching the -// cgroup manager to cgroupfs. Note, this only applies to rootless. -func (c *Config) checkCgroupsAndAdjustConfig() { - if !rootless.IsRootless() || c.CgroupManager != define.SystemdCgroupsManager { +// cgroup manager to cgroupfs and the events logger backend to 'file'. +// Note, this only applies to rootless. +func (c *Config) checkCgroupsAndLogger() { + if !rootless.IsRootless() || (c.CgroupManager != + define.SystemdCgroupsManager && c.EventsLogger == "file") { return } @@ -543,7 +545,8 @@ func (c *Config) checkCgroupsAndAdjustConfig() { logrus.Warningf("The cgroups manager is set to systemd but there is no systemd user session available") logrus.Warningf("For using systemd, you may need to login using an user session") logrus.Warningf("Alternatively, you can enable lingering with: `loginctl enable-linger %d` (possibly as root)", rootless.GetRootlessUID()) - logrus.Warningf("Falling back to --cgroup-manager=cgroupfs") + logrus.Warningf("Falling back to --cgroup-manager=cgroupfs and --events-backend=file") c.CgroupManager = define.CgroupfsCgroupsManager + c.EventsLogger = "file" } } diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 028d7601d..4ff1913b5 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -652,6 +652,11 @@ func (c *Container) removeConmonFiles() error { return errors.Wrapf(err, "error removing container %s ctl file", c.ID()) } + winszFile := filepath.Join(c.bundlePath(), "winsz") + if err := os.Remove(winszFile); err != nil && !os.IsNotExist(err) { + return errors.Wrapf(err, "error removing container %s winsz file", c.ID()) + } + oomFile := filepath.Join(c.bundlePath(), "oom") if err := os.Remove(oomFile); err != nil && !os.IsNotExist(err) { return errors.Wrapf(err, "error removing container %s OOM file", c.ID()) diff --git a/libpod/image/image.go b/libpod/image/image.go index c912ac2ca..75ac85311 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -765,109 +765,65 @@ func (i *Image) History(ctx context.Context) ([]*History, error) { return nil, err } - // Use our layers list to find images that use any of them (or no - // layer, since every base layer is derived from an empty layer) as its - // topmost layer. - interestingLayers := make(map[string]bool) - var layer *storage.Layer - if i.TopLayer() != "" { - if layer, err = i.imageruntime.store.Layer(i.TopLayer()); err != nil { - return nil, err - } + // Build a mapping from top-layer to image ID. + images, err := i.imageruntime.GetImages() + if err != nil { + return nil, err } - interestingLayers[""] = true - for layer != nil { - interestingLayers[layer.ID] = true - if layer.Parent == "" { - break + topLayerMap := make(map[string]string) + for _, image := range images { + if _, exists := topLayerMap[image.TopLayer()]; !exists { + topLayerMap[image.TopLayer()] = image.ID() } - layer, err = i.imageruntime.store.Layer(layer.Parent) + } + + var allHistory []*History + var layer *storage.Layer + + // Check if we have an actual top layer to prevent lookup errors. + if i.TopLayer() != "" { + layer, err = i.imageruntime.store.Layer(i.TopLayer()) if err != nil { return nil, err } } - // Get the IDs of the images that share some of our layers. Hopefully - // this step means that we'll be able to avoid reading the - // configuration of every single image in local storage later on. - images, err := i.imageruntime.GetImages() - if err != nil { - return nil, errors.Wrapf(err, "error getting images from store") - } - interestingImages := make([]*Image, 0, len(images)) - for i := range images { - if interestingLayers[images[i].TopLayer()] { - interestingImages = append(interestingImages, images[i]) - } - } + // Iterate in reverse order over the history entries, and lookup the + // corresponding image ID, size and get the next later if needed. + numHistories := len(oci.History) - 1 + for x := numHistories; x >= 0; x-- { + var size int64 - // Build a list of image IDs that correspond to our history entries. - historyImages := make([]*Image, len(oci.History)) - if len(oci.History) > 0 { - // The starting image shares its whole history with itself. - historyImages[len(historyImages)-1] = i - for i := range interestingImages { - image, err := images[i].ociv1Image(ctx) - if err != nil { - return nil, errors.Wrapf(err, "error getting image configuration for image %q", images[i].ID()) + id := "<missing>" + if x == numHistories { + id = i.ID() + } else if layer != nil { + if !oci.History[x].EmptyLayer { + size = layer.UncompressedSize } - // If the candidate has a longer history or no history - // at all, then it doesn't share the portion of our - // history that we're interested in matching with other - // images. - if len(image.History) == 0 || len(image.History) > len(historyImages) { - continue - } - // If we don't include all of the layers that the - // candidate image does (i.e., our rootfs didn't look - // like its rootfs at any point), then it can't be part - // of our history. - if len(image.RootFS.DiffIDs) > len(oci.RootFS.DiffIDs) { - continue - } - candidateLayersAreUsed := true - for i := range image.RootFS.DiffIDs { - if image.RootFS.DiffIDs[i] != oci.RootFS.DiffIDs[i] { - candidateLayersAreUsed = false - break - } - } - if !candidateLayersAreUsed { - continue - } - // If the candidate's entire history is an initial - // portion of our history, then we're based on it, - // either directly or indirectly. - sharedHistory := historiesMatch(oci.History, image.History) - if sharedHistory == len(image.History) { - historyImages[sharedHistory-1] = images[i] + if imageID, exists := topLayerMap[layer.ID]; exists { + id = imageID + // Delete the entry to avoid reusing it for following history items. + delete(topLayerMap, layer.ID) } } - } - var ( - size int64 - sizeCount = 1 - allHistory []*History - ) - - for i := len(oci.History) - 1; i >= 0; i-- { - imageID := "<missing>" - if historyImages[i] != nil { - imageID = historyImages[i].ID() - } - if !oci.History[i].EmptyLayer { - size = img.LayerInfos()[len(img.LayerInfos())-sizeCount].Size - sizeCount++ - } allHistory = append(allHistory, &History{ - ID: imageID, - Created: oci.History[i].Created, - CreatedBy: oci.History[i].CreatedBy, + ID: id, + Created: oci.History[x].Created, + CreatedBy: oci.History[x].CreatedBy, Size: size, - Comment: oci.History[i].Comment, + Comment: oci.History[x].Comment, }) + + if layer != nil && layer.Parent != "" && !oci.History[x].EmptyLayer { + layer, err = i.imageruntime.store.Layer(layer.Parent) + if err != nil { + return nil, err + } + } } + return allHistory, nil } diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 287bd8474..02da9ec8c 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -79,7 +79,17 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa } logrus.Debugf("Setting maximum stop workers to %d", maxWorkers) - ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime) + names := cli.InputArgs + for _, cidFile := range cli.CIDFiles { + content, err := ioutil.ReadFile(cidFile) + if err != nil { + return nil, nil, errors.Wrap(err, "error reading CIDFile") + } + id := strings.Split(string(content), "\n")[0] + names = append(names, id) + } + + ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, names, r.Runtime) if err != nil { return nil, nil, err } @@ -203,7 +213,17 @@ func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmVa return ok, failures, nil } - ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime) + names := cli.InputArgs + for _, cidFile := range cli.CIDFiles { + content, err := ioutil.ReadFile(cidFile) + if err != nil { + return nil, nil, errors.Wrap(err, "error reading CIDFile") + } + id := strings.Split(string(content), "\n")[0] + names = append(names, id) + } + + ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, names, r.Runtime) if err != nil { // Failed to get containers. If force is specified, get the containers ID // and evict them diff --git a/test/e2e/build/basicalpine/Containerfile b/test/e2e/build/basicalpine/Containerfile new file mode 100644 index 000000000..67fd37901 --- /dev/null +++ b/test/e2e/build/basicalpine/Containerfile @@ -0,0 +1 @@ +FROM alpine diff --git a/test/e2e/build/context_dir_a_file b/test/e2e/build/context_dir_a_file new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/e2e/build/context_dir_a_file diff --git a/test/e2e/build/squash/Dockerfile.squash-a b/test/e2e/build/squash/Dockerfile.squash-a new file mode 100644 index 000000000..f084e093d --- /dev/null +++ b/test/e2e/build/squash/Dockerfile.squash-a @@ -0,0 +1,2 @@ +FROM busybox:latest +ADD alpinetest.tgz /data diff --git a/test/e2e/build/squash/Dockerfile.squash-b b/test/e2e/build/squash/Dockerfile.squash-b new file mode 100644 index 000000000..4c5fdb153 --- /dev/null +++ b/test/e2e/build/squash/Dockerfile.squash-b @@ -0,0 +1,2 @@ +FROM test-squash-a:latest +RUN rm -rf /data diff --git a/test/e2e/build/squash/Dockerfile.squash-c b/test/e2e/build/squash/Dockerfile.squash-c new file mode 100644 index 000000000..df9c90388 --- /dev/null +++ b/test/e2e/build/squash/Dockerfile.squash-c @@ -0,0 +1,3 @@ +FROM busybox:latest +ADD alpinetest.tgz /data +RUN rm -rf /data diff --git a/test/e2e/build/squash/alpinetest.tgz b/test/e2e/build/squash/alpinetest.tgz Binary files differnew file mode 100644 index 000000000..9b8e7bda7 --- /dev/null +++ b/test/e2e/build/squash/alpinetest.tgz diff --git a/test/e2e/build_test.go b/test/e2e/build_test.go new file mode 100644 index 000000000..71f5d1b02 --- /dev/null +++ b/test/e2e/build_test.go @@ -0,0 +1,108 @@ +// +build !remoteclient + +package integration + +import ( + "os" + "strings" + + . "github.com/containers/libpod/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman build", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.RestoreAllArtifacts() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + }) + + // Let's first do the most simple build possible to make sure stuff is + // happy and then clean up after ourselves to make sure that works too. + It("podman build and remove basic alpine", func() { + session := podmanTest.PodmanNoCache([]string{"build", "build/basicalpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.PodmanNoCache([]string{"rmi", "alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + // If the context directory is pointing at a file and not a directory, + // that's a no no, fail out. + It("podman build context directory a file", func() { + session := podmanTest.PodmanNoCache([]string{"build", "build/context_dir_a_file"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + }) + + // Check that builds with different values for the squash options + // create the appropriate number of layers, then clean up after. + It("podman build basic alpine with squash", func() { + session := podmanTest.PodmanNoCache([]string{"build", "-f", "build/squash/Dockerfile.squash-a", "-t", "test-squash-a:latest", "build/squash"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.PodmanNoCache([]string{"inspect", "--format", "{{.RootFS.Layers}}", "test-squash-a"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + // Check for two layers + Expect(len(strings.Fields(session.OutputToString()))).To(Equal(2)) + + session = podmanTest.PodmanNoCache([]string{"build", "-f", "build/squash/Dockerfile.squash-b", "--squash", "-t", "test-squash-b:latest", "build/squash"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.PodmanNoCache([]string{"inspect", "--format", "{{.RootFS.Layers}}", "test-squash-b"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + // Check for three layers + Expect(len(strings.Fields(session.OutputToString()))).To(Equal(3)) + + session = podmanTest.PodmanNoCache([]string{"build", "-f", "build/squash/Dockerfile.squash-c", "--squash", "-t", "test-squash-c:latest", "build/squash"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.PodmanNoCache([]string{"inspect", "--format", "{{.RootFS.Layers}}", "test-squash-c"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + // Check for two layers + Expect(len(strings.Fields(session.OutputToString()))).To(Equal(2)) + + session = podmanTest.PodmanNoCache([]string{"build", "-f", "build/squash/Dockerfile.squash-c", "--squash-all", "-t", "test-squash-d:latest", "build/squash"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.PodmanNoCache([]string{"inspect", "--format", "{{.RootFS.Layers}}", "test-squash-d"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + // Check for one layers + Expect(len(strings.Fields(session.OutputToString()))).To(Equal(1)) + + session = podmanTest.PodmanNoCache([]string{"rm", "-a"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.PodmanNoCache([]string{"rmi", "-a", "-f"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) +}) diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index e125c62b4..80e6d4444 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -360,7 +360,6 @@ LABEL "com.example.vendor"="Example Vendor" session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) output = session.OutputToString() - Expect(output).To(Not(MatchRegexp("<missing>"))) Expect(output).To(Not(MatchRegexp("error"))) session = podmanTest.Podman([]string{"history", "--quiet", "foo"}) diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go index 8ee7dc750..531f14feb 100644 --- a/test/e2e/rm_test.go +++ b/test/e2e/rm_test.go @@ -1,6 +1,7 @@ package integration import ( + "io/ioutil" "os" . "github.com/containers/libpod/test/utils" @@ -138,11 +139,86 @@ var _ = Describe("Podman rm", func() { }) + It("podman rm --cidfile", func() { + SkipIfRemote() + + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile := tmpDir + "cid" + + defer os.RemoveAll(tmpDir) + + session := podmanTest.Podman([]string{"create", "--cidfile", tmpFile, ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToStringArray()[0] + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + result := podmanTest.Podman([]string{"rm", "--cidfile", tmpFile}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + output := result.OutputToString() + Expect(output).To(ContainSubstring(cid)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + }) + + It("podman rm multiple --cidfile", func() { + SkipIfRemote() + + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile1 := tmpDir + "cid-1" + tmpFile2 := tmpDir + "cid-2" + + defer os.RemoveAll(tmpDir) + + session := podmanTest.Podman([]string{"create", "--cidfile", tmpFile1, ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid1 := session.OutputToStringArray()[0] + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + session = podmanTest.Podman([]string{"create", "--cidfile", tmpFile2, ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid2 := session.OutputToStringArray()[0] + Expect(podmanTest.NumberOfContainers()).To(Equal(2)) + + result := podmanTest.Podman([]string{"rm", "--cidfile", tmpFile1, "--cidfile", tmpFile2}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + output := result.OutputToString() + Expect(output).To(ContainSubstring(cid1)) + Expect(output).To(ContainSubstring(cid2)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + }) + + It("podman rm invalid --latest and --cidfile and --all", func() { + SkipIfRemote() + + result := podmanTest.Podman([]string{"rm", "--cidfile", "foobar", "--latest"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + + result = podmanTest.Podman([]string{"rm", "--cidfile", "foobar", "--all"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + + result = podmanTest.Podman([]string{"rm", "--cidfile", "foobar", "--all", "--latest"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + + result = podmanTest.Podman([]string{"rm", "--latest", "--all"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + }) + It("podman rm bogus container", func() { session := podmanTest.Podman([]string{"rm", "bogus"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(1)) }) + It("podman rm bogus container and a running container", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go index 77ab6dbb0..c76cccfef 100644 --- a/test/e2e/stop_test.go +++ b/test/e2e/stop_test.go @@ -1,6 +1,7 @@ package integration import ( + "io/ioutil" "os" "strings" @@ -215,4 +216,79 @@ var _ = Describe("Podman stop", func() { Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) + It("podman stop --cidfile", func() { + SkipIfRemote() + + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile := tmpDir + "cid" + + defer os.RemoveAll(tmpDir) + + session := podmanTest.Podman([]string{"create", "--cidfile", tmpFile, "-d", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToStringArray()[0] + + session = podmanTest.Podman([]string{"start", cid}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + result := podmanTest.Podman([]string{"stop", "--cidfile", tmpFile}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + output := result.OutputToString() + Expect(output).To(ContainSubstring(cid)) + }) + + It("podman stop multiple --cidfile", func() { + SkipIfRemote() + + tmpDir, err := ioutil.TempDir("", "") + Expect(err).To(BeNil()) + tmpFile1 := tmpDir + "cid-1" + tmpFile2 := tmpDir + "cid-2" + + defer os.RemoveAll(tmpDir) + + session := podmanTest.Podman([]string{"run", "--cidfile", tmpFile1, "-d", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid1 := session.OutputToStringArray()[0] + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + session = podmanTest.Podman([]string{"run", "--cidfile", tmpFile2, "-d", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid2 := session.OutputToStringArray()[0] + Expect(podmanTest.NumberOfContainers()).To(Equal(2)) + + result := podmanTest.Podman([]string{"stop", "--cidfile", tmpFile1, "--cidfile", tmpFile2}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + output := result.OutputToString() + Expect(output).To(ContainSubstring(cid1)) + Expect(output).To(ContainSubstring(cid2)) + Expect(podmanTest.NumberOfContainers()).To(Equal(2)) + }) + + It("podman stop invalid --latest and --cidfile and --all", func() { + SkipIfRemote() + + result := podmanTest.Podman([]string{"stop", "--cidfile", "foobar", "--latest"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + + result = podmanTest.Podman([]string{"stop", "--cidfile", "foobar", "--all"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + + result = podmanTest.Podman([]string{"stop", "--cidfile", "foobar", "--all", "--latest"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + + result = podmanTest.Podman([]string{"stop", "--latest", "--all"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + }) }) |