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

Integration tests v2 #856

Merged
merged 23 commits into from
Oct 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fa8b02a
tsic: Tailscale in Container abstraction
kradalby Oct 13, 2022
308b9e7
Defince control server interface
kradalby Oct 13, 2022
b331e3f
hsic: ControlServer implementation of headscale in docker
kradalby Oct 13, 2022
f68ba75
Move some helper functions into dockertestutil package
kradalby Oct 13, 2022
a9c3b14
Define a "scenario", which is a controlserver with nodes
kradalby Oct 13, 2022
eda4321
Skip integration tests on short or lack of docker
kradalby Oct 14, 2022
f109b54
Join test suite container to network, allowing seperate networks
kradalby Oct 14, 2022
25e39d9
Add get ips command to scenario
kradalby Oct 14, 2022
b0a4ee4
test login with one node
kradalby Oct 14, 2022
13aa845
Add comment about scenario test
kradalby Oct 14, 2022
aef77a1
use variable for namespace
kradalby Oct 14, 2022
84f9f60
go mod tidy
kradalby Oct 18, 2022
c90d0dd
remove the need to bind host port
kradalby Oct 18, 2022
3951f39
Add wait for peers and status to tsic
kradalby Oct 18, 2022
0db608a
Add tailscale versions, waiters and helpers for scenario
kradalby Oct 18, 2022
39bc6f7
Port PingAll test to new test suite
kradalby Oct 18, 2022
2bf50bc
Add new integration tests to ci
kradalby Oct 18, 2022
4cb7d63
Set better names for different integration tests
kradalby Oct 18, 2022
36ad000
golangci-lint --fix
kradalby Oct 18, 2022
8502a0a
dont request tty
kradalby Oct 18, 2022
12ee9bc
Fix golangcilint
kradalby Oct 18, 2022
6d8c18d
Fix golangcilint
kradalby Oct 18, 2022
b2bca2a
Only run integration tests from dir in new tests
kradalby Oct 18, 2022
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 .github/workflows/test-integration-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: CI
on: [pull_request]

jobs:
integration-test:
integration-test-cli:
runs-on: ubuntu-latest

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-integration-derp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: CI
on: [pull_request]

jobs:
integration-test:
integration-test-derp:
runs-on: ubuntu-latest

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-integration-general.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: CI
on: [pull_request]

jobs:
integration-test:
integration-test-general:
runs-on: ubuntu-latest

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-integration-oidc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: CI
on: [pull_request]

jobs:
integration-test:
integration-test-oidc:
runs-on: ubuntu-latest

steps:
Expand Down
40 changes: 40 additions & 0 deletions .github/workflows/test-integration-v2-general.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI

on: [pull_request]

jobs:
integration-test-v2-general:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
with:
fetch-depth: 2

- name: Set Swap Space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10

- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v14.1
with:
files: |
*.nix
go.*
**/*.go
integration_test/
config-example.yaml

- uses: cachix/install-nix-action@v16
if: steps.changed-files.outputs.any_changed == 'true'

- name: Run general integration tests
if: steps.changed-files.outputs.any_changed == 'true'
uses: nick-fields/retry@v2
with:
timeout_minutes: 240
max_attempts: 5
retry_on: error
command: nix develop --command -- make test_integration_v2_general
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ test_integration_oidc:
-v /var/run/docker.sock:/var/run/docker.sock golang:1 \
go test -failfast -timeout 30m -count=1 -run IntegrationOIDC ./...

test_integration_v2_general:
docker run \
-t --rm \
-v ~/.cache/hs-integration-go:/go \
--name headscale-test-suite \
-v $$PWD:$$PWD -w $$PWD/integration \
-v /var/run/docker.sock:/var/run/docker.sock \
golang:1 \
go test ./... -timeout 15m -v

coverprofile_func:
go tool cover -func=coverage.out

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.19
require (
github.com/AlecAivazis/survey/v2 v2.3.5
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029
github.com/cenkalti/backoff/v4 v4.1.3
github.com/coreos/go-oidc/v3 v3.3.0
github.com/deckarep/golang-set/v2 v2.1.0
github.com/efekarakus/termcolor v1.0.1
Expand Down Expand Up @@ -54,7 +55,6 @@ require (
github.com/akutz/memconn v0.1.0 // indirect
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/continuity v0.3.0 // indirect
Expand Down
126 changes: 2 additions & 124 deletions go.sum

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions integration/control.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package integration

import v1 "github.com/juanfont/headscale/gen/go/headscale/v1"

type ControlServer interface {
juanfont marked this conversation as resolved.
Show resolved Hide resolved
Shutdown() error
GetHealthEndpoint() string
GetEndpoint() string
WaitForReady() error
CreateNamespace(namespace string) error
CreateAuthKey(namespace string) (*v1.PreAuthKey, error)
ListNodes(namespace string) ([]*v1.Machine, error)
}
40 changes: 40 additions & 0 deletions integration/dockertestutil/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package dockertestutil

import (
"os"

"github.com/ory/dockertest/v3/docker"
)

func IsRunningInContainer() bool {
if _, err := os.Stat("/.dockerenv"); err != nil {
return false
}

return true
}

func DockerRestartPolicy(config *docker.HostConfig) {
// set AutoRemove to true so that stopped container goes away by itself on error *immediately*.
// when set to false, containers remain until the end of the integration test.
config.AutoRemove = false
config.RestartPolicy = docker.RestartPolicy{
Name: "no",
}
}

func DockerAllowLocalIPv6(config *docker.HostConfig) {
if config.Sysctls == nil {
config.Sysctls = make(map[string]string, 1)
}
config.Sysctls["net.ipv6.conf.all.disable_ipv6"] = "0"
}

func DockerAllowNetworkAdministration(config *docker.HostConfig) {
config.CapAdd = append(config.CapAdd, "NET_ADMIN")
config.Mounts = append(config.Mounts, docker.HostMount{
Type: "bind",
Source: "/dev/net/tun",
Target: "/dev/net/tun",
})
}
94 changes: 94 additions & 0 deletions integration/dockertestutil/execute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package dockertestutil

import (
"bytes"
"errors"
"fmt"
"time"

"github.com/ory/dockertest/v3"
)

const dockerExecuteTimeout = time.Second * 10

var (
ErrDockertestCommandFailed = errors.New("dockertest command failed")
ErrDockertestCommandTimeout = errors.New("dockertest command timed out")
)

type ExecuteCommandConfig struct {
timeout time.Duration
}

type ExecuteCommandOption func(*ExecuteCommandConfig) error

func ExecuteCommandTimeout(timeout time.Duration) ExecuteCommandOption {
return ExecuteCommandOption(func(conf *ExecuteCommandConfig) error {
conf.timeout = timeout

return nil
})
}

func ExecuteCommand(
resource *dockertest.Resource,
cmd []string,
env []string,
options ...ExecuteCommandOption,
) (string, string, error) {
var stdout bytes.Buffer
var stderr bytes.Buffer

execConfig := ExecuteCommandConfig{
timeout: dockerExecuteTimeout,
}

for _, opt := range options {
if err := opt(&execConfig); err != nil {
return "", "", fmt.Errorf("execute-command/options: %w", err)
}
}

type result struct {
exitCode int
err error
}

resultChan := make(chan result, 1)

// Run your long running function in it's own goroutine and pass back it's
// response into our channel.
go func() {
exitCode, err := resource.Exec(
cmd,
dockertest.ExecOptions{
Env: append(env, "HEADSCALE_LOG_LEVEL=disabled"),
StdOut: &stdout,
StdErr: &stderr,
},
)
resultChan <- result{exitCode, err}
}()

// Listen on our channel AND a timeout channel - which ever happens first.
select {
case res := <-resultChan:
if res.err != nil {
return stdout.String(), stderr.String(), res.err
}

if res.exitCode != 0 {
// Uncomment for debugging
// log.Println("Command: ", cmd)
// log.Println("stdout: ", stdout.String())
// log.Println("stderr: ", stderr.String())

return stdout.String(), stderr.String(), ErrDockertestCommandFailed
}

return stdout.String(), stderr.String(), nil
case <-time.After(execConfig.timeout):

return stdout.String(), stderr.String(), ErrDockertestCommandTimeout
}
}
62 changes: 62 additions & 0 deletions integration/dockertestutil/network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package dockertestutil

import (
"errors"

"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
)

var ErrContainerNotFound = errors.New("container not found")

func GetFirstOrCreateNetwork(pool *dockertest.Pool, name string) (*dockertest.Network, error) {
networks, err := pool.NetworksByName(name)
if err != nil || len(networks) == 0 {
if _, err := pool.CreateNetwork(name); err == nil {
// Create does not give us an updated version of the resource, so we need to
// get it again.
networks, err := pool.NetworksByName(name)
if err != nil {
return nil, err
}

return &networks[0], nil
}
}

return &networks[0], nil
}

func AddContainerToNetwork(
pool *dockertest.Pool,
network *dockertest.Network,
testContainer string,
) error {
containers, err := pool.Client.ListContainers(docker.ListContainersOptions{
All: true,
Filters: map[string][]string{
"name": {testContainer},
},
})
if err != nil {
return err
}

err = pool.Client.ConnectNetwork(network.Network.ID, docker.NetworkConnectionOptions{
Container: containers[0].ID,
})
if err != nil {
return err
}

// TODO(kradalby): This doesnt work reliably, but calling the exact same functions
// seem to work fine...
// if container, ok := pool.ContainerByName("/" + testContainer); ok {
// err := container.ConnectToNetwork(network)
// if err != nil {
// return err
// }
// }

return nil
}
Loading