mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-10-06 14:07:50 +00:00
For Podman 6, we still have providers and will continue to have a default provider for each platform. But where a platform has multiple providers, we want users to be able to cross provider boudnaries imposed in Podman 4/5. The key change is to look up virtual machines by name, as before, but to then also iterate all possible providers. As of this PR, init will still only create with the default provider, but a subsequent PR will introdouce an provider override. I also removed the "--all-providers" command line option on `podman machine ls` because it no longer makes sense. And I marked the all provider list test to be skipped. Signed-off-by: Brent Baude <bbaude@redhat.com>
156 lines
4.1 KiB
Go
156 lines
4.1 KiB
Go
//go:build amd64 || arm64
|
|
|
|
package machine
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
|
|
"github.com/containers/podman/v6/cmd/podman/registry"
|
|
"github.com/containers/podman/v6/libpod/events"
|
|
"github.com/containers/podman/v6/pkg/copy"
|
|
"github.com/containers/podman/v6/pkg/machine"
|
|
"github.com/containers/podman/v6/pkg/machine/define"
|
|
"github.com/containers/podman/v6/pkg/machine/shim"
|
|
"github.com/containers/podman/v6/pkg/machine/vmconfigs"
|
|
"github.com/containers/podman/v6/pkg/specgen"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
type cpOptions struct {
|
|
Quiet bool
|
|
Machine *vmconfigs.MachineConfig
|
|
IsSrc bool
|
|
SrcPath string
|
|
DestPath string
|
|
}
|
|
|
|
var (
|
|
cpCmd = &cobra.Command{
|
|
Use: "cp [options] SRC_PATH DEST_PATH",
|
|
Short: "Securely copy contents between the virtual machine",
|
|
Long: "Securely copy files or directories between the virtual machine and your host",
|
|
PersistentPreRunE: machinePreRunE,
|
|
RunE: cp,
|
|
Args: cobra.ExactArgs(2),
|
|
Example: `podman machine cp ~/ca.crt podman-machine-default:/etc/containers/certs.d/ca.crt`,
|
|
ValidArgsFunction: autocompleteMachineCp,
|
|
}
|
|
|
|
cpOpts = cpOptions{}
|
|
)
|
|
|
|
func init() {
|
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
|
Command: cpCmd,
|
|
Parent: machineCmd,
|
|
})
|
|
|
|
flags := cpCmd.Flags()
|
|
quietFlagName := "quiet"
|
|
flags.BoolVarP(&cpOpts.Quiet, quietFlagName, "q", false, "Suppress copy status output")
|
|
}
|
|
|
|
func cp(_ *cobra.Command, args []string) error {
|
|
var err error
|
|
|
|
srcMachine, srcPath, destMachine, destPath, err := copy.ParseSourceAndDestination(args[0], args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// NOTE: This will most likely break hyperv or wsl machines with single-letter
|
|
// names. It is most likely similar to https://github.com/containers/podman/issues/25218
|
|
//
|
|
// Passing an absolute windows path of the format <volume>:\<path> will cause
|
|
// `copy.ParseSourceAndDestination` to think the volume is a Machine. Check
|
|
// if the raw cmdline argument is a Windows host path.
|
|
if specgen.IsHostWinPath(args[0]) {
|
|
srcMachine = ""
|
|
srcPath = args[0]
|
|
}
|
|
|
|
if specgen.IsHostWinPath(args[1]) {
|
|
destMachine = ""
|
|
destPath = args[1]
|
|
}
|
|
|
|
vmName, err := resolveMachineName(srcMachine, destMachine)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
mc, vmProvider, err := shim.VMExists(vmName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
state, err := vmProvider.State(mc, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if state != define.Running {
|
|
return fmt.Errorf("vm %q is not running", mc.Name)
|
|
}
|
|
|
|
cpOpts.Machine = mc
|
|
cpOpts.SrcPath = srcPath
|
|
cpOpts.DestPath = destPath
|
|
|
|
err = localhostSSHCopy(&cpOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("copy failed: %s", err.Error())
|
|
}
|
|
|
|
fmt.Println("Copy successful")
|
|
newMachineEvent(events.Copy, events.Event{Name: mc.Name})
|
|
return nil
|
|
}
|
|
|
|
// localhostSSHCopy uses scp to copy files from/to a localhost machine using ssh.
|
|
func localhostSSHCopy(opts *cpOptions) error {
|
|
srcPath := opts.SrcPath
|
|
destPath := opts.DestPath
|
|
sshConfig := opts.Machine.SSH
|
|
|
|
username := sshConfig.RemoteUsername
|
|
if cpOpts.Machine.HostUser.Rootful {
|
|
username = "root"
|
|
}
|
|
username += "@localhost:"
|
|
|
|
if opts.IsSrc {
|
|
srcPath = username + srcPath
|
|
} else {
|
|
destPath = username + destPath
|
|
}
|
|
|
|
args := []string{"-r", "-i", sshConfig.IdentityPath, "-P", strconv.Itoa(sshConfig.Port)}
|
|
args = append(args, machine.LocalhostSSHArgs()...) // Warning: This MUST NOT be generalized to allow communication over untrusted networks.
|
|
args = append(args, []string{srcPath, destPath}...)
|
|
|
|
cmd := exec.Command("scp", args...)
|
|
if !opts.Quiet {
|
|
cmd.Stdout = os.Stdout
|
|
}
|
|
cmd.Stderr = os.Stderr
|
|
return cmd.Run()
|
|
}
|
|
|
|
func resolveMachineName(srcMachine, destMachine string) (string, error) {
|
|
if len(srcMachine) > 0 && len(destMachine) > 0 {
|
|
return "", errors.New("copying between two machines is unsupported")
|
|
}
|
|
|
|
if len(srcMachine) == 0 && len(destMachine) == 0 {
|
|
return "", errors.New("a machine name must prefix either the source path or destination path")
|
|
}
|
|
name := destMachine
|
|
if len(srcMachine) > 0 {
|
|
cpOpts.IsSrc = true
|
|
name = srcMachine
|
|
}
|
|
return name, nil
|
|
}
|