summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE.md2
-rw-r--r--README.md31
-rw-r--r--cmd/podman/commands.go7
-rw-r--r--cmd/podman/errors.go20
-rw-r--r--cmd/podman/play_kube.go2
-rw-r--r--cmd/podman/pod.go5
-rw-r--r--cmd/podman/pod_create.go77
-rw-r--r--cmd/podman/pod_pause.go40
-rw-r--r--cmd/podman/pod_ps.go46
-rw-r--r--cmd/podman/pod_restart.go40
-rw-r--r--cmd/podman/pod_unpause.go40
-rw-r--r--cmd/podman/rm.go20
-rw-r--r--cmd/podman/rmi.go54
-rw-r--r--cmd/podman/shared/pod.go4
-rw-r--r--cmd/podman/utils.go2
-rw-r--r--contrib/cirrus/packer/fedora_setup.sh1
-rw-r--r--contrib/cirrus/packer/ubuntu_setup.sh1
-rw-r--r--docs/libpod.conf.5.md5
-rw-r--r--docs/podman-container-cleanup.1.md2
-rw-r--r--docs/podman-rm.1.md13
-rw-r--r--docs/podman-rmi.1.md10
-rw-r--r--docs/podman-run.1.md14
-rw-r--r--libpod.conf6
-rw-r--r--libpod/container_internal_linux.go43
-rw-r--r--libpod/options.go4
-rw-r--r--pkg/adapter/containers_remote.go31
-rw-r--r--pkg/adapter/pods.go173
-rw-r--r--pkg/adapter/pods_remote.go231
-rw-r--r--pkg/spec/createconfig.go10
-rw-r--r--pkg/spec/spec.go58
-rw-r--r--pkg/varlinkapi/pods.go4
-rw-r--r--test/e2e/rm_test.go5
-rw-r--r--test/e2e/rmi_test.go2
-rw-r--r--test/e2e/run_networking_test.go39
34 files changed, 793 insertions, 249 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index a7663f3e3..2946f0b91 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -52,7 +52,7 @@ Briefly describe the problem you are having in a few paragraphs.
(paste your output here)
```
-**Output of `podman info`:**
+**Output of `podman info --debug`:**
```
(paste your output here)
diff --git a/README.md b/README.md
index fcc15fa6e..4ea63d5b9 100644
--- a/README.md
+++ b/README.md
@@ -88,18 +88,23 @@ Information about contributing to this project.
## Buildah and Podman relationship
-Buildah and Podman are two complementary Open-source projects that are available on
-most Linux platforms and both projects reside at [GitHub.com](https://github.com)
-with [Buildah](https://buildah.io) [(GitHub)](https://github.com/containers/buildah) and
-[Podman](https://podman.io) [(GitHub)](https://github.com/containers/libpod). Both Buildah and Podman are
-command line tools that work on OCI images and containers. The two projects
-differentiate in their specialization.
+Buildah and Podman are two complementary open-source projects that are
+available on most Linux platforms and both projects reside at
+[GitHub.com](https://github.com) with Buildah
+[here](https://github.com/containers/buildah) and Podman
+[here](https://github.com/containers/libpod). Both, Buildah and Podman are
+command line tools that work on Open Container Initiative (OCI) images and
+containers. The two projects differentiate in their specialization.
Buildah specializes in building OCI images. Buildah's commands replicate all
-of the commands that are found in a Dockerfile. Buildah’s goal is also to
-provide a lower level coreutils interface to build images, allowing people to build
-containers without requiring a Dockerfile. The intent with Buildah is to allow other
-scripting languages to build container images, without requiring a daemon.
+of the commands that are found in a Dockerfile. This allows building images
+with and without Dockerfiles while not requiring any root privileges.
+Buildah’s ultimate goal is to provide a lower-level coreutils interface to
+build images. The flexibility of building images without Dockerfiles allows
+for the integration of other scripting languages into the build process.
+Buildah follows a simple fork-exec model and does not run as a daemon
+but it is based on a comprehensive API in golang, which can be vendored
+into other tools.
Podman specializes in all of the commands and functions that help you to maintain and modify
OCI images, such as pulling and tagging. It also allows you to create, run, and maintain those containers
@@ -108,12 +113,12 @@ created from those images.
A major difference between Podman and Buildah is their concept of a container. Podman
allows users to create "traditional containers" where the intent of these containers is
to be long lived. While Buildah containers are really just created to allow content
-to be added back to the container image. An easy way to think of it is the
+to be added back to the container image. An easy way to think of it is the
`buildah run` command emulates the RUN command in a Dockerfile while the `podman run`
command emulates the `docker run` command in functionality. Because of this and their underlying
-storage differences, you cannot see Podman containers from within Buildah or vice versa.
+storage differences, you can not see Podman containers from within Buildah or vice versa.
-In short Buildah is an efficient way to create OCI images while Podman allows
+In short, Buildah is an efficient way to create OCI images while Podman allows
you to manage and maintain those images and containers in a production environment using
familiar container cli commands. For more details, see the
[Container Tools Guide](https://github.com/containers/buildah/tree/master/docs/containertools).
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go
index fef5f1763..fadcca689 100644
--- a/cmd/podman/commands.go
+++ b/cmd/podman/commands.go
@@ -17,7 +17,6 @@ func getMainCommands() []*cobra.Command {
_diffCommand,
_execCommand,
_generateCommand,
- _containerKubeCommand,
_playCommand,
_psCommand,
_loginCommand,
@@ -39,7 +38,6 @@ func getMainCommands() []*cobra.Command {
_topCommand,
_umountCommand,
_unpauseCommand,
- volumeCommand.Command,
_waitCommand,
}
@@ -94,13 +92,8 @@ func getContainerSubCommands() []*cobra.Command {
// Commands that the local client implements
func getPodSubCommands() []*cobra.Command {
return []*cobra.Command{
- _podCreateCommand,
- _podPauseCommand,
- _podPsCommand,
- _podRestartCommand,
_podStatsCommand,
_podTopCommand,
- _podUnpauseCommand,
}
}
diff --git a/cmd/podman/errors.go b/cmd/podman/errors.go
index 192f97049..2572b8779 100644
--- a/cmd/podman/errors.go
+++ b/cmd/podman/errors.go
@@ -6,8 +6,6 @@ import (
"os/exec"
"syscall"
- "github.com/containers/libpod/cmd/podman/varlink"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -20,22 +18,6 @@ func outputError(err error) {
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())
+ fmt.Fprintln(os.Stderr, "Error:", err.Error())
}
}
diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go
index 9fc06dde9..a59460b71 100644
--- a/cmd/podman/play_kube.go
+++ b/cmd/podman/play_kube.go
@@ -52,8 +52,6 @@ func init() {
flags.BoolVarP(&playKubeCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.StringVar(&playKubeCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries (default: true)")
-
- rootCmd.AddCommand(playKubeCommand.Command)
}
func playKubeYAMLCmd(c *cliconfig.KubePlayValues) error {
diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go
index 4c7c416ce..c1350bd4d 100644
--- a/cmd/podman/pod.go
+++ b/cmd/podman/pod.go
@@ -20,12 +20,17 @@ var podCommand = cliconfig.PodmanCommand{
//podSubCommands are implemented both in local and remote clients
var podSubCommands = []*cobra.Command{
+ _podCreateCommand,
_podExistsCommand,
_podInspectCommand,
_podKillCommand,
+ _podPauseCommand,
+ _podPsCommand,
+ _podRestartCommand,
_podRmCommand,
_podStartCommand,
_podStopCommand,
+ _podUnpauseCommand,
}
func init() {
diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go
index d5ca5e13a..f1bbecb84 100644
--- a/cmd/podman/pod_create.go
+++ b/cmd/podman/pod_create.go
@@ -3,12 +3,10 @@ package main
import (
"fmt"
"os"
- "strings"
"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/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -56,20 +54,29 @@ func init() {
}
func podCreateCmd(c *cliconfig.PodCreateValues) error {
- var options []libpod.PodCreateOption
- var err error
+ var (
+ err error
+ podIdFile *os.File
+ )
if len(c.InputArgs) > 0 {
return errors.New("podman pod create does not accept any arguments")
}
-
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
defer runtime.Shutdown(false)
- var podIdFile *os.File
+ if len(c.Publish) > 0 {
+ if !c.Infra {
+ return errors.Errorf("you must have an infra container to publish port bindings to the host")
+ }
+ }
+
+ if !c.Infra && c.Flag("share").Changed && c.Share != "none" && c.Share != "" {
+ return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container")
+ }
if c.Flag("pod-id-file").Changed && os.Geteuid() == 0 {
podIdFile, err = libpod.OpenExclusiveFile(c.PodIDFile)
if err != nil && os.IsExist(err) {
@@ -82,67 +89,21 @@ func podCreateCmd(c *cliconfig.PodCreateValues) error {
defer podIdFile.Sync()
}
- if len(c.Publish) > 0 {
- if !c.Infra {
- return errors.Errorf("you must have an infra container to publish port bindings to the host")
- }
- }
-
- if !c.Infra && c.Flag("share").Changed && c.Share != "none" && c.Share != "" {
- return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container")
- }
-
- if c.Flag("cgroup-parent").Changed {
- options = append(options, libpod.WithPodCgroupParent(c.CgroupParent))
- }
-
labels, err := getAllLabels(c.LabelFile, c.Labels)
if err != nil {
return errors.Wrapf(err, "unable to process labels")
}
- if len(labels) != 0 {
- options = append(options, libpod.WithPodLabels(labels))
- }
-
- if c.Flag("name").Changed {
- options = append(options, libpod.WithPodName(c.Name))
- }
-
- if c.Infra {
- options = append(options, libpod.WithInfraContainer())
- nsOptions, err := shared.GetNamespaceOptions(strings.Split(c.Share, ","))
- if err != nil {
- return err
- }
- options = append(options, nsOptions...)
- }
- if len(c.Publish) > 0 {
- portBindings, err := shared.CreatePortBindings(c.Publish)
- if err != nil {
- return err
- }
- options = append(options, libpod.WithInfraContainerPorts(portBindings))
-
- }
- // always have containers use pod cgroups
- // User Opt out is not yet supported
- options = append(options, libpod.WithPodCgroups())
-
- ctx := getContext()
- pod, err := runtime.NewPod(ctx, options...)
+ podID, err := runtime.CreatePod(getContext(), c, labels)
if err != nil {
- return err
+ return errors.Wrapf(err, "unable to create pod")
}
-
if podIdFile != nil {
- _, err = podIdFile.WriteString(pod.ID())
+ _, err = podIdFile.WriteString(podID)
if err != nil {
logrus.Error(err)
}
}
-
- fmt.Printf("%s\n", pod.ID())
-
+ fmt.Printf("%s\n", podID)
return nil
}
diff --git a/cmd/podman/pod_pause.go b/cmd/podman/pod_pause.go
index e01d73c9b..284740d22 100644
--- a/cmd/podman/pod_pause.go
+++ b/cmd/podman/pod_pause.go
@@ -3,7 +3,7 @@ package main
import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -40,37 +40,33 @@ func init() {
}
func podPauseCmd(c *cliconfig.PodPauseValues) error {
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ var lastError error
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
defer runtime.Shutdown(false)
- // getPodsFromContext returns an error when a requested pod
- // isn't found. The only fatal error scenerio is when there are no pods
- // in which case the following loop will be skipped.
- pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime)
+ pauseIDs, conErrors, pauseErrors := runtime.PausePods(c)
- for _, pod := range pods {
- ctr_errs, err := pod.Pause()
- if ctr_errs != nil {
- for ctr, err := range ctr_errs {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to pause container %q on pod %q", ctr, pod.ID())
- }
- continue
- }
- if err != nil {
+ for _, p := range pauseIDs {
+ fmt.Println(p)
+ }
+ if conErrors != nil && len(conErrors) > 0 {
+ for ctr, err := range conErrors {
if lastError != nil {
logrus.Errorf("%q", lastError)
}
- lastError = errors.Wrapf(err, "unable to pause pod %q", pod.ID())
- continue
+ lastError = errors.Wrapf(err, "unable to pause container %s", ctr)
}
- fmt.Println(pod.ID())
}
-
+ if len(pauseErrors) > 0 {
+ lastError = pauseErrors[len(pauseErrors)-1]
+ // Remove the last error from the error slice
+ pauseErrors = pauseErrors[:len(pauseErrors)-1]
+ }
+ for _, err := range pauseErrors {
+ logrus.Errorf("%q", err)
+ }
return lastError
}
diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go
index 85467b6ad..70e077651 100644
--- a/cmd/podman/pod_ps.go
+++ b/cmd/podman/pod_ps.go
@@ -10,9 +10,9 @@ import (
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/formats"
- "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/util"
"github.com/docker/go-units"
"github.com/pkg/errors"
@@ -29,6 +29,8 @@ const (
NUM_CTR_INFO = 10
)
+type PodFilter func(*adapter.Pod) bool
+
var (
bc_opts shared.PsOptions
)
@@ -152,7 +154,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
return errors.Wrapf(err, "error with flags passed")
}
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
@@ -173,7 +175,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
opts.Format = genPodPsFormat(c)
- var filterFuncs []libpod.PodFilter
+ var filterFuncs []PodFilter
if c.Filter != "" {
filters := strings.Split(c.Filter, ",")
for _, f := range filters {
@@ -181,7 +183,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
if len(filterSplit) < 2 {
return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
}
- generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1], runtime)
+ generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1])
if err != nil {
return errors.Wrapf(err, "invalid filter")
}
@@ -189,7 +191,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
}
}
- var pods []*libpod.Pod
+ var pods []*adapter.Pod
if c.Latest {
pod, err := runtime.GetLatestPod()
if err != nil {
@@ -203,7 +205,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
}
}
- podsFiltered := make([]*libpod.Pod, 0, len(pods))
+ podsFiltered := make([]*adapter.Pod, 0, len(pods))
for _, pod := range pods {
include := true
for _, filter := range filterFuncs {
@@ -215,7 +217,7 @@ func podPsCmd(c *cliconfig.PodPsValues) error {
}
}
- return generatePodPsOutput(podsFiltered, opts, runtime)
+ return generatePodPsOutput(podsFiltered, opts)
}
// podPsCheckFlagsPassed checks if mutually exclusive flags are passed together
@@ -234,10 +236,10 @@ func podPsCheckFlagsPassed(c *cliconfig.PodPsValues) error {
return nil
}
-func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) (func(pod *libpod.Pod) bool, error) {
+func generatePodFilterFuncs(filter, filterValue string) (func(pod *adapter.Pod) bool, error) {
switch filter {
case "ctr-ids":
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
ctrIds, err := p.AllContainersByID()
if err != nil {
return false
@@ -245,7 +247,7 @@ func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime)
return util.StringInSlice(filterValue, ctrIds)
}, nil
case "ctr-names":
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
ctrs, err := p.AllContainers()
if err != nil {
return false
@@ -258,7 +260,7 @@ func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime)
return false
}, nil
case "ctr-number":
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
ctrIds, err := p.AllContainersByID()
if err != nil {
return false
@@ -274,7 +276,7 @@ func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime)
if !util.StringInSlice(filterValue, []string{"created", "restarting", "running", "paused", "exited", "unknown"}) {
return nil, errors.Errorf("%s is not a valid status", filterValue)
}
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
ctr_statuses, err := p.Status()
if err != nil {
return false
@@ -291,19 +293,19 @@ func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime)
return false
}, nil
case "id":
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
return strings.Contains(p.ID(), filterValue)
}, nil
case "name":
- return func(p *libpod.Pod) bool {
+ return func(p *adapter.Pod) bool {
return strings.Contains(p.Name(), filterValue)
}, nil
case "status":
if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) {
return nil, errors.Errorf("%s is not a valid pod status", filterValue)
}
- return func(p *libpod.Pod) bool {
- status, err := shared.GetPodStatus(p)
+ return func(p *adapter.Pod) bool {
+ status, err := p.GetPodStatus()
if err != nil {
return false
}
@@ -448,7 +450,7 @@ func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podP
return psOutput, nil
}
-func getNamespaces(pod *libpod.Pod) []string {
+func getNamespaces(pod *adapter.Pod) []string {
var shared []string
if pod.SharesPID() {
shared = append(shared, "pid")
@@ -475,7 +477,7 @@ func getNamespaces(pod *libpod.Pod) []string {
}
// getAndSortPodJSONOutput returns the container info in its raw, sorted form
-func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) ([]podPsJSONParams, error) {
+func getAndSortPodJSONParams(pods []*adapter.Pod, opts podPsOptions) ([]podPsJSONParams, error) {
var (
psOutput []podPsJSONParams
)
@@ -487,7 +489,7 @@ func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *lib
return nil, err
}
ctrNum := len(ctrs)
- status, err := shared.GetPodStatus(pod)
+ status, err := pod.GetPodStatus()
if err != nil {
return nil, err
}
@@ -497,7 +499,7 @@ func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *lib
return nil, err
}
for _, ctr := range ctrs {
- batchInfo, err := shared.BatchContainerOp(ctr, bc_opts)
+ batchInfo, err := adapter.BatchContainerOp(ctr, bc_opts)
if err != nil {
return nil, err
}
@@ -539,11 +541,11 @@ func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *lib
return sortPodPsOutput(opts.Sort, psOutput)
}
-func generatePodPsOutput(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) error {
+func generatePodPsOutput(pods []*adapter.Pod, opts podPsOptions) error {
if len(pods) == 0 && opts.Format != formats.JSONString {
return nil
}
- psOutput, err := getAndSortPodJSONParams(pods, opts, runtime)
+ psOutput, err := getAndSortPodJSONParams(pods, opts)
if err != nil {
return err
}
diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go
index be54630db..741fce588 100644
--- a/cmd/podman/pod_restart.go
+++ b/cmd/podman/pod_restart.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -42,37 +42,33 @@ func init() {
}
func podRestartCmd(c *cliconfig.PodRestartValues) error {
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ var lastError error
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
- // getPodsFromContext returns an error when a requested pod
- // isn't found. The only fatal error scenerio is when there are no pods
- // in which case the following loop will be skipped.
- pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime)
+ restartIDs, conErrors, restartErrors := runtime.RestartPods(getContext(), c)
- ctx := getContext()
- for _, pod := range pods {
- ctr_errs, err := pod.Restart(ctx)
- if ctr_errs != nil {
- for ctr, err := range ctr_errs {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to restart container %q on pod %q", ctr, pod.ID())
- }
- continue
- }
- if err != nil {
+ for _, p := range restartIDs {
+ fmt.Println(p)
+ }
+ if conErrors != nil && len(conErrors) > 0 {
+ for ctr, err := range conErrors {
if lastError != nil {
logrus.Errorf("%q", lastError)
}
- lastError = errors.Wrapf(err, "unable to restart pod %q", pod.ID())
- continue
+ lastError = errors.Wrapf(err, "unable to pause container %s", ctr)
}
- fmt.Println(pod.ID())
+ }
+ if len(restartErrors) > 0 {
+ lastError = restartErrors[len(restartErrors)-1]
+ // Remove the last error from the error slice
+ restartErrors = restartErrors[:len(restartErrors)-1]
+ }
+ for _, err := range restartErrors {
+ logrus.Errorf("%q", err)
}
return lastError
}
diff --git a/cmd/podman/pod_unpause.go b/cmd/podman/pod_unpause.go
index 35128e87b..16481d0e2 100644
--- a/cmd/podman/pod_unpause.go
+++ b/cmd/podman/pod_unpause.go
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/pkg/adapter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -41,37 +41,33 @@ func init() {
}
func podUnpauseCmd(c *cliconfig.PodUnpauseValues) error {
- runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
+ var lastError error
+ runtime, err := adapter.GetRuntime(&c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
defer runtime.Shutdown(false)
- // getPodsFromContext returns an error when a requested pod
- // isn't found. The only fatal error scenerio is when there are no pods
- // in which case the following loop will be skipped.
- pods, lastError := getPodsFromContext(&c.PodmanCommand, runtime)
+ unpauseIDs, conErrors, unpauseErrors := runtime.UnpausePods(c)
- for _, pod := range pods {
- ctr_errs, err := pod.Unpause()
- if ctr_errs != nil {
- for ctr, err := range ctr_errs {
- if lastError != nil {
- logrus.Errorf("%q", lastError)
- }
- lastError = errors.Wrapf(err, "unable to unpause container %q on pod %q", ctr, pod.ID())
- }
- continue
- }
- if err != nil {
+ for _, p := range unpauseIDs {
+ fmt.Println(p)
+ }
+ if conErrors != nil && len(conErrors) > 0 {
+ for ctr, err := range conErrors {
if lastError != nil {
logrus.Errorf("%q", lastError)
}
- lastError = errors.Wrapf(err, "unable to unpause pod %q", pod.ID())
- continue
+ lastError = errors.Wrapf(err, "unable to unpause container %s", ctr)
}
- fmt.Println(pod.ID())
}
-
+ if len(unpauseErrors) > 0 {
+ lastError = unpauseErrors[len(unpauseErrors)-1]
+ // Remove the last error from the error slice
+ unpauseErrors = unpauseErrors[:len(unpauseErrors)-1]
+ }
+ for _, err := range unpauseErrors {
+ logrus.Errorf("%q", err)
+ }
return lastError
}
diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go
index 01ed70f52..2dcb491d7 100644
--- a/cmd/podman/rm.go
+++ b/cmd/podman/rm.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/image"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -61,15 +62,21 @@ func rmCmd(c *cliconfig.RmValues) error {
}
defer runtime.Shutdown(false)
+ failureCnt := 0
delContainers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all")
if err != nil {
if c.Force && len(c.InputArgs) > 0 {
if errors.Cause(err) == libpod.ErrNoSuchCtr {
err = nil
+ } else {
+ failureCnt++
}
runtime.RemoveContainersFromStorage(c.InputArgs)
}
if len(delContainers) == 0 {
+ if err != nil && failureCnt == 0 {
+ exitCode = 1
+ }
return err
}
if err != nil {
@@ -96,5 +103,16 @@ func rmCmd(c *cliconfig.RmValues) error {
// Run the parallel funcs
deleteErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs)
- return printParallelOutput(deleteErrors, errCount)
+ err = printParallelOutput(deleteErrors, errCount)
+ if err != nil {
+ for _, result := range deleteErrors {
+ if result != nil && errors.Cause(result) != image.ErrNoSuchCtr {
+ failureCnt++
+ }
+ }
+ if failureCnt == 0 {
+ exitCode = 1
+ }
+ }
+ return err
}
diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go
index fbaa19336..709ed14e0 100644
--- a/cmd/podman/rmi.go
+++ b/cmd/podman/rmi.go
@@ -5,6 +5,8 @@ import (
"os"
"github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/adapter"
"github.com/containers/storage"
"github.com/pkg/errors"
@@ -29,6 +31,17 @@ var (
}
)
+func imageNotFound(err error) bool {
+ if errors.Cause(err) == image.ErrNoSuchImage {
+ return true
+ }
+ switch err.(type) {
+ case *iopodman.ImageNotFound:
+ return true
+ }
+ return false
+}
+
func init() {
rmiCommand.Command = _rmiCommand
rmiCommand.SetUsageTemplate(UsageTemplate())
@@ -39,10 +52,8 @@ func init() {
func rmiCmd(c *cliconfig.RmiValues) error {
var (
- lastError error
- deleted bool
- deleteErr error
- msg string
+ lastError error
+ failureCnt int
)
ctx := getContext()
@@ -64,19 +75,21 @@ func rmiCmd(c *cliconfig.RmiValues) error {
images := args[:]
removeImage := func(img *adapter.ContainerImage) {
- deleted = true
- msg, deleteErr = runtime.RemoveImage(ctx, img, c.Force)
- if deleteErr != nil {
- if errors.Cause(deleteErr) == storage.ErrImageUsedByContainer {
+ msg, err := runtime.RemoveImage(ctx, img, c.Force)
+ if err != nil {
+ if errors.Cause(err) == storage.ErrImageUsedByContainer {
fmt.Printf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID())
}
+ if !imageNotFound(err) {
+ failureCnt++
+ }
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
- lastError = deleteErr
- } else {
- fmt.Println(msg)
+ lastError = err
+ return
}
+ fmt.Println(msg)
}
if removeAll {
@@ -121,22 +134,21 @@ func rmiCmd(c *cliconfig.RmiValues) error {
for _, i := range images {
newImage, err := runtime.NewImageFromLocal(i)
if err != nil {
- fmt.Fprintln(os.Stderr, err)
+ if lastError != nil {
+ if !imageNotFound(lastError) {
+ failureCnt++
+ }
+ fmt.Fprintln(os.Stderr, lastError)
+ }
+ lastError = err
continue
}
removeImage(newImage)
}
}
- // If the user calls remove all and there are none, it should not be a
- // non-zero exit
- if !deleted && removeAll {
- return nil
- }
- // the user tries to remove images that do not exist, that should be a
- // non-zero exit
- if !deleted {
- return errors.Errorf("no valid images to delete")
+ if imageNotFound(lastError) && failureCnt == 0 {
+ exitCode = 1
}
return lastError
diff --git a/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go
index 30dd14845..5f65c40ac 100644
--- a/cmd/podman/shared/pod.go
+++ b/cmd/podman/shared/pod.go
@@ -26,6 +26,10 @@ func GetPodStatus(pod *libpod.Pod) (string, error) {
if err != nil {
return errored, err
}
+ return CreatePodStatusResults(ctrStatuses)
+}
+
+func CreatePodStatusResults(ctrStatuses map[string]libpod.ContainerStatus) (string, error) {
ctrNum := len(ctrStatuses)
if ctrNum == 0 {
return created, nil
diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go
index 0fbea417b..4ec0f8a13 100644
--- a/cmd/podman/utils.go
+++ b/cmd/podman/utils.go
@@ -172,7 +172,7 @@ func getPodsFromContext(c *cliconfig.PodmanCommand, r *libpod.Runtime) ([]*libpo
var err error
if c.Bool("all") {
- pods, err = r.Pods()
+ pods, err = r.GetAllPods()
if err != nil {
return nil, errors.Wrapf(err, "unable to get running pods")
}
diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh
index 01c468901..de7ad4506 100644
--- a/contrib/cirrus/packer/fedora_setup.sh
+++ b/contrib/cirrus/packer/fedora_setup.sh
@@ -40,6 +40,7 @@ ooe.sh sudo dnf install -y \
golang-github-cpuguy83-go-md2man \
gpgme-devel \
iptables \
+ iproute \
libassuan-devel \
libcap-devel \
libnet \
diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh
index 7d49c5dc7..5b7e1d714 100644
--- a/contrib/cirrus/packer/ubuntu_setup.sh
+++ b/contrib/cirrus/packer/ubuntu_setup.sh
@@ -48,6 +48,7 @@ ooe.sh sudo -E apt-get -qq install \
gettext \
go-md2man \
golang \
+ iproute \
iptables \
libaio-dev \
libapparmor-dev \
diff --git a/docs/libpod.conf.5.md b/docs/libpod.conf.5.md
index 0c11e2013..0836c45fa 100644
--- a/docs/libpod.conf.5.md
+++ b/docs/libpod.conf.5.md
@@ -82,6 +82,11 @@ libpod to manage containers.
**label**="true|false"
Indicates whether the containers should use label separation.
+**num_locks**=""
+ Number of locks available for containers and pods. Each created container or pod consumes one lock.
+ The default number available is 2048.
+ If this is changed, a lock renumbering must be performed, using the `podman system renumber` command.
+
## FILES
`/usr/share/containers/libpod.conf`, default libpod configuration path
diff --git a/docs/podman-container-cleanup.1.md b/docs/podman-container-cleanup.1.md
index e375c12ec..2ad39d214 100644
--- a/docs/podman-container-cleanup.1.md
+++ b/docs/podman-container-cleanup.1.md
@@ -30,7 +30,7 @@ The latest option is not supported on the remote client.
`podman container cleanup 860a4b23`
-`podman container-cleanup -a`
+`podman container cleanup -a`
`podman container cleanup --latest`
diff --git a/docs/podman-rm.1.md b/docs/podman-rm.1.md
index 10ebe97f9..dc1729188 100644
--- a/docs/podman-rm.1.md
+++ b/docs/podman-rm.1.md
@@ -1,9 +1,11 @@
-% podman-rm(1)
+% podman-container-rm(1)
## NAME
-podman\-rm - Remove one or more containers
+podman\-container\-rm (podman\-rm) - Remove one or more containers
## SYNOPSIS
+**podman container rm** [*options*] *container*
+
**podman rm** [*options*] *container*
## DESCRIPTION
@@ -57,8 +59,13 @@ Forcibly remove the latest container created.
podman rm -f --latest
```
+## Exit Status
+**_0_** if all specified containers removed
+**_1_** if one of the specified containers did not exist, and no other failures
+**_125_** if command fails for a reason other then an container did not exist
+
## SEE ALSO
-podman(1), podman-rmi(1)
+podman(1), podman-image-rm(1)
## HISTORY
August 2017, Originally compiled by Ryan Cole <rycole@redhat.com>
diff --git a/docs/podman-rmi.1.md b/docs/podman-rmi.1.md
index 9c080c9f1..8c22bba2c 100644
--- a/docs/podman-rmi.1.md
+++ b/docs/podman-rmi.1.md
@@ -1,9 +1,11 @@
-% podman-rmi(1)
+% podman-image-rm(1)
## NAME
-podman\-rmi - Removes one or more images
+podman\-image\-rm (podman\-rmi) - Removes one or more images
## SYNOPSIS
+**podman image rm** *image* ...
+
**podman rmi** *image* ...
## DESCRIPTION
@@ -38,6 +40,10 @@ Remove all images and containers.
```
podman rmi -a -f
```
+## Exit Status
+**_0_** if all specified images removed
+**_1_** if one of the specified images did not exist, and no other failures
+**_125_** if command fails for a reason other then an image did not exist
## SEE ALSO
podman(1)
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index b928f61f5..bbf10a2ce 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -28,6 +28,8 @@ servers in the created `resolv.conf`). Additionally, an empty file is created in
each container to indicate to programs they are running in a container. This file
is located at `/run/.containerenv`.
+When running from a user defined network namespace, the /etc/netns/NSNAME/resolv.conf will be used if it exists, otherwise /etc/resolv.conf will be used.
+
## OPTIONS
**--add-host**=[]
@@ -694,21 +696,21 @@ Current supported mount TYPES are bind, and tmpfs.
Common Options:
- · src, source: mount source spec for bind and volume. Mandatory for bind.
+ · src, source: mount source spec for bind and volume. Mandatory for bind.
- · dst, destination, target: mount destination spec.
+ · dst, destination, target: mount destination spec.
- · ro, read-only: true or false (default).
+ · ro, read-only: true or false (default).
Options specific to bind:
- · bind-propagation: Z, z, shared, slave, private, rshared, rslave, or rprivate(default). See also mount(2).
+ · bind-propagation: Z, z, shared, slave, private, rshared, rslave, or rprivate(default). See also mount(2).
Options specific to tmpfs:
- · tmpfs-size: Size of the tmpfs mount in bytes. Unlimited by default in Linux.
+ · tmpfs-size: Size of the tmpfs mount in bytes. Unlimited by default in Linux.
- · tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux.
+ · tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux.
**--userns**=""
diff --git a/libpod.conf b/libpod.conf
index c4e7dc628..8d6158ed5 100644
--- a/libpod.conf
+++ b/libpod.conf
@@ -88,6 +88,12 @@ pause_command = "/pause"
# Default libpod support for container labeling
# label=true
+# Number of locks available for containers and pods.
+# If this is changed, a lock renumber must be performed (e.g. with the
+# 'podman system renumber' command).
+num_locks = 2048
+
+# Default OCI runtime
runtime = "runc"
# Paths to look for a valid OCI runtime (runc, runv, etc)
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 86f94477e..f182b6bdf 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -26,7 +26,6 @@ import (
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/secrets"
"github.com/containers/storage/pkg/idtools"
- "github.com/mrunalp/fileutils"
"github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
@@ -677,20 +676,12 @@ func (c *Container) makeBindMounts() error {
// If it doesn't, don't copy them
resolvPath, exists := bindMounts["/etc/resolv.conf"]
if exists {
- resolvDest := filepath.Join(c.state.RunDir, "resolv.conf")
- if err := fileutils.CopyFile(resolvPath, resolvDest); err != nil {
- return errors.Wrapf(err, "error copying resolv.conf from dependency container %s of container %s", depCtr.ID(), c.ID())
- }
- c.state.BindMounts["/etc/resolv.conf"] = resolvDest
- }
+ c.state.BindMounts["/etc/resolv.conf"] = resolvPath
+ }
hostsPath, exists := bindMounts["/etc/hosts"]
if exists {
- hostsDest := filepath.Join(c.state.RunDir, "hosts")
- if err := fileutils.CopyFile(hostsPath, hostsDest); err != nil {
- return errors.Wrapf(err, "error copying hosts file from dependency container %s of container %s", depCtr.ID(), c.ID())
- }
- c.state.BindMounts["/etc/hosts"] = hostsDest
+ c.state.BindMounts["/etc/hosts"] = hostsPath
}
} else {
newResolv, err := c.generateResolvConf()
@@ -705,6 +696,14 @@ func (c *Container) makeBindMounts() error {
}
c.state.BindMounts["/etc/hosts"] = newHosts
}
+
+ if err := label.Relabel(c.state.BindMounts["/etc/hosts"], c.config.MountLabel, true); err != nil {
+ return err
+ }
+
+ if err := label.Relabel(c.state.BindMounts["/etc/resolv.conf"], c.config.MountLabel, true); err != nil {
+ return err
+ }
}
// SHM is always added when we mount the container
@@ -758,8 +757,24 @@ func (c *Container) makeBindMounts() error {
// generateResolvConf generates a containers resolv.conf
func (c *Container) generateResolvConf() (string, error) {
+ resolvConf := "/etc/resolv.conf"
+ for _, ns := range c.config.Spec.Linux.Namespaces {
+ if ns.Type == spec.NetworkNamespace {
+ if ns.Path != "" && !strings.HasPrefix(ns.Path, "/proc/") {
+ definedPath := filepath.Join("/etc/netns", filepath.Base(ns.Path), "resolv.conf")
+ _, err := os.Stat(definedPath)
+ if err == nil {
+ resolvConf = definedPath
+ } else if !os.IsNotExist(err) {
+ return "", errors.Wrapf(err, "failed to stat %s", definedPath)
+ }
+ }
+ break
+ }
+ }
+
// Determine the endpoint for resolv.conf in case it is a symlink
- resolvPath, err := filepath.EvalSymlinks("/etc/resolv.conf")
+ resolvPath, err := filepath.EvalSymlinks(resolvConf)
if err != nil {
return "", err
}
@@ -809,7 +824,7 @@ func (c *Container) generateResolvConf() (string, error) {
}
// Relabel resolv.conf for the container
- if err := label.Relabel(destPath, c.config.MountLabel, false); err != nil {
+ if err := label.Relabel(destPath, c.config.MountLabel, true); err != nil {
return "", err
}
diff --git a/libpod/options.go b/libpod/options.go
index 9aa020b56..e22c81f91 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -904,10 +904,10 @@ func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netmo
}
ctr.config.PostConfigureNetNS = postConfigureNetNS
- ctr.config.CreateNetNS = true
+ ctr.config.NetMode = namespaces.NetworkMode(netmode)
+ ctr.config.CreateNetNS = !ctr.config.NetMode.IsUserDefined()
ctr.config.PortMappings = portMappings
ctr.config.Networks = networks
- ctr.config.NetMode = namespaces.NetworkMode(netmode)
return nil
}
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index 9623304e5..3f43a6905 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -4,6 +4,7 @@ package adapter
import (
"encoding/json"
+ "github.com/containers/libpod/cmd/podman/shared"
iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
@@ -48,3 +49,33 @@ func (c *Container) Config() *libpod.ContainerConfig {
}
return c.Runtime.Config(c.ID())
}
+
+// Name returns the name of the container
+func (c *Container) Name() string {
+ return c.config.Name
+}
+
+// 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
+ //}
+ //
+ //size := new(shared.ContainerSize)
+ //size.RootFsSize = data.SizeRootFs
+ //size.RwSize = data.SizeRw
+
+ bcs := shared.BatchContainerStruct{
+ ConConfig: ctr.config,
+ ConState: ctr.state.State,
+ ExitCode: ctr.state.ExitCode,
+ Pid: ctr.state.PID,
+ StartedTime: ctr.state.StartedTime,
+ ExitedTime: ctr.state.FinishedTime,
+ //Size: size,
+ }
+ return bcs, nil
+}
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
index f2bcbd473..706a8fe96 100644
--- a/pkg/adapter/pods.go
+++ b/pkg/adapter/pods.go
@@ -4,10 +4,12 @@ package adapter
import (
"context"
- "github.com/containers/libpod/pkg/adapter/shortcuts"
+ "strings"
"github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/adapter/shortcuts"
)
// Pod ...
@@ -45,6 +47,21 @@ func (r *LocalRuntime) GetLatestPod() (*Pod, error) {
return &pod, err
}
+// GetAllPods gets all pods and wraps it in an adapter pod
+func (r *LocalRuntime) GetAllPods() ([]*Pod, error) {
+ var pods []*Pod
+ allPods, err := r.Runtime.GetAllPods()
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range allPods {
+ pod := Pod{}
+ pod.Pod = p
+ pods = append(pods, &pod)
+ }
+ return pods, nil
+}
+
// LookupPod gets a pod by name or id and wraps it in an adapter pod
func (r *LocalRuntime) LookupPod(nameOrID string) (*Pod, error) {
pod := Pod{}
@@ -150,3 +167,157 @@ func (r *LocalRuntime) StartPods(ctx context.Context, cli *cliconfig.PodStartVal
}
return podids, errs
}
+
+// CreatePod is a wrapper for libpod and creating a new pod from the cli context
+func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateValues, labels map[string]string) (string, error) {
+ var (
+ options []libpod.PodCreateOption
+ err error
+ )
+
+ if cli.Flag("cgroup-parent").Changed {
+ options = append(options, libpod.WithPodCgroupParent(cli.CgroupParent))
+ }
+
+ if len(labels) != 0 {
+ options = append(options, libpod.WithPodLabels(labels))
+ }
+
+ if cli.Flag("name").Changed {
+ options = append(options, libpod.WithPodName(cli.Name))
+ }
+
+ if cli.Infra {
+ options = append(options, libpod.WithInfraContainer())
+ nsOptions, err := shared.GetNamespaceOptions(strings.Split(cli.Share, ","))
+ if err != nil {
+ return "", err
+ }
+ options = append(options, nsOptions...)
+ }
+
+ if len(cli.Publish) > 0 {
+ portBindings, err := shared.CreatePortBindings(cli.Publish)
+ if err != nil {
+ return "", err
+ }
+ options = append(options, libpod.WithInfraContainerPorts(portBindings))
+
+ }
+ // always have containers use pod cgroups
+ // User Opt out is not yet supported
+ options = append(options, libpod.WithPodCgroups())
+
+ pod, err := r.NewPod(ctx, options...)
+ if err != nil {
+ return "", err
+ }
+ return pod.ID(), nil
+}
+
+// GetPodStatus is a wrapper to get the status of a local libpod pod
+func (p *Pod) GetPodStatus() (string, error) {
+ return shared.GetPodStatus(p.Pod)
+}
+
+// BatchContainerOp is a wrapper for the shared function of the same name
+func BatchContainerOp(ctr *libpod.Container, opts shared.PsOptions) (shared.BatchContainerStruct, error) {
+ return shared.BatchContainerOp(ctr, opts)
+}
+
+// PausePods is a wrapper for pausing pods via libpod
+func (r *LocalRuntime) PausePods(c *cliconfig.PodPauseValues) ([]string, map[string]error, []error) {
+ var (
+ pauseIDs []string
+ pauseErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := shortcuts.GetPodsByContext(c.All, c.Latest, c.InputArgs, r.Runtime)
+ if err != nil {
+ pauseErrors = append(pauseErrors, err)
+ return nil, containerErrors, pauseErrors
+ }
+
+ for _, pod := range pods {
+ ctrErrs, err := pod.Pause()
+ if err != nil {
+ pauseErrors = append(pauseErrors, err)
+ continue
+ }
+ if ctrErrs != nil {
+ for ctr, err := range ctrErrs {
+ containerErrors[ctr] = err
+ }
+ continue
+ }
+ pauseIDs = append(pauseIDs, pod.ID())
+
+ }
+ return pauseIDs, containerErrors, pauseErrors
+}
+
+// UnpausePods is a wrapper for unpausing pods via libpod
+func (r *LocalRuntime) UnpausePods(c *cliconfig.PodUnpauseValues) ([]string, map[string]error, []error) {
+ var (
+ unpauseIDs []string
+ unpauseErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := shortcuts.GetPodsByContext(c.All, c.Latest, c.InputArgs, r.Runtime)
+ if err != nil {
+ unpauseErrors = append(unpauseErrors, err)
+ return nil, containerErrors, unpauseErrors
+ }
+
+ for _, pod := range pods {
+ ctrErrs, err := pod.Unpause()
+ if err != nil {
+ unpauseErrors = append(unpauseErrors, err)
+ continue
+ }
+ if ctrErrs != nil {
+ for ctr, err := range ctrErrs {
+ containerErrors[ctr] = err
+ }
+ continue
+ }
+ unpauseIDs = append(unpauseIDs, pod.ID())
+
+ }
+ return unpauseIDs, containerErrors, unpauseErrors
+}
+
+// RestartPods is a wrapper to restart pods via libpod
+func (r *LocalRuntime) RestartPods(ctx context.Context, c *cliconfig.PodRestartValues) ([]string, map[string]error, []error) {
+ var (
+ restartIDs []string
+ restartErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := shortcuts.GetPodsByContext(c.All, c.Latest, c.InputArgs, r.Runtime)
+ if err != nil {
+ restartErrors = append(restartErrors, err)
+ return nil, containerErrors, restartErrors
+ }
+
+ for _, pod := range pods {
+ ctrErrs, err := pod.Restart(ctx)
+ if err != nil {
+ restartErrors = append(restartErrors, err)
+ continue
+ }
+ if ctrErrs != nil {
+ for ctr, err := range ctrErrs {
+ containerErrors[ctr] = err
+ }
+ continue
+ }
+ restartIDs = append(restartIDs, pod.ID())
+
+ }
+ return restartIDs, containerErrors, restartErrors
+
+}
diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go
index 04484780a..220f7163f 100644
--- a/pkg/adapter/pods_remote.go
+++ b/pkg/adapter/pods_remote.go
@@ -5,8 +5,11 @@ package adapter
import (
"context"
"encoding/json"
+ "strings"
+ "time"
"github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/pkg/errors"
@@ -168,3 +171,231 @@ func (r *LocalRuntime) StartPods(ctx context.Context, cli *cliconfig.PodStartVal
}
return startPods, startErrs
}
+
+// CreatePod creates a pod for the remote client over a varlink connection
+func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateValues, labels map[string]string) (string, error) {
+ pc := iopodman.PodCreate{
+ Name: cli.Name,
+ CgroupParent: cli.CgroupParent,
+ Labels: labels,
+ Share: strings.Split(cli.Share, ","),
+ Infra: cli.Infra,
+ InfraCommand: cli.InfraCommand,
+ InfraImage: cli.InfraCommand,
+ Publish: cli.Publish,
+ }
+
+ return iopodman.CreatePod().Call(r.Conn, pc)
+}
+
+// GetAllPods is a helper function that gets all pods for the remote client
+func (r *LocalRuntime) GetAllPods() ([]*Pod, error) {
+ var pods []*Pod
+ podIDs, err := iopodman.GetPodsByContext().Call(r.Conn, true, false, []string{})
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range podIDs {
+ pod, err := r.LookupPod(p)
+ if err != nil {
+ return nil, err
+ }
+ pods = append(pods, pod)
+ }
+ return pods, nil
+}
+
+// ID returns the id of a remote pod
+func (p *Pod) ID() string {
+ return p.config.ID
+}
+
+// Name returns the name of the remote pod
+func (p *Pod) Name() string {
+ return p.config.Name
+}
+
+// AllContainersByID returns a slice of a pod's container IDs
+func (p *Pod) AllContainersByID() ([]string, error) {
+ var containerIDs []string
+ for _, ctr := range p.containers {
+ containerIDs = append(containerIDs, ctr.ID)
+ }
+ return containerIDs, nil
+}
+
+// AllContainers returns a pods containers
+func (p *Pod) AllContainers() ([]*Container, error) {
+ var containers []*Container
+ for _, ctr := range p.containers {
+ container, err := p.Runtime.LookupContainer(ctr.ID)
+ if err != nil {
+ return nil, err
+ }
+ containers = append(containers, container)
+ }
+ return containers, nil
+}
+
+// Status ...
+func (p *Pod) Status() (map[string]libpod.ContainerStatus, error) {
+ ctrs := make(map[string]libpod.ContainerStatus)
+ for _, i := range p.containers {
+ var status libpod.ContainerStatus
+ switch i.State {
+ case "exited":
+ status = libpod.ContainerStateExited
+ case "stopped":
+ status = libpod.ContainerStateStopped
+ case "running":
+ status = libpod.ContainerStateRunning
+ case "paused":
+ status = libpod.ContainerStatePaused
+ case "created":
+ status = libpod.ContainerStateCreated
+ case "configured":
+ status = libpod.ContainerStateConfigured
+ default:
+ status = libpod.ContainerStateUnknown
+ }
+ ctrs[i.ID] = status
+ }
+ return ctrs, nil
+}
+
+// GetPodStatus is a wrapper to get the string version of the status
+func (p *Pod) GetPodStatus() (string, error) {
+ ctrStatuses, err := p.Status()
+ if err != nil {
+ return "", err
+ }
+ return shared.CreatePodStatusResults(ctrStatuses)
+}
+
+// InfraContainerID returns the ID of the infra container in a pod
+func (p *Pod) InfraContainerID() (string, error) {
+ return p.state.InfraContainerID, nil
+}
+
+// CreatedTime returns the time the container was created as a time.Time
+func (p *Pod) CreatedTime() time.Time {
+ return p.config.CreatedTime
+}
+
+// SharesPID ....
+func (p *Pod) SharesPID() bool {
+ return p.config.UsePodPID
+}
+
+// SharesIPC returns whether containers in pod
+// default to use IPC namespace of first container in pod
+func (p *Pod) SharesIPC() bool {
+ return p.config.UsePodIPC
+}
+
+// SharesNet returns whether containers in pod
+// default to use network namespace of first container in pod
+func (p *Pod) SharesNet() bool {
+ return p.config.UsePodNet
+}
+
+// SharesMount returns whether containers in pod
+// default to use PID namespace of first container in pod
+func (p *Pod) SharesMount() bool {
+ return p.config.UsePodMount
+}
+
+// SharesUser returns whether containers in pod
+// default to use user namespace of first container in pod
+func (p *Pod) SharesUser() bool {
+ return p.config.UsePodUser
+}
+
+// SharesUTS returns whether containers in pod
+// default to use UTS namespace of first container in pod
+func (p *Pod) SharesUTS() bool {
+ return p.config.UsePodUTS
+}
+
+// SharesCgroup returns whether containers in the pod will default to this pod's
+// cgroup instead of the default libpod parent
+func (p *Pod) SharesCgroup() bool {
+ return p.config.UsePodCgroup
+}
+
+// CgroupParent returns the pod's CGroup parent
+func (p *Pod) CgroupParent() string {
+ return p.config.CgroupParent
+}
+
+// PausePods pauses a pod using varlink and the remote client
+func (r *LocalRuntime) PausePods(c *cliconfig.PodPauseValues) ([]string, map[string]error, []error) {
+ var (
+ pauseIDs []string
+ pauseErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := iopodman.GetPodsByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs)
+ if err != nil {
+ pauseErrors = append(pauseErrors, err)
+ return nil, containerErrors, pauseErrors
+ }
+ for _, pod := range pods {
+ reply, err := iopodman.PausePod().Call(r.Conn, pod)
+ if err != nil {
+ pauseErrors = append(pauseErrors, err)
+ continue
+ }
+ pauseIDs = append(pauseIDs, reply)
+ }
+ return pauseIDs, nil, pauseErrors
+}
+
+// UnpausePods unpauses a pod using varlink and the remote client
+func (r *LocalRuntime) UnpausePods(c *cliconfig.PodUnpauseValues) ([]string, map[string]error, []error) {
+ var (
+ unpauseIDs []string
+ unpauseErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := iopodman.GetPodsByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs)
+ if err != nil {
+ unpauseErrors = append(unpauseErrors, err)
+ return nil, containerErrors, unpauseErrors
+ }
+ for _, pod := range pods {
+ reply, err := iopodman.UnpausePod().Call(r.Conn, pod)
+ if err != nil {
+ unpauseErrors = append(unpauseErrors, err)
+ continue
+ }
+ unpauseIDs = append(unpauseIDs, reply)
+ }
+ return unpauseIDs, nil, unpauseErrors
+}
+
+// RestartPods restarts pods using varlink and the remote client
+func (r *LocalRuntime) RestartPods(ctx context.Context, c *cliconfig.PodRestartValues) ([]string, map[string]error, []error) {
+ var (
+ restartIDs []string
+ restartErrors []error
+ )
+ containerErrors := make(map[string]error)
+
+ pods, err := iopodman.GetPodsByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs)
+ if err != nil {
+ restartErrors = append(restartErrors, err)
+ return nil, containerErrors, restartErrors
+ }
+ for _, pod := range pods {
+ reply, err := iopodman.RestartPod().Call(r.Conn, pod)
+ if err != nil {
+ restartErrors = append(restartErrors, err)
+ continue
+ }
+ restartIDs = append(restartIDs, reply)
+ }
+ return restartIDs, nil, restartErrors
+}
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 8da44a2f0..50e07ee74 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -446,7 +446,15 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime, pod *l
}
if IsNS(string(c.NetMode)) {
- // pass
+ split := strings.SplitN(string(c.NetMode), ":", 2)
+ if len(split[0]) != 2 {
+ return nil, errors.Errorf("invalid user defined network namespace %q", c.NetMode.UserDefined())
+ }
+ _, err := os.Stat(split[1])
+ if err != nil {
+ return nil, err
+ }
+ options = append(options, libpod.WithNetNS(portBindings, false, string(c.NetMode), networks))
} else if c.NetMode.IsContainer() {
connectedCtr, err := c.Runtime.LookupContainer(c.NetMode.Container())
if err != nil {
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 76b8963ff..28a636fa6 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -3,10 +3,12 @@ package createconfig
import (
"os"
"path"
+ "path/filepath"
"strings"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage/pkg/mount"
+ pmount "github.com/containers/storage/pkg/mount"
"github.com/docker/docker/daemon/caps"
"github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/user"
@@ -392,9 +394,65 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
configSpec.Linux.Resources = &spec.LinuxResources{}
}
+ // Make sure that the bind mounts keep options like nosuid, noexec, nodev.
+ mounts, err := pmount.GetMounts()
+ if err != nil {
+ return nil, err
+ }
+ for i := range configSpec.Mounts {
+ m := &configSpec.Mounts[i]
+ isBind := false
+ for _, o := range m.Options {
+ if o == "bind" || o == "rbind" {
+ isBind = true
+ break
+ }
+ }
+ if !isBind {
+ continue
+ }
+ mount, err := findMount(m.Source, mounts)
+ if err != nil {
+ return nil, err
+ }
+ if mount == nil {
+ continue
+ }
+ next_option:
+ for _, o := range strings.Split(mount.Opts, ",") {
+ if o == "nosuid" || o == "noexec" || o == "nodev" {
+ for _, e := range m.Options {
+ if e == o {
+ continue next_option
+ }
+ }
+ m.Options = append(m.Options, o)
+ }
+ }
+ }
+
return configSpec, nil
}
+func findMount(target string, mounts []*pmount.Info) (*pmount.Info, error) {
+ var err error
+ target, err = filepath.Abs(target)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot resolve %s", target)
+ }
+ var bestSoFar *pmount.Info
+ for _, i := range mounts {
+ if bestSoFar != nil && len(bestSoFar.Mountpoint) > len(i.Mountpoint) {
+ // Won't be better than what we have already found
+ continue
+ }
+ if strings.HasPrefix(target, i.Mountpoint) {
+ bestSoFar = i
+ }
+ }
+ return bestSoFar, nil
+}
+
func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator) {
if config.PidMode.IsHost() && rootless.IsRootless() {
return
diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go
index ceeed6681..4ca4c4270 100644
--- a/pkg/varlinkapi/pods.go
+++ b/pkg/varlinkapi/pods.go
@@ -14,10 +14,6 @@ import (
// CreatePod ...
func (i *LibpodAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCreate) error {
var options []libpod.PodCreateOption
-
- if create.InfraCommand != "" || create.InfraImage != "" {
- return call.ReplyErrorOccurred("the infra-command and infra-image options are not supported yet")
- }
if create.CgroupParent != "" {
options = append(options, libpod.WithPodCgroupParent(create.CgroupParent))
}
diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go
index bc1431bce..71dacfa80 100644
--- a/test/e2e/rm_test.go
+++ b/test/e2e/rm_test.go
@@ -128,4 +128,9 @@ var _ = Describe("Podman rm", func() {
Expect(podmanTest.NumberOfContainers()).To(Equal(1))
})
+ It("podman rm bogus container", func() {
+ session := podmanTest.Podman([]string{"rm", "bogus"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(1))
+ })
})
diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go
index c160e1bc5..dcbda2df4 100644
--- a/test/e2e/rmi_test.go
+++ b/test/e2e/rmi_test.go
@@ -36,7 +36,7 @@ var _ = Describe("Podman rmi", func() {
It("podman rmi bogus image", func() {
session := podmanTest.Podman([]string{"rmi", "debian:6.0.10"})
session.WaitWithDefaultTimeout()
- Expect(session.ExitCode()).To(Equal(125))
+ Expect(session.ExitCode()).To(Equal(1))
})
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 1c09a4d0b..a07e4d047 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -36,19 +36,19 @@ var _ = Describe("Podman run networking", func() {
})
It("podman run network connection with default bridge", func() {
- session := podmanTest.Podman([]string{"run", "-dt", ALPINE, "wget", "www.projectatomic.io"})
+ session := podmanTest.Podman([]string{"run", "-dt", ALPINE, "wget", "www.podman.io"})
session.Wait(90)
Expect(session.ExitCode()).To(Equal(0))
})
It("podman run network connection with host", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.projectatomic.io"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.podman.io"})
session.Wait(90)
Expect(session.ExitCode()).To(Equal(0))
})
It("podman run network connection with loopback", func() {
- session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.projectatomic.io"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.podman.io"})
session.Wait(90)
Expect(session.ExitCode()).To(Equal(0))
})
@@ -178,4 +178,37 @@ var _ = Describe("Podman run networking", func() {
Expect(exec4.ExitCode()).To(Equal(0))
Expect(exec4.OutputToString()).To(ContainSubstring("192.0.2.2 test1"))
})
+
+ It("podman run network in user created network namespace", func() {
+ if Containerized() {
+ Skip("Can not be run within a container.")
+ }
+ SystemExec("ip", []string{"netns", "add", "xxx"})
+ session := podmanTest.Podman([]string{"run", "-dt", "--net", "ns:/run/netns/xxx", ALPINE, "wget", "www.podman.io"})
+ session.Wait(90)
+ Expect(session.ExitCode()).To(Equal(0))
+ SystemExec("ip", []string{"netns", "delete", "xxx"})
+ })
+
+ It("podman run n user created network namespace with resolv.conf", func() {
+ if Containerized() {
+ Skip("Can not be run within a container.")
+ }
+ SystemExec("ip", []string{"netns", "add", "xxx"})
+ SystemExec("mkdir", []string{"-p", "/etc/netns/xxx"})
+ SystemExec("bash", []string{"-c", "echo nameserver 11.11.11.11 > /etc/netns/xxx/resolv.conf"})
+ session := podmanTest.Podman([]string{"run", "--net", "ns:/run/netns/xxx", ALPINE, "cat", "/etc/resolv.conf"})
+ session.Wait(90)
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(ContainSubstring("11.11.11.11"))
+ SystemExec("ip", []string{"netns", "delete", "xxx"})
+ SystemExec("rm", []string{"-rf", "/etc/netns/xxx"})
+ })
+
+ It("podman run network in bogus user created network namespace", func() {
+ session := podmanTest.Podman([]string{"run", "-dt", "--net", "ns:/run/netns/xxy", ALPINE, "wget", "www.podman.io"})
+ session.Wait(90)
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ Expect(session.ErrorToString()).To(ContainSubstring("stat /run/netns/xxy: no such file or directory"))
+ })
})