diff --git a/Makefile b/Makefile index b3ea57eb57..3d463ff450 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .DEFAULT_GOAL := help .PHONY : check lint install-linters dep test -.PHONY : build clean install format +.PHONY : build clean install format bin .PHONY : host-apps bin .PHONY : run stop config .PHONY : docker-image docker-clean docker-network @@ -173,6 +173,24 @@ docker-rerun: docker-stop docker container start -i ${DOCKER_NODE} +integration-startup: ## Starts up the required transports between 'skywire-node's of interactive testing environment + ./integration/startup.sh + +integration-teardown: ## Tears down all saved configs and states of integration executables + ./integration/tear-down.sh + +integration-run-generic: ## Runs the generic interactive testing environment + ./integration/run-generic-env.sh + +integration-run-messaging: ## Runs the messaging interactive testing environment + ./integration/run-messaging-env.sh + +integration-run-proxy: ## Runs the proxy interactive testing environment + ./integration/run-proxy-env.sh + +integration-run-ssh: ## Runs the ssh interactive testing environment + ./integration/run-ssh-env.sh + help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/cmd/skywire-cli/commands/node/gen-config.go b/cmd/skywire-cli/commands/node/gen-config.go index 4e9ef259e6..c7e88ab89a 100644 --- a/cmd/skywire-cli/commands/node/gen-config.go +++ b/cmd/skywire-cli/commands/node/gen-config.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "fmt" "path/filepath" + "time" "github.com/spf13/cobra" @@ -102,6 +103,7 @@ func defaultConfig() *node.Config { conf.Routing.SetupNodes = []cipher.PubKey{sPK} conf.Routing.Table.Type = "boltdb" conf.Routing.Table.Location = "./skywire/routing.db" + conf.Routing.RouteFinderTimeout = node.Duration(10 * time.Second) conf.ManagerNodes = []node.ManagerConfig{} @@ -110,6 +112,8 @@ func defaultConfig() *node.Config { conf.LogLevel = "info" + conf.ShutdownTimeout = node.Duration(10 * time.Second) + conf.Interfaces.RPCAddress = "localhost:3435" return conf diff --git a/cmd/skywire-cli/commands/rtfind/root.go b/cmd/skywire-cli/commands/rtfind/root.go index fcb0bfeda8..cc2c9e8596 100644 --- a/cmd/skywire-cli/commands/rtfind/root.go +++ b/cmd/skywire-cli/commands/rtfind/root.go @@ -2,6 +2,7 @@ package rtfind import ( "fmt" + "time" "github.com/spf13/cobra" @@ -12,11 +13,13 @@ import ( var frAddr string var frMinHops, frMaxHops uint16 +var timeout time.Duration func init() { RootCmd.Flags().StringVar(&frAddr, "addr", "https://routefinder.skywire.skycoin.net", "address in which to contact route finder service") RootCmd.Flags().Uint16Var(&frMinHops, "min-hops", 1, "min hops for the returning routeFinderRoutesCmd") RootCmd.Flags().Uint16Var(&frMaxHops, "max-hops", 1000, "max hops for the returning routeFinderRoutesCmd") + RootCmd.Flags().DurationVar(&timeout, "timeout", 10*time.Second, "timeout for remote server requests") } // RootCmd is the command that queries the route-finder. @@ -25,7 +28,7 @@ var RootCmd = &cobra.Command{ Short: "Queries the Route Finder for available routes between two nodes", Args: cobra.MinimumNArgs(2), Run: func(_ *cobra.Command, args []string) { - rfc := client.NewHTTP(frAddr) + rfc := client.NewHTTP(frAddr, timeout) var srcPK, dstPK cipher.PubKey internal.Catch(srcPK.Set(args[0])) diff --git a/cmd/skywire-node/commands/root.go b/cmd/skywire-node/commands/root.go index ec52912ffd..f6e88d0e1d 100644 --- a/cmd/skywire-node/commands/root.go +++ b/cmd/skywire-node/commands/root.go @@ -21,6 +21,7 @@ import ( ) const configEnv = "SW_CONFIG" +const defaultShutdownTimeout = node.Duration(10 * time.Second) var ( syslogAddr string @@ -73,12 +74,15 @@ var rootCmd = &cobra.Command{ } }() + if conf.ShutdownTimeout == 0 { + conf.ShutdownTimeout = defaultShutdownTimeout + } ch := make(chan os.Signal, 2) signal.Notify(ch, []os.Signal{syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT}...) <-ch go func() { select { - case <-time.After(10 * time.Second): + case <-time.After(time.Duration(conf.ShutdownTimeout)): logger.Fatal("Timeout reached: terminating") case s := <-ch: logger.Fatalf("Received signal %s: terminating", s) diff --git a/integration/InteractiveEnvironments.md b/integration/InteractiveEnvironments.md new file mode 100644 index 0000000000..10f3b78bcc --- /dev/null +++ b/integration/InteractiveEnvironments.md @@ -0,0 +1,274 @@ +# Interactive test environments + +## Table of contents + +- [Interactive test environments](#interactive-test-environments) + - [Table of contents](#table-of-contents) + - [Code structure](#code-structure) + - [Dependencies](#dependencies) + - [Environments & scenarios](#environments--scenarios) + - [Base Environment](#base-environment) + - [Generic Test Environment](#generic-test-environment) + - [SSH Test Environment](#ssh-test-environment) + - [Proxy test environment](#proxy-test-environment) + - [Preparation](#preparation) + - [Scenario. Proxy test #1](#scenario-proxy-test-1) + - [Notes & recipes](#notes--recipes) + - [Delays](#delays) + - [Tmux for new users](#tmux-for-new-users) + - [Guidelines for new test creation](#guidelines-for-new-test-creation) + +## Code structure + +```text +integration +├── generic # Generic environmnent +│   ├── env-vars.sh # +│   ├── nodeA.json # +│   └── nodeC.json # +├── messaging # Messaging testing environment +│   ├── env-vars.sh # +│   ├── nodeA.json # +│   └── nodeC.json # +├── proxy # Proxy testing environment +│   ├── env-vars.sh # +│   ├── nodeA.json # +│   └── nodeC.json # +├── ssh # ssh testing environment +│   ├── env-vars.sh # +│   ├── nodeA.json # +│   └── nodeC.json #S +├── InteractiveEnvironments.md # You're reading it +├── intermediary-nodeB.json # NodeB configurationS +├── run-base-env.sh # base environment in detached tmux session +├── run-generic-env.sh # generic environment in tmux +├── run-proxy-env.sh # proxy environment in tmux +├── run-ssh-env.sh # ssh environment in tmuxS +├── start-restart-nodeB.sh # script for restart in cycle NodeB +├── startup.sh # add transports between nodes +├── tear-down.sh # tear down everything +├── test-messaging-loop.sh # Test script for messaging in infinite loop +├── test-messaging.sh # Test one message between NodeA-NodeC, NodeC-NodeA +├── test-proxy.sh # Test script for proxy +├── test-ssh.sh # Test script for ssh +``` + +## Dependencies + +1. `tmux` (required for `integration/run-*-env.sh` scripts) +2. `jq` (required for `integration/*/env-vars.sh` scripts) +3. `bash` v3.x or greater (or compatible shell) + +**Notes for Mac OSX users** + +1. Running `tmux` in `iterm2` is known to be faulty. Consider switching to an alternative terminal emulator. +2. To install `jq` and `tmux` via brew: `brew install jq tmux` + + +## Environments & scenarios + +### Base Environment + +Base environment with `skywire-services` running on localhost + +Usage: +- as base for other environments as `source ./intergration/run-base-env.sh` in other `run-*-env.sh` scripts +- standalone: `./integration/run-base-env.sh && tmux attach -t skywire` + +### Generic Test Environment + +The generic test environment will define the following: + +- skywire-services running on localhost +- 3 skywire-nodes: + - NodeA, NodeC running all apps + - NodeB - intermediary node without apps + +**Run** + +```bash +# Tear down everything +$ make integration-teardown + +# Start all services and nodes +$ make integration-run-generic + +# Adds pre-defined transports +$ make integration-startup +``` + +**Stop** + +This is the recommended way to stop environment: + +```bash +$ tmux kill-session -t skywire +``` + +And optionally: + +```bash +$ make integration-teardown +``` + +**Commands** + +Instead of `../skywire/skywire-cli --rpc localhost:port [command]`, one can use: + +- `CLI_A node ls-tp` - list transports from nodeA +- `CLI_B node add-tp $PK_A` - add transport on nodeB to nodeA + +Consult with `./integration/env-vars.sh` for details. + +**Tests** + +These tests assume that the generic environment is running (via the aforementioned steps). + +- **TEST 1: Send messages back and forth once.** + ```bash + # To be run in the 'shell' tab of tmux. + ./integration/test-messaging.sh + ``` +- **TEST 2: Test send/receive with unstable NodeB.** + 1. Stop NodeB by switching to the 7th tmux window (`Ctrl+B` & `6`) and sending SIGTERM (`Ctrl-C`). + 2. Run the following in the same window: + ```bash + $ ./integration/start-restart-nodeB.sh + ``` + 3. Switch to the `shell` window and run: + ```bash + ./integration/test-messaging-loop.sh + ``` + +**Detailed Description** + +The following steps will be performed: + +1. copy sw*.json and start-restart-nodeB.sh into skywire directory +2. Create 9 tmux windows: + 1. MSGD: messaging-discovery + 2. MSG: messaging-server + 3. TRD: transport-discovery + 4. RF: route-finder + 5. SN: setup-node + 6. NodeA: first skywire-node running with generic/nodeA.json + 7. NodeB: first skywire-node running with intermediary-nodeB.json + 8. NodeC: first skywire-node running with generic/nodeC.json + 9. shell: new shell for interactive exploration +3. ENV-vars in shell-window: + 1. $MSG_PK, $SN_PK - public keys of messaging-server and setup-node + 2. $PK_A, $PK_B, $PK_C - public keys of node_A, node_B, node_C + 3. $RPC_A, $RPC_B, $RPC_C - `--rpc` param for ../skywire/skywire-cli + 4. $CHAT_A, $CHAT_B - addresses and ports for `skychat`-apps on node_A and node_C +4. Aliases in shell-window: `CLI_A`, `CLI_B`, `CLI_C` + +### SSH Test Environment + +The SSH Test Environment will define the following: + +- skywire-services running on localhost +- 3 skywire-nodes: + - NodeA - running `SSH` app + - NodeB - intermediary node without apps + - NodeC - running `SSH-client` app + +**Run** + +```bash +# Tear down everything +$ make integration-teardown + +# Prerequisite +$ echo $PK_C > ~/.therealssh/authorized_keys + +# Start all services and nodes +$ make integration-run-ssh + +# Adds pre-defined transports +$ make integration-startup +``` + +**Tests** + +- **TEST 1** + 1. Run `./integration/run-ssh-env.sh` - it will run: + 1. skywire-services on localhost + 2. NodeA with configured `SSH` app + 3. NodeB - intermediary + 4. NodeC with configured `SSH-client` app + 2. Run `./integration/test-ssh.sh` which will run in cycle: + 1. `./SSH-cli $PK_A "export n=1; loop -n $n echo A"` + 2. kill all skywire-nodes + 3. Collect logs + 4. Increase n by power of 2 + 5. Repeat + +### Proxy test environment + +The proxy test environment will define the following: + +- skywire-services running on localhost +- 3 skywire-nodes: + - NodeA - running `SSH` app + - NodeB - intermediary node without apps + - NodeC - running `SSH-client` app + +#### Preparation + +It's really tricky to make socks5 proxy work now from clean start. + +Because `socksproxy-client` needs: +- transport to NodeA +- NodeA must be running **before** start of `socksproxy-client` + +Recipe for clean start: + +1. Run `make integration-teardown` +2. Start `./integration/run-proxy-env.sh` +3. Run `make integration-startup` +4. Stop NodeA, NodeB, NodeC +5. Restart all nodes +6. Wait for message in NodeC logs about successful start of +socksproxy-client +7. Check `lsof -i :9999` that it's really started +8. Check `curl -v --retry 5 --retry-connrefused 1 --connect-timeout 5 -x socks5://123456:@localhost:9999 https://www.google.com` + + +#### Scenario. Proxy test #1 + +1. `./integration/run-proxy-env.sh` +2. In `shell` window run: `./integration/test-proxy.sh` +3. Examine `./logs/proxy` + +## Notes & recipes + +### Delays + +It's possible that a service could start earlier or later than needed. + +Examine windows, in case of failed service - restart it (E.g. `KeyUp`-`Enter`) + +Problem still exists in proxy test environment: + - NodeC cannot start `SSH-client` when NodeA is still starting `SSH` + +### Tmux for new users + +1. Read `man tmux` +2. Run `tmux list-keys` +3. Find your `send-prefix` key: `tmux list-keys | grep send-prefix` +4. Use this prefix for switching between windows + +### Guidelines for new test creation + +1. **Decide**: + - new test is new scenario in existing environments + - or new environment with new scenario +2. If existing environment is sufficient: + - create new script in `./integration` with name `test-[name of test].sh` + - use existing `./integration/run*.sh` for inspiration + - add section describing this scenario in this document +3. In case of need in special environment: + - `cp -r ./integration/generic ./integration/[new environment]` + - `cp ./integraton/run-generic-env.sh ./integration/run-[new environment].sh` + - modify whats needed + - add section describing new environment and scenario(s) in this document diff --git a/integration/check-route-finder.sh b/integration/check-route-finder.sh new file mode 100755 index 0000000000..094b2080d1 --- /dev/null +++ b/integration/check-route-finder.sh @@ -0,0 +1,11 @@ +echo -e "\n\n" ROUTE-FINDER "\n\n" + +echo +echo '{"src_pk":''"'$PK_A'","dst_pk":''"'$PK_C'","min_hops":0, "max_hops":50}' +echo '{"src_pk":''"'$PK_A'","dst_pk":''"'$PK_C'","min_hops":0, "max_hops":50}' |curl -X GET $RF/routes -d@- +echo +echo '{"src_pk":''"'$PK_A'","dst_pk":''"'$PK_B'","min_hops":0, "max_hops":50}' +echo '{"src_pk":''"'$PK_A'","dst_pk":''"'$PK_B'","min_hops":0, "max_hops":50}' |curl -X GET $RF/routes -d@- +echo +echo '{"src_pk":''"'$PK_B'","dst_pk":''"'$PK_C'","min_hops":0, "max_hops":50}' +echo '{"src_pk":''"'$PK_B'","dst_pk":''"'$PK_C'","min_hops":0, "max_hops":50}' |curl -X GET $RF/routes -d@- diff --git a/integration/check-services.sh b/integration/check-services.sh new file mode 100755 index 0000000000..17b1a22fb3 --- /dev/null +++ b/integration/check-services.sh @@ -0,0 +1,43 @@ + +echo -e "\n\n" MESSAGING-DISCOVERY + +echo -e "\n\n" $MSGD/messaging-discovery/available_servers"\n" +curl $MSGD/messaging-discovery/available_servers + +echo -e "\n\n" $MSGD/messaging-discovery/entry/PK_A"\n" +curl $MSGD/messaging-discovery/entry/$PK_A + +echo -e "\n\n" $MSGD/messaging-discovery/entry/PK_B"\n" +curl $MSGD/messaging-discovery/entry/$PK_B + +echo -e "\n\n" $MSGD/messaging-discovery/entry/PK_C"\n" +curl $MSGD/messaging-discovery/entry/$PK_C + + +echo -e "\n\n" TRANSPORT-DISCOVERY + +echo -e "\n\n" $TRD/security/nonces/PK_A"\n" +curl $TRD/security/nonces/$PK_A +echo -e "\n\n" $TRD/transports/edge:PK_A "\n" +curl $TRD/transports/edge:$PK_A + +echo -e "\n\n" $TRD/security/nonces/PK_B"\n" +curl $TRD/security/nonces/$PK_B +echo -e "\n\n" $TRD/transports/edge:PK_B "\n" +curl $TRD/transports/edge:$PK_B +echo -e "\n\n" $TRD/security/nonces/PK_C"\n" +curl $TRD/security/nonces/$PK_C +echo -e "\n\n" $TRD/transports/edge:PK_C "\n" +curl $TRD/transports/edge:$PK_C + +echo -e "\n\n" ROUTE-FINDER "\n\n" + +echo +echo '{"src_pk":''"'$PK_A'","dst_pk":''"'$PK_C'","min_hops":0, "max_hops":50}' +echo '{"src_pk":''"'$PK_A'","dst_pk":''"'$PK_C'","min_hops":0, "max_hops":50}' |curl -X GET $RF/routes -d@- +echo +echo '{"src_pk":''"'$PK_A'","dst_pk":''"'$PK_B'","min_hops":0, "max_hops":50}' +echo '{"src_pk":''"'$PK_A'","dst_pk":''"'$PK_B'","min_hops":0, "max_hops":50}' |curl -X GET $RF/routes -d@- +echo +echo '{"src_pk":''"'$PK_B'","dst_pk":''"'$PK_C'","min_hops":0, "max_hops":50}' +echo '{"src_pk":''"'$PK_B'","dst_pk":''"'$PK_C'","min_hops":0, "max_hops":50}' |curl -X GET $RF/routes -d@- diff --git a/integration/generic/env-vars.sh b/integration/generic/env-vars.sh new file mode 100644 index 0000000000..7a355a47c6 --- /dev/null +++ b/integration/generic/env-vars.sh @@ -0,0 +1,26 @@ +# This script needs to be `source`d from bash-compatible shell +# E.g. `source ./integration/generic/env-vars.sh` or `. ./integration/generic/env-vars.sh` +export PK_A=$(jq -r ".node.static_public_key" ./integration/generic/nodeA.json) +export RPC_A=$(jq -r ".interfaces.rpc" ./integration/generic/nodeA.json) +export PK_B=$(jq -r ".node.static_public_key" ./integration/intermediary-nodeB.json) +export RPC_B=$(jq -r ".interfaces.rpc" ./integration/intermediary-nodeB.json) +export PK_C=$(jq -r ".node.static_public_key" ./integration/generic/nodeC.json) +export RPC_C=$(jq -r ".interfaces.rpc" ./integration/generic/nodeC.json) + +export CHAT_A=http://localhost:8000/message +export CHAT_C=http://localhost$(jq -r '.apps [] |select(.app=="skychat")| .args[1] ' ./integration/generic/nodeC.json)/message + +alias CLI_A='./skywire-cli --rpc $RPC_A' +alias CLI_B='./skywire-cli --rpc $RPC_B' +alias CLI_C='./skywire-cli --rpc $RPC_C' + +export MSGD=https://messaging.discovery.skywire.skycoin.net +export TRD=https://transport.discovery.skywire.skycoin.net +export RF=https://routefinder.skywire.skycoin.net + +echo PK_A: $PK_A +echo PK_B: $PK_B +echo PK_C: $PK_C + +echo CHAT_A: $CHAT_A +echo CHAT_C: $CHAT_C diff --git a/integration/generic/nodeA.json b/integration/generic/nodeA.json new file mode 100644 index 0000000000..e984c6f482 --- /dev/null +++ b/integration/generic/nodeA.json @@ -0,0 +1,59 @@ +{ + "version": "1.0", + "node": { + "static_public_key": "02072dd1e2ccd761e717096e1a264de1d8917e78e3176ca99dbf7ccf7292969845", + "static_secret_key": "7073e557aa2308b448525397ea2f45d56c9962c4dcdf82c5fdb5cc02fca0481c" + }, + "messaging": { + "discovery": "https://messaging.discovery.skywire.skycoin.net", + "server_count": 1 + }, + "transport": { + "discovery": "https://transport.discovery.skywire.skycoin.net", + "log_store": { + "type": "file", + "location": "./local/nodeA/transport_logs" + } + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "https://routefinder.skywire.skycoin.net/", + "table": { + "type": "boltdb", + "location": "./local/nodeA/routing.db" + } + }, + "apps": [ + { + "app": "skychat", + "version": "1.0", + "auto_start": true, + "port": 1, + "args": [] + }, + { + "app": "SSH", + "version": "1.0", + "auto_start": true, + "port": 2, + "args": [] + }, + { + "app": "socksproxy", + "version": "1.0", + "auto_start": true, + "port": 3, + "args": [] + } + ], + "trusted_nodes": [], + "manager_nodes": [], + "apps_path": "./apps", + "local_path": "./local/nodeA", + "log_level": "info", + "interfaces": { + "rpc": "localhost:3436" + } +} diff --git a/integration/generic/nodeC.json b/integration/generic/nodeC.json new file mode 100644 index 0000000000..0b9aa6136a --- /dev/null +++ b/integration/generic/nodeC.json @@ -0,0 +1,59 @@ +{ + "version": "1.0", + "node": { + "static_public_key": "02c9ddf5c2ae6a5a2166028dafbc814eff3ec2352f429fb0aa37d96e1aa668f332", + "static_secret_key": "5ab3744ab56e4d0b82f9a915e07b8f05d51ec0f16ff8496bd92f4e378ca6c1fc" + }, + "messaging": { + "discovery": "https://messaging.discovery.skywire.skycoin.net", + "server_count": 1 + }, + "transport": { + "discovery": "https://transport.discovery.skywire.skycoin.net", + "log_store": { + "type": "file", + "location": "./local/nodeC/transport_logs" + } + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "https://routefinder.skywire.skycoin.net/", + "table": { + "type": "boltdb", + "location": "./local/nodeC/routing.db" + } + }, + "apps": [ + { + "app": "skychat", + "version": "1.0", + "auto_start": true, + "port": 1, + "args": ["-addr", ":8001"] + }, + { + "app": "SSH-client", + "version": "1.0", + "auto_start": true, + "port": 12, + "args": [] + }, + { + "app": "socksproxy-client", + "version": "1.0", + "auto_start": true, + "port": 13, + "args": ["-srv", "024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7"] + } + ], + "trusted_nodes": [], + "manager_nodes": [], + "apps_path": "./apps", + "local_path": "./local/nodeC", + "log_level": "info", + "interfaces": { + "rpc": "localhost:3438" + } +} diff --git a/integration/intermediary-nodeB.json b/integration/intermediary-nodeB.json new file mode 100644 index 0000000000..2d246dc2eb --- /dev/null +++ b/integration/intermediary-nodeB.json @@ -0,0 +1,38 @@ +{ + "version": "1.0", + "node": { + "static_public_key": "0372ee0a2b99b55906ac33b49704073ab90ab4f395d814d48d597b5b42c39e5c79", + "static_secret_key": "5092f14fe18bd7dcd34479c5d26bb3bf68b708ec74a12c4557091e82dd6e6c36" + }, + "messaging": { + "discovery": "https://messaging.discovery.skywire.skycoin.net", + "server_count": 1 + }, + "transport": { + "discovery": "https://transport.discovery.skywire.skycoin.net", + "log_store": { + "type": "file", + "location": "./local/nodeB/transport_logs" + } + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "https://routefinder.skywire.skycoin.net/", + "route_finder_timeout": "60s", + "table": { + "type": "boltdb", + "location": "./local/nodeB/routing.db" + } + }, + "apps": [], + "trusted_nodes": [], + "manager_nodes": [], + "apps_path": "./bin/apps", + "local_path": "./local/localB", + "log_level": "info", + "interfaces": { + "rpc": "localhost:3435" + } +} diff --git a/integration/messaging/env-vars.sh b/integration/messaging/env-vars.sh new file mode 100644 index 0000000000..8c5f41f47b --- /dev/null +++ b/integration/messaging/env-vars.sh @@ -0,0 +1,30 @@ +# This script needs to be `source`d from bash-compatible shell +# E.g. `source ./integration/generic/env-vars.sh` or `. ./integration/messaging/env-vars.sh` +export PK_A=$(jq -r ".node.static_public_key" ./integration/messaging/nodeA.json) +export RPC_A=$(jq -r ".interfaces.rpc" ./integration/messaging/nodeA.json) +export PK_B=$(jq -r ".node.static_public_key" ./integration/intermediary-nodeB.json) +export RPC_B=$(jq -r ".interfaces.rpc" ./integration/intermediary-nodeB.json) +export PK_C=$(jq -r ".node.static_public_key" ./integration/messaging/nodeC.json) +export RPC_C=$(jq -r ".interfaces.rpc" ./integration/messaging/nodeC.json) + +export CHAT_A=http://localhost:8000/message +export CHAT_C=http://localhost$(jq -r '.apps [] |select(.app=="skychat")| .args[1] ' ./integration/messaging/nodeC.json)/message + +export MSGD=https://messaging.discovery.skywire.skycoin.net +export TRD=https://transport.discovery.skywire.skycoin.net +export RF=https://routefinder.skywire.skycoin.net + +alias CLI_A='./skywire-cli --rpc $RPC_A' +alias CLI_B='./skywire-cli --rpc $RPC_B' +alias CLI_C='./skywire-cli --rpc $RPC_C' + +alias RUN_A='./skywire-node ./integration/messaging/nodeA.json --tag NodeA' +alias RUN_B='./skywire-node ./integration/messaging/intermediary-nodeB.json --tag NodeB' +alias RUN_C='./skywire-node ./integration/messaging/nodeC.json --tag NodeC' + +echo PK_A: $PK_A +echo PK_B: $PK_B +echo PK_C: $PK_C + +echo CHAT_A: $CHAT_A +echo CHAT_C: $CHAT_C diff --git a/integration/messaging/nodeA.json b/integration/messaging/nodeA.json new file mode 100644 index 0000000000..446ec9d2d2 --- /dev/null +++ b/integration/messaging/nodeA.json @@ -0,0 +1,47 @@ +{ + "version": "1.0", + "node": { + "static_public_key": "02072dd1e2ccd761e717096e1a264de1d8917e78e3176ca99dbf7ccf7292969845", + "static_secret_key": "7073e557aa2308b448525397ea2f45d56c9962c4dcdf82c5fdb5cc02fca0481c" + }, + "messaging": { + "discovery": "https://messaging.discovery.skywire.skycoin.net", + "server_count": 1 + }, + "transport": { + "discovery": "https://transport.discovery.skywire.skycoin.net", + "log_store": { + "type": "file", + "location": "./local/nodeA/transport_logs" + } + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "https://routefinder.skywire.skycoin.net/", + "route_finder_timeout": "60s", + "table": { + "type": "boltdb", + "location": "./local/nodeA/routing.db" + } + }, + "apps": [ + { + "app": "skychat", + "version": "1.0", + "auto_start": true, + "port": 1, + "args": [] + } + ], + "trusted_nodes": [], + "manager_nodes": [], + "apps_path": "./apps", + "shutdown_timeout": "30s", + "local_path": "./local/nodeA", + "log_level": "info", + "interfaces": { + "rpc": "localhost:3436" + } +} diff --git a/integration/messaging/nodeC.json b/integration/messaging/nodeC.json new file mode 100644 index 0000000000..2d41cf3024 --- /dev/null +++ b/integration/messaging/nodeC.json @@ -0,0 +1,47 @@ +{ + "version": "1.0", + "node": { + "static_public_key": "02c9ddf5c2ae6a5a2166028dafbc814eff3ec2352f429fb0aa37d96e1aa668f332", + "static_secret_key": "5ab3744ab56e4d0b82f9a915e07b8f05d51ec0f16ff8496bd92f4e378ca6c1fc" + }, + "messaging": { + "discovery": "https://messaging.discovery.skywire.skycoin.net", + "server_count": 1 + }, + "transport": { + "discovery": "https://transport.discovery.skywire.skycoin.net", + "log_store": { + "type": "file", + "location": "./local/nodeC/transport_logs" + } + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "https://routefinder.skywire.skycoin.net/", + "route_finder_timeout": "60s", + "table": { + "type": "boltdb", + "location": "./local/nodeC/routing.db" + } + }, + "apps": [ + { + "app": "skychat", + "version": "1.0", + "auto_start": true, + "port": 1, + "args": ["-addr", ":8001"] + } + ], + "trusted_nodes": [], + "manager_nodes": [], + "apps_path": "./apps", + "shutdown_timeout": "30s", + "local_path": "./local/nodeC", + "log_level": "info", + "interfaces": { + "rpc": "localhost:3438" + } +} diff --git a/integration/proxy/env-vars.sh b/integration/proxy/env-vars.sh new file mode 100644 index 0000000000..07a9aca479 --- /dev/null +++ b/integration/proxy/env-vars.sh @@ -0,0 +1,24 @@ +# This script needs to be `source`d from bash-compatible shell +# E.g. `source ./integration/proxy/env-vars.sh` or `. ./integration/proxy/env-vars.sh` +export PK_A=$(jq -r ".node.static_public_key" ./integration/proxy/nodeA.json) +export RPC_A=$(jq -r ".interfaces.rpc" ./integration/proxy/nodeA.json) +export PK_B=$(jq -r ".node.static_public_key" ./integration/intermediary-nodeB.json) +export RPC_B=$(jq -r ".interfaces.rpc" ./integration/intermediary-nodeB.json) +export PK_C=$(jq -r ".node.static_public_key" ./integration/proxy/nodeC.json) +export RPC_C=$(jq -r ".interfaces.rpc" ./integration/proxy/nodeC.json) + +alias CLI_A='./skywire-cli --rpc $RPC_A' +alias CLI_B='./skywire-cli --rpc $RPC_B' +alias CLI_C='./skywire-cli --rpc $RPC_C' + +export MSGD=https://messaging.discovery.skywire.skycoin.net +export TRD=https://transport.discovery.skywire.skycoin.net +export RF=https://routefinder.skywire.skycoin.net + +alias RUN_A='go run ./cmd/skywire-node ./integration/messaging/nodeA.json --tag NodeA' +alias RUN_B='go run ./cmd/skywire-node ./integration/intermediary-nodeB.json --tag NodeB' +alias RUN_C='go run ./cmd/skywire-node ./integration/messaging/nodeC.json --tag NodeC' + +echo PK_A: $PK_A +echo PK_B: $PK_B +echo PK_C: $PK_C diff --git a/integration/proxy/nodeA.json b/integration/proxy/nodeA.json new file mode 100644 index 0000000000..28aea37235 --- /dev/null +++ b/integration/proxy/nodeA.json @@ -0,0 +1,45 @@ +{ + "version": "1.0", + "node": { + "static_public_key": "02072dd1e2ccd761e717096e1a264de1d8917e78e3176ca99dbf7ccf7292969845", + "static_secret_key": "7073e557aa2308b448525397ea2f45d56c9962c4dcdf82c5fdb5cc02fca0481c" + }, + "messaging": { + "discovery": "https://messaging.discovery.skywire.skycoin.net", + "server_count": 1 + }, + "transport": { + "discovery": "https://transport.discovery.skywire.skycoin.net", + "log_store": { + "type": "file", + "location": "./local/nodeA/transport_logs" + } + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "https://routefinder.skywire.skycoin.net/", + "table": { + "type": "boltdb", + "location": "./local/nodeA/routing.db" + } + }, + "apps": [ + { + "app": "socksproxy", + "version": "1.0", + "auto_start": true, + "port": 3, + "args": [] + } + ], + "trusted_nodes": [], + "manager_nodes": [], + "apps_path": "./apps", + "local_path": "./local/nodeA", + "log_level": "info", + "interfaces": { + "rpc": "localhost:3436" + } +} diff --git a/integration/proxy/nodeC.json b/integration/proxy/nodeC.json new file mode 100644 index 0000000000..b86df4a7b3 --- /dev/null +++ b/integration/proxy/nodeC.json @@ -0,0 +1,45 @@ +{ + "version": "1.0", + "node": { + "static_public_key": "02c9ddf5c2ae6a5a2166028dafbc814eff3ec2352f429fb0aa37d96e1aa668f332", + "static_secret_key": "5ab3744ab56e4d0b82f9a915e07b8f05d51ec0f16ff8496bd92f4e378ca6c1fc" + }, + "messaging": { + "discovery": "https://messaging.discovery.skywire.skycoin.net", + "server_count": 1 + }, + "transport": { + "discovery": "https://transport.discovery.skywire.skycoin.net", + "log_store": { + "type": "file", + "location": "./local/nodeC/transport_logs" + } + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "https://routefinder.skywire.skycoin.net/", + "table": { + "type": "boltdb", + "location": "./local/nodeC/routing.db" + } + }, + "apps": [ + { + "app": "socksproxy-client", + "version": "1.0", + "auto_start": true, + "port": 13, + "args": ["-srv", "024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7"] + } + ], + "trusted_nodes": [], + "manager_nodes": [], + "apps_path": "./apps", + "local_path": "./local/nodeC", + "log_level": "info", + "interfaces": { + "rpc": "localhost:3438" + } +} diff --git a/integration/run-generic-env.sh b/integration/run-generic-env.sh new file mode 100755 index 0000000000..97139c5292 --- /dev/null +++ b/integration/run-generic-env.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +## SKYWIRE + +tmux new -s skywire -d + +source ./integration/generic/env-vars.sh + +echo "Checking transport-discovery is up" +curl --retry 5 --retry-connrefused 1 --connect-timeout 5 https://transport.discovery.skywire.skycoin.net/security/nonces/$PK_A + +tmux rename-window -t skywire NodeA +tmux send-keys -t NodeA './skywire-node ./integration/generic/nodeA.json --tag NodeA' C-m +tmux new-window -t skywire -n NodeB +tmux send-keys -t NodeB './skywire-node ./integration/intermediary-nodeB.json --tag NodeB' C-m +tmux new-window -t skywire -n NodeC +tmux send-keys -t NodeC './skywire-node ./integration/generic/nodeC.json --tag NodeC' C-m + +tmux new-window -t skywire -n shell + +tmux send-keys -t shell 'source ./integration/generic/env-vars.sh' C-m + +tmux attach -t skywire diff --git a/integration/run-messaging-env.sh b/integration/run-messaging-env.sh new file mode 100755 index 0000000000..53e50f3390 --- /dev/null +++ b/integration/run-messaging-env.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +## SKYWIRE + +tmux new -s skywire -d + +source ./integration/messaging/env-vars.sh + +echo "Checking transport-discovery is up" +curl --retry 5 --retry-connrefused 1 --connect-timeout 5 https://transport.discovery.skywire.skycoin.net/security/nonces/$PK_A + +tmux rename-window -t skywire NodeA +tmux send-keys -t NodeA './skywire-node ./integration/messaging/nodeA.json --tag NodeA' C-m +tmux new-window -t skywire -n NodeB +tmux send-keys -t NodeB './skywire-node ./integration/intermediary-nodeB.json --tag NodeB' C-m +tmux new-window -t skywire -n NodeC +tmux send-keys -t NodeC './skywire-node ./integration/messaging/nodeC.json --tag NodeC' C-m + +tmux new-window -t skywire -n shell + +tmux send-keys -t shell 'source ./integration/messaging/env-vars.sh' C-m + +tmux attach -t skywire diff --git a/integration/run-proxy-env.sh b/integration/run-proxy-env.sh new file mode 100755 index 0000000000..deec8d4179 --- /dev/null +++ b/integration/run-proxy-env.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +## SKYWIRE + +tmux new -s skywire -d + +source ./integration/proxy/env-vars.sh + +echo "Checking transport-discovery is up" +curl --retry 5 --retry-connrefused 1 --connect-timeout 5 https://transport.discovery.skywire.skycoin.net/security/nonces/$PK_A + +tmux rename-window -t skywire NodeA +tmux send-keys -t NodeA './skywire-node ./integration/proxy/nodeA.json --tag NodeA' C-m +tmux new-window -t skywire -n NodeB +tmux send-keys -t NodeB './skywire-node ./integration/intermediary-nodeB.json --tag NodeB' C-m +tmux new-window -t skywire -n NodeC +tmux send-keys -t NodeC './skywire-node ./integration/proxy/nodeC.json --tag NodeC' C-m + +tmux new-window -t skywire -n shell + +tmux send-keys -t shell 'source ./integration/proxy/env-vars.sh' C-m + +tmux attach -t skywire diff --git a/integration/run-ssh-env.sh b/integration/run-ssh-env.sh new file mode 100755 index 0000000000..2bec864e3d --- /dev/null +++ b/integration/run-ssh-env.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +## SKYWIRE + +tmux new -s skywire -d + +source ./integration/ssh/env-vars.sh + +echo "Checking transport-discovery is up" +curl --retry 5 --retry-connrefused 1 --connect-timeout 5 https://transport.discovery.skywire.skycoin.net/security/nonces/$PK_A + +tmux rename-window -t skywire NodeA +tmux send-keys -t NodeA './skywire-node ./integration/ssh/nodeA.json --tag NodeA' C-m +tmux new-window -t skywire -n NodeB +tmux send-keys -t NodeB './skywire-node ./integration/intermediary-nodeB.json --tag NodeB' C-m +tmux new-window -t skywire -n NodeC +tmux send-keys -t NodeC './skywire-node ./integration/ssh/nodeC.json --tag NodeC' C-m + +tmux new-window -t skywire -n shell + +tmux send-keys -t shell 'source ./integration/ssh/env-vars.sh' C-m + +tmux attach -t skywire diff --git a/integration/ssh/env-vars.sh b/integration/ssh/env-vars.sh new file mode 100644 index 0000000000..d938a1b5a9 --- /dev/null +++ b/integration/ssh/env-vars.sh @@ -0,0 +1,24 @@ +# This script needs to be `source`d from bash-compatible shell +# E.g. `source ./integration/ssh/env-vars.sh` or `. ./integration/ssh/env-vars.sh` +export PK_A=$(jq -r ".node.static_public_key" ./integration/ssh/nodeA.json) +export RPC_A=$(jq -r ".interfaces.rpc" ./integration/ssh/nodeA.json) +export PK_B=$(jq -r ".node.static_public_key" ./integration/intermediary-nodeB.json) +export RPC_B=$(jq -r ".interfaces.rpc" ./integration/intermediary-nodeB.json) +export PK_C=$(jq -r ".node.static_public_key" ./integration/ssh/nodeC.json) +export RPC_C=$(jq -r ".interfaces.rpc" ./integration/ssh/nodeC.json) + +alias CLI_A='./skywire-cli --rpc $RPC_A' +alias CLI_B='./skywire-cli --rpc $RPC_B' +alias CLI_C='./skywire-cli --rpc $RPC_C' + +export MSGD=https://messaging.discovery.skywire.skycoin.net +export TRD=https://transport.discovery.skywire.skycoin.net +export RF=https://routefinder.skywire.skycoin.net + +alias RUN_A='go run ./cmd/skywire-node ./integration/messaging/nodeA.json --tag NodeA' +alias RUN_B='go run ./cmd/skywire-node ./integration/intermediary-nodeB.json --tag NodeB' +alias RUN_C='go run ./cmd/skywire-node ./integration/messaging/nodeC.json --tag NodeC' + +echo PK_A: $PK_A +echo PK_B: $PK_B +echo PK_C: $PK_C diff --git a/integration/ssh/nodeA.json b/integration/ssh/nodeA.json new file mode 100644 index 0000000000..e984c6f482 --- /dev/null +++ b/integration/ssh/nodeA.json @@ -0,0 +1,59 @@ +{ + "version": "1.0", + "node": { + "static_public_key": "02072dd1e2ccd761e717096e1a264de1d8917e78e3176ca99dbf7ccf7292969845", + "static_secret_key": "7073e557aa2308b448525397ea2f45d56c9962c4dcdf82c5fdb5cc02fca0481c" + }, + "messaging": { + "discovery": "https://messaging.discovery.skywire.skycoin.net", + "server_count": 1 + }, + "transport": { + "discovery": "https://transport.discovery.skywire.skycoin.net", + "log_store": { + "type": "file", + "location": "./local/nodeA/transport_logs" + } + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "https://routefinder.skywire.skycoin.net/", + "table": { + "type": "boltdb", + "location": "./local/nodeA/routing.db" + } + }, + "apps": [ + { + "app": "skychat", + "version": "1.0", + "auto_start": true, + "port": 1, + "args": [] + }, + { + "app": "SSH", + "version": "1.0", + "auto_start": true, + "port": 2, + "args": [] + }, + { + "app": "socksproxy", + "version": "1.0", + "auto_start": true, + "port": 3, + "args": [] + } + ], + "trusted_nodes": [], + "manager_nodes": [], + "apps_path": "./apps", + "local_path": "./local/nodeA", + "log_level": "info", + "interfaces": { + "rpc": "localhost:3436" + } +} diff --git a/integration/ssh/nodeC.json b/integration/ssh/nodeC.json new file mode 100644 index 0000000000..0b9aa6136a --- /dev/null +++ b/integration/ssh/nodeC.json @@ -0,0 +1,59 @@ +{ + "version": "1.0", + "node": { + "static_public_key": "02c9ddf5c2ae6a5a2166028dafbc814eff3ec2352f429fb0aa37d96e1aa668f332", + "static_secret_key": "5ab3744ab56e4d0b82f9a915e07b8f05d51ec0f16ff8496bd92f4e378ca6c1fc" + }, + "messaging": { + "discovery": "https://messaging.discovery.skywire.skycoin.net", + "server_count": 1 + }, + "transport": { + "discovery": "https://transport.discovery.skywire.skycoin.net", + "log_store": { + "type": "file", + "location": "./local/nodeC/transport_logs" + } + }, + "routing": { + "setup_nodes": [ + "0324579f003e6b4048bae2def4365e634d8e0e3054a20fc7af49daf2a179658557" + ], + "route_finder": "https://routefinder.skywire.skycoin.net/", + "table": { + "type": "boltdb", + "location": "./local/nodeC/routing.db" + } + }, + "apps": [ + { + "app": "skychat", + "version": "1.0", + "auto_start": true, + "port": 1, + "args": ["-addr", ":8001"] + }, + { + "app": "SSH-client", + "version": "1.0", + "auto_start": true, + "port": 12, + "args": [] + }, + { + "app": "socksproxy-client", + "version": "1.0", + "auto_start": true, + "port": 13, + "args": ["-srv", "024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7"] + } + ], + "trusted_nodes": [], + "manager_nodes": [], + "apps_path": "./apps", + "local_path": "./local/nodeC", + "log_level": "info", + "interfaces": { + "rpc": "localhost:3438" + } +} diff --git a/integration/start-restart-nodeB.sh b/integration/start-restart-nodeB.sh new file mode 100755 index 0000000000..dbf9c0e5de --- /dev/null +++ b/integration/start-restart-nodeB.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +mkdir -p ./logs +echo Press Ctrl-C to exit +for ((;;)) +do + ./bin/skywire-node ./integration/intermediary-nodeB.json --tag NodeB 2>> ./logs/nodeB.log >> ./logs/nodeB.log & + echo node starting NodeB + sleep 25 + echo Killing NodeB on $(ps aux |grep "[N]odeB" |awk '{print $2}') + kill $(ps aux |grep "[N]odeB" |awk '{print $2}') + sleep 3 + echo Restarting NodeB +done diff --git a/integration/startup.sh b/integration/startup.sh new file mode 100755 index 0000000000..1ca41aa4c5 --- /dev/null +++ b/integration/startup.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# Use this script: +# - inside tmux session created by run-*-env.sh scripts +# - or standalone `source ./integration/[name of environment]/env-vars.sh && ./integration/startup.sh` + +./skywire-cli --rpc $RPC_A node add-tp $PK_B +./skywire-cli --rpc $RPC_C node add-tp $PK_B +sleep 1 + +echo "NodeA Transports:" +./skywire-cli --rpc $RPC_A node ls-tp + +echo "NodeB Transports:" +./skywire-cli --rpc $RPC_B node ls-tp diff --git a/integration/tear-down.sh b/integration/tear-down.sh new file mode 100755 index 0000000000..37c685deed --- /dev/null +++ b/integration/tear-down.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# In case skywire-nodes are not stopped properly. +kill $(ps aux |grep "[N]odeA" |awk '{print $2}') +kill $(ps aux |grep "[N]odeB" |awk '{print $2}') +kill $(ps aux |grep "[N]odeC" |awk '{print $2}') + +echo Removing ./local +rm -rf ./local diff --git a/integration/test-messaging-loop.sh b/integration/test-messaging-loop.sh new file mode 100755 index 0000000000..97223bde10 --- /dev/null +++ b/integration/test-messaging-loop.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +source ./integration/generic/env-vars.sh +echo "Press Ctrl-C to exit" +for ((;;)) +do + curl --data {'"recipient":"'$PK_A'", "message":"Hello Joe!"}' -X POST $CHAT_C + curl --data {'"recipient":"'$PK_C'", "message":"Hello Mike!"}' -X POST $CHAT_A +done diff --git a/integration/test-messaging.sh b/integration/test-messaging.sh new file mode 100755 index 0000000000..3a8ed4cf49 --- /dev/null +++ b/integration/test-messaging.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +source ./integration/generic/env-vars.sh +# curl --data {'"recipient":"'$PK_A'", "message":"Hello Joe!"}' -X POST $CHAT_C +curl --data {'"recipient":"'$PK_C'", "message":"Hello Mike!"}' -X POST $CHAT_A diff --git a/integration/test-proxy.sh b/integration/test-proxy.sh new file mode 100755 index 0000000000..1c72d0a6f3 --- /dev/null +++ b/integration/test-proxy.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +echo Starting ssh test +echo Press Ctrl-C to exit + +source ./integration/proxy/env-vars.sh + +export N=1 +for i in {1..16} +do + echo Test with $N requests + mkdir -p ./logs/proxy/$N + + echo Killing nodes + echo Killing $(ps aux |grep "[N]odeA\|[N]odeB\|[N]odeC" |awk '{print $2}') + kill $(ps aux |grep "[N]odeA\|[N]odeB\|[N]odeC" |awk '{print $2}') + + # This sleep needed to allow clean exit of node + sleep 10 + + echo Restarting nodeA and NodeB + ./bin/skywire-node ./integration/proxy/nodeA.json --tag NodeA &> ./logs/proxy/$N/nodeA.log & + ./bin/skywire-node ./integration/intermediary-nodeB.json --tag NodeB &> ./logs/proxy/$N/nodeB.log & + + # TODO: improve this sleep + sleep 5 + echo Restarting nodeC + ./bin/skywire-node ./integration/proxy/nodeC.json --tag NodeC &> ./logs/proxy/$N/nodeC.log & + + sleep 20 + echo Trying socks5 proxy + + for ((j=0; j<$N; j++)) + do + echo Request $j + curl -v --retry 5 --retry-connrefused 1 --connect-timeout 5 -x socks5://123456:@localhost:9999 https://www.google.com &>> ./logs/proxy/$N/curl.out + done + + export N=$(($N*2)) +done \ No newline at end of file diff --git a/integration/test-ssh.sh b/integration/test-ssh.sh new file mode 100755 index 0000000000..2aad08ab6a --- /dev/null +++ b/integration/test-ssh.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +echo Starting ssh test +echo Press Ctrl-C to exit + +source ./integration/ssh/env-vars.sh + +export N=1 +for i in {1..16} +do + echo Test with $N lines + mkdir -p ./logs/ssh/$N + + echo Killing nodes and SSH-cli + echo Killing $(ps aux |grep "[N]odeA\|[N]odeB\|[N]odeC\|[s]kywire/SSH-cli" |awk '{print $2}') + kill $(ps aux |grep "[N]odeA\|[N]odeB\|[N]odeC\|[s]kywire/SSH-cli" |awk '{print $2}') + + echo Restarting nodes + ./bin/skywire-node ./integration/ssh/nodeA.json --tag NodeA &> ./logs/ssh/$N/nodeA.log & + ./bin/skywire-node ./integration/intermediary-nodeB.json --tag NodeB &> ./logs/ssh/$N/nodeB.log & + ./bin/skywire-node ./integration/ssh/nodeC.json --tag NodeC &> ./logs/ssh/$N/nodeC.log & + + sleep 20 + echo Trying SSH-cli + export CMD=$(echo ./bin/SSH-cli $PK_A \"loop -n $N echo A\") + echo $CMD + eval $CMD &>./logs/ssh/$N/SSH-cli.out + + + export N=$(($N*2)) +done diff --git a/pkg/messaging/channel.go b/pkg/messaging/channel.go index 5dbffb8e47..02c298abec 100644 --- a/pkg/messaging/channel.go +++ b/pkg/messaging/channel.go @@ -54,6 +54,7 @@ func newChannel(initiator bool, secKey cipher.SecKey, remote cipher.PubKey, link readChan: make(chan []byte), doneChan: make(chan struct{}), noise: noiseInstance, + deadline: time.Time{}, }, nil } diff --git a/pkg/messaging/factory.go b/pkg/messaging/factory.go index b35fff7444..edaf2a26fb 100644 --- a/pkg/messaging/factory.go +++ b/pkg/messaging/factory.go @@ -26,9 +26,9 @@ var ( // ErrRejected indicates that ChannelOpen frame was rejected by remote server. ErrRejected = errors.New("rejected") // ErrChannelClosed indicates that underlying channel is being closed and writes are prohibited. - ErrChannelClosed = errors.New("channel closed") + ErrChannelClosed = errors.New("messaging channel closed") // ErrDeadlineExceeded indicates that read/write operation failed due to timeout. - ErrDeadlineExceeded = errors.New("deadline exceeded") + ErrDeadlineExceeded = errors.New("deadline exceeded in messaging") // ErrClientClosed indicates that client is closed and not accepting new connections. ErrClientClosed = errors.New("client closed") ) diff --git a/pkg/node/config.go b/pkg/node/config.go index 2df9e89797..b6b358cc1c 100644 --- a/pkg/node/config.go +++ b/pkg/node/config.go @@ -1,6 +1,7 @@ package node import ( + "encoding/json" "errors" "fmt" "os" @@ -39,9 +40,10 @@ type Config struct { } `json:"transport"` Routing struct { - SetupNodes []cipher.PubKey `json:"setup_nodes"` - RouteFinder string `json:"route_finder"` - Table struct { + SetupNodes []cipher.PubKey `json:"setup_nodes"` + RouteFinder string `json:"route_finder"` + RouteFinderTimeout Duration `json:"route_finder_timeout"` + Table struct { Type string `json:"type"` Location string `json:"location"` } `json:"table"` @@ -55,7 +57,8 @@ type Config struct { AppsPath string `json:"apps_path"` LocalPath string `json:"local_path"` - LogLevel string `json:"log_level"` + LogLevel string `json:"log_level"` + ShutdownTimeout Duration `json:"shutdown_timeout"` // time value, examples: 10s, 1m, etc Interfaces InterfaceConfig `json:"interfaces"` } @@ -174,3 +177,33 @@ type AppConfig struct { type InterfaceConfig struct { RPCAddress string `json:"rpc"` // RPC address and port for command-line interface (leave blank to disable RPC interface). } + +// Duration wraps around time.Duration to allow parsing from and to JSON +type Duration time.Duration + +// MarshalJSON implements json marshaling +func (d Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(time.Duration(d).String()) +} + +// UnmarshalJSON implements unmarshal from json +func (d *Duration) UnmarshalJSON(b []byte) error { + var v interface{} + if err := json.Unmarshal(b, &v); err != nil { + return err + } + switch value := v.(type) { + case float64: + *d = Duration(time.Duration(value)) + return nil + case string: + tmp, err := time.ParseDuration(value) + if err != nil { + return err + } + *d = Duration(tmp) + return nil + default: + return errors.New("invalid duration") + } +} diff --git a/pkg/node/node.go b/pkg/node/node.go index 931c120bcd..e073304c82 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -150,7 +150,7 @@ func NewNode(config *Config) (*Node, error) { SecKey: sk, TransportManager: node.tm, RoutingTable: node.rt, - RouteFinder: routeFinder.NewHTTP(config.Routing.RouteFinder), + RouteFinder: routeFinder.NewHTTP(config.Routing.RouteFinder, time.Duration(config.Routing.RouteFinderTimeout)), SetupNodes: config.Routing.SetupNodes, } r := router.New(rConfig) diff --git a/pkg/route-finder/client/client.go b/pkg/route-finder/client/client.go index d11813cb99..d80abd8167 100644 --- a/pkg/route-finder/client/client.go +++ b/pkg/route-finder/client/client.go @@ -15,7 +15,7 @@ import ( "github.com/skycoin/skywire/pkg/routing" ) -const contextTimeout = 10 * time.Second +const defaultContextTimeout = 10 * time.Second // GetRoutesRequest parses json body for /routes endpoint request type GetRoutesRequest struct { @@ -50,15 +50,21 @@ type Client interface { // APIClient implements Client interface type apiClient struct { - addr string - client http.Client + addr string + client http.Client + apiTimeout time.Duration } // NewHTTP constructs new Client that communicates over http. -func NewHTTP(addr string) Client { +func NewHTTP(addr string, apiTimeout time.Duration) Client { + if apiTimeout == 0 { + apiTimeout = defaultContextTimeout + } + return &apiClient{ - addr: sanitizedAddr(addr), - client: http.Client{}, + addr: sanitizedAddr(addr), + client: http.Client{}, + apiTimeout: apiTimeout, } } @@ -81,7 +87,7 @@ func (c *apiClient) PairedRoutes(source, destiny cipher.PubKey, minHops, maxHops return nil, nil, err } req.Header.Set("Content-Type", "application/json") - ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) + ctx, cancel := context.WithTimeout(context.Background(), c.apiTimeout) defer cancel() req = req.WithContext(ctx) diff --git a/pkg/transport/handshake.go b/pkg/transport/handshake.go index 7c8f829e3d..10dc1a1dd9 100644 --- a/pkg/transport/handshake.go +++ b/pkg/transport/handshake.go @@ -23,7 +23,8 @@ func (handshake settlementHandshake) Do(tm *Manager, tr Transport, timeout time. case <-done: return entry, err case <-time.After(timeout): - return nil, errors.New("deadline exceeded") + tm.Logger.Infof("handshake.Do timeout exceeded for value: %v", timeout) + return nil, errors.New("deadline exceeded on handshake") } }