Added helper functions for container migration

This adds a couple of function in structure members needed in the next
commit to make container migration actually work. This just splits of
the function which are not modifying existing code.

Signed-off-by: Adrian Reber <areber@redhat.com>
This commit is contained in:
Adrian Reber
2019-02-06 19:17:25 +00:00
parent e0c8c14f5b
commit a05cfd24bb
3 changed files with 120 additions and 4 deletions

View File

@ -815,6 +815,11 @@ type ContainerCheckpointOptions struct {
// TCPEstablished tells the API to checkpoint a container
// even if it contains established TCP connections
TCPEstablished bool
// Export tells the API to write the checkpoint image to
// the filename set in TargetFile
// Import tells the API to read the checkpoint image from
// the filename set in TargetFile
TargetFile string
}
// Checkpoint checkpoints a container

View File

@ -21,6 +21,7 @@ import (
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/mount"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
opentracing "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
@ -1501,3 +1502,40 @@ func (c *Container) checkReadyForRemoval() error {
return nil
}
// writeJSONFile marshalls and writes the given data to a JSON file
// in the bundle path
func (c *Container) writeJSONFile(v interface{}, file string) (err error) {
fileJSON, err := json.MarshalIndent(v, "", " ")
if err != nil {
return errors.Wrapf(err, "error writing JSON to %s for container %s", file, c.ID())
}
file = filepath.Join(c.bundlePath(), file)
if err := ioutil.WriteFile(file, fileJSON, 0644); err != nil {
return err
}
return nil
}
// prepareCheckpointExport writes the config and spec to
// JSON files for later export
func (c *Container) prepareCheckpointExport() (err error) {
// save live config
if err := c.writeJSONFile(c.Config(), "config.dump"); err != nil {
return err
}
// save spec
jsonPath := filepath.Join(c.bundlePath(), "config.json")
g, err := generate.NewFromFile(jsonPath)
if err != nil {
logrus.Debugf("generating spec for container %q failed with %v", c.ID(), err)
return err
}
if err := c.writeJSONFile(g.Spec(), "spec.dump"); err != nil {
return err
}
return nil
}

View File

@ -5,6 +5,7 @@ package libpod
import (
"context"
"fmt"
"io"
"io/ioutil"
"net"
"os"
@ -25,6 +26,7 @@ import (
"github.com/containers/libpod/pkg/lookup"
"github.com/containers/libpod/pkg/resolvconf"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage/pkg/archive"
securejoin "github.com/cyphar/filepath-securejoin"
"github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
@ -496,6 +498,42 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr
return nil
}
func (c *Container) exportCheckpoint(dest string) (err error) {
logrus.Debugf("Exporting checkpoint image of container %q to %q", c.ID(), dest)
input, err := archive.TarWithOptions(c.bundlePath(), &archive.TarOptions{
Compression: archive.Gzip,
IncludeSourceDir: true,
IncludeFiles: []string{
"checkpoint",
"artifacts",
"ctr.log",
"config.dump",
"spec.dump",
"network.status"},
})
if err != nil {
return errors.Wrapf(err, "error reading checkpoint directory %q", c.ID())
}
outFile, err := os.Create(dest)
if err != nil {
return errors.Wrapf(err, "error creating checkpoint export file %q", dest)
}
defer outFile.Close()
if err := os.Chmod(dest, 0600); err != nil {
return errors.Wrapf(err, "cannot chmod %q", dest)
}
_, err = io.Copy(outFile, input)
if err != nil {
return err
}
return nil
}
func (c *Container) checkpointRestoreSupported() (err error) {
if !criu.CheckForCriu() {
return errors.Errorf("Checkpoint/Restore requires at least CRIU %d", criu.MinCriuVersion)
@ -561,15 +599,50 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
}
if !options.Keep {
// Remove log file
os.Remove(filepath.Join(c.bundlePath(), "dump.log"))
// Remove statistic file
os.Remove(filepath.Join(c.bundlePath(), "stats-dump"))
cleanup := []string{
"dump.log",
"stats-dump",
"config.dump",
"spec.dump",
}
for _, delete := range cleanup {
file := filepath.Join(c.bundlePath(), delete)
os.Remove(file)
}
}
return c.save()
}
func (c *Container) importCheckpoint(input string) (err error) {
archiveFile, err := os.Open(input)
if err != nil {
return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input)
}
defer archiveFile.Close()
options := &archive.TarOptions{
ExcludePatterns: []string{
// config.dump and spec.dump are only required
// container creation
"config.dump",
"spec.dump",
},
}
err = archive.Untar(archiveFile, c.bundlePath(), options)
if err != nil {
return errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input)
}
// Make sure the newly created config.json exists on disk
g := generate.NewFromSpec(c.config.Spec)
if err = c.saveSpec(g.Spec()); err != nil {
return errors.Wrap(err, "Saving imported container specification for restore failed")
}
return nil
}
func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (err error) {
if err := c.checkpointRestoreSupported(); err != nil {