summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml2
-rw-r--r--Makefile2
-rw-r--r--cmd/podman/containers/checkpoint.go6
-rw-r--r--cmd/podman/containers/restore.go7
-rwxr-xr-xcontrib/cirrus/runner.sh3
-rw-r--r--docs/source/markdown/podman-container-checkpoint.1.md14
-rw-r--r--docs/source/markdown/podman-container-restore.1.md7
-rw-r--r--libpod/container_api.go13
-rw-r--r--libpod/container_internal.go11
-rw-r--r--libpod/container_internal_linux.go41
-rw-r--r--libpod/oci_attach_linux.go29
-rw-r--r--libpod/oci_attach_linux_cgo.go11
-rw-r--r--libpod/oci_attach_linux_nocgo.go7
-rw-r--r--libpod/oci_conmon_exec_linux.go6
-rw-r--r--libpod/oci_conmon_linux.go21
-rw-r--r--pkg/domain/entities/containers.go3
-rw-r--r--pkg/domain/infra/abi/containers.go3
-rw-r--r--pkg/domain/infra/abi/images_list.go5
-rw-r--r--test/e2e/checkpoint_test.go75
-rw-r--r--test/system/010-images.bats13
20 files changed, 233 insertions, 46 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 18cb889ad..3eaa4ede8 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -311,6 +311,8 @@ alt_build_task:
ALT_NAME: 'Build Without CGO'
- env:
ALT_NAME: 'Test build RPM'
+ - env:
+ ALT_NAME: 'Alt Arch. Cross'
setup_script: *setup
main_script: *main
always: *binary_artifacts
diff --git a/Makefile b/Makefile
index c0c1e4003..085af6d80 100644
--- a/Makefile
+++ b/Makefile
@@ -225,7 +225,7 @@ bin/podman.cross.%: .gopathok
TARGET="$*"; \
GOOS="$${TARGET%%.*}" \
GOARCH="$${TARGET##*.}" \
- $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags '$(BUILDTAGS_CROSS)' -o "$@" ./cmd/podman
+ CGO_ENABLED=0 $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags '$(BUILDTAGS_CROSS)' -o "$@" ./cmd/podman
# Update nix/nixpkgs.json its latest stable commit
.PHONY: nixpkgs
diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go
index 4a477eb10..14abfd5a7 100644
--- a/cmd/podman/containers/checkpoint.go
+++ b/cmd/podman/containers/checkpoint.go
@@ -58,6 +58,9 @@ func init() {
flags.BoolVar(&checkpointOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not include root file-system changes when exporting")
flags.BoolVar(&checkpointOptions.IgnoreVolumes, "ignore-volumes", false, "Do not export volumes associated with container")
+ flags.BoolVarP(&checkpointOptions.PreCheckPoint, "pre-checkpoint", "P", false, "Dump container's memory information only, leave the container running")
+ flags.BoolVar(&checkpointOptions.WithPrevious, "with-previous", false, "Checkpoint container with pre-checkpoint images")
+
validate.AddLatestFlag(checkpointCommand, &checkpointOptions.Latest)
}
@@ -72,6 +75,9 @@ func checkpoint(cmd *cobra.Command, args []string) error {
if checkpointOptions.Export == "" && checkpointOptions.IgnoreVolumes {
return errors.Errorf("--ignore-volumes can only be used with --export")
}
+ if checkpointOptions.WithPrevious && checkpointOptions.PreCheckPoint {
+ return errors.Errorf("--with-previous can not be used with --pre-checkpoint")
+ }
responses, err := registry.ContainerEngine().ContainerCheckpoint(context.Background(), args, checkpointOptions)
if err != nil {
return err
diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go
index 5245a68fa..49c0be88e 100644
--- a/cmd/podman/containers/restore.go
+++ b/cmd/podman/containers/restore.go
@@ -59,6 +59,10 @@ func init() {
flags.StringVarP(&restoreOptions.Name, nameFlagName, "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)")
_ = restoreCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone)
+ importPreviousFlagName := "import-previous"
+ flags.StringVar(&restoreOptions.ImportPrevious, importPreviousFlagName, "", "Restore from exported pre-checkpoint archive (tar.gz)")
+ _ = restoreCommand.RegisterFlagCompletionFunc(importPreviousFlagName, completion.AutocompleteDefault)
+
flags.BoolVar(&restoreOptions.IgnoreRootFS, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint")
flags.BoolVar(&restoreOptions.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip")
flags.BoolVar(&restoreOptions.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address")
@@ -71,6 +75,9 @@ func restore(_ *cobra.Command, args []string) error {
if rootless.IsRootless() {
return errors.New("restoring a container requires root")
}
+ if restoreOptions.Import == "" && restoreOptions.ImportPrevious != "" {
+ return errors.Errorf("--import-previous can only be used with --import")
+ }
if restoreOptions.Import == "" && restoreOptions.IgnoreRootFS {
return errors.Errorf("--ignore-rootfs can only be used with --import")
}
diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh
index 7f9afd1fd..e09b2af9c 100755
--- a/contrib/cirrus/runner.sh
+++ b/contrib/cirrus/runner.sh
@@ -178,6 +178,9 @@ function _run_altbuild() {
make -f ./.copr/Makefile
rpmbuild --rebuild ./podman-*.src.rpm
;;
+ Alt*Cross)
+ make local-cross
+ ;;
*Static*)
req_env_vars CTR_FQIN
[[ "$UID" -eq 0 ]] || \
diff --git a/docs/source/markdown/podman-container-checkpoint.1.md b/docs/source/markdown/podman-container-checkpoint.1.md
index 6a9469156..ea05979cd 100644
--- a/docs/source/markdown/podman-container-checkpoint.1.md
+++ b/docs/source/markdown/podman-container-checkpoint.1.md
@@ -58,12 +58,26 @@ This option must be used in combination with the **--export, -e** option.
When this option is specified, the content of volumes associated with
the container will not be included into the checkpoint tar.gz file.
+#### **--pre-checkpoint**, **-P**
+
+Dump the container's memory information only, leaving the container running. Later
+operations will supersede prior dumps. It only works on runc 1.0-rc3 or higher.
+
+#### **--with-previous**
+
+Check out the container with previous criu image files in pre-dump. It only works
+without **--pre-checkpoint** or **-P**. It only works on runc 1.0-rc3 or higher.
+
## EXAMPLE
podman container checkpoint mywebserver
podman container checkpoint 860a4b23
+podman container checkpoint -P -e pre-checkpoint.tar.gz -l
+
+podman container checkpoint --with-previous -e checkpoint.tar.gz -l
+
## SEE ALSO
podman(1), podman-container-restore(1)
diff --git a/docs/source/markdown/podman-container-restore.1.md b/docs/source/markdown/podman-container-restore.1.md
index 0593e6fe9..192b8765b 100644
--- a/docs/source/markdown/podman-container-restore.1.md
+++ b/docs/source/markdown/podman-container-restore.1.md
@@ -48,6 +48,11 @@ Import a checkpoint tar.gz file, which was exported by Podman. This can be used
to import a checkpointed container from another host. Do not specify a *container*
argument when using this option.
+#### **--import-previous**
+
+Import a pre-checkpoint tar.gz file which was exported by Podman. This option
+must be used with **-i** or **--import**. It only works on runc 1.0-rc3 or higher.
+
#### **--name**, **-n**
This is only available in combination with **--import, -i**. If a container is restored
@@ -98,6 +103,8 @@ podman container restore mywebserver
podman container restore 860a4b23
+podman container restore --import-previous pre-checkpoint.tar.gz --import checkpoint.tar.gz
+
## SEE ALSO
podman(1), podman-container-checkpoint(1)
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 2c7dc79c9..87ff764e3 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -706,6 +706,13 @@ type ContainerCheckpointOptions struct {
// IgnoreVolumes tells the API to not export or not to import
// the content of volumes associated with the container
IgnoreVolumes bool
+ // Pre Checkpoint container and leave container running
+ PreCheckPoint bool
+ // Dump container with Pre Checkpoint images
+ WithPrevious bool
+ // ImportPrevious tells the API to restore container with two
+ // images. One is TargetFile, the other is ImportPrevious.
+ ImportPrevious string
}
// Checkpoint checkpoints a container
@@ -718,6 +725,12 @@ func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointO
}
}
+ if options.WithPrevious {
+ if err := c.canWithPrevious(); err != nil {
+ return err
+ }
+ }
+
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 540230c26..c7548e0e5 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -134,6 +134,11 @@ func (c *Container) CheckpointPath() string {
return filepath.Join(c.bundlePath(), "checkpoint")
}
+// PreCheckpointPath returns the path to the directory containing the pre-checkpoint-images
+func (c *Container) PreCheckPointPath() string {
+ return filepath.Join(c.bundlePath(), "pre-checkpoint")
+}
+
// AttachSocketPath retrieves the path of the container's attach socket
func (c *Container) AttachSocketPath() (string, error) {
return c.ociRuntime.AttachSocketPath(c)
@@ -2023,6 +2028,12 @@ func (c *Container) checkReadyForRemoval() error {
return nil
}
+// canWithPrevious return the stat of the preCheckPoint dir
+func (c *Container) canWithPrevious() error {
+ _, err := os.Stat(c.PreCheckPointPath())
+ return err
+}
+
// writeJSONFile marshalls and writes the given data to a JSON file
// in the bundle path
func (c *Container) writeJSONFile(v interface{}, file string) error {
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index ac20e1f25..705086bda 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -812,6 +812,9 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
"spec.dump",
"network.status"}
+ if options.PreCheckPoint {
+ includeFiles[0] = "pre-checkpoint"
+ }
// Get root file-system changes included in the checkpoint archive
rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")
deleteFilesList := filepath.Join(c.bundlePath(), "deleted.files")
@@ -1015,6 +1018,15 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
defer c.newContainerEvent(events.Checkpoint)
+ // There is a bug from criu: https://github.com/checkpoint-restore/criu/issues/116
+ // We have to change the symbolic link from absolute path to relative path
+ if options.WithPrevious {
+ os.Remove(path.Join(c.CheckpointPath(), "parent"))
+ if err := os.Symlink("../pre-checkpoint", path.Join(c.CheckpointPath(), "parent")); err != nil {
+ return err
+ }
+ }
+
if options.TargetFile != "" {
if err = c.exportCheckpoint(options); err != nil {
return err
@@ -1023,7 +1035,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
logrus.Debugf("Checkpointed container %s", c.ID())
- if !options.KeepRunning {
+ if !options.KeepRunning && !options.PreCheckPoint {
c.state.State = define.ContainerStateStopped
// Cleanup Storage and Network
@@ -1032,7 +1044,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
}
}
- if !options.Keep {
+ if !options.Keep && !options.PreCheckPoint {
cleanup := []string{
"dump.log",
"stats-dump",
@@ -1080,6 +1092,21 @@ func (c *Container) importCheckpoint(input string) error {
return nil
}
+func (c *Container) importPreCheckpoint(input string) error {
+ archiveFile, err := os.Open(input)
+ if err != nil {
+ return errors.Wrap(err, "failed to open pre-checkpoint archive for import")
+ }
+
+ defer archiveFile.Close()
+
+ err = archive.Untar(archiveFile, c.bundlePath(), nil)
+ if err != nil {
+ return errors.Wrapf(err, "Unpacking of pre-checkpoint archive %s failed", input)
+ }
+ return nil
+}
+
func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (retErr error) {
if err := c.checkpointRestoreSupported(); err != nil {
return err
@@ -1089,6 +1116,12 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is running or paused, cannot restore", c.ID())
}
+ if options.ImportPrevious != "" {
+ if err := c.importPreCheckpoint(options.ImportPrevious); err != nil {
+ return err
+ }
+ }
+
if options.TargetFile != "" {
if err := c.importCheckpoint(options.TargetFile); err != nil {
return err
@@ -1322,6 +1355,10 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if err != nil {
logrus.Debugf("Non-fatal: removal of checkpoint directory (%s) failed: %v", c.CheckpointPath(), err)
}
+ err = os.RemoveAll(c.PreCheckPointPath())
+ if err != nil {
+ logrus.Debugf("Non-fatal: removal of pre-checkpoint directory (%s) failed: %v", c.PreCheckPointPath(), err)
+ }
cleanup := [...]string{"restore.log", "dump.log", "stats-dump", "stats-restore", "network.status", "rootfs-diff.tar", "deleted.files"}
for _, del := range cleanup {
file := filepath.Join(c.bundlePath(), del)
diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go
index fbc95510e..4556eba94 100644
--- a/libpod/oci_attach_linux.go
+++ b/libpod/oci_attach_linux.go
@@ -28,6 +28,15 @@ const (
AttachPipeStderr = 3
)
+func openUnixSocket(path string) (*net.UnixConn, error) {
+ fd, err := unix.Open(path, unix.O_PATH, 0)
+ if err != nil {
+ return nil, err
+ }
+ defer unix.Close(fd)
+ return net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: fmt.Sprintf("/proc/self/fd/%d", fd), Net: "unixpacket"})
+}
+
// Attach to the given container
// Does not check if state is appropriate
// started is only required if startContainer is true
@@ -52,11 +61,10 @@ func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-
if err != nil {
return err
}
- socketPath := buildSocketPath(attachSock)
- conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: socketPath, Net: "unixpacket"})
+ conn, err := openUnixSocket(attachSock)
if err != nil {
- return errors.Wrapf(err, "failed to connect to container's attach socket: %v", socketPath)
+ return errors.Wrapf(err, "failed to connect to container's attach socket: %v", attachSock)
}
defer func() {
if err := conn.Close(); err != nil {
@@ -124,7 +132,6 @@ func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, se
if err != nil {
return err
}
- socketPath := buildSocketPath(sockPath)
// 2: read from attachFd that the parent process has set up the console socket
if _, err := readConmonPipeData(attachFd, ""); err != nil {
@@ -132,9 +139,9 @@ func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, se
}
// 2: then attach
- conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: socketPath, Net: "unixpacket"})
+ conn, err := openUnixSocket(sockPath)
if err != nil {
- return errors.Wrapf(err, "failed to connect to container's attach socket: %v", socketPath)
+ return errors.Wrapf(err, "failed to connect to container's attach socket: %v", sockPath)
}
defer func() {
if err := conn.Close(); err != nil {
@@ -182,16 +189,6 @@ func registerResizeFunc(resize <-chan remotecommand.TerminalSize, bundlePath str
})
}
-func buildSocketPath(socketPath string) string {
- maxUnixLength := unixPathLength()
- if maxUnixLength < len(socketPath) {
- socketPath = socketPath[0:maxUnixLength]
- }
-
- logrus.Debug("connecting to socket ", socketPath)
- return socketPath
-}
-
func setupStdioChannels(streams *define.AttachStreams, conn *net.UnixConn, detachKeys []byte) (chan error, chan error) {
receiveStdoutError := make(chan error)
go func() {
diff --git a/libpod/oci_attach_linux_cgo.go b/libpod/oci_attach_linux_cgo.go
deleted file mode 100644
index d81243360..000000000
--- a/libpod/oci_attach_linux_cgo.go
+++ /dev/null
@@ -1,11 +0,0 @@
-//+build linux,cgo
-
-package libpod
-
-//#include <sys/un.h>
-// extern int unix_path_length(){struct sockaddr_un addr; return sizeof(addr.sun_path) - 1;}
-import "C"
-
-func unixPathLength() int {
- return int(C.unix_path_length())
-}
diff --git a/libpod/oci_attach_linux_nocgo.go b/libpod/oci_attach_linux_nocgo.go
deleted file mode 100644
index a514a555d..000000000
--- a/libpod/oci_attach_linux_nocgo.go
+++ /dev/null
@@ -1,7 +0,0 @@
-//+build linux,!cgo
-
-package libpod
-
-func unixPathLength() int {
- return 107
-}
diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go
index d6b63f25e..dc5dd03df 100644
--- a/libpod/oci_conmon_exec_linux.go
+++ b/libpod/oci_conmon_exec_linux.go
@@ -2,7 +2,6 @@ package libpod
import (
"fmt"
- "net"
"net/http"
"os"
"os/exec"
@@ -512,7 +511,6 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp
if err != nil {
return err
}
- socketPath := buildSocketPath(sockPath)
// 2: read from attachFd that the parent process has set up the console socket
if _, err := readConmonPipeData(pipes.attachPipe, ""); err != nil {
@@ -520,9 +518,9 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp
}
// 2: then attach
- conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: socketPath, Net: "unixpacket"})
+ conn, err := openUnixSocket(sockPath)
if err != nil {
- return errors.Wrapf(err, "failed to connect to container's attach socket: %v", socketPath)
+ return errors.Wrapf(err, "failed to connect to container's attach socket: %v", sockPath)
}
defer func() {
if err := conn.Close(); err != nil {
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 16b9f0eac..70896cda4 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -529,13 +529,12 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http.
if err != nil {
return err
}
- socketPath := buildSocketPath(attachSock)
var conn *net.UnixConn
if streamAttach {
- newConn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: socketPath, Net: "unixpacket"})
+ newConn, err := openUnixSocket(attachSock)
if err != nil {
- return errors.Wrapf(err, "failed to connect to container's attach socket: %v", socketPath)
+ return errors.Wrapf(err, "failed to connect to container's attach socket: %v", attachSock)
}
conn = newConn
defer func() {
@@ -544,7 +543,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http.
}
}()
- logrus.Debugf("Successfully connected to container %s attach socket %s", ctr.ID(), socketPath)
+ logrus.Debugf("Successfully connected to container %s attach socket %s", ctr.ID(), attachSock)
}
detachString := ctr.runtime.config.Engine.DetachKeys
@@ -769,10 +768,14 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
}
// imagePath is used by CRIU to store the actual checkpoint files
imagePath := ctr.CheckpointPath()
+ if options.PreCheckPoint {
+ imagePath = ctr.PreCheckPointPath()
+ }
// 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)
+ logrus.Debugf("Pre-dump the container %t", options.PreCheckPoint)
args := []string{}
args = append(args, r.runtimeFlags...)
args = append(args, "checkpoint")
@@ -786,6 +789,15 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
if options.TCPEstablished {
args = append(args, "--tcp-established")
}
+ if !options.PreCheckPoint && options.KeepRunning {
+ args = append(args, "--leave-running")
+ }
+ if options.PreCheckPoint {
+ args = append(args, "--pre-dump")
+ }
+ if !options.PreCheckPoint && options.WithPrevious {
+ args = append(args, "--parent-path", ctr.PreCheckPointPath())
+ }
runtimeDir, err := util.GetRuntimeDir()
if err != nil {
return err
@@ -794,6 +806,7 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
}
args = append(args, ctr.ID())
+ logrus.Debugf("the args to checkpoint: %s %s", r.path, strings.Join(args, " "))
return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...)
}
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index a67ecebd5..96687b1de 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -178,6 +178,8 @@ type CheckpointOptions struct {
Latest bool
LeaveRunning bool
TCPEstablished bool
+ PreCheckPoint bool
+ WithPrevious bool
}
type CheckpointReport struct {
@@ -196,6 +198,7 @@ type RestoreOptions struct {
Latest bool
Name string
TCPEstablished bool
+ ImportPrevious string
}
type RestoreReport struct {
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index f7a538934..2c79b6187 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -489,6 +489,8 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [
IgnoreRootfs: options.IgnoreRootFS,
IgnoreVolumes: options.IgnoreVolumes,
KeepRunning: options.LeaveRunning,
+ PreCheckPoint: options.PreCheckPoint,
+ WithPrevious: options.WithPrevious,
}
if options.All {
@@ -529,6 +531,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
IgnoreVolumes: options.IgnoreVolumes,
IgnoreStaticIP: options.IgnoreStaticIP,
IgnoreStaticMAC: options.IgnoreStaticMAC,
+ ImportPrevious: options.ImportPrevious,
}
filterFuncs := []libpod.ContainerFilter{
diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go
index c4b0b7712..2d3b9f36a 100644
--- a/pkg/domain/infra/abi/images_list.go
+++ b/pkg/domain/infra/abi/images_list.go
@@ -44,7 +44,10 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
}
e.Labels, err = img.Labels(ctx)
if err != nil {
- return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID())
+ // Ignore empty manifest lists.
+ if errors.Cause(err) != libpodImage.ErrImageIsBareList {
+ return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID())
+ }
}
ctnrs, err := img.Containers()
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 4c6b2cf5c..abc37792a 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -4,6 +4,7 @@ import (
"net"
"os"
"os/exec"
+ "strings"
"github.com/containers/podman/v2/pkg/criu"
. "github.com/containers/podman/v2/test/utils"
@@ -747,4 +748,78 @@ var _ = Describe("Podman checkpoint", func() {
// Remove exported checkpoint
os.Remove(checkpointFileName)
})
+
+ It("podman checkpoint container with --pre-checkpoint", func() {
+ if !strings.Contains(podmanTest.OCIRuntime, "runc") {
+ Skip("Test only works on runc 1.0-rc3 or higher.")
+ }
+ localRunString := getRunString([]string{ALPINE, "top"})
+ session := podmanTest.Podman(localRunString)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ cid := session.OutputToString()
+
+ result := podmanTest.Podman([]string{"container", "checkpoint", "-P", cid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ result = podmanTest.Podman([]string{"container", "checkpoint", "--with-previous", cid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+ result = podmanTest.Podman([]string{"container", "restore", cid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+ })
+
+ It("podman checkpoint container with --pre-checkpoint and export (migration)", func() {
+ if !strings.Contains(podmanTest.OCIRuntime, "runc") {
+ Skip("Test only works on runc 1.0-rc3 or higher.")
+ }
+ localRunString := getRunString([]string{ALPINE, "top"})
+ session := podmanTest.Podman(localRunString)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ cid := session.OutputToString()
+ preCheckpointFileName := "/tmp/pre-checkpoint-" + cid + ".tar.gz"
+ checkpointFileName := "/tmp/checkpoint-" + cid + ".tar.gz"
+
+ result := podmanTest.Podman([]string{"container", "checkpoint", "-P", "-e", preCheckpointFileName, cid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ result = podmanTest.Podman([]string{"container", "checkpoint", "--with-previous", "-e", checkpointFileName, cid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+ result = podmanTest.Podman([]string{"rm", "-f", cid})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+ result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName, "--import-previous", preCheckpointFileName})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ os.Remove(checkpointFileName)
+ os.Remove(preCheckpointFileName)
+ })
})
diff --git a/test/system/010-images.bats b/test/system/010-images.bats
index 76caf282b..e7c88408e 100644
--- a/test/system/010-images.bats
+++ b/test/system/010-images.bats
@@ -228,4 +228,17 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z
run_podman rmi ${aaa_name}:${aaa_tag} ${zzz_name}:${zzz_tag}
}
+# Regression test for #8931
+@test "podman images - bare manifest list" {
+ # Create an empty manifest list and list images.
+
+ run_podman inspect --format '{{.ID}}' $IMAGE
+ iid=$output
+
+ run_podman manifest create test:1.0
+ run_podman images --format '{{.ID}}' --no-trunc
+ [[ "$output" == *"sha256:$iid"* ]]
+
+ run_podman rmi test:1.0
+}
# vim: filetype=sh