diff options
-rw-r--r-- | cmd/podman/common/netflags.go | 9 | ||||
-rw-r--r-- | cmd/podman/containers/create.go | 2 | ||||
-rw-r--r-- | cmd/podman/containers/run.go | 2 | ||||
-rw-r--r-- | cmd/podman/pods/create.go | 3 | ||||
-rw-r--r-- | libpod/networking_linux.go | 24 | ||||
-rw-r--r-- | pkg/machine/config.go | 1 | ||||
-rw-r--r-- | pkg/machine/ignition.go | 17 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 118 | ||||
-rw-r--r-- | pkg/machine/qemu/options_darwin.go | 2 | ||||
-rw-r--r-- | pkg/machine/qemu/options_darwin_amd64.go | 2 | ||||
-rw-r--r-- | pkg/machine/qemu/options_linux.go | 10 |
11 files changed, 169 insertions, 21 deletions
diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go index 9941bc716..4f634f355 100644 --- a/cmd/podman/common/netflags.go +++ b/cmd/podman/common/netflags.go @@ -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 ( err error ) @@ -193,7 +196,9 @@ func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) { 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") if err != nil { return nil, err diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 07a859983..68a17abd0 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -87,7 +87,7 @@ func create(cmd *cobra.Command, args []string) error { var ( err error ) - cliVals.Net, err = common.NetFlagsToNetOptions(cmd) + cliVals.Net, err = common.NetFlagsToNetOptions(cmd, cliVals.Pod == "") if err != nil { return err } diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 2611c5b6e..579af4eb1 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -106,7 +106,7 @@ func init() { func run(cmd *cobra.Command, args []string) error { var err error - cliVals.Net, err = common.NetFlagsToNetOptions(cmd) + cliVals.Net, err = common.NetFlagsToNetOptions(cmd, cliVals.Pod == "") if err != nil { return err } diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 3c2c8a3bc..735dfa78c 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -166,10 +166,11 @@ func create(cmd *cobra.Command, args []string) error { defer errorhandling.SyncQuiet(podIDFD) } - createOptions.Net, err = common.NetFlagsToNetOptions(cmd) + createOptions.Net, err = common.NetFlagsToNetOptions(cmd, createOptions.Infra) if err != nil { return err } + if len(createOptions.Net.PublishPorts) > 0 { if !createOptions.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 0e8a4f768..c928e02a6 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -273,7 +273,6 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) { if err != nil { return nil, errors.Wrap(err, "error creating rootless cni network namespace") } - // setup slirp4netns here path := r.config.Engine.NetworkCmdPath if path == "" { @@ -437,9 +436,32 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) { 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 // networks. If rootless it will join/create the rootless cni namespace. 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) if err != nil { return nil, err diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 652229963..58794ce42 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -32,6 +32,7 @@ var ( ErrVMAlreadyExists = errors.New("VM already exists") ErrVMAlreadyRunning = errors.New("VM already running") ErrMultipleActiveVM = errors.New("only one VM can be active at a time") + ForwarderBinaryName = "gvproxy" ) type Download struct { diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 00068a136..a5c7210af 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -118,6 +118,7 @@ func getDirs(usrName string) []Directory { // in one swoop, then the leading dirs are creates as root. newDirs := []string{ "/home/" + usrName + "/.config", + "/home/" + usrName + "/.config/containers", "/home/" + usrName + "/.config/systemd", "/home/" + usrName + "/.config/systemd/user", "/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 files = append(files, File{ Node: Node{ diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 0bd711c90..31c355d4a 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -13,6 +13,8 @@ import ( "strings" "time" + "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/utils" "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"}...) // Add network - cmd = append(cmd, "-nic", "user,model=virtio,hostfwd=tcp::"+strconv.Itoa(vm.Port)+"-:22") - - socketPath, err := getSocketDir() + // 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 + 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 { return nil, err } @@ -235,12 +238,35 @@ func (v *MachineVM) Init(opts machine.InitOptions) error { // Start executes the qemu command line and forks it func (v *MachineVM) Start(name string, _ machine.StartOptions) error { var ( - conn net.Conn - err error - wait time.Duration = time.Millisecond * 500 + conn net.Conn + err error + qemuSocketConn net.Conn + 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) - files := []*os.File{os.Stdin, os.Stdout, os.Stderr} + files := []*os.File{os.Stdin, os.Stdout, os.Stderr, fd} attr.Files = files logrus.Debug(v.CmdLine) cmd := v.CmdLine @@ -256,7 +282,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { return err } fmt.Println("Waiting for VM ...") - socketPath, err := getSocketDir() + socketPath, err := getRuntimeDir() if err != nil { return err } @@ -309,16 +335,42 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { logrus.Error(err) } }() - _, err = qmpMonitor.Run(input) - return err + if _, err = qmpMonitor.Run(input); err != nil { + 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 func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) { - rtDir, err := getSocketDir() + rtDir, err := getRuntimeDir() if err != nil { return Monitor{}, err } + if !rootless.IsRootless() { + rtDir = "/run" + } rtDir = filepath.Join(rtDir, "podman") if _, err := os.Stat(filepath.Join(rtDir)); os.IsNotExist(err) { // TODO 0644 is fine on linux but macos is weird @@ -533,3 +585,47 @@ func CheckActiveVM() (bool, string, error) { } 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 +} diff --git a/pkg/machine/qemu/options_darwin.go b/pkg/machine/qemu/options_darwin.go index 46ccf24cb..440937131 100644 --- a/pkg/machine/qemu/options_darwin.go +++ b/pkg/machine/qemu/options_darwin.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" ) -func getSocketDir() (string, error) { +func getRuntimeDir() (string, error) { tmpDir, ok := os.LookupEnv("TMPDIR") if !ok { return "", errors.New("unable to resolve TMPDIR") diff --git a/pkg/machine/qemu/options_darwin_amd64.go b/pkg/machine/qemu/options_darwin_amd64.go index 69f7982b2..ee1036291 100644 --- a/pkg/machine/qemu/options_darwin_amd64.go +++ b/pkg/machine/qemu/options_darwin_amd64.go @@ -5,7 +5,7 @@ var ( ) func (v *MachineVM) addArchOptions() []string { - opts := []string{"-cpu", "host"} + opts := []string{"-machine", "q35,accel=hvf:tcg"} return opts } diff --git a/pkg/machine/qemu/options_linux.go b/pkg/machine/qemu/options_linux.go index 0a2e40d8f..c73a68cc6 100644 --- a/pkg/machine/qemu/options_linux.go +++ b/pkg/machine/qemu/options_linux.go @@ -1,7 +1,13 @@ 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() } |