Skip to content

Commit

Permalink
Add integration tests that check logout and relogin
Browse files Browse the repository at this point in the history
  • Loading branch information
juanfont committed Dec 21, 2022
1 parent 593040b commit b54c0e3
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 1 deletion.
161 changes: 161 additions & 0 deletions integration/auth_web_flow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"log"
"net/http"
"net/netip"
"net/url"
"strings"
"testing"
Expand Down Expand Up @@ -59,7 +60,122 @@ func TestAuthWebFlowAuthenticationPingAll(t *testing.T) {
}

success := 0
for _, client := range allClients {
for _, ip := range allIps {
err := client.Ping(ip.String())
if err != nil {
t.Errorf("failed to ping %s from %s: %s", ip, client.Hostname(), err)
} else {
success++
}
}
}

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 TestAuthWebFlowLogoutAndRelogin(t *testing.T) {
IntegrationSkip(t)
t.Parallel()

baseScenario, err := NewScenario()
if err != nil {
t.Errorf("failed to create scenario: %s", err)
}

scenario := AuthWebFlowScenario{
Scenario: baseScenario,
}

spec := map[string]int{
"namespace1": len(TailscaleVersions),
"namespace2": len(TailscaleVersions),
}

err = scenario.CreateHeadscaleEnv(spec, hsic.WithTestName("weblogout"))
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)
}

success := 0
for _, client := range allClients {
for _, ip := range allIps {
err := client.Ping(ip.String())
if err != nil {
t.Errorf("failed to ping %s from %s: %s", ip, client.Hostname(), err)
} else {
success++
}
}
}

t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps))

clientIPs := make(map[TailscaleClient][]netip.Addr)
for _, client := range allClients {
ips, err := client.IPs()
if err != nil {
t.Errorf("failed to get IPs for client %s: %s", client.Hostname(), err)
}
clientIPs[client] = ips
}

for _, client := range allClients {
_, _, err = client.Execute([]string{"tailscale", "logout"})
if err != nil {
t.Errorf("failed to logout client %s: %s", client.Hostname(), err)
}
}

scenario.waitForTailscaleLogout()

t.Logf("all clients logged out")

headscale, err := scenario.Headscale()
if err != nil {
t.Errorf("failed to get headscale server: %s", err)
}

for namespaceName := range spec {
err = scenario.runTailscaleUp(namespaceName, headscale.GetEndpoint())
if err != nil {
t.Errorf("failed to run tailscale up: %s", err)
}
}

t.Logf("all clients logged in again")

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)
}

success = 0
for _, client := range allClients {
for _, ip := range allIps {
err := client.Ping(ip.String())
Expand All @@ -73,6 +189,35 @@ func TestAuthWebFlowAuthenticationPingAll(t *testing.T) {

t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps))

for _, client := range allClients {
ips, err := client.IPs()
if err != nil {
t.Errorf("failed to get IPs for client %s: %s", client.Hostname(), err)
}

// lets check if the IPs are the same
if len(ips) != len(clientIPs[client]) {
t.Errorf("IPs changed for client %s", client.Hostname())
}

for _, ip := range ips {
found := false
for _, oldIP := range clientIPs[client] {
if ip == oldIP {
found = true

break
}
}

if !found {
t.Errorf("IPs changed for client %s. Used to be %v now %v", client.Hostname(), clientIPs[client], ips)
}
}
}

t.Logf("all clients IPs are the same")

err = scenario.Shutdown()
if err != nil {
t.Errorf("failed to tear down scenario: %s", err)
Expand Down Expand Up @@ -114,6 +259,22 @@ func (s *AuthWebFlowScenario) CreateHeadscaleEnv(
return nil
}

func (s *AuthWebFlowScenario) waitForTailscaleLogout() {
for _, namespace := range s.namespaces {
for _, client := range namespace.Clients {
namespace.syncWaitGroup.Add(1)

go func(c TailscaleClient) {
defer namespace.syncWaitGroup.Done()

// TODO(kradalby): error handle this
_ = c.WaitForLogout()
}(client)
}
namespace.syncWaitGroup.Wait()
}
}

func (s *AuthWebFlowScenario) runTailscaleUp(
namespaceStr, loginServer string,
) error {
Expand Down
3 changes: 2 additions & 1 deletion integration/tailscale.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"tailscale.com/ipn/ipnstate"
)

//nolint
// nolint
type TailscaleClient interface {
Hostname() string
Shutdown() error
Expand All @@ -19,6 +19,7 @@ type TailscaleClient interface {
FQDN() (string, error)
Status() (*ipnstate.Status, error)
WaitForReady() error
WaitForLogout() error
WaitForPeers(expected int) error
Ping(hostnameOrIP string) error
ID() string
Expand Down
16 changes: 16 additions & 0 deletions integration/tsic/tsic.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ var (
errTailscaleWrongPeerCount = errors.New("wrong peer count")
errTailscaleCannotUpWithoutAuthkey = errors.New("cannot up without authkey")
errTailscaleNotConnected = errors.New("tailscale not connected")
errTailscaleNotLoggedOut = errors.New("tailscale not logged out")
)

type TailscaleInContainer struct {
Expand Down Expand Up @@ -350,6 +351,21 @@ func (t *TailscaleInContainer) WaitForReady() error {
})
}

func (t *TailscaleInContainer) WaitForLogout() error {
return t.pool.Retry(func() error {
status, err := t.Status()
if err != nil {
return fmt.Errorf("failed to fetch tailscale status: %w", err)
}

if status.CurrentTailnet == nil {
return nil
}

return errTailscaleNotLoggedOut
})
}

func (t *TailscaleInContainer) WaitForPeers(expected int) error {
return t.pool.Retry(func() error {
status, err := t.Status()
Expand Down

0 comments on commit b54c0e3

Please sign in to comment.