[lxc-devel] [lxd/master] Added proxy device for forwarding tcp connections
jialin-li on Github
lxc-bot at linuxcontainers.org
Sat Dec 16 03:23:47 UTC 2017
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 402 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20171216/fec9c892/attachment.bin>
-------------- next part --------------
From a76c9357cdee5ff2f59dbace594cb79a94298f70 Mon Sep 17 00:00:00 2001
From: KishanRPatel katiewasnothere dinopanda jialin-li kianaalcala
<kishanrpatel at utexas.edu>
Date: Fri, 15 Dec 2017 21:21:15 -0600
Subject: [PATCH] Added proxy device for forwarding tcp connections
Signed-off-by: KishanRPatel katiewasnothere dinopanda jialin-li kianaalcala <kishanrpatel at utexas.edu>
---
doc/containers.md | 23 +++++++
lxd/container.go | 27 +++++++-
lxd/container_lxc.go | 154 +++++++++++++++++++++++++++++++++++++++++++
lxd/daemon.go | 1 -
lxd/db/devices.go | 4 ++
lxd/main.go | 3 +-
lxd/main_args.go | 2 +
lxd/main_nsexec.go | 37 +++++++++++
lxd/main_proxy.go | 165 ++++++++++++++++++++++++++++++++++++++++++++++
lxd/proxy_device_utils.go | 93 ++++++++++++++++++++++++++
shared/util.go | 18 +++++
test/suites/proxy.sh | 78 ++++++++++++++++++++++
12 files changed, 601 insertions(+), 4 deletions(-)
create mode 100644 lxd/main_proxy.go
create mode 100644 lxd/proxy_device_utils.go
create mode 100644 test/suites/proxy.sh
diff --git a/doc/containers.md b/doc/containers.md
index 8d93f1f0a..3b918fcba 100644
--- a/doc/containers.md
+++ b/doc/containers.md
@@ -154,6 +154,7 @@ ID (database) | Name | Description
4 | [unix-block](#type-unix-block) | Unix block device
5 | [usb](#type-usb) | USB device
6 | [gpu](#type-gpu) | GPU device
+7 | [proxy] (#type-proxy) | Proxy device
### Type: none
A none type device doesn't have any property and doesn't create anything inside the container.
@@ -318,6 +319,28 @@ uid | int | 0 | no | UID of the device owne
gid | int | 0 | no | GID of the device owner in the container
mode | int | 0660 | no | Mode of the device in the container
+### Type: proxy
+Proxy devices are processes that forward data to/from ports in the container and
+ports in host. Used to communicate easily between two network namespaces.
+
+There will eventually be three supported connection types:
+ - `TCP`
+ - `UDP`
+ - `Unix`
+
+Current supported connection types:
+ - `TCP - TCP`
+
+Key | Type | Default | Required | Description
+:-- | :-- | :-- | :-- | :--
+listen | string | - | yes | the address and port to bind and listen
+connect | string | - | yes | the address and port to connect to
+bind | string | - | yes | which side to bind on (host/container)
+
+```
+lxc config device add <container_name> <device-name> proxy listen=<type>:<addr>:<port> listen=<type>:<addr>:<port> bind=<host/container>
+```
+
## Instance types
LXD supports simple instance types. Those are represented as a string
which can be passed at container creation time.
diff --git a/lxd/container.go b/lxd/container.go
index 4515d9c3a..92c9472ec 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -199,6 +199,17 @@ func containerValidDeviceConfigKey(t, k string) bool {
default:
return false
}
+ case "proxy":
+ switch k {
+ case "listen":
+ return true
+ case "connect":
+ return true
+ case "bind":
+ return true
+ default:
+ return false
+ }
case "none":
return false
default:
@@ -290,7 +301,7 @@ func containerValidDevices(db *db.Node, devices types.Devices, profile bool, exp
return fmt.Errorf("Missing device type for device '%s'", name)
}
- if !shared.StringInSlice(m["type"], []string{"none", "nic", "disk", "unix-char", "unix-block", "usb", "gpu"}) {
+ if !shared.StringInSlice(m["type"], []string{"none", "nic", "disk", "unix-char", "unix-block", "usb", "gpu", "proxy"}) {
return fmt.Errorf("Invalid device type for device '%s'", name)
}
@@ -384,7 +395,19 @@ func containerValidDevices(db *db.Node, devices types.Devices, profile bool, exp
} else if m["type"] == "gpu" {
// Probably no checks needed, since we allow users to
// pass in all GPUs.
- } else if m["type"] == "none" {
+ } else if m["type"] == "proxy" {
+ if m["listen"] == "" {
+ return fmt.Errorf("Proxy device entry is missing the required \"listen\" property.")
+ }
+
+ if m["connect"] == "" {
+ return fmt.Errorf("Proxy device entry is missing the required \"connect\" property.")
+ }
+
+ if m["bind"] == "" {
+ return fmt.Errorf("Proxy device entry is missing the required \"bind\" property.")
+ }
+ } else if m["type"] == "none" {
continue
} else {
return fmt.Errorf("Invalid device type: %s", m["type"])
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index f223fb702..32fbf7157 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -1844,6 +1844,7 @@ func (c *containerLXC) startCommon() (string, error) {
c.removeUnixDevices()
c.removeDiskDevices()
c.removeNetworkFilters()
+ c.removeProxyDevices()
var usbs []usbDevice
var gpus []gpuDevice
@@ -2234,6 +2235,8 @@ func (c *containerLXC) Start(stateful bool) error {
return err
}
+ c.restartProxyDevices()
+
logger.Info("Started container", ctxMap)
return nil
@@ -2553,6 +2556,12 @@ func (c *containerLXC) OnStop(target string) error {
logger.Error("Unable to remove network filters", log.Ctx{"container": c.Name(), "err": err})
}
+ // Clean all proxy devices
+ err = c.removeProxyDevices()
+ if err != nil {
+ logger.Error("Unable to remove proxy devices", log.Ctx{"container": c.Name(), "err": err})
+ }
+
// Reboot the container
if target == "reboot" {
// Start the container again
@@ -2916,6 +2925,7 @@ func (c *containerLXC) cleanup() {
c.removeUnixDevices()
c.removeDiskDevices()
c.removeNetworkFilters()
+ c.removeProxyDevices()
// Remove the security profiles
AADeleteProfile(c)
@@ -3950,6 +3960,11 @@ func (c *containerLXC) Update(args db.ContainerArgs, userRequested bool) error {
}
}
}
+ } else if m["type"] == "proxy" {
+ err = c.removeProxyDevice(k)
+ if err != nil {
+ return err
+ }
}
}
@@ -4043,6 +4058,11 @@ func (c *containerLXC) Update(args db.ContainerArgs, userRequested bool) error {
logger.Error(msg)
return fmt.Errorf(msg)
}
+ } else if m["type"] == "proxy" {
+ err = c.insertProxyDevice(k, m)
+ if err != nil {
+ return err
+ }
}
}
@@ -4071,6 +4091,11 @@ func (c *containerLXC) Update(args db.ContainerArgs, userRequested bool) error {
return err
}
}
+ } else if m["type"] == "proxy" {
+ err = c.updateProxyDevice(k, m)
+ if err != nil {
+ return err
+ }
}
}
@@ -5969,6 +5994,135 @@ func (c *containerLXC) removeUnixDevices() error {
return nil
}
+func (c *containerLXC) insertProxyDevice(devName string, m types.Device) error {
+ if !c.IsRunning() {
+ return fmt.Errorf("Can't add proxy device to stopped container")
+ }
+
+ proxyValues, err := setupProxyProcInfo(c, m)
+ if err != nil {
+ return err
+ }
+
+ proxyPid, err := shared.RunCommandGetPid(
+ c.state.OS.ExecPath,
+ "proxydevstart",
+ proxyValues.listenPid,
+ proxyValues.listenAddr,
+ proxyValues.connectPid,
+ proxyValues.connectAddr,
+ "0")
+ if err != nil {
+ return fmt.Errorf("Error occurred when starting proxy device: %s", err)
+ }
+
+ err = createProxyDevInfoFile(c.DevicesPath(), devName, proxyPid)
+ if err != nil {
+ process, _ := os.FindProcess(proxyPid)
+ process.Kill()
+ return fmt.Errorf("Error occurred when writing metadata for proxy process: %s", err)
+ }
+
+ return nil
+}
+
+func (c *containerLXC) removeProxyDevice(devName string) error {
+ if !c.IsRunning() {
+ return fmt.Errorf("Can't remove proxy device from stopped container")
+ }
+
+ devFileName := fmt.Sprintf("proxy.%s", devName)
+ devPath := filepath.Join(c.DevicesPath(), devFileName)
+ err := killProxyProc(devPath)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *containerLXC) removeProxyDevices() error {
+ // Check that we actually have devices to remove
+ if !shared.PathExists(c.DevicesPath()) {
+ return nil
+ }
+
+ // Load the directory listing
+ devFiles, err := ioutil.ReadDir(c.DevicesPath())
+ if err != nil {
+ return err
+ }
+
+ for _, f := range devFiles {
+ // Skip non-proxy devices
+ if !strings.HasPrefix(f.Name(), "proxy.") {
+ continue
+ }
+
+ // Kill the process
+ devicePath := filepath.Join(c.DevicesPath(), f.Name())
+ err = killProxyProc(devicePath)
+ if err != nil {
+ logger.Error("failed removing proxy device", log.Ctx{"err": err, "path": devicePath})
+ }
+ }
+
+ return nil
+}
+
+func (c *containerLXC) updateProxyDevice(devName string, m types.Device) error {
+ if !c.IsRunning() {
+ return fmt.Errorf("Can't update proxy device in stopped container")
+ }
+
+ fmt.Printf("updating the proxy device")
+ proxyValues, err := setupProxyProcInfo(c, m)
+ if err != nil {
+ return err
+ }
+
+ devFileName := fmt.Sprintf("proxy.%s", devName)
+ devPath := filepath.Join(c.DevicesPath(), devFileName)
+ err = killProxyProc(devPath)
+
+ if err != nil {
+ return fmt.Errorf("Error occurred when removing old proxy device")
+ }
+
+ proxyPid, err := shared.RunCommandGetPid(
+ c.state.OS.ExecPath,
+ "proxydevstart",
+ proxyValues.listenPid,
+ proxyValues.listenAddr,
+ proxyValues.connectPid,
+ proxyValues.connectAddr,
+ "0")
+ if err != nil {
+ return fmt.Errorf("Error occurred when starting new proxy device")
+ }
+
+ err = createProxyDevInfoFile(c.DevicesPath(), devName, proxyPid)
+ if err != nil {
+ process, _ := os.FindProcess(proxyPid)
+ process.Kill()
+ return fmt.Errorf("Error occurred when writing metadata for proxy process")
+ }
+
+ return nil
+}
+
+func (c *containerLXC) restartProxyDevices() {
+ for _, name := range c.expandedDevices.DeviceNames() {
+ m := c.expandedDevices[name]
+ if m["type"] == "proxy" {
+ err := c.insertProxyDevice(name, m)
+ if err != nil {
+ fmt.Printf("Error when starting proxy device '%s' for container %s: %s\n", name, c.name, err)
+ }
+ }
+ }
+}
+
// Network device handling
func (c *containerLXC) createNetworkDevice(name string, m types.Device) (string, error) {
var dev, n1 string
diff --git a/lxd/daemon.go b/lxd/daemon.go
index 3a22932cb..64a6246ce 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -436,7 +436,6 @@ func (d *Daemon) init() error {
if err != nil {
return fmt.Errorf("cannot start API endpoints: %v", err)
}
-
// Run the post initialization actions
err = d.Ready()
if err != nil {
diff --git a/lxd/db/devices.go b/lxd/db/devices.go
index 6911f4360..db4036e7f 100644
--- a/lxd/db/devices.go
+++ b/lxd/db/devices.go
@@ -25,6 +25,8 @@ func dbDeviceTypeToString(t int) (string, error) {
return "usb", nil
case 6:
return "gpu", nil
+ case 7:
+ return "proxy", nil
default:
return "", fmt.Errorf("Invalid device type %d", t)
}
@@ -46,6 +48,8 @@ func dbDeviceTypeToInt(t string) (int, error) {
return 5, nil
case "gpu":
return 6, nil
+ case "proxy":
+ return 7, nil
default:
return -1, fmt.Errorf("Invalid device type %s", t)
}
diff --git a/lxd/main.go b/lxd/main.go
index 35df502be..d702e3a13 100644
--- a/lxd/main.go
+++ b/lxd/main.go
@@ -45,7 +45,7 @@ func main() {
// Index of SubCommand functions by command line name
//
// "forkputfile", "forkgetfile", "forkmount" and "forkumount" are handled specially in main_nsexec.go
-// "forkgetnet" is partially handled in nsexec.go (setns)
+// "forkgetnet" is partially handled in main_nsexec.go (setns)
var subcommands = map[string]SubCommand{
// Main commands
"activateifneeded": cmdActivateIfNeeded,
@@ -65,4 +65,5 @@ var subcommands = map[string]SubCommand{
"forkexec": cmdForkExec,
"netcat": cmdNetcat,
"migratedumpsuccess": cmdMigrateDumpSuccess,
+ "proxydevstart": cmdProxyDevStart,
}
diff --git a/lxd/main_args.go b/lxd/main_args.go
index 6a9db8a01..27ed25ee8 100644
--- a/lxd/main_args.go
+++ b/lxd/main_args.go
@@ -133,4 +133,6 @@ Internal commands (don't call these directly):
Indicate that a migration dump was successful
netcat
Mirror a unix socket to stdin/stdout
+ proxydevstart
+ Start proxy device process for a container
`
diff --git a/lxd/main_nsexec.go b/lxd/main_nsexec.go
index dde8b3d23..8ae4a0589 100644
--- a/lxd/main_nsexec.go
+++ b/lxd/main_nsexec.go
@@ -681,6 +681,41 @@ void forkgetnet(char *buf, char *cur, ssize_t size) {
// The rest happens in Go
}
+void proxydevstart(char *buf, char *cur, ssize_t size) {
+ int cmdline, listen_pid, connect_pid, fdnum;
+
+ // Get the arguments
+ ADVANCE_ARG_REQUIRED();
+ listen_pid = atoi(cur);
+ ADVANCE_ARG_REQUIRED();
+ ADVANCE_ARG_REQUIRED();
+ connect_pid = atoi(cur);
+ ADVANCE_ARG_REQUIRED();
+ ADVANCE_ARG_REQUIRED();
+ fdnum = atoi(cur);
+
+ // Cannot pass through -1 to runCommand since it is interpreted as a flag
+ fdnum = fdnum == 0 ? -1 : fdnum;
+
+ char fdpath[80];
+ sprintf(fdpath, "/proc/self/fd/%d", fdnum);
+
+ // Join the listener ns if not already setup
+ if (access(fdpath, F_OK) < 0) {
+ // Attach to the network namespace of the listener
+ if (dosetns(listen_pid, "net") < 0) {
+ fprintf(stderr, "Failed setns to listener network namespace: %s\n", strerror(errno));
+ _exit(1);
+ }
+ } else {
+ // Join the connector ns now
+ if (dosetns(connect_pid, "net") < 0) {
+ fprintf(stderr, "Failed setns to connector network namespace: %s\n", strerror(errno));
+ _exit(1);
+ }
+ }
+}
+
__attribute__((constructor)) void init(void) {
int cmdline;
char buf[CMDLINE_SIZE];
@@ -723,6 +758,8 @@ __attribute__((constructor)) void init(void) {
forkumount(buf, cur, size);
} else if (strcmp(cur, "forkgetnet") == 0) {
forkgetnet(buf, cur, size);
+ } else if (strcmp(cur, "proxydevstart") == 0) {
+ proxydevstart(buf, cur, size);
}
}
*/
diff --git a/lxd/main_proxy.go b/lxd/main_proxy.go
new file mode 100644
index 000000000..9cc2fb2d7
--- /dev/null
+++ b/lxd/main_proxy.go
@@ -0,0 +1,165 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "os"
+ "os/signal"
+ "strings"
+ "strconv"
+ "syscall"
+
+ "github.com/lxc/lxd/shared"
+)
+
+func cmdProxyDevStart(args *Args) error {
+ err := run(args)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error: %v\n", err)
+ os.Exit(1)
+ }
+
+ os.Exit(0)
+ return nil
+}
+
+func run(args *Args) error {
+ if (len(args.Params) != 5) {
+ return fmt.Errorf("Invalid number of arguments")
+ }
+
+ // Get all our arguments
+ listenPid := args.Params[0]
+ listenAddr := args.Params[1]
+ connectPid := args.Params[2]
+ connectAddr := args.Params[3]
+
+ fd := -1
+ if args.Params[4] != "0" {
+ fd, _ = strconv.Atoi(args.Params[4])
+ }
+
+ // Check where we are in initialization
+ if !shared.PathExists(fmt.Sprintf("/proc/self/fd/%d", fd)) {
+ fmt.Fprintf(os.Stdout, "Listening on %s in %s, forwarding to %s from %s\n", listenAddr, listenPid, connectAddr, connectPid)
+
+ file, err := getListenerFile(listenAddr)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ listenerFd := file.Fd()
+ if err != nil {
+ return fmt.Errorf("failed to duplicate the listener fd: %v", err)
+ }
+
+ newFd, _ := syscall.Dup(int(listenerFd))
+
+ fmt.Fprintf(os.Stdout, "Re-executing ourselves\n")
+
+ args.Params[4] = strconv.Itoa(int(newFd))
+ execArgs := append([]string{"lxd" ,"proxydevstart"}, args.Params...)
+
+ err = syscall.Exec("/proc/self/exe", execArgs, []string{})
+ if err != nil {
+ return fmt.Errorf("failed to re-exec: %v", err)
+ }
+ }
+
+
+ // Re-create listener from fd
+ listenFile := os.NewFile(uintptr(fd), "listener")
+ listener, err := net.FileListener(listenFile)
+ if err != nil {
+ return fmt.Errorf("failed to re-assemble listener: %v", err)
+ }
+
+ defer listener.Close()
+
+ fmt.Fprintf(os.Stdout, "Starting to proxy\n")
+
+ // begin proxying
+ for {
+ // Accept a new client
+ srcConn, err := listener.Accept()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error: Failed to accept new connection: %v\n", err)
+ continue
+ }
+ fmt.Printf("Accepted a new connection\n")
+
+ // Connect to the target
+ dstConn, err := getDestConn(connectAddr)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error: Failed to connect to target: %v\n", err)
+ srcConn.Close()
+ continue
+ }
+
+ go io.Copy(srcConn, dstConn)
+ go io.Copy(dstConn, srcConn)
+ }
+
+ return nil
+}
+
+func getListenerFile(listenAddr string) (os.File, error) {
+ fields := strings.SplitN(listenAddr, ":", 2)
+ addr := strings.Join(fields[1:], "")
+
+ listener, err := net.Listen(fields[0], addr)
+ if err != nil {
+ return os.File{}, err
+ }
+
+ file := &os.File{}
+ switch listener.(type) {
+ case *net.TCPListener:
+ tcpListener := listener.(*net.TCPListener)
+ file, err = tcpListener.File()
+ case *net.UnixListener:
+ unixListener := listener.(*net.UnixListener)
+ file, err = unixListener.File()
+ }
+
+ if err != nil {
+ return os.File{}, fmt.Errorf("Failed to get file from listener: %v\n", err)
+ }
+
+ return *file, nil
+}
+
+func getDestConn(connectAddr string) (net.Conn, error) {
+ fields := strings.SplitN(connectAddr, ":", 2)
+ addr := strings.Join(fields[1:], "")
+ return net.Dial(fields[0], addr)
+}
+
+func cleanupUnixSocket(listenAddr string) error {
+ fields := strings.SplitN(listenAddr, ":", 2)
+ addr := strings.Join(fields[1:], "")
+ if fields[0] == "unix" {
+ err := syscall.Unlink(addr)
+ return err
+ }
+ return nil
+}
+
+func handleSignal(listenAddr string) {
+ sigc := make(chan os.Signal, 1)
+ signal.Notify(sigc, os.Interrupt, syscall.SIGINT)
+ go func() {
+ // Wait for a SIGINT
+ sig := <-sigc
+ fmt.Fprintf(os.Stdout, "Caught signal %s: cleaning up...", sig)
+ err := cleanupUnixSocket(listenAddr)
+ if err != nil {
+ fmt.Fprintf(os.Stdout, "Error unlinking unix socket: %v", err)
+ }
+ // And we're done:
+ os.Exit(0)
+ }()
+}
+
diff --git a/lxd/proxy_device_utils.go b/lxd/proxy_device_utils.go
new file mode 100644
index 000000000..287c60666
--- /dev/null
+++ b/lxd/proxy_device_utils.go
@@ -0,0 +1,93 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "syscall"
+)
+
+type proxyProcInfo struct {
+ listenPid string
+ connectPid string
+ connectAddr string
+ listenAddr string
+}
+
+func createProxyDevInfoFile(devicesPath string, proxyDev string, proxyPid int) error {
+ devFileName := fmt.Sprintf("proxy.%s", proxyDev)
+ filePath := filepath.Join(devicesPath, devFileName)
+ f, err := os.Create(filePath)
+
+ if err != nil {
+ return err
+ }
+
+ defer f.Close()
+
+ info := fmt.Sprintf("%d", proxyPid)
+ _, err = f.WriteString(info)
+
+ return err
+}
+
+func setupProxyProcInfo(c container, device map[string]string) (*proxyProcInfo, error) {
+ pid := c.InitPID()
+ containerPid := strconv.Itoa(int(pid))
+ lxdPid := strconv.Itoa(os.Getpid())
+
+ connectAddr := device["connect"]
+ listenAddr := device["listen"]
+
+ connectionType := strings.SplitN(connectAddr, ":", 2)[0]
+ listenerType := strings.SplitN(listenAddr, ":", 2)[0]
+
+ if connectionType != "tcp" {
+ return nil, fmt.Errorf("Proxy device currently doesnt support the connection type: %s", connectionType)
+ }
+ if listenerType != "tcp" {
+ return nil, fmt.Errorf("Proxy device currently doesnt support the listener type: %s", listenerType)
+ }
+
+ listenPid := "-1"
+ connectPid := "-1"
+
+ if (device["bind"] == "container") {
+ listenPid = containerPid
+ connectPid = lxdPid
+ } else if (device["bind"] == "host") {
+ listenPid = lxdPid
+ connectPid = containerPid
+ } else {
+ return nil, fmt.Errorf("No indicated binding side")
+ }
+
+ p := &proxyProcInfo{
+ listenPid: listenPid,
+ connectPid: connectPid,
+ connectAddr: connectAddr,
+ listenAddr: listenAddr,
+ }
+
+ return p, nil
+}
+
+func killProxyProc(devPath string) error {
+ contents, err := ioutil.ReadFile(devPath)
+ if err != nil {
+ return err
+ }
+
+ pid, _ := strconv.Atoi(string(contents))
+ if err != nil {
+ return err
+ }
+
+ syscall.Kill(pid, syscall.SIGINT)
+ os.Remove(devPath)
+
+ return nil
+}
diff --git a/shared/util.go b/shared/util.go
index e84eaeb26..6ac886f4c 100644
--- a/shared/util.go
+++ b/shared/util.go
@@ -843,6 +843,24 @@ func RunCommand(name string, arg ...string) (string, error) {
return string(output), nil
}
+func RunCommandGetPid(name string, arg ...string) (int, error) {
+ cmd := exec.Command(name, arg...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err := cmd.Start()
+ go cmd.Wait()
+ if err != nil {
+ err := RunError{
+ msg: fmt.Sprintf("Failed to run: %s %s: %s", name, strings.Join(arg, " ")),
+ Err: err,
+ }
+ return -1, err
+ }
+
+ processPid := cmd.Process.Pid
+ return processPid, nil
+}
+
func TryRunCommand(name string, arg ...string) (string, error) {
var err error
var output string
diff --git a/test/suites/proxy.sh b/test/suites/proxy.sh
new file mode 100644
index 000000000..6472b5107
--- /dev/null
+++ b/test/suites/proxy.sh
@@ -0,0 +1,78 @@
+test_proxy_device() {
+
+ MESSAGE="Proxy device test string"
+
+ lxc launch busybox proxyTester
+ lxc config device add proxyTester proxyDev proxy listen=tcp:127.0.0.1:1234 connect=tcp:127.0.0.1:4321 bind=host
+ if [ $(lxc config device list proxyTester) != "proxyDev" ]; then
+ echo "Proxy device was not added to container"
+ fi
+
+
+ lxc config device remove proxyTester proxyDev
+ if [ $(lxc config device list proxyTester) ]; then
+ echo "Proxy device was not removed from container"
+ fi
+
+ lxc config device add proxyTester proxyDev proxy listen=tcp:127.0.0.1:1234 connect=tcp:127.0.0.1:4321 bind=host
+ lxc stop proxyTester
+ if [ $(lxc config device list proxyTester) != "proxyDev" ]; then
+ echo "Proxy device should not be deleted from config on container stop"
+ fi
+ lxc delete proxyTester
+
+ lxc launch busybox proxyTester
+ if [ $(lxc config device list proxyTester) ]; then
+ echo "Proxy device was not deleted from config on container deletion"
+ fi
+
+
+ lxc config device add proxyTester proxyDev proxy listen=tcp:127.0.0.1:1234 connect=tcp:127.0.0.1:4321 bind=host
+ nsenter -n -t $(lxc query /1.0/containers/proxyTester/state | jq .pid) -- nc -6 -l 4321 > proxyTest.out &
+ sleep 2
+
+ exec 3>/dev/tcp/localhost/1234
+ echo ${MESSAGE} >&3
+ sleep 1
+
+ if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+ echo "Proxy device did not properly send data from host to container"
+ fi
+
+ rm -f proxyTest.out
+
+ lxc restart proxyTester
+ if [ $(lxc config device list proxyTester) != "proxyDev" ]; then
+ echo "Proxy device should not be removed on container restart"
+ fi
+
+ nsenter -n -t $(lxc query /1.0/containers/proxyTester/state | jq .pid) -- nc -6 -l 4321 > proxyTest.out &
+ sleep 2
+
+ exec 3>/dev/tcp/localhost/1234
+ echo ${MESSAGE} >&3
+ sleep 1
+
+ if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+ echo "Proxy device did not properly restart on container restart"
+ fi
+
+ rm -f proxyTest.out
+
+ lxc config device set proxyTester proxyDev connect tcp:127.0.0.1:1337
+ nsenter -n -t $(lxc query /1.0/containers/proxyTester/state | jq .pid) -- nc -6 -l 1337 > proxyTest.out &
+ sleep 2
+
+ exec 3>/dev/tcp/localhost/1234
+ echo ${MESSAGE} >&3
+ sleep 1
+
+ if [ "$(cat proxyTest.out)" != "${MESSAGE}" ]; then
+ echo "Proxy device did not properly restart when config was updated"
+ fi
+
+ rm -f proxyTest.out
+ lxc stop proxyTester
+ lxc delete proxyTester
+}
+
More information about the lxc-devel
mailing list