Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 143 additions & 30 deletions internal/localapi/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"strings"

"github.com/containers/podman/v5/pkg/bindings"
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/containers/podman/v5/pkg/machine/define"
"github.com/containers/podman/v5/pkg/machine/env"
"github.com/containers/podman/v5/pkg/machine/provider"
Expand All @@ -25,44 +26,46 @@ import (
// 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 {
for _, machineProvider := range provider.GetAll() {
logrus.Debugf("Checking provider: %s", machineProvider.VMType())
dirs, err := env.GetMachineDirs(machineProvider.VMType())
if err != nil {
logrus.Debugf("Failed to get machine dirs for provider %s: %v", machineProvider.VMType(), err)
continue
}

state, err := machineProvider.State(mc, false)
machineList, err := vmconfigs.LoadMachinesInDir(dirs)
if err != nil {
return nil, nil, err
logrus.Debugf("Failed to list machines: %v", err)
continue
}

if state != define.Running {
return nil, nil, fmt.Errorf("machine %s is not running but in state %s", mc.Name, state)
// 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 {
logrus.Debugf("Failed to parse connection port: %v", err)
continue
}

return mc, machineProvider, nil
for _, mc := range machineList {
if connectionPort != mc.SSH.Port {
continue
}

state, err := machineProvider.State(mc, false)
if err != nil {
logrus.Debugf("Failed to get machine state for %s: %v", mc.Name, err)
continue
}

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)
Expand Down Expand Up @@ -154,3 +157,113 @@ func CheckPathOnRunningMachine(ctx context.Context, path string) (*LocalAPIMap,

return isPathAvailableOnMachine(mounts, vmType, path)
}

// CheckIfImageBuildPathsOnRunningMachine checks if the build context directory and all specified
// Containerfiles are available on the running machine. If they are, it translates their paths
// to the corresponding remote paths and returns them along with a flag indicating success.
func CheckIfImageBuildPathsOnRunningMachine(ctx context.Context, containerFiles []string, options entities.BuildOptions) ([]string, entities.BuildOptions, bool) {
if machineMode := bindings.GetMachineMode(ctx); !machineMode {
logrus.Debug("Machine mode is not enabled, skipping machine check")
return nil, options, false
}

conn, err := bindings.GetClient(ctx)
if err != nil {
logrus.Debugf("Failed to get client connection: %v", err)
return nil, options, false
}

mounts, vmType, err := getMachineMountsAndVMType(conn.URI.String(), conn.URI)
if err != nil {
logrus.Debugf("Failed to get machine mounts: %v", err)
return nil, options, false
}

// Context directory
if err := fileutils.Lexists(options.ContextDirectory); errors.Is(err, fs.ErrNotExist) {
logrus.Debugf("Path %s does not exist locally, skipping machine check", options.ContextDirectory)
return nil, options, false
}
mapping, found := isPathAvailableOnMachine(mounts, vmType, options.ContextDirectory)
if !found {
logrus.Debugf("Path %s is not available on the running machine", options.ContextDirectory)
return nil, options, false
}
options.ContextDirectory = mapping.RemotePath

// Containerfiles
translatedContainerFiles := []string{}
for _, containerFile := range containerFiles {
if strings.HasPrefix(containerFile, "http://") || strings.HasPrefix(containerFile, "https://") {
translatedContainerFiles = append(translatedContainerFiles, containerFile)
continue
}

// If Containerfile does not exist, assume it is in context directory
if err := fileutils.Lexists(containerFile); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
logrus.Fatalf("Failed to check if containerfile %s exists: %v", containerFile, err)
return nil, options, false
}
continue
}

mapping, found := isPathAvailableOnMachine(mounts, vmType, containerFile)
if !found {
logrus.Debugf("Path %s is not available on the running machine", containerFile)
return nil, options, false
}
translatedContainerFiles = append(translatedContainerFiles, mapping.RemotePath)
}

// Additional build contexts
for _, context := range options.AdditionalBuildContexts {
switch {
case context.IsImage, context.IsURL:
continue
default:
if err := fileutils.Lexists(context.Value); errors.Is(err, fs.ErrNotExist) {
logrus.Debugf("Path %s does not exist locally, skipping machine check", context.Value)
return nil, options, false
}
mapping, found := isPathAvailableOnMachine(mounts, vmType, context.Value)
if !found {
logrus.Debugf("Path %s is not available on the running machine", context.Value)
return nil, options, false
}
context.Value = mapping.RemotePath
}
}
return translatedContainerFiles, options, true
}

// IsHyperVProvider checks if the current machine provider is Hyper-V.
// It returns true if the provider is Hyper-V, false otherwise, or an error if the check fails.
func IsHyperVProvider(ctx context.Context) (bool, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
logrus.Debugf("Failed to get client connection: %v", err)
return false, err
}

_, vmProvider, err := FindMachineByPort(conn.URI.String(), conn.URI)
if err != nil {
logrus.Debugf("Failed to get machine hypervisor type: %v", err)
return false, err
}

return vmProvider.VMType() == define.HyperVVirt, nil
}

// ValidatePathForLocalAPI checks if the provided path satisfies requirements for local API usage.
// It returns an error if the path is not absolute or does not exist on the filesystem.
func ValidatePathForLocalAPI(path string) error {
if !filepath.IsAbs(path) {
return fmt.Errorf("path %q is not absolute", path)
}

if err := fileutils.Exists(path); err != nil {
return err
}
return nil
}
16 changes: 16 additions & 0 deletions internal/localapi/utils_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,26 @@ package localapi
import (
"context"

"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/sirupsen/logrus"
)

func CheckPathOnRunningMachine(ctx context.Context, path string) (*LocalAPIMap, bool) {
logrus.Debug("CheckPathOnRunningMachine is not supported")
return nil, false
}

func CheckIfImageBuildPathsOnRunningMachine(ctx context.Context, containerFiles []string, options entities.BuildOptions) ([]string, entities.BuildOptions, bool) {
logrus.Debug("CheckIfImageBuildPathsOnRunningMachine is not supported")
return nil, options, false
}

func IsHyperVProvider(ctx context.Context) (bool, error) {
logrus.Debug("IsHyperVProvider is not supported")
return false, nil
}

func ValidatePathForLocalAPI(path string) error {
logrus.Debug("ValidatePathForLocalAPI is not supported")
return nil
}
Loading