mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-31 13:07:46 +09:00 
			
		
		
		
	Add tailscale versions, waiters and helpers for scenario
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
		| @@ -7,6 +7,7 @@ import ( | |||||||
| 	"net/netip" | 	"net/netip" | ||||||
| 	"os" | 	"os" | ||||||
| 	"sync" | 	"sync" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/juanfont/headscale" | 	"github.com/juanfont/headscale" | ||||||
| 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1" | 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1" | ||||||
| @@ -20,12 +21,32 @@ const scenarioHashLength = 6 | |||||||
|  |  | ||||||
| var errNoHeadscaleAvailable = errors.New("no headscale available") | var errNoHeadscaleAvailable = errors.New("no headscale available") | ||||||
| var errNoNamespaceAvailable = errors.New("no namespace available") | var errNoNamespaceAvailable = errors.New("no namespace available") | ||||||
|  | var TailscaleVersions = []string{ | ||||||
|  | 	"head", | ||||||
|  | 	"unstable", | ||||||
|  | 	"1.32.0", | ||||||
|  | 	"1.30.2", | ||||||
|  | 	"1.28.0", | ||||||
|  | 	"1.26.2", | ||||||
|  | 	"1.24.2", | ||||||
|  | 	"1.22.2", | ||||||
|  | 	"1.20.4", | ||||||
|  | 	"1.18.2", | ||||||
|  | 	"1.16.2", | ||||||
|  |  | ||||||
|  | 	// These versions seem to fail when fetching from apt. | ||||||
|  | 	// "1.14.6", | ||||||
|  | 	// "1.12.4", | ||||||
|  | 	// "1.10.2", | ||||||
|  | 	// "1.8.7", | ||||||
|  | } | ||||||
|  |  | ||||||
| type Namespace struct { | type Namespace struct { | ||||||
| 	Clients map[string]*tsic.TailscaleInContainer | 	Clients map[string]*tsic.TailscaleInContainer | ||||||
|  |  | ||||||
| 	createWaitGroup sync.WaitGroup | 	createWaitGroup sync.WaitGroup | ||||||
| 	joinWaitGroup   sync.WaitGroup | 	joinWaitGroup   sync.WaitGroup | ||||||
|  | 	syncWaitGroup   sync.WaitGroup | ||||||
| } | } | ||||||
|  |  | ||||||
| // TODO(kradalby): make control server configurable, test test correctness with | // TODO(kradalby): make control server configurable, test test correctness with | ||||||
| @@ -52,6 +73,8 @@ func NewScenario() (*Scenario, error) { | |||||||
| 		return nil, fmt.Errorf("could not connect to docker: %w", err) | 		return nil, fmt.Errorf("could not connect to docker: %w", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	pool.MaxWait = 60 * time.Second | ||||||
|  |  | ||||||
| 	networkName := fmt.Sprintf("hs-%s", hash) | 	networkName := fmt.Sprintf("hs-%s", hash) | ||||||
| 	if overrideNetworkName := os.Getenv("HEADSCALE_TEST_NETWORK_NAME"); overrideNetworkName != "" { | 	if overrideNetworkName := os.Getenv("HEADSCALE_TEST_NETWORK_NAME"); overrideNetworkName != "" { | ||||||
| 		networkName = overrideNetworkName | 		networkName = overrideNetworkName | ||||||
| @@ -162,11 +185,16 @@ func (s *Scenario) CreateNamespace(namespace string) error { | |||||||
|  |  | ||||||
| func (s *Scenario) CreateTailscaleNodesInNamespace( | func (s *Scenario) CreateTailscaleNodesInNamespace( | ||||||
| 	namespace string, | 	namespace string, | ||||||
| 	version string, | 	requestedVersion string, | ||||||
| 	count int, | 	count int, | ||||||
| ) error { | ) error { | ||||||
| 	if ns, ok := s.namespaces[namespace]; ok { | 	if ns, ok := s.namespaces[namespace]; ok { | ||||||
| 		for i := 0; i < count; i++ { | 		for i := 0; i < count; i++ { | ||||||
|  | 			version := requestedVersion | ||||||
|  | 			if requestedVersion == "all" { | ||||||
|  | 				version = TailscaleVersions[i%len(TailscaleVersions)] | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			ns.createWaitGroup.Add(1) | 			ns.createWaitGroup.Add(1) | ||||||
|  |  | ||||||
| 			go func() { | 			go func() { | ||||||
| @@ -197,12 +225,12 @@ func (s *Scenario) RunTailscaleUp( | |||||||
| 		for _, client := range ns.Clients { | 		for _, client := range ns.Clients { | ||||||
| 			ns.joinWaitGroup.Add(1) | 			ns.joinWaitGroup.Add(1) | ||||||
|  |  | ||||||
| 			go func() { | 			go func(c *tsic.TailscaleInContainer) { | ||||||
| 				defer ns.joinWaitGroup.Done() | 				defer ns.joinWaitGroup.Done() | ||||||
|  |  | ||||||
| 				// TODO(kradalby): error handle this | 				// TODO(kradalby): error handle this | ||||||
| 				_ = client.Up(loginServer, authKey) | 				_ = c.Up(loginServer, authKey) | ||||||
| 			}() | 			}(client) | ||||||
| 		} | 		} | ||||||
| 		ns.joinWaitGroup.Wait() | 		ns.joinWaitGroup.Wait() | ||||||
|  |  | ||||||
| @@ -212,6 +240,75 @@ func (s *Scenario) RunTailscaleUp( | |||||||
| 	return fmt.Errorf("failed to up tailscale node: %w", errNoNamespaceAvailable) | 	return fmt.Errorf("failed to up tailscale node: %w", errNoNamespaceAvailable) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (s *Scenario) CountTailscale() int { | ||||||
|  | 	count := 0 | ||||||
|  |  | ||||||
|  | 	for _, ns := range s.namespaces { | ||||||
|  | 		count += len(ns.Clients) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return count | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Scenario) WaitForTailscaleSync() error { | ||||||
|  | 	tsCount := s.CountTailscale() | ||||||
|  |  | ||||||
|  | 	for _, ns := range s.namespaces { | ||||||
|  | 		for _, client := range ns.Clients { | ||||||
|  | 			ns.syncWaitGroup.Add(1) | ||||||
|  |  | ||||||
|  | 			go func(c *tsic.TailscaleInContainer) { | ||||||
|  | 				defer ns.syncWaitGroup.Done() | ||||||
|  |  | ||||||
|  | 				// TODO(kradalby): error handle this | ||||||
|  | 				_ = c.WaitForPeers(tsCount) | ||||||
|  | 			}(client) | ||||||
|  | 		} | ||||||
|  | 		ns.syncWaitGroup.Wait() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CreateHeadscaleEnv is a conventient method returning a set up Headcale | ||||||
|  | // test environment with nodes of all versions, joined to the server with X | ||||||
|  | // namespaces | ||||||
|  | func (s *Scenario) CreateHeadscaleEnv(namespaces map[string]int) error { | ||||||
|  | 	err := s.StartHeadscale() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	err = s.Headscale().WaitForReady() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for namespaceName, clientCount := range namespaces { | ||||||
|  | 		err = s.CreateNamespace(namespaceName) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err = s.CreateTailscaleNodesInNamespace(namespaceName, "all", clientCount) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		key, err := s.CreatePreAuthKey(namespaceName) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err = s.RunTailscaleUp(namespaceName, s.Headscale().GetEndpoint(), key.GetKey()) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func (s *Scenario) GetIPs(namespace string) ([]netip.Addr, error) { | func (s *Scenario) GetIPs(namespace string) ([]netip.Addr, error) { | ||||||
| 	var ips []netip.Addr | 	var ips []netip.Addr | ||||||
| 	if ns, ok := s.namespaces[namespace]; ok { | 	if ns, ok := s.namespaces[namespace]; ok { | ||||||
| @@ -228,3 +325,16 @@ func (s *Scenario) GetIPs(namespace string) ([]netip.Addr, error) { | |||||||
|  |  | ||||||
| 	return ips, fmt.Errorf("failed to get ips: %w", errNoNamespaceAvailable) | 	return ips, fmt.Errorf("failed to get ips: %w", errNoNamespaceAvailable) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (s *Scenario) GetClients(namespace string) ([]*tsic.TailscaleInContainer, error) { | ||||||
|  | 	var clients []*tsic.TailscaleInContainer | ||||||
|  | 	if ns, ok := s.namespaces[namespace]; ok { | ||||||
|  | 		for _, client := range ns.Clients { | ||||||
|  | 			clients = append(clients, client) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return clients, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return clients, fmt.Errorf("failed to get clients: %w", errNoNamespaceAvailable) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -74,12 +74,9 @@ func TestHeadscale(t *testing.T) { | |||||||
| func TestCreateTailscale(t *testing.T) { | func TestCreateTailscale(t *testing.T) { | ||||||
| 	IntegrationSkip(t) | 	IntegrationSkip(t) | ||||||
|  |  | ||||||
| 	var scenario *Scenario |  | ||||||
| 	var err error |  | ||||||
|  |  | ||||||
| 	namespace := "only-create-containers" | 	namespace := "only-create-containers" | ||||||
|  |  | ||||||
| 	scenario, err = NewScenario() | 	scenario, err := NewScenario() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("failed to create scenario: %s", err) | 		t.Errorf("failed to create scenario: %s", err) | ||||||
| 	} | 	} | ||||||
| @@ -89,7 +86,7 @@ func TestCreateTailscale(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	t.Run("create-tailscale", func(t *testing.T) { | 	t.Run("create-tailscale", func(t *testing.T) { | ||||||
| 		err := scenario.CreateTailscaleNodesInNamespace(namespace, "1.32.0", 3) | 		err := scenario.CreateTailscaleNodesInNamespace(namespace, "all", 3) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to add tailscale nodes: %s", err) | 			t.Errorf("failed to add tailscale nodes: %s", err) | ||||||
| 		} | 		} | ||||||
| @@ -97,6 +94,8 @@ func TestCreateTailscale(t *testing.T) { | |||||||
| 		if clients := len(scenario.namespaces[namespace].Clients); clients != 3 { | 		if clients := len(scenario.namespaces[namespace].Clients); clients != 3 { | ||||||
| 			t.Errorf("wrong number of tailscale clients: %d != %d", clients, 3) | 			t.Errorf("wrong number of tailscale clients: %d != %d", clients, 3) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		// TODO(kradalby): Test "all" version logic | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	err = scenario.Shutdown() | 	err = scenario.Shutdown() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user