From 2011782d9d52958546e481f84892d00a548b9e12 Mon Sep 17 00:00:00 2001 From: baude Date: Mon, 29 Oct 2018 12:06:48 -0500 Subject: Make restart parallel and add --all When attempting to restart many containers, we can benefit from making the restarts parallel. For convenience, two new options are added: --all attempts to restart all containers --run-only when used with --all will attempt to restart only running containers Signed-off-by: baude --- docs/podman-restart.1.md | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/podman-restart.1.md b/docs/podman-restart.1.md index caacaf31d..875afa385 100644 --- a/docs/podman-restart.1.md +++ b/docs/podman-restart.1.md @@ -12,33 +12,51 @@ Containers will be stopped if they are running and then restarted. Stopped containers will not be stopped and will only be started. ## OPTIONS -**--timeout** - -Timeout to wait before forcibly stopping the container +**--all, -a** +Restart all containers regardless of their current state. **--latest, -l** - Instead of providing the container name or ID, use the last created container. If you use methods other than Podman to run containers such as CRI-O, the last started container could be from either of those methods. +**--running** +Restart all containers that are already in the *running* state. + +**--timeout** +Timeout to wait before forcibly stopping the container. + + ## EXAMPLES ## +Restart the latest container ``` $ podman restart -l ec588fc80b05e19d3006bf2e8aa325f0a2e2ff1f609b7afb39176ca8e3e13467 ``` +Restart a specific container by partial container ID ``` $ podman restart ff6cf1 ff6cf1e5e77e6dba1efc7f3fcdb20e8b89ad8947bc0518be1fcb2c78681f226f ``` +Restart two containers by name with a timeout of 4 seconds ``` $ podman restart --timeout 4 test1 test2 c3bb026838c30e5097f079fa365c9a4769d52e1017588278fa00d5c68ebc1502 17e13a63081a995136f907024bcfe50ff532917988a152da229db9d894c5a9ec ``` +Restart all running containers +``` +$ podman restart --running +``` + +Restart all containers +``` +$ podman restart --all +``` + ## SEE ALSO podman(1), podman-run(1), podman-start(1), podman-create(1) -- cgit v1.2.3-54-g00ecf From b559c19c2fa739cf1c8ede50eab8f5acf74f6bf3 Mon Sep 17 00:00:00 2001 From: baude Date: Mon, 29 Oct 2018 13:53:39 -0500 Subject: Make kill, pause, and unpause parallel. Operations like kill, pause, and unpause -- which can operation on one or more containers -- can greatly benefit from parallizing its main job (eq kill). In the case of pauseand unpause, an --all option as was added. pause --all will pause all **running** containers. And unpause --all will unpause all **paused** containers. Signed-off-by: baude --- cmd/podman/kill.go | 51 +++++++++++++++++++++++------- cmd/podman/pause.go | 72 +++++++++++++++++++++++++++++++++--------- cmd/podman/shared/parallel.go | 15 +++++++++ cmd/podman/stop.go | 4 ++- cmd/podman/unpause.go | 73 ++++++++++++++++++++++++++++++++++--------- completions/bash/podman | 9 ++++++ docs/podman-kill.1.md | 2 +- docs/podman-pause.1.md | 19 ++++++++++- docs/podman-unpause.1.md | 20 +++++++++++- test/e2e/pause_test.go | 62 ++++++++++++++++++++++++++++++++++++ 10 files changed, 282 insertions(+), 45 deletions(-) (limited to 'docs') diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go index 7ca5bd7c5..27882aeee 100644 --- a/cmd/podman/kill.go +++ b/cmd/podman/kill.go @@ -1,15 +1,16 @@ package main import ( - "os" + "fmt" "syscall" - "fmt" "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/rootless" "github.com/docker/docker/pkg/signal" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/urfave/cli" ) @@ -41,6 +42,12 @@ var ( // killCmd kills one or more containers with a signal func killCmd(c *cli.Context) error { + var ( + lastError error + killFuncs []shared.ParallelWorkerInput + killSignal uint = uint(syscall.SIGTERM) + ) + if err := checkAllAndLatest(c); err != nil { return err } @@ -56,7 +63,6 @@ func killCmd(c *cli.Context) error { } defer runtime.Shutdown(false) - var killSignal uint = uint(syscall.SIGTERM) if c.String("signal") != "" { // Check if the signalString provided by the user is valid // Invalid signals will return err @@ -67,17 +73,40 @@ func killCmd(c *cli.Context) error { killSignal = uint(sysSignal) } - containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") - + containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") + if err != nil { + return err + } for _, ctr := range containers { - if err := ctr.Kill(killSignal); err != nil { - if lastError != nil { - fmt.Fprintln(os.Stderr, lastError) + con := ctr + f := func() error { + return con.Kill(killSignal) + } + + killFuncs = append(killFuncs, shared.ParallelWorkerInput{ + ContainerID: con.ID(), + ParallelFunc: f, + }) + } + + maxWorkers := shared.Parallelize("kill") + if c.GlobalIsSet("max-workers") { + maxWorkers = c.GlobalInt("max-workers") + } + logrus.Debugf("Setting maximum workers to %d", maxWorkers) + + killErrors := shared.ParallelExecuteWorkerPool(maxWorkers, killFuncs) + + for cid, result := range killErrors { + if result != nil { + if len(killErrors) > 1 { + fmt.Println(result.Error()) } - lastError = errors.Wrapf(err, "unable to find container %v", ctr.ID()) - } else { - fmt.Println(ctr.ID()) + lastError = result + continue } + fmt.Println(cid) } + return lastError } diff --git a/cmd/podman/pause.go b/cmd/podman/pause.go index 203fa6070..1e1585216 100644 --- a/cmd/podman/pause.go +++ b/cmd/podman/pause.go @@ -5,11 +5,20 @@ import ( "os" "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/libpod" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/urfave/cli" ) var ( + pauseFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "all, a", + Usage: "pause all running containers", + }, + } pauseDescription = ` podman pause @@ -19,6 +28,7 @@ var ( Name: "pause", Usage: "Pauses all the processes in one or more containers", Description: pauseDescription, + Flags: pauseFlags, Action: pauseCmd, ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", OnUsageError: usageErrorHandler, @@ -26,6 +36,11 @@ var ( ) func pauseCmd(c *cli.Context) error { + var ( + lastError error + pauseContainers []*libpod.Container + pauseFuncs []shared.ParallelWorkerInput + ) if os.Geteuid() != 0 { return errors.New("pause is not supported for rootless containers") } @@ -37,28 +52,55 @@ func pauseCmd(c *cli.Context) error { defer runtime.Shutdown(false) args := c.Args() - if len(args) < 1 { + if len(args) < 1 && !c.Bool("all") { return errors.Errorf("you must provide at least one container name or id") } - - var lastError error - for _, arg := range args { - ctr, err := runtime.LookupContainer(arg) + if c.Bool("all") { + containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") if err != nil { - if lastError != nil { - fmt.Fprintln(os.Stderr, lastError) + return err + } + pauseContainers = append(pauseContainers, containers...) + } else { + for _, arg := range args { + ctr, err := runtime.LookupContainer(arg) + if err != nil { + return err } - lastError = errors.Wrapf(err, "error looking up container %q", arg) - continue + pauseContainers = append(pauseContainers, ctr) + } + } + + // Now assemble the slice of pauseFuncs + for _, ctr := range pauseContainers { + con := ctr + + f := func() error { + return con.Pause() } - if err = ctr.Pause(); err != nil { - if lastError != nil { - fmt.Fprintln(os.Stderr, lastError) + pauseFuncs = append(pauseFuncs, shared.ParallelWorkerInput{ + ContainerID: con.ID(), + ParallelFunc: f, + }) + } + + maxWorkers := shared.Parallelize("pause") + if c.GlobalIsSet("max-workers") { + maxWorkers = c.GlobalInt("max-workers") + } + logrus.Debugf("Setting maximum workers to %d", maxWorkers) + + pauseErrors := shared.ParallelExecuteWorkerPool(maxWorkers, pauseFuncs) + + for cid, result := range pauseErrors { + if result != nil { + if len(pauseErrors) > 1 { + fmt.Println(result.Error()) } - lastError = errors.Wrapf(err, "failed to pause container %v", ctr.ID()) - } else { - fmt.Println(ctr.ID()) + lastError = result + continue } + fmt.Println(cid) } return lastError } diff --git a/cmd/podman/shared/parallel.go b/cmd/podman/shared/parallel.go index dbf43a982..633781a45 100644 --- a/cmd/podman/shared/parallel.go +++ b/cmd/podman/shared/parallel.go @@ -72,6 +72,16 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map func Parallelize(job string) int { numCpus := runtime.NumCPU() switch job { + case "kill": + if numCpus <= 3 { + return numCpus * 3 + } + return numCpus * 4 + case "pause": + if numCpus <= 3 { + return numCpus * 3 + } + return numCpus * 4 case "ps": return 8 case "restart": @@ -88,6 +98,11 @@ func Parallelize(job string) int { } else { return numCpus * 3 } + case "unpause": + if numCpus <= 3 { + return numCpus * 3 + } + return numCpus * 4 } return 3 } diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index afeb49f76..cb36fd5cd 100644 --- a/cmd/podman/stop.go +++ b/cmd/podman/stop.go @@ -89,7 +89,9 @@ func stopCmd(c *cli.Context) error { for cid, result := range stopErrors { if result != nil && result != libpod.ErrCtrStopped { - fmt.Println(result.Error()) + if len(stopErrors) > 1 { + fmt.Println(result.Error()) + } lastError = result continue } diff --git a/cmd/podman/unpause.go b/cmd/podman/unpause.go index a792aaf6d..648fc9d3d 100644 --- a/cmd/podman/unpause.go +++ b/cmd/podman/unpause.go @@ -5,11 +5,20 @@ import ( "os" "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/libpod" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/urfave/cli" ) var ( + unpauseFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "all, a", + Usage: "unpause all paused containers", + }, + } unpauseDescription = ` podman unpause @@ -19,6 +28,7 @@ var ( Name: "unpause", Usage: "Unpause the processes in one or more containers", Description: unpauseDescription, + Flags: unpauseFlags, Action: unpauseCmd, ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]", OnUsageError: usageErrorHandler, @@ -26,6 +36,11 @@ var ( ) func unpauseCmd(c *cli.Context) error { + var ( + lastError error + unpauseContainers []*libpod.Container + unpauseFuncs []shared.ParallelWorkerInput + ) if os.Geteuid() != 0 { return errors.New("unpause is not supported for rootless containers") } @@ -37,28 +52,56 @@ func unpauseCmd(c *cli.Context) error { defer runtime.Shutdown(false) args := c.Args() - if len(args) < 1 { + if len(args) < 1 && !c.Bool("all") { return errors.Errorf("you must provide at least one container name or id") } - - var lastError error - for _, arg := range args { - ctr, err := runtime.LookupContainer(arg) + if c.Bool("all") { + cs, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStatePaused, "paused") if err != nil { - if lastError != nil { - fmt.Fprintln(os.Stderr, lastError) + return err + } + unpauseContainers = append(unpauseContainers, cs...) + } else { + for _, arg := range args { + ctr, err := runtime.LookupContainer(arg) + if err != nil { + return err } - lastError = errors.Wrapf(err, "error looking up container %q", arg) - continue + unpauseContainers = append(unpauseContainers, ctr) } - if err = ctr.Unpause(); err != nil { - if lastError != nil { - fmt.Fprintln(os.Stderr, lastError) + } + + // Assemble the unpause funcs + for _, ctr := range unpauseContainers { + con := ctr + f := func() error { + return con.Unpause() + } + + unpauseFuncs = append(unpauseFuncs, shared.ParallelWorkerInput{ + ContainerID: con.ID(), + ParallelFunc: f, + }) + } + + maxWorkers := shared.Parallelize("unpause") + if c.GlobalIsSet("max-workers") { + maxWorkers = c.GlobalInt("max-workers") + } + logrus.Debugf("Setting maximum workers to %d", maxWorkers) + + unpauseErrors := shared.ParallelExecuteWorkerPool(maxWorkers, unpauseFuncs) + + for cid, result := range unpauseErrors { + if result != nil && result != libpod.ErrCtrStopped { + if len(unpauseErrors) > 1 { + fmt.Println(result.Error()) } - lastError = errors.Wrapf(err, "failed to unpause container %v", ctr.ID()) - } else { - fmt.Println(ctr.ID()) + lastError = result + continue } + fmt.Println(cid) } + return lastError } diff --git a/completions/bash/podman b/completions/bash/podman index ed4e080c9..c029f893a 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1,5 +1,6 @@ : ${PROG:=$(basename ${BASH_SOURCE})} + __podman_previous_extglob_setting=$(shopt -p extglob) shopt -s extglob @@ -1934,6 +1935,10 @@ _podman_save() { } _podman_pause() { + local boolean_options=" + -a + --all + " local options_with_args=" --help -h " @@ -2035,6 +2040,10 @@ _podman_stop() { } _podman_unpause() { + local boolean_options=" + -a + --all + " local options_with_args=" --help -h " diff --git a/docs/podman-kill.1.md b/docs/podman-kill.1.md index 14066d151..85f68a73d 100644 --- a/docs/podman-kill.1.md +++ b/docs/podman-kill.1.md @@ -4,7 +4,7 @@ podman\-kill - Kills one or more containers with a signal ## SYNOPSIS -**podman kill** [*options*] *container* ... +**podman kill** [*options*] [*container* ...] ## DESCRIPTION The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal. diff --git a/docs/podman-pause.1.md b/docs/podman-pause.1.md index b4930de8d..f19fa5d6a 100644 --- a/docs/podman-pause.1.md +++ b/docs/podman-pause.1.md @@ -4,16 +4,33 @@ podman\-pause - Pause one or more containers ## SYNOPSIS -**podman pause** [*options*] *container* ... +**podman pause** [*options*] [*container*...] ## DESCRIPTION Pauses all the processes in one or more containers. You may use container IDs or names as input. +## OPTIONS + +**--all, -a** + +Pause all running containers. + ## EXAMPLE +Pause a container named 'mywebserver' +``` podman pause mywebserver +``` +Pause a container by partial container ID. +``` podman pause 860a4b23 +``` + +Pause all **running** containers. +``` +podman stop -a +``` ## SEE ALSO podman(1), podman-unpause(1) diff --git a/docs/podman-unpause.1.md b/docs/podman-unpause.1.md index 9404e7648..acfab0930 100644 --- a/docs/podman-unpause.1.md +++ b/docs/podman-unpause.1.md @@ -4,16 +4,34 @@ podman\-unpause - Unpause one or more containers ## SYNOPSIS -**podman unpause** [*options*] *container* ... +**podman unpause** [*options*] [*container*...] ## DESCRIPTION Unpauses the processes in one or more containers. You may use container IDs or names as input. +## OPTIONS + +**--all, -a** + +Unpause all paused containers. + ## EXAMPLE +Unpause a container called 'mywebserver' +``` podman unpause mywebserver +``` +Unpause a container by a partial container ID. + +``` podman unpause 860a4b23 +``` + +Unpause all **paused** containers. +``` +podman unpause -a +``` ## SEE ALSO podman(1), podman-pause(1) diff --git a/test/e2e/pause_test.go b/test/e2e/pause_test.go index c34964f59..24876b6d6 100644 --- a/test/e2e/pause_test.go +++ b/test/e2e/pause_test.go @@ -213,4 +213,66 @@ var _ = Describe("Podman pause", func() { result.WaitWithDefaultTimeout() }) + It("Pause all containers (no containers exist)", func() { + result := podmanTest.Podman([]string{"pause", "--all"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + + }) + + It("Unpause all containers (no paused containers exist)", func() { + result := podmanTest.Podman([]string{"unpause", "--all"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + }) + + It("Pause a bunch of running containers", func() { + podmanTest.RestoreArtifact(nginx) + for i := 0; i < 3; i++ { + name := fmt.Sprintf("test%d", i) + run := podmanTest.Podman([]string{"run", "-dt", "--name", name, nginx}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(Equal(0)) + + } + running := podmanTest.Podman([]string{"ps", "-q"}) + running.WaitWithDefaultTimeout() + Expect(running.ExitCode()).To(Equal(0)) + Expect(len(running.OutputToStringArray())).To(Equal(3)) + + pause := podmanTest.Podman([]string{"pause", "--all"}) + pause.WaitWithDefaultTimeout() + Expect(pause.ExitCode()).To(Equal(0)) + + running = podmanTest.Podman([]string{"ps", "-q"}) + running.WaitWithDefaultTimeout() + Expect(running.ExitCode()).To(Equal(0)) + Expect(len(running.OutputToStringArray())).To(Equal(0)) + }) + + It("Unpause a bunch of running containers", func() { + podmanTest.RestoreArtifact(nginx) + for i := 0; i < 3; i++ { + name := fmt.Sprintf("test%d", i) + run := podmanTest.Podman([]string{"run", "-dt", "--name", name, nginx}) + run.WaitWithDefaultTimeout() + Expect(run.ExitCode()).To(Equal(0)) + + } + pause := podmanTest.Podman([]string{"pause", "--all"}) + pause.WaitWithDefaultTimeout() + Expect(pause.ExitCode()).To(Equal(0)) + + unpause := podmanTest.Podman([]string{"unpause", "--all"}) + unpause.WaitWithDefaultTimeout() + Expect(unpause.ExitCode()).To(Equal(0)) + + running := podmanTest.Podman([]string{"ps", "-q"}) + running.WaitWithDefaultTimeout() + Expect(running.ExitCode()).To(Equal(0)) + Expect(len(running.OutputToStringArray())).To(Equal(3)) + }) + }) -- cgit v1.2.3-54-g00ecf From 185ec6de43ffc5cba4a51e80bfb627a5ba187020 Mon Sep 17 00:00:00 2001 From: TomSweeneyRedHat Date: Wed, 7 Nov 2018 14:23:01 -0500 Subject: Touch up --log* options and daemons in man pages Signed-off-by: TomSweeneyRedHat --- docs/podman-build.1.md | 2 +- docs/podman-create.1.md | 12 ++++++------ docs/podman-run.1.md | 13 +++++-------- 3 files changed, 12 insertions(+), 15 deletions(-) (limited to 'docs') diff --git a/docs/podman-build.1.md b/docs/podman-build.1.md index 0cbce15c0..f887d68cd 100644 --- a/docs/podman-build.1.md +++ b/docs/podman-build.1.md @@ -171,7 +171,7 @@ value can be entered. The password is entered without echo. **--disable-content-trust** This is a Docker specific option to disable image verification to a Docker -registry and is not supported by Buildah. This flag is a NOOP and provided +registry and is not supported by Podman. This flag is a NOOP and provided soley for scripting compatibility. **--file, -f** *Dockerfile* diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 5a4d7fb5a..68c00685b 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -66,7 +66,7 @@ Write the container ID to the file **--conmon-pidfile**="" -Write the pid of the `conmon` process to a file. `conmon` daemonizes separate from Podman, so this is necessary when using systemd to restart Podman containers. +Write the pid of the `conmon` process to a file. `conmon` runs in a separate process than Podman, so this is necessary when using systemd to restart Podman containers. **--cpu-count**=*0* @@ -321,13 +321,13 @@ Not implemented **--log-driver**="*json-file*" -Logging driver for the container. Default is defined by daemon `--log-driver` flag. -**Warning**: the `podman logs` command works only for the `json-file` and -`journald` logging drivers. +Logging driver for the container. Currently not supported. This flag is a NOOP provided soley for scripting compatibility. **--log-opt**=[] -Logging driver specific options. +Logging driver specific options. Used to set the path to the container log file. For example: + +`--log-opt path=/var/log/container/mycontainer.json` **--mac-address**="" @@ -414,7 +414,7 @@ UUID short identifier (“f78375b1c487”) Name (“jonah”) podman generates a UUID for each container, and if a name is not assigned -to the container with **--name** then the daemon will also generate a random +to the container with **--name** then it will generate a random string name. The name is useful any place you need to identify a container. This works for both background and foreground containers. diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index b708e3407..912026a55 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -78,7 +78,7 @@ Write the container ID to the file **--conmon-pidfile**="" -Write the pid of the `conmon` process to a file. `conmon` daemonizes separate from Podman, so this is necessary when using systemd to restart Podman containers. +Write the pid of the `conmon` process to a file. `conmon` runs in a separate process than Podman, so this is necessary when using systemd to restart Podman containers. **--cpu-period**=*0* @@ -333,16 +333,13 @@ Not implemented **--log-driver**="*json-file*" -Logging driver for the container. Default is defined by daemon `--log-driver` flag. - -**Warning**: the `podman logs` command works only for the `json-file` and -`journald` logging drivers. +Logging driver for the container. Currently not supported. This flag is a NOOP provided soley for scripting compatibility. **--log-opt**=[] -Logging driver specific options. +Logging driver specific options. Used to set the path to the container log file. For example: -`path=/var/log/container/mycontainer.json`: Set the path to the container log file. +`--log-opt path=/var/log/container/mycontainer.json` **--mac-address**="" @@ -399,7 +396,7 @@ The operator can identify a container in three ways: - Name (“jonah”) podman generates a UUID for each container, and if a name is not assigned -to the container with **--name** then the daemon will also generate a random +to the container with **--name** then it will generate a random string name. The name is useful any place you need to identify a container. This works for both background and foreground containers. -- cgit v1.2.3-54-g00ecf From c7926aa7cae3ec950fba290af662e693313854e0 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Tue, 30 Oct 2018 12:34:06 +0100 Subject: rootless: default to fuse-overlayfs when available If fuse-overlayfs is present, rootless containers default to use it. This can still be overriden either via the command line with --storage-driver or in the ~/.config/containers/storage.conf configuration file. Signed-off-by: Giuseppe Scrivano --- docs/podman.1.md | 2 +- pkg/util/utils.go | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/podman.1.md b/docs/podman.1.md index 085af97ff..c66a9cf05 100644 --- a/docs/podman.1.md +++ b/docs/podman.1.md @@ -56,7 +56,7 @@ Path to the OCI compatible binary used to run containers **--storage-driver, -s**=**value** -Storage driver. The default storage driver for UID 0 is configured in /etc/containers/storage.conf (`$HOME/.config/containers/storage.conf` in rootless mode), and is *vfs* for other users. The `STORAGE_DRIVER` environment variable overrides the default. The --storage-driver specified driver overrides all. +Storage driver. The default storage driver for UID 0 is configured in /etc/containers/storage.conf (`$HOME/.config/containers/storage.conf` in rootless mode), and is *vfs* for non-root users when *fuse-overlayfs* is not available. The `STORAGE_DRIVER` environment variable overrides the default. The --storage-driver specified driver overrides all. Overriding this option will cause the *storage-opt* settings in /etc/containers/storage.conf to be ignored. The user must specify additional options via the `--storage-opt` flag. diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 69f49e72a..3b43489b2 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -3,6 +3,7 @@ package util import ( "fmt" "os" + "os/exec" "path/filepath" "strconv" "strings" @@ -273,7 +274,12 @@ func GetRootlessStorageOpts() (storage.StoreOptions, error) { dataDir = filepath.Join(resolvedHome, ".local", "share") } opts.GraphRoot = filepath.Join(dataDir, "containers", "storage") - opts.GraphDriverName = "vfs" + if path, err := exec.LookPath("fuse-overlayfs"); err == nil { + opts.GraphDriverName = "overlay" + opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)} + } else { + opts.GraphDriverName = "vfs" + } return opts, nil } -- cgit v1.2.3-54-g00ecf From da6f15ad212b7c309fd54bb7f1a470ce70d1b576 Mon Sep 17 00:00:00 2001 From: Vincent Batts Date: Thu, 8 Nov 2018 14:21:25 -0500 Subject: podman_tutorial: cni build path has changed Signed-off-by: Vincent Batts --- docs/tutorials/podman_tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md index 152d65a59..d48df289f 100644 --- a/docs/tutorials/podman_tutorial.md +++ b/docs/tutorials/podman_tutorial.md @@ -77,7 +77,7 @@ $ sudo curl https://raw.githubusercontent.com/containers/skopeo/master/default-p ```console $ git clone https://github.com/containernetworking/plugins.git $GOPATH/src/github.com/containernetworking/plugins $ cd $GOPATH/src/github.com/containernetworking/plugins -$ ./build.sh +$ ./build_linux.sh $ sudo mkdir -p /usr/libexec/cni $ sudo cp bin/* /usr/libexec/cni ``` -- cgit v1.2.3-54-g00ecf From 2dd9cae37cb076418393ba61c0fb7b8cf97148f3 Mon Sep 17 00:00:00 2001 From: baude Date: Wed, 7 Nov 2018 13:20:43 -0600 Subject: rm -f now removes a paused container We now can remove a paused container by sending it a kill signal while it is paused. We then unpause the container and it is immediately killed. Also, reworked how the parallelWorker results are handled to provide a more consistent approach to how each subcommand implements it. It also fixes a bug where if one container errors, the error message is duplicated when printed out. Signed-off-by: baude --- cmd/podman/kill.go | 23 +++++++---------------- cmd/podman/pause.go | 17 ++--------------- cmd/podman/restart.go | 16 ++-------------- cmd/podman/rm.go | 26 +++++++++++--------------- cmd/podman/shared/parallel.go | 10 +++++++--- cmd/podman/stop.go | 23 +++++++++-------------- cmd/podman/unpause.go | 18 ++---------------- cmd/podman/utils.go | 17 +++++++++++++++++ docs/podman-rm.1.md | 19 ++++++++++++++++--- libpod/runtime_ctr.go | 14 +++++++++++++- test/e2e/pause_test.go | 19 ++++++++++--------- 11 files changed, 96 insertions(+), 106 deletions(-) (limited to 'docs') diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go index 27882aeee..cfe4b4218 100644 --- a/cmd/podman/kill.go +++ b/cmd/podman/kill.go @@ -43,7 +43,6 @@ var ( // killCmd kills one or more containers with a signal func killCmd(c *cli.Context) error { var ( - lastError error killFuncs []shared.ParallelWorkerInput killSignal uint = uint(syscall.SIGTERM) ) @@ -75,8 +74,12 @@ func killCmd(c *cli.Context) error { containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") if err != nil { - return err + if len(containers) == 0 { + return err + } + fmt.Println(err.Error()) } + for _, ctr := range containers { con := ctr f := func() error { @@ -95,18 +98,6 @@ func killCmd(c *cli.Context) error { } logrus.Debugf("Setting maximum workers to %d", maxWorkers) - killErrors := shared.ParallelExecuteWorkerPool(maxWorkers, killFuncs) - - for cid, result := range killErrors { - if result != nil { - if len(killErrors) > 1 { - fmt.Println(result.Error()) - } - lastError = result - continue - } - fmt.Println(cid) - } - - return lastError + killErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, killFuncs) + return printParallelOutput(killErrors, errCount) } diff --git a/cmd/podman/pause.go b/cmd/podman/pause.go index 1e1585216..fcb2f3cb8 100644 --- a/cmd/podman/pause.go +++ b/cmd/podman/pause.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" "github.com/containers/libpod/cmd/podman/libpodruntime" @@ -37,7 +36,6 @@ var ( func pauseCmd(c *cli.Context) error { var ( - lastError error pauseContainers []*libpod.Container pauseFuncs []shared.ParallelWorkerInput ) @@ -90,17 +88,6 @@ func pauseCmd(c *cli.Context) error { } logrus.Debugf("Setting maximum workers to %d", maxWorkers) - pauseErrors := shared.ParallelExecuteWorkerPool(maxWorkers, pauseFuncs) - - for cid, result := range pauseErrors { - if result != nil { - if len(pauseErrors) > 1 { - fmt.Println(result.Error()) - } - lastError = result - continue - } - fmt.Println(cid) - } - return lastError + pauseErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, pauseFuncs) + return printParallelOutput(pauseErrors, errCount) } diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go index 2e264db79..630493ef4 100644 --- a/cmd/podman/restart.go +++ b/cmd/podman/restart.go @@ -1,8 +1,6 @@ package main import ( - "fmt" - "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" @@ -46,7 +44,6 @@ func restartCmd(c *cli.Context) error { var ( restartFuncs []shared.ParallelWorkerInput containers []*libpod.Container - lastError error restartContainers []*libpod.Container ) @@ -124,15 +121,6 @@ func restartCmd(c *cli.Context) error { logrus.Debugf("Setting maximum workers to %d", maxWorkers) - restartErrors := shared.ParallelExecuteWorkerPool(maxWorkers, restartFuncs) - - for cid, result := range restartErrors { - if result != nil { - fmt.Println(result.Error()) - lastError = result - continue - } - fmt.Println(cid) - } - return lastError + restartErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, restartFuncs) + return printParallelOutput(restartErrors, errCount) } diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 0fb5345ee..7c0569b78 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/libpod" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -46,9 +45,7 @@ Running containers will not be removed without the -f option. // saveCmd saves the image to either docker-archive or oci func rmCmd(c *cli.Context) error { var ( - delContainers []*libpod.Container - lastError error - deleteFuncs []shared.ParallelWorkerInput + deleteFuncs []shared.ParallelWorkerInput ) ctx := getContext() @@ -65,7 +62,13 @@ func rmCmd(c *cli.Context) error { return err } - delContainers, lastError = getAllOrLatestContainers(c, runtime, -1, "all") + delContainers, err := getAllOrLatestContainers(c, runtime, -1, "all") + if err != nil { + if len(delContainers) == 0 { + return err + } + fmt.Println(err.Error()) + } for _, container := range delContainers { con := container @@ -84,14 +87,7 @@ func rmCmd(c *cli.Context) error { } logrus.Debugf("Setting maximum workers to %d", maxWorkers) - deleteErrors := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs) - for cid, result := range deleteErrors { - if result != nil { - fmt.Println(result.Error()) - lastError = result - continue - } - fmt.Println(cid) - } - return lastError + // Run the parallel funcs + deleteErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs) + return printParallelOutput(deleteErrors, errCount) } diff --git a/cmd/podman/shared/parallel.go b/cmd/podman/shared/parallel.go index 633781a45..e6ce50f95 100644 --- a/cmd/podman/shared/parallel.go +++ b/cmd/podman/shared/parallel.go @@ -30,9 +30,10 @@ func ParallelWorker(wg *sync.WaitGroup, jobs <-chan ParallelWorkerInput, results // ParallelExecuteWorkerPool takes container jobs and performs them in parallel. The worker // int determines how many workers/threads should be premade. -func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map[string]error { +func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) (map[string]error, int) { var ( - wg sync.WaitGroup + wg sync.WaitGroup + errorCount int ) resultChan := make(chan containerError, len(functions)) @@ -62,9 +63,12 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map close(resultChan) for ctrError := range resultChan { results[ctrError.ContainerID] = ctrError.Err + if ctrError.Err != nil { + errorCount += 1 + } } - return results + return results, errorCount } // Parallelize provides the maximum number of parallel workers (int) as calculated by a basic diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index cb36fd5cd..04022839a 100644 --- a/cmd/podman/stop.go +++ b/cmd/podman/stop.go @@ -59,7 +59,13 @@ func stopCmd(c *cli.Context) error { } defer runtime.Shutdown(false) - containers, lastError := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") + containers, err := getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running") + if err != nil { + if len(containers) == 0 { + return err + } + fmt.Println(err.Error()) + } var stopFuncs []shared.ParallelWorkerInput for _, ctr := range containers { @@ -85,17 +91,6 @@ func stopCmd(c *cli.Context) error { } logrus.Debugf("Setting maximum workers to %d", maxWorkers) - stopErrors := shared.ParallelExecuteWorkerPool(maxWorkers, stopFuncs) - - for cid, result := range stopErrors { - if result != nil && result != libpod.ErrCtrStopped { - if len(stopErrors) > 1 { - fmt.Println(result.Error()) - } - lastError = result - continue - } - fmt.Println(cid) - } - return lastError + stopErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, stopFuncs) + return printParallelOutput(stopErrors, errCount) } diff --git a/cmd/podman/unpause.go b/cmd/podman/unpause.go index 648fc9d3d..d77e056f8 100644 --- a/cmd/podman/unpause.go +++ b/cmd/podman/unpause.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" "github.com/containers/libpod/cmd/podman/libpodruntime" @@ -37,7 +36,6 @@ var ( func unpauseCmd(c *cli.Context) error { var ( - lastError error unpauseContainers []*libpod.Container unpauseFuncs []shared.ParallelWorkerInput ) @@ -90,18 +88,6 @@ func unpauseCmd(c *cli.Context) error { } logrus.Debugf("Setting maximum workers to %d", maxWorkers) - unpauseErrors := shared.ParallelExecuteWorkerPool(maxWorkers, unpauseFuncs) - - for cid, result := range unpauseErrors { - if result != nil && result != libpod.ErrCtrStopped { - if len(unpauseErrors) > 1 { - fmt.Println(result.Error()) - } - lastError = result - continue - } - fmt.Println(cid) - } - - return lastError + unpauseErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, unpauseFuncs) + return printParallelOutput(unpauseErrors, errCount) } diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go index afeccb668..5735156c2 100644 --- a/cmd/podman/utils.go +++ b/cmd/podman/utils.go @@ -207,3 +207,20 @@ func getPodsFromContext(c *cli.Context, r *libpod.Runtime) ([]*libpod.Pod, error } return pods, lastError } + +//printParallelOutput takes the map of parallel worker results and outputs them +// to stdout +func printParallelOutput(m map[string]error, errCount int) error { + var lastError error + for cid, result := range m { + if result != nil { + if errCount > 1 { + fmt.Println(result.Error()) + } + lastError = result + continue + } + fmt.Println(cid) + } + return lastError +} diff --git a/docs/podman-rm.1.md b/docs/podman-rm.1.md index 7474a0d1f..56664a8c1 100644 --- a/docs/podman-rm.1.md +++ b/docs/podman-rm.1.md @@ -13,7 +13,7 @@ podman\-rm - Remove one or more containers **--force, f** -Force the removal of a running container +Force the removal of a running and paused containers **--all, a** @@ -29,16 +29,29 @@ to run containers such as CRI-O, the last started container could be from either Remove the volumes associated with the container. (Not yet implemented) ## EXAMPLE - +Remove a container by its name *mywebserver* +``` podman rm mywebserver - +``` +Remove several containers by name and container id. +``` podman rm mywebserver myflaskserver 860a4b23 +``` +Forcibly remove a container by container ID. +``` podman rm -f 860a4b23 +``` +Remove all containers regardless of its run state. +``` podman rm -f -a +``` +Forcibly remove the latest container created. +``` podman rm -f --latest +``` ## SEE ALSO podman(1), podman-rmi(1) diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 09dc7c48b..09d0ec042 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -246,7 +246,19 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool) } if c.state.State == ContainerStatePaused { - return errors.Wrapf(ErrCtrStateInvalid, "container %s is paused, cannot remove until unpaused", c.ID()) + if !force { + return errors.Wrapf(ErrCtrStateInvalid, "container %s is paused, cannot remove until unpaused", c.ID()) + } + if err := c.runtime.ociRuntime.killContainer(c, 9); err != nil { + return err + } + if err := c.unpause(); err != nil { + return err + } + // Need to update container state to make sure we know it's stopped + if err := c.waitForExitFileAndSync(); err != nil { + return err + } } // Check that the container's in a good state to be removed diff --git a/test/e2e/pause_test.go b/test/e2e/pause_test.go index 1a2eb1a09..e80915670 100644 --- a/test/e2e/pause_test.go +++ b/test/e2e/pause_test.go @@ -91,7 +91,7 @@ var _ = Describe("Podman pause", func() { }) - It("podman remove a paused container by id", func() { + It("podman remove a paused container by id without force", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -111,25 +111,26 @@ var _ = Describe("Podman pause", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring(pausedState)) - result = podmanTest.Podman([]string{"rm", "--force", cid}) - result.WaitWithDefaultTimeout() + }) - Expect(result.ExitCode()).To(Equal(125)) - Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) - Expect(podmanTest.GetContainerStatus()).To(ContainSubstring(pausedState)) + It("podman remove a paused container by id with force", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() - result = podmanTest.Podman([]string{"unpause", cid}) + result := podmanTest.Podman([]string{"pause", cid}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) - Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring(pausedState)) result = podmanTest.Podman([]string{"rm", "--force", cid}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) - }) It("podman stop a paused container by id", func() { -- cgit v1.2.3-54-g00ecf From 542d8fe95f66c0a3aabb9d964c260a0b3b9aaae8 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Fri, 9 Nov 2018 11:22:44 -0500 Subject: Better document rootless containers Need to return an error pointing user in right direction if rootless podman fails, because of no /etc/subuid or /etc/subgid files. Also fix up man pages to better describe rootless podman. Signed-off-by: Daniel J Walsh --- docs/podman.1.md | 9 +++++---- pkg/rootless/rootless_linux.go | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/podman.1.md b/docs/podman.1.md index c66a9cf05..b7433d850 100644 --- a/docs/podman.1.md +++ b/docs/podman.1.md @@ -192,7 +192,7 @@ the exit codes follow the `chroot` standard, see below: When Podman runs in rootless mode, the file `$HOME/.config/containers/storage.conf` is also loaded. ## Rootless mode -Podman can also be used as non-root user. When podman runs in rootless mode, an user namespace is automatically created. +Podman can also be used as non-root user. When podman runs in rootless mode, a user namespace is automatically created for the user, defined in /etc/subuid and /etc/subgid. Containers created by a non-root user are not visible to other users and are not seen or managed by podman running as root. @@ -209,13 +209,14 @@ Or just add the content manually. $ echo USERNAME:10000:65536 >> /etc/subuid $ echo USERNAME:10000:65536 >> /etc/subgid +See the `subuid(5)` and `subgid(5)` man pages for more information. + Images are pulled under `XDG_DATA_HOME` when specified, otherwise in the home directory of the user under `.local/share/containers/storage`. -Currently it is not possible to create a network device, so rootless containers need to run in the host network namespace. If a rootless container creates a network namespace, -then only the loopback device will be available. +Currently the slirp4netns package is required to be installed to create a network device, otherwise rootless containers need to run in the network namespace of the host. ## SEE ALSO -`containers-mounts.conf(5)`, `containers-registries.conf(5)`, `containers-storage.conf(5)`, `crio(8)`, `libpod.conf(5)`, `oci-hooks(5)`, `policy.json(5)` +`containers-mounts.conf(5)`, `containers-registries.conf(5)`, `containers-storage.conf(5)`, `crio(8)`, `libpod.conf(5)`, `oci-hooks(5)`, `policy.json(5)`, `subuid(5)`, `subgid(5)`, `slirp4netns(1)` ## HISTORY Dec 2016, Originally compiled by Dan Walsh diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 5c45f2694..ff8c8fe34 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -187,6 +187,9 @@ func BecomeRootInUserNS() (bool, int, error) { if username == "" { user, err := user.LookupId(fmt.Sprintf("%d", os.Getuid())) if err != nil && os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") == "" { + if os.IsNotExist(err) { + return false, 0, errors.Wrapf(err, "/etc/subuid or /etc/subgid does not exist, see subuid/subgid man pages for information on these files") + } return false, 0, errors.Wrapf(err, "could not find user by UID nor USER env was set") } if err == nil { -- cgit v1.2.3-54-g00ecf From c8c174aaca6455a31eb84e57bf822312e5634783 Mon Sep 17 00:00:00 2001 From: Xavier Krantz Date: Sun, 11 Nov 2018 07:44:18 +0100 Subject: docs: Fix duplicated entry for pod-container-unmount Signed-off-by: Xavier Krantz --- docs/podman-container.1.md | 1 - 1 file changed, 1 deletion(-) (limited to 'docs') diff --git a/docs/podman-container.1.md b/docs/podman-container.1.md index eac3343d5..67d42bfef 100644 --- a/docs/podman-container.1.md +++ b/docs/podman-container.1.md @@ -38,7 +38,6 @@ The container command allows you to manage containers | stop | [podman-stop(1)](podman-stop.1.md) | Stop one or more running containers. | | top | [podman-top(1)](podman-top.1.md) | Display the running processes of a container. | | umount | [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. | -| unmount | [podman-umount(1)](podman-umount.1.md) | Unmount a working container's root filesystem. | | unpause | [podman-unpause(1)](podman-unpause.1.md) | Unpause one or more containers. | | wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. | -- cgit v1.2.3-54-g00ecf From eb91f82bb2064606a2b66d6b1f1a06bb0c91aabc Mon Sep 17 00:00:00 2001 From: Jonathan Dowland Date: Fri, 9 Nov 2018 14:01:41 +0000 Subject: remove $-prefix from (most) shell examples For the shell examples related to building and installing podman, remove the shell prompt indicator character '$'. This makes copying and pasting lines much easier. Retain the prompt indicator for the post-install shell examples, since they (often) mix shell commands and shell command output, so it serves a useful purpose to distinguish them. Signed-off-by: Jonathan Dowland --- docs/tutorials/podman_tutorial.md | 99 ++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 48 deletions(-) (limited to 'docs') diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md index d48df289f..5a8f997b8 100644 --- a/docs/tutorials/podman_tutorial.md +++ b/docs/tutorials/podman_tutorial.md @@ -5,10 +5,13 @@ Podman is a utility provided as part of the libpod library. It can be used to c containers. The following tutorial will teach you how to set up Podman and perform some basic commands with Podman. +**NOTE**: the code samples are intended to be run as a non-root user, and use `sudo` where +root escalation is required. + ## Install Podman on Fedora from RPM Repositories Fedora 27 and later provide Podman via the package manager. ```console -$ sudo dnf install -y podman +sudo dnf install -y podman ``` ## Install Podman on Fedora from Source @@ -18,10 +21,10 @@ acquire the source, and build it. ### Installing build and runtime dependencies ```console -$ sudo dnf install -y git runc libassuan-devel golang golang-github-cpuguy83-go-md2man glibc-static \ - gpgme-devel glib2-devel device-mapper-devel libseccomp-devel \ - atomic-registries iptables skopeo-containers containernetworking-cni \ - conmon +sudo dnf install -y git runc libassuan-devel golang golang-github-cpuguy83-go-md2man glibc-static \ + gpgme-devel glib2-devel device-mapper-devel libseccomp-devel \ + atomic-registries iptables skopeo-containers containernetworking-cni \ + conmon ``` ### Building and installing podman @@ -29,12 +32,12 @@ First, configure a `GOPATH` (if you are using go1.8 or later, this defaults to ` and make libpod. ```console -$ export GOPATH=~/go -$ mkdir -p $GOPATH -$ git clone https://github.com/containers/libpod/ $GOPATH/src/github.com/containers/libpod -$ cd $GOPATH/src/github.com/containers/libpod -$ make -$ sudo make install PREFIX=/usr +export GOPATH=~/go +mkdir -p $GOPATH +git clone https://github.com/containers/libpod/ $GOPATH/src/github.com/containers/libpod +cd $GOPATH/src/github.com/containers/libpod +make +sudo make install PREFIX=/usr ``` You now have a working podman environment. Jump to [Familiarizing yourself with Podman](#familiarizing-yourself-with-podman) @@ -50,8 +53,8 @@ tutorial. For this tutorial, the Ubuntu **artful-server-cloudimg** image was use #### Installing base packages ```console -$ sudo apt-get update -$ sudo apt-get install libdevmapper-dev libglib2.0-dev libgpgme11-dev golang libseccomp-dev \ +sudo apt-get update +sudo apt-get install libdevmapper-dev libglib2.0-dev libgpgme11-dev golang libseccomp-dev \ go-md2man libprotobuf-dev libprotobuf-c0-dev libseccomp-dev python3-setuptools ``` #### Building and installing conmon @@ -59,42 +62,42 @@ First, configure a `GOPATH` (if you are using go1.8 or later, this defaults to ` and make libpod. ```console -$ export GOPATH=~/go -$ mkdir -p $GOPATH -$ git clone https://github.com/kubernetes-sigs/cri-o $GOPATH/src/github.com/kubernetes-sigs/cri-o -$ cd $GOPATH/src/github.com/kubernetes-sigs/cri-o -$ mkdir bin -$ make bin/conmon -$ sudo install -D -m 755 bin/conmon /usr/libexec/podman/conmon +export GOPATH=~/go +mkdir -p $GOPATH +git clone https://github.com/kubernetes-sigs/cri-o $GOPATH/src/github.com/kubernetes-sigs/cri-o +cd $GOPATH/src/github.com/kubernetes-sigs/cri-o +mkdir bin +make bin/conmon +sudo install -D -m 755 bin/conmon /usr/libexec/podman/conmon ``` #### Adding required configuration files ```console -$ sudo mkdir -p /etc/containers -$ sudo curl https://raw.githubusercontent.com/projectatomic/registries/master/registries.fedora -o /etc/containers/registries.conf -$ sudo curl https://raw.githubusercontent.com/containers/skopeo/master/default-policy.json -o /etc/containers/policy.json +sudo mkdir -p /etc/containers +sudo curl https://raw.githubusercontent.com/projectatomic/registries/master/registries.fedora -o /etc/containers/registries.conf +sudo curl https://raw.githubusercontent.com/containers/skopeo/master/default-policy.json -o /etc/containers/policy.json ``` #### Installing CNI plugins ```console -$ git clone https://github.com/containernetworking/plugins.git $GOPATH/src/github.com/containernetworking/plugins -$ cd $GOPATH/src/github.com/containernetworking/plugins -$ ./build_linux.sh -$ sudo mkdir -p /usr/libexec/cni -$ sudo cp bin/* /usr/libexec/cni +git clone https://github.com/containernetworking/plugins.git $GOPATH/src/github.com/containernetworking/plugins +cd $GOPATH/src/github.com/containernetworking/plugins +./build_linux.sh +sudo mkdir -p /usr/libexec/cni +sudo cp bin/* /usr/libexec/cni ``` #### Installing runc ```console -$ git clone https://github.com/opencontainers/runc.git $GOPATH/src/github.com/opencontainers/runc -$ cd $GOPATH/src/github.com/opencontainers/runc -$ make BUILDTAGS="seccomp" -$ sudo cp runc /usr/bin/runc +git clone https://github.com/opencontainers/runc.git $GOPATH/src/github.com/opencontainers/runc +cd $GOPATH/src/github.com/opencontainers/runc +make BUILDTAGS="seccomp" +sudo cp runc /usr/bin/runc ``` ### Building and installing Podman ```console -$ git clone https://github.com/containers/libpod/ $GOPATH/src/github.com/containers/libpod -$ cd $GOPATH/src/github.com/containers/libpod -$ make -$ sudo make install PREFIX=/usr +git clone https://github.com/containers/libpod/ $GOPATH/src/github.com/containers/libpod +cd $GOPATH/src/github.com/containers/libpod +make +sudo make install PREFIX=/usr ``` ## Familiarizing yourself with Podman @@ -103,10 +106,10 @@ $ sudo make install PREFIX=/usr This sample container will run a very basic httpd server that serves only its index page. ```console -$ sudo podman run -dt -e HTTPD_VAR_RUN=/var/run/httpd -e HTTPD_MAIN_CONF_D_PATH=/etc/httpd/conf.d \ - -e HTTPD_MAIN_CONF_PATH=/etc/httpd/conf \ - -e HTTPD_CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/httpd/ \ - registry.fedoraproject.org/f27/httpd /usr/bin/run-httpd +sudo podman run -dt -e HTTPD_VAR_RUN=/var/run/httpd -e HTTPD_MAIN_CONF_D_PATH=/etc/httpd/conf.d \ + -e HTTPD_MAIN_CONF_PATH=/etc/httpd/conf \ + -e HTTPD_CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/httpd/ \ + registry.fedoraproject.org/f27/httpd /usr/bin/run-httpd ``` Because the container is being run in detached mode, represented by the *-d* in the podman run command, podman will print the container ID after it has run. @@ -114,7 +117,7 @@ will print the container ID after it has run. ### Listing running containers The Podman *ps* command is used to list creating and running containers. ```console -$ sudo podman ps +sudo podman ps ``` Note: If you add *-a* to the *ps* command, Podman will show all containers. @@ -134,7 +137,7 @@ Now that we have the IP address of the container, we can test the network commun operating system and the container using curl. The following command should display the index page of our containerized httpd server. ```console -# curl http://:8080 +curl http://:8080 ``` ### Viewing the container's logs @@ -163,7 +166,7 @@ With this a container can later be restored and continue running at exactly the checkpoint. This capability requires CRIU 3.11 or later installed on the system. To checkpoint the container use: ```console -$ sudo podman container checkpoint +sudo podman container checkpoint ``` ### Restoring the container @@ -171,29 +174,29 @@ Restoring a container is only possible for a previously checkpointed container. continue to run at exactly the same point in time it was checkpointed. To restore the container use: ```console -$ sudo podman container restore +sudo podman container restore ``` After being restored, the container will answer requests again as it did before checkpointing. ```console -# curl http://:8080 +curl http://:8080 ``` ### Stopping the container To stop the httpd container: ```console -$ sudo podman stop --latest +sudo podman stop --latest ``` You can also check the status of one or more containers using the *ps* subcommand. In this case, we should use the *-a* argument to list all containers. ```console -$ sudo podman ps -a +sudo podman ps -a ``` ### Removing the container To remove the httpd container: ```console -$ sudo podman rm --latest +sudo podman rm --latest ``` You can verify the deletion of the container by running *podman ps -a*. -- cgit v1.2.3-54-g00ecf From b6fabf1d214f7ec0694bd642f879c2afaa037fa7 Mon Sep 17 00:00:00 2001 From: Qi Wang Date: Mon, 12 Nov 2018 14:05:15 -0500 Subject: Add space between num & unit in images output Signed-off-by: Qi Wang --- cmd/podman/images.go | 6 ++++-- docs/podman-images.1.md | 44 ++++++++++++++++++++++---------------------- 2 files changed, 26 insertions(+), 24 deletions(-) (limited to 'docs') diff --git a/cmd/podman/images.go b/cmd/podman/images.go index a8955e49e..c52b26260 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -6,8 +6,7 @@ import ( "sort" "strings" "time" - - "github.com/sirupsen/logrus" + "unicode" "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/libpodruntime" @@ -16,6 +15,7 @@ import ( "github.com/docker/go-units" digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/urfave/cli" ) @@ -289,6 +289,8 @@ func getImagesTemplateOutput(ctx context.Context, runtime *libpod.Runtime, image sizeStr = err.Error() } else { sizeStr = units.HumanSizeWithPrecision(float64(*size), 3) + lastNumIdx := strings.LastIndexFunc(sizeStr, unicode.IsNumber) + sizeStr = sizeStr[:lastNumIdx+1] + " " + sizeStr[lastNumIdx+1:] } params := imagesTemplateParams{ Repository: repo, diff --git a/docs/podman-images.1.md b/docs/podman-images.1.md index 0bd829a8e..832df0e23 100644 --- a/docs/podman-images.1.md +++ b/docs/podman-images.1.md @@ -49,9 +49,9 @@ Sort by created, id, repository, size or tag (default: created) ``` # podman images REPOSITORY TAG IMAGE ID CREATED SIZE -docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251kB - ebb91b73692b 4 weeks ago 27.2MB -docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126MB +docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251 kB + ebb91b73692b 4 weeks ago 27.2 MB +docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126 MB ``` ``` @@ -63,17 +63,17 @@ ebb91b73692b ``` # podman images --noheading -docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251kB - ebb91b73692b 4 weeks ago 27.2MB -docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126MB +docker.io/kubernetes/pause latest e3d42bcaf643 3 years ago 251 kB + ebb91b73692b 4 weeks ago 27.2 MB +docker.io/library/ubuntu latest 4526339ae51c 6 weeks ago 126 MB ``` ``` # podman images --no-trunc REPOSITORY TAG IMAGE ID CREATED SIZE -docker.io/kubernetes/pause latest sha256:e3d42bcaf643097dd1bb0385658ae8cbe100a80f773555c44690d22c25d16b27 3 years ago 251kB - sha256:ebb91b73692bd27890685846412ae338d13552165eacf7fcd5f139bfa9c2d6d9 4 weeks ago 27.2MB -docker.io/library/ubuntu latest sha256:4526339ae51c3cdc97956a7a961c193c39dfc6bd9733b0d762a36c6881b5583a 6 weeks ago 126MB +docker.io/kubernetes/pause latest sha256:e3d42bcaf643097dd1bb0385658ae8cbe100a80f773555c44690d22c25d16b27 3 years ago 251 kB + sha256:ebb91b73692bd27890685846412ae338d13552165eacf7fcd5f139bfa9c2d6d9 4 weeks ago 27.2 MB +docker.io/library/ubuntu latest sha256:4526339ae51c3cdc97956a7a961c193c39dfc6bd9733b0d762a36c6881b5583a 6 weeks ago 126 MB ``` ``` @@ -87,7 +87,7 @@ ebb91b73692b ``` # podman images --filter dangling=true REPOSITORY TAG IMAGE ID CREATED SIZE - ebb91b73692b 4 weeks ago 27.2MB + ebb91b73692b 4 weeks ago 27.2 MB ``` ``` @@ -126,25 +126,25 @@ REPOSITORY TAG IMAGE ID CREATED SIZE ``` # podman images --sort repository REPOSITORY TAG IMAGE ID CREATED SIZE - 2460217d76fc About a minute ago 4.41MB -docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41MB -localhost/myapp latest b2e0ad03474a About a minute ago 4.41MB -registry.access.redhat.com/rhel7 latest 7a840db7f020 2 weeks ago 211MB -registry.fedoraproject.org/fedora 27 801894bc0e43 6 weeks ago 246MB + 2460217d76fc About a minute ago 4.41 MB +docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB +localhost/myapp latest b2e0ad03474a About a minute ago 4.41 MB +registry.access.redhat.com/rhel7 latest 7a840db7f020 2 weeks ago 211 MB +registry.fedoraproject.org/fedora 27 801894bc0e43 6 weeks ago 246 MB ``` ``` # podman images REPOSITORY TAG IMAGE ID CREATED SIZE -localhost/test latest 18f0c080cd72 4 seconds ago 4.42MB -docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41MB +localhost/test latest 18f0c080cd72 4 seconds ago 4.42 MB +docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB # podman images -a REPOSITORY TAG IMAGE ID CREATED SIZE -localhost/test latest 18f0c080cd72 6 seconds ago 4.42MB - 270e70dc54c0 7 seconds ago 4.42MB - 4ed6fbe43414 8 seconds ago 4.41MB - 6b0df8e71508 8 seconds ago 4.41MB -docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41MB +localhost/test latest 18f0c080cd72 6 seconds ago 4.42 MB + 270e70dc54c0 7 seconds ago 4.42 MB + 4ed6fbe43414 8 seconds ago 4.41 MB + 6b0df8e71508 8 seconds ago 4.41 MB +docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB ``` ## SEE ALSO -- cgit v1.2.3-54-g00ecf From 690c52a113124efcedccb84e44198e7602f064ec Mon Sep 17 00:00:00 2001 From: baude Date: Mon, 19 Nov 2018 13:20:56 -0600 Subject: Allow users to expose ports from the pod to the host we need to allow users to expose ports to the host for the purposes of networking, like a webserver. the port exposure must be done at the time the pod is created. strictly speaking, the port exposure occurs on the infra container. Signed-off-by: baude --- cmd/podman/pod_create.go | 59 ++++++++++++++++++ completions/bash/podman | 6 +- docs/podman-pod-create.1.md | 9 +++ libpod/options.go | 11 ++++ libpod/pod.go | 4 +- libpod/pod_easyjson.go | 128 ++++++++++++++++++++++++++++++++++++++ libpod/runtime_pod_infra_linux.go | 4 +- pkg/spec/createconfig.go | 1 - test/e2e/pod_create_test.go | 39 ++++++++++++ 9 files changed, 254 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index 63fa6b294..a3364ac4b 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -3,11 +3,15 @@ package main import ( "fmt" "os" + "strconv" "strings" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/rootless" + "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/docker/go-connections/nat" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -58,6 +62,10 @@ var podCreateFlags = []cli.Flag{ Name: "pod-id-file", Usage: "Write the pod ID to the file", }, + cli.StringSliceFlag{ + Name: "publish, p", + Usage: "Publish a container's port, or a range of ports, to the host (default [])", + }, cli.StringFlag{ Name: "share", Usage: "A comma delimited list of kernel namespaces the pod will share", @@ -102,6 +110,16 @@ func podCreateCmd(c *cli.Context) error { defer podIdFile.Close() defer podIdFile.Sync() } + + if len(c.StringSlice("publish")) > 0 { + if !c.BoolT("infra") { + return errors.Errorf("you must have an infra container to publish port bindings to the host") + } + if rootless.IsRootless() { + return errors.Errorf("rootless networking does not allow port binding to the host") + } + } + if !c.BoolT("infra") && c.IsSet("share") && c.String("share") != "none" && c.String("share") != "" { return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container") } @@ -131,6 +149,14 @@ func podCreateCmd(c *cli.Context) error { options = append(options, nsOptions...) } + if len(c.StringSlice("publish")) > 0 { + portBindings, err := CreatePortBindings(c.StringSlice("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()) @@ -152,3 +178,36 @@ func podCreateCmd(c *cli.Context) error { return nil } + +// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands +func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) { + var portBindings []ocicni.PortMapping + // The conversion from []string to natBindings is temporary while mheon reworks the port + // deduplication code. Eventually that step will not be required. + _, natBindings, err := nat.ParsePortSpecs(ports) + if err != nil { + return nil, err + } + for containerPb, hostPb := range natBindings { + var pm ocicni.PortMapping + pm.ContainerPort = int32(containerPb.Int()) + for _, i := range hostPb { + var hostPort int + var err error + pm.HostIP = i.HostIP + if i.HostPort == "" { + hostPort = containerPb.Int() + } else { + hostPort, err = strconv.Atoi(i.HostPort) + if err != nil { + return nil, errors.Wrapf(err, "unable to convert host port to integer") + } + } + + pm.HostPort = int32(hostPort) + pm.Protocol = containerPb.Proto() + portBindings = append(portBindings, pm) + } + } + return portBindings, nil +} diff --git a/completions/bash/podman b/completions/bash/podman index c029f893a..222511a3c 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2178,12 +2178,14 @@ _podman_pod_create() { --cgroup-parent --infra-command --infra-image - --share - --podidfile --label-file --label -l --name + --podidfile + --publish + -p + --share " local boolean_options=" diff --git a/docs/podman-pod-create.1.md b/docs/podman-pod-create.1.md index 673ad9a8c..a63b12d73 100644 --- a/docs/podman-pod-create.1.md +++ b/docs/podman-pod-create.1.md @@ -51,6 +51,15 @@ Assign a name to the pod Write the pod ID to the file +**-p**, **--publish**=[] + +Publish a port or range of ports from the pod to the host + +Format: `ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort` +Both hostPort and containerPort can be specified as a range of ports. +When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range. +Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT` + **--share**="" A comma deliminated list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, user, uts. diff --git a/libpod/options.go b/libpod/options.go index 8d044313b..507847d65 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1295,3 +1295,14 @@ func WithInfraContainer() PodCreateOption { return nil } } + +// WithInfraContainerPorts tells the pod to add port bindings to the pause container +func WithInfraContainerPorts(bindings []ocicni.PortMapping) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return ErrPodFinalized + } + pod.config.InfraContainer.PortBindings = bindings + return nil + } +} diff --git a/libpod/pod.go b/libpod/pod.go index 8ac976f6a..07f41f5c6 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -4,6 +4,7 @@ import ( "time" "github.com/containers/storage" + "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" ) @@ -96,7 +97,8 @@ type PodContainerInfo struct { // InfraContainerConfig is the configuration for the pod's infra container type InfraContainerConfig struct { - HasInfraContainer bool `json:"makeInfraContainer"` + HasInfraContainer bool `json:"makeInfraContainer"` + PortBindings []ocicni.PortMapping `json:"infraPortBindings"` } // ID retrieves the pod's ID diff --git a/libpod/pod_easyjson.go b/libpod/pod_easyjson.go index 6c1c939f3..8ea9a5e72 100644 --- a/libpod/pod_easyjson.go +++ b/libpod/pod_easyjson.go @@ -6,6 +6,7 @@ package libpod import ( json "encoding/json" + ocicni "github.com/cri-o/ocicni/pkg/ocicni" easyjson "github.com/mailru/easyjson" jlexer "github.com/mailru/easyjson/jlexer" jwriter "github.com/mailru/easyjson/jwriter" @@ -721,6 +722,29 @@ func easyjsonBe091417DecodeGithubComContainersLibpodLibpod5(in *jlexer.Lexer, ou switch key { case "makeInfraContainer": out.HasInfraContainer = bool(in.Bool()) + case "infraPortBindings": + if in.IsNull() { + in.Skip() + out.PortBindings = nil + } else { + in.Delim('[') + if out.PortBindings == nil { + if !in.IsDelim(']') { + out.PortBindings = make([]ocicni.PortMapping, 0, 1) + } else { + out.PortBindings = []ocicni.PortMapping{} + } + } else { + out.PortBindings = (out.PortBindings)[:0] + } + for !in.IsDelim(']') { + var v6 ocicni.PortMapping + easyjsonBe091417DecodeGithubComContainersLibpodVendorGithubComCriOOcicniPkgOcicni(in, &v6) + out.PortBindings = append(out.PortBindings, v6) + in.WantComma() + } + in.Delim(']') + } default: in.SkipRecursive() } @@ -745,5 +769,109 @@ func easyjsonBe091417EncodeGithubComContainersLibpodLibpod5(out *jwriter.Writer, } out.Bool(bool(in.HasInfraContainer)) } + { + const prefix string = ",\"infraPortBindings\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + if in.PortBindings == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v7, v8 := range in.PortBindings { + if v7 > 0 { + out.RawByte(',') + } + easyjsonBe091417EncodeGithubComContainersLibpodVendorGithubComCriOOcicniPkgOcicni(out, v8) + } + out.RawByte(']') + } + } + out.RawByte('}') +} +func easyjsonBe091417DecodeGithubComContainersLibpodVendorGithubComCriOOcicniPkgOcicni(in *jlexer.Lexer, out *ocicni.PortMapping) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "hostPort": + out.HostPort = int32(in.Int32()) + case "containerPort": + out.ContainerPort = int32(in.Int32()) + case "protocol": + out.Protocol = string(in.String()) + case "hostIP": + out.HostIP = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonBe091417EncodeGithubComContainersLibpodVendorGithubComCriOOcicniPkgOcicni(out *jwriter.Writer, in ocicni.PortMapping) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"hostPort\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int32(int32(in.HostPort)) + } + { + const prefix string = ",\"containerPort\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int32(int32(in.ContainerPort)) + } + { + const prefix string = ",\"protocol\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Protocol)) + } + { + const prefix string = ",\"hostIP\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.HostIP)) + } out.RawByte('}') } diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index fea79e994..450a2fb32 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -7,7 +7,6 @@ import ( "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/rootless" - "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" ) @@ -50,9 +49,8 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID options = append(options, withIsInfra()) // Since user namespace sharing is not implemented, we only need to check if it's rootless - portMappings := make([]ocicni.PortMapping, 0) networks := make([]string, 0) - options = append(options, WithNetNS(portMappings, isRootless, networks)) + options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, isRootless, networks)) return r.newContainer(ctx, g.Config, options...) } diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index 6ac9d82da..6a0642ee7 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -335,7 +335,6 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib } options = append(options, runtime.WithPod(pod)) } - if len(c.PortBindings) > 0 { portBindings, err = c.CreatePortBindings() if err != nil { diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 51522ffd1..5abf9613b 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -80,4 +80,43 @@ var _ = Describe("Podman pod create", func() { check.WaitWithDefaultTimeout() Expect(len(check.OutputToStringArray())).To(Equal(0)) }) + + It("podman create pod without network portbindings", func() { + name := "test" + session := podmanTest.Podman([]string{"pod", "create", "--name", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + pod := session.OutputToString() + + webserver := podmanTest.Podman([]string{"run", "--pod", pod, "-dt", nginx}) + webserver.WaitWithDefaultTimeout() + Expect(webserver.ExitCode()).To(Equal(0)) + + check := SystemExec("nc", []string{"-z", "localhost", "80"}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(1)) + }) + + It("podman create pod with network portbindings", func() { + name := "test" + session := podmanTest.Podman([]string{"pod", "create", "--name", name, "-p", "80:80"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + pod := session.OutputToString() + + webserver := podmanTest.Podman([]string{"run", "--pod", pod, "-dt", nginx}) + webserver.WaitWithDefaultTimeout() + Expect(webserver.ExitCode()).To(Equal(0)) + + check := SystemExec("nc", []string{"-z", "localhost", "80"}) + check.WaitWithDefaultTimeout() + Expect(check.ExitCode()).To(Equal(0)) + }) + + It("podman create pod with no infra but portbindings should fail", func() { + name := "test" + session := podmanTest.Podman([]string{"pod", "create", "--infra=false", "--name", name, "-p", "80:80"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + }) }) -- cgit v1.2.3-54-g00ecf From 24c0739453b3f103a1b548c1bb611013b488afbe Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Tue, 20 Nov 2018 16:22:48 +0000 Subject: Update checkpoint/restore man pages This adds the '--leave-running, -R' to the container-checkpoint man page. As the information for '--all, -a' and '--latest, -l' was also still missing it is included in this commit. Signed-off-by: Adrian Reber --- docs/podman-container-checkpoint.1.md | 12 ++++++++++++ docs/podman-container-restore.1.md | 8 ++++++++ 2 files changed, 20 insertions(+) (limited to 'docs') diff --git a/docs/podman-container-checkpoint.1.md b/docs/podman-container-checkpoint.1.md index 4906e0e12..6f454dfd1 100644 --- a/docs/podman-container-checkpoint.1.md +++ b/docs/podman-container-checkpoint.1.md @@ -17,6 +17,18 @@ are not deleted if checkpointing fails for further debugging. If checkpointing s files are theoretically not needed, but if these files are needed Podman can keep the files for further analysis. +**--all, -a** + +Checkpoint all running containers. + +**--latest, -l** + +Instead of providing the container name or ID, checkpoint the last created container. + +**--leave-running, -R** + +Leave the container running after checkpointing instead of stopping it. + ## EXAMPLE podman container checkpoint mywebserver diff --git a/docs/podman-container-restore.1.md b/docs/podman-container-restore.1.md index 6360bccb0..4dd5ea7c7 100644 --- a/docs/podman-container-restore.1.md +++ b/docs/podman-container-restore.1.md @@ -24,6 +24,14 @@ processes in the checkpointed container. Without the **-k**, **--keep** option the checkpoint will be consumed and cannot be used again. +**--all, -a** + +Restore all checkpointed containers. + +**--latest, -l** + +Instead of providing the container name or ID, restore the last created container. + ## EXAMPLE podman container restore mywebserver -- cgit v1.2.3-54-g00ecf From 22d971cd751489516a810933f2e0c297dcd5b62c Mon Sep 17 00:00:00 2001 From: Steve Phillips Date: Sat, 24 Nov 2018 15:02:44 -0800 Subject: podman_tutorial.md typos: arguement -> argument; missing 'a' Signed-off-by: Steve Phillips --- docs/tutorials/podman_tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md index 5a8f997b8..ce94d7d15 100644 --- a/docs/tutorials/podman_tutorial.md +++ b/docs/tutorials/podman_tutorial.md @@ -129,7 +129,7 @@ $ sudo podman inspect -l | grep IPAddress\": "IPAddress": "10.88.6.140", ``` -Note: The -l is convenience arguement for **latest container**. You can also use the container's ID instead +Note: The -l is a convenience argument for **latest container**. You can also use the container's ID instead of -l. ### Testing the httpd server -- cgit v1.2.3-54-g00ecf From 43c20d02006fb83d05414a2f868b1c0a4dd4346b Mon Sep 17 00:00:00 2001 From: Tomas Tomecek Date: Fri, 23 Nov 2018 22:25:45 +0100 Subject: implement --format for version command Signed-off-by: Tomas Tomecek --- cmd/podman/version.go | 33 ++++++++++++++++++++++++++++----- completions/bash/podman | 15 ++++++++++----- docs/podman-version.1.md | 23 +++++++++++++++++++++++ 3 files changed, 61 insertions(+), 10 deletions(-) (limited to 'docs') diff --git a/cmd/podman/version.go b/cmd/podman/version.go index d80f24a14..d81deb696 100644 --- a/cmd/podman/version.go +++ b/cmd/podman/version.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/libpod" "github.com/pkg/errors" "github.com/urfave/cli" @@ -15,6 +16,19 @@ func versionCmd(c *cli.Context) error { if err != nil { errors.Wrapf(err, "unable to determine version") } + + versionOutputFormat := c.String("format") + if versionOutputFormat != "" { + var out formats.Writer + switch versionOutputFormat { + case formats.JSONString: + out = formats.JSONStruct{Output: output} + default: + out = formats.StdoutTemplate{Output: output, Template: versionOutputFormat} + } + formats.Writer(out).Out() + return nil + } fmt.Println("Version: ", output.Version) fmt.Println("Go Version: ", output.GoVersion) if output.GitCommit != "" { @@ -30,8 +44,17 @@ func versionCmd(c *cli.Context) error { } // Cli command to print out the full version of podman -var versionCommand = cli.Command{ - Name: "version", - Usage: "Display the PODMAN Version Information", - Action: versionCmd, -} +var ( + versionCommand = cli.Command{ + Name: "version", + Usage: "Display the Podman Version Information", + Action: versionCmd, + Flags: versionFlags, + } + versionFlags = []cli.Flag{ + cli.StringFlag{ + Name: "format", + Usage: "Change the output format to JSON or a Go template", + }, + } +) diff --git a/completions/bash/podman b/completions/bash/podman index 222511a3c..8eaa1e6a9 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1906,11 +1906,16 @@ _podman_top() { } _podman_version() { - local options_with_args=" - " - local boolean_options=" - " - _complete_ "$options_with_args" "$boolean_options" + local boolean_options=" + --help + -h + " + local options_with_args=" + --format + " + local all_options="$options_with_args $boolean_options" + + _complete_ "$options_with_args" "$boolean_options" } _podman_save() { diff --git a/docs/podman-version.1.md b/docs/podman-version.1.md index 0c9b9ceed..749a33afd 100644 --- a/docs/podman-version.1.md +++ b/docs/podman-version.1.md @@ -16,8 +16,31 @@ OS, and Architecture. Print usage statement +**--format** + +Change output format to "json" or a Go template. + +## Example + +A sample output of the `version` command: +``` +$ podman version +Version: 0.11.1 +Go Version: go1.11 +Git Commit: "8967a1d691ed44896b81ad48c863033f23c65eb0-dirty" +Built: Thu Nov 8 22:35:40 2018 +OS/Arch: linux/amd64 +``` + +Filtering out only the version: +``` +$ podman version --format '{{.Version}}' +0.11.2 +``` + ## SEE ALSO podman(1), crio(8) ## HISTORY +November 2018, Added --format flag by Tomas Tomecek July 2017, Originally compiled by Urvashi Mohnani -- cgit v1.2.3-54-g00ecf From 9d883d2032b112d5c65040629313cfba0de6c479 Mon Sep 17 00:00:00 2001 From: baude Date: Sun, 25 Nov 2018 16:26:39 -0600 Subject: add podman container|image exists Add an exists subcommand to podman container and podman image that allows users to verify the existence of a container or image by ID or name. The return code can be 0 (success), 1 (failed to find), or 125 (failed to work with runtime). Issue #1845 Signed-off-by: baude --- cmd/podman/container.go | 1 + cmd/podman/exists.go | 83 ++++++++++++++++++++++++++++++++++++++ cmd/podman/image.go | 1 + completions/bash/podman | 16 ++++++++ docs/podman-container-exists.1.md | 40 ++++++++++++++++++ docs/podman-container.1.md | 1 + docs/podman-image-exists.1.md | 40 ++++++++++++++++++ docs/podman-image.1.md | 1 + libpod/image/errors.go | 15 +++++++ libpod/image/image.go | 4 +- test/e2e/exists_test.go | 85 +++++++++++++++++++++++++++++++++++++++ 11 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 cmd/podman/exists.go create mode 100644 docs/podman-container-exists.1.md create mode 100644 docs/podman-image-exists.1.md create mode 100644 libpod/image/errors.go create mode 100644 test/e2e/exists_test.go (limited to 'docs') diff --git a/cmd/podman/container.go b/cmd/podman/container.go index ff634278f..b6262f890 100644 --- a/cmd/podman/container.go +++ b/cmd/podman/container.go @@ -9,6 +9,7 @@ var ( attachCommand, checkpointCommand, cleanupCommand, + containerExistsCommand, commitCommand, createCommand, diffCommand, diff --git a/cmd/podman/exists.go b/cmd/podman/exists.go new file mode 100644 index 000000000..2f7b7c185 --- /dev/null +++ b/cmd/podman/exists.go @@ -0,0 +1,83 @@ +package main + +import ( + "os" + + "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/image" + "github.com/pkg/errors" + "github.com/urfave/cli" +) + +var ( + imageExistsDescription = ` + podman image exists + + Check if an image exists in local storage +` + + imageExistsCommand = cli.Command{ + Name: "exists", + Usage: "Check if an image exists in local storage", + Description: imageExistsDescription, + Action: imageExistsCmd, + ArgsUsage: "IMAGE-NAME", + OnUsageError: usageErrorHandler, + } +) + +var ( + containerExistsDescription = ` + podman container exists + + Check if a container exists in local storage +` + + containerExistsCommand = cli.Command{ + Name: "exists", + Usage: "Check if a container exists in local storage", + Description: containerExistsDescription, + Action: containerExistsCmd, + ArgsUsage: "CONTAINER-NAME", + OnUsageError: usageErrorHandler, + } +) + +func imageExistsCmd(c *cli.Context) error { + args := c.Args() + if len(args) > 1 || len(args) < 1 { + return errors.New("you may only check for the existence of one image at a time") + } + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + if _, err := runtime.ImageRuntime().NewFromLocal(args[0]); err != nil { + if errors.Cause(err) == image.ErrNoSuchImage { + os.Exit(1) + } + return err + } + return nil +} + +func containerExistsCmd(c *cli.Context) error { + args := c.Args() + if len(args) > 1 || len(args) < 1 { + return errors.New("you may only check for the existence of one container at a time") + } + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + if _, err := runtime.LookupContainer(args[0]); err != nil { + if errors.Cause(err) == libpod.ErrNoSuchCtr { + os.Exit(1) + } + return err + } + return nil +} diff --git a/cmd/podman/image.go b/cmd/podman/image.go index e67f61799..418b442e3 100644 --- a/cmd/podman/image.go +++ b/cmd/podman/image.go @@ -9,6 +9,7 @@ var ( buildCommand, historyCommand, importCommand, + imageExistsCommand, inspectCommand, loadCommand, lsImagesCommand, diff --git a/completions/bash/podman b/completions/bash/podman index 8eaa1e6a9..3c6b6ec50 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2178,6 +2178,22 @@ _podman_container_runlabel() { esac } +_podman_container_exists() { + local options_with_args=" + " + + local boolean_options=" + " +} + +_podman_image_exists() { + local options_with_args=" + " + + local boolean_options=" + " +} + _podman_pod_create() { local options_with_args=" --cgroup-parent diff --git a/docs/podman-container-exists.1.md b/docs/podman-container-exists.1.md new file mode 100644 index 000000000..76701e2c2 --- /dev/null +++ b/docs/podman-container-exists.1.md @@ -0,0 +1,40 @@ +% PODMAN(1) Podman Man Pages +% Brent Baude +% November 2018 +# NAME +podman-container-exists- Check if a container exists in local storage + +# SYNOPSIS +**podman container exists** +[**-h**|**--help**] +CONTAINER + +# DESCRIPTION +**podman container exists** checks if a container exists in local storage. The **ID** or **Name** +of the container may be used as input. Podman will return an exit code +of `0` when the container is found. A `1` will be returned otherwise. An exit code of `125` indicates there +was an issue accessing the local storage. + +## Examples ## + +Check if an container called `webclient` exists in local storage (the container does actually exist). +``` +$ sudo podman container exists webclient +$ echo $? +0 +$ +``` + +Check if an container called `webbackend` exists in local storage (the container does not actually exist). +``` +$ sudo podman container exists webbackend +$ echo $? +1 +$ +``` + +## SEE ALSO +podman(1) + +# HISTORY +November 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) diff --git a/docs/podman-container.1.md b/docs/podman-container.1.md index 67d42bfef..aa5dfa82c 100644 --- a/docs/podman-container.1.md +++ b/docs/podman-container.1.md @@ -20,6 +20,7 @@ The container command allows you to manage containers | create | [podman-create(1)](podman-create.1.md) | Create a new container. | | diff | [podman-diff(1)](podman-diff.1.md) | Inspect changes on a container or image's filesystem. | | exec | [podman-exec(1)](podman-exec.1.md) | Execute a command in a running container. | +| exists | [podman-exists(1)](podman-container-exists.1.md) | Check if a container exists in local storage | | export | [podman-export(1)](podman-export.1.md) | Export a container's filesystem contents as a tar archive. | | inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. | | kill | [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. | diff --git a/docs/podman-image-exists.1.md b/docs/podman-image-exists.1.md new file mode 100644 index 000000000..e04c23721 --- /dev/null +++ b/docs/podman-image-exists.1.md @@ -0,0 +1,40 @@ +% PODMAN(1) Podman Man Pages +% Brent Baude +% November 2018 +# NAME +podman-image-exists- Check if an image exists in local storage + +# SYNOPSIS +**podman image exists** +[**-h**|**--help**] +IMAGE + +# DESCRIPTION +**podman image exists** checks if an image exists in local storage. The **ID** or **Name** +of the image may be used as input. Podman will return an exit code +of `0` when the image is found. A `1` will be returned otherwise. An exit code of `125` indicates there +was an issue accessing the local storage. + +## Examples ## + +Check if an image called `webclient` exists in local storage (the image does actually exist). +``` +$ sudo podman image exists webclient +$ echo $? +0 +$ +``` + +Check if an image called `webbackend` exists in local storage (the image does not actually exist). +``` +$ sudo podman image exists webbackend +$ echo $? +1 +$ +``` + +## SEE ALSO +podman(1) + +# HISTORY +November 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) diff --git a/docs/podman-image.1.md b/docs/podman-image.1.md index 33de0456f..446f8667d 100644 --- a/docs/podman-image.1.md +++ b/docs/podman-image.1.md @@ -14,6 +14,7 @@ The image command allows you to manage images | Command | Man Page | Description | | -------- | ----------------------------------------- | ------------------------------------------------------------------------------ | | build | [podman-build(1)](podman-build.1.md) | Build a container using a Dockerfile. | +| exists | [podman-exists(1)](podman-image-exists.1.md) | Check if a image exists in local storage | | history | [podman-history(1)](podman-history.1.md) | Show the history of an image. | | import | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. | | inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a image or image's configuration. | diff --git a/libpod/image/errors.go b/libpod/image/errors.go new file mode 100644 index 000000000..4088946cb --- /dev/null +++ b/libpod/image/errors.go @@ -0,0 +1,15 @@ +package image + +import ( + "errors" +) + +// Copied directly from libpod errors to avoid circular imports +var ( + // ErrNoSuchCtr indicates the requested container does not exist + ErrNoSuchCtr = errors.New("no such container") + // ErrNoSuchPod indicates the requested pod does not exist + ErrNoSuchPod = errors.New("no such pod") + // ErrNoSuchImage indicates the requested image does not exist + ErrNoSuchImage = errors.New("no such image") +) diff --git a/libpod/image/image.go b/libpod/image/image.go index 7e520d97e..a05c15160 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -252,7 +252,7 @@ func (i *Image) getLocalImage() (*storage.Image, error) { // The image has a registry name in it and we made sure we looked for it locally // with a tag. It cannot be local. if decomposedImage.hasRegistry { - return nil, errors.Errorf("%s", imageError) + return nil, errors.Wrapf(ErrNoSuchImage, imageError) } @@ -275,7 +275,7 @@ func (i *Image) getLocalImage() (*storage.Image, error) { return repoImage, nil } - return nil, errors.Wrapf(err, imageError) + return nil, errors.Wrapf(ErrNoSuchImage, err.Error()) } // ID returns the image ID as a string diff --git a/test/e2e/exists_test.go b/test/e2e/exists_test.go new file mode 100644 index 000000000..9165e8902 --- /dev/null +++ b/test/e2e/exists_test.go @@ -0,0 +1,85 @@ +package integration + +import ( + "fmt" + "os" + + . "github.com/containers/libpod/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman image|container exists", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.RestoreAllArtifacts() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()) + GinkgoWriter.Write([]byte(timedResult)) + + }) + It("podman image exists in local storage by fq name", func() { + session := podmanTest.Podman([]string{"image", "exists", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman image exists in local storage by short name", func() { + session := podmanTest.Podman([]string{"image", "exists", "alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman image does not exist in local storage", func() { + session := podmanTest.Podman([]string{"image", "exists", "alpine9999"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(1)) + }) + It("podman container exists in local storage by name", func() { + setup := podmanTest.RunTopContainer("foobar") + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"container", "exists", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman container exists in local storage by container ID", func() { + setup := podmanTest.RunTopContainer("") + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + cid := setup.OutputToString() + + session := podmanTest.Podman([]string{"container", "exists", cid}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman container exists in local storage by short container ID", func() { + setup := podmanTest.RunTopContainer("") + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + cid := setup.OutputToString()[0:12] + + session := podmanTest.Podman([]string{"container", "exists", cid}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman container does not exist in local storage", func() { + session := podmanTest.Podman([]string{"container", "exists", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(1)) + }) + +}) -- cgit v1.2.3-54-g00ecf From af7d67771be7a4b19afb9611a8f8d48acd7010ee Mon Sep 17 00:00:00 2001 From: Adrian Reber Date: Wed, 21 Nov 2018 15:30:41 +0000 Subject: Add '--tcp-established' to checkpoint/restore man page Signed-off-by: Adrian Reber --- docs/podman-container-checkpoint.1.md | 7 +++++++ docs/podman-container-restore.1.md | 8 ++++++++ 2 files changed, 15 insertions(+) (limited to 'docs') diff --git a/docs/podman-container-checkpoint.1.md b/docs/podman-container-checkpoint.1.md index 6f454dfd1..94e52dc78 100644 --- a/docs/podman-container-checkpoint.1.md +++ b/docs/podman-container-checkpoint.1.md @@ -29,6 +29,13 @@ Instead of providing the container name or ID, checkpoint the last created conta Leave the container running after checkpointing instead of stopping it. +**--tcp-established** + +Checkpoint a container with established TCP connections. If the checkpoint +image contains established TCP connections, this options is required during +restore. Defaults to not checkpointing containers with established TCP +connections. + ## EXAMPLE podman container checkpoint mywebserver diff --git a/docs/podman-container-restore.1.md b/docs/podman-container-restore.1.md index 4dd5ea7c7..44219f3ef 100644 --- a/docs/podman-container-restore.1.md +++ b/docs/podman-container-restore.1.md @@ -32,6 +32,14 @@ Restore all checkpointed containers. Instead of providing the container name or ID, restore the last created container. +**--tcp-established** + +Restore a container with established TCP connections. If the checkpoint image +contains established TCP connections, this option is required during restore. +If the checkpoint image does not contain established TCP connections this +option is ignored. Defaults to not restoring containers with established TCP +connections. + ## EXAMPLE podman container restore mywebserver -- cgit v1.2.3-54-g00ecf From 95f22a2ca055d6dec0281cee109375dc4fd9b78b Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Mon, 26 Nov 2018 21:31:06 +0100 Subject: network: allow slirp4netns mode also for root containers Signed-off-by: Giuseppe Scrivano --- docs/podman-create.1.md | 3 ++- docs/podman-run.1.md | 3 ++- libpod/container.go | 3 +++ libpod/container_easyjson.go | 13 +++++++++++++ libpod/container_internal.go | 2 +- libpod/oci.go | 2 +- libpod/options.go | 4 +++- libpod/runtime_pod_infra_linux.go | 6 +++++- pkg/spec/createconfig.go | 4 ++-- 9 files changed, 32 insertions(+), 8 deletions(-) (limited to 'docs') diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 68c00685b..474796a35 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -426,7 +426,8 @@ Set the Network mode for the container 'container:': reuse another container's network stack 'host': use the podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. '|': connect to a user-defined network - 'ns:' path to a network namespace to join + 'ns:': path to a network namespace to join + 'slirp4netns': use slirp4netns to create a user network stack. This is the default for rootless containers **--network-alias**=[] diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index 912026a55..202091b07 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -408,7 +408,8 @@ Set the Network mode for the container: - `container:`: reuse another container's network stack - `host`: use the podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. - `|`: connect to a user-defined network -- `ns:` path to a network namespace to join +- `ns:`: path to a network namespace to join +- `slirp4netns`: use slirp4netns to create a user network stack. This is the default for rootless containers **--network-alias**=[] diff --git a/libpod/container.go b/libpod/container.go index 16f61d021..a8a58f4d8 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -9,6 +9,7 @@ import ( "github.com/containernetworking/cni/pkg/types" cnitypes "github.com/containernetworking/cni/pkg/types/current" + "github.com/containers/libpod/pkg/namespaces" "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -296,6 +297,8 @@ type ContainerConfig struct { HostAdd []string `json:"hostsAdd,omitempty"` // Network names (CNI) to add container to. Empty to use default network. Networks []string `json:"networks,omitempty"` + // Network mode specified for the default network. + NetMode namespaces.NetworkMode `json:"networkMode,omitempty"` // Image Config diff --git a/libpod/container_easyjson.go b/libpod/container_easyjson.go index 041cc08ac..8bf5cb64f 100644 --- a/libpod/container_easyjson.go +++ b/libpod/container_easyjson.go @@ -8,6 +8,7 @@ import ( json "encoding/json" types "github.com/containernetworking/cni/pkg/types" current "github.com/containernetworking/cni/pkg/types/current" + namespaces "github.com/containers/libpod/pkg/namespaces" storage "github.com/containers/storage" idtools "github.com/containers/storage/pkg/idtools" ocicni "github.com/cri-o/ocicni/pkg/ocicni" @@ -1550,6 +1551,8 @@ func easyjson1dbef17bDecodeGithubComContainersLibpodLibpod2(in *jlexer.Lexer, ou } in.Delim(']') } + case "networkMode": + out.NetMode = namespaces.NetworkMode(in.String()) case "userVolumes": if in.IsNull() { in.Skip() @@ -2177,6 +2180,16 @@ func easyjson1dbef17bEncodeGithubComContainersLibpodLibpod2(out *jwriter.Writer, out.RawByte(']') } } + if in.NetMode != "" { + const prefix string = ",\"networkMode\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.NetMode)) + } if len(in.UserVolumes) != 0 { const prefix string = ",\"userVolumes\":" if first { diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 051e0aeb7..e44ec76ec 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -586,7 +586,7 @@ func (c *Container) completeNetworkSetup() error { if err := c.syncContainer(); err != nil { return err } - if rootless.IsRootless() { + if c.config.NetMode == "slirp4netns" { return c.runtime.setupRootlessNetNS(c) } return c.runtime.setupNetNS(c) diff --git a/libpod/oci.go b/libpod/oci.go index a7aec06e5..e9cceda82 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -329,7 +329,7 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res cmd.ExtraFiles = append(cmd.ExtraFiles, ports...) } - if rootless.IsRootless() { + if ctr.config.NetMode.IsSlirp4netns() { ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe() if err != nil { return errors.Wrapf(err, "failed to create rootless network sync pipe") diff --git a/libpod/options.go b/libpod/options.go index 507847d65..7f4e3ac6b 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -7,6 +7,7 @@ import ( "regexp" "syscall" + "github.com/containers/libpod/pkg/namespaces" "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" "github.com/cri-o/ocicni/pkg/ocicni" @@ -817,7 +818,7 @@ func WithDependencyCtrs(ctrs []*Container) CtrCreateOption { // namespace with a minimal configuration. // An optional array of port mappings can be provided. // Conflicts with WithNetNSFrom(). -func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, networks []string) CtrCreateOption { +func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption { return func(ctr *Container) error { if ctr.valid { return ErrCtrFinalized @@ -831,6 +832,7 @@ func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netwo ctr.config.CreateNetNS = true ctr.config.PortMappings = portMappings ctr.config.Networks = networks + ctr.config.NetMode = namespaces.NetworkMode(netmode) return nil } diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 450a2fb32..8a5dbef56 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -50,7 +50,11 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID // Since user namespace sharing is not implemented, we only need to check if it's rootless networks := make([]string, 0) - options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, isRootless, networks)) + netmode := "bridge" + if isRootless { + netmode = "slirp4netns" + } + options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, isRootless, netmode, networks)) return r.newContainer(ctx, g.Config, options...) } diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index 6a0642ee7..a0fd40318 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -391,11 +391,11 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib options = append(options, libpod.WithNetNSFrom(connectedCtr)) } else if !c.NetMode.IsHost() && !c.NetMode.IsNone() { isRootless := rootless.IsRootless() - postConfigureNetNS := isRootless || (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() + postConfigureNetNS := c.NetMode.IsSlirp4netns() || (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() if isRootless && len(portBindings) > 0 { return nil, errors.New("port bindings are not yet supported by rootless containers") } - options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, networks)) + options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks)) } if c.PidMode.IsContainer() { -- cgit v1.2.3-54-g00ecf From 3beacb73bced227b211bf3b8710382b94358614b Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Fri, 16 Nov 2018 06:51:26 -0500 Subject: Disable mount options when running --privileged We now default to setting storage options to "nodev", when running privileged containers, we need to turn this off so the processes can manipulate the image. Signed-off-by: Daniel J Walsh --- docs/podman-create.1.md | 7 ++++--- docs/podman-run.1.md | 5 +++-- libpod/container_internal.go | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) (limited to 'docs') diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 68c00685b..ab2cb8c60 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -465,9 +465,10 @@ By default, podman containers are This is because by default a container is not allowed to access any devices. A “privileged” container is given access to all devices. -When the operator executes **podman run --privileged**, podman enables access -to all devices on the host as well as set turn off most of the security measures -protecting the host from the container. +When the operator executes a privileged container, podman enables access +to all devices on the host, turns off graphdriver mount options, as well as +turning off most of the security measures protecting the host from the +container. **-p**, **--publish**=[] diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index 912026a55..f99d2f863 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -450,8 +450,9 @@ container is not allowed to access any devices. A “privileged” container is given access to all devices. When the operator executes **podman run --privileged**, podman enables access -to all devices on the host as well as set turn off most of the security measures -protecting the host from the container. +to all devices on the host, turns off graphdriver mount options, as well as +turning off most of the security measures protecting the host from the +container. **-p**, **--publish**=[] diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 051e0aeb7..a426191a4 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -273,6 +273,27 @@ func (c *Container) setupStorage(ctx context.Context) error { }, LabelOpts: c.config.LabelOpts, } + if c.config.Privileged { + privOpt := func(opt string) bool { + for _, privopt := range []string{"nodev", "nosuid", "noexec"} { + if opt == privopt { + return true + } + } + return false + } + defOptions, err := storage.GetDefaultMountOptions() + if err != nil { + return errors.Wrapf(err, "error getting default mount options") + } + var newOptions []string + for _, opt := range defOptions { + if !privOpt(opt) { + newOptions = append(newOptions, opt) + } + } + options.MountOpts = newOptions + } if c.config.Rootfs == "" { options.IDMappingOptions = c.config.IDMappings -- cgit v1.2.3-54-g00ecf From 82bcef38954b4d03b7e047d7843e77bc94647c82 Mon Sep 17 00:00:00 2001 From: baude Date: Fri, 30 Nov 2018 09:15:11 -0600 Subject: add pod short option to ps podman ps has a flag --pod; simply adding a short option of -p Signed-off-by: baude --- cmd/podman/ps.go | 2 +- completions/bash/podman | 2 +- docs/podman-ps.1.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 83274c9a8..0b03388a2 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -184,7 +184,7 @@ var ( Usage: "Display the extended information", }, cli.BoolFlag{ - Name: "pod", + Name: "pod, p", Usage: "Print the ID and name of the pod the containers are associated with", }, cli.BoolFlag{ diff --git a/completions/bash/podman b/completions/bash/podman index 076ec7ab4..3cccf2192 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2033,7 +2033,7 @@ _podman_ps() { --help -h --latest -l --no-trunc - --pod + --pod -p --quiet -q --size -s --namespace --ns diff --git a/docs/podman-ps.1.md b/docs/podman-ps.1.md index 2cb77ffed..7333a1095 100644 --- a/docs/podman-ps.1.md +++ b/docs/podman-ps.1.md @@ -24,7 +24,7 @@ all the containers information. By default it lists: Show all the containers, default is only running containers -**--pod** +**--pod, -p** Display the pods the containers are associated with -- cgit v1.2.3-54-g00ecf From 318bf7017bcb82da9f73cfce9e3a963b61252788 Mon Sep 17 00:00:00 2001 From: baude Date: Sat, 1 Dec 2018 13:51:58 -0600 Subject: podman pod exists like containers and images, users would benefit from being able to check if a pod exists in local storage. if the pod exists, the return code is 0. if the pod does not exists, the return code is 1. Any other return code indicates a real errors, such as permissions or runtime. Signed-off-by: baude --- cmd/podman/exists.go | 37 +++++++++++++++++++++++++++++++++++++ cmd/podman/pod.go | 1 + completions/bash/podman | 8 ++++++++ docs/podman-pod-exists.1.md | 40 ++++++++++++++++++++++++++++++++++++++++ test/e2e/exists_test.go | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+) create mode 100644 docs/podman-pod-exists.1.md (limited to 'docs') diff --git a/cmd/podman/exists.go b/cmd/podman/exists.go index 2f7b7c185..2e2559ec7 100644 --- a/cmd/podman/exists.go +++ b/cmd/podman/exists.go @@ -44,6 +44,23 @@ var ( } ) +var ( + podExistsDescription = ` + podman pod exists + + Check if a pod exists in local storage +` + + podExistsCommand = cli.Command{ + Name: "exists", + Usage: "Check if a pod exists in local storage", + Description: podExistsDescription, + Action: podExistsCmd, + ArgsUsage: "POD-NAME", + OnUsageError: usageErrorHandler, + } +) + func imageExistsCmd(c *cli.Context) error { args := c.Args() if len(args) > 1 || len(args) < 1 { @@ -81,3 +98,23 @@ func containerExistsCmd(c *cli.Context) error { } return nil } + +func podExistsCmd(c *cli.Context) error { + args := c.Args() + if len(args) > 1 || len(args) < 1 { + return errors.New("you may only check for the existence of one pod at a time") + } + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + if _, err := runtime.LookupPod(args[0]); err != nil { + if errors.Cause(err) == libpod.ErrNoSuchPod { + os.Exit(1) + } + return err + } + return nil +} diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go index 0c6ec5e8c..a30361134 100644 --- a/cmd/podman/pod.go +++ b/cmd/podman/pod.go @@ -11,6 +11,7 @@ Pods are a group of one or more containers sharing the same network, pid and ipc ` podSubCommands = []cli.Command{ podCreateCommand, + podExistsCommand, podInspectCommand, podKillCommand, podPauseCommand, diff --git a/completions/bash/podman b/completions/bash/podman index 3cccf2192..9518cfa22 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2235,6 +2235,14 @@ _podman_container_exists() { " } +_podman_pod_exists() { + local options_with_args=" + " + + local boolean_options=" + " +} + _podman_image_exists() { local options_with_args=" " diff --git a/docs/podman-pod-exists.1.md b/docs/podman-pod-exists.1.md new file mode 100644 index 000000000..8fb2fc90e --- /dev/null +++ b/docs/podman-pod-exists.1.md @@ -0,0 +1,40 @@ +% podman-pod-exits(1) Podman Man Pages +% Brent Baude +% December 2018 +# NAME +podman-pod-exists- Check if a pod exists in local storage + +# SYNOPSIS +**podman pod exists** +[**-h**|**--help**] +POD + +# DESCRIPTION +**podman pod exists** checks if a pod exists in local storage. The **ID** or **Name** +of the pod may be used as input. Podman will return an exit code +of `0` when the pod is found. A `1` will be returned otherwise. An exit code of `125` indicates there +was an issue accessing the local storage. + +## Examples ## + +Check if a pod called `web` exists in local storage (the pod does actually exist). +``` +$ sudo podman pod exists web +$ echo $? +0 +$ +``` + +Check if a pod called `backend` exists in local storage (the pod does not actually exist). +``` +$ sudo podman pod exists backend +$ echo $? +1 +$ +``` + +## SEE ALSO +podman-pod(1), podman(1) + +# HISTORY +December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) diff --git a/test/e2e/exists_test.go b/test/e2e/exists_test.go index 9165e8902..d9652de4b 100644 --- a/test/e2e/exists_test.go +++ b/test/e2e/exists_test.go @@ -82,4 +82,36 @@ var _ = Describe("Podman image|container exists", func() { Expect(session.ExitCode()).To(Equal(1)) }) + It("podman pod exists in local storage by name", func() { + setup, rc, _ := podmanTest.CreatePod("foobar") + setup.WaitWithDefaultTimeout() + Expect(rc).To(Equal(0)) + + session := podmanTest.Podman([]string{"pod", "exists", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman pod exists in local storage by container ID", func() { + setup, rc, podID := podmanTest.CreatePod("") + setup.WaitWithDefaultTimeout() + Expect(rc).To(Equal(0)) + + session := podmanTest.Podman([]string{"pod", "exists", podID}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman pod exists in local storage by short container ID", func() { + setup, rc, podID := podmanTest.CreatePod("") + setup.WaitWithDefaultTimeout() + Expect(rc).To(Equal(0)) + + session := podmanTest.Podman([]string{"pod", "exists", podID[0:12]}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + It("podman pod does not exist in local storage", func() { + session := podmanTest.Podman([]string{"pod", "exists", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(1)) + }) }) -- cgit v1.2.3-54-g00ecf From a4b483c8484bb6fb9ae487264bccc663f007e711 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 2 Dec 2018 21:22:08 -0800 Subject: libpod/container_internal: Deprecate implicit hook directories Part of the motivation for 800eb863 (Hooks supports two directories, process default and override, 2018-09-17, #1487) was [1]: > We only use this for override. The reason this was caught is people > are trying to get hooks to work with CoreOS. You are not allowed to > write to /usr/share... on CoreOS, so they wanted podman to also look > at /etc, where users and third parties can write. But we'd also been disabling hooks completely for rootless users. And even for root users, the override logic was tricky when folks actually had content in both directories. For example, if you wanted to disable a hook from the default directory, you'd have to add a no-op hook to the override directory. Also, the previous implementation failed to handle the case where there hooks defined in the override directory but the default directory did not exist: $ podman version Version: 0.11.2-dev Go Version: go1.10.3 Git Commit: "6df7409cb5a41c710164c42ed35e33b28f3f7214" Built: Sun Dec 2 21:30:06 2018 OS/Arch: linux/amd64 $ ls -l /etc/containers/oci/hooks.d/test.json -rw-r--r--. 1 root root 184 Dec 2 16:27 /etc/containers/oci/hooks.d/test.json $ podman --log-level=debug run --rm docker.io/library/alpine echo 'successful container' 2>&1 | grep -i hook time="2018-12-02T21:31:19-08:00" level=debug msg="reading hooks from /usr/share/containers/oci/hooks.d" time="2018-12-02T21:31:19-08:00" level=warning msg="failed to load hooks: {}%!(EXTRA *os.PathError=open /usr/share/containers/oci/hooks.d: no such file or directory)" With this commit: $ podman --log-level=debug run --rm docker.io/library/alpine echo 'successful container' 2>&1 | grep -i hook time="2018-12-02T21:33:07-08:00" level=debug msg="reading hooks from /usr/share/containers/oci/hooks.d" time="2018-12-02T21:33:07-08:00" level=debug msg="reading hooks from /etc/containers/oci/hooks.d" time="2018-12-02T21:33:07-08:00" level=debug msg="added hook /etc/containers/oci/hooks.d/test.json" time="2018-12-02T21:33:07-08:00" level=debug msg="hook test.json matched; adding to stages [prestart]" time="2018-12-02T21:33:07-08:00" level=warning msg="implicit hook directories are deprecated; set --hooks-dir="/etc/containers/oci/hooks.d" explicitly to continue to load hooks from this directory" time="2018-12-02T21:33:07-08:00" level=error msg="container create failed: container_linux.go:336: starting container process caused "process_linux.go:399: container init caused \"process_linux.go:382: running prestart hook 0 caused \\\"error running hook: exit status 1, stdout: , stderr: oh, noes!\\\\n\\\"\"" (I'd setup the hook to error out). You can see that it's silenly ignoring the ENOENT for /usr/share/containers/oci/hooks.d and continuing on to load hooks from /etc/containers/oci/hooks.d. When it loads the hook, it also logs a warning-level message suggesting that callers explicitly configure their hook directories. That will help consumers migrate, so we can drop the implicit hook directories in some future release. When folks *do* explicitly configure hook directories (via the newly-public --hooks-dir and hooks_dir options), we error out if they're missing: $ podman --hooks-dir /does/not/exist run --rm docker.io/library/alpine echo 'successful container' error setting up OCI Hooks: open /does/not/exist: no such file or directory I've dropped the trailing "path" from the old, hidden --hooks-dir-path and hooks_dir_path because I think "dir(ectory)" is already enough context for "we expect a path argument". I consider this name change non-breaking because the old forms were undocumented. Coming back to rootless users, I've enabled hooks now. I expect they were previously disabled because users had no way to avoid /usr/share/containers/oci/hooks.d which might contain hooks that required root permissions. But now rootless users will have to explicitly configure hook directories, and since their default config is from ~/.config/containers/libpod.conf, it's a misconfiguration if it contains hooks_dir entries which point at directories with hooks that require root access. We error out so they can fix their libpod.conf. [1]: https://github.com/containers/libpod/pull/1487#discussion_r218149355 Signed-off-by: W. Trevor King --- cmd/podman/libpodruntime/runtime.go | 4 ++-- cmd/podman/main.go | 9 +++---- docs/libpod.conf.5.md | 12 ++++++++++ docs/podman.1.md | 24 +++++++++---------- libpod/container_internal.go | 48 ++++++++++++++++++++++--------------- libpod/container_internal_linux.go | 6 ++--- libpod/options.go | 15 ++++++------ libpod/runtime.go | 10 ++++---- libpod/testdata/config.toml | 2 +- test/e2e/run_test.go | 2 +- 10 files changed, 73 insertions(+), 59 deletions(-) (limited to 'docs') diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index a4b3581be..f69eaf3a4 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -90,8 +90,8 @@ func GetRuntimeWithStorageOpts(c *cli.Context, storageOpts *storage.StoreOptions if c.GlobalIsSet("default-mounts-file") { options = append(options, libpod.WithDefaultMountsFile(c.GlobalString("default-mounts-file"))) } - if c.GlobalIsSet("hooks-dir-path") { - options = append(options, libpod.WithHooksDir(c.GlobalString("hooks-dir-path"))) + if c.GlobalIsSet("hooks-dir") { + options = append(options, libpod.WithHooksDir(c.GlobalStringSlice("hooks-dir")...)) } // TODO flag to set CNI plugins dir? diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 6be192593..bcae04575 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -8,7 +8,6 @@ import ( "syscall" "github.com/containers/libpod/libpod" - "github.com/containers/libpod/pkg/hooks" _ "github.com/containers/libpod/pkg/hooks/0.1.0" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/version" @@ -206,11 +205,9 @@ func main() { Usage: "path to default mounts file", Hidden: true, }, - cli.StringFlag{ - Name: "hooks-dir-path", - Usage: "set the OCI hooks directory path", - Value: hooks.DefaultDir, - Hidden: true, + cli.StringSliceFlag{ + Name: "hooks-dir", + Usage: "set the OCI hooks directory path (may be set multiple times)", }, cli.IntFlag{ Name: "max-workers", diff --git a/docs/libpod.conf.5.md b/docs/libpod.conf.5.md index 198e927ee..d63baeb88 100644 --- a/docs/libpod.conf.5.md +++ b/docs/libpod.conf.5.md @@ -24,6 +24,18 @@ libpod to manage containers. **cgroup_manager**="" Specify the CGroup Manager to use; valid values are "systemd" and "cgroupfs" +**hooks_dir**=["*path*", ...] + + Each `*.json` file in the path configures a hook for Podman containers. For more details on the syntax of the JSON files and the semantics of hook injection, see `oci-hooks(5)`. Podman and libpod currently support both the 1.0.0 and 0.1.0 hook schemas, although the 0.1.0 schema is deprecated. + + Paths listed later in the array higher precedence (`oci-hooks(5)` discusses directory precedence). + + For the annotation conditions, libpod uses any annotations set in the generated OCI configuration. + + For the bind-mount conditions, only mounts explicitly requested by the caller via `--volume` are considered. Bind mounts that libpod inserts by default (e.g. `/dev/shm`) are not considered. + + If `hooks_dir` is unset for root callers, Podman and libpod will currently default to `/usr/share/containers/oci/hooks.d` and `/etc/containers/oci/hooks.d` in order of increasing precedence. Using these defaults is deprecated, and callers should migrate to explicitly setting `hooks_dir`. + **static_dir**="" Directory for persistent libpod files (database, etc) By default this will be configured relative to where containers/storage diff --git a/docs/podman.1.md b/docs/podman.1.md index b7433d850..bde349e6f 100644 --- a/docs/podman.1.md +++ b/docs/podman.1.md @@ -31,6 +31,18 @@ CGroup manager to use for container cgroups. Supported values are cgroupfs or sy Path to where the cpu performance results should be written +**--hooks-dir**=**path** + +Each `*.json` file in the path configures a hook for Podman containers. For more details on the syntax of the JSON files and the semantics of hook injection, see `oci-hooks(5)`. Podman and libpod currently support both the 1.0.0 and 0.1.0 hook schemas, although the 0.1.0 schema is deprecated. + +This option may be set multiple times; paths from later options have higher precedence (`oci-hooks(5)` discusses directory precedence). + +For the annotation conditions, libpod uses any annotations set in the generated OCI configuration. + +For the bind-mount conditions, only mounts explicitly requested by the caller via `--volume` are considered. Bind mounts that libpod inserts by default (e.g. `/dev/shm`) are not considered. + +If `--hooks-dir` is unset for root callers, Podman and libpod will currently default to `/usr/share/containers/oci/hooks.d` and `/etc/containers/oci/hooks.d` in order of increasing precedence. Using these defaults is deprecated, and callers should migrate to explicitly setting `--hooks-dir`. + **--log-level** Log messages above specified level: debug, info, warn, error (default), fatal or panic @@ -161,18 +173,6 @@ the exit codes follow the `chroot` standard, see below: The mounts.conf file specifies volume mount directories that are automatically mounted inside containers when executing the `podman run` or `podman start` commands. When Podman runs in rootless mode, the file `$HOME/.config/containers/mounts.conf` is also used. Please refer to containers-mounts.conf(5) for further details. -**OCI hooks JSON** (`/etc/containers/oci/hooks.d/*.json`, `/usr/share/containers/oci/hooks.d/*.json`) - - Each `*.json` file in `/etc/containers/oci/hooks.d` and `/usr/share/containers/oci/hooks.d` configures a hook for Podman containers, with `/etc/containers/oci/hooks.d` having higher precedence. For more details on the syntax of the JSON files and the semantics of hook injection, see `oci-hooks(5)`. - - Podman and libpod currently support both the 1.0.0 and 0.1.0 hook schemas, although the 0.1.0 schema is deprecated. - - For the annotation conditions, libpod uses any annotations set in the generated OCI configuration. - - For the bind-mount conditions, only mounts explicitly requested by the caller via `--volume` are considered. Bind mounts that libpod inserts by default (e.g. `/dev/shm`) are not considered. - - Hooks are not used when running in rootless mode. - **policy.json** (`/etc/containers/policy.json`) Signature verification policy files are used to specify policy, e.g. trusted keys, applicable when deciding whether to accept an image, or individual signatures of that image, as valid. diff --git a/libpod/container_internal.go b/libpod/container_internal.go index e31a8099c..934ad7a22 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1168,10 +1168,6 @@ func (c *Container) saveSpec(spec *spec.Spec) error { } func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (extensionStageHooks map[string][]spec.Hook, err error) { - if len(c.runtime.config.HooksDir) == 0 { - return nil, nil - } - var locale string var ok bool for _, envVar := range []string{ @@ -1199,25 +1195,39 @@ func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (exten } } - allHooks := make(map[string][]spec.Hook) - for _, hDir := range c.runtime.config.HooksDir { - manager, err := hooks.New(ctx, []string{hDir}, []string{"poststop"}, lang) - if err != nil { - if c.runtime.config.HooksDirNotExistFatal || !os.IsNotExist(err) { - return nil, err - } - logrus.Warnf("failed to load hooks: %q", err) + if c.runtime.config.HooksDir == nil { + if rootless.IsRootless() { return nil, nil } - hooks, err := manager.Hooks(config, c.Spec().Annotations, len(c.config.UserVolumes) > 0) - if err != nil { - return nil, err - } - for i, hook := range hooks { - allHooks[i] = hook + allHooks := make(map[string][]spec.Hook) + for _, hDir := range []string{hooks.DefaultDir, hooks.OverrideDir} { + manager, err := hooks.New(ctx, []string{hDir}, []string{"poststop"}, lang) + if err != nil { + if os.IsNotExist(err) { + continue + } + return nil, err + } + hooks, err := manager.Hooks(config, c.Spec().Annotations, len(c.config.UserVolumes) > 0) + if err != nil { + return nil, err + } + if len(hooks) > 0 || config.Hooks != nil { + logrus.Warnf("implicit hook directories are deprecated; set --hooks-dir=%q explicitly to continue to load hooks from this directory", hDir) + } + for i, hook := range hooks { + allHooks[i] = hook + } } + return allHooks, nil } - return allHooks, nil + + manager, err := hooks.New(ctx, c.runtime.config.HooksDir, []string{"poststop"}, lang) + if err != nil { + return nil, err + } + + return manager.Hooks(config, c.Spec().Annotations, len(c.config.UserVolumes) > 0) } // mount mounts the container's root filesystem diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 8861d7728..780bf5279 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -224,10 +224,8 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } } - if !rootless.IsRootless() { - if c.state.ExtensionStageHooks, err = c.setupOCIHooks(ctx, g.Config); err != nil { - return nil, errors.Wrapf(err, "error setting up OCI Hooks") - } + if c.state.ExtensionStageHooks, err = c.setupOCIHooks(ctx, g.Config); err != nil { + return nil, errors.Wrapf(err, "error setting up OCI Hooks") } // Bind builtin image volumes diff --git a/libpod/options.go b/libpod/options.go index 7f4e3ac6b..e1d0b5007 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -179,21 +179,20 @@ func WithStaticDir(dir string) RuntimeOption { } } -// WithHooksDir sets the directory to look for OCI runtime hooks config. -// Note we are not saving this in database, since this is really just for used -// for testing. -func WithHooksDir(hooksDir string) RuntimeOption { +// WithHooksDir sets the directories to look for OCI runtime hook configuration. +func WithHooksDir(hooksDirs ...string) RuntimeOption { return func(rt *Runtime) error { if rt.valid { return ErrRuntimeFinalized } - if hooksDir == "" { - return errors.Wrap(ErrInvalidArg, "empty-string hook directories are not supported") + for _, hooksDir := range hooksDirs { + if hooksDir == "" { + return errors.Wrap(ErrInvalidArg, "empty-string hook directories are not supported") + } } - rt.config.HooksDir = []string{hooksDir} - rt.config.HooksDirNotExistFatal = true + rt.config.HooksDir = hooksDirs return nil } } diff --git a/libpod/runtime.go b/libpod/runtime.go index 9feae03fc..e043715b1 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -12,7 +12,6 @@ import ( "github.com/containers/image/types" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/firewall" - "github.com/containers/libpod/pkg/hooks" sysreg "github.com/containers/libpod/pkg/registries" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/util" @@ -141,11 +140,11 @@ type RuntimeConfig struct { // CNIDefaultNetwork is the network name of the default CNI network // to attach pods to CNIDefaultNetwork string `toml:"cni_default_network,omitempty"` - // HooksDir Path to the directory containing hooks configuration files + // HooksDir holds paths to the directories containing hooks + // configuration files. When the same filename is present in in + // multiple directories, the file in the directory listed last in + // this slice takes precedence. HooksDir []string `toml:"hooks_dir"` - // HooksDirNotExistFatal switches between fatal errors and non-fatal - // warnings if the configured HooksDir does not exist. - HooksDirNotExistFatal bool `toml:"hooks_dir_not_exist_fatal"` // DefaultMountsFile is the path to the default mounts file for testing // purposes only DefaultMountsFile string `toml:"-"` @@ -203,7 +202,6 @@ var ( "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", }, CgroupManager: SystemdCgroupsManager, - HooksDir: []string{hooks.DefaultDir, hooks.OverrideDir}, StaticDir: filepath.Join(storage.DefaultStoreOptions.GraphRoot, "libpod"), TmpDir: "", MaxLogSize: -1, diff --git a/libpod/testdata/config.toml b/libpod/testdata/config.toml index e19d36017..1d78f2083 100644 --- a/libpod/testdata/config.toml +++ b/libpod/testdata/config.toml @@ -14,7 +14,7 @@ seccomp_profile = "/etc/crio/seccomp.json" apparmor_profile = "crio-default" cgroup_manager = "cgroupfs" - hooks_dir_path = "/usr/share/containers/oci/hooks.d" + hooks_dir = ["/usr/share/containers/oci/hooks.d"] pids_limit = 2048 container_exits_dir = "/var/run/podman/exits" [crio.image] diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 38504828b..b04116218 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -336,7 +336,7 @@ var _ = Describe("Podman run", func() { hooksDir := tempdir + "/hooks" os.Mkdir(hooksDir, 0755) fileutils.CopyFile("hooks/hooks.json", hooksDir) - os.Setenv("HOOK_OPTION", fmt.Sprintf("--hooks-dir-path=%s", hooksDir)) + os.Setenv("HOOK_OPTION", fmt.Sprintf("--hooks-dir=%s", hooksDir)) os.Remove(hcheck) session := podmanTest.Podman([]string{"run", ALPINE, "ls"}) session.Wait(10) -- cgit v1.2.3-54-g00ecf From 9c359a31d542074ff686a2f9ad29deee73e92d79 Mon Sep 17 00:00:00 2001 From: baude Date: Fri, 30 Nov 2018 10:23:28 -0600 Subject: create pod on the fly when a user specifies --pod to podman create|run, we should create that pod automatically. the port bindings from the container are then inherited by the infra container. this signicantly improves the workflow of running containers inside pods with podman. the user is still encouraged to use podman pod create to have more granular control of the pod create options. Signed-off-by: baude --- cmd/podman/create.go | 65 +++++++++++++++++++++++++++++++++++++----------- docs/podman-create.1.md | 3 ++- docs/podman-run.1.md | 3 ++- pkg/spec/createconfig.go | 7 +++++- test/e2e/create_test.go | 11 ++++++++ test/e2e/run_test.go | 11 ++++++++ 6 files changed, 82 insertions(+), 18 deletions(-) (limited to 'docs') diff --git a/cmd/podman/create.go b/cmd/podman/create.go index bcf830c7c..c40650f83 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -11,6 +11,7 @@ import ( "syscall" "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" ann "github.com/containers/libpod/pkg/annotations" @@ -375,8 +376,8 @@ func configureEntrypoint(c *cli.Context, data *inspect.ImageData) []string { return entrypoint } -func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string) (map[string]string, error) { - pod, err := runtime.LookupPod(c.String("pod")) +func configurePod(c *cli.Context, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, error) { + pod, err := runtime.LookupPod(podName) if err != nil { return namespaces, err } @@ -409,6 +410,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim inputCommand, command []string memoryLimit, memoryReservation, memorySwap, memoryKernel int64 blkioWeight uint16 + namespaces map[string]string ) idmappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidname"), c.String("subgidname")) if err != nil { @@ -492,12 +494,21 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim return nil, errors.Errorf("--cpu-quota and --cpus cannot be set together") } + // EXPOSED PORTS + var portBindings map[nat.Port][]nat.PortBinding + if data != nil { + portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts) + if err != nil { + return nil, err + } + } + // Kernel Namespaces // TODO Fix handling of namespace from pod // Instead of integrating here, should be done in libpod // However, that also involves setting up security opts // when the pod's namespace is integrated - namespaces := map[string]string{ + namespaces = map[string]string{ "pid": c.String("pid"), "net": c.String("net"), "ipc": c.String("ipc"), @@ -505,8 +516,41 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim "uts": c.String("uts"), } + originalPodName := c.String("pod") + podName := strings.Replace(originalPodName, "new:", "", 1) + // after we strip out :new, make sure there is something left for a pod name + if len(podName) < 1 && c.IsSet("pod") { + return nil, errors.Errorf("new pod name must be at least one character") + } if c.IsSet("pod") { - namespaces, err = configurePod(c, runtime, namespaces) + if strings.HasPrefix(originalPodName, "new:") { + // pod does not exist; lets make it + var podOptions []libpod.PodCreateOption + podOptions = append(podOptions, libpod.WithPodName(podName), libpod.WithInfraContainer(), libpod.WithPodCgroups()) + if len(portBindings) > 0 { + ociPortBindings, err := cc.NatToOCIPortBindings(portBindings) + if err != nil { + return nil, err + } + podOptions = append(podOptions, libpod.WithInfraContainerPorts(ociPortBindings)) + } + + podNsOptions, err := shared.GetNamespaceOptions(strings.Split(DefaultKernelNamespaces, ",")) + if err != nil { + return nil, err + } + podOptions = append(podOptions, podNsOptions...) + // make pod + pod, err := runtime.NewPod(ctx, podOptions...) + if err != nil { + return nil, err + } + logrus.Debugf("pod %s created by new container request", pod.ID()) + + // The container now cannot have port bindings; so we reset the map + portBindings = make(map[nat.Port][]nat.PortBinding) + } + namespaces, err = configurePod(c, runtime, namespaces, podName) if err != nil { return nil, err } @@ -535,7 +579,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim // Make sure if network is set to container namespace, port binding is not also being asked for netMode := ns.NetworkMode(namespaces["net"]) if netMode.IsContainer() { - if len(c.StringSlice("publish")) > 0 || c.Bool("publish-all") { + if len(portBindings) > 0 { return nil, errors.Errorf("cannot set port bindings on an existing container network namespace") } } @@ -644,15 +688,6 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim return nil, errors.Errorf("No command specified on command line or as CMD or ENTRYPOINT in this image") } - // EXPOSED PORTS - var portBindings map[nat.Port][]nat.PortBinding - if data != nil { - portBindings, err = cc.ExposedPorts(c.StringSlice("expose"), c.StringSlice("publish"), c.Bool("publish-all"), data.ContainerConfig.ExposedPorts) - if err != nil { - return nil, err - } - } - // SHM Size shmSize, err := units.FromHumanSize(c.String("shm-size")) if err != nil { @@ -746,7 +781,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim NetMode: netMode, UtsMode: utsMode, PidMode: pidMode, - Pod: c.String("pod"), + Pod: podName, Privileged: c.Bool("privileged"), Publish: c.StringSlice("publish"), PublishAll: c.Bool("publish-all"), diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 6fbdd0d03..f1409a554 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -455,7 +455,8 @@ Tune the container's pids limit. Set `-1` to have unlimited pids for the contain **--pod**="" -Run container in an existing pod +Run container in an existing pod. If you want podman to make the pod for you, preference the pod name with `new:`. +To make a pod with more granular options, use the `podman pod create` command before creating a container. **--privileged**=*true*|*false* diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index a6761a393..5917f6f7a 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -439,7 +439,8 @@ Tune the container's pids limit. Set `-1` to have unlimited pids for the contain **--pod**="" -Run container in an existing pod +Run container in an existing pod. If you want podman to make the pod for you, preference the pod name with `new:`. +To make a pod with more granular options, use the `podman pod create` command before creating a container. **--privileged**=*true*|*false* diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index a0fd40318..25f8cd7a1 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -496,8 +496,13 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib // CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands func (c *CreateConfig) CreatePortBindings() ([]ocicni.PortMapping, error) { + return NatToOCIPortBindings(c.PortBindings) +} + +// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice +func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) { var portBindings []ocicni.PortMapping - for containerPb, hostPb := range c.PortBindings { + for containerPb, hostPb := range ports { var pm ocicni.PortMapping pm.ContainerPort = int32(containerPb.Int()) for _, i := range hostPb { diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 366a58f02..684a7cd88 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -189,4 +189,15 @@ var _ = Describe("Podman create", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring("/create/test rw,nosuid,nodev,noexec,relatime - tmpfs")) }) + + It("podman create --pod automatically", func() { + session := podmanTest.Podman([]string{"create", "--pod", "new:foobar", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"pod", "ps", "--no-trunc"}) + check.WaitWithDefaultTimeout() + match, _ := check.GrepString("foobar") + Expect(match).To(BeTrue()) + }) }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 38504828b..aaee7fa53 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -628,4 +628,15 @@ USER mail` isSharedOnly := !strings.Contains(shared[0], "shared,") Expect(isSharedOnly).Should(BeTrue()) }) + + It("podman run --pod automatically", func() { + session := podmanTest.Podman([]string{"run", "--pod", "new:foobar", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"pod", "ps", "--no-trunc"}) + check.WaitWithDefaultTimeout() + match, _ := check.GrepString("foobar") + Expect(match).To(BeTrue()) + }) }) -- cgit v1.2.3-54-g00ecf