mirror of
https://github.com/containers/podman.git
synced 2025-05-17 15:18:43 +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>
287 lines
8.0 KiB
Go
287 lines
8.0 KiB
Go
//go:build !remote
|
|
|
|
package libpod
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/containers/common/libimage"
|
|
"github.com/containers/common/libnetwork/types"
|
|
"github.com/containers/podman/v4/libpod/define"
|
|
"github.com/containers/podman/v4/pkg/errorhandling"
|
|
"github.com/containers/podman/v4/pkg/util"
|
|
"github.com/containers/storage"
|
|
"github.com/containers/storage/pkg/lockfile"
|
|
stypes "github.com/containers/storage/types"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// removeAllDirs removes all Podman storage directories. It is intended to be
|
|
// used as a backup for reset() when that function cannot be used due to
|
|
// failures in initializing libpod.
|
|
// It does not expect that all the directories match what is in use by Podman,
|
|
// as this is a common failure point for `system reset`. As such, our ability to
|
|
// interface with containers and pods is somewhat limited.
|
|
// This function assumes that we do not have a working c/storage store.
|
|
func (r *Runtime) removeAllDirs() error {
|
|
var lastErr error
|
|
|
|
// Grab the runtime alive lock.
|
|
// This ensures that no other Podman process can run while we are doing
|
|
// a reset, so no race conditions with containers/pods/etc being created
|
|
// while we are resetting storage.
|
|
// TODO: maybe want a helper for getting the path? This is duped from
|
|
// runtime.go
|
|
runtimeAliveLock := filepath.Join(r.config.Engine.TmpDir, "alive.lck")
|
|
aliveLock, err := lockfile.GetLockFile(runtimeAliveLock)
|
|
if err != nil {
|
|
logrus.Errorf("Lock runtime alive lock %s: %v", runtimeAliveLock, err)
|
|
} else {
|
|
aliveLock.Lock()
|
|
defer aliveLock.Unlock()
|
|
}
|
|
|
|
// We do not have a store - so we can't really try and remove containers
|
|
// or pods or volumes...
|
|
// Try and remove the directories, in hopes that they are unmounted.
|
|
// This is likely to fail but it's the best we can do.
|
|
|
|
// Volume path
|
|
if err := os.RemoveAll(r.config.Engine.VolumePath); err != nil {
|
|
lastErr = fmt.Errorf("removing volume path: %w", err)
|
|
}
|
|
|
|
// Tmpdir
|
|
if err := os.RemoveAll(r.config.Engine.TmpDir); err != nil {
|
|
if lastErr != nil {
|
|
logrus.Errorf("Reset: %v", lastErr)
|
|
}
|
|
lastErr = fmt.Errorf("removing tmp dir: %w", err)
|
|
}
|
|
|
|
// Runroot
|
|
if err := os.RemoveAll(r.storageConfig.RunRoot); err != nil {
|
|
if lastErr != nil {
|
|
logrus.Errorf("Reset: %v", lastErr)
|
|
}
|
|
lastErr = fmt.Errorf("removing run root: %w", err)
|
|
}
|
|
|
|
// Static dir
|
|
if err := os.RemoveAll(r.config.Engine.StaticDir); err != nil {
|
|
if lastErr != nil {
|
|
logrus.Errorf("Reset: %v", lastErr)
|
|
}
|
|
lastErr = fmt.Errorf("removing static dir: %w", err)
|
|
}
|
|
|
|
// Graph root
|
|
if err := os.RemoveAll(r.storageConfig.GraphRoot); err != nil {
|
|
if lastErr != nil {
|
|
logrus.Errorf("Reset: %v", lastErr)
|
|
}
|
|
lastErr = fmt.Errorf("removing graph root: %w", err)
|
|
}
|
|
|
|
return lastErr
|
|
}
|
|
|
|
// Reset removes all Libpod files.
|
|
// All containers, images, volumes, pods, and networks will be removed.
|
|
// Calls Shutdown(), rendering the runtime unusable after this is run.
|
|
func (r *Runtime) Reset(ctx context.Context) error {
|
|
// Acquire the alive lock and hold it.
|
|
// Ensures that we don't let other Podman commands run while we are
|
|
// removing everything.
|
|
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
|
|
}
|
|
|
|
var timeout *uint
|
|
pods, err := r.GetAllPods()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, p := range pods {
|
|
if ctrs, err := r.RemovePod(ctx, p, true, true, timeout); err != nil {
|
|
if errors.Is(err, define.ErrNoSuchPod) {
|
|
continue
|
|
}
|
|
for ctr, err := range ctrs {
|
|
if err != nil {
|
|
logrus.Errorf("Error removing pod %s container %s: %v", p.ID(), ctr, err)
|
|
}
|
|
}
|
|
logrus.Errorf("Removing Pod %s: %v", p.ID(), err)
|
|
}
|
|
}
|
|
|
|
ctrs, err := r.GetAllContainers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, c := range ctrs {
|
|
if ctrs, _, err := r.RemoveContainerAndDependencies(ctx, c, true, true, timeout); err != nil {
|
|
for ctr, err := range ctrs {
|
|
logrus.Errorf("Error removing container %s: %v", ctr, err)
|
|
}
|
|
|
|
if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) {
|
|
continue
|
|
}
|
|
|
|
logrus.Errorf("Removing container %s: %v", c.ID(), err)
|
|
|
|
// There's no point trying a storage container removal
|
|
// here. High likelihood it doesn't work
|
|
// (RemoveStorageContainer will refuse to remove if a
|
|
// container exists in the Libpod database) and, even if
|
|
// it would, we'll get the container later on during
|
|
// image removal.
|
|
}
|
|
}
|
|
|
|
if err := r.stopPauseProcess(); err != nil {
|
|
logrus.Errorf("Stopping pause process: %v", err)
|
|
}
|
|
|
|
// Volumes before images, as volumes can mount images.
|
|
volumes, err := r.state.AllVolumes()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, v := range volumes {
|
|
if err := r.RemoveVolume(ctx, v, true, timeout); err != nil {
|
|
if errors.Is(err, define.ErrNoSuchVolume) {
|
|
continue
|
|
}
|
|
logrus.Errorf("Removing volume %s: %v", v.config.Name, err)
|
|
}
|
|
}
|
|
|
|
// Set force and ignore.
|
|
// Ignore shouldn't be necessary, but it seems safer. We want everything
|
|
// gone anyways...
|
|
rmiOptions := &libimage.RemoveImagesOptions{
|
|
Force: true,
|
|
Ignore: true,
|
|
RemoveContainerFunc: r.RemoveContainersForImageCallback(ctx),
|
|
Filters: []string{"readonly=false"},
|
|
}
|
|
if _, rmiErrors := r.LibimageRuntime().RemoveImages(ctx, nil, rmiOptions); rmiErrors != nil {
|
|
return errorhandling.JoinErrors(rmiErrors)
|
|
}
|
|
|
|
// remove all networks
|
|
nets, err := r.network.NetworkList()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, net := range nets {
|
|
// do not delete the default network
|
|
if net.Name == r.network.DefaultNetworkName() {
|
|
continue
|
|
}
|
|
// ignore not exists errors because of the TOCTOU problem
|
|
if err := r.network.NetworkRemove(net.Name); err != nil && !errors.Is(err, types.ErrNoSuchNetwork) {
|
|
logrus.Errorf("Removing network %s: %v", net.Name, err)
|
|
}
|
|
}
|
|
|
|
xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR")
|
|
if xdgRuntimeDir != "" {
|
|
xdgRuntimeDir, err = filepath.EvalSymlinks(xdgRuntimeDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
_, prevError := r.store.Shutdown(true)
|
|
graphRoot := filepath.Clean(r.store.GraphRoot())
|
|
if graphRoot == xdgRuntimeDir {
|
|
if prevError != nil {
|
|
logrus.Error(prevError)
|
|
}
|
|
prevError = fmt.Errorf("failed to remove runtime graph root dir %s, since it is the same as XDG_RUNTIME_DIR", graphRoot)
|
|
} else {
|
|
if err := os.RemoveAll(graphRoot); err != nil {
|
|
if prevError != nil {
|
|
logrus.Error(prevError)
|
|
}
|
|
prevError = err
|
|
}
|
|
}
|
|
runRoot := filepath.Clean(r.store.RunRoot())
|
|
if runRoot == xdgRuntimeDir {
|
|
if prevError != nil {
|
|
logrus.Error(prevError)
|
|
}
|
|
prevError = fmt.Errorf("failed to remove runtime root dir %s, since it is the same as XDG_RUNTIME_DIR", runRoot)
|
|
} else {
|
|
if err := os.RemoveAll(runRoot); err != nil {
|
|
if prevError != nil {
|
|
logrus.Error(prevError)
|
|
}
|
|
prevError = err
|
|
}
|
|
}
|
|
runtimeDir, err := util.GetRootlessRuntimeDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tempDir := r.config.Engine.TmpDir
|
|
if tempDir == runtimeDir {
|
|
tempDir = filepath.Join(tempDir, "containers")
|
|
}
|
|
if filepath.Clean(tempDir) == xdgRuntimeDir {
|
|
if prevError != nil {
|
|
logrus.Error(prevError)
|
|
}
|
|
prevError = fmt.Errorf("failed to remove runtime tmpdir %s, since it is the same as XDG_RUNTIME_DIR", tempDir)
|
|
} else {
|
|
if err := os.RemoveAll(tempDir); err != nil {
|
|
if prevError != nil {
|
|
logrus.Error(prevError)
|
|
}
|
|
prevError = err
|
|
}
|
|
}
|
|
if storageConfPath, err := storage.DefaultConfigFile(); err == nil {
|
|
switch storageConfPath {
|
|
case stypes.SystemConfigFile:
|
|
break
|
|
default:
|
|
if _, err = os.Stat(storageConfPath); err == nil {
|
|
fmt.Printf(" A %q config file exists.\n", storageConfPath)
|
|
fmt.Println("Remove this file if you did not modify the configuration.")
|
|
}
|
|
}
|
|
} else {
|
|
if prevError != nil {
|
|
logrus.Error(prevError)
|
|
}
|
|
prevError = err
|
|
}
|
|
|
|
// Shut down the runtime, it's no longer usable after mass-deletion.
|
|
if err := r.Shutdown(false); err != nil {
|
|
if prevError != nil {
|
|
logrus.Error(prevError)
|
|
}
|
|
prevError = err
|
|
}
|
|
|
|
return prevError
|
|
}
|