From d242ceac46d1fd7a17de8785c22ee59344ff9a6c Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 21 Oct 2022 14:07:46 +0200 Subject: [PATCH 1/8] Make hostname dns safe, allow string in ping command Signed-off-by: Kristoffer Dalby --- integration/general_test.go | 2 +- integration/tailscale.go | 2 +- integration/tsic/tsic.go | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/integration/general_test.go b/integration/general_test.go index 50ce89c4..e4765f71 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -58,7 +58,7 @@ func TestPingAll(t *testing.T) { for _, client := range allClients { for _, ip := range allIps { - err := client.Ping(ip) + err := client.Ping(ip.String()) if err != nil { t.Errorf("failed to ping %s from %s: %s", ip, client.Hostname(), err) } else { diff --git a/integration/tailscale.go b/integration/tailscale.go index b163c290..4d21798f 100644 --- a/integration/tailscale.go +++ b/integration/tailscale.go @@ -14,5 +14,5 @@ type TailscaleClient interface { IPs() ([]netip.Addr, error) Status() (*ipnstate.Status, error) WaitForPeers(expected int) error - Ping(ip netip.Addr) error + Ping(hostnameOrIP string) error } diff --git a/integration/tsic/tsic.go b/integration/tsic/tsic.go index f21dccae..1cdc3711 100644 --- a/integration/tsic/tsic.go +++ b/integration/tsic/tsic.go @@ -46,7 +46,7 @@ func New( return nil, err } - hostname := fmt.Sprintf("ts-%s-%s", version, hash) + hostname := fmt.Sprintf("ts-%s-%s", strings.ReplaceAll(version, ".", "-"), hash) // TODO(kradalby): figure out why we need to "refresh" the network here. // network, err = dockertestutil.GetFirstOrCreateNetwork(pool, network.Network.Name) @@ -220,14 +220,14 @@ func (t *TailscaleInContainer) WaitForPeers(expected int) error { } // TODO(kradalby): Make multiping, go routine magic. -func (t *TailscaleInContainer) Ping(ip netip.Addr) error { +func (t *TailscaleInContainer) Ping(hostnameOrIP string) error { return t.pool.Retry(func() error { command := []string{ "tailscale", "ping", "--timeout=1s", "--c=10", "--until-direct=true", - ip.String(), + hostnameOrIP, } result, _, err := dockertestutil.ExecuteCommand( @@ -238,8 +238,8 @@ func (t *TailscaleInContainer) Ping(ip netip.Addr) error { if err != nil { log.Printf( "failed to run ping command from %s to %s, err: %s", - t.hostname, - ip.String(), + t.Hostname(), + hostnameOrIP, err, ) From fa3d21cbc07489aacad8bdb18ccd6a610b93a7db Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 21 Oct 2022 14:08:04 +0200 Subject: [PATCH 2/8] Rename pingall test to signal ip Signed-off-by: Kristoffer Dalby --- integration/general_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/general_test.go b/integration/general_test.go index e4765f71..9f75ed85 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func TestPingAll(t *testing.T) { +func TestPingAllByIP(t *testing.T) { IntegrationSkip(t) scenario, err := NewScenario() From d8144ee2ed9edef2a8396b4f44314c523ee289fd Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 21 Oct 2022 14:08:14 +0200 Subject: [PATCH 3/8] Add initial pingallbyhostname Signed-off-by: Kristoffer Dalby --- integration/general_test.go | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/integration/general_test.go b/integration/general_test.go index 9f75ed85..ba000b58 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -74,3 +74,58 @@ func TestPingAllByIP(t *testing.T) { // t.Errorf("failed to tear down scenario: %s", err) // } } + +func TestPingAllByHostname(t *testing.T) { + IntegrationSkip(t) + + scenario, err := NewScenario() + if err != nil { + t.Errorf("failed to create scenario: %s", err) + } + + spec := map[string]int{ + "namespace3": len(TailscaleVersions), + "namespace4": len(TailscaleVersions), + } + + err = scenario.CreateHeadscaleEnv(spec) + if err != nil { + t.Errorf("failed to create headscale environment: %s", err) + } + + var allClients []*tsic.TailscaleInContainer + + for namespace := range spec { + clients, err := scenario.GetClients(namespace) + if err != nil { + t.Errorf("failed to get tailscale clients: %s", err) + } + + allClients = append(allClients, clients...) + } + + err = scenario.WaitForTailscaleSync() + if err != nil { + t.Errorf("failed wait for tailscale clients to be in sync: %s", err) + } + + success := 0 + + for _, client := range allClients { + for _, peer := range allClients { + err := client.Ping(peer.Hostname) + if err != nil { + t.Errorf("failed to ping %s from %s: %s", peer.Hostname, client.Hostname, err) + } else { + success++ + } + } + } + + t.Logf("%d successful pings out of %d", success, len(allClients)*len(allClients)) + + err = scenario.Shutdown() + if err != nil { + t.Errorf("failed to tear down scenario: %s", err) + } +} From cbbf9fbdefc2f94bbf6558d10f20a279b78ee2c0 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 21 Oct 2022 17:44:40 +0200 Subject: [PATCH 4/8] Use FQDN from tailscale client Signed-off-by: Kristoffer Dalby --- integration/general_test.go | 24 +++++++++++++++++------- integration/tsic/tsic.go | 9 +++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/integration/general_test.go b/integration/general_test.go index ba000b58..183f6615 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -69,10 +69,10 @@ func TestPingAllByIP(t *testing.T) { 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) - // } + err = scenario.Shutdown() + if err != nil { + t.Errorf("failed to tear down scenario: %s", err) + } } func TestPingAllByHostname(t *testing.T) { @@ -94,6 +94,7 @@ func TestPingAllByHostname(t *testing.T) { } var allClients []*tsic.TailscaleInContainer + var allHostnames []string for namespace := range spec { clients, err := scenario.GetClients(namespace) @@ -109,13 +110,22 @@ func TestPingAllByHostname(t *testing.T) { t.Errorf("failed wait for tailscale clients to be in sync: %s", err) } + for _, client := range allClients { + fqdn, err := client.FQDN() + if err != nil { + t.Errorf("failed to get fqdn of client %s: %s", t.Name(), err) + } + + allHostnames = append(allHostnames, fqdn) + } + success := 0 for _, client := range allClients { - for _, peer := range allClients { - err := client.Ping(peer.Hostname) + for _, hostname := range allHostnames { + err := client.Ping(hostname) if err != nil { - t.Errorf("failed to ping %s from %s: %s", peer.Hostname, client.Hostname, err) + t.Errorf("failed to ping %s from %s: %s", hostname, client.Hostname, err) } else { success++ } diff --git a/integration/tsic/tsic.go b/integration/tsic/tsic.go index 1cdc3711..d62ea1de 100644 --- a/integration/tsic/tsic.go +++ b/integration/tsic/tsic.go @@ -204,6 +204,15 @@ func (t *TailscaleInContainer) Status() (*ipnstate.Status, error) { return &status, err } +func (t *TailscaleInContainer) FQDN() (string, error) { + status, err := t.Status() + if err != nil { + return "", fmt.Errorf("failed to get FQDN: %w", err) + } + + return status.Self.DNSName, nil +} + func (t *TailscaleInContainer) WaitForPeers(expected int) error { return t.pool.Retry(func() error { status, err := t.Status() From d706c3516dca8f65f837c88288e1e1cb8b668a5f Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 23 Oct 2022 11:42:15 +0200 Subject: [PATCH 5/8] Remove 1.16 from FQDN, bump 1.32.1 Signed-off-by: Kristoffer Dalby --- integration/general_test.go | 7 ++++--- integration/scenario.go | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/integration/general_test.go b/integration/general_test.go index 183f6615..026a0691 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -84,8 +84,9 @@ func TestPingAllByHostname(t *testing.T) { } spec := map[string]int{ - "namespace3": len(TailscaleVersions), - "namespace4": len(TailscaleVersions), + // Omit 1.16.2 (-1) because it does not have the FQDN field + "namespace3": len(TailscaleVersions) - 1, + "namespace4": len(TailscaleVersions) - 1, } err = scenario.CreateHeadscaleEnv(spec) @@ -113,7 +114,7 @@ func TestPingAllByHostname(t *testing.T) { for _, client := range allClients { fqdn, err := client.FQDN() if err != nil { - t.Errorf("failed to get fqdn of client %s: %s", t.Name(), err) + t.Errorf("failed to get fqdn of client %s: %s", client.Hostname, err) } allHostnames = append(allHostnames, fqdn) diff --git a/integration/scenario.go b/integration/scenario.go index 4676fe4e..d391be46 100644 --- a/integration/scenario.go +++ b/integration/scenario.go @@ -29,7 +29,7 @@ var ( TailscaleVersions = []string{ "head", "unstable", - "1.32.0", + "1.32.1", "1.30.2", "1.28.0", "1.26.2", From 53b4bb220dd0cafc7f77cf1b3f41b57c8489881a Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 23 Oct 2022 11:55:37 +0200 Subject: [PATCH 6/8] Fixup after ts interface Signed-off-by: Kristoffer Dalby --- integration/general_test.go | 6 +++--- integration/tailscale.go | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/integration/general_test.go b/integration/general_test.go index 026a0691..501be87f 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -94,7 +94,7 @@ func TestPingAllByHostname(t *testing.T) { t.Errorf("failed to create headscale environment: %s", err) } - var allClients []*tsic.TailscaleInContainer + var allClients []TailscaleClient var allHostnames []string for namespace := range spec { @@ -114,7 +114,7 @@ func TestPingAllByHostname(t *testing.T) { for _, client := range allClients { fqdn, err := client.FQDN() if err != nil { - t.Errorf("failed to get fqdn of client %s: %s", client.Hostname, err) + t.Errorf("failed to get fqdn of client %s: %s", client.Hostname(), err) } allHostnames = append(allHostnames, fqdn) @@ -126,7 +126,7 @@ func TestPingAllByHostname(t *testing.T) { for _, hostname := range allHostnames { err := client.Ping(hostname) if err != nil { - t.Errorf("failed to ping %s from %s: %s", hostname, client.Hostname, err) + t.Errorf("failed to ping %s from %s: %s", hostname, client.Hostname(), err) } else { success++ } diff --git a/integration/tailscale.go b/integration/tailscale.go index 4d21798f..2d3ec72a 100644 --- a/integration/tailscale.go +++ b/integration/tailscale.go @@ -12,6 +12,7 @@ type TailscaleClient interface { Version() string Up(loginServer, authKey string) error IPs() ([]netip.Addr, error) + FQDN() (string, error) Status() (*ipnstate.Status, error) WaitForPeers(expected int) error Ping(hostnameOrIP string) error From 40c048fb45b490632a5be61cc7ddc61a97afc7ae Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 23 Oct 2022 12:01:03 +0200 Subject: [PATCH 7/8] Fix lint Signed-off-by: Kristoffer Dalby --- integration/general_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration/general_test.go b/integration/general_test.go index 501be87f..4e2e3ab7 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -94,8 +94,8 @@ func TestPingAllByHostname(t *testing.T) { t.Errorf("failed to create headscale environment: %s", err) } - var allClients []TailscaleClient - var allHostnames []string + allClients := make([]TailscaleClient, 0) + allHostnames := make([]string, 0) for namespace := range spec { clients, err := scenario.GetClients(namespace) From 7155b22043ee830a845c91f1fc18ffb58bfcc447 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 23 Oct 2022 12:41:35 +0200 Subject: [PATCH 8/8] Factor out some commonly used patterns Signed-off-by: Kristoffer Dalby --- integration/control.go | 6 ++-- integration/general_test.go | 54 +++++++----------------------- integration/hsic/hsic.go | 2 +- integration/scenario.go | 67 +++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 44 deletions(-) diff --git a/integration/control.go b/integration/control.go index bcda4a56..58a4661f 100644 --- a/integration/control.go +++ b/integration/control.go @@ -1,6 +1,8 @@ package integration -import v1 "github.com/juanfont/headscale/gen/go/headscale/v1" +import ( + v1 "github.com/juanfont/headscale/gen/go/headscale/v1" +) type ControlServer interface { Shutdown() error @@ -9,5 +11,5 @@ type ControlServer interface { WaitForReady() error CreateNamespace(namespace string) error CreateAuthKey(namespace string) (*v1.PreAuthKey, error) - ListNodes(namespace string) ([]*v1.Machine, error) + ListMachinesInNamespace(namespace string) ([]*v1.Machine, error) } diff --git a/integration/general_test.go b/integration/general_test.go index 4e2e3ab7..3d7063da 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -1,7 +1,6 @@ package integration import ( - "net/netip" "testing" ) @@ -23,30 +22,14 @@ func TestPingAllByIP(t *testing.T) { t.Errorf("failed to create headscale environment: %s", err) } - var allIps []netip.Addr - var allClients []TailscaleClient + allClients, err := scenario.ListTailscaleClients() + if err != nil { + t.Errorf("failed to get clients: %s", err) + } - for namespace, count := range spec { - ips, err := scenario.GetIPs(namespace) - if err != nil { - t.Errorf("failed to get tailscale ips: %s", err) - } - - if len(ips) != count*2 { - t.Errorf( - "got the wrong amount of tailscale ips, %d != %d", - len(ips), - count*2, - ) - } - - clients, err := scenario.GetClients(namespace) - if err != nil { - t.Errorf("failed to get tailscale clients: %s", err) - } - - allIps = append(allIps, ips...) - allClients = append(allClients, clients...) + allIps, err := scenario.ListTailscaleClientsIPs() + if err != nil { + t.Errorf("failed to get clients: %s", err) } err = scenario.WaitForTailscaleSync() @@ -94,16 +77,9 @@ func TestPingAllByHostname(t *testing.T) { t.Errorf("failed to create headscale environment: %s", err) } - allClients := make([]TailscaleClient, 0) - allHostnames := make([]string, 0) - - for namespace := range spec { - clients, err := scenario.GetClients(namespace) - if err != nil { - t.Errorf("failed to get tailscale clients: %s", err) - } - - allClients = append(allClients, clients...) + allClients, err := scenario.ListTailscaleClients() + if err != nil { + t.Errorf("failed to get clients: %s", err) } err = scenario.WaitForTailscaleSync() @@ -111,13 +87,9 @@ func TestPingAllByHostname(t *testing.T) { t.Errorf("failed wait for tailscale clients to be in sync: %s", err) } - for _, client := range allClients { - fqdn, err := client.FQDN() - if err != nil { - t.Errorf("failed to get fqdn of client %s: %s", client.Hostname(), err) - } - - allHostnames = append(allHostnames, fqdn) + allHostnames, err := scenario.ListTailscaleClientsFQDNs() + if err != nil { + t.Errorf("failed to get FQDNs: %s", err) } success := 0 diff --git a/integration/hsic/hsic.go b/integration/hsic/hsic.go index 1790a0a2..a6372a5a 100644 --- a/integration/hsic/hsic.go +++ b/integration/hsic/hsic.go @@ -199,7 +199,7 @@ func (t *HeadscaleInContainer) CreateAuthKey( return &preAuthKey, nil } -func (t *HeadscaleInContainer) ListNodes( +func (t *HeadscaleInContainer) ListMachinesInNamespace( namespace string, ) ([]*v1.Machine, error) { command := []string{"headscale", "--namespace", namespace, "nodes", "list", "--output", "json"} diff --git a/integration/scenario.go b/integration/scenario.go index d391be46..6847db7d 100644 --- a/integration/scenario.go +++ b/integration/scenario.go @@ -138,6 +138,15 @@ func (s *Scenario) Shutdown() error { return nil } +func (s *Scenario) Namespaces() []string { + namespaces := make([]string, 0) + for namespace := range s.namespaces { + namespaces = append(namespaces, namespace) + } + + return namespaces +} + /// Headscale related stuff // Note: These functions assume that there is a _single_ headscale instance for now @@ -345,3 +354,61 @@ func (s *Scenario) GetClients(namespace string) ([]TailscaleClient, error) { return clients, fmt.Errorf("failed to get clients: %w", errNoNamespaceAvailable) } + +func (s *Scenario) ListTailscaleClients(namespaces ...string) ([]TailscaleClient, error) { + var allClients []TailscaleClient + + if len(namespaces) == 0 { + namespaces = s.Namespaces() + } + + for _, namespace := range namespaces { + clients, err := s.GetClients(namespace) + if err != nil { + return nil, err + } + + allClients = append(allClients, clients...) + } + + return allClients, nil +} + +func (s *Scenario) ListTailscaleClientsIPs(namespaces ...string) ([]netip.Addr, error) { + var allIps []netip.Addr + + if len(namespaces) == 0 { + namespaces = s.Namespaces() + } + + for _, namespace := range namespaces { + ips, err := s.GetIPs(namespace) + if err != nil { + return nil, err + } + + allIps = append(allIps, ips...) + } + + return allIps, nil +} + +func (s *Scenario) ListTailscaleClientsFQDNs(namespaces ...string) ([]string, error) { + allFQDNs := make([]string, 0) + + clients, err := s.ListTailscaleClients(namespaces...) + if err != nil { + return nil, err + } + + for _, client := range clients { + fqdn, err := client.FQDN() + if err != nil { + return nil, err + } + + allFQDNs = append(allFQDNs, fqdn) + } + + return allFQDNs, nil +}