diff options
-rw-r--r-- | cmd/podman/containers/checkpoint.go | 1 | ||||
-rw-r--r-- | cmd/podman/containers/restore.go | 1 | ||||
-rw-r--r-- | docs/source/markdown/podman-container-checkpoint.1.md | 8 | ||||
-rw-r--r-- | docs/source/markdown/podman-container-restore.1.md | 9 | ||||
-rw-r--r-- | libpod/container_api.go | 3 | ||||
-rw-r--r-- | libpod/networking_linux.go | 20 | ||||
-rw-r--r-- | libpod/oci_conmon_linux.go | 6 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers.go | 4 | ||||
-rw-r--r-- | pkg/bindings/containers/types.go | 4 | ||||
-rw-r--r-- | pkg/bindings/containers/types_checkpoint_options.go | 45 | ||||
-rw-r--r-- | pkg/bindings/containers/types_restore_options.go | 15 | ||||
-rw-r--r-- | pkg/domain/entities/containers.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 1 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 8 | ||||
-rw-r--r-- | pkg/machine/ignition.go | 2 | ||||
-rw-r--r-- | test/e2e/checkpoint_test.go | 38 |
16 files changed, 159 insertions, 8 deletions
diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go index d92bc3e5e..e8dd25978 100644 --- a/cmd/podman/containers/checkpoint.go +++ b/cmd/podman/containers/checkpoint.go @@ -55,6 +55,7 @@ func init() { flags.BoolVarP(&checkpointOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files") flags.BoolVarP(&checkpointOptions.LeaveRunning, "leave-running", "R", false, "Leave the container running after writing checkpoint to disk") flags.BoolVar(&checkpointOptions.TCPEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections") + flags.BoolVar(&checkpointOptions.FileLocks, "file-locks", false, "Checkpoint a container with file locks") flags.BoolVarP(&checkpointOptions.All, "all", "a", false, "Checkpoint all running containers") exportFlagName := "export" diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go index 4ac14001a..cf0ad5f80 100644 --- a/cmd/podman/containers/restore.go +++ b/cmd/podman/containers/restore.go @@ -53,6 +53,7 @@ func init() { flags.BoolVarP(&restoreOptions.All, "all", "a", false, "Restore all checkpointed containers") flags.BoolVarP(&restoreOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files") flags.BoolVar(&restoreOptions.TCPEstablished, "tcp-established", false, "Restore a container with established TCP connections") + flags.BoolVar(&restoreOptions.FileLocks, "file-locks", false, "Restore a container with file locks") importFlagName := "import" flags.StringVarP(&restoreOptions.Import, importFlagName, "i", "", "Restore from exported checkpoint archive (tar.gz)") diff --git a/docs/source/markdown/podman-container-checkpoint.1.md b/docs/source/markdown/podman-container-checkpoint.1.md index 1faa40a94..200920ca9 100644 --- a/docs/source/markdown/podman-container-checkpoint.1.md +++ b/docs/source/markdown/podman-container-checkpoint.1.md @@ -110,6 +110,14 @@ restore. Defaults to not checkpointing *containers* with established TCP connections.\ The default is **false**. +#### **--file-locks** + +Checkpoint a *container* with file locks. If an application running in the container +is using file locks, this OPTION is required during checkpoint and restore. Otherwise +checkpointing *containers* with file locks is expected to fail. If file locks are not +used, this option is ignored.\ +The default is **false**. + #### **--with-previous** Check out the *container* with previous criu image files in pre-dump. It only works on `runc 1.0-rc3` or `higher`.\ diff --git a/docs/source/markdown/podman-container-restore.1.md b/docs/source/markdown/podman-container-restore.1.md index 962770ea0..10477fc77 100644 --- a/docs/source/markdown/podman-container-restore.1.md +++ b/docs/source/markdown/podman-container-restore.1.md @@ -81,6 +81,7 @@ to import a checkpointed *container* from another host.\ 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`. +*IMPORTANT: This OPTION is not supported on the remote client.* #### **--name**, **-n**=*name* @@ -142,6 +143,14 @@ option is ignored. Defaults to not restoring *containers* with established TCP connections.\ The default is **false**. +#### **--file-locks** + +Restore a *container* with file locks. This option is required to +restore file locks from a checkpoint image. If the checkpoint image +does not contain file locks, this option is ignored. Defaults to not +restoring file locks.\ +The default is **false**. + ## EXAMPLE Restores the container "mywebserver". ``` diff --git a/libpod/container_api.go b/libpod/container_api.go index a41bb03df..7ae9f497c 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -798,6 +798,9 @@ type ContainerCheckpointOptions struct { // how much time each component in the stack requires to // checkpoint a container. PrintStats bool + // FileLocks tells the API to checkpoint/restore a container + // with file-locks + FileLocks bool } // Checkpoint checkpoints a container diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 9be600bb4..314a74427 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -4,6 +4,7 @@ package libpod import ( "crypto/rand" + "crypto/sha1" "fmt" "io/ioutil" "net" @@ -400,10 +401,7 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { return nil, nil } var rootlessNetNS *RootlessNetNS - runDir, err := util.GetRuntimeDir() - if err != nil { - return nil, err - } + runDir := r.config.Engine.TmpDir lfile := filepath.Join(runDir, "rootless-netns.lock") lock, err := lockfile.GetLockfile(lfile) @@ -429,7 +427,15 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { if err != nil { return nil, err } - path := filepath.Join(nsDir, rootlessNetNsName) + + // create a hash from the static dir + // the cleanup will check if there are running containers + // if you run a several libpod instances with different root/runroot directories this check will fail + // we want one netns for each libpod static dir so we use the hash to prevent name collisions + hash := sha1.Sum([]byte(r.config.Engine.StaticDir)) + netnsName := fmt.Sprintf("%s-%x", rootlessNetNsName, hash[:10]) + + path := filepath.Join(nsDir, netnsName) ns, err := ns.GetNS(path) if err != nil { if !new { @@ -437,8 +443,8 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { return nil, errors.Wrap(err, "error getting rootless network namespace") } // create a new namespace - logrus.Debug("creating rootless network namespace") - ns, err = netns.NewNSWithName(rootlessNetNsName) + logrus.Debugf("creating rootless network namespace with name %q", netnsName) + ns, err = netns.NewNSWithName(netnsName) if err != nil { return nil, errors.Wrap(err, "error creating rootless network namespace") } diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 533a0d78b..e007d0b92 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -794,6 +794,9 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container if options.TCPEstablished { args = append(args, "--tcp-established") } + if options.FileLocks { + args = append(args, "--file-locks") + } if !options.PreCheckPoint && options.KeepRunning { args = append(args, "--leave-running") } @@ -1101,6 +1104,9 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co if restoreOptions.TCPEstablished { args = append(args, "--runtime-opt", "--tcp-established") } + if restoreOptions.FileLocks { + args = append(args, "--runtime-opt", "--file-locks") + } if restoreOptions.Pod != "" { mountLabel := ctr.config.MountLabel processLabel := ctr.config.ProcessLabel diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index b0cec2b1f..d5da22a91 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -219,6 +219,8 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) { Export bool `schema:"export"` IgnoreRootFS bool `schema:"ignoreRootFS"` PrintStats bool `schema:"printStats"` + PreCheckpoint bool `schema:"preCheckpoint"` + WithPrevious bool `schema:"withPrevious"` }{ // override any golang type defaults } @@ -242,6 +244,8 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) { TCPEstablished: query.TCPEstablished, IgnoreRootFS: query.IgnoreRootFS, PrintStats: query.PrintStats, + PreCheckPoint: query.PreCheckpoint, + WithPrevious: query.WithPrevious, } if query.Export { diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 4bbb4a62b..81a53a549 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -51,6 +51,9 @@ type CheckpointOptions struct { LeaveRunning *bool TCPEstablished *bool PrintStats *bool + PreCheckpoint *bool + WithPrevious *bool + FileLocks *bool } //go:generate go run ../generator/generator.go RestoreOptions @@ -67,6 +70,7 @@ type RestoreOptions struct { Pod *string PrintStats *bool PublishPorts []string + FileLocks *bool } //go:generate go run ../generator/generator.go CreateOptions diff --git a/pkg/bindings/containers/types_checkpoint_options.go b/pkg/bindings/containers/types_checkpoint_options.go index b606922e0..391748d76 100644 --- a/pkg/bindings/containers/types_checkpoint_options.go +++ b/pkg/bindings/containers/types_checkpoint_options.go @@ -106,3 +106,48 @@ func (o *CheckpointOptions) GetPrintStats() bool { } return *o.PrintStats } + +// WithPreCheckpoint set field PreCheckpoint to given value +func (o *CheckpointOptions) WithPreCheckpoint(value bool) *CheckpointOptions { + o.PreCheckpoint = &value + return o +} + +// GetPreCheckpoint returns value of field PreCheckpoint +func (o *CheckpointOptions) GetPreCheckpoint() bool { + if o.PreCheckpoint == nil { + var z bool + return z + } + return *o.PreCheckpoint +} + +// WithWithPrevious set field WithPrevious to given value +func (o *CheckpointOptions) WithWithPrevious(value bool) *CheckpointOptions { + o.WithPrevious = &value + return o +} + +// GetWithPrevious returns value of field WithPrevious +func (o *CheckpointOptions) GetWithPrevious() bool { + if o.WithPrevious == nil { + var z bool + return z + } + return *o.WithPrevious +} + +// WithFileLocks set field FileLocks to given value +func (o *CheckpointOptions) WithFileLocks(value bool) *CheckpointOptions { + o.FileLocks = &value + return o +} + +// GetFileLocks returns value of field FileLocks +func (o *CheckpointOptions) GetFileLocks() bool { + if o.FileLocks == nil { + var z bool + return z + } + return *o.FileLocks +} diff --git a/pkg/bindings/containers/types_restore_options.go b/pkg/bindings/containers/types_restore_options.go index 8817b834b..7af2bba32 100644 --- a/pkg/bindings/containers/types_restore_options.go +++ b/pkg/bindings/containers/types_restore_options.go @@ -181,3 +181,18 @@ func (o *RestoreOptions) GetPublishPorts() []string { } return o.PublishPorts } + +// WithFileLocks set field FileLocks to given value +func (o *RestoreOptions) WithFileLocks(value bool) *RestoreOptions { + o.FileLocks = &value + return o +} + +// GetFileLocks returns value of field FileLocks +func (o *RestoreOptions) GetFileLocks() bool { + if o.FileLocks == nil { + var z bool + return z + } + return *o.FileLocks +} diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 1a4019bb1..1677c067f 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -191,6 +191,7 @@ type CheckpointOptions struct { WithPrevious bool Compression archive.Compression PrintStats bool + FileLocks bool } type CheckpointReport struct { @@ -215,6 +216,7 @@ type RestoreOptions struct { PublishPorts []string Pod string PrintStats bool + FileLocks bool } type RestoreReport struct { diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 69c628669..e04c7a38a 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -516,6 +516,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ WithPrevious: options.WithPrevious, Compression: options.Compression, PrintStats: options.PrintStats, + FileLocks: options.FileLocks, } if options.All { diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 152e3c302..2127f8749 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -303,12 +303,15 @@ func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, opts entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { options := new(containers.CheckpointOptions) + options.WithFileLocks(opts.FileLocks) options.WithIgnoreRootfs(opts.IgnoreRootFS) options.WithKeep(opts.Keep) options.WithExport(opts.Export) options.WithTCPEstablished(opts.TCPEstablished) options.WithPrintStats(opts.PrintStats) + options.WithPreCheckpoint(opts.PreCheckPoint) options.WithLeaveRunning(opts.LeaveRunning) + options.WithWithPrevious(opts.WithPrevious) var ( err error @@ -345,7 +348,12 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ } func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, opts entities.RestoreOptions) ([]*entities.RestoreReport, error) { + if opts.ImportPrevious != "" { + return nil, fmt.Errorf("--import-previous is not supported on the remote client") + } + options := new(containers.RestoreOptions) + options.WithFileLocks(opts.FileLocks) options.WithIgnoreRootfs(opts.IgnoreRootFS) options.WithIgnoreVolumes(opts.IgnoreVolumes) options.WithIgnoreStaticIP(opts.IgnoreStaticIP) diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 42d729458..e19940b22 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -81,7 +81,7 @@ func NewIgnitionFile(ign DynamicIgnition) error { // so a listening host knows it can being interacting with it ready := `[Unit] Requires=dev-virtio\\x2dports-%s.device -After=remove-moby.service +After=remove-moby.service sshd.socket sshd.service OnFailure=emergency.target OnFailureJobMode=isolate [Service] diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index 37935fd38..4963b04fc 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -899,6 +899,7 @@ var _ = Describe("Podman checkpoint", func() { }) It("podman checkpoint container with --pre-checkpoint and export (migration)", func() { + SkipIfRemote("--import-previous is not yet supported on the remote client") if !strings.Contains(podmanTest.OCIRuntime, "runc") { Skip("Test only works on runc 1.0-rc3 or higher.") } @@ -1339,4 +1340,41 @@ var _ = Describe("Podman checkpoint", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) + It("podman checkpoint and restore container with --file-locks", func() { + if !strings.Contains(podmanTest.OCIRuntime, "runc") { + // TODO: Enable test for crun when this feature has been released + // https://github.com/containers/crun/pull/783 + Skip("FIXME: requires crun >= 1.4") + } + localRunString := getRunString([]string{"--name", "test_name", ALPINE, "flock", "test.lock", "sleep", "100"}) + session := podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // Checkpoint is expected to fail without --file-locks + result := podmanTest.Podman([]string{"container", "checkpoint", "test_name"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("criu failed")) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + + // Checkpoint is expected to succeed with --file-locks + result = podmanTest.Podman([]string{"container", "checkpoint", "--file-locks", "test_name"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) + + result = podmanTest.Podman([]string{"container", "restore", "--file-locks", "test_name"}) + result.WaitWithDefaultTimeout() + + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + result = podmanTest.Podman([]string{"rm", "-t", "0", "-f", "test_name"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + }) }) |