mirror of
https://github.com/containers/podman.git
synced 2025-10-17 11:14:40 +08:00
Merge pull request #26895 from intirix/tcp-close
Add support for criu's tcp-close functionality.
This commit is contained in:
@ -52,6 +52,7 @@ func init() {
|
|||||||
flags.BoolVarP(&restoreOptions.All, "all", "a", false, "Restore all checkpointed containers")
|
flags.BoolVarP(&restoreOptions.All, "all", "a", false, "Restore all checkpointed containers")
|
||||||
flags.BoolVarP(&restoreOptions.Keep, "keep", "k", false, "Keep all temporary checkpoint files")
|
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.TCPEstablished, "tcp-established", false, "Restore a container with established TCP connections")
|
||||||
|
flags.BoolVar(&restoreOptions.TCPClose, "tcp-close", false, "Restore a container and close all TCP connections")
|
||||||
flags.BoolVar(&restoreOptions.FileLocks, "file-locks", false, "Restore a container with file locks")
|
flags.BoolVar(&restoreOptions.FileLocks, "file-locks", false, "Restore a container with file locks")
|
||||||
|
|
||||||
importFlagName := "import"
|
importFlagName := "import"
|
||||||
|
@ -150,6 +150,14 @@ initial *container* start, with a new set of port forwarding rules.
|
|||||||
|
|
||||||
For more details, see **[podman run --publish](podman-run.1.md#--publish)**.
|
For more details, see **[podman run --publish](podman-run.1.md#--publish)**.
|
||||||
|
|
||||||
|
#### **--tcp-close**
|
||||||
|
|
||||||
|
Restore a *container* and close all TCP connections. This option is useful
|
||||||
|
when TCP connections are not needed after restore or when connections
|
||||||
|
will be reestablished by the application. If the checkpoint image was created with
|
||||||
|
**--tcp-close**, this option should be used during restore.\
|
||||||
|
The default is **false**.
|
||||||
|
|
||||||
#### **--tcp-established**
|
#### **--tcp-established**
|
||||||
|
|
||||||
Restore a *container* with established TCP connections. If the checkpoint image
|
Restore a *container* with established TCP connections. If the checkpoint image
|
||||||
|
@ -965,6 +965,8 @@ type ContainerCheckpointOptions struct {
|
|||||||
// TCPEstablished tells the API to checkpoint a container
|
// TCPEstablished tells the API to checkpoint a container
|
||||||
// even if it contains established TCP connections
|
// even if it contains established TCP connections
|
||||||
TCPEstablished bool
|
TCPEstablished bool
|
||||||
|
// TCPClose tells the API to close all TCP connections during restore
|
||||||
|
TCPClose bool
|
||||||
// TargetFile tells the API to read (or write) the checkpoint image
|
// TargetFile tells the API to read (or write) the checkpoint image
|
||||||
// from (or to) the filename set in TargetFile
|
// from (or to) the filename set in TargetFile
|
||||||
TargetFile string
|
TargetFile string
|
||||||
|
@ -1082,6 +1082,9 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
|
|||||||
if restoreOptions.TCPEstablished {
|
if restoreOptions.TCPEstablished {
|
||||||
args = append(args, "--runtime-opt", "--tcp-established")
|
args = append(args, "--runtime-opt", "--tcp-established")
|
||||||
}
|
}
|
||||||
|
if restoreOptions.TCPClose {
|
||||||
|
args = append(args, "--runtime-opt", "--tcp-close")
|
||||||
|
}
|
||||||
if restoreOptions.FileLocks {
|
if restoreOptions.FileLocks {
|
||||||
args = append(args, "--runtime-opt", "--file-locks")
|
args = append(args, "--runtime-opt", "--file-locks")
|
||||||
}
|
}
|
||||||
|
@ -307,6 +307,7 @@ func Restore(w http.ResponseWriter, r *http.Request) {
|
|||||||
query := struct {
|
query := struct {
|
||||||
Keep bool `schema:"keep"`
|
Keep bool `schema:"keep"`
|
||||||
TCPEstablished bool `schema:"tcpEstablished"`
|
TCPEstablished bool `schema:"tcpEstablished"`
|
||||||
|
TCPClose bool `schema:"tcpClose"`
|
||||||
Import bool `schema:"import"`
|
Import bool `schema:"import"`
|
||||||
Name string `schema:"name"`
|
Name string `schema:"name"`
|
||||||
IgnoreRootFS bool `schema:"ignoreRootFS"`
|
IgnoreRootFS bool `schema:"ignoreRootFS"`
|
||||||
@ -329,6 +330,7 @@ func Restore(w http.ResponseWriter, r *http.Request) {
|
|||||||
Name: query.Name,
|
Name: query.Name,
|
||||||
Keep: query.Keep,
|
Keep: query.Keep,
|
||||||
TCPEstablished: query.TCPEstablished,
|
TCPEstablished: query.TCPEstablished,
|
||||||
|
TCPClose: query.TCPClose,
|
||||||
IgnoreRootFS: query.IgnoreRootFS,
|
IgnoreRootFS: query.IgnoreRootFS,
|
||||||
IgnoreVolumes: query.IgnoreVolumes,
|
IgnoreVolumes: query.IgnoreVolumes,
|
||||||
IgnoreStaticIP: query.IgnoreStaticIP,
|
IgnoreStaticIP: query.IgnoreStaticIP,
|
||||||
|
@ -1636,7 +1636,11 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
|||||||
// - in: query
|
// - in: query
|
||||||
// name: tcpEstablished
|
// name: tcpEstablished
|
||||||
// type: boolean
|
// type: boolean
|
||||||
// description: checkpoint a container with established TCP connections
|
// description: restore a container with established TCP connections
|
||||||
|
// - in: query
|
||||||
|
// name: tcpClose
|
||||||
|
// type: boolean
|
||||||
|
// description: restore a container but close the TCP connections
|
||||||
// - in: query
|
// - in: query
|
||||||
// name: import
|
// name: import
|
||||||
// type: boolean
|
// type: boolean
|
||||||
|
@ -83,6 +83,7 @@ type RestoreOptions struct {
|
|||||||
Keep *bool
|
Keep *bool
|
||||||
Name *string
|
Name *string
|
||||||
TCPEstablished *bool
|
TCPEstablished *bool
|
||||||
|
TCPClose *bool
|
||||||
Pod *string
|
Pod *string
|
||||||
PrintStats *bool
|
PrintStats *bool
|
||||||
PublishPorts []string
|
PublishPorts []string
|
||||||
|
@ -152,6 +152,21 @@ func (o *RestoreOptions) GetTCPEstablished() bool {
|
|||||||
return *o.TCPEstablished
|
return *o.TCPEstablished
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTCPClose set field TCPClose to given value
|
||||||
|
func (o *RestoreOptions) WithTCPClose(value bool) *RestoreOptions {
|
||||||
|
o.TCPClose = &value
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTCPClose returns value of field TCPClose
|
||||||
|
func (o *RestoreOptions) GetTCPClose() bool {
|
||||||
|
if o.TCPClose == nil {
|
||||||
|
var z bool
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
return *o.TCPClose
|
||||||
|
}
|
||||||
|
|
||||||
// WithPod set field Pod to given value
|
// WithPod set field Pod to given value
|
||||||
func (o *RestoreOptions) WithPod(value string) *RestoreOptions {
|
func (o *RestoreOptions) WithPod(value string) *RestoreOptions {
|
||||||
o.Pod = &value
|
o.Pod = &value
|
||||||
|
@ -227,6 +227,7 @@ type RestoreOptions struct {
|
|||||||
Latest bool
|
Latest bool
|
||||||
Name string
|
Name string
|
||||||
TCPEstablished bool
|
TCPEstablished bool
|
||||||
|
TCPClose bool
|
||||||
ImportPrevious string
|
ImportPrevious string
|
||||||
PublishPorts []string
|
PublishPorts []string
|
||||||
Pod string
|
Pod string
|
||||||
|
@ -738,6 +738,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
|
|||||||
restoreOptions := libpod.ContainerCheckpointOptions{
|
restoreOptions := libpod.ContainerCheckpointOptions{
|
||||||
Keep: options.Keep,
|
Keep: options.Keep,
|
||||||
TCPEstablished: options.TCPEstablished,
|
TCPEstablished: options.TCPEstablished,
|
||||||
|
TCPClose: options.TCPClose,
|
||||||
TargetFile: options.Import,
|
TargetFile: options.Import,
|
||||||
Name: options.Name,
|
Name: options.Name,
|
||||||
IgnoreRootfs: options.IgnoreRootFS,
|
IgnoreRootfs: options.IgnoreRootFS,
|
||||||
|
@ -472,6 +472,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
|
|||||||
options.WithKeep(opts.Keep)
|
options.WithKeep(opts.Keep)
|
||||||
options.WithName(opts.Name)
|
options.WithName(opts.Name)
|
||||||
options.WithTCPEstablished(opts.TCPEstablished)
|
options.WithTCPEstablished(opts.TCPEstablished)
|
||||||
|
options.WithTCPClose(opts.TCPClose)
|
||||||
options.WithPod(opts.Pod)
|
options.WithPod(opts.Pod)
|
||||||
options.WithPrintStats(opts.PrintStats)
|
options.WithPrintStats(opts.PrintStats)
|
||||||
options.WithPublishPorts(opts.PublishPorts)
|
options.WithPublishPorts(opts.PublishPorts)
|
||||||
|
@ -402,6 +402,55 @@ var _ = Describe("Podman checkpoint", func() {
|
|||||||
conn.Close()
|
conn.Close()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman restore container with tcp-close", func() {
|
||||||
|
Skip("FIXME: #26289 - Rawhide only issue, skip for now")
|
||||||
|
|
||||||
|
// Start a container with redis (which listens on tcp port)
|
||||||
|
localRunString := getRunString([]string{REDIS_IMAGE})
|
||||||
|
session := podmanTest.Podman(localRunString)
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(ExitCleanly())
|
||||||
|
cid := session.OutputToString()
|
||||||
|
if !WaitContainerReady(podmanTest, cid, "Ready to accept connections", 20, 1) {
|
||||||
|
Fail("Container failed to get ready")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get container IP
|
||||||
|
IP := podmanTest.PodmanExitCleanly("inspect", cid, fmt.Sprintf("--format={{(index .NetworkSettings.Networks \"%s\").IPAddress}}", netname))
|
||||||
|
|
||||||
|
// Open a network connection to the redis server
|
||||||
|
conn, err := net.DialTimeout("tcp4", IP.OutputToString()+":6379", time.Duration(3)*time.Second)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Checkpoint with --tcp-established since we have an open connection
|
||||||
|
podmanTest.PodmanExitCleanly("container", "checkpoint", cid, "--tcp-established")
|
||||||
|
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
|
||||||
|
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
|
||||||
|
|
||||||
|
// Restore should fail as the checkpoint image contains established TCP connections
|
||||||
|
result := podmanTest.Podman([]string{"container", "restore", cid})
|
||||||
|
result.WaitWithDefaultTimeout()
|
||||||
|
|
||||||
|
// default message when using crun
|
||||||
|
expectStderr := "crun: CRIU restoring failed -52. Please check CRIU logfile"
|
||||||
|
if podmanTest.OCIRuntime == "runc" {
|
||||||
|
expectStderr = "runc: criu failed: type NOTIFY errno 0"
|
||||||
|
}
|
||||||
|
if !IsRemote() {
|
||||||
|
// This part is only seen with podman local, never remote
|
||||||
|
expectStderr = "OCI runtime error: " + expectStderr
|
||||||
|
}
|
||||||
|
Expect(result).Should(ExitWithError(125, expectStderr))
|
||||||
|
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
|
||||||
|
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
|
||||||
|
|
||||||
|
// Now it should work thanks to "--tcp-close"
|
||||||
|
podmanTest.PodmanExitCleanly("container", "restore", cid, "--tcp-close")
|
||||||
|
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
|
||||||
|
Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
|
||||||
|
})
|
||||||
|
|
||||||
It("podman checkpoint with --leave-running", func() {
|
It("podman checkpoint with --leave-running", func() {
|
||||||
localRunString := getRunString([]string{ALPINE, "top"})
|
localRunString := getRunString([]string{ALPINE, "top"})
|
||||||
session := podmanTest.Podman(localRunString)
|
session := podmanTest.Podman(localRunString)
|
||||||
|
Reference in New Issue
Block a user