mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #6178 from calfonso/freezer-pr-integration
Freezer integration to support container pause/unpause
This commit is contained in:
commit
048833c246
9 changed files with 227 additions and 0 deletions
|
@ -65,6 +65,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
||||||
{"login", "Register or Login to the docker registry server"},
|
{"login", "Register or Login to the docker registry server"},
|
||||||
{"logs", "Fetch the logs of a container"},
|
{"logs", "Fetch the logs of a container"},
|
||||||
{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
|
{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
|
||||||
|
{"pause", "Pause all processes within a container"},
|
||||||
{"ps", "List containers"},
|
{"ps", "List containers"},
|
||||||
{"pull", "Pull an image or a repository from the docker registry server"},
|
{"pull", "Pull an image or a repository from the docker registry server"},
|
||||||
{"push", "Push an image or a repository to the docker registry server"},
|
{"push", "Push an image or a repository to the docker registry server"},
|
||||||
|
@ -78,6 +79,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
||||||
{"stop", "Stop a running container"},
|
{"stop", "Stop a running container"},
|
||||||
{"tag", "Tag an image into a repository"},
|
{"tag", "Tag an image into a repository"},
|
||||||
{"top", "Lookup the running processes of a container"},
|
{"top", "Lookup the running processes of a container"},
|
||||||
|
{"unpause", "Unpause a paused container"},
|
||||||
{"version", "Show the docker version information"},
|
{"version", "Show the docker version information"},
|
||||||
{"wait", "Block until a container stops, then print its exit code"},
|
{"wait", "Block until a container stops, then print its exit code"},
|
||||||
} {
|
} {
|
||||||
|
@ -648,6 +650,52 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cli *DockerCli) CmdUnpause(args ...string) error {
|
||||||
|
cmd := cli.Subcmd("unpause", "CONTAINER", "Unpause all processes within a container")
|
||||||
|
if err := cmd.Parse(args); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.NArg() != 1 {
|
||||||
|
cmd.Usage()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var encounteredError error
|
||||||
|
for _, name := range cmd.Args() {
|
||||||
|
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil {
|
||||||
|
fmt.Fprintf(cli.err, "%s\n", err)
|
||||||
|
encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(cli.out, "%s\n", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return encounteredError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cli *DockerCli) CmdPause(args ...string) error {
|
||||||
|
cmd := cli.Subcmd("pause", "CONTAINER", "Pause all processes within a container")
|
||||||
|
if err := cmd.Parse(args); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.NArg() != 1 {
|
||||||
|
cmd.Usage()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var encounteredError error
|
||||||
|
for _, name := range cmd.Args() {
|
||||||
|
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil {
|
||||||
|
fmt.Fprintf(cli.err, "%s\n", err)
|
||||||
|
encounteredError = fmt.Errorf("Error: failed to pause container named %s", name)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(cli.out, "%s\n", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return encounteredError
|
||||||
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) CmdInspect(args ...string) error {
|
func (cli *DockerCli) CmdInspect(args ...string) error {
|
||||||
cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container/image")
|
cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container/image")
|
||||||
tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.")
|
tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.")
|
||||||
|
|
|
@ -165,6 +165,36 @@ func postContainersKill(eng *engine.Engine, version version.Version, w http.Resp
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func postContainersPause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
job := eng.Job("pause", vars["name"])
|
||||||
|
if err := job.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func postContainersUnpause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
|
if vars == nil {
|
||||||
|
return fmt.Errorf("Missing parameter")
|
||||||
|
}
|
||||||
|
if err := parseForm(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
job := eng.Job("unpause", vars["name"])
|
||||||
|
if err := job.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
func getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||||
if vars == nil {
|
if vars == nil {
|
||||||
return fmt.Errorf("Missing parameter")
|
return fmt.Errorf("Missing parameter")
|
||||||
|
@ -1087,6 +1117,8 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, dockerVersion st
|
||||||
"/images/{name:.*}/tag": postImagesTag,
|
"/images/{name:.*}/tag": postImagesTag,
|
||||||
"/containers/create": postContainersCreate,
|
"/containers/create": postContainersCreate,
|
||||||
"/containers/{name:.*}/kill": postContainersKill,
|
"/containers/{name:.*}/kill": postContainersKill,
|
||||||
|
"/containers/{name:.*}/pause": postContainersPause,
|
||||||
|
"/containers/{name:.*}/unpause": postContainersUnpause,
|
||||||
"/containers/{name:.*}/restart": postContainersRestart,
|
"/containers/{name:.*}/restart": postContainersRestart,
|
||||||
"/containers/{name:.*}/start": postContainersStart,
|
"/containers/{name:.*}/start": postContainersStart,
|
||||||
"/containers/{name:.*}/stop": postContainersStop,
|
"/containers/{name:.*}/stop": postContainersStop,
|
||||||
|
|
|
@ -538,12 +538,37 @@ func (container *Container) KillSig(sig int) error {
|
||||||
container.Lock()
|
container.Lock()
|
||||||
defer container.Unlock()
|
defer container.Unlock()
|
||||||
|
|
||||||
|
// We could unpause the container for them rather than returning this error
|
||||||
|
if container.State.IsPaused() {
|
||||||
|
return fmt.Errorf("Container %s is paused. Unpause the container before stopping", container.ID)
|
||||||
|
}
|
||||||
|
|
||||||
if !container.State.IsRunning() {
|
if !container.State.IsRunning() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return container.daemon.Kill(container, sig)
|
return container.daemon.Kill(container, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (container *Container) Pause() error {
|
||||||
|
if container.State.IsPaused() {
|
||||||
|
return fmt.Errorf("Container %s is already paused", container.ID)
|
||||||
|
}
|
||||||
|
if !container.State.IsRunning() {
|
||||||
|
return fmt.Errorf("Container %s is not running", container.ID)
|
||||||
|
}
|
||||||
|
return container.daemon.Pause(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) Unpause() error {
|
||||||
|
if !container.State.IsPaused() {
|
||||||
|
return fmt.Errorf("Container %s is not paused", container.ID)
|
||||||
|
}
|
||||||
|
if !container.State.IsRunning() {
|
||||||
|
return fmt.Errorf("Container %s is not running", container.ID)
|
||||||
|
}
|
||||||
|
return container.daemon.Unpause(container)
|
||||||
|
}
|
||||||
|
|
||||||
func (container *Container) Kill() error {
|
func (container *Container) Kill() error {
|
||||||
if !container.State.IsRunning() {
|
if !container.State.IsRunning() {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1014,6 +1014,22 @@ func (daemon *Daemon) Run(c *Container, pipes *execdriver.Pipes, startCallback e
|
||||||
return daemon.execDriver.Run(c.command, pipes, startCallback)
|
return daemon.execDriver.Run(c.command, pipes, startCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (daemon *Daemon) Pause(c *Container) error {
|
||||||
|
if err := daemon.execDriver.Pause(c.command); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.State.SetPaused()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (daemon *Daemon) Unpause(c *Container) error {
|
||||||
|
if err := daemon.execDriver.Unpause(c.command); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.State.SetUnpaused()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) Kill(c *Container, sig int) error {
|
func (daemon *Daemon) Kill(c *Container, sig int) error {
|
||||||
return daemon.execDriver.Kill(c.command, sig)
|
return daemon.execDriver.Kill(c.command, sig)
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,8 @@ type TtyTerminal interface {
|
||||||
type Driver interface {
|
type Driver interface {
|
||||||
Run(c *Command, pipes *Pipes, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code
|
Run(c *Command, pipes *Pipes, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code
|
||||||
Kill(c *Command, sig int) error
|
Kill(c *Command, sig int) error
|
||||||
|
Pause(c *Command) error
|
||||||
|
Unpause(c *Command) error
|
||||||
Name() string // Driver name
|
Name() string // Driver name
|
||||||
Info(id string) Info // "temporary" hack (until we move state from core to plugins)
|
Info(id string) Info // "temporary" hack (until we move state from core to plugins)
|
||||||
GetPidsForContainer(id string) ([]int, error) // Returns a list of pids for the given container.
|
GetPidsForContainer(id string) ([]int, error) // Returns a list of pids for the given container.
|
||||||
|
|
|
@ -218,6 +218,30 @@ func (d *driver) Kill(c *execdriver.Command, sig int) error {
|
||||||
return KillLxc(c.ID, sig)
|
return KillLxc(c.ID, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *driver) Pause(c *execdriver.Command) error {
|
||||||
|
_, err := exec.LookPath("lxc-freeze")
|
||||||
|
if err == nil {
|
||||||
|
output, errExec := exec.Command("lxc-freeze", "-n", c.ID).CombinedOutput()
|
||||||
|
if errExec != nil {
|
||||||
|
return fmt.Errorf("Err: %s Output: %s", errExec, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Unpause(c *execdriver.Command) error {
|
||||||
|
_, err := exec.LookPath("lxc-unfreeze")
|
||||||
|
if err == nil {
|
||||||
|
output, errExec := exec.Command("lxc-unfreeze", "-n", c.ID).CombinedOutput()
|
||||||
|
if errExec != nil {
|
||||||
|
return fmt.Errorf("Err: %s Output: %s", errExec, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (d *driver) Terminate(c *execdriver.Command) error {
|
func (d *driver) Terminate(c *execdriver.Command) error {
|
||||||
return KillLxc(c.ID, 9)
|
return KillLxc(c.ID, 9)
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,6 +145,30 @@ func (d *driver) Kill(p *execdriver.Command, sig int) error {
|
||||||
return syscall.Kill(p.Process.Pid, syscall.Signal(sig))
|
return syscall.Kill(p.Process.Pid, syscall.Signal(sig))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *driver) Pause(c *execdriver.Command) error {
|
||||||
|
active := d.activeContainers[c.ID]
|
||||||
|
if active == nil {
|
||||||
|
return fmt.Errorf("active container for %s does not exist", c.ID)
|
||||||
|
}
|
||||||
|
active.container.Cgroups.Freezer = "FROZEN"
|
||||||
|
if systemd.UseSystemd() {
|
||||||
|
return systemd.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer)
|
||||||
|
}
|
||||||
|
return fs.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Unpause(c *execdriver.Command) error {
|
||||||
|
active := d.activeContainers[c.ID]
|
||||||
|
if active == nil {
|
||||||
|
return fmt.Errorf("active container for %s does not exist", c.ID)
|
||||||
|
}
|
||||||
|
active.container.Cgroups.Freezer = "THAWED"
|
||||||
|
if systemd.UseSystemd() {
|
||||||
|
return systemd.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer)
|
||||||
|
}
|
||||||
|
return fs.Freeze(active.container.Cgroups, active.container.Cgroups.Freezer)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *driver) Terminate(p *execdriver.Command) error {
|
func (d *driver) Terminate(p *execdriver.Command) error {
|
||||||
// lets check the start time for the process
|
// lets check the start time for the process
|
||||||
started, err := d.readStartTime(p)
|
started, err := d.readStartTime(p)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
type State struct {
|
type State struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
Running bool
|
Running bool
|
||||||
|
Paused bool
|
||||||
Pid int
|
Pid int
|
||||||
ExitCode int
|
ExitCode int
|
||||||
StartedAt time.Time
|
StartedAt time.Time
|
||||||
|
@ -23,6 +24,9 @@ func (s *State) String() string {
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
|
|
||||||
if s.Running {
|
if s.Running {
|
||||||
|
if s.Paused {
|
||||||
|
return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
|
||||||
|
}
|
||||||
return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
|
return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
|
||||||
}
|
}
|
||||||
if s.FinishedAt.IsZero() {
|
if s.FinishedAt.IsZero() {
|
||||||
|
@ -50,6 +54,7 @@ func (s *State) SetRunning(pid int) {
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
s.Running = true
|
s.Running = true
|
||||||
|
s.Paused = false
|
||||||
s.ExitCode = 0
|
s.ExitCode = 0
|
||||||
s.Pid = pid
|
s.Pid = pid
|
||||||
s.StartedAt = time.Now().UTC()
|
s.StartedAt = time.Now().UTC()
|
||||||
|
@ -64,3 +69,22 @@ func (s *State) SetStopped(exitCode int) {
|
||||||
s.FinishedAt = time.Now().UTC()
|
s.FinishedAt = time.Now().UTC()
|
||||||
s.ExitCode = exitCode
|
s.ExitCode = exitCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *State) SetPaused() {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
s.Paused = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) SetUnpaused() {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
s.Paused = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *State) IsPaused() bool {
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
|
|
||||||
|
return s.Paused
|
||||||
|
}
|
||||||
|
|
|
@ -125,6 +125,8 @@ func InitServer(job *engine.Job) engine.Status {
|
||||||
"restart": srv.ContainerRestart,
|
"restart": srv.ContainerRestart,
|
||||||
"start": srv.ContainerStart,
|
"start": srv.ContainerStart,
|
||||||
"kill": srv.ContainerKill,
|
"kill": srv.ContainerKill,
|
||||||
|
"pause": srv.ContainerPause,
|
||||||
|
"unpause": srv.ContainerUnpause,
|
||||||
"wait": srv.ContainerWait,
|
"wait": srv.ContainerWait,
|
||||||
"tag": srv.ImageTag, // FIXME merge with "image_tag"
|
"tag": srv.ImageTag, // FIXME merge with "image_tag"
|
||||||
"resize": srv.ContainerResize,
|
"resize": srv.ContainerResize,
|
||||||
|
@ -168,6 +170,36 @@ func InitServer(job *engine.Job) engine.Status {
|
||||||
return engine.StatusOK
|
return engine.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (srv *Server) ContainerPause(job *engine.Job) engine.Status {
|
||||||
|
if len(job.Args) != 1 {
|
||||||
|
return job.Errorf("Usage: %s CONTAINER", job.Name)
|
||||||
|
}
|
||||||
|
name := job.Args[0]
|
||||||
|
container := srv.daemon.Get(name)
|
||||||
|
if container == nil {
|
||||||
|
return job.Errorf("No such container: %s", name)
|
||||||
|
}
|
||||||
|
if err := container.Pause(); err != nil {
|
||||||
|
return job.Errorf("Cannot pause container %s: %s", name, err)
|
||||||
|
}
|
||||||
|
return engine.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *Server) ContainerUnpause(job *engine.Job) engine.Status {
|
||||||
|
if n := len(job.Args); n < 1 || n > 2 {
|
||||||
|
return job.Errorf("Usage: %s CONTAINER", job.Name)
|
||||||
|
}
|
||||||
|
name := job.Args[0]
|
||||||
|
container := srv.daemon.Get(name)
|
||||||
|
if container == nil {
|
||||||
|
return job.Errorf("No such container: %s", name)
|
||||||
|
}
|
||||||
|
if err := container.Unpause(); err != nil {
|
||||||
|
return job.Errorf("Cannot unpause container %s: %s", name, err)
|
||||||
|
}
|
||||||
|
return engine.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerKill send signal to the container
|
// ContainerKill send signal to the container
|
||||||
// If no signal is given (sig 0), then Kill with SIGKILL and wait
|
// If no signal is given (sig 0), then Kill with SIGKILL and wait
|
||||||
// for the container to exit.
|
// for the container to exit.
|
||||||
|
|
Loading…
Add table
Reference in a new issue