From 82e6534e9c5d47ad8671eff18ca5abd675758b32 Mon Sep 17 00:00:00 2001 From: Lorenzo Fontana Date: Wed, 26 Apr 2017 02:34:57 +0200 Subject: [PATCH] Port forwarding in qemu runner Example usage: linuxkit run qemu -publish 1111:1111/udp -publish 8081:80/tcp example Signed-off-by: Lorenzo Fontana --- src/cmd/linuxkit/run_qemu.go | 169 +++++++++++++++++++++++++++++------ 1 file changed, 143 insertions(+), 26 deletions(-) diff --git a/src/cmd/linuxkit/run_qemu.go b/src/cmd/linuxkit/run_qemu.go index cc90f4541..014759878 100644 --- a/src/cmd/linuxkit/run_qemu.go +++ b/src/cmd/linuxkit/run_qemu.go @@ -7,6 +7,8 @@ import ( "os" "os/exec" "path/filepath" + "strconv" + "strings" log "github.com/Sirupsen/logrus" ) @@ -16,21 +18,22 @@ const QemuImg = "linuxkit/qemu:17f052263d63c8a2b641ad91c589edcbb8a18c82" // QemuConfig contains the config for Qemu type QemuConfig struct { - Prefix string - ISO bool - UEFI bool - Kernel bool - GUI bool - DiskPath string - DiskSize string - FWPath string - Arch string - CPUs string - Memory string - KVM bool - Containerized bool - QemuBinPath string - QemuImgPath string + Prefix string + ISO bool + UEFI bool + Kernel bool + GUI bool + DiskPath string + DiskSize string + FWPath string + Arch string + CPUs string + Memory string + KVM bool + Containerized bool + QemuBinPath string + QemuImgPath string + PublishedPorts []string } func runQemu(args []string) { @@ -60,6 +63,9 @@ func runQemu(args []string) { qemuCPUs := qemuFlags.String("cpus", "1", "Number of CPUs") 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 { log.Fatal("Unable to parse args") } @@ -78,17 +84,18 @@ func runQemu(args []string) { } config := QemuConfig{ - Prefix: prefix, - ISO: *qemuIso, - UEFI: *qemuUEFI, - Kernel: *qemuKernel, - GUI: *qemuGUI, - DiskPath: *qemuDiskPath, - DiskSize: *qemuDiskSize, - FWPath: *qemuFWPath, - Arch: *qemuArch, - CPUs: *qemuCPUs, - Memory: *qemuMem, + Prefix: prefix, + ISO: *qemuIso, + UEFI: *qemuUEFI, + Kernel: *qemuKernel, + GUI: *qemuGUI, + DiskPath: *qemuDiskPath, + DiskSize: *qemuDiskSize, + FWPath: *qemuFWPath, + Arch: *qemuArch, + CPUs: *qemuCPUs, + Memory: *qemuMem, + PublishedPorts: publishFlags, } config, qemuArgs := buildQemuCmdline(config) @@ -159,6 +166,14 @@ func runQemuContainer(config QemuConfig, args []string) error { 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") if err != nil { 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 { qemuArgs = append(qemuArgs, "-nographic") } @@ -285,3 +309,96 @@ func buildPath(prefix string, postfix string) string { } 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 : or :/") + } + + 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 +}