Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sync errorgroup handling in integration tests #1460

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion integration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Tests are located in files ending with `_test.go` and the framework are located

## Running integration tests locally

The easiest way to run tests locally is to use `[act](INSERT LINK)`, a local GitHub Actions runner:
The easiest way to run tests locally is to use `[act](https://github.com/nektos/act)`, a local GitHub Actions runner:

```
act pull_request -W .github/workflows/test-integration-v2-TestPingAllByIP.yaml
Expand Down
31 changes: 14 additions & 17 deletions integration/auth_oidc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ func TestOIDCExpireNodesBasedOnTokenExpiry(t *testing.T) {
t.Logf("%d successful pings out of %d (before expiry)", success, len(allClients)*len(allIps))

// await all nodes being logged out after OIDC token expiry
scenario.WaitForTailscaleLogout()
if err = scenario.WaitForTailscaleLogout(); err != nil {
t.Errorf("failed to logout tailscale nodes: %s", err)
}

err = scenario.Shutdown()
if err != nil {
Expand Down Expand Up @@ -310,15 +312,11 @@ func (s *AuthOIDCScenario) runTailscaleUp(
log.Printf("running tailscale up for user %s", userStr)
if user, ok := s.users[userStr]; ok {
for _, client := range user.Clients {
user.joinWaitGroup.Add(1)

go func(c TailscaleClient) {
defer user.joinWaitGroup.Done()

// TODO(juanfont): error handle this
c := client
user.joinWaitGroup.Go(func() error {
loginURL, err := c.UpWithLoginURL(loginServer)
if err != nil {
log.Printf("failed to run tailscale up: %s", err)
return fmt.Errorf("failed to run tailscale up: %w", err)
}

loginURL.Host = fmt.Sprintf("%s:8080", headscale.GetIP())
Expand All @@ -335,22 +333,19 @@ func (s *AuthOIDCScenario) runTailscaleUp(
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, loginURL.String(), nil)
resp, err := httpClient.Do(req)
if err != nil {
log.Printf("%s failed to get login url %s: %s", c.Hostname(), loginURL, err)

return
return fmt.Errorf("%s failed to get login url %s: %w", c.Hostname(), loginURL, err)
}

defer resp.Body.Close()

_, err = io.ReadAll(resp.Body)
if err != nil {
log.Printf("%s failed to read response body: %s", c.Hostname(), err)

return
return fmt.Errorf("%s failed to read response body: %w", c.Hostname(), err)
}

log.Printf("Finished request for %s to join tailnet", c.Hostname())
}(client)

return nil
})

err = client.WaitForReady()
if err != nil {
Expand All @@ -360,7 +355,9 @@ func (s *AuthOIDCScenario) runTailscaleUp(
log.Printf("client %s is ready", client.Hostname())
}

user.joinWaitGroup.Wait()
if err := user.joinWaitGroup.Wait(); err != nil {
return fmt.Errorf("failed to bring up tailscale nodes: %w", err)
}

for _, client := range user.Clients {
err := client.WaitForReady()
Expand Down
24 changes: 13 additions & 11 deletions integration/auth_web_flow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ func TestAuthWebFlowLogoutAndRelogin(t *testing.T) {
}
}

scenario.WaitForTailscaleLogout()
if err = scenario.WaitForTailscaleLogout(); err != nil {
t.Errorf("failed to logout tailscale nodes: %s", err)
}

t.Logf("all clients logged out")

Expand Down Expand Up @@ -250,29 +252,29 @@ func (s *AuthWebFlowScenario) runTailscaleUp(
log.Printf("running tailscale up for user %s", userStr)
if user, ok := s.users[userStr]; ok {
for _, client := range user.Clients {
user.joinWaitGroup.Add(1)

go func(c TailscaleClient) {
defer user.joinWaitGroup.Done()

// TODO(juanfont): error handle this
c := client
user.joinWaitGroup.Go(func() error {
loginURL, err := c.UpWithLoginURL(loginServer)
if err != nil {
log.Printf("failed to run tailscale up: %s", err)
return fmt.Errorf("failed to run tailscale up: %w", err)
}

err = s.runHeadscaleRegister(userStr, loginURL)
if err != nil {
log.Printf("failed to register client: %s", err)
return fmt.Errorf("failed to register client: %w", err)
}
}(client)

return nil
})

err := client.WaitForReady()
if err != nil {
log.Printf("error waiting for client %s to be ready: %s", client.Hostname(), err)
}
}
user.joinWaitGroup.Wait()
if err := user.joinWaitGroup.Wait(); err != nil {
return fmt.Errorf("failed to up tailscale nodes: %w", err)
}

for _, client := range user.Clients {
err := client.WaitForReady()
Expand Down
21 changes: 7 additions & 14 deletions integration/embedded_derp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package integration

import (
"fmt"
"log"
"net/url"
"testing"

Expand Down Expand Up @@ -186,39 +185,33 @@ func (s *EmbeddedDERPServerScenario) CreateTailscaleIsolatedNodesInUser(

cert := hsServer.GetCert()

user.createWaitGroup.Add(1)

opts = append(opts,
tsic.WithHeadscaleTLS(cert),
)

go func() {
defer user.createWaitGroup.Done()

// TODO(kradalby): error handle this
user.createWaitGroup.Go(func() error {
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)
return fmt.Errorf("failed to create tailscale node: %w", 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)
return fmt.Errorf("failed to wait for tailscaled: %w", err)
}

user.Clients[tsClient.Hostname()] = tsClient
}()

return nil
})
}
user.createWaitGroup.Wait()

return nil
return user.createWaitGroup.Wait()
}

return fmt.Errorf("failed to add tailscale node: %w", errNoUserAvailable)
Expand Down
8 changes: 6 additions & 2 deletions integration/general_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ func TestAuthKeyLogoutAndRelogin(t *testing.T) {
}
}

scenario.WaitForTailscaleLogout()
if err = scenario.WaitForTailscaleLogout(); err != nil {
t.Errorf("failed to logout tailscale nodes: %s", err)
}

t.Logf("all clients logged out")

Expand Down Expand Up @@ -261,7 +263,9 @@ func TestEphemeral(t *testing.T) {
}
}

scenario.WaitForTailscaleLogout()
if err = scenario.WaitForTailscaleLogout(); err != nil {
t.Errorf("failed to logout tailscale nodes: %s", err)
}

t.Logf("all clients logged out")

Expand Down
79 changes: 35 additions & 44 deletions integration/scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/juanfont/headscale/integration/tsic"
"github.com/ory/dockertest/v3"
"github.com/puzpuzpuz/xsync/v2"
"golang.org/x/sync/errgroup"
)

const (
Expand Down Expand Up @@ -79,9 +80,9 @@ var (
type User struct {
Clients map[string]TailscaleClient

createWaitGroup sync.WaitGroup
joinWaitGroup sync.WaitGroup
syncWaitGroup sync.WaitGroup
createWaitGroup errgroup.Group
joinWaitGroup errgroup.Group
syncWaitGroup errgroup.Group
}

// Scenario is a representation of an environment with one ControlServer and
Expand Down Expand Up @@ -286,40 +287,34 @@ func (s *Scenario) CreateTailscaleNodesInUser(
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
user.createWaitGroup.Go(func() error {
tsClient, err := tsic.New(
s.pool,
version,
s.network,
opts...,
)
if err != nil {
// return fmt.Errorf("failed to add tailscale node: %w", err)
log.Printf("failed to create tailscale node: %s", err)
return fmt.Errorf("failed to add tailscale node: %w", 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)
return fmt.Errorf("failed to add tailscale node: %w", err)
}

user.Clients[tsClient.Hostname()] = tsClient
}()

return nil
})
}
user.createWaitGroup.Wait()

return nil
return user.createWaitGroup.Wait()
}

return fmt.Errorf("failed to add tailscale node: %w", errNoUserAvailable)
Expand All @@ -332,22 +327,20 @@ func (s *Scenario) RunTailscaleUp(
) error {
if user, ok := s.users[userStr]; ok {
for _, client := range user.Clients {
user.joinWaitGroup.Add(1)

go func(c TailscaleClient) {
defer user.joinWaitGroup.Done()

// TODO(kradalby): error handle this
_ = c.Up(loginServer, authKey)
}(client)
c := client
user.joinWaitGroup.Go(func() error {
return c.Up(loginServer, authKey)
})

err := client.WaitForReady()
if err != nil {
log.Printf("error waiting for client %s to be ready: %s", client.Hostname(), err)
}
}

user.joinWaitGroup.Wait()
if err := user.joinWaitGroup.Wait(); err != nil {
return fmt.Errorf("failed to up tailscale nodes: %w", err)
}

for _, client := range user.Clients {
err := client.WaitForReady()
Expand Down Expand Up @@ -383,16 +376,14 @@ func (s *Scenario) WaitForTailscaleSync() error {

for _, user := range s.users {
for _, client := range user.Clients {
user.syncWaitGroup.Add(1)

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

// TODO(kradalby): error handle this
_ = c.WaitForPeers(tsCount)
}(client)
c := client
user.syncWaitGroup.Go(func() error {
return c.WaitForPeers(tsCount)
})
}
if err := user.syncWaitGroup.Wait(); err != nil {
return err
}
user.syncWaitGroup.Wait()
}

return nil
Expand Down Expand Up @@ -555,18 +546,18 @@ func (s *Scenario) ListTailscaleClientsFQDNs(users ...string) ([]string, error)

// WaitForTailscaleLogout blocks execution until all TailscaleClients have
// logged out of the ControlServer.
func (s *Scenario) WaitForTailscaleLogout() {
func (s *Scenario) WaitForTailscaleLogout() error {
for _, user := range s.users {
for _, client := range user.Clients {
user.syncWaitGroup.Add(1)

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

// TODO(kradalby): error handle this
_ = c.WaitForLogout()
}(client)
c := client
user.syncWaitGroup.Go(func() error {
return c.WaitForLogout()
})
}
if err := user.syncWaitGroup.Wait(); err != nil {
return err
}
user.syncWaitGroup.Wait()
}

return nil
}