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 <bbaude@redhat.com>
This commit is contained in:
baude
2018-10-29 12:06:48 -05:00
parent 9c1985fc4e
commit 2011782d9d
5 changed files with 147 additions and 41 deletions

View File

@ -1,18 +1,26 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"os"
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var ( var (
restartFlags = []cli.Flag{ restartFlags = []cli.Flag{
cli.BoolFlag{
Name: "all, a",
Usage: "restart all non-running containers",
},
cli.BoolFlag{
Name: "running",
Usage: "restart only running containers when --all is used",
},
cli.UintFlag{ cli.UintFlag{
Name: "timeout, time, t", Name: "timeout, time, t",
Usage: "Seconds to wait for stop before killing the container", Usage: "Seconds to wait for stop before killing the container",
@ -35,11 +43,19 @@ var (
) )
func restartCmd(c *cli.Context) error { func restartCmd(c *cli.Context) error {
var (
restartFuncs []shared.ParallelWorkerInput
containers []*libpod.Container
lastError error
restartContainers []*libpod.Container
)
args := c.Args() args := c.Args()
if len(args) < 1 && !c.Bool("latest") { runOnly := c.Bool("running")
all := c.Bool("all")
if len(args) < 1 && !c.Bool("latest") && !all {
return errors.Wrapf(libpod.ErrInvalidArg, "you must provide at least one container name or ID") return errors.Wrapf(libpod.ErrInvalidArg, "you must provide at least one container name or ID")
} }
if err := validateFlags(c, restartFlags); err != nil { if err := validateFlags(c, restartFlags); err != nil {
return err return err
} }
@ -50,8 +66,6 @@ func restartCmd(c *cli.Context) error {
} }
defer runtime.Shutdown(false) defer runtime.Shutdown(false)
var lastError error
timeout := c.Uint("timeout") timeout := c.Uint("timeout")
useTimeout := c.IsSet("timeout") useTimeout := c.IsSet("timeout")
@ -59,39 +73,66 @@ func restartCmd(c *cli.Context) error {
if c.Bool("latest") { if c.Bool("latest") {
lastCtr, err := runtime.GetLatestContainer() lastCtr, err := runtime.GetLatestContainer()
if err != nil { if err != nil {
lastError = errors.Wrapf(err, "unable to get latest container") return errors.Wrapf(err, "unable to get latest container")
} else { }
ctrTimeout := lastCtr.StopTimeout() restartContainers = append(restartContainers, lastCtr)
if useTimeout { } else if runOnly {
ctrTimeout = timeout containers, err = getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
if err != nil {
return err
}
restartContainers = append(restartContainers, containers...)
} else if all {
containers, err = runtime.GetAllContainers()
if err != nil {
return err
}
restartContainers = append(restartContainers, containers...)
} else {
for _, id := range args {
ctr, err := runtime.LookupContainer(id)
if err != nil {
return err
} }
restartContainers = append(restartContainers, ctr)
lastError = lastCtr.RestartWithTimeout(context.TODO(), ctrTimeout)
} }
} }
for _, id := range args { // We now have a slice of all the containers to be restarted. Iterate them to
ctr, err := runtime.LookupContainer(id) // create restart Funcs with a timeout as needed
if err != nil { for _, ctr := range restartContainers {
if lastError != nil { con := ctr
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "unable to find container %s", id)
continue
}
ctrTimeout := ctr.StopTimeout() ctrTimeout := ctr.StopTimeout()
if useTimeout { if useTimeout {
ctrTimeout = timeout ctrTimeout = timeout
} }
if err := ctr.RestartWithTimeout(context.TODO(), ctrTimeout); err != nil { f := func() error {
if lastError != nil { return con.RestartWithTimeout(getContext(), ctrTimeout)
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "error restarting container %s", ctr.ID())
} }
restartFuncs = append(restartFuncs, shared.ParallelWorkerInput{
ContainerID: con.ID(),
ParallelFunc: f,
})
} }
maxWorkers := shared.Parallelize("restart")
if c.GlobalIsSet("max-workers") {
maxWorkers = c.GlobalInt("max-workers")
}
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 return lastError
} }

View File

@ -72,20 +72,22 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map
func Parallelize(job string) int { func Parallelize(job string) int {
numCpus := runtime.NumCPU() numCpus := runtime.NumCPU()
switch job { switch job {
case "stop": case "ps":
if numCpus <= 2 { return 8
return 4 case "restart":
} else { return numCpus * 2
return numCpus * 3
}
case "rm": case "rm":
if numCpus <= 3 { if numCpus <= 3 {
return numCpus * 3 return numCpus * 3
} else { } else {
return numCpus * 4 return numCpus * 4
} }
case "ps": case "stop":
return 8 if numCpus <= 2 {
return 4
} else {
return numCpus * 3
}
} }
return 3 return 3
} }

View File

@ -1770,8 +1770,13 @@ _podman_restart() {
--timeout -t --timeout -t
" "
local boolean_options=" local boolean_options="
--all
-a
--latest --latest
-l" -l
--running
--timeout
-t"
case "$cur" in case "$cur" in
-*) -*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur")) COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))

View File

@ -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. containers will not be stopped and will only be started.
## OPTIONS ## OPTIONS
**--timeout** **--all, -a**
Restart all containers regardless of their current state.
Timeout to wait before forcibly stopping the container
**--latest, -l** **--latest, -l**
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman 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. 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 ## ## EXAMPLES ##
Restart the latest container
``` ```
$ podman restart -l $ podman restart -l
ec588fc80b05e19d3006bf2e8aa325f0a2e2ff1f609b7afb39176ca8e3e13467 ec588fc80b05e19d3006bf2e8aa325f0a2e2ff1f609b7afb39176ca8e3e13467
``` ```
Restart a specific container by partial container ID
``` ```
$ podman restart ff6cf1 $ podman restart ff6cf1
ff6cf1e5e77e6dba1efc7f3fcdb20e8b89ad8947bc0518be1fcb2c78681f226f ff6cf1e5e77e6dba1efc7f3fcdb20e8b89ad8947bc0518be1fcb2c78681f226f
``` ```
Restart two containers by name with a timeout of 4 seconds
``` ```
$ podman restart --timeout 4 test1 test2 $ podman restart --timeout 4 test1 test2
c3bb026838c30e5097f079fa365c9a4769d52e1017588278fa00d5c68ebc1502 c3bb026838c30e5097f079fa365c9a4769d52e1017588278fa00d5c68ebc1502
17e13a63081a995136f907024bcfe50ff532917988a152da229db9d894c5a9ec 17e13a63081a995136f907024bcfe50ff532917988a152da229db9d894c5a9ec
``` ```
Restart all running containers
```
$ podman restart --running
```
Restart all containers
```
$ podman restart --all
```
## SEE ALSO ## SEE ALSO
podman(1), podman-run(1), podman-start(1), podman-create(1) podman(1), podman-run(1), podman-start(1), podman-create(1)

View File

@ -136,4 +136,44 @@ var _ = Describe("Podman restart", func() {
Expect(timeSince < 10*time.Second).To(BeTrue()) Expect(timeSince < 10*time.Second).To(BeTrue())
Expect(timeSince > 2*time.Second).To(BeTrue()) Expect(timeSince > 2*time.Second).To(BeTrue())
}) })
It("Podman restart --all", func() {
_, exitCode, _ := podmanTest.RunLsContainer("test1")
Expect(exitCode).To(Equal(0))
test2 := podmanTest.RunTopContainer("test2")
test2.WaitWithDefaultTimeout()
Expect(test2.ExitCode()).To(Equal(0))
startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
startTime.WaitWithDefaultTimeout()
session := podmanTest.Podman([]string{"restart", "-all"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
restartTime.WaitWithDefaultTimeout()
Expect(restartTime.OutputToStringArray()[0]).To(Not(Equal(startTime.OutputToStringArray()[0])))
Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1])))
})
It("Podman restart --all --running", func() {
_, exitCode, _ := podmanTest.RunLsContainer("test1")
Expect(exitCode).To(Equal(0))
test2 := podmanTest.RunTopContainer("test2")
test2.WaitWithDefaultTimeout()
Expect(test2.ExitCode()).To(Equal(0))
startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
startTime.WaitWithDefaultTimeout()
session := podmanTest.Podman([]string{"restart", "-a", "--running"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
restartTime.WaitWithDefaultTimeout()
Expect(restartTime.OutputToStringArray()[0]).To(Equal(startTime.OutputToStringArray()[0]))
Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1])))
})
}) })