Skip to content

Commit 5a1f298

Browse files
committed
Update checkpoint/restore support in the daemon.
Docker-DCO-1.1-Signed-off-by: Ross Boucher <rboucher@gmail.com> (github: boucher)
1 parent d17726e commit 5a1f298

File tree

9 files changed

+153
-155
lines changed

9 files changed

+153
-155
lines changed

api/server/server.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/docker/docker/pkg/version"
3737
"github.com/docker/docker/runconfig"
3838
"github.com/docker/docker/utils"
39+
"github.com/docker/libcontainer"
3940
"github.com/docker/libnetwork/portallocator"
4041
)
4142

@@ -1286,32 +1287,44 @@ func (s *Server) postContainersCopy(version version.Version, w http.ResponseWrit
12861287
return nil
12871288
}
12881289

1289-
func postContainersCheckpoint(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1290+
func (s *Server) postContainersCheckpoint(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
12901291
if vars == nil {
12911292
return fmt.Errorf("Missing parameter")
12921293
}
12931294
if err := parseForm(r); err != nil {
12941295
return err
12951296
}
1296-
job := eng.Job("checkpoint", vars["name"])
1297-
if err := job.Run(); err != nil {
1297+
1298+
criuOpts := &libcontainer.CriuOpts{}
1299+
if err := json.NewDecoder(r.Body).Decode(criuOpts); err != nil {
12981300
return err
12991301
}
1302+
1303+
if err := s.daemon.ContainerCheckpoint(vars["name"], criuOpts); err != nil {
1304+
return err
1305+
}
1306+
13001307
w.WriteHeader(http.StatusNoContent)
13011308
return nil
13021309
}
13031310

1304-
func postContainersRestore(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1311+
func (s *Server) postContainersRestore(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
13051312
if vars == nil {
13061313
return fmt.Errorf("Missing parameter")
13071314
}
13081315
if err := parseForm(r); err != nil {
13091316
return err
13101317
}
1311-
job := eng.Job("restore", vars["name"])
1312-
if err := job.Run(); err != nil {
1318+
1319+
restoreOpts := runconfig.RestoreConfig{}
1320+
if err := json.NewDecoder(r.Body).Decode(&restoreOpts); err != nil {
13131321
return err
13141322
}
1323+
1324+
if err := s.daemon.ContainerRestore(vars["name"], &restoreOpts.CriuOpts, restoreOpts.ForceRestore); err != nil {
1325+
return err
1326+
}
1327+
13151328
w.WriteHeader(http.StatusNoContent)
13161329
return nil
13171330
}

daemon/checkpoint.go

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,56 @@
11
package daemon
22

33
import (
4-
"github.com/docker/docker/engine"
4+
"fmt"
5+
6+
"github.com/docker/libcontainer"
57
)
68

79
// Checkpoint a running container.
8-
func (daemon *Daemon) ContainerCheckpoint(job *engine.Job) engine.Status {
9-
if len(job.Args) != 1 {
10-
return job.Errorf("Usage: %s CONTAINER\n", job.Name)
11-
}
12-
13-
name := job.Args[0]
10+
func (daemon *Daemon) ContainerCheckpoint(name string, opts *libcontainer.CriuOpts) error {
1411
container, err := daemon.Get(name)
1512
if err != nil {
16-
return job.Error(err)
13+
return err
1714
}
1815
if !container.IsRunning() {
19-
return job.Errorf("Container %s not running", name)
16+
return fmt.Errorf("Container %s not running", name)
2017
}
21-
22-
if err := container.Checkpoint(); err != nil {
23-
return job.Errorf("Cannot checkpoint container %s: %s", name, err)
18+
if err := container.Checkpoint(opts); err != nil {
19+
return fmt.Errorf("Cannot checkpoint container %s: %s", name, err)
2420
}
2521

2622
container.LogEvent("checkpoint")
27-
return engine.StatusOK
23+
return nil
2824
}
2925

3026
// Restore a checkpointed container.
31-
func (daemon *Daemon) ContainerRestore(job *engine.Job) engine.Status {
32-
if len(job.Args) != 1 {
33-
return job.Errorf("Usage: %s CONTAINER\n", job.Name)
34-
}
35-
36-
name := job.Args[0]
27+
func (daemon *Daemon) ContainerRestore(name string, opts *libcontainer.CriuOpts, forceRestore bool) error {
3728
container, err := daemon.Get(name)
3829
if err != nil {
39-
return job.Error(err)
40-
}
41-
if container.IsRunning() {
42-
return job.Errorf("Container %s already running", name)
30+
return err
4331
}
44-
if !container.State.IsCheckpointed() {
45-
return job.Errorf("Container %s is not checkpointed", name)
32+
33+
if !forceRestore {
34+
// TODO: It's possible we only want to bypass the checkpointed check,
35+
// I'm not sure how this will work if the container is already running
36+
if container.IsRunning() {
37+
return fmt.Errorf("Container %s already running", name)
38+
}
39+
40+
if !container.IsCheckpointed() {
41+
return fmt.Errorf("Container %s is not checkpointed", name)
42+
}
43+
} else {
44+
if !container.HasBeenCheckpointed() && opts.ImagesDirectory == "" {
45+
return fmt.Errorf("You must specify an image directory to restore from %s", name)
46+
}
4647
}
4748

48-
if err := container.Restore(); err != nil {
49+
if err = container.Restore(opts, forceRestore); err != nil {
4950
container.LogEvent("die")
50-
return job.Errorf("Cannot restore container %s: %s", name, err)
51+
return fmt.Errorf("Cannot restore container %s: %s", name, err)
5152
}
5253

5354
container.LogEvent("restore")
54-
return engine.StatusOK
55+
return nil
5556
}

daemon/container.go

Lines changed: 43 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"syscall"
1414
"time"
1515

16+
"github.com/docker/libcontainer"
1617
"github.com/docker/libcontainer/label"
1718

1819
"github.com/Sirupsen/logrus"
@@ -255,7 +256,7 @@ func (container *Container) Start() (err error) {
255256
if err := container.Mount(); err != nil {
256257
return err
257258
}
258-
if err := container.initializeNetworking(); err != nil {
259+
if err := container.initializeNetworking(false); err != nil {
259260
return err
260261
}
261262
container.verifyDaemonSettings()
@@ -339,12 +340,11 @@ func (container *Container) isNetworkAllocated() bool {
339340
return container.NetworkSettings.IPAddress != ""
340341
}
341342

342-
343343
// cleanup releases any network resources allocated to the container along with any rules
344344
// around how containers are linked together. It also unmounts the container's root filesystem.
345345
func (container *Container) cleanup() {
346346
if container.IsCheckpointed() {
347-
log.CRDbg("not calling ReleaseNetwork() for checkpointed container %s", container.ID)
347+
logrus.Debugf("not calling ReleaseNetwork() for checkpointed container %s", container.ID)
348348
} else {
349349
container.ReleaseNetwork()
350350
}
@@ -569,7 +569,6 @@ func validateID(id string) error {
569569
return nil
570570
}
571571

572-
573572
func (container *Container) Checkpoint(opts *libcontainer.CriuOpts) error {
574573
if err := container.daemon.Checkpoint(container, opts); err != nil {
575574
return err
@@ -581,6 +580,43 @@ func (container *Container) Checkpoint(opts *libcontainer.CriuOpts) error {
581580
return nil
582581
}
583582

583+
func (container *Container) Restore(opts *libcontainer.CriuOpts, forceRestore bool) error {
584+
var err error
585+
container.Lock()
586+
defer container.Unlock()
587+
588+
defer func() {
589+
if err != nil {
590+
container.cleanup()
591+
}
592+
}()
593+
if err := container.Mount(); err != nil {
594+
return err
595+
}
596+
if err = container.initializeNetworking(true); err != nil {
597+
return err
598+
}
599+
container.verifyDaemonSettings()
600+
601+
linkedEnv, err := container.setupLinkedContainers()
602+
if err != nil {
603+
return err
604+
}
605+
if err = container.setupWorkingDirectory(); err != nil {
606+
return err
607+
}
608+
609+
env := container.createDaemonEnvironment(linkedEnv)
610+
if err = populateCommand(container, env); err != nil {
611+
return err
612+
}
613+
614+
if err = container.setupMounts(); err != nil {
615+
return err
616+
}
617+
618+
return container.waitForRestore(opts, forceRestore)
619+
}
584620

585621
func (container *Container) Copy(resource string) (io.ReadCloser, error) {
586622
container.Lock()
@@ -641,41 +677,6 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
641677
nil
642678
}
643679

644-
func (container *Container) Checkpoint() error {
645-
return container.daemon.Checkpoint(container)
646-
}
647-
648-
func (container *Container) Restore() error {
649-
var err error
650-
651-
container.Lock()
652-
defer container.Unlock()
653-
654-
defer func() {
655-
if err != nil {
656-
container.cleanup()
657-
}
658-
}()
659-
660-
if err = container.initializeNetworking(); err != nil {
661-
return err
662-
}
663-
664-
linkedEnv, err := container.setupLinkedContainers()
665-
if err != nil {
666-
return err
667-
}
668-
if err = container.setupWorkingDirectory(); err != nil {
669-
return err
670-
}
671-
env := container.createDaemonEnvironment(linkedEnv)
672-
if err = populateCommandRestore(container, env); err != nil {
673-
return err
674-
}
675-
676-
return container.waitForRestore()
677-
}
678-
679680
// Returns true if the container exposes a certain port
680681
func (container *Container) Exposes(p nat.Port) bool {
681682
_, exists := container.Config.ExposedPorts[p]
@@ -762,10 +763,7 @@ func (container *Container) waitForStart() error {
762763
return nil
763764
}
764765

765-
// Like waitForStart() but for restoring a container.
766-
//
767-
// XXX Does RestartPolicy apply here?
768-
func (container *Container) waitForRestore() error {
766+
func (container *Container) waitForRestore(opts *libcontainer.CriuOpts, forceRestore bool) error {
769767
container.monitor = newContainerMonitor(container, container.hostConfig.RestartPolicy)
770768

771769
// After calling promise.Go() we'll have two goroutines:
@@ -778,7 +776,7 @@ func (container *Container) waitForRestore() error {
778776
if container.ExitCode != 0 {
779777
return fmt.Errorf("restore process failed")
780778
}
781-
case err := <-promise.Go(container.monitor.Restore):
779+
case err := <-promise.Go(func() error { return container.monitor.Restore(opts, forceRestore) }):
782780
return err
783781
}
784782

@@ -989,6 +987,7 @@ func attach(streamConfig *StreamConfig, openStdin, stdinOnce, tty bool, stdin io
989987
_, err = copyEscapable(cStdin, stdin)
990988
} else {
991989
_, err = io.Copy(cStdin, stdin)
990+
992991
}
993992
if err == io.ErrClosedPipe {
994993
err = nil

0 commit comments

Comments
 (0)