mirror of https://github.com/containers/podman.git
Merge pull request #10488 from baude/machinehostnetwork
Enable port forwarding on host
This commit is contained in:
commit
cbffdddce6
|
@ -85,7 +85,10 @@ func DefineNetFlags(cmd *cobra.Command) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) {
|
// NetFlagsToNetOptions parses the network flags for the given cmd.
|
||||||
|
// The netnsFromConfig bool is used to indicate if the --network flag
|
||||||
|
// should always be parsed regardless if it was set on the cli.
|
||||||
|
func NetFlagsToNetOptions(cmd *cobra.Command, netnsFromConfig bool) (*entities.NetOptions, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
@ -193,7 +196,9 @@ func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.Flags().Changed("network") {
|
// parse the --network value only when the flag is set or we need to use
|
||||||
|
// the netns config value, e.g. when --pod is not used
|
||||||
|
if netnsFromConfig || cmd.Flag("network").Changed {
|
||||||
network, err := cmd.Flags().GetString("network")
|
network, err := cmd.Flags().GetString("network")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -87,7 +87,7 @@ func create(cmd *cobra.Command, args []string) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
cliVals.Net, err = common.NetFlagsToNetOptions(cmd)
|
cliVals.Net, err = common.NetFlagsToNetOptions(cmd, cliVals.Pod == "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ func init() {
|
||||||
|
|
||||||
func run(cmd *cobra.Command, args []string) error {
|
func run(cmd *cobra.Command, args []string) error {
|
||||||
var err error
|
var err error
|
||||||
cliVals.Net, err = common.NetFlagsToNetOptions(cmd)
|
cliVals.Net, err = common.NetFlagsToNetOptions(cmd, cliVals.Pod == "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,10 +166,11 @@ func create(cmd *cobra.Command, args []string) error {
|
||||||
defer errorhandling.SyncQuiet(podIDFD)
|
defer errorhandling.SyncQuiet(podIDFD)
|
||||||
}
|
}
|
||||||
|
|
||||||
createOptions.Net, err = common.NetFlagsToNetOptions(cmd)
|
createOptions.Net, err = common.NetFlagsToNetOptions(cmd, createOptions.Infra)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(createOptions.Net.PublishPorts) > 0 {
|
if len(createOptions.Net.PublishPorts) > 0 {
|
||||||
if !createOptions.Infra {
|
if !createOptions.Infra {
|
||||||
return errors.Errorf("you must have an infra container to publish port bindings to the host")
|
return errors.Errorf("you must have an infra container to publish port bindings to the host")
|
||||||
|
|
|
@ -273,7 +273,6 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error creating rootless cni network namespace")
|
return nil, errors.Wrap(err, "error creating rootless cni network namespace")
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup slirp4netns here
|
// setup slirp4netns here
|
||||||
path := r.config.Engine.NetworkCmdPath
|
path := r.config.Engine.NetworkCmdPath
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
@ -437,9 +436,32 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
|
||||||
return rootlessCNINS, nil
|
return rootlessCNINS, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setPrimaryMachineIP is used for podman-machine and it sets
|
||||||
|
// and environment variable with the IP address of the podman-machine
|
||||||
|
// host.
|
||||||
|
func setPrimaryMachineIP() error {
|
||||||
|
// no connection is actually made here
|
||||||
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := conn.Close(); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
addr := conn.LocalAddr().(*net.UDPAddr)
|
||||||
|
return os.Setenv("PODMAN_MACHINE_HOST", addr.IP.String())
|
||||||
|
}
|
||||||
|
|
||||||
// setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni
|
// setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni
|
||||||
// networks. If rootless it will join/create the rootless cni namespace.
|
// networks. If rootless it will join/create the rootless cni namespace.
|
||||||
func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) {
|
func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) {
|
||||||
|
if r.config.MachineEnabled() {
|
||||||
|
if err := setPrimaryMachineIP(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
rootlessCNINS, err := r.GetRootlessCNINetNs(true)
|
rootlessCNINS, err := r.GetRootlessCNINetNs(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -32,6 +32,7 @@ var (
|
||||||
ErrVMAlreadyExists = errors.New("VM already exists")
|
ErrVMAlreadyExists = errors.New("VM already exists")
|
||||||
ErrVMAlreadyRunning = errors.New("VM already running")
|
ErrVMAlreadyRunning = errors.New("VM already running")
|
||||||
ErrMultipleActiveVM = errors.New("only one VM can be active at a time")
|
ErrMultipleActiveVM = errors.New("only one VM can be active at a time")
|
||||||
|
ForwarderBinaryName = "gvproxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Download struct {
|
type Download struct {
|
||||||
|
|
|
@ -118,6 +118,7 @@ func getDirs(usrName string) []Directory {
|
||||||
// in one swoop, then the leading dirs are creates as root.
|
// in one swoop, then the leading dirs are creates as root.
|
||||||
newDirs := []string{
|
newDirs := []string{
|
||||||
"/home/" + usrName + "/.config",
|
"/home/" + usrName + "/.config",
|
||||||
|
"/home/" + usrName + "/.config/containers",
|
||||||
"/home/" + usrName + "/.config/systemd",
|
"/home/" + usrName + "/.config/systemd",
|
||||||
"/home/" + usrName + "/.config/systemd/user",
|
"/home/" + usrName + "/.config/systemd/user",
|
||||||
"/home/" + usrName + "/.config/systemd/user/default.target.wants",
|
"/home/" + usrName + "/.config/systemd/user/default.target.wants",
|
||||||
|
@ -159,6 +160,22 @@ func getFiles(usrName string) []File {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Set containers.conf up for core user to use cni networks
|
||||||
|
// by default
|
||||||
|
files = append(files, File{
|
||||||
|
Node: Node{
|
||||||
|
Group: getNodeGrp(usrName),
|
||||||
|
Path: "/home/" + usrName + "/.config/containers/containers.conf",
|
||||||
|
User: getNodeUsr(usrName),
|
||||||
|
},
|
||||||
|
FileEmbedded1: FileEmbedded1{
|
||||||
|
Append: nil,
|
||||||
|
Contents: Resource{
|
||||||
|
Source: strToPtr("data:,%5Bcontainers%5D%0D%0Anetns%3D%22bridge%22%0D%0Arootless_networking%3D%22cni%22"),
|
||||||
|
},
|
||||||
|
Mode: intToPtr(484),
|
||||||
|
},
|
||||||
|
})
|
||||||
// Add a file into linger
|
// Add a file into linger
|
||||||
files = append(files, File{
|
files = append(files, File{
|
||||||
Node: Node{
|
Node: Node{
|
||||||
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v3/pkg/rootless"
|
||||||
|
|
||||||
"github.com/containers/podman/v3/pkg/machine"
|
"github.com/containers/podman/v3/pkg/machine"
|
||||||
"github.com/containers/podman/v3/utils"
|
"github.com/containers/podman/v3/utils"
|
||||||
"github.com/containers/storage/pkg/homedir"
|
"github.com/containers/storage/pkg/homedir"
|
||||||
|
@ -82,9 +84,10 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
||||||
cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server=on,wait=off"}...)
|
cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server=on,wait=off"}...)
|
||||||
|
|
||||||
// Add network
|
// Add network
|
||||||
cmd = append(cmd, "-nic", "user,model=virtio,hostfwd=tcp::"+strconv.Itoa(vm.Port)+"-:22")
|
// Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is
|
||||||
|
// why we can only run one vm at a time right now
|
||||||
socketPath, err := getSocketDir()
|
cmd = append(cmd, []string{"-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee"}...)
|
||||||
|
socketPath, err := getRuntimeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -237,10 +240,33 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
||||||
var (
|
var (
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
err error
|
err error
|
||||||
|
qemuSocketConn net.Conn
|
||||||
wait time.Duration = time.Millisecond * 500
|
wait time.Duration = time.Millisecond * 500
|
||||||
)
|
)
|
||||||
|
if err := v.startHostNetworking(); err != nil {
|
||||||
|
return errors.Errorf("unable to start host networking: %q", err)
|
||||||
|
}
|
||||||
|
qemuSocketPath, _, err := v.getSocketandPid()
|
||||||
|
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
qemuSocketConn, err = net.Dial("unix", qemuSocketPath)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(wait)
|
||||||
|
wait++
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := qemuSocketConn.(*net.UnixConn).File()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
attr := new(os.ProcAttr)
|
attr := new(os.ProcAttr)
|
||||||
files := []*os.File{os.Stdin, os.Stdout, os.Stderr}
|
files := []*os.File{os.Stdin, os.Stdout, os.Stderr, fd}
|
||||||
attr.Files = files
|
attr.Files = files
|
||||||
logrus.Debug(v.CmdLine)
|
logrus.Debug(v.CmdLine)
|
||||||
cmd := v.CmdLine
|
cmd := v.CmdLine
|
||||||
|
@ -256,7 +282,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("Waiting for VM ...")
|
fmt.Println("Waiting for VM ...")
|
||||||
socketPath, err := getSocketDir()
|
socketPath, err := getRuntimeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -309,16 +335,42 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
|
||||||
logrus.Error(err)
|
logrus.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
_, err = qmpMonitor.Run(input)
|
if _, err = qmpMonitor.Run(input); err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
_, pidFile, err := v.getSocketandPid()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(pidFile); os.IsNotExist(err) {
|
||||||
|
logrus.Infof("pid file %s does not exist", pidFile)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pidString, err := ioutil.ReadFile(pidFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pidNum, err := strconv.Atoi(string(pidString))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := os.FindProcess(pidNum)
|
||||||
|
if p == nil && err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return p.Kill()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewQMPMonitor creates the monitor subsection of our vm
|
// NewQMPMonitor creates the monitor subsection of our vm
|
||||||
func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) {
|
func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) {
|
||||||
rtDir, err := getSocketDir()
|
rtDir, err := getRuntimeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Monitor{}, err
|
return Monitor{}, err
|
||||||
}
|
}
|
||||||
|
if !rootless.IsRootless() {
|
||||||
|
rtDir = "/run"
|
||||||
|
}
|
||||||
rtDir = filepath.Join(rtDir, "podman")
|
rtDir = filepath.Join(rtDir, "podman")
|
||||||
if _, err := os.Stat(filepath.Join(rtDir)); os.IsNotExist(err) {
|
if _, err := os.Stat(filepath.Join(rtDir)); os.IsNotExist(err) {
|
||||||
// TODO 0644 is fine on linux but macos is weird
|
// TODO 0644 is fine on linux but macos is weird
|
||||||
|
@ -533,3 +585,47 @@ func CheckActiveVM() (bool, string, error) {
|
||||||
}
|
}
|
||||||
return false, "", nil
|
return false, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// startHostNetworking runs a binary on the host system that allows users
|
||||||
|
// to setup port forwarding to the podman virtual machine
|
||||||
|
func (v *MachineVM) startHostNetworking() error {
|
||||||
|
binary, err := exec.LookPath(machine.ForwarderBinaryName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Listen on all at port 7777 for setting up and tearing
|
||||||
|
// down forwarding
|
||||||
|
listenSocket := "tcp://0.0.0.0:7777"
|
||||||
|
qemuSocket, pidFile, err := v.getSocketandPid()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
attr := new(os.ProcAttr)
|
||||||
|
// Pass on stdin, stdout, stderr
|
||||||
|
files := []*os.File{os.Stdin, os.Stdout, os.Stderr}
|
||||||
|
attr.Files = files
|
||||||
|
cmd := []string{binary}
|
||||||
|
cmd = append(cmd, []string{"-listen", listenSocket, "-listen-qemu", fmt.Sprintf("unix://%s", qemuSocket), "-pid-file", pidFile}...)
|
||||||
|
// Add the ssh port
|
||||||
|
cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", v.Port)}...)
|
||||||
|
if logrus.GetLevel() == logrus.DebugLevel {
|
||||||
|
cmd = append(cmd, "--debug")
|
||||||
|
fmt.Println(cmd)
|
||||||
|
}
|
||||||
|
_, err = os.StartProcess(cmd[0], cmd, attr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *MachineVM) getSocketandPid() (string, string, error) {
|
||||||
|
rtPath, err := getRuntimeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
if !rootless.IsRootless() {
|
||||||
|
rtPath = "/run"
|
||||||
|
}
|
||||||
|
socketDir := filepath.Join(rtPath, "podman")
|
||||||
|
pidFile := filepath.Join(socketDir, fmt.Sprintf("%s.pid", v.Name))
|
||||||
|
qemuSocket := filepath.Join(socketDir, fmt.Sprintf("qemu_%s.sock", v.Name))
|
||||||
|
return qemuSocket, pidFile, nil
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getSocketDir() (string, error) {
|
func getRuntimeDir() (string, error) {
|
||||||
tmpDir, ok := os.LookupEnv("TMPDIR")
|
tmpDir, ok := os.LookupEnv("TMPDIR")
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", errors.New("unable to resolve TMPDIR")
|
return "", errors.New("unable to resolve TMPDIR")
|
||||||
|
|
|
@ -5,7 +5,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v *MachineVM) addArchOptions() []string {
|
func (v *MachineVM) addArchOptions() []string {
|
||||||
opts := []string{"-cpu", "host"}
|
opts := []string{"-machine", "q35,accel=hvf:tcg"}
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
package qemu
|
package qemu
|
||||||
|
|
||||||
import "github.com/containers/podman/v3/pkg/util"
|
import (
|
||||||
|
"github.com/containers/podman/v3/pkg/rootless"
|
||||||
|
"github.com/containers/podman/v3/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
func getSocketDir() (string, error) {
|
func getRuntimeDir() (string, error) {
|
||||||
|
if !rootless.IsRootless() {
|
||||||
|
return "/run", nil
|
||||||
|
}
|
||||||
return util.GetRuntimeDir()
|
return util.GetRuntimeDir()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue