mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Expand tsic to offer PingViaDerp
This commit is contained in:
		
							parent
							
								
									a5afe4bd06
								
							
						
					
					
						commit
						bb07aec82c
					
				@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"net/netip"
 | 
						"net/netip"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/juanfont/headscale/integration/dockertestutil"
 | 
				
			||||||
	"github.com/juanfont/headscale/integration/tsic"
 | 
						"github.com/juanfont/headscale/integration/tsic"
 | 
				
			||||||
	"tailscale.com/ipn/ipnstate"
 | 
						"tailscale.com/ipn/ipnstate"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -13,7 +14,7 @@ type TailscaleClient interface {
 | 
				
			|||||||
	Hostname() string
 | 
						Hostname() string
 | 
				
			||||||
	Shutdown() error
 | 
						Shutdown() error
 | 
				
			||||||
	Version() string
 | 
						Version() string
 | 
				
			||||||
	Execute(command []string) (string, string, error)
 | 
						Execute(command []string, options ...dockertestutil.ExecuteCommandOption) (string, string, error)
 | 
				
			||||||
	Up(loginServer, authKey string) error
 | 
						Up(loginServer, authKey string) error
 | 
				
			||||||
	UpWithLoginURL(loginServer string) (*url.URL, error)
 | 
						UpWithLoginURL(loginServer string) (*url.URL, error)
 | 
				
			||||||
	Logout() error
 | 
						Logout() error
 | 
				
			||||||
@ -24,6 +25,7 @@ type TailscaleClient interface {
 | 
				
			|||||||
	WaitForLogout() error
 | 
						WaitForLogout() error
 | 
				
			||||||
	WaitForPeers(expected int) error
 | 
						WaitForPeers(expected int) error
 | 
				
			||||||
	Ping(hostnameOrIP string, opts ...tsic.PingOption) error
 | 
						Ping(hostnameOrIP string, opts ...tsic.PingOption) error
 | 
				
			||||||
 | 
						PingViaDERP(hostnameOrIP string, opts ...tsic.PingOption) error
 | 
				
			||||||
	Curl(url string, opts ...tsic.CurlOption) (string, error)
 | 
						Curl(url string, opts ...tsic.CurlOption) (string, error)
 | 
				
			||||||
	ID() string
 | 
						ID() string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -29,6 +29,7 @@ const (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	errTailscalePingFailed             = errors.New("ping failed")
 | 
						errTailscalePingFailed             = errors.New("ping failed")
 | 
				
			||||||
 | 
						errTailscalePingNotDERP            = errors.New("ping not via DERP")
 | 
				
			||||||
	errTailscaleNotLoggedIn            = errors.New("tailscale not logged in")
 | 
						errTailscaleNotLoggedIn            = errors.New("tailscale not logged in")
 | 
				
			||||||
	errTailscaleWrongPeerCount         = errors.New("wrong peer count")
 | 
						errTailscaleWrongPeerCount         = errors.New("wrong peer count")
 | 
				
			||||||
	errTailscaleCannotUpWithoutAuthkey = errors.New("cannot up without authkey")
 | 
						errTailscaleCannotUpWithoutAuthkey = errors.New("cannot up without authkey")
 | 
				
			||||||
@ -56,6 +57,7 @@ type TailscaleInContainer struct {
 | 
				
			|||||||
	withSSH           bool
 | 
						withSSH           bool
 | 
				
			||||||
	withTags          []string
 | 
						withTags          []string
 | 
				
			||||||
	withEntrypoint    []string
 | 
						withEntrypoint    []string
 | 
				
			||||||
 | 
						withExtraHosts    []string
 | 
				
			||||||
	workdir           string
 | 
						workdir           string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -124,6 +126,12 @@ func WithDockerWorkdir(dir string) Option {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func WithExtraHosts(hosts []string) Option {
 | 
				
			||||||
 | 
						return func(tsic *TailscaleInContainer) {
 | 
				
			||||||
 | 
							tsic.withExtraHosts = hosts
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithDockerEntrypoint allows the docker entrypoint of the container
 | 
					// WithDockerEntrypoint allows the docker entrypoint of the container
 | 
				
			||||||
// to be overridden. This is a dangerous option which can make
 | 
					// to be overridden. This is a dangerous option which can make
 | 
				
			||||||
// the container not work as intended as a typo might prevent
 | 
					// the container not work as intended as a typo might prevent
 | 
				
			||||||
@ -169,11 +177,12 @@ func New(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	tailscaleOptions := &dockertest.RunOptions{
 | 
						tailscaleOptions := &dockertest.RunOptions{
 | 
				
			||||||
		Name:     hostname,
 | 
							Name:     hostname,
 | 
				
			||||||
		Networks: []*dockertest.Network{network},
 | 
							Networks: []*dockertest.Network{tsic.network},
 | 
				
			||||||
		// Cmd: []string{
 | 
							// Cmd: []string{
 | 
				
			||||||
		// 	"tailscaled", "--tun=tsdev",
 | 
							// 	"tailscaled", "--tun=tsdev",
 | 
				
			||||||
		// },
 | 
							// },
 | 
				
			||||||
		Entrypoint: tsic.withEntrypoint,
 | 
							Entrypoint: tsic.withEntrypoint,
 | 
				
			||||||
 | 
							ExtraHosts: tsic.withExtraHosts,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if tsic.headscaleHostname != "" {
 | 
						if tsic.headscaleHostname != "" {
 | 
				
			||||||
@ -248,11 +257,13 @@ func (t *TailscaleInContainer) ID() string {
 | 
				
			|||||||
// result of stdout as a string.
 | 
					// result of stdout as a string.
 | 
				
			||||||
func (t *TailscaleInContainer) Execute(
 | 
					func (t *TailscaleInContainer) Execute(
 | 
				
			||||||
	command []string,
 | 
						command []string,
 | 
				
			||||||
 | 
						options ...dockertestutil.ExecuteCommandOption,
 | 
				
			||||||
) (string, string, error) {
 | 
					) (string, string, error) {
 | 
				
			||||||
	stdout, stderr, err := dockertestutil.ExecuteCommand(
 | 
						stdout, stderr, err := dockertestutil.ExecuteCommand(
 | 
				
			||||||
		t.container,
 | 
							t.container,
 | 
				
			||||||
		command,
 | 
							command,
 | 
				
			||||||
		[]string{},
 | 
							[]string{},
 | 
				
			||||||
 | 
							options...,
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Printf("command stderr: %s\n", stderr)
 | 
							log.Printf("command stderr: %s\n", stderr)
 | 
				
			||||||
@ -477,7 +488,7 @@ func (t *TailscaleInContainer) WaitForPeers(expected int) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type (
 | 
					type (
 | 
				
			||||||
	// PingOption repreent optional settings that can be given
 | 
						// PingOption represent optional settings that can be given
 | 
				
			||||||
	// to ping another host.
 | 
						// to ping another host.
 | 
				
			||||||
	PingOption = func(args *pingArgs)
 | 
						PingOption = func(args *pingArgs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -488,6 +499,15 @@ type (
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type (
 | 
				
			||||||
 | 
						DERPPingOption = func(args *derpPingArgs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						derpPingArgs struct {
 | 
				
			||||||
 | 
							timeout time.Duration
 | 
				
			||||||
 | 
							count   int
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithPingTimeout sets the timeout for the ping command.
 | 
					// WithPingTimeout sets the timeout for the ping command.
 | 
				
			||||||
func WithPingTimeout(timeout time.Duration) PingOption {
 | 
					func WithPingTimeout(timeout time.Duration) PingOption {
 | 
				
			||||||
	return func(args *pingArgs) {
 | 
						return func(args *pingArgs) {
 | 
				
			||||||
@ -555,6 +575,62 @@ func (t *TailscaleInContainer) Ping(hostnameOrIP string, opts ...PingOption) err
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PingViaDERP executes the Tailscale ping command and pings a hostname
 | 
				
			||||||
 | 
					// or IP via the DERP network (i.e., not a direct connection). It accepts a series of DERPPingOption.
 | 
				
			||||||
 | 
					// TODO(kradalby): Make multiping, go routine magic.
 | 
				
			||||||
 | 
					func (t *TailscaleInContainer) PingViaDERP(hostnameOrIP string, opts ...PingOption) error {
 | 
				
			||||||
 | 
						args := pingArgs{
 | 
				
			||||||
 | 
							timeout: time.Second,
 | 
				
			||||||
 | 
							count:   defaultPingCount,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, opt := range opts {
 | 
				
			||||||
 | 
							opt(&args)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						command := []string{
 | 
				
			||||||
 | 
							"tailscale", "ping",
 | 
				
			||||||
 | 
							fmt.Sprintf("--timeout=%s", args.timeout),
 | 
				
			||||||
 | 
							fmt.Sprintf("--c=%d", args.count),
 | 
				
			||||||
 | 
							"--until-direct=false",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						command = append(command, hostnameOrIP)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return t.pool.Retry(func() error {
 | 
				
			||||||
 | 
							result, _, err := t.Execute(
 | 
				
			||||||
 | 
								command,
 | 
				
			||||||
 | 
								dockertestutil.ExecuteCommandTimeout(
 | 
				
			||||||
 | 
									time.Duration(int64(args.timeout)*int64(args.count)),
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								fmt.Printf(
 | 
				
			||||||
 | 
									"failed to run ping command from %s to %s, err: %s",
 | 
				
			||||||
 | 
									t.Hostname(),
 | 
				
			||||||
 | 
									hostnameOrIP,
 | 
				
			||||||
 | 
									err,
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if strings.Contains(result, "is local") {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !strings.Contains(result, "pong") {
 | 
				
			||||||
 | 
								return backoff.Permanent(errTailscalePingFailed)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !strings.Contains(result, "via DERP") {
 | 
				
			||||||
 | 
								return backoff.Permanent(errTailscalePingNotDERP)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type (
 | 
					type (
 | 
				
			||||||
	// CurlOption repreent optional settings that can be given
 | 
						// CurlOption repreent optional settings that can be given
 | 
				
			||||||
	// to curl another host.
 | 
						// to curl another host.
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,9 @@ package integration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/juanfont/headscale/integration/tsic"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func pingAllHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
 | 
					func pingAllHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
 | 
				
			||||||
@ -22,6 +25,51 @@ func pingAllHelper(t *testing.T, clients []TailscaleClient, addrs []string) int
 | 
				
			|||||||
	return success
 | 
						return success
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func pingDerpAllHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
 | 
				
			||||||
 | 
						t.Helper()
 | 
				
			||||||
 | 
						success := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, client := range clients {
 | 
				
			||||||
 | 
							for _, addr := range addrs {
 | 
				
			||||||
 | 
								if isSelfClient(client, addr) {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err := client.PingViaDERP(
 | 
				
			||||||
 | 
									addr,
 | 
				
			||||||
 | 
									tsic.WithPingTimeout(2*time.Second),
 | 
				
			||||||
 | 
									tsic.WithPingCount(10),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									t.Errorf("failed to ping %s from %s: %s", addr, client.Hostname(), err)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									success++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return success
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isSelfClient(client TailscaleClient, addr string) bool {
 | 
				
			||||||
 | 
						if addr == client.Hostname() {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ips, err := client.IPs()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, ip := range ips {
 | 
				
			||||||
 | 
							if ip.String() == addr {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// pingAllNegativeHelper is intended to have 1 or more nodes timeing out from the ping,
 | 
					// pingAllNegativeHelper is intended to have 1 or more nodes timeing out from the ping,
 | 
				
			||||||
// it counts failures instead of successes.
 | 
					// it counts failures instead of successes.
 | 
				
			||||||
// func pingAllNegativeHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
 | 
					// func pingAllNegativeHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user