Skip to content

Commit 576e4df

Browse files
authored
RSDK-9865 Add E2E CLI test for tunneling (viamrobotics#4842)
1 parent 069debf commit 576e4df

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

cli/client_test.go

+116
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import (
99
"io"
1010
"io/fs"
1111
"maps"
12+
"net"
1213
"os"
1314
"path/filepath"
1415
"strconv"
1516
"strings"
17+
"sync"
1618
"testing"
1719
"time"
1820

@@ -38,6 +40,7 @@ import (
3840
"go.viam.com/rdk/testutils/inject"
3941
"go.viam.com/rdk/testutils/robottestutils"
4042
"go.viam.com/rdk/utils"
43+
"go.viam.com/rdk/web/server"
4144
)
4245

4346
var (
@@ -1320,3 +1323,116 @@ func TestUpdateOAuthAppAction(t *testing.T) {
13201323
test.That(t, len(out.messages), test.ShouldEqual, 0)
13211324
})
13221325
}
1326+
1327+
func TestTunnelE2ECLI(t *testing.T) {
1328+
// `TestTunnelE2ECLI` attempts to send "Hello, World!" across a tunnel created by the
1329+
// CLI. It is mostly identical to `TestTunnelE2E` in web/server/entrypoint_test.go.
1330+
// The tunnel is:
1331+
//
1332+
// test-process <-> cli-listener(localhost:23659) <-> machine(localhost:23658) <-> dest-listener(localhost:23657)
1333+
1334+
tunnelMsg := "Hello, World!"
1335+
destPort := 23657
1336+
destListenerAddr := net.JoinHostPort("localhost", strconv.Itoa(destPort))
1337+
machineAddr := net.JoinHostPort("localhost", "23658")
1338+
sourcePort := 23657
1339+
sourceListenerAddr := net.JoinHostPort("localhost", strconv.Itoa(sourcePort))
1340+
1341+
logger := logging.NewTestLogger(t)
1342+
ctx := context.Background()
1343+
runServerCtx, runServerCtxCancel := context.WithCancel(ctx)
1344+
var wg sync.WaitGroup
1345+
1346+
// Start "destination" listener.
1347+
destListener, err := net.Listen("tcp", destListenerAddr)
1348+
test.That(t, err, test.ShouldBeNil)
1349+
defer func() {
1350+
test.That(t, destListener.Close(), test.ShouldBeNil)
1351+
}()
1352+
1353+
wg.Add(1)
1354+
go func() {
1355+
defer wg.Done()
1356+
1357+
logger.Infof("Listening on %s for tunnel message", destListenerAddr)
1358+
conn, err := destListener.Accept()
1359+
test.That(t, err, test.ShouldBeNil)
1360+
defer func() {
1361+
test.That(t, conn.Close(), test.ShouldBeNil)
1362+
}()
1363+
1364+
bytes := make([]byte, 1024)
1365+
n, err := conn.Read(bytes)
1366+
test.That(t, err, test.ShouldBeNil)
1367+
test.That(t, n, test.ShouldEqual, len(tunnelMsg))
1368+
test.That(t, string(bytes), test.ShouldContainSubstring, tunnelMsg)
1369+
logger.Info("Received expected tunnel message at", destListenerAddr)
1370+
1371+
// Write the same message back.
1372+
n, err = conn.Write([]byte(tunnelMsg))
1373+
test.That(t, err, test.ShouldBeNil)
1374+
test.That(t, n, test.ShouldEqual, len(tunnelMsg))
1375+
1376+
// Cancel `runServerCtx` once message has made it all the way across and has been
1377+
// echoed back. This should stop the `RunServer` goroutine below.
1378+
runServerCtxCancel()
1379+
}()
1380+
1381+
// Start a machine at `machineAddr` (`RunServer` in a goroutine.)
1382+
wg.Add(1)
1383+
go func() {
1384+
defer wg.Done()
1385+
1386+
// Create a temporary config file.
1387+
tempConfigFile, err := os.CreateTemp(t.TempDir(), "temp_config.json")
1388+
test.That(t, err, test.ShouldBeNil)
1389+
cfg := &robotconfig.Config{
1390+
Network: robotconfig.NetworkConfig{
1391+
NetworkConfigData: robotconfig.NetworkConfigData{
1392+
TrafficTunnelEndpoints: []robotconfig.TrafficTunnelEndpoint{
1393+
{
1394+
Port: destPort, // allow tunneling to destination port
1395+
},
1396+
},
1397+
BindAddress: machineAddr,
1398+
},
1399+
},
1400+
}
1401+
cfgBytes, err := json.Marshal(&cfg)
1402+
test.That(t, err, test.ShouldBeNil)
1403+
test.That(t, os.WriteFile(tempConfigFile.Name(), cfgBytes, 0o755), test.ShouldBeNil)
1404+
1405+
args := []string{"viam-server", "-config", tempConfigFile.Name()}
1406+
test.That(t, server.RunServer(runServerCtx, args, logger), test.ShouldBeNil)
1407+
}()
1408+
1409+
rc := robottestutils.NewRobotClient(t, logger, machineAddr, time.Second)
1410+
1411+
// Start CLI tunneler.
1412+
//nolint:dogsled
1413+
cCtx, _, _, _ := setup(nil, nil, nil, nil, "token")
1414+
wg.Add(1)
1415+
go func() {
1416+
defer wg.Done()
1417+
tunnelTraffic(cCtx, rc, sourcePort, destPort)
1418+
}()
1419+
1420+
// Write `tunnelMsg` to CLI tunneler over TCP from this test process.
1421+
conn, err := net.Dial("tcp", sourceListenerAddr)
1422+
test.That(t, err, test.ShouldBeNil)
1423+
defer func() {
1424+
test.That(t, conn.Close(), test.ShouldBeNil)
1425+
}()
1426+
n, err := conn.Write([]byte(tunnelMsg))
1427+
test.That(t, err, test.ShouldBeNil)
1428+
test.That(t, n, test.ShouldEqual, len(tunnelMsg))
1429+
1430+
// Expect `tunnelMsg` to be written back.
1431+
bytes := make([]byte, 1024)
1432+
n, err = conn.Read(bytes)
1433+
test.That(t, err, test.ShouldBeNil)
1434+
test.That(t, n, test.ShouldEqual, len(tunnelMsg))
1435+
test.That(t, string(bytes), test.ShouldContainSubstring, tunnelMsg)
1436+
1437+
wg.Wait()
1438+
}

0 commit comments

Comments
 (0)