@@ -9,10 +9,12 @@ import (
9
9
"io"
10
10
"io/fs"
11
11
"maps"
12
+ "net"
12
13
"os"
13
14
"path/filepath"
14
15
"strconv"
15
16
"strings"
17
+ "sync"
16
18
"testing"
17
19
"time"
18
20
@@ -38,6 +40,7 @@ import (
38
40
"go.viam.com/rdk/testutils/inject"
39
41
"go.viam.com/rdk/testutils/robottestutils"
40
42
"go.viam.com/rdk/utils"
43
+ "go.viam.com/rdk/web/server"
41
44
)
42
45
43
46
var (
@@ -1320,3 +1323,116 @@ func TestUpdateOAuthAppAction(t *testing.T) {
1320
1323
test .That (t , len (out .messages ), test .ShouldEqual , 0 )
1321
1324
})
1322
1325
}
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