mirror of
https://github.com/containers/podman.git
synced 2025-05-29 22:46:25 +08:00

Before this, for some special Podman commands (system reset, system migrate, system renumber), Podman would create a first Libpod runtime to do initialization and flag parsing, then stop that runtime and create an entirely new runtime to perform the actual task. This is an artifact of the pre-Podman 2.0 days, when there was almost no indirection between Libpod and the CLI, and we only used one runtime because we didn't need a second runtime for flag parsing and basic init. This system was clunky, and apparently, very buggy. When we migrated to SQLite, some logic was introduced where we'd select a different database location based on whether or not Libpod's StaticDir was manually set - which differed between the first invocation of Libpod and the second. So we'd get a different database for some commands (like `system reset`) and they would not be able to see existing containers, meaning they would not function properly. The immediate cause is obviously the SQLite behavior, but I'm certain there's a lot more baggage hiding behind this multiple Libpod runtime logic, so let's just refactor it out. It doesn't make sense, and complicates the code. Instead, make Reset, Renumber, and Migrate methods of the libpod Runtime. For Reset and Renumber, we can shut the runtime down afterwards to achieve the desired effect (no valid runtime after). Then pipe all of them through the ContainerEngine so cmd/podman can access them. As part of this, remove the SystemEngine part of pkg/domain. This was supposed to encompass these "special" commands, but every command in SystemEngine is actually a ContainerEngine command. Reset, Renumber, Migrate - they all need a full Libpod and access to all containers. There's no point to a separate engine if it just wraps Libpod in the exact same way as ContainerEngine. This consolidation saves us a bit more code and complexity. Signed-off-by: Matt Heon <mheon@redhat.com>
121 lines
3.2 KiB
Go
121 lines
3.2 KiB
Go
//go:build !remote
|
|
|
|
package libpod
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"syscall"
|
|
|
|
"github.com/containers/podman/v4/libpod/define"
|
|
"github.com/containers/podman/v4/pkg/rootless"
|
|
"github.com/containers/podman/v4/pkg/util"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
func (r *Runtime) stopPauseProcess() error {
|
|
if rootless.IsRootless() {
|
|
pausePidPath, err := util.GetRootlessPauseProcessPidPath()
|
|
if err != nil {
|
|
return fmt.Errorf("could not get pause process pid file path: %w", err)
|
|
}
|
|
data, err := os.ReadFile(pausePidPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("cannot read pause process pid file: %w", err)
|
|
}
|
|
pausePid, err := strconv.Atoi(string(data))
|
|
if err != nil {
|
|
return fmt.Errorf("cannot parse pause pid file %s: %w", pausePidPath, err)
|
|
}
|
|
if err := os.Remove(pausePidPath); err != nil {
|
|
return fmt.Errorf("cannot delete pause pid file %s: %w", pausePidPath, err)
|
|
}
|
|
if err := syscall.Kill(pausePid, syscall.SIGKILL); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Migrate stops the rootless pause process and performs any necessary database
|
|
// migrations that are required. It can also migrate all containers to a new OCI
|
|
// runtime, if requested.
|
|
func (r *Runtime) Migrate(newRuntime string) error {
|
|
// Acquire the alive lock and hold it.
|
|
// Ensures that we don't let other Podman commands run while we are
|
|
// rewriting things in the DB.
|
|
aliveLock, err := r.getRuntimeAliveLock()
|
|
if err != nil {
|
|
return fmt.Errorf("retrieving alive lock: %w", err)
|
|
}
|
|
aliveLock.Lock()
|
|
defer aliveLock.Unlock()
|
|
|
|
if !r.valid {
|
|
return define.ErrRuntimeStopped
|
|
}
|
|
|
|
runningContainers, err := r.GetRunningContainers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
allCtrs, err := r.state.AllContainers(false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
logrus.Infof("Stopping all containers")
|
|
for _, ctr := range runningContainers {
|
|
fmt.Printf("stopped %s\n", ctr.ID())
|
|
if err := ctr.Stop(); err != nil {
|
|
return fmt.Errorf("cannot stop container %s: %w", ctr.ID(), err)
|
|
}
|
|
}
|
|
|
|
// Did the user request a new runtime?
|
|
runtimeChangeRequested := newRuntime != ""
|
|
var requestedRuntime OCIRuntime
|
|
if runtimeChangeRequested {
|
|
runtime, exists := r.ociRuntimes[newRuntime]
|
|
if !exists {
|
|
return fmt.Errorf("change to runtime %q requested but no such runtime is defined: %w", newRuntime, define.ErrInvalidArg)
|
|
}
|
|
requestedRuntime = runtime
|
|
}
|
|
|
|
for _, ctr := range allCtrs {
|
|
needsWrite := false
|
|
|
|
// Reset pause process location
|
|
oldLocation := filepath.Join(ctr.state.RunDir, "conmon.pid")
|
|
if ctr.config.ConmonPidFile == oldLocation {
|
|
logrus.Infof("Changing conmon PID file for %s", ctr.ID())
|
|
ctr.config.ConmonPidFile = filepath.Join(ctr.config.StaticDir, "conmon.pid")
|
|
needsWrite = true
|
|
}
|
|
|
|
// Reset runtime
|
|
if runtimeChangeRequested && ctr.config.OCIRuntime != newRuntime {
|
|
logrus.Infof("Resetting container %s runtime to runtime %s", ctr.ID(), newRuntime)
|
|
ctr.config.OCIRuntime = newRuntime
|
|
ctr.ociRuntime = requestedRuntime
|
|
|
|
needsWrite = true
|
|
}
|
|
|
|
if needsWrite {
|
|
if err := r.state.RewriteContainerConfig(ctr, ctr.config); err != nil {
|
|
return fmt.Errorf("rewriting config for container %s: %w", ctr.ID(), err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return r.stopPauseProcess()
|
|
}
|