summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_api.go30
-rw-r--r--libpod/container_easyjson.go2
-rw-r--r--libpod/container_internal.go111
-rw-r--r--libpod/container_internal_linux.go158
-rw-r--r--libpod/container_internal_unsupported.go8
-rw-r--r--libpod/oci.go83
-rw-r--r--libpod/oci_linux.go6
-rw-r--r--libpod/oci_unsupported.go2
8 files changed, 315 insertions, 85 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 192ccd347..93becb80d 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -832,3 +832,33 @@ func (c *Container) Refresh(ctx context.Context) error {
return nil
}
+
+// Checkpoint checkpoints a container
+func (c *Container) Checkpoint(ctx context.Context, keep bool) error {
+ logrus.Debugf("Trying to checkpoint container %s", c)
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return err
+ }
+ }
+
+ return c.checkpoint(ctx, keep)
+}
+
+// Restore restores a container
+func (c *Container) Restore(ctx context.Context, keep bool) (err error) {
+ logrus.Debugf("Trying to restore container %s", c)
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return err
+ }
+ }
+
+ return c.restore(ctx, keep)
+}
diff --git a/libpod/container_easyjson.go b/libpod/container_easyjson.go
index 2d0481f3b..916118aec 100644
--- a/libpod/container_easyjson.go
+++ b/libpod/container_easyjson.go
@@ -1,3 +1,5 @@
+// +build seccomp ostree selinux varlink exclude_graphdriver_devicemapper
+
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package libpod
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 033426817..77bba9e85 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -15,9 +15,9 @@ import (
"github.com/containers/libpod/pkg/chrootuser"
"github.com/containers/libpod/pkg/hooks"
"github.com/containers/libpod/pkg/hooks/exec"
+ "github.com/containers/libpod/pkg/resolvconf"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/secrets"
- "github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/chrootarchive"
@@ -129,6 +129,11 @@ func (c *Container) ControlSocketPath() string {
return filepath.Join(c.bundlePath(), "ctl")
}
+// CheckpointPath returns the path to the directory containing the checkpoint
+func (c *Container) CheckpointPath() string {
+ return filepath.Join(c.bundlePath(), "checkpoint")
+}
+
// AttachSocketPath retrieves the path of the container's attach socket
func (c *Container) AttachSocketPath() string {
return filepath.Join(c.runtime.ociRuntime.socketsDir, c.ID(), "attach")
@@ -523,7 +528,7 @@ func (c *Container) init(ctx context.Context) error {
}
// With the spec complete, do an OCI create
- if err := c.runtime.ociRuntime.createContainer(c, c.config.CgroupParent); err != nil {
+ if err := c.runtime.ociRuntime.createContainer(c, c.config.CgroupParent, false); err != nil {
return err
}
@@ -1012,12 +1017,6 @@ func (c *Container) writeStringToRundir(destFile, output string) (string, error)
return filepath.Join(c.state.DestinationRunDir, destFile), nil
}
-type resolvConf struct {
- nameServers []string
- searchDomains []string
- options []string
-}
-
// generateResolvConf generates a containers resolv.conf
func (c *Container) generateResolvConf() (string, error) {
// Determine the endpoint for resolv.conf in case it is a symlink
@@ -1025,86 +1024,56 @@ func (c *Container) generateResolvConf() (string, error) {
if err != nil {
return "", err
}
- orig, err := ioutil.ReadFile(resolvPath)
+
+ contents, err := ioutil.ReadFile(resolvPath)
if err != nil {
return "", errors.Wrapf(err, "unable to read %s", resolvPath)
}
- if len(c.config.DNSServer) == 0 && len(c.config.DNSSearch) == 0 && len(c.config.DNSOption) == 0 {
- return c.writeStringToRundir("resolv.conf", fmt.Sprintf("%s", orig))
- }
- // Read and organize the hosts /etc/resolv.conf
- resolv := createResolv(string(orig[:]))
-
- // Populate the resolv struct with user's dns search domains
- if len(c.config.DNSSearch) > 0 {
- resolv.searchDomains = nil
- // The . character means the user doesnt want any search domains in the container
- if !util.StringInSlice(".", c.config.DNSSearch) {
- resolv.searchDomains = append(resolv.searchDomains, c.Config().DNSSearch...)
- }
+ // Process the file to remove localhost nameservers
+ // TODO: set ipv6 enable bool more sanely
+ resolv, err := resolvconf.FilterResolvDNS(contents, true)
+ if err != nil {
+ return "", errors.Wrapf(err, "error parsing host resolv.conf")
}
- // Populate the resolv struct with user's dns servers
+ // Make a new resolv.conf
+ nameservers := resolvconf.GetNameservers(resolv.Content)
if len(c.config.DNSServer) > 0 {
- resolv.nameServers = nil
- for _, i := range c.config.DNSServer {
- resolv.nameServers = append(resolv.nameServers, i.String())
+ // We store DNS servers as net.IP, so need to convert to string
+ nameservers = []string{}
+ for _, server := range c.config.DNSServer {
+ nameservers = append(nameservers, server.String())
}
}
- // Populate the resolve struct with the users dns options
+ search := resolvconf.GetSearchDomains(resolv.Content)
+ if len(c.config.DNSSearch) > 0 {
+ search = c.config.DNSSearch
+ }
+
+ options := resolvconf.GetOptions(resolv.Content)
if len(c.config.DNSOption) > 0 {
- resolv.options = nil
- resolv.options = append(resolv.options, c.Config().DNSOption...)
+ options = c.config.DNSOption
}
- return c.writeStringToRundir("resolv.conf", resolv.ToString())
-}
-// createResolv creates a resolv struct from an input string
-func createResolv(input string) resolvConf {
- var resolv resolvConf
- for _, line := range strings.Split(input, "\n") {
- if strings.HasPrefix(line, "search") {
- fields := strings.Fields(line)
- if len(fields) < 2 {
- logrus.Debugf("invalid resolv.conf line %s", line)
- continue
- }
- resolv.searchDomains = append(resolv.searchDomains, fields[1:]...)
- } else if strings.HasPrefix(line, "nameserver") {
- fields := strings.Fields(line)
- if len(fields) < 2 {
- logrus.Debugf("invalid resolv.conf line %s", line)
- continue
- }
- resolv.nameServers = append(resolv.nameServers, fields[1])
- } else if strings.HasPrefix(line, "options") {
- fields := strings.Fields(line)
- if len(fields) < 2 {
- logrus.Debugf("invalid resolv.conf line %s", line)
- continue
- }
- resolv.options = append(resolv.options, fields[1:]...)
- }
+ destPath := filepath.Join(c.state.RunDir, "resolv.conf")
+
+ if err := os.Remove(destPath); err != nil && !os.IsNotExist(err) {
+ return "", errors.Wrapf(err, "error removing resolv.conf for container %s", c.ID())
}
- return resolv
-}
-//ToString returns a resolv struct in the form of a resolv.conf
-func (r resolvConf) ToString() string {
- var result string
- // Populate the output string with search domains
- result += fmt.Sprintf("search %s\n", strings.Join(r.searchDomains, " "))
- // Populate the output string with name servers
- for _, i := range r.nameServers {
- result += fmt.Sprintf("nameserver %s\n", i)
+ // Build resolv.conf
+ if _, err = resolvconf.Build(destPath, nameservers, search, options); err != nil {
+ return "", errors.Wrapf(err, "error building resolv.conf for container %s")
}
- // Populate the output string with dns options
- for _, i := range r.options {
- result += fmt.Sprintf("options %s\n", i)
+
+ // Relabel resolv.conf for the container
+ if err := label.Relabel(destPath, c.config.MountLabel, false); err != nil {
+ return "", err
}
- return result
+
+ return filepath.Join(c.state.DestinationRunDir, "resolv.conf"), nil
}
// generateHosts creates a containers hosts file
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index b77beaf64..0353124dd 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -4,12 +4,18 @@ package libpod
import (
"context"
+ "encoding/json"
"fmt"
+ "io/ioutil"
+ "net"
+ "os"
"path"
+ "path/filepath"
"strings"
"syscall"
"time"
+ cnitypes "github.com/containernetworking/cni/pkg/types/current"
crioAnnotations "github.com/containers/libpod/pkg/annotations"
"github.com/containers/libpod/pkg/chrootuser"
"github.com/containers/libpod/pkg/rootless"
@@ -307,3 +313,155 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr
return nil
}
+
+func (c *Container) checkpoint(ctx context.Context, keep bool) (err error) {
+
+ if c.state.State != ContainerStateRunning {
+ return errors.Wrapf(ErrCtrStateInvalid, "%q is not running, cannot checkpoint", c.state.State)
+ }
+ if err := c.runtime.ociRuntime.checkpointContainer(c); err != nil {
+ return err
+ }
+
+ // Save network.status. This is needed to restore the container with
+ // the same IP. Currently limited to one IP address in a container
+ // with one interface.
+ formatJSON, err := json.MarshalIndent(c.state.NetworkStatus, "", " ")
+ if err != nil {
+ return err
+ }
+ if err := ioutil.WriteFile(filepath.Join(c.bundlePath(), "network.status"), formatJSON, 0644); err != nil {
+ return err
+ }
+
+ logrus.Debugf("Checkpointed container %s", c.ID())
+
+ c.state.State = ContainerStateStopped
+
+ // Cleanup Storage and Network
+ if err := c.cleanup(ctx); err != nil {
+ return err
+ }
+
+ if !keep {
+ // Remove log file
+ os.Remove(filepath.Join(c.bundlePath(), "dump.log"))
+ // Remove statistic file
+ os.Remove(filepath.Join(c.bundlePath(), "stats-dump"))
+ }
+
+ return c.save()
+}
+
+func (c *Container) restore(ctx context.Context, keep bool) (err error) {
+
+ if (c.state.State != ContainerStateConfigured) && (c.state.State != ContainerStateExited) {
+ return errors.Wrapf(ErrCtrStateInvalid, "container %s is running or paused, cannot restore", c.ID())
+ }
+
+ // Let's try to stat() CRIU's inventory file. If it does not exist, it makes
+ // no sense to try a restore. This is a minimal check if a checkpoint exist.
+ if _, err := os.Stat(filepath.Join(c.CheckpointPath(), "inventory.img")); os.IsNotExist(err) {
+ return errors.Wrapf(err, "A complete checkpoint for this container cannot be found, cannot restore")
+ }
+
+ // Read network configuration from checkpoint
+ // Currently only one interface with one IP is supported.
+ networkStatusFile, err := os.Open(filepath.Join(c.bundlePath(), "network.status"))
+ if err == nil {
+ // The file with the network.status does exist. Let's restore the
+ // container with the same IP address as during checkpointing.
+ defer networkStatusFile.Close()
+ var networkStatus []*cnitypes.Result
+ networkJSON, err := ioutil.ReadAll(networkStatusFile)
+ if err != nil {
+ return err
+ }
+ json.Unmarshal(networkJSON, &networkStatus)
+ // Take the first IP address
+ var IP net.IP
+ if len(networkStatus) > 0 {
+ if len(networkStatus[0].IPs) > 0 {
+ IP = networkStatus[0].IPs[0].Address.IP
+ }
+ }
+ if IP != nil {
+ env := fmt.Sprintf("IP=%s", IP)
+ // Tell CNI which IP address we want.
+ os.Setenv("CNI_ARGS", env)
+ logrus.Debugf("Restoring container with %s", env)
+ }
+ }
+
+ if err := c.prepare(); err != nil {
+ return err
+ }
+ defer func() {
+ if err != nil {
+ if err2 := c.cleanup(ctx); err2 != nil {
+ logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
+ }
+ }
+ }()
+
+ // TODO: use existing way to request static IPs, once it is merged in ocicni
+ // https://github.com/cri-o/ocicni/pull/23/
+
+ // CNI_ARGS was used to request a certain IP address. Unconditionally remove it.
+ os.Unsetenv("CNI_ARGS")
+
+ // Read config
+ jsonPath := filepath.Join(c.bundlePath(), "config.json")
+ logrus.Debugf("generate.NewFromFile at %v", jsonPath)
+ g, err := generate.NewFromFile(jsonPath)
+ if err != nil {
+ logrus.Debugf("generate.NewFromFile failed with %v", err)
+ return err
+ }
+
+ // We want to have the same network namespace as before.
+ if c.config.CreateNetNS {
+ g.AddOrReplaceLinuxNamespace(spec.NetworkNamespace, c.state.NetNS.Path())
+ }
+
+ // Save the OCI spec to disk
+ if err := c.saveSpec(g.Spec()); err != nil {
+ return err
+ }
+
+ if err := c.makeBindMounts(); err != nil {
+ return err
+ }
+
+ // Cleanup for a working restore.
+ c.removeConmonFiles()
+
+ if err := c.runtime.ociRuntime.createContainer(c, c.config.CgroupParent, true); err != nil {
+ return err
+ }
+
+ logrus.Debugf("Restored container %s", c.ID())
+
+ c.state.State = ContainerStateRunning
+
+ if !keep {
+ // Delete all checkpoint related files. At this point, in theory, all files
+ // should exist. Still ignoring errors for now as the container should be
+ // restored and running. Not erroring out just because some cleanup operation
+ // failed. Starting with the checkpoint directory
+ err = os.RemoveAll(c.CheckpointPath())
+ if err != nil {
+ logrus.Debugf("Non-fatal: removal of checkpoint directory (%s) failed: %v", c.CheckpointPath(), err)
+ }
+ cleanup := [...]string{"restore.log", "dump.log", "stats-dump", "stats-restore", "network.status"}
+ for _, delete := range cleanup {
+ file := filepath.Join(c.bundlePath(), delete)
+ err = os.Remove(file)
+ if err != nil {
+ logrus.Debugf("Non-fatal: removal of checkpoint file (%s) failed: %v", file, err)
+ }
+ }
+ }
+
+ return c.save()
+}
diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go
index 45b54efab..eed0449a9 100644
--- a/libpod/container_internal_unsupported.go
+++ b/libpod/container_internal_unsupported.go
@@ -27,3 +27,11 @@ func (c *Container) cleanupNetwork() error {
func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
return nil, ErrNotImplemented
}
+
+func (c *Container) checkpoint(ctx context.Context, keep bool) error {
+ return ErrNotImplemented
+}
+
+func (c *Container) restore(ctx context.Context, keep bool) error {
+ return ErrNotImplemented
+}
diff --git a/libpod/oci.go b/libpod/oci.go
index e5db06540..f6d320017 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -227,7 +227,7 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) {
return files, nil
}
-func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string) (err error) {
+func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, restoreContainer bool) (err error) {
var stderrBuf bytes.Buffer
runtimeDir, err := GetRootlessRuntimeDir()
@@ -289,6 +289,10 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string) (er
args = append(args, "--syslog")
}
+ if restoreContainer {
+ args = append(args, "--restore", ctr.CheckpointPath())
+ }
+
logrus.WithFields(logrus.Fields{
"args": args,
}).Debugf("running conmon: %s", r.conmonPath)
@@ -452,9 +456,20 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
cmd := exec.Command(r.path, "state", ctr.ID())
cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir))
-
- out, err := cmd.CombinedOutput()
+ outPipe, err := cmd.StdoutPipe()
+ if err != nil {
+ return errors.Wrapf(err, "getting stdout pipe")
+ }
+ errPipe, err := cmd.StderrPipe()
if err != nil {
+ return errors.Wrapf(err, "getting stderr pipe")
+ }
+
+ if err := cmd.Start(); err != nil {
+ out, err2 := ioutil.ReadAll(errPipe)
+ if err2 != nil {
+ return errors.Wrapf(err, "error getting container %s state", ctr.ID())
+ }
if strings.Contains(string(out), "does not exist") {
ctr.removeConmonFiles()
ctr.state.State = ContainerStateExited
@@ -462,6 +477,12 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
}
return errors.Wrapf(err, "error getting container %s state. stderr/out: %s", ctr.ID(), out)
}
+
+ errPipe.Close()
+ out, err := ioutil.ReadAll(outPipe)
+ if err != nil {
+ return errors.Wrapf(err, "error reading stdout: %s", ctr.ID())
+ }
if err := json.NewDecoder(bytes.NewBuffer(out)).Decode(state); err != nil {
return errors.Wrapf(err, "error decoding container status for container %s", ctr.ID())
}
@@ -535,7 +556,12 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
// Sets time the container was started, but does not save it.
func (r *OCIRuntime) startContainer(ctr *Container) error {
// TODO: streams should probably *not* be our STDIN/OUT/ERR - redirect to buffers?
- if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, "start", ctr.ID()); err != nil {
+ runtimeDir, err := GetRootlessRuntimeDir()
+ if err != nil {
+ return err
+ }
+ env := []string{fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)}
+ if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, "start", ctr.ID()); err != nil {
return err
}
@@ -547,7 +573,12 @@ func (r *OCIRuntime) startContainer(ctr *Container) error {
// killContainer sends the given signal to the given container
func (r *OCIRuntime) killContainer(ctr *Container, signal uint) error {
logrus.Debugf("Sending signal %d to container %s", signal, ctr.ID())
- if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, "kill", ctr.ID(), fmt.Sprintf("%d", signal)); err != nil {
+ runtimeDir, err := GetRootlessRuntimeDir()
+ if err != nil {
+ return err
+ }
+ env := []string{fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)}
+ if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, "kill", ctr.ID(), fmt.Sprintf("%d", signal)); err != nil {
return errors.Wrapf(err, "error sending signal to container %s", ctr.ID())
}
@@ -605,7 +636,12 @@ func (r *OCIRuntime) stopContainer(ctr *Container, timeout uint) error {
args = []string{"kill", "--all", ctr.ID(), "KILL"}
}
- if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...); err != nil {
+ runtimeDir, err := GetRootlessRuntimeDir()
+ if err != nil {
+ return err
+ }
+ env := []string{fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)}
+ if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, args...); err != nil {
// Again, check if the container is gone. If it is, exit cleanly.
err := unix.Kill(ctr.state.PID, 0)
if err == unix.ESRCH {
@@ -631,12 +667,22 @@ func (r *OCIRuntime) deleteContainer(ctr *Container) error {
// pauseContainer pauses the given container
func (r *OCIRuntime) pauseContainer(ctr *Container) error {
- return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, "pause", ctr.ID())
+ runtimeDir, err := GetRootlessRuntimeDir()
+ if err != nil {
+ return err
+ }
+ env := []string{fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)}
+ return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, "pause", ctr.ID())
}
// unpauseContainer unpauses the given container
func (r *OCIRuntime) unpauseContainer(ctr *Container) error {
- return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, "resume", ctr.ID())
+ runtimeDir, err := GetRootlessRuntimeDir()
+ if err != nil {
+ return err
+ }
+ env := []string{fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)}
+ return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, "resume", ctr.ID())
}
// execContainer executes a command in a running container
@@ -734,13 +780,18 @@ func (r *OCIRuntime) execStopContainer(ctr *Container, timeout uint) error {
if len(execSessions) == 0 {
return nil
}
+ runtimeDir, err := GetRootlessRuntimeDir()
+ if err != nil {
+ return err
+ }
+ env := []string{fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)}
// If timeout is 0, just use SIGKILL
if timeout > 0 {
// Stop using SIGTERM by default
// Use SIGSTOP after a timeout
logrus.Debugf("Killing all processes in container %s with SIGTERM", ctr.ID())
- if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, "kill", "--all", ctr.ID(), "TERM"); err != nil {
+ if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, "kill", "--all", ctr.ID(), "TERM"); err != nil {
return errors.Wrapf(err, "error sending SIGTERM to container %s processes", ctr.ID())
}
@@ -755,7 +806,7 @@ func (r *OCIRuntime) execStopContainer(ctr *Container, timeout uint) error {
// Send SIGKILL
logrus.Debugf("Killing all processes in container %s with SIGKILL", ctr.ID())
- if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, "kill", "--all", ctr.ID(), "KILL"); err != nil {
+ if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, "kill", "--all", ctr.ID(), "KILL"); err != nil {
return errors.Wrapf(err, "error sending SIGKILL to container %s processes", ctr.ID())
}
@@ -766,3 +817,15 @@ func (r *OCIRuntime) execStopContainer(ctr *Container, timeout uint) error {
return nil
}
+
+// checkpointContainer checkpoints the given container
+func (r *OCIRuntime) checkpointContainer(ctr *Container) error {
+ // imagePath is used by CRIU to store the actual checkpoint files
+ imagePath := ctr.CheckpointPath()
+ // workPath will be used to store dump.log and stats-dump
+ workPath := ctr.bundlePath()
+ logrus.Debugf("Writing checkpoint to %s", imagePath)
+ logrus.Debugf("Writing checkpoint logs to %s", workPath)
+ return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, "checkpoint",
+ "--image-path", imagePath, "--work-path", workPath, ctr.ID())
+}
diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go
index 210ba57d1..0447670b3 100644
--- a/libpod/oci_linux.go
+++ b/libpod/oci_linux.go
@@ -63,10 +63,10 @@ func newPipe() (parent *os.File, child *os.File, err error) {
// CreateContainer creates a container in the OCI runtime
// TODO terminal support for container
// Presently just ignoring conmon opts related to it
-func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string) (err error) {
+func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restoreContainer bool) (err error) {
if ctr.state.UserNSRoot == "" {
// no need of an intermediate mount ns
- return r.createOCIContainer(ctr, cgroupParent)
+ return r.createOCIContainer(ctr, cgroupParent, restoreContainer)
}
var wg sync.WaitGroup
wg.Add(1)
@@ -103,7 +103,7 @@ func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string) (err e
if err != nil {
return
}
- err = r.createOCIContainer(ctr, cgroupParent)
+ err = r.createOCIContainer(ctr, cgroupParent, restoreContainer)
}()
wg.Wait()
diff --git a/libpod/oci_unsupported.go b/libpod/oci_unsupported.go
index 8cb4994d3..b133eb402 100644
--- a/libpod/oci_unsupported.go
+++ b/libpod/oci_unsupported.go
@@ -15,7 +15,7 @@ func newPipe() (parent *os.File, child *os.File, err error) {
return nil, nil, ErrNotImplemented
}
-func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string) (err error) {
+func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restoreContainer bool) (err error) {
return ErrNotImplemented
}