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/url" | ||||
| 
 | ||||
| 	"github.com/juanfont/headscale/integration/dockertestutil" | ||||
| 	"github.com/juanfont/headscale/integration/tsic" | ||||
| 	"tailscale.com/ipn/ipnstate" | ||||
| ) | ||||
| @ -13,7 +14,7 @@ type TailscaleClient interface { | ||||
| 	Hostname() string | ||||
| 	Shutdown() error | ||||
| 	Version() string | ||||
| 	Execute(command []string) (string, string, error) | ||||
| 	Execute(command []string, options ...dockertestutil.ExecuteCommandOption) (string, string, error) | ||||
| 	Up(loginServer, authKey string) error | ||||
| 	UpWithLoginURL(loginServer string) (*url.URL, error) | ||||
| 	Logout() error | ||||
| @ -24,6 +25,7 @@ type TailscaleClient interface { | ||||
| 	WaitForLogout() error | ||||
| 	WaitForPeers(expected int) error | ||||
| 	Ping(hostnameOrIP string, opts ...tsic.PingOption) error | ||||
| 	PingViaDERP(hostnameOrIP string, opts ...tsic.PingOption) error | ||||
| 	Curl(url string, opts ...tsic.CurlOption) (string, error) | ||||
| 	ID() string | ||||
| } | ||||
|  | ||||
| @ -29,6 +29,7 @@ const ( | ||||
| 
 | ||||
| var ( | ||||
| 	errTailscalePingFailed             = errors.New("ping failed") | ||||
| 	errTailscalePingNotDERP            = errors.New("ping not via DERP") | ||||
| 	errTailscaleNotLoggedIn            = errors.New("tailscale not logged in") | ||||
| 	errTailscaleWrongPeerCount         = errors.New("wrong peer count") | ||||
| 	errTailscaleCannotUpWithoutAuthkey = errors.New("cannot up without authkey") | ||||
| @ -56,6 +57,7 @@ type TailscaleInContainer struct { | ||||
| 	withSSH           bool | ||||
| 	withTags          []string | ||||
| 	withEntrypoint    []string | ||||
| 	withExtraHosts    []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
 | ||||
| // to be overridden. This is a dangerous option which can make
 | ||||
| // the container not work as intended as a typo might prevent
 | ||||
| @ -169,11 +177,12 @@ func New( | ||||
| 
 | ||||
| 	tailscaleOptions := &dockertest.RunOptions{ | ||||
| 		Name:     hostname, | ||||
| 		Networks: []*dockertest.Network{network}, | ||||
| 		Networks: []*dockertest.Network{tsic.network}, | ||||
| 		// Cmd: []string{
 | ||||
| 		// 	"tailscaled", "--tun=tsdev",
 | ||||
| 		// },
 | ||||
| 		Entrypoint: tsic.withEntrypoint, | ||||
| 		ExtraHosts: tsic.withExtraHosts, | ||||
| 	} | ||||
| 
 | ||||
| 	if tsic.headscaleHostname != "" { | ||||
| @ -248,11 +257,13 @@ func (t *TailscaleInContainer) ID() string { | ||||
| // result of stdout as a string.
 | ||||
| func (t *TailscaleInContainer) Execute( | ||||
| 	command []string, | ||||
| 	options ...dockertestutil.ExecuteCommandOption, | ||||
| ) (string, string, error) { | ||||
| 	stdout, stderr, err := dockertestutil.ExecuteCommand( | ||||
| 		t.container, | ||||
| 		command, | ||||
| 		[]string{}, | ||||
| 		options..., | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		log.Printf("command stderr: %s\n", stderr) | ||||
| @ -477,7 +488,7 @@ func (t *TailscaleInContainer) WaitForPeers(expected int) error { | ||||
| } | ||||
| 
 | ||||
| type ( | ||||
| 	// PingOption repreent optional settings that can be given
 | ||||
| 	// PingOption represent optional settings that can be given
 | ||||
| 	// to ping another host.
 | ||||
| 	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.
 | ||||
| func WithPingTimeout(timeout time.Duration) PingOption { | ||||
| 	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 ( | ||||
| 	// CurlOption repreent optional settings that can be given
 | ||||
| 	// to curl another host.
 | ||||
|  | ||||
| @ -2,6 +2,9 @@ package integration | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/juanfont/headscale/integration/tsic" | ||||
| ) | ||||
| 
 | ||||
| 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 | ||||
| } | ||||
| 
 | ||||
| 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,
 | ||||
| // it counts failures instead of successes.
 | ||||
| // func pingAllNegativeHelper(t *testing.T, clients []TailscaleClient, addrs []string) int {
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user