diff options
44 files changed, 758 insertions, 217 deletions
@@ -43,6 +43,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func GetContainerStats(name: string) ContainerStats](#GetContainerStats) +[func GetContainersByContext(all: bool, latest: bool, args: []string) []string](#GetContainersByContext) + [func GetImage(id: string) Image](#GetImage) [func GetInfo() PodmanInfo](#GetInfo) @@ -460,6 +462,13 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.GetContainerStats '{"name } } ~~~ +### <a name="GetContainersByContext"></a>func GetContainersByContext +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method GetContainersByContext(all: [bool](https://godoc.org/builtin#bool), latest: [bool](https://godoc.org/builtin#bool), args: [[]string](#[]string)) [[]string](#[]string)</div> +GetContainersByContext allows you to get a list of container ids depending on all, latest, or a list of +container names. The definition of latest container means the latest by creation date. In a multi- +user environment, results might differ from what you expect. ### <a name="GetImage"></a>func GetImage <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 67975730e..65a3f5eea 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,16 @@ # Release Notes +## 1.1.2 +### Bugfixes +- Fixed a bug where the `podman image list`, `podman image rm`, and `podman container list` had broken global storage options +- Fixed a bug where the `--label` option to `podman create` and `podman run` was missing the `-l` alias +- Fixed a bug where running Podman with the `--config` flag would not set an appropriate default value for `tmp_dir` ([#2408](https://github.com/containers/libpod/issues/2408)) +- Fixed a bug where the `podman logs` command with the `--timestamps` flag produced unreadable output ([#2500](https://github.com/containers/libpod/issues/2500)) +- Fixed a bug where the `podman cp` command would automatically extract `.tar` files copied into the container ([#2509](https://github.com/containers/libpod/issues/2509)) + +### Misc +- The `podman container stop` command is now usable with the Podman remote client + ## 1.1.1 ### Bugfixes - Fixed a bug where `podman container restore` was erroneously available as `podman restore` ([#2191](https://github.com/containers/libpod/issues/2191)) diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index 2f9a9cfe2..c261e54e2 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -32,7 +32,6 @@ func getMainCommands() []*cobra.Command { _searchCommand, _startCommand, _statsCommand, - _stopCommand, _topCommand, _umountCommand, _unpauseCommand, @@ -55,9 +54,6 @@ func getImageSubCommands() []*cobra.Command { // Commands that the local client implements func getContainerSubCommands() []*cobra.Command { - var _listSubCommand = _psCommand - _listSubCommand.Use = "list" - return []*cobra.Command{ _attachCommand, _checkpointCommand, @@ -68,7 +64,6 @@ func getContainerSubCommands() []*cobra.Command { _execCommand, _exportCommand, _killCommand, - &_listSubCommand, _logsCommand, _mountCommand, _pauseCommand, diff --git a/cmd/podman/common.go b/cmd/podman/common.go index d3387b8f4..c9e937825 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -311,8 +311,8 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "kernel-memory", "", "Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)", ) - createFlags.StringSlice( - "label", []string{}, + createFlags.StringSliceP( + "label", "l", []string{}, "Set metadata on container (default [])", ) createFlags.StringSlice( diff --git a/cmd/podman/container.go b/cmd/podman/container.go index 338bb005c..0bcdf533a 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -1,27 +1,50 @@ package main import ( + "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/spf13/cobra" ) -var containerDescription = "Manage containers" -var containerCommand = cliconfig.PodmanCommand{ - Command: &cobra.Command{ - Use: "container", - Short: "Manage Containers", - Long: containerDescription, - TraverseChildren: true, - }, -} +var ( + containerDescription = "Manage containers" + containerCommand = cliconfig.PodmanCommand{ + Command: &cobra.Command{ + Use: "container", + Short: "Manage Containers", + Long: containerDescription, + TraverseChildren: true, + }, + } -// Commands that are universally implemented. -var containerCommands = []*cobra.Command{ - _containerExistsCommand, - _inspectCommand, -} + listSubCommand cliconfig.PsValues + _listSubCommand = &cobra.Command{ + Use: strings.Replace(_psCommand.Use, "ps", "list", 1), + Args: noSubArgs, + Short: _psCommand.Short, + Long: _psCommand.Long, + Aliases: []string{"ls"}, + RunE: func(cmd *cobra.Command, args []string) error { + listSubCommand.InputArgs = args + listSubCommand.GlobalFlags = MainGlobalOpts + return psCmd(&listSubCommand) + }, + Example: strings.Replace(_psCommand.Example, "podman ps", "podman container list", -1), + } + + // Commands that are universally implemented. + containerCommands = []*cobra.Command{ + _containerExistsCommand, + _inspectCommand, + _listSubCommand, + } +) func init() { + listSubCommand.Command = _listSubCommand + psInit(&listSubCommand) + containerCommand.AddCommand(containerCommands...) containerCommand.AddCommand(getContainerSubCommands()...) containerCommand.SetUsageTemplate(UsageTemplate()) diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go index 30b6d75d2..5958370b6 100644 --- a/cmd/podman/cp.go +++ b/cmd/podman/cp.go @@ -240,7 +240,6 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch // return functions for copying items copyFileWithTar := chrootarchive.CopyFileWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap) copyWithTar := chrootarchive.CopyWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap) - untarPath := chrootarchive.UntarPathAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap) if srcfi.IsDir() { @@ -263,17 +262,11 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch if destfi != nil && destfi.IsDir() { destPath = filepath.Join(destPath, filepath.Base(srcPath)) } - // Copy the file, preserving attributes. - logrus.Debugf("copying %q to %q", srcPath, destPath) - if err = copyFileWithTar(srcPath, destPath); err != nil { - return errors.Wrapf(err, "error copying %q to %q", srcPath, destPath) - } - return nil } - // We're extracting an archive into the destination directory. - logrus.Debugf("extracting contents of %q into %q", srcPath, destPath) - if err = untarPath(srcPath, destPath); err != nil { - return errors.Wrapf(err, "error extracting %q into %q", srcPath, destPath) + // Copy the file, preserving attributes. + logrus.Debugf("copying %q to %q", srcPath, destPath) + if err = copyFileWithTar(srcPath, destPath); err != nil { + return errors.Wrapf(err, "error copying %q to %q", srcPath, destPath) } return nil } diff --git a/cmd/podman/errors.go b/cmd/podman/errors.go index 2572b8779..9731037f4 100644 --- a/cmd/podman/errors.go +++ b/cmd/podman/errors.go @@ -1,3 +1,5 @@ +// +build !remoteclient + package main import ( @@ -13,7 +15,8 @@ func outputError(err error) { if MainGlobalOpts.LogLevel == "debug" { logrus.Errorf(err.Error()) } else { - if ee, ok := err.(*exec.ExitError); ok { + ee, ok := err.(*exec.ExitError) + if ok { if status, ok := ee.Sys().(syscall.WaitStatus); ok { exitCode = status.ExitStatus() } diff --git a/cmd/podman/errors_remote.go b/cmd/podman/errors_remote.go new file mode 100644 index 000000000..ab255ea56 --- /dev/null +++ b/cmd/podman/errors_remote.go @@ -0,0 +1,43 @@ +// +build remoteclient + +package main + +import ( + "fmt" + "os" + "os/exec" + "syscall" + + "github.com/containers/libpod/cmd/podman/varlink" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func outputError(err error) { + if MainGlobalOpts.LogLevel == "debug" { + logrus.Errorf(err.Error()) + } else { + if ee, ok := err.(*exec.ExitError); ok { + if status, ok := ee.Sys().(syscall.WaitStatus); ok { + exitCode = status.ExitStatus() + } + } + var ne error + switch e := err.(type) { + // For some reason golang wont let me list them with commas so listing them all. + case *iopodman.ImageNotFound: + ne = errors.New(e.Reason) + case *iopodman.ContainerNotFound: + ne = errors.New(e.Reason) + case *iopodman.PodNotFound: + ne = errors.New(e.Reason) + case *iopodman.VolumeNotFound: + ne = errors.New(e.Reason) + case *iopodman.ErrorOccurred: + ne = errors.New(e.Reason) + default: + ne = err + } + fmt.Fprintln(os.Stderr, "Error:", ne.Error()) + } +} diff --git a/cmd/podman/image.go b/cmd/podman/image.go index 0777425eb..57be7fe14 100644 --- a/cmd/podman/image.go +++ b/cmd/podman/image.go @@ -16,14 +16,39 @@ var ( Long: imageDescription, }, } - _imagesSubCommand = _imagesCommand - _rmSubCommand = _rmiCommand + imagesSubCommand cliconfig.ImagesValues + _imagesSubCommand = &cobra.Command{ + Use: strings.Replace(_imagesCommand.Use, "images", "list", 1), + Short: _imagesCommand.Short, + Long: _imagesCommand.Long, + Aliases: []string{"ls"}, + RunE: func(cmd *cobra.Command, args []string) error { + imagesSubCommand.InputArgs = args + imagesSubCommand.GlobalFlags = MainGlobalOpts + return imagesCmd(&imagesSubCommand) + }, + Example: strings.Replace(_imagesCommand.Example, "podman images", "podman image list", -1), + } + + rmSubCommand cliconfig.RmiValues + _rmSubCommand = &cobra.Command{ + Use: strings.Replace(_rmiCommand.Use, "rmi", "rm", 1), + Short: _rmiCommand.Short, + Long: _rmiCommand.Long, + RunE: func(cmd *cobra.Command, args []string) error { + rmSubCommand.InputArgs = args + rmSubCommand.GlobalFlags = MainGlobalOpts + return rmiCmd(&rmSubCommand) + }, + Example: strings.Replace(_rmiCommand.Example, "podman rmi", "podman image rm", -1), + } ) //imageSubCommands are implemented both in local and remote clients var imageSubCommands = []*cobra.Command{ _buildCommand, _historyCommand, + _imagesSubCommand, _imageExistsCommand, _importCommand, _inspectCommand, @@ -31,23 +56,20 @@ var imageSubCommands = []*cobra.Command{ _pruneImagesCommand, _pullCommand, _pushCommand, + _rmSubCommand, _saveCommand, _tagCommand, } func init() { + rmSubCommand.Command = _rmSubCommand + rmiInit(&rmSubCommand) + + imagesSubCommand.Command = _imagesSubCommand + imagesInit(&imagesSubCommand) + imageCommand.SetUsageTemplate(UsageTemplate()) imageCommand.AddCommand(imageSubCommands...) imageCommand.AddCommand(getImageSubCommands()...) - // Setup of "images" to appear as "list" - _imagesSubCommand.Use = strings.Replace(_imagesSubCommand.Use, "images", "list", 1) - _imagesSubCommand.Aliases = []string{"ls"} - _imagesSubCommand.Example = strings.Replace(_imagesSubCommand.Example, "podman images", "podman image list", -1) - imageCommand.AddCommand(&_imagesSubCommand) - - // It makes no sense to keep 'podman images rmi'; just use 'rm' - _rmSubCommand.Use = strings.Replace(_rmSubCommand.Use, "rmi", "rm", 1) - _rmSubCommand.Example = strings.Replace(_rmSubCommand.Example, "podman rmi", "podman image rm", -1) - imageCommand.AddCommand(&_rmSubCommand) } diff --git a/cmd/podman/images.go b/cmd/podman/images.go index e6f4d9a60..26e51bef7 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -102,22 +102,26 @@ var ( } ) -func init() { - imagesCommand.Command = &_imagesCommand - imagesCommand.SetUsageTemplate(UsageTemplate()) - - flags := imagesCommand.Flags() - flags.BoolVarP(&imagesCommand.All, "all", "a", false, "Show all images (default hides intermediate images)") - flags.BoolVar(&imagesCommand.Digests, "digests", false, "Show digests") - flags.StringSliceVarP(&imagesCommand.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") - flags.StringVar(&imagesCommand.Format, "format", "", "Change the output format to JSON or a Go template") - flags.BoolVarP(&imagesCommand.Noheading, "noheading", "n", false, "Do not print column headings") +func imagesInit(command *cliconfig.ImagesValues) { + command.SetUsageTemplate(UsageTemplate()) + + flags := command.Flags() + flags.BoolVarP(&command.All, "all", "a", false, "Show all images (default hides intermediate images)") + flags.BoolVar(&command.Digests, "digests", false, "Show digests") + flags.StringSliceVarP(&command.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") + flags.StringVar(&command.Format, "format", "", "Change the output format to JSON or a Go template") + flags.BoolVarP(&command.Noheading, "noheading", "n", false, "Do not print column headings") // TODO Need to learn how to deal with second name being a string instead of a char. // This needs to be "no-trunc, notruncate" - flags.BoolVar(&imagesCommand.NoTrunc, "no-trunc", false, "Do not truncate output") - flags.BoolVarP(&imagesCommand.Quiet, "quiet", "q", false, "Display only image IDs") - flags.StringVar(&imagesCommand.Sort, "sort", "created", "Sort by created, id, repository, size, or tag") + 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") + +} +func init() { + imagesCommand.Command = &_imagesCommand + imagesInit(&imagesCommand) } func imagesCmd(c *cliconfig.ImagesValues) error { diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 98e2f23ca..b3faf05c0 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -49,6 +49,7 @@ var mainCommands = []*cobra.Command{ _pushCommand, &_rmiCommand, _saveCommand, + _stopCommand, _tagCommand, _versionCommand, imageCommand.Command, diff --git a/cmd/podman/pod_start.go b/cmd/podman/pod_start.go index eef9d2a71..ca8ad08cf 100644 --- a/cmd/podman/pod_start.go +++ b/cmd/podman/pod_start.go @@ -18,7 +18,7 @@ var ( Starts one or more pods. The pod name or ID can be used. ` _podStartCommand = &cobra.Command{ - Use: "start POD [POD...]", + Use: "start [flags] POD [POD...]", Short: "Start one or more pods", Long: podStartDescription, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index acb5fd7da..0dedd850d 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -173,27 +173,31 @@ var ( } ) -func init() { - psCommand.Command = &_psCommand - psCommand.SetUsageTemplate(UsageTemplate()) - flags := psCommand.Flags() - flags.BoolVarP(&psCommand.All, "all", "a", false, "Show all the containers, default is only running containers") - flags.StringSliceVarP(&psCommand.Filter, "filter", "f", []string{}, "Filter output based on conditions given") - flags.StringVar(&psCommand.Format, "format", "", "Pretty-print containers to JSON or using a Go template") - flags.IntVarP(&psCommand.Last, "last", "n", -1, "Print the n last created containers (all states)") - flags.BoolVarP(&psCommand.Latest, "latest", "l", false, "Show the latest container created (all states)") - flags.BoolVar(&psCommand.Namespace, "namespace", false, "Display namespace information") - flags.BoolVar(&psCommand.Namespace, "ns", false, "Display namespace information") - flags.BoolVar(&psCommand.NoTrunct, "no-trunc", false, "Display the extended information") - flags.BoolVarP(&psCommand.Pod, "pod", "p", false, "Print the ID and name of the pod the containers are associated with") - flags.BoolVarP(&psCommand.Quiet, "quiet", "q", false, "Print the numeric IDs of the containers only") - flags.BoolVarP(&psCommand.Size, "size", "s", false, "Display the total file sizes") - flags.StringVar(&psCommand.Sort, "sort", "created", "Sort output by command, created, id, image, names, runningfor, size, or status") - flags.BoolVar(&psCommand.Sync, "sync", false, "Sync container state with OCI runtime") +func psInit(command *cliconfig.PsValues) { + command.SetUsageTemplate(UsageTemplate()) + flags := command.Flags() + flags.BoolVarP(&command.All, "all", "a", false, "Show all the containers, default is only running containers") + flags.StringSliceVarP(&command.Filter, "filter", "f", []string{}, "Filter output based on conditions given") + flags.StringVar(&command.Format, "format", "", "Pretty-print containers to JSON or using a Go template") + flags.IntVarP(&command.Last, "last", "n", -1, "Print the n last created containers (all states)") + flags.BoolVarP(&command.Latest, "latest", "l", false, "Show the latest container created (all states)") + flags.BoolVar(&command.Namespace, "namespace", false, "Display namespace information") + flags.BoolVar(&command.Namespace, "ns", false, "Display namespace information") + flags.BoolVar(&command.NoTrunct, "no-trunc", false, "Display the extended information") + flags.BoolVarP(&command.Pod, "pod", "p", false, "Print the ID and name of the pod the containers are associated with") + flags.BoolVarP(&command.Quiet, "quiet", "q", false, "Print the numeric IDs of the containers only") + flags.BoolVarP(&command.Size, "size", "s", false, "Display the total file sizes") + flags.StringVar(&command.Sort, "sort", "created", "Sort output by command, created, id, image, names, runningfor, size, or status") + flags.BoolVar(&command.Sync, "sync", false, "Sync container state with OCI runtime") markFlagHiddenForRemoteClient("latest", flags) } +func init() { + psCommand.Command = &_psCommand + psInit(&psCommand) +} + func psCmd(c *cliconfig.PsValues) error { if c.Bool("trace") { span, _ := opentracing.StartSpanFromContext(Ctx, "psCmd") diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go index 5b8bf1ea3..511668df7 100644 --- a/cmd/podman/rmi.go +++ b/cmd/podman/rmi.go @@ -29,12 +29,16 @@ var ( } ) +func rmiInit(command *cliconfig.RmiValues) { + command.SetUsageTemplate(UsageTemplate()) + flags := command.Flags() + flags.BoolVarP(&command.All, "all", "a", false, "Remove all images") + flags.BoolVarP(&command.Force, "force", "f", false, "Force Removal of the image") +} + func init() { rmiCommand.Command = &_rmiCommand - rmiCommand.SetUsageTemplate(UsageTemplate()) - flags := rmiCommand.Flags() - flags.BoolVarP(&rmiCommand.All, "all", "a", false, "Remove all images") - flags.BoolVarP(&rmiCommand.Force, "force", "f", false, "Force Removal of the image") + rmiInit(&rmiCommand) } func rmiCmd(c *cliconfig.RmiValues) error { diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index ab9a2cf38..7bd160494 100644 --- a/cmd/podman/stop.go +++ b/cmd/podman/stop.go @@ -2,15 +2,14 @@ package main import ( "fmt" + "reflect" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/rootless" - opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -52,63 +51,43 @@ func init() { markFlagHiddenForRemoteClient("latest", flags) } +// stopCmd stops a container or containers func stopCmd(c *cliconfig.StopValues) error { + if c.Flag("timeout").Changed && c.Flag("time").Changed { + return errors.New("the --timeout and --time flags are mutually exclusive") + } + if c.Bool("trace") { span, _ := opentracing.StartSpanFromContext(Ctx, "stopCmd") defer span.Finish() } rootless.SetSkipStorageSetup(true) - runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) - containers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, libpod.ContainerStateRunning, "running") + ok, failures, err := runtime.StopContainers(getContext(), c) if err != nil { - if len(containers) == 0 { - return err - } - fmt.Println(err.Error()) + return err } - if c.Flag("timeout").Changed && c.Flag("time").Changed { - return errors.New("the --timeout and --time flags are mutually exclusive") + for _, id := range ok { + fmt.Println(id) } - var stopFuncs []shared.ParallelWorkerInput - for _, ctr := range containers { - con := ctr - var stopTimeout uint - if c.Flag("timeout").Changed || c.Flag("time").Changed { - stopTimeout = c.Timeout - } else { - stopTimeout = ctr.StopTimeout() - logrus.Debugf("Set timeout to container %s default (%d)", ctr.ID(), stopTimeout) - } - f := func() error { - if err := con.StopWithTimeout(stopTimeout); err != nil { - if errors.Cause(err) == libpod.ErrCtrStopped { - logrus.Debugf("Container %s already stopped", con.ID()) - return nil - } - return err - } - return nil - } - stopFuncs = append(stopFuncs, shared.ParallelWorkerInput{ - ContainerID: con.ID(), - ParallelFunc: f, - }) - } + if len(failures) > 0 { + keys := reflect.ValueOf(failures).MapKeys() + lastKey := keys[len(keys)-1].String() + lastErr := failures[lastKey] + delete(failures, lastKey) - maxWorkers := shared.Parallelize("stop") - if c.GlobalIsSet("max-workers") { - maxWorkers = c.GlobalFlags.MaxWorks + for _, err := range failures { + outputError(err) + } + return lastErr } - logrus.Debugf("Setting maximum workers to %d", maxWorkers) - - stopErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, stopFuncs) - return printParallelOutput(stopErrors, errCount) + return nil } diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go index a823dcad1..f566387fa 100644 --- a/cmd/podman/system_prune.go +++ b/cmd/podman/system_prune.go @@ -23,6 +23,7 @@ var ( ` _pruneSystemCommand = &cobra.Command{ Use: "prune", + Args: noSubArgs, Short: "Remove unused data", Long: pruneSystemDescription, RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 618af3481..73e6c15f9 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -459,6 +459,11 @@ method ListContainers() -> (containers: []Container) # [InspectContainer](#InspectContainer). method GetContainer(id: string) -> (container: Container) +# GetContainersByContext allows you to get a list of container ids depending on all, latest, or a list of +# container names. The definition of latest container means the latest by creation date. In a multi- +# user environment, results might differ from what you expect. +method GetContainersByContext(all: bool, latest: bool, args: []string) -> (containers: []string) + # CreateContainer creates a new container from an image. It uses a [Create](#Create) type for input. The minimum # input required for CreateContainer is an image name. If the image name is not found, an [ImageNotFound](#ImageNotFound) # error will be returned. Otherwise, the ID of the newly created container will be returned. diff --git a/libpod/runtime.go b/libpod/runtime.go index f53cdd8b8..112b6820a 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -477,6 +477,12 @@ func NewRuntimeFromConfig(configPath string, options ...RuntimeOption) (runtime runtime.config.OCIRuntime = defaultRuntimeConfig.OCIRuntime runtime.config.StorageConfig = storage.StoreOptions{} + tmpDir, err := getDefaultTmpDir() + if err != nil { + return nil, err + } + runtime.config.TmpDir = tmpDir + // Check to see if the given configuration file exists if _, err := os.Stat(configPath); err != nil { return nil, errors.Wrapf(err, "error checking existence of configuration file %s", configPath) diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go new file mode 100644 index 000000000..7514f30d2 --- /dev/null +++ b/pkg/adapter/containers.go @@ -0,0 +1,81 @@ +// +build !remoteclient + +package adapter + +import ( + "context" + + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/adapter/shortcuts" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// GetLatestContainer gets the latest Container and wraps it in an adapter Container +func (r *LocalRuntime) GetLatestContainer() (*Container, error) { + Container := Container{} + c, err := r.Runtime.GetLatestContainer() + Container.Container = c + return &Container, err +} + +// GetAllContainers gets all Containers and wraps each one in an adapter Container +func (r *LocalRuntime) GetAllContainers() ([]*Container, error) { + var containers []*Container + allContainers, err := r.Runtime.GetAllContainers() + if err != nil { + return nil, err + } + + for _, c := range allContainers { + containers = append(containers, &Container{c}) + } + return containers, nil +} + +// LookupContainer gets a Container by name or id and wraps it in an adapter Container +func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { + ctr, err := r.Runtime.LookupContainer(idOrName) + if err != nil { + return nil, err + } + return &Container{ctr}, nil +} + +// StopContainers stops container(s) based on CLI inputs. +// Returns list of successful id(s), map of failed id(s) + error, or error not from container +func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopValues) ([]string, map[string]error, error) { + var timeout *uint + if cli.Flags().Changed("timeout") || cli.Flags().Changed("time") { + t := uint(cli.Timeout) + timeout = &t + } + + var ( + ok = []string{} + failures = map[string]error{} + ) + + ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime) + if err != nil { + return ok, failures, err + } + + for _, c := range ctrs { + if timeout == nil { + t := c.StopTimeout() + timeout = &t + logrus.Debugf("Set timeout to container %s default (%d)", c.ID(), *timeout) + } + if err := c.StopWithTimeout(*timeout); err == nil { + ok = append(ok, c.ID()) + } else if errors.Cause(err) == libpod.ErrCtrStopped { + ok = append(ok, c.ID()) + logrus.Debugf("Container %s is already stopped", c.ID()) + } else { + failures[c.ID()] = err + } + } + return ok, failures, nil +} diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index 3f43a6905..df40c8efd 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -3,8 +3,13 @@ package adapter import ( + "context" "encoding/json" + "errors" + + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" + "github.com/sirupsen/logrus" iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" @@ -29,6 +34,70 @@ func (c *Container) ID() string { return c.config.ID } +// Config returns a container config +func (r *LocalRuntime) Config(name string) *libpod.ContainerConfig { + // TODO the Spec being returned is not populated. Matt and I could not figure out why. Will defer + // further looking into it for after devconf. + // The libpod function for this has no errors so we are kind of in a tough + // spot here. Logging the errors for now. + reply, err := iopodman.ContainerConfig().Call(r.Conn, name) + if err != nil { + logrus.Error("call to container.config failed") + } + data := libpod.ContainerConfig{} + if err := json.Unmarshal([]byte(reply), &data); err != nil { + logrus.Error("failed to unmarshal container inspect data") + } + return &data + +} + +// ContainerState returns the "state" of the container. +func (r *LocalRuntime) ContainerState(name string) (*libpod.ContainerState, error) { // no-lint + reply, err := iopodman.ContainerStateData().Call(r.Conn, name) + if err != nil { + return nil, err + } + data := libpod.ContainerState{} + if err := json.Unmarshal([]byte(reply), &data); err != nil { + return nil, err + } + return &data, err + +} + +// LookupContainer gets basic information about container over a varlink +// connection and then translates it to a *Container +func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { + state, err := r.ContainerState(idOrName) + if err != nil { + return nil, err + } + config := r.Config(idOrName) + if err != nil { + return nil, err + } + + return &Container{ + remoteContainer{ + r, + config, + state, + }, + }, nil +} + +func (r *LocalRuntime) GetLatestContainer() (*Container, error) { + reply, err := iopodman.GetContainersByContext().Call(r.Conn, false, true, nil) + if err != nil { + return nil, err + } + if len(reply) > 0 { + return r.LookupContainer(reply[0]) + } + return nil, errors.New("no containers exist") +} + // GetArtifact returns a container's artifacts func (c *Container) GetArtifact(name string) ([]byte, error) { var data []byte @@ -55,18 +124,42 @@ func (c *Container) Name() string { return c.config.Name } +// StopContainers stops requested containers using CLI inputs. +// Returns the list of stopped container ids, map of failed to stop container ids + errors, or any non-container error +func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopValues) ([]string, map[string]error, error) { + var ( + ok = []string{} + failures = map[string]error{} + ) + + ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs) + if err != nil { + return ok, failures, err + } + + for _, id := range ids { + stopped, err := iopodman.StopContainer().Call(r.Conn, id, int64(cli.Timeout)) + if err != nil { + failures[id] = err + } else { + ok = append(ok, stopped) + } + } + return ok, failures, nil +} + // BatchContainerOp is wrapper func to mimic shared's function with a similar name meant for libpod func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContainerStruct, error) { // TODO If pod ps ever shows container's sizes, re-enable this code; otherwise it isn't needed // and would be a perf hit - //data, err := ctr.Inspect(true) - //if err != nil { - // return shared.BatchContainerStruct{}, err - //} + // data, err := ctr.Inspect(true) + // if err != nil { + // return shared.BatchContainerStruct{}, err + // } // - //size := new(shared.ContainerSize) - //size.RootFsSize = data.SizeRootFs - //size.RwSize = data.SizeRw + // size := new(shared.ContainerSize) + // size.RootFsSize = data.SizeRootFs + // size.RwSize = data.SizeRw bcs := shared.BatchContainerStruct{ ConConfig: ctr.config, @@ -75,7 +168,7 @@ func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContai Pid: ctr.state.PID, StartedTime: ctr.state.StartedTime, ExitedTime: ctr.state.FinishedTime, - //Size: size, + // Size: size, } return bcs, nil } diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 8624981b1..5be2ca150 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -108,15 +108,6 @@ func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, for return r.Runtime.RemoveImage(ctx, img.Image, force) } -// LookupContainer ... -func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { - ctr, err := r.Runtime.LookupContainer(idOrName) - if err != nil { - return nil, err - } - return &Container{ctr}, nil -} - // PruneImages is wrapper into PruneImages within the image pkg func (r *LocalRuntime) PruneImages(all bool) ([]string, error) { return r.ImageRuntime().PruneImages(all) diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 29b43e9b0..14cda7bff 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -5,7 +5,6 @@ package adapter import ( "bufio" "context" - "encoding/json" "fmt" "io" "io/ioutil" @@ -49,14 +48,13 @@ func GetRuntime(c *cliconfig.PodmanCommand) (*LocalRuntime, error) { if err != nil { return nil, err } - rr := RemoteRuntime{ - Conn: conn, - Remote: true, - } - foo := LocalRuntime{ - &rr, - } - return &foo, nil + + return &LocalRuntime{ + &RemoteRuntime{ + Conn: conn, + Remote: true, + }, + }, nil } // Shutdown is a bogus wrapper for compat with the libpod runtime @@ -315,66 +313,6 @@ func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error) return imageHistories, nil } -// LookupContainer gets basic information about container over a varlink -// connection and then translates it to a *Container -func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { - state, err := r.ContainerState(idOrName) - if err != nil { - return nil, err - } - config := r.Config(idOrName) - if err != nil { - return nil, err - } - - rc := remoteContainer{ - r, - config, - state, - } - - c := Container{ - rc, - } - return &c, nil -} - -func (r *LocalRuntime) GetLatestContainer() (*Container, error) { - return nil, libpod.ErrNotImplemented -} - -// ContainerState returns the "state" of the container. -func (r *LocalRuntime) ContainerState(name string) (*libpod.ContainerState, error) { //no-lint - reply, err := iopodman.ContainerStateData().Call(r.Conn, name) - if err != nil { - return nil, err - } - data := libpod.ContainerState{} - if err := json.Unmarshal([]byte(reply), &data); err != nil { - return nil, err - } - return &data, err - -} - -// Config returns a container config -func (r *LocalRuntime) Config(name string) *libpod.ContainerConfig { - // TODO the Spec being returned is not populated. Matt and I could not figure out why. Will defer - // further looking into it for after devconf. - // The libpod function for this has no errors so we are kind of in a tough - // spot here. Logging the errors for now. - reply, err := iopodman.ContainerConfig().Call(r.Conn, name) - if err != nil { - logrus.Error("call to container.config failed") - } - data := libpod.ContainerConfig{} - if err := json.Unmarshal([]byte(reply), &data); err != nil { - logrus.Error("failed to unmarshal container inspect data") - } - return &data - -} - // PruneImages is the wrapper call for a remote-client to prune images func (r *LocalRuntime) PruneImages(all bool) ([]string, error) { return iopodman.ImagesPrune().Call(r.Conn, all) diff --git a/pkg/adapter/shortcuts/shortcuts.go b/pkg/adapter/shortcuts/shortcuts.go index 0633399ae..677d88457 100644 --- a/pkg/adapter/shortcuts/shortcuts.go +++ b/pkg/adapter/shortcuts/shortcuts.go @@ -25,3 +25,30 @@ func GetPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) } return outpods, nil } + +// GetContainersByContext gets pods whether all, latest, or a slice of names/ids +func GetContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) ([]*libpod.Container, error) { + var ctrs = []*libpod.Container{} + + if all { + return runtime.GetAllContainers() + } + + if latest { + c, err := runtime.GetLatestContainer() + if err != nil { + return nil, err + } + ctrs = append(ctrs, c) + return ctrs, nil + } + + for _, c := range names { + ctr, err := runtime.LookupContainer(c) + if err != nil { + return nil, err + } + ctrs = append(ctrs, ctr) + } + return ctrs, nil +} diff --git a/pkg/logs/logs.go b/pkg/logs/logs.go index b104c592b..7fb5c7ea8 100644 --- a/pkg/logs/logs.go +++ b/pkg/logs/logs.go @@ -35,7 +35,10 @@ import ( const ( // timeFormat is the time format used in the log. - timeFormat = time.RFC3339Nano + // It is a modified version of RFC3339Nano that guarantees trailing + // zeroes are not trimmed, taken from + // https://github.com/golang/go/issues/19635 + timeFormat = "2006-01-02T15:04:05.000000000Z07:00" ) // LogStreamType is the type of the stream in CRI container log. @@ -277,10 +280,11 @@ func readLog(reader *bufio.Reader, opts *LogOptions) []string { // logWriter controls the writing into the stream based on the log options. type logWriter struct { - stdout io.Writer - stderr io.Writer - opts *LogOptions - remain int64 + stdout io.Writer + stderr io.Writer + opts *LogOptions + remain int64 + doAppend bool } // errMaximumWrite is returned when all bytes have been written. @@ -309,9 +313,15 @@ func (w *logWriter) write(msg *logMessage) error { return nil } line := msg.log - if w.opts.Timestamps { + if w.opts.Timestamps && !w.doAppend { prefix := append([]byte(msg.timestamp.Format(timeFormat)), delimiter[0]) line = append(prefix, line...) + if len(line) > 0 && line[len(line)-1] != '\n' { + w.doAppend = true + } + } + if w.doAppend && len(line) > 0 && line[len(line)-1] == '\n' { + w.doAppend = false } // If the line is longer than the remaining bytes, cut it. if int64(len(line)) > w.remain { diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index ad9f107a7..27b8d15d2 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -13,6 +13,7 @@ import ( "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/adapter/shortcuts" cc "github.com/containers/libpod/pkg/spec" "github.com/containers/storage/pkg/archive" "github.com/pkg/errors" @@ -60,6 +61,21 @@ func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, id string) error { return call.ReplyGetContainer(makeListContainer(ctr.ID(), batchInfo)) } +// GetContainersByContext returns a slice of container ids based on all, latest, or a list +func (i *LibpodAPI) GetContainersByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error { + var ids []string + + ctrs, err := shortcuts.GetContainersByContext(all, latest, input, i.Runtime) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + + for _, c := range ctrs { + ids = append(ids, c.ID()) + } + return call.ReplyGetContainersByContext(ids) +} + // InspectContainer ... func (i *LibpodAPI) InspectContainer(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) diff --git a/test/e2e/attach_test.go b/test/e2e/attach_test.go index 42866d5a1..9c013e459 100644 --- a/test/e2e/attach_test.go +++ b/test/e2e/attach_test.go @@ -51,6 +51,16 @@ var _ = Describe("Podman attach", func() { Expect(results.ExitCode()).To(Equal(125)) }) + It("podman container attach to non-running container", func() { + session := podmanTest.Podman([]string{"container", "create", "--name", "test1", "-d", "-i", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + results := podmanTest.Podman([]string{"container", "attach", "test1"}) + results.WaitWithDefaultTimeout() + Expect(results.ExitCode()).To(Equal(125)) + }) + It("podman attach to multiple containers", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() diff --git a/test/e2e/commit_test.go b/test/e2e/commit_test.go index 34b218621..6b65d9b75 100644 --- a/test/e2e/commit_test.go +++ b/test/e2e/commit_test.go @@ -50,6 +50,21 @@ var _ = Describe("Podman commit", func() { Expect(StringInSlice("foobar.com/test1-image:latest", data[0].RepoTags)).To(BeTrue()) }) + It("podman container commit container", func() { + _, ec, _ := podmanTest.RunLsContainer("test1") + Expect(ec).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + session := podmanTest.Podman([]string{"container", "commit", "test1", "foobar.com/test1-image:latest"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"container", "inspect", "foobar.com/test1-image:latest"}) + check.WaitWithDefaultTimeout() + data := check.InspectImageJSON() + Expect(StringInSlice("foobar.com/test1-image:latest", data[0].RepoTags)).To(BeTrue()) + }) + It("podman commit container with message", func() { _, ec, _ := podmanTest.RunLsContainer("test1") Expect(ec).To(Equal(0)) diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 9a526b778..12e4f3508 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -56,6 +56,13 @@ var _ = Describe("Podman create", func() { Expect(podmanTest.NumberOfContainers()).To(Equal(1)) }) + It("podman container create container based on a remote image", func() { + session := podmanTest.Podman([]string{"container", "create", BB_GLIBC, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + }) + It("podman create using short options", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/diff_test.go b/test/e2e/diff_test.go index 94e150467..82ced7cfa 100644 --- a/test/e2e/diff_test.go +++ b/test/e2e/diff_test.go @@ -43,6 +43,13 @@ var _ = Describe("Podman diff", func() { Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) }) + It("podman container diff of image", func() { + session := podmanTest.Podman([]string{"container", "diff", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) + }) + It("podman diff bogus image", func() { session := podmanTest.Podman([]string{"diff", "1234"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go index 5839b364d..667a81d07 100644 --- a/test/e2e/exec_test.go +++ b/test/e2e/exec_test.go @@ -57,6 +57,16 @@ var _ = Describe("Podman exec", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman container exec simple command", func() { + setup := podmanTest.RunTopContainer("test1") + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"container", "exec", "test1", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman exec simple command using latest", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() diff --git a/test/e2e/export_test.go b/test/e2e/export_test.go index dba0a2255..114c28a3d 100644 --- a/test/e2e/export_test.go +++ b/test/e2e/export_test.go @@ -50,6 +50,22 @@ var _ = Describe("Podman export", func() { Expect(err).To(BeNil()) }) + It("podman container export output flag", func() { + SkipIfRemote() + _, ec, cid := podmanTest.RunLsContainer("") + Expect(ec).To(Equal(0)) + + outfile := filepath.Join(podmanTest.TempDir, "container.tar") + result := podmanTest.Podman([]string{"container", "export", "-o", outfile, cid}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + _, err := os.Stat(outfile) + Expect(err).To(BeNil()) + + err = os.Remove(outfile) + Expect(err).To(BeNil()) + }) + It("podman export bad filename", func() { _, ec, cid := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index 595084403..e26f4affd 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -43,6 +43,15 @@ var _ = Describe("Podman images", func() { Expect(session.LineInOuputStartsWith("docker.io/library/busybox")).To(BeTrue()) }) + It("podman image List", func() { + session := podmanTest.Podman([]string{"image", "list"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 2)) + Expect(session.LineInOuputStartsWith("docker.io/library/alpine")).To(BeTrue()) + Expect(session.LineInOuputStartsWith("docker.io/library/busybox")).To(BeTrue()) + }) + It("podman images with multiple tags", func() { // tag "docker.io/library/alpine:latest" to "foo:{a,b,c}" session := podmanTest.Podman([]string{"tag", ALPINE, "foo:a", "foo:b", "foo:c"}) @@ -135,6 +144,23 @@ var _ = Describe("Podman images", func() { Expect(len(result.OutputToStringArray())).To(Equal(1)) }) + It("podman image list filter after image", func() { + if podmanTest.RemoteTest { + Skip("Does not work on remote client") + } + rmi := podmanTest.Podman([]string{"image", "rm", "busybox"}) + rmi.WaitWithDefaultTimeout() + Expect(rmi.ExitCode()).To(Equal(0)) + + dockerfile := `FROM docker.io/library/alpine:latest +` + podmanTest.BuildImage(dockerfile, "foobar.com/before:latest", "false") + result := podmanTest.Podman([]string{"image", "list", "-q", "-f", "after=docker.io/library/alpine:latest"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(len(result.OutputToStringArray())).To(Equal(1)) + }) + It("podman images filter dangling", func() { if podmanTest.RemoteTest { Skip("Does not work on remote client") @@ -164,6 +190,21 @@ var _ = Describe("Podman images", func() { Expect(result.ExitCode()).To(Equal(0)) }) + It("podman check for image with sha256: prefix", func() { + if podmanTest.RemoteTest { + Skip("Does not work on remote client") + } + session := podmanTest.Podman([]string{"image", "inspect", "--format=json", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.IsJSONOutputValid()).To(BeTrue()) + imageData := session.InspectImageJSON() + + result := podmanTest.Podman([]string{"image", "ls", fmt.Sprintf("sha256:%s", imageData[0].ID)}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + }) + It("podman images sort by tag", func() { session := podmanTest.Podman([]string{"images", "--sort", "tag", "--format={{.Tag}}"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/kill_test.go b/test/e2e/kill_test.go index 5f1f5f4c1..cde8729c8 100644 --- a/test/e2e/kill_test.go +++ b/test/e2e/kill_test.go @@ -41,6 +41,19 @@ var _ = Describe("Podman kill", func() { Expect(session.ExitCode()).To(Not(Equal(0))) }) + It("podman container kill a running container by id", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + + result := podmanTest.Podman([]string{"container", "kill", cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + }) + It("podman kill a running container by id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() diff --git a/test/e2e/mount_test.go b/test/e2e/mount_test.go index 94218e6a9..bf0442de2 100644 --- a/test/e2e/mount_test.go +++ b/test/e2e/mount_test.go @@ -49,6 +49,21 @@ var _ = Describe("Podman mount", func() { Expect(umount.ExitCode()).To(Equal(0)) }) + It("podman container mount", func() { + setup := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + cid := setup.OutputToString() + + mount := podmanTest.Podman([]string{"container", "mount", cid}) + mount.WaitWithDefaultTimeout() + Expect(mount.ExitCode()).To(Equal(0)) + + umount := podmanTest.Podman([]string{"container", "umount", cid}) + umount.WaitWithDefaultTimeout() + Expect(umount.ExitCode()).To(Equal(0)) + }) + It("podman mount with json format", func() { setup := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() diff --git a/test/e2e/pause_test.go b/test/e2e/pause_test.go index f1ea17ead..7530ca85c 100644 --- a/test/e2e/pause_test.go +++ b/test/e2e/pause_test.go @@ -80,6 +80,23 @@ var _ = Describe("Podman pause", func() { result.WaitWithDefaultTimeout() }) + It("podman container pause a running container by id", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + + result := podmanTest.Podman([]string{"container", "pause", cid}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring(pausedState)) + + result = podmanTest.Podman([]string{"container", "unpause", cid}) + result.WaitWithDefaultTimeout() + }) + It("podman unpause a running container by id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() diff --git a/test/e2e/port_test.go b/test/e2e/port_test.go index fa633c379..6ddc5d34f 100644 --- a/test/e2e/port_test.go +++ b/test/e2e/port_test.go @@ -60,6 +60,19 @@ var _ = Describe("Podman port", func() { Expect(result.LineInOuputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) }) + It("podman container port -l nginx", func() { + podmanTest.RestoreArtifact(nginx) + session := podmanTest.Podman([]string{"container", "run", "-dt", "-P", nginx}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + result := podmanTest.Podman([]string{"container", "port", "-l"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + port := strings.Split(result.OutputToStringArray()[0], ":")[1] + Expect(result.LineInOuputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) + }) + It("podman port -l port nginx", func() { podmanTest.RestoreArtifact(nginx) session := podmanTest.Podman([]string{"run", "-dt", "-P", nginx}) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 9b1c55bb4..a31fc3f09 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -65,6 +65,21 @@ var _ = Describe("Podman ps", func() { Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) }) + It("podman container list all", func() { + _, ec, _ := podmanTest.RunLsContainer("") + Expect(ec).To(Equal(0)) + + result := podmanTest.Podman([]string{"container", "list", "-a"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) + + result = podmanTest.Podman([]string{"container", "ls", "-a"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) + }) + It("podman ps size flag", func() { _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go index 5c914a367..a101219d4 100644 --- a/test/e2e/restart_test.go +++ b/test/e2e/restart_test.go @@ -90,6 +90,21 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString()))) }) + It("Podman container restart running container", func() { + _ = podmanTest.RunTopContainer("test1") + ok := WaitForContainer(podmanTest) + Expect(ok).To(BeTrue()) + startTime := podmanTest.Podman([]string{"container", "inspect", "--format='{{.State.StartedAt}}'", "test1"}) + startTime.WaitWithDefaultTimeout() + + session := podmanTest.Podman([]string{"container", "restart", "test1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + restartTime := podmanTest.Podman([]string{"container", "inspect", "--format='{{.State.StartedAt}}'", "test1"}) + restartTime.WaitWithDefaultTimeout() + Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString()))) + }) + It("Podman restart multiple containers", func() { _, exitCode, _ := podmanTest.RunLsContainer("test1") Expect(exitCode).To(Equal(0)) diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go index 71dacfa80..db08dda8b 100644 --- a/test/e2e/rm_test.go +++ b/test/e2e/rm_test.go @@ -65,6 +65,17 @@ var _ = Describe("Podman rm", func() { Expect(result.ExitCode()).To(Equal(0)) }) + It("podman container rm created container", func() { + session := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + + result := podmanTest.Podman([]string{"container", "rm", cid}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + }) + It("podman rm running container with -f", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 93ee5036f..4ba32a94a 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -68,6 +68,20 @@ var _ = Describe("Podman run", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman container run a container based on on a short name with localhost", func() { + podmanTest.RestoreArtifact(nginx) + tag := podmanTest.Podman([]string{"image", "tag", nginx, "localhost/libpod/alpine_nginx:latest"}) + tag.WaitWithDefaultTimeout() + + rmi := podmanTest.Podman([]string{"image", "rm", nginx}) + rmi.WaitWithDefaultTimeout() + + session := podmanTest.Podman([]string{"container", "run", "libpod/alpine_nginx:latest", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ErrorToString()).ToNot(ContainSubstring("Trying to pull")) + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman run a container based on local image with short options", func() { session := podmanTest.Podman([]string{"run", "-dt", ALPINE, "ls"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go index c4ed6f545..51fece3f1 100644 --- a/test/e2e/start_test.go +++ b/test/e2e/start_test.go @@ -50,6 +50,16 @@ var _ = Describe("Podman start", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman container start single container by id", func() { + session := podmanTest.Podman([]string{"container", "create", "-d", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + session = podmanTest.Podman([]string{"container", "start", cid}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman start single container by name", func() { session := podmanTest.Podman([]string{"create", "-d", "--name", "foobar99", ALPINE, "ls"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go index 8fffedbb9..cd0d804ee 100644 --- a/test/e2e/stop_test.go +++ b/test/e2e/stop_test.go @@ -59,6 +59,15 @@ var _ = Describe("Podman stop", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman stop container by name", func() { + session := podmanTest.RunTopContainer("test1") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"container", "stop", "test1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman stop stopped container", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() @@ -73,7 +82,7 @@ var _ = Describe("Podman stop", func() { Expect(session3.ExitCode()).To(Equal(0)) }) - It("podman stop all containers", func() { + It("podman stop all containers -t", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -98,6 +107,32 @@ var _ = Describe("Podman stop", func() { Expect(output).To(ContainSubstring(cid3)) }) + It("podman stop container --time", func() { + session := podmanTest.RunTopContainer("test4") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid1 := session.OutputToString() + + session = podmanTest.Podman([]string{"stop", "--time", "1", "test4"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + output := session.OutputToString() + Expect(output).To(ContainSubstring(cid1)) + }) + + It("podman stop container --timeout", func() { + session := podmanTest.RunTopContainer("test5") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid1 := session.OutputToString() + + session = podmanTest.Podman([]string{"stop", "--timeout", "1", "test5"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + output := session.OutputToString() + Expect(output).To(ContainSubstring(cid1)) + }) + It("podman stop latest containers", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() diff --git a/test/e2e/top_test.go b/test/e2e/top_test.go index 067358468..156c37035 100644 --- a/test/e2e/top_test.go +++ b/test/e2e/top_test.go @@ -65,6 +65,17 @@ var _ = Describe("Podman top", func() { Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) }) + It("podman container top on container", func() { + session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "top", "-d", "2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + result := podmanTest.Podman([]string{"container", "top", "-l"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) + }) + It("podman top with options", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/wait_test.go b/test/e2e/wait_test.go index 08da97aa0..69e64774c 100644 --- a/test/e2e/wait_test.go +++ b/test/e2e/wait_test.go @@ -66,4 +66,11 @@ var _ = Describe("Podman wait", func() { session = podmanTest.Podman([]string{"wait", "-l"}) session.Wait(20) }) + It("podman container wait on latest container", func() { + session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) + session.Wait(20) + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"container", "wait", "-l"}) + session.Wait(20) + }) }) |