mirror of
https://github.com/containers/podman.git
synced 2025-09-18 15:54:49 +08:00

Add support for loading images directly from machine paths to avoid unnecessary file transfers when the image archive is already accessible on the running machine through mounted directories. Changes include: - New /libpod/local/images/load API endpoint for direct machine loading - Machine detection and path mapping functionality - Fallback in tunnel mode to try optimized loading first This optimization significantly speeds up image loading operations when working with remote Podman machines by eliminating redundant file transfers for already-accessible image archives. Fixes: https://issues.redhat.com/browse/RUN-3249 Fixes: https://github.com/containers/podman/issues/26321 Signed-off-by: Jan Rodák <hony.com@seznam.cz>
157 lines
5.0 KiB
Go
157 lines
5.0 KiB
Go
//go:build amd64 || arm64
|
|
|
|
package localapi
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"net/url"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/containers/podman/v5/pkg/bindings"
|
|
"github.com/containers/podman/v5/pkg/machine/define"
|
|
"github.com/containers/podman/v5/pkg/machine/env"
|
|
"github.com/containers/podman/v5/pkg/machine/provider"
|
|
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
|
|
"github.com/containers/podman/v5/pkg/specgen"
|
|
"github.com/containers/storage/pkg/fileutils"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// FindMachineByPort finds a running machine that matches the given connection port.
|
|
// It returns the machine configuration and provider, or an error if not found.
|
|
func FindMachineByPort(connectionURI string, parsedConnection *url.URL) (*vmconfigs.MachineConfig, vmconfigs.VMProvider, error) {
|
|
machineProvider, err := provider.Get()
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("getting machine provider: %w", err)
|
|
}
|
|
|
|
dirs, err := env.GetMachineDirs(machineProvider.VMType())
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
machineList, err := vmconfigs.LoadMachinesInDir(dirs)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("listing machines: %w", err)
|
|
}
|
|
|
|
// Now we know that the connection points to a machine and we
|
|
// can find the machine by looking for the one with the
|
|
// matching port.
|
|
connectionPort, err := strconv.Atoi(parsedConnection.Port())
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("parsing connection port: %w", err)
|
|
}
|
|
|
|
for _, mc := range machineList {
|
|
if connectionPort != mc.SSH.Port {
|
|
continue
|
|
}
|
|
|
|
state, err := machineProvider.State(mc, false)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if state != define.Running {
|
|
return nil, nil, fmt.Errorf("machine %s is not running but in state %s", mc.Name, state)
|
|
}
|
|
|
|
return mc, machineProvider, nil
|
|
}
|
|
|
|
return nil, nil, fmt.Errorf("could not find a matching machine for connection %q", connectionURI)
|
|
}
|
|
|
|
// getMachineMountsAndVMType retrieves the mounts and VM type of a machine based on the connection URI and parsed URL.
|
|
// It returns a slice of mounts, the VM type, or an error if the machine cannot be found or is not running.
|
|
func getMachineMountsAndVMType(connectionURI string, parsedConnection *url.URL) ([]*vmconfigs.Mount, define.VMType, error) {
|
|
mc, machineProvider, err := FindMachineByPort(connectionURI, parsedConnection)
|
|
if err != nil {
|
|
return nil, define.UnknownVirt, err
|
|
}
|
|
return mc.Mounts, machineProvider.VMType(), nil
|
|
}
|
|
|
|
// isPathAvailableOnMachine checks if a local path is available on the machine through mounted directories.
|
|
// If the path is available, it returns a LocalAPIMap with the corresponding remote path.
|
|
func isPathAvailableOnMachine(mounts []*vmconfigs.Mount, vmType define.VMType, path string) (*LocalAPIMap, bool) {
|
|
pathABS, err := filepath.Abs(path)
|
|
if err != nil {
|
|
logrus.Debugf("Failed to get absolute path for %s: %v", path, err)
|
|
return nil, false
|
|
}
|
|
|
|
// WSLVirt is a special case where there is no real concept of doing a mount in WSL,
|
|
// WSL by default mounts the drives to /mnt/c, /mnt/d, etc...
|
|
if vmType == define.WSLVirt {
|
|
converted_path, err := specgen.ConvertWinMountPath(pathABS)
|
|
if err != nil {
|
|
logrus.Debugf("Failed to convert Windows mount path: %v", err)
|
|
return nil, false
|
|
}
|
|
|
|
return &LocalAPIMap{
|
|
ClientPath: pathABS,
|
|
RemotePath: converted_path,
|
|
}, true
|
|
}
|
|
|
|
for _, mount := range mounts {
|
|
mountSource := filepath.Clean(mount.Source)
|
|
relPath, err := filepath.Rel(mountSource, pathABS)
|
|
if err != nil {
|
|
logrus.Debugf("Failed to get relative path: %v", err)
|
|
continue
|
|
}
|
|
// If relPath starts with ".." or is absolute, pathABS is not under mountSource
|
|
if relPath == "." || (!strings.HasPrefix(relPath, "..") && !filepath.IsAbs(relPath)) {
|
|
target := filepath.Join(mount.Target, relPath)
|
|
converted_path, err := specgen.ConvertWinMountPath(target)
|
|
if err != nil {
|
|
logrus.Debugf("Failed to convert Windows mount path: %v", err)
|
|
return nil, false
|
|
}
|
|
logrus.Debugf("Converted client path: %q", converted_path)
|
|
return &LocalAPIMap{
|
|
ClientPath: pathABS,
|
|
RemotePath: converted_path,
|
|
}, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
// CheckPathOnRunningMachine is a convenience function that checks if a path is available
|
|
// on any currently running machine. It combines machine inspection and path checking.
|
|
func CheckPathOnRunningMachine(ctx context.Context, path string) (*LocalAPIMap, bool) {
|
|
if err := fileutils.Exists(path); errors.Is(err, fs.ErrNotExist) {
|
|
logrus.Debugf("Path %s does not exist locally, skipping machine check", path)
|
|
return nil, false
|
|
}
|
|
|
|
if machineMode := bindings.GetMachineMode(ctx); !machineMode {
|
|
logrus.Debug("Machine mode is not enabled, skipping machine check")
|
|
return nil, false
|
|
}
|
|
|
|
conn, err := bindings.GetClient(ctx)
|
|
if err != nil {
|
|
logrus.Debugf("Failed to get client connection: %v", err)
|
|
return nil, false
|
|
}
|
|
|
|
mounts, vmType, err := getMachineMountsAndVMType(conn.URI.String(), conn.URI)
|
|
if err != nil {
|
|
logrus.Debugf("Failed to get machine mounts: %v", err)
|
|
return nil, false
|
|
}
|
|
|
|
return isPathAvailableOnMachine(mounts, vmType, path)
|
|
}
|