summaryrefslogtreecommitdiffhomepage
path: root/tstest/integration/vms/vms_steps_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'tstest/integration/vms/vms_steps_test.go')
-rw-r--r--tstest/integration/vms/vms_steps_test.go134
1 files changed, 134 insertions, 0 deletions
diff --git a/tstest/integration/vms/vms_steps_test.go b/tstest/integration/vms/vms_steps_test.go
new file mode 100644
index 000000000..45ba95e12
--- /dev/null
+++ b/tstest/integration/vms/vms_steps_test.go
@@ -0,0 +1,134 @@
+// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build linux
+
+package vms
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "net"
+ "net/http"
+ "strings"
+ "testing"
+ "time"
+
+ "golang.org/x/crypto/ssh"
+ "inet.af/netaddr"
+)
+
+const timeout = 15 * time.Second
+
+func retry(t *testing.T, fn func() error) {
+ t.Helper()
+ const tries = 3
+ var err error
+ for i := 0; i < tries; i++ {
+ err = fn()
+ if err != nil {
+ t.Logf("%dth invocation failed, trying again: %v", i, err)
+ time.Sleep(50 * time.Millisecond)
+ }
+ if err == nil {
+ return
+ }
+ }
+ t.Fatalf("tried %d times, got: %v", tries, err)
+}
+
+func (h Harness) testPing(t *testing.T, ipAddr netaddr.IP, cli *ssh.Client) {
+ var outp []byte
+ var err error
+ retry(t, func() error {
+ sess := getSession(t, cli)
+
+ outp, err = sess.CombinedOutput(fmt.Sprintf("tailscale ping -c 1 %s", ipAddr))
+ return err
+ })
+
+ if !bytes.Contains(outp, []byte("pong")) {
+ t.Log(string(outp))
+ t.Fatal("no pong")
+ }
+
+ retry(t, func() error {
+ sess := getSession(t, cli)
+
+ // NOTE(Xe): the ping command is inconsistent across distros. Joy.
+ pingCmd := fmt.Sprintf("sh -c 'ping -c 1 %[1]s || ping -6 -c 1 %[1]s || ping6 -c 1 %[1]s\n'", ipAddr)
+ t.Logf("running %q", pingCmd)
+ outp, err = sess.CombinedOutput(pingCmd)
+ return err
+ })
+
+ if !bytes.Contains(outp, []byte("bytes")) {
+ t.Log(string(outp))
+ t.Fatalf("wanted output to contain %q, it did not", "bytes")
+ }
+}
+
+func getSession(t *testing.T, cli *ssh.Client) *ssh.Session {
+ sess, err := cli.NewSession()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ t.Cleanup(func() {
+ sess.Close()
+ })
+
+ return sess
+}
+
+func (h Harness) testOutgoingTCP(t *testing.T, ipAddr netaddr.IP, cli *ssh.Client) {
+ const sendmsg = "this is a message that curl won't print"
+ ctx, cancel := context.WithCancel(context.Background())
+ s := &http.Server{
+ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ t.Logf("http connection from %s", r.RemoteAddr)
+ cancel()
+ fmt.Fprintln(w, sendmsg)
+ }),
+ }
+ ln, err := net.Listen("tcp", net.JoinHostPort("::", "0"))
+ if err != nil {
+ t.Fatalf("can't make HTTP server: %v", err)
+ }
+ _, port, _ := net.SplitHostPort(ln.Addr().String())
+ go s.Serve(ln)
+
+ // sess := getSession(t, cli)
+ // sess.Stderr = logger.FuncWriter(t.Logf)
+ // sess.Stdout = logger.FuncWriter(t.Logf)
+ // sess.Run("ip route show table all")
+
+ // sess = getSession(t, cli)
+ // sess.Stderr = logger.FuncWriter(t.Logf)
+ // sess.Stdout = logger.FuncWriter(t.Logf)
+ // sess.Run("sysctl -a")
+
+ var outp []byte
+ retry(t, func() error {
+ var err error
+ sess := getSession(t, cli)
+ v6Arg := ""
+ if ipAddr.Is6() {
+ v6Arg = "-6 -g"
+ }
+ cmd := fmt.Sprintf("curl -v %s -s -f http://%s\n", v6Arg, net.JoinHostPort(ipAddr.String(), port))
+ t.Logf("running: %s", cmd)
+ outp, err = sess.CombinedOutput(cmd)
+ if err != nil {
+ t.Log(string(outp))
+ }
+ return err
+ })
+
+ if msg := string(bytes.TrimSpace(outp)); !strings.Contains(msg, sendmsg) {
+ t.Fatalf("wanted %q, got: %q", sendmsg, msg)
+ }
+ <-ctx.Done()
+}