Port forwarding in qemu runner

Example usage:
	linuxkit run qemu -publish 1111:1111/udp -publish 8081:80/tcp example

Signed-off-by: Lorenzo Fontana <lo@linux.com>
This commit is contained in:
Lorenzo Fontana 2017-04-26 02:34:57 +02:00
parent f3157af1db
commit 82e6534e9c

View File

@ -7,6 +7,8 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strconv"
"strings"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
) )
@ -16,21 +18,22 @@ const QemuImg = "linuxkit/qemu:17f052263d63c8a2b641ad91c589edcbb8a18c82"
// QemuConfig contains the config for Qemu // QemuConfig contains the config for Qemu
type QemuConfig struct { type QemuConfig struct {
Prefix string Prefix string
ISO bool ISO bool
UEFI bool UEFI bool
Kernel bool Kernel bool
GUI bool GUI bool
DiskPath string DiskPath string
DiskSize string DiskSize string
FWPath string FWPath string
Arch string Arch string
CPUs string CPUs string
Memory string Memory string
KVM bool KVM bool
Containerized bool Containerized bool
QemuBinPath string QemuBinPath string
QemuImgPath string QemuImgPath string
PublishedPorts []string
} }
func runQemu(args []string) { func runQemu(args []string) {
@ -60,6 +63,9 @@ func runQemu(args []string) {
qemuCPUs := qemuFlags.String("cpus", "1", "Number of CPUs") qemuCPUs := qemuFlags.String("cpus", "1", "Number of CPUs")
qemuMem := qemuFlags.String("mem", "1024", "Amount of memory in MB") qemuMem := qemuFlags.String("mem", "1024", "Amount of memory in MB")
publishFlags := multipleFlag{}
qemuFlags.Var(&publishFlags, "publish", "Publish a vm's port(s) to the host (default [])")
if err := qemuFlags.Parse(args); err != nil { if err := qemuFlags.Parse(args); err != nil {
log.Fatal("Unable to parse args") log.Fatal("Unable to parse args")
} }
@ -78,17 +84,18 @@ func runQemu(args []string) {
} }
config := QemuConfig{ config := QemuConfig{
Prefix: prefix, Prefix: prefix,
ISO: *qemuIso, ISO: *qemuIso,
UEFI: *qemuUEFI, UEFI: *qemuUEFI,
Kernel: *qemuKernel, Kernel: *qemuKernel,
GUI: *qemuGUI, GUI: *qemuGUI,
DiskPath: *qemuDiskPath, DiskPath: *qemuDiskPath,
DiskSize: *qemuDiskSize, DiskSize: *qemuDiskSize,
FWPath: *qemuFWPath, FWPath: *qemuFWPath,
Arch: *qemuArch, Arch: *qemuArch,
CPUs: *qemuCPUs, CPUs: *qemuCPUs,
Memory: *qemuMem, Memory: *qemuMem,
PublishedPorts: publishFlags,
} }
config, qemuArgs := buildQemuCmdline(config) config, qemuArgs := buildQemuCmdline(config)
@ -159,6 +166,14 @@ func runQemuContainer(config QemuConfig, args []string) error {
dockerArgs = append(dockerArgs, "--device", "/dev/kvm") dockerArgs = append(dockerArgs, "--device", "/dev/kvm")
} }
if config.PublishedPorts != nil && len(config.PublishedPorts) > 0 {
forwardings, err := buildDockerForwardings(config.PublishedPorts)
if err != nil {
return err
}
dockerArgs = append(dockerArgs, forwardings...)
}
dockerPath, err := exec.LookPath("docker") dockerPath, err := exec.LookPath("docker")
if err != nil { if err != nil {
return fmt.Errorf("Unable to find docker in the $PATH") return fmt.Errorf("Unable to find docker in the $PATH")
@ -271,6 +286,15 @@ func buildQemuCmdline(config QemuConfig) (QemuConfig, []string) {
} }
} }
if config.PublishedPorts != nil && len(config.PublishedPorts) > 0 {
forwardings, err := buildQemuForwardings(config.PublishedPorts, config.Containerized)
if err != nil {
log.Error(err)
}
qemuArgs = append(qemuArgs, "-net", forwardings)
qemuArgs = append(qemuArgs, "-net", "nic")
}
if config.GUI != true { if config.GUI != true {
qemuArgs = append(qemuArgs, "-nographic") qemuArgs = append(qemuArgs, "-nographic")
} }
@ -285,3 +309,96 @@ func buildPath(prefix string, postfix string) string {
} }
return path return path
} }
type multipleFlag []string
type publishedPorts struct {
guest int
host int
protocol string
}
func (f *multipleFlag) String() string {
return "A multiple flag is a type of flag that can be repeated any number of times"
}
func (f *multipleFlag) Set(value string) error {
*f = append(*f, value)
return nil
}
func splitPublish(publish string) (publishedPorts, error) {
p := publishedPorts{}
slice := strings.Split(publish, ":")
if len(slice) < 2 {
return p, fmt.Errorf("Unable to parse the ports to be published, should be in format <host>:<guest> or <host>:<guest>/<tcp|udp>")
}
hostPort, err := strconv.Atoi(slice[0])
if err != nil {
return p, fmt.Errorf("The provided hostPort can't be converted to int")
}
right := strings.Split(slice[1], "/")
protocol := "tcp"
if len(right) == 2 {
protocol = strings.TrimSpace(strings.ToLower(right[1]))
}
if protocol != "tcp" && protocol != "udp" {
return p, fmt.Errorf("Provided protocol is not valid, valid options are: udp and tcp")
}
guestPort, err := strconv.Atoi(right[0])
if err != nil {
return p, fmt.Errorf("The provided guestPort can't be converted to int")
}
if hostPort < 1 || hostPort > 65535 {
return p, fmt.Errorf("Invalid hostPort: %d", hostPort)
}
if guestPort < 1 || guestPort > 65535 {
return p, fmt.Errorf("Invalid guestPort: %d", guestPort)
}
p.guest = guestPort
p.host = hostPort
p.protocol = protocol
return p, nil
}
func buildQemuForwardings(publishFlags multipleFlag, containerized bool) (string, error) {
forwardings := "user"
for _, publish := range publishFlags {
p, err := splitPublish(publish)
if err != nil {
return "", err
}
hostPort := p.host
guestPort := p.guest
if containerized {
hostPort = guestPort
}
forwardings = fmt.Sprintf("%s,hostfwd=%s::%d-:%d", forwardings, p.protocol, hostPort, guestPort)
}
return forwardings, nil
}
func buildDockerForwardings(publishedPorts []string) ([]string, error) {
pmap := []string{}
for _, port := range publishedPorts {
s, err := splitPublish(port)
if err != nil {
return nil, err
}
pmap = append(pmap, "-p", fmt.Sprintf("%d:%d/%s", s.host, s.guest, s.protocol))
}
return pmap, nil
}