diff options
author | Valentin Rothberg <rothberg@redhat.com> | 2020-06-10 16:26:24 +0200 |
---|---|---|
committer | Valentin Rothberg <rothberg@redhat.com> | 2020-06-15 15:53:51 +0200 |
commit | fa3b8a75c4ec571f8cbb2622ea624b42bc5c2472 (patch) | |
tree | 23b4ebcca03d20cedadfa094d51469d55e756b83 | |
parent | f4c3b718eb22a161a897a6ed55d10f3a07e31aa8 (diff) | |
download | podman-fa3b8a75c4ec571f8cbb2622ea624b42bc5c2472.tar.gz podman-fa3b8a75c4ec571f8cbb2622ea624b42bc5c2472.tar.bz2 podman-fa3b8a75c4ec571f8cbb2622ea624b42bc5c2472.zip |
{create,run} --replace
Add a `--replace` flag to the `container {create,run}` commands.
If another container with the same name already exists, it will
be replaced and removed.
Adding this flag is motivated by #5485 to make running Podman in systemd
units (or any other scripts/automation) more robust. In case of a
crash, a container may not be removed by a sytemd unit anymore. The
`--replace` flag allows for supporting crashes.
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
-rw-r--r-- | cmd/podman/common/create.go | 5 | ||||
-rw-r--r-- | cmd/podman/common/create_opts.go | 1 | ||||
-rw-r--r-- | cmd/podman/containers/create.go | 17 | ||||
-rw-r--r-- | cmd/podman/containers/rm.go | 16 | ||||
-rw-r--r-- | cmd/podman/containers/run.go | 6 | ||||
-rw-r--r-- | docs/source/markdown/podman-create.1.md | 4 | ||||
-rw-r--r-- | docs/source/markdown/podman-run.1.md | 4 | ||||
-rw-r--r-- | test/e2e/create_test.go | 15 | ||||
-rw-r--r-- | test/e2e/run_test.go | 15 |
9 files changed, 80 insertions, 3 deletions
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index e79c5c20b..921cd5a71 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -373,6 +373,11 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "read-only-tmpfs", true, "When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp", ) + createFlags.BoolVar( + &cf.Replace, + "replace", false, + `If a container with the same name exists, replace it`, + ) createFlags.StringVar( &cf.Restart, "restart", "", diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 98dc6744c..49052704e 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -76,6 +76,7 @@ type ContainerCLIOpts struct { ReadOnly bool ReadOnlyTmpFS bool Restart string + Replace bool Rm bool RootFS bool SecurityOpt []string diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index ed09585ba..6269ec781 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -122,6 +122,12 @@ func create(cmd *cobra.Command, args []string) error { return err } + if cliVals.Replace { + if err := replaceContainer(cliVals.Name); err != nil { + return err + } + } + report, err := registry.ContainerEngine().ContainerCreate(registry.GetContext(), s) if err != nil { return err @@ -138,6 +144,17 @@ func create(cmd *cobra.Command, args []string) error { return nil } +func replaceContainer(name string) error { + if len(name) == 0 { + return errors.New("cannot replace container without --name being set") + } + rmOptions := entities.RmOptions{ + Force: true, // force stop & removal + Ignore: true, // ignore errors when a container doesn't exit + } + return removeContainers([]string{name}, rmOptions, false) +} + func createInit(c *cobra.Command) error { if c.Flag("privileged").Changed && c.Flag("security-opt").Changed { logrus.Warn("setting security options with --privileged has no effect") diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index b25473a8d..22d6d59b4 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -87,6 +87,14 @@ func init() { } func rm(cmd *cobra.Command, args []string) error { + return removeContainers(args, rmOptions, true) +} + +// removeContainers will remove the specified containers (names or IDs). +// Allows for sharing removal logic across commands. If setExit is set, +// removeContainers will set the exit code according to the `podman-rm` man +// page. +func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit bool) error { var ( errs utils.OutputErrors ) @@ -96,9 +104,9 @@ func rm(cmd *cobra.Command, args []string) error { return errors.Errorf("--storage conflicts with --volumes, --all, --latest, --ignore and --cidfile") } } - responses, err := registry.ContainerEngine().ContainerRm(context.Background(), args, rmOptions) + responses, err := registry.ContainerEngine().ContainerRm(context.Background(), namesOrIDs, rmOptions) if err != nil { - if len(args) < 2 { + if setExit && len(namesOrIDs) < 2 { setExitCode(err) } return err @@ -109,7 +117,9 @@ func rm(cmd *cobra.Command, args []string) error { if errors.Cause(err) == define.ErrWillDeadlock { logrus.Errorf("Potential deadlock detected - please run 'podman system renumber' to resolve") } - setExitCode(r.Err) + if setExit { + setExitCode(r.Err) + } errs = append(errs, r.Err) } else { fmt.Println(r.Id) diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 8a02c63c0..b9c196b64 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -129,6 +129,12 @@ func run(cmd *cobra.Command, args []string) error { } } + if cliVals.Replace { + if err := replaceContainer(cliVals.Name); err != nil { + return err + } + } + // If -i is not set, clear stdin if !cliVals.Interactive { runOpts.InputStream = nil diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index dbc835920..1da9d72e6 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -662,6 +662,10 @@ its root filesystem mounted as read only prohibiting any writes. If container is running in --read-only mode, then mount a read-write tmpfs on /run, /tmp, and /var/tmp. The default is *true* +**--replace**=**true**|**false** + +If another container with the same name already exists, replace and remove it. The default is **false**. + **--restart**=*policy* Restart policy to follow when containers exit. diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 22f7cae09..3e1ade047 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -671,6 +671,10 @@ its root filesystem mounted as read only prohibiting any writes. If container is running in **--read-only** mode, then mount a read-write tmpfs on _/run_, _/tmp_, and _/var/tmp_. The default is **true**. +**--replace**=**true**|**false** + +If another container with the same name already exists, replace and remove it. The default is **false**. + **--restart**=*policy* Restart policy to follow when containers exit. diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index b9a1ff83d..822e470f2 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -429,4 +429,19 @@ var _ = Describe("Podman create", func() { Expect(len(data)).To(Equal(1)) Expect(data[0].HostConfig.NanoCpus).To(Equal(int64(nanoCPUs))) }) + + It("podman create --replace", func() { + // Make sure we error out with --name. + session := podmanTest.Podman([]string{"create", "--replace", ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + + // Create and replace 5 times in a row the "same" container. + ctrName := "testCtr" + for i := 0; i < 5; i++ { + session = podmanTest.Podman([]string{"create", "--replace", "--name", ctrName, ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + } + }) }) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 59215c7e5..76944b3db 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -931,4 +931,19 @@ USER mail` session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) }) + + It("podman run --replace", func() { + // Make sure we error out with --name. + session := podmanTest.Podman([]string{"create", "--replace", ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(125)) + + // Run and replace 5 times in a row the "same" container. + ctrName := "testCtr" + for i := 0; i < 5; i++ { + session := podmanTest.Podman([]string{"run", "--detach", "--replace", "--name", ctrName, ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + } + }) }) |