mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Added DERP integration tests
Linting fixes Set listen addr to :8443
This commit is contained in:
		
							parent
							
								
									b8bf0a3b9f
								
							
						
					
					
						commit
						adecb7b0ea
					
				
							
								
								
									
										247
									
								
								integration/embedded_derp_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								integration/embedded_derp_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,247 @@ | ||||
| package integration | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"net/url" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/juanfont/headscale" | ||||
| 	"github.com/juanfont/headscale/integration/dockertestutil" | ||||
| 	"github.com/juanfont/headscale/integration/hsic" | ||||
| 	"github.com/juanfont/headscale/integration/tsic" | ||||
| 	"github.com/ory/dockertest/v3" | ||||
| ) | ||||
| 
 | ||||
| type EmbeddedDERPServerScenario struct { | ||||
| 	*Scenario | ||||
| 
 | ||||
| 	tsicNetworks map[string]*dockertest.Network | ||||
| } | ||||
| 
 | ||||
| func TestDERPServerScenario(t *testing.T) { | ||||
| 	IntegrationSkip(t) | ||||
| 	// t.Parallel()
 | ||||
| 
 | ||||
| 	baseScenario, err := NewScenario() | ||||
| 	if err != nil { | ||||
| 		t.Errorf("failed to create scenario: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	scenario := EmbeddedDERPServerScenario{ | ||||
| 		Scenario:     baseScenario, | ||||
| 		tsicNetworks: map[string]*dockertest.Network{}, | ||||
| 	} | ||||
| 
 | ||||
| 	spec := map[string]int{ | ||||
| 		"user1": len(TailscaleVersions), | ||||
| 	} | ||||
| 
 | ||||
| 	headscaleConfig := hsic.DefaultConfigEnv() | ||||
| 	headscaleConfig["HEADSCALE_LISTEN_ADDR"] = "0.0.0.0:8443" | ||||
| 	headscaleConfig["HEADSCALE_DERP_URLS"] = "" | ||||
| 	headscaleConfig["HEADSCALE_DERP_SERVER_ENABLED"] = "true" | ||||
| 	headscaleConfig["HEADSCALE_DERP_SERVER_REGION_ID"] = "999" | ||||
| 	headscaleConfig["HEADSCALE_DERP_SERVER_REGION_CODE"] = "headscale" | ||||
| 	headscaleConfig["HEADSCALE_DERP_SERVER_REGION_NAME"] = "Headscale Embedded DERP" | ||||
| 	headscaleConfig["HEADSCALE_DERP_SERVER_STUN_LISTEN_ADDR"] = "0.0.0.0:3478" | ||||
| 
 | ||||
| 	err = scenario.CreateHeadscaleEnv( | ||||
| 		spec, | ||||
| 		hsic.WithConfigEnv(headscaleConfig), | ||||
| 		hsic.WithPort(8443), | ||||
| 		hsic.WithTestName("derpserver"), | ||||
| 		hsic.WithHostPortBindings( | ||||
| 			map[string][]string{ | ||||
| 				"8443/tcp": {"8443"}, | ||||
| 				"3478/udp": {"3478"}, | ||||
| 			}, | ||||
| 		), | ||||
| 		hsic.WithExtraPorts([]string{"3478/udp"}), | ||||
| 		hsic.WithTLS(), | ||||
| 		hsic.WithHostnameAsServerURL(), | ||||
| 	) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		t.Errorf("failed to create headscale environment: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	allClients, err := scenario.ListTailscaleClients() | ||||
| 	if err != nil { | ||||
| 		t.Errorf("failed to get clients: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	allIps, err := scenario.ListTailscaleClientsIPs() | ||||
| 	if err != nil { | ||||
| 		t.Errorf("failed to get clients: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = scenario.WaitForTailscaleSync() | ||||
| 	if err != nil { | ||||
| 		t.Errorf("failed wait for tailscale clients to be in sync: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	allHostnames, err := scenario.ListTailscaleClientsFQDNs() | ||||
| 	if err != nil { | ||||
| 		t.Errorf("failed to get FQDNs: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	success := pingDerpAllHelper(t, allClients, allHostnames) | ||||
| 
 | ||||
| 	t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps)) | ||||
| 
 | ||||
| 	err = scenario.Shutdown() | ||||
| 	if err != nil { | ||||
| 		t.Errorf("failed to tear down scenario: %s", err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *EmbeddedDERPServerScenario) CreateHeadscaleEnv( | ||||
| 	users map[string]int, | ||||
| 	opts ...hsic.Option, | ||||
| ) error { | ||||
| 	hsServer, err := s.Headscale(opts...) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	headscaleEndpoint := hsServer.GetEndpoint() | ||||
| 	headscaleURL, err := url.Parse(headscaleEndpoint) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	headscaleURL.Host = fmt.Sprintf("%s:%s", hsServer.GetHostname(), headscaleURL.Port()) | ||||
| 
 | ||||
| 	extraHosts := []string{ | ||||
| 		"host.docker.internal:host-gateway", | ||||
| 		fmt.Sprintf("%s:host-gateway", hsServer.GetHostname()), | ||||
| 	} | ||||
| 
 | ||||
| 	err = hsServer.WaitForReady() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	hash, err := headscale.GenerateRandomStringDNSSafe(scenarioHashLength) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	for userName, clientCount := range users { | ||||
| 		err = s.CreateUser(userName) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		err = s.CreateTailscaleIsolatedNodesInUser( | ||||
| 			hash, | ||||
| 			userName, | ||||
| 			"all", | ||||
| 			clientCount, | ||||
| 			tsic.WithExtraHosts(extraHosts), | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		key, err := s.CreatePreAuthKey(userName, true, false) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		err = s.RunTailscaleUp(userName, headscaleURL.String(), key.GetKey()) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (s *EmbeddedDERPServerScenario) CreateTailscaleIsolatedNodesInUser( | ||||
| 	hash string, | ||||
| 	userStr string, | ||||
| 	requestedVersion string, | ||||
| 	count int, | ||||
| 	opts ...tsic.Option, | ||||
| ) error { | ||||
| 	if user, ok := s.users[userStr]; ok { | ||||
| 		for clientN := 0; clientN < count; clientN++ { | ||||
| 			networkName := fmt.Sprintf("tsnet-%s-%s-%d", | ||||
| 				hash, | ||||
| 				userStr, | ||||
| 				clientN, | ||||
| 			) | ||||
| 			network, err := dockertestutil.GetFirstOrCreateNetwork( | ||||
| 				s.pool, | ||||
| 				networkName, | ||||
| 			) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("failed to create or get %s network: %w", networkName, err) | ||||
| 			} | ||||
| 
 | ||||
| 			s.tsicNetworks[networkName] = network | ||||
| 
 | ||||
| 			version := requestedVersion | ||||
| 			if requestedVersion == "all" { | ||||
| 				version = TailscaleVersions[clientN%len(TailscaleVersions)] | ||||
| 			} | ||||
| 
 | ||||
| 			headscale, err := s.Headscale() | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("failed to create tailscale node: %w", err) | ||||
| 			} | ||||
| 
 | ||||
| 			cert := headscale.GetCert() | ||||
| 			hostname := headscale.GetHostname() | ||||
| 
 | ||||
| 			user.createWaitGroup.Add(1) | ||||
| 
 | ||||
| 			opts = append(opts, | ||||
| 				tsic.WithHeadscaleTLS(cert), | ||||
| 				tsic.WithHeadscaleName(hostname), | ||||
| 			) | ||||
| 
 | ||||
| 			go func() { | ||||
| 				defer user.createWaitGroup.Done() | ||||
| 
 | ||||
| 				// TODO(kradalby): error handle this
 | ||||
| 				tsClient, err := tsic.New( | ||||
| 					s.pool, | ||||
| 					version, | ||||
| 					network, | ||||
| 					opts..., | ||||
| 				) | ||||
| 				if err != nil { | ||||
| 					// return fmt.Errorf("failed to add tailscale node: %w", err)
 | ||||
| 					log.Printf("failed to create tailscale node: %s", err) | ||||
| 				} | ||||
| 
 | ||||
| 				err = tsClient.WaitForReady() | ||||
| 				if err != nil { | ||||
| 					// return fmt.Errorf("failed to add tailscale node: %w", err)
 | ||||
| 					log.Printf("failed to wait for tailscaled: %s", err) | ||||
| 				} | ||||
| 
 | ||||
| 				user.Clients[tsClient.Hostname()] = tsClient | ||||
| 			}() | ||||
| 		} | ||||
| 		user.createWaitGroup.Wait() | ||||
| 
 | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	return fmt.Errorf("failed to add tailscale node: %w", errNoUserAvailable) | ||||
| } | ||||
| 
 | ||||
| func (s *EmbeddedDERPServerScenario) Shutdown() error { | ||||
| 	for _, network := range s.tsicNetworks { | ||||
| 		err := s.pool.RemoveNetwork(network) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return s.Scenario.Shutdown() | ||||
| } | ||||
| @ -112,7 +112,7 @@ func WithPort(port int) Option { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithExtraPorts exposes additional ports on the container (e.g. 3478/udp for STUN)
 | ||||
| // WithExtraPorts exposes additional ports on the container (e.g. 3478/udp for STUN).
 | ||||
| func WithExtraPorts(ports []string) Option { | ||||
| 	return func(hsic *HeadscaleInContainer) { | ||||
| 		hsic.extraPorts = ports | ||||
| @ -190,14 +190,14 @@ func New( | ||||
| 
 | ||||
| 	portProto := fmt.Sprintf("%d/tcp", hsic.port) | ||||
| 
 | ||||
| 	serverUrl, err := url.Parse(hsic.env["HEADSCALE_SERVER_URL"]) | ||||
| 	serverURL, err := url.Parse(hsic.env["HEADSCALE_SERVER_URL"]) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if len(hsic.tlsCert) != 0 && len(hsic.tlsKey) != 0 { | ||||
| 		serverUrl.Scheme = "https" | ||||
| 		hsic.env["HEADSCALE_SERVER_URL"] = serverUrl.String() | ||||
| 		serverURL.Scheme = "https" | ||||
| 		hsic.env["HEADSCALE_SERVER_URL"] = serverURL.String() | ||||
| 	} | ||||
| 
 | ||||
| 	headscaleBuildOptions := &dockertest.BuildOptions{ | ||||
|  | ||||
| @ -499,15 +499,6 @@ 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) { | ||||
| @ -605,7 +596,7 @@ func (t *TailscaleInContainer) PingViaDERP(hostnameOrIP string, opts ...PingOpti | ||||
| 			), | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			fmt.Printf( | ||||
| 			log.Printf( | ||||
| 				"failed to run ping command from %s to %s, err: %s", | ||||
| 				t.Hostname(), | ||||
| 				hostnameOrIP, | ||||
|  | ||||
| @ -7,6 +7,11 @@ import ( | ||||
| 	"github.com/juanfont/headscale/integration/tsic" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	derpPingTimeout = 2 * time.Second | ||||
| 	derpPingCount   = 10 | ||||
| ) | ||||
| 
 | ||||
| func pingAllHelper(t *testing.T, clients []TailscaleClient, addrs []string) int { | ||||
| 	t.Helper() | ||||
| 	success := 0 | ||||
| @ -37,8 +42,8 @@ func pingDerpAllHelper(t *testing.T, clients []TailscaleClient, addrs []string) | ||||
| 
 | ||||
| 			err := client.PingViaDERP( | ||||
| 				addr, | ||||
| 				tsic.WithPingTimeout(2*time.Second), | ||||
| 				tsic.WithPingCount(10), | ||||
| 				tsic.WithPingTimeout(derpPingTimeout), | ||||
| 				tsic.WithPingCount(derpPingCount), | ||||
| 			) | ||||
| 			if err != nil { | ||||
| 				t.Errorf("failed to ping %s from %s: %s", addr, client.Hostname(), err) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user