Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make restart parallel and add --all #1738

Merged
merged 1 commit into from
Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 68 additions & 27 deletions cmd/podman/restart.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
package main

import (
"context"
"fmt"
"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 (
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{
Name: "timeout, time, t",
Usage: "Seconds to wait for stop before killing the container",
Expand All @@ -35,11 +43,19 @@ var (
)

func restartCmd(c *cli.Context) error {
var (
restartFuncs []shared.ParallelWorkerInput
containers []*libpod.Container
lastError error
restartContainers []*libpod.Container
)

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")
}

if err := validateFlags(c, restartFlags); err != nil {
return err
}
Expand All @@ -50,48 +66,73 @@ func restartCmd(c *cli.Context) error {
}
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 = lastCtr.RestartWithTimeout(context.TODO(), ctrTimeout)
return errors.Wrapf(err, "unable to get latest container")
}
}

for _, id := range args {
ctr, err := runtime.LookupContainer(id)
restartContainers = append(restartContainers, lastCtr)
} else if runOnly {
containers, err = getAllOrLatestContainers(c, runtime, libpod.ContainerStateRunning, "running")
if err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
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
}
lastError = errors.Wrapf(err, "unable to find container %s", id)
continue
restartContainers = append(restartContainers, ctr)
}
}

// We now have a slice of all the containers to be restarted. Iterate them to
// create restart Funcs with a timeout as needed
for _, ctr := range restartContainers {
con := ctr
ctrTimeout := ctr.StopTimeout()
if useTimeout {
ctrTimeout = timeout
}

if err := ctr.RestartWithTimeout(context.TODO(), ctrTimeout); err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "error restarting container %s", ctr.ID())
f := func() error {
return con.RestartWithTimeout(getContext(), ctrTimeout)
}

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
}
18 changes: 10 additions & 8 deletions cmd/podman/shared/parallel.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,22 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) map
func Parallelize(job string) int {
numCpus := runtime.NumCPU()
switch job {
case "stop":
if numCpus <= 2 {
return 4
} else {
return numCpus * 3
}
case "ps":
return 8
case "restart":
return numCpus * 2
case "rm":
if numCpus <= 3 {
return numCpus * 3
} else {
return numCpus * 4
}
case "ps":
return 8
case "stop":
if numCpus <= 2 {
return 4
} else {
return numCpus * 3
}
}
return 3
}
7 changes: 6 additions & 1 deletion completions/bash/podman
Original file line number Diff line number Diff line change
Expand Up @@ -1770,8 +1770,13 @@ _podman_restart() {
--timeout -t
"
local boolean_options="
--all
-a
--latest
-l"
-l
--running
--timeout
-t"
case "$cur" in
-*)
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
Expand Down
26 changes: 22 additions & 4 deletions docs/podman-restart.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
40 changes: 40 additions & 0 deletions test/e2e/restart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,44 @@ var _ = Describe("Podman restart", func() {
Expect(timeSince < 10*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])))
})
})