Skip to content

Commit

Permalink
Merge pull request #6553 from vrothberg/replace
Browse files Browse the repository at this point in the history
--replace for containers and pods
  • Loading branch information
openshift-merge-robot authored Jun 15, 2020
2 parents b897297 + 6118ab4 commit 10c6c80
Show file tree
Hide file tree
Showing 18 changed files with 252 additions and 28 deletions.
5 changes: 5 additions & 0 deletions cmd/podman/common/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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", "",
Expand Down
1 change: 1 addition & 0 deletions cmd/podman/common/create_opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type ContainerCLIOpts struct {
ReadOnly bool
ReadOnlyTmpFS bool
Restart string
Replace bool
Rm bool
RootFS bool
SecurityOpt []string
Expand Down
17 changes: 17 additions & 0 deletions cmd/podman/containers/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand Down
16 changes: 13 additions & 3 deletions cmd/podman/containers/rm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand All @@ -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
Expand All @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions cmd/podman/containers/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions cmd/podman/pods/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var (
createOptions entities.PodCreateOptions
labels, labelFile []string
podIDFile string
replace bool
share string
)

Expand All @@ -61,6 +62,7 @@ func init() {
flags.StringVarP(&createOptions.Name, "name", "n", "", "Assign a name to the pod")
flags.StringVarP(&createOptions.Hostname, "hostname", "", "", "Set a hostname to the pod")
flags.StringVar(&podIDFile, "pod-id-file", "", "Write the pod ID to the file")
flags.BoolVar(&replace, "replace", false, "If a pod with the same exists, replace it")
flags.StringVar(&share, "share", specgen.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share")
flags.SetNormalizeFunc(aliasNetworkFlag)
}
Expand Down Expand Up @@ -147,6 +149,12 @@ func create(cmd *cobra.Command, args []string) error {
}
}

if replace {
if err := replacePod(createOptions.Name); err != nil {
return err
}
}

response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions)
if err != nil {
return err
Expand All @@ -159,3 +167,14 @@ func create(cmd *cobra.Command, args []string) error {
fmt.Println(response.Id)
return nil
}

func replacePod(name string) error {
if len(name) == 0 {
return errors.New("cannot replace pod without --name being set")
}
rmOptions := entities.PodRmOptions{
Force: true, // stop and remove pod
Ignore: true, // ignore if pod doesn't exist
}
return removePods([]string{name}, rmOptions, false)
}
18 changes: 12 additions & 6 deletions cmd/podman/pods/rm.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,30 @@ func init() {
}

func rm(cmd *cobra.Command, args []string) error {
var (
errs utils.OutputErrors
)

ids, err := common.ReadPodIDFiles(rmOptions.PodIDFiles)
if err != nil {
return err
}
args = append(args, ids...)
return removePods(args, rmOptions.PodRmOptions, true)
}

responses, err := registry.ContainerEngine().PodRm(context.Background(), args, rmOptions.PodRmOptions)
// removePods removes the specified pods (names or IDs). Allows for sharing
// pod-removal logic across commands.
func removePods(namesOrIDs []string, rmOptions entities.PodRmOptions, printIDs bool) error {
var errs utils.OutputErrors

responses, err := registry.ContainerEngine().PodRm(context.Background(), namesOrIDs, rmOptions)
if err != nil {
return err
}

// in the cli, first we print out all the successful attempts
for _, r := range responses {
if r.Err == nil {
fmt.Println(r.Id)
if printIDs {
fmt.Println(r.Id)
}
} else {
errs = append(errs, r.Err)
}
Expand Down
1 change: 1 addition & 0 deletions completions/bash/podman
Original file line number Diff line number Diff line change
Expand Up @@ -3118,6 +3118,7 @@ _podman_pod_create() {
--help
-h
--infra
--replace
"
_complete_ "$options_with_args" "$boolean_options"
}
Expand Down
4 changes: 4 additions & 0 deletions docs/source/markdown/podman-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions docs/source/markdown/podman-pod-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPO

NOTE: This cannot be modified once the pod is created.

**--replace**=**true**|**false**

If another pod with the same name already exists, replace and remove it. The default is **false**.

**--share**=*namespace*

A comma delimited 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.
Expand Down
4 changes: 4 additions & 0 deletions docs/source/markdown/podman-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
38 changes: 28 additions & 10 deletions pkg/systemd/generate/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,24 +207,42 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
info.CreateCommand = filterPodFlags(info.CreateCommand)
}

// Enforce detaching
//
// since we use systemd `Type=forking` service
// @see https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=
// when we generated systemd service file with the --new param,
// `ExecStart` will have `/usr/bin/podman run ...`
// if `info.CreateCommand` has no `-d` or `--detach` param,
// podman will run the container in default attached mode,
// as a result, `systemd start` will wait the `podman run` command exit until failed with timeout error.
// Presence check for certain flags/options.
hasDetachParam := false
hasNameParam := false
hasReplaceParam := false
for _, p := range info.CreateCommand[index:] {
if p == "--detach" || p == "-d" {
switch p {
case "--detach", "-d":
hasDetachParam = true
case "--name":
hasNameParam = true
case "--replace":
hasReplaceParam = true
}
}

if !hasDetachParam {
// Enforce detaching
//
// since we use systemd `Type=forking` service @see
// https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=
// when we generated systemd service file with the
// --new param, `ExecStart` will have `/usr/bin/podman
// run ...` if `info.CreateCommand` has no `-d` or
// `--detach` param, podman will run the container in
// default attached mode, as a result, `systemd start`
// will wait the `podman run` command exit until failed
// with timeout error.
startCommand = append(startCommand, "-d")
}
if hasNameParam && !hasReplaceParam {
// Enforce --replace for named containers. This will
// make systemd units more robuts as it allows them to
// start after system crashes (see
// github.com/containers/libpod/issues/5485).
startCommand = append(startCommand, "--replace")
}
startCommand = append(startCommand, info.CreateCommand[index:]...)

info.ExecStartPre = "/usr/bin/rm -f {{.PIDFile}} {{.ContainerIDFile}}"
Expand Down
36 changes: 30 additions & 6 deletions pkg/systemd/generate/containers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Type=forking
[Install]
WantedBy=multi-user.target default.target`

goodNameNew := `# jadda-jadda.service
goodWithNameAndGeneric := `# jadda-jadda.service
# autogenerated by Podman CI
[Unit]
Expand All @@ -116,7 +116,30 @@ After=network-online.target
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStartPre=/usr/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id
ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42
ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id
PIDFile=%t/jadda-jadda.pid
KillMode=none
Type=forking
[Install]
WantedBy=multi-user.target default.target`

goodWithExplicitShortDetachParam := `# jadda-jadda.service
# autogenerated by Podman CI
[Unit]
Description=Podman jadda-jadda.service
Documentation=man:podman-generate-systemd(1)
Wants=network.target
After=network-online.target
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStartPre=/usr/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id
ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42
ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id
PIDFile=%t/jadda-jadda.pid
Expand All @@ -139,7 +162,7 @@ After=network-online.target
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStartPre=/usr/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id
ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --pod-id-file /tmp/pod-foobar.pod-id-file -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --pod-id-file /tmp/pod-foobar.pod-id-file --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42
ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id
PIDFile=%t/jadda-jadda.pid
Expand All @@ -148,6 +171,7 @@ Type=forking
[Install]
WantedBy=multi-user.target default.target`

goodNameNewDetach := `# jadda-jadda.service
# autogenerated by Podman CI
Expand All @@ -161,7 +185,7 @@ After=network-online.target
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStartPre=/usr/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id
ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --replace --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42
ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id
PIDFile=%t/jadda-jadda.pid
Expand Down Expand Up @@ -274,7 +298,7 @@ WantedBy=multi-user.target default.target`
CreateCommand: []string{"I'll get stripped", "container", "run", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"},
EnvVariable: EnvVariable,
},
goodNameNew,
goodWithNameAndGeneric,
true,
false,
},
Expand All @@ -290,7 +314,7 @@ WantedBy=multi-user.target default.target`
CreateCommand: []string{"I'll get stripped", "container", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"},
EnvVariable: EnvVariable,
},
goodNameNew,
goodWithExplicitShortDetachParam,
true,
false,
},
Expand Down
17 changes: 16 additions & 1 deletion pkg/systemd/generate/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
if podCreateIndex == 0 {
return "", errors.Errorf("pod does not appear to be created via `podman pod create`: %v", info.CreateCommand)
}
podRootArgs = info.CreateCommand[1 : podCreateIndex-2]
podRootArgs = info.CreateCommand[0 : podCreateIndex-2]
podCreateArgs = filterPodFlags(info.CreateCommand[podCreateIndex+1:])
}
// We're hard-coding the first five arguments and append the
Expand All @@ -277,6 +277,21 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
"--infra-conmon-pidfile", "{{.PIDFile}}",
"--pod-id-file", "{{.PodIDFile}}"}...)

// Presence check for certain flags/options.
hasNameParam := false
hasReplaceParam := false
for _, p := range podCreateArgs {
switch p {
case "--name":
hasNameParam = true
case "--replace":
hasReplaceParam = true
}
}
if hasNameParam && !hasReplaceParam {
podCreateArgs = append(podCreateArgs, "--replace")
}

startCommand = append(startCommand, podCreateArgs...)

info.ExecStartPre1 = "/usr/bin/rm -f {{.PIDFile}} {{.PodIDFile}}"
Expand Down
Loading

0 comments on commit 10c6c80

Please sign in to comment.