mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 08:11:32 +01:00 
			
		
		
		
	Updates #11058 Change-Id: I35e7ef9b90e83cac04ca93fd964ad00ed5b48430 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			132 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) Tailscale Inc & AUTHORS
 | |
| // SPDX-License-Identifier: BSD-3-Clause
 | |
| 
 | |
| //go:build !windows && !plan9
 | |
| 
 | |
| package vms
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"net/http"
 | |
| 	"net/netip"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"golang.org/x/crypto/ssh"
 | |
| )
 | |
| 
 | |
| func retry(t *testing.T, fn func() error) {
 | |
| 	t.Helper()
 | |
| 	const tries = 3
 | |
| 	var err error
 | |
| 	for i := range tries {
 | |
| 		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 netip.Addr, cli *ssh.Client) {
 | |
| 	retry(t, func() error {
 | |
| 		sess := getSession(t, cli)
 | |
| 		cmd := fmt.Sprintf("tailscale ping --verbose %s", ipAddr)
 | |
| 		outp, err := sess.CombinedOutput(cmd)
 | |
| 		if err == nil && !bytes.Contains(outp, []byte("pong")) {
 | |
| 			err = fmt.Errorf("%s: no pong", cmd)
 | |
| 		}
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("%s : %v, output: %s", cmd, err, outp)
 | |
| 		}
 | |
| 		t.Logf("%s", outp)
 | |
| 		return nil
 | |
| 	})
 | |
| 
 | |
| 	retry(t, func() error {
 | |
| 		sess := getSession(t, cli)
 | |
| 
 | |
| 		// NOTE(Xe): the ping command is inconsistent across distros. Joy.
 | |
| 		cmd := 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", cmd)
 | |
| 		outp, err := sess.CombinedOutput(cmd)
 | |
| 		if err == nil && !bytes.Contains(outp, []byte("bytes")) {
 | |
| 			err = fmt.Errorf("%s: wanted output to contain %q, it did not", cmd, "bytes")
 | |
| 		}
 | |
| 		if err != nil {
 | |
| 			err = fmt.Errorf("%s: %v, output: %s", cmd, err, outp)
 | |
| 		}
 | |
| 		return err
 | |
| 	})
 | |
| }
 | |
| 
 | |
| 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 netip.Addr, 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")
 | |
| 
 | |
| 	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 msg := string(bytes.TrimSpace(outp)); err == nil && !strings.Contains(msg, sendmsg) {
 | |
| 			err = fmt.Errorf("wanted %q, got: %q", sendmsg, msg)
 | |
| 		}
 | |
| 		if err != nil {
 | |
| 			err = fmt.Errorf("%v, output: %s", err, outp)
 | |
| 		}
 | |
| 		return err
 | |
| 	})
 | |
| 
 | |
| 	<-ctx.Done()
 | |
| }
 |