diff options
-rw-r--r-- | cmd/podman/main.go | 1 | ||||
-rw-r--r-- | cmd/podman/mount.go | 2 | ||||
-rw-r--r-- | cmd/podman/restart.go | 115 | ||||
-rw-r--r-- | commands.md | 1 | ||||
-rw-r--r-- | completions/bash/podman | 12 | ||||
-rw-r--r-- | docs/podman-restart.1.md | 46 | ||||
-rw-r--r-- | docs/podman.1.md | 1 | ||||
-rw-r--r-- | libpod/container_api.go | 27 | ||||
-rw-r--r-- | test/e2e/restart_test.go | 81 | ||||
-rw-r--r-- | test/e2e/rm_test.go | 19 | ||||
-rw-r--r-- | transfer.md | 62 |
11 files changed, 314 insertions, 53 deletions
diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 4034be267..2a0ca30ee 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -56,6 +56,7 @@ func main() { portCommand, pullCommand, pushCommand, + restartCommand, rmCommand, rmiCommand, runCommand, diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go index 6c6b89ee9..2d274cfaf 100644 --- a/cmd/podman/mount.go +++ b/cmd/podman/mount.go @@ -79,7 +79,7 @@ func mountCmd(c *cli.Context) error { if err != nil { return errors.Wrapf(err, "error looking up container %q", args[0]) } - mountPoint, err := ctr.Mount(ctr.MountLabel()) + mountPoint, err := ctr.Mount() if err != nil { return errors.Wrapf(err, "error mounting container %q", ctr.ID()) } diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go new file mode 100644 index 000000000..84aa5f830 --- /dev/null +++ b/cmd/podman/restart.go @@ -0,0 +1,115 @@ +package main + +import ( + "fmt" + "os" + + "github.com/pkg/errors" + "github.com/projectatomic/libpod/libpod" + "github.com/urfave/cli" +) + +var ( + restartFlags = []cli.Flag{ + cli.UintFlag{ + Name: "timeout, time, t", + Usage: "Seconds to wait for stop before killing the container", + Value: libpod.CtrRemoveTimeout, + }, + LatestFlag, + } + restartDescription = `Restarts one or more running containers. The container ID or name can be used. A timeout before forcibly stopping can be set, but defaults to 10 seconds` + + restartCommand = cli.Command{ + Name: "restart", + Usage: "Restart one or more containers", + Description: restartDescription, + Flags: restartFlags, + Action: restartCmd, + ArgsUsage: "CONTAINER [CONTAINER ...]", + UseShortOptionHandling: true, + } +) + +func restartCmd(c *cli.Context) error { + args := c.Args() + if len(args) < 1 && !c.Bool("latest") { + return errors.Wrapf(libpod.ErrInvalidArg, "you must provide at least one container name or ID") + } + + if err := validateFlags(c, restartFlags); err != nil { + return err + } + + runtime, err := getRuntime(c) + if err != nil { + return errors.Wrapf(err, "error creating libpod runtime") + } + defer runtime.Shutdown(false) + + var lastError error + + timeout := c.Uint("timeout") + useTimeout := c.IsSet("timeout") + + // Handle --latest + if c.Bool("latest") { + lastCtr, err := runtime.GetLatestContainer() + if err != nil { + lastError = errors.Wrapf(err, "unable to get latest container") + } else { + ctrTimeout := lastCtr.StopTimeout() + if useTimeout { + ctrTimeout = timeout + } + + lastError = restartCtr(ctrTimeout, lastCtr) + } + } + + for _, id := range args { + ctr, err := runtime.LookupContainer(id) + if err != nil { + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "unable to find container %s", id) + continue + } + + ctrTimeout := ctr.StopTimeout() + if useTimeout { + ctrTimeout = timeout + } + + if err := restartCtr(ctrTimeout, ctr); err != nil { + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } + lastError = errors.Wrapf(err, "error restarting container %s", ctr.ID()) + } + } + + return lastError +} + +// Restart a single container +func restartCtr(timeout uint, ctr *libpod.Container) error { + state, err := ctr.State() + if err != nil { + return err + } + if state == libpod.ContainerStateRunning { + if err := ctr.StopWithTimeout(timeout); err != nil { + return err + } + } + + if err := ctr.Start(); err != nil { + return err + } + + fmt.Printf("%s\n", ctr.ID()) + + return nil +} diff --git a/commands.md b/commands.md index c2301420a..987f4abfe 100644 --- a/commands.md +++ b/commands.md @@ -29,6 +29,7 @@ | [podman-ps(1)](/docs/podman-ps.1.md) | Prints out information about containers |[![...](/docs/play.png)](https://asciinema.org/a/bbT41kac6CwZ5giESmZLIaTLR)| | [podman-pull(1)](/docs/podman-pull.1.md) | Pull an image from a registry |[![...](/docs/play.png)](https://asciinema.org/a/lr4zfoynHJOUNu1KaXa1dwG2X)| | [podman-push(1)](/docs/podman-push.1.md) | Push an image to a specified destination |[![...](/docs/play.png)](https://asciinema.org/a/133276)| +| [podman-restart](/docs/podman-restart.1.md) | Restarts one or more containers || | [podman-rm(1)](/docs/podman-rm.1.md) | Removes one or more containers |[![...](/docs/play.png)](https://asciinema.org/a/7EMk22WrfGtKWmgHJX9Nze1Qp)| | [podman-rmi(1)](/docs/podman-rmi.1.md) | Removes one or more images |[![...](/docs/play.png)](https://asciinema.org/a/133799)| | [podman-run(1)](/docs/podman-run.1.md) | Run a command in a container || diff --git a/completions/bash/podman b/completions/bash/podman index ae7db9f6d..4313fe94f 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1001,7 +1001,6 @@ _podman_mount() { " local options_with_args=" - --label --format " @@ -1314,6 +1313,16 @@ _podman_run() { _podman_container_run } +_podman_restart() { + local options_with_args=" + --timeout -t + " + local boolean_options=" + --latest + -l" + _complete_ "$options_with_args" "$boolean_options" +} + _podman_rm() { local boolean_options=" --all @@ -1609,6 +1618,7 @@ _podman_podman() { ps pull push + restart rm rmi run diff --git a/docs/podman-restart.1.md b/docs/podman-restart.1.md new file mode 100644 index 000000000..7b9e6173a --- /dev/null +++ b/docs/podman-restart.1.md @@ -0,0 +1,46 @@ +% podman(1) podman-restart - Restart a container +% Matt Heon +# podman-restart "1" "March 2017" "podman" + +## NAME +podman restart - Restart a container + +## SYNOPSIS +**podman attach [OPTIONS] CONTAINER [CONTAINER...]** + +## DESCRIPTION +The restart command allows containers to be restarted using their ID or name. +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 + +**--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. + +## EXAMPLES ## + +``` +podman restart -l +ec588fc80b05e19d3006bf2e8aa325f0a2e2ff1f609b7afb39176ca8e3e13467 +``` + +``` +podman restart ff6cf1 +ff6cf1e5e77e6dba1efc7f3fcdb20e8b89ad8947bc0518be1fcb2c78681f226f +``` + +``` +podman restart --timeout 4 test1 test2 +c3bb026838c30e5097f079fa365c9a4769d52e1017588278fa00d5c68ebc1502 +17e13a63081a995136f907024bcfe50ff532917988a152da229db9d894c5a9ec +``` + +## SEE ALSO +podman(1), podman-run(1), podman-start(1), podman-create(1) + +## HISTORY +March 2018, Originally compiled by Matt Heon <mheon@redhat.com> diff --git a/docs/podman.1.md b/docs/podman.1.md index 60ad16f37..e7639192b 100644 --- a/docs/podman.1.md +++ b/docs/podman.1.md @@ -78,6 +78,7 @@ has the capability to debug pods/images created by crio. | [podman-ps(1)](podman-ps.1.md) | Prints out information about containers. | | [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. | | [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. | +| [podman-restart(1)](podman-restart.1.md) | Restart one or more containers. | | [podman-rm(1)](podman-rm.1.md) | Remove one or more containers. | | [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. | | [podman-run(1)](podman-run.1.md) | Run a command in a container. | diff --git a/libpod/container_api.go b/libpod/container_api.go index cfffa2232..ef94aa4c9 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -368,7 +368,7 @@ func (c *Container) Attach(noStdin bool, keys string) error { // Mount mounts a container's filesystem on the host // The path where the container has been mounted is returned -func (c *Container) Mount(label string) (string, error) { +func (c *Container) Mount() (string, error) { if !c.locked { c.lock.Lock() defer c.lock.Unlock() @@ -378,27 +378,11 @@ func (c *Container) Mount(label string) (string, error) { } } - // return mountpoint if container already mounted - if c.state.Mounted { - return c.state.Mountpoint, nil - } - - mountLabel := label - if label == "" { - mountLabel = c.config.MountLabel - } - mountPoint, err := c.runtime.store.Mount(c.ID(), mountLabel) - if err != nil { + if err := c.mountStorage(); err != nil { return "", err } - c.state.Mountpoint = mountPoint - c.state.Mounted = true - if err := c.save(); err != nil { - return "", err - } - - return mountPoint, nil + return c.state.Mountpoint, nil } // Unmount unmounts a container's filesystem on the host @@ -416,6 +400,11 @@ func (c *Container) Unmount() error { return errors.Wrapf(ErrCtrStateInvalid, "cannot remove storage for container %s as it is running or paused", c.ID()) } + // Check if we have active exec sessions + if len(c.state.ExecSessions) != 0 { + return errors.Wrapf(ErrCtrStateInvalid, "container %s has active exec sessions, refusing to clean up", c.ID()) + } + return c.cleanupStorage() } diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go new file mode 100644 index 000000000..ea03d022f --- /dev/null +++ b/test/e2e/restart_test.go @@ -0,0 +1,81 @@ +package integration + +import ( + "os" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman restart", func() { + var ( + tempdir string + err error + podmanTest PodmanTest + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanCreate(tempdir) + podmanTest.RestoreAllArtifacts() + }) + + AfterEach(func() { + podmanTest.Cleanup() + }) + + It("Podman restart bogus container", func() { + session := podmanTest.Podman([]string{"start", "123"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + }) + + It("Podman restart stopped container by name", func() { + _, exitCode, _ := podmanTest.RunLsContainer("test1") + Expect(exitCode).To(Equal(0)) + + session := podmanTest.Podman([]string{"restart", "test1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("Podman restart stopped container by ID", func() { + session := podmanTest.Podman([]string{"create", "-d", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid := session.OutputToString() + + startSession := podmanTest.Podman([]string{"start", cid}) + startSession.WaitWithDefaultTimeout() + Expect(startSession.ExitCode()).To(Equal(0)) + + session2 := podmanTest.Podman([]string{"restart", cid}) + session2.WaitWithDefaultTimeout() + Expect(session2.ExitCode()).To(Equal(0)) + }) + + It("Podman restart running container", func() { + _ = podmanTest.RunTopContainer("test1") + ok := WaitForContainer(&podmanTest) + Expect(ok).To(BeTrue()) + + session := podmanTest.Podman([]string{"restart", "--latest"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("Podman restart multiple containers", func() { + _, exitCode, _ := podmanTest.RunLsContainer("test1") + Expect(exitCode).To(Equal(0)) + + _, exitCode, _ = podmanTest.RunLsContainer("test2") + Expect(exitCode).To(Equal(0)) + + session := podmanTest.Podman([]string{"restart", "test1", "test2"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) +}) diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go index 910656ef0..1404312d9 100644 --- a/test/e2e/rm_test.go +++ b/test/e2e/rm_test.go @@ -59,7 +59,7 @@ var _ = Describe("Podman rm", func() { Expect(result.ExitCode()).To(Equal(0)) }) - It("podman rm created container", func() { + It("podman rm running container with -f", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -105,4 +105,21 @@ var _ = Describe("Podman rm", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) }) + + It("podman rm the latest container", func() { + session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + _, ec, cid := podmanTest.RunLsContainer("") + Expect(ec).To(Equal(0)) + + result := podmanTest.Podman([]string{"rm", "-l"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + output := result.OutputToString() + Expect(output).To(ContainSubstring(cid)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + }) }) diff --git a/transfer.md b/transfer.md index 53d6c472c..2748004ec 100644 --- a/transfer.md +++ b/transfer.md @@ -38,36 +38,37 @@ There are other equivalents for these tools | Existing Step | PODMAN (and friends) | | :--- | :--- | -| `docker attach` | [`podman exec`](./docs/podman-attach.1.md)| -| `docker build` | [`podman build`](./docs/podman-build.1.md) | -| `docker commit` | [`podman commit`](./docs/podman-commit.1.md)| -| `docker cp` | [`podman mount`](./docs/podman-cp.1.md) **** | -| `docker create` | [`podman create`](./docs/podman-create.1.md) | -| `docker diff` | [`podman diff`](./docs/podman-diff.1.md) | -| `docker export` | [`podman export`](./docs/podman-export.1.md) | -| `docker history`| [`podman history`](./docs/podman-history.1.md)| -| `docker images` | [`podman images`](./docs/podman-images.1.md) | -| `docker import` | [`podman import`](./docs/podman-import.1.md) | -| `docker kill` | [`podman kill`](./docs/podman-kill.1.md) | -| `docker load` | [`podman load`](./docs/podman-load.1.md) | -| `docker login` | [`podman login`](./docs/podman-login.1.md) | -| `docker logout` | [`podman logout`](./docs/podman-logout.1.md) | -| `docker pause` | [`podman pause`](./docs/podman-pause.1.md) | -| `docker ps` | [`podman ps`](./docs/podman-ps.1.md) | -| `docker pull` | [`podman pull`](./docs/podman-pull.1.md) | -| `docker push` | [`podman push`](./docs/podman-push.1.md) | -| `docker rm` | [`podman rm`](./docs/podman-rm.1.md) | -| `docker rmi` | [`podman rmi`](./docs/podman-rmi.1.md) | -| `docker run` | [`podman run`](./docs/podman-run.1.md) | -| `docker save` | [`podman save`](./docs/podman-save.1.md) | -| `docker search` | [`podman search`](./docs/podman-search.1.md) | -| `docker start` | [`podman start`](./docs/podman-start.1.md) | -| `docker stop` | [`podman stop`](./docs/podman-stop.1.md) | -| `docker tag` | [`podman tag`](./docs/podman-tag.1.md) | -| `docker top` | [`podman top`](./docs/podman-top.1.md) | -| `docker unpause`| [`podman unpause`](./docs/podman-unpause.1.md)| -| `docker version`| [`podman version`](./docs/podman-version.1.md)| -| `docker wait` | [`podman wait`](./docs/podman-wait.1.md) | +| `docker attach` | [`podman exec`](./docs/podman-attach.1.md) | +| `docker build` | [`podman build`](./docs/podman-build.1.md) | +| `docker commit` | [`podman commit`](./docs/podman-commit.1.md) | +| `docker cp` | [`podman mount`](./docs/podman-cp.1.md) **** | +| `docker create` | [`podman create`](./docs/podman-create.1.md) | +| `docker diff` | [`podman diff`](./docs/podman-diff.1.md) | +| `docker export` | [`podman export`](./docs/podman-export.1.md) | +| `docker history` | [`podman history`](./docs/podman-history.1.md) | +| `docker images` | [`podman images`](./docs/podman-images.1.md) | +| `docker import` | [`podman import`](./docs/podman-import.1.md) | +| `docker kill` | [`podman kill`](./docs/podman-kill.1.md) | +| `docker load` | [`podman load`](./docs/podman-load.1.md) | +| `docker login` | [`podman login`](./docs/podman-login.1.md) | +| `docker logout` | [`podman logout`](./docs/podman-logout.1.md) | +| `docker pause` | [`podman pause`](./docs/podman-pause.1.md) | +| `docker ps` | [`podman ps`](./docs/podman-ps.1.md) | +| `docker pull` | [`podman pull`](./docs/podman-pull.1.md) | +| `docker push` | [`podman push`](./docs/podman-push.1.md) | +| `docker restart` | [`podman restart`](./docs/podman-restart.1.md)] | +| `docker rm` | [`podman rm`](./docs/podman-rm.1.md) | +| `docker rmi` | [`podman rmi`](./docs/podman-rmi.1.md) | +| `docker run` | [`podman run`](./docs/podman-run.1.md) | +| `docker save` | [`podman save`](./docs/podman-save.1.md) | +| `docker search` | [`podman search`](./docs/podman-search.1.md) | +| `docker start` | [`podman start`](./docs/podman-start.1.md) | +| `docker stop` | [`podman stop`](./docs/podman-stop.1.md) | +| `docker tag` | [`podman tag`](./docs/podman-tag.1.md) | +| `docker top` | [`podman top`](./docs/podman-top.1.md) | +| `docker unpause` | [`podman unpause`](./docs/podman-unpause.1.md) | +| `docker version` | [`podman version`](./docs/podman-version.1.md) | +| `docker wait` | [`podman wait`](./docs/podman-wait.1.md) | **** Use mount to take advantage of the entire linux tool chain rather then just cp. Read [`here`](./docs/podman-cp.1.md) for more information. @@ -85,7 +86,6 @@ Those Docker commands currently do not have equivalents in `podman`: | `docker plugin` |podman does not support plugins. We recommend you use alternative OCI Runtimes or OCI Runtime Hooks to alter behavior of podman.| | `docker port` || | `docker rename` | podman does not support rename, you need to use `podman rm` and `podman create` to rename a container.| -| `docker restart` | podman does not support restart. We recommend that you put your podman containers into a systemd unit file and use it for restarting applications.| | `docker secret` || | `docker service` || | `docker stack` || |