mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			235 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package integration
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"log"
 | 
						|
	"net/url"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/juanfont/headscale/hscontrol/util"
 | 
						|
	"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()
 | 
						|
	assertNoErr(t, err)
 | 
						|
 | 
						|
	scenario := EmbeddedDERPServerScenario{
 | 
						|
		Scenario:     baseScenario,
 | 
						|
		tsicNetworks: map[string]*dockertest.Network{},
 | 
						|
	}
 | 
						|
	defer scenario.Shutdown()
 | 
						|
 | 
						|
	spec := map[string]int{
 | 
						|
		"user1": 10,
 | 
						|
		// "user1": len(MustTestVersions),
 | 
						|
	}
 | 
						|
 | 
						|
	headscaleConfig := map[string]string{
 | 
						|
		"HEADSCALE_DERP_URLS":                    "",
 | 
						|
		"HEADSCALE_DERP_SERVER_ENABLED":          "true",
 | 
						|
		"HEADSCALE_DERP_SERVER_REGION_ID":        "999",
 | 
						|
		"HEADSCALE_DERP_SERVER_REGION_CODE":      "headscale",
 | 
						|
		"HEADSCALE_DERP_SERVER_REGION_NAME":      "Headscale Embedded DERP",
 | 
						|
		"HEADSCALE_DERP_SERVER_STUN_LISTEN_ADDR": "0.0.0.0:3478",
 | 
						|
		"HEADSCALE_DERP_SERVER_PRIVATE_KEY_PATH": "/tmp/derp.key",
 | 
						|
 | 
						|
		// Envknob for enabling DERP debug logs
 | 
						|
		"DERP_DEBUG_LOGS":        "true",
 | 
						|
		"DERP_PROBER_DEBUG_LOGS": "true",
 | 
						|
	}
 | 
						|
 | 
						|
	err = scenario.CreateHeadscaleEnv(
 | 
						|
		spec,
 | 
						|
		hsic.WithConfigEnv(headscaleConfig),
 | 
						|
		hsic.WithTestName("derpserver"),
 | 
						|
		hsic.WithExtraPorts([]string{"3478/udp"}),
 | 
						|
		hsic.WithTLS(),
 | 
						|
		hsic.WithHostnameAsServerURL(),
 | 
						|
	)
 | 
						|
	assertNoErrHeadscaleEnv(t, err)
 | 
						|
 | 
						|
	allClients, err := scenario.ListTailscaleClients()
 | 
						|
	assertNoErrListClients(t, err)
 | 
						|
 | 
						|
	allIps, err := scenario.ListTailscaleClientsIPs()
 | 
						|
	assertNoErrListClientIPs(t, err)
 | 
						|
 | 
						|
	err = scenario.WaitForTailscaleSync()
 | 
						|
	assertNoErrSync(t, err)
 | 
						|
 | 
						|
	allHostnames, err := scenario.ListTailscaleClientsFQDNs()
 | 
						|
	assertNoErrListFQDN(t, err)
 | 
						|
 | 
						|
	success := pingDerpAllHelper(t, allClients, allHostnames)
 | 
						|
 | 
						|
	t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps))
 | 
						|
}
 | 
						|
 | 
						|
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())
 | 
						|
 | 
						|
	err = hsServer.WaitForRunning()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	hash, err := util.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,
 | 
						|
		)
 | 
						|
		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 {
 | 
						|
	hsServer, err := s.Headscale()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	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
 | 
						|
 | 
						|
			err = hsServer.ConnectToNetwork(network)
 | 
						|
			if err != nil {
 | 
						|
				return fmt.Errorf("failed to connect headscale to %s network: %w", networkName, err)
 | 
						|
			}
 | 
						|
 | 
						|
			version := requestedVersion
 | 
						|
			if requestedVersion == "all" {
 | 
						|
				version = MustTestVersions[clientN%len(MustTestVersions)]
 | 
						|
			}
 | 
						|
 | 
						|
			cert := hsServer.GetCert()
 | 
						|
 | 
						|
			opts = append(opts,
 | 
						|
				tsic.WithHeadscaleTLS(cert),
 | 
						|
			)
 | 
						|
 | 
						|
			user.createWaitGroup.Go(func() error {
 | 
						|
				tsClient, err := tsic.New(
 | 
						|
					s.pool,
 | 
						|
					version,
 | 
						|
					network,
 | 
						|
					opts...,
 | 
						|
				)
 | 
						|
				if err != nil {
 | 
						|
					return fmt.Errorf(
 | 
						|
						"failed to create tailscale (%s) node: %w",
 | 
						|
						tsClient.Hostname(),
 | 
						|
						err,
 | 
						|
					)
 | 
						|
				}
 | 
						|
 | 
						|
				err = tsClient.WaitForNeedsLogin()
 | 
						|
				if err != nil {
 | 
						|
					return fmt.Errorf(
 | 
						|
						"failed to wait for tailscaled (%s) to need login: %w",
 | 
						|
						tsClient.Hostname(),
 | 
						|
						err,
 | 
						|
					)
 | 
						|
				}
 | 
						|
 | 
						|
				s.mu.Lock()
 | 
						|
				user.Clients[tsClient.Hostname()] = tsClient
 | 
						|
				s.mu.Unlock()
 | 
						|
 | 
						|
				return nil
 | 
						|
			})
 | 
						|
		}
 | 
						|
 | 
						|
		if err := user.createWaitGroup.Wait(); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	return fmt.Errorf("failed to add tailscale nodes: %w", errNoUserAvailable)
 | 
						|
}
 | 
						|
 | 
						|
func (s *EmbeddedDERPServerScenario) Shutdown() {
 | 
						|
	for _, network := range s.tsicNetworks {
 | 
						|
		err := s.pool.RemoveNetwork(network)
 | 
						|
		if err != nil {
 | 
						|
			log.Printf("failed to remove DERP network %s", network.Network.Name)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	s.Scenario.Shutdown()
 | 
						|
}
 |