Skip to content

Commit

Permalink
Merge pull request #844 from kradalby/container-exist-fix
Browse files Browse the repository at this point in the history
Run integration tests inside docker, dont depend on local platform
  • Loading branch information
kradalby authored Oct 8, 2022
2 parents d575dac + 8ee35c9 commit f18e222
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 59 deletions.
32 changes: 28 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,40 @@ test:
test_integration: test_integration_cli test_integration_derp test_integration_oidc test_integration_general

test_integration_cli:
go test -failfast -tags integration_cli,integration -timeout 30m -count=1 ./...
docker network rm $$(docker network ls --filter name=headscale --quiet) || true
docker network create headscale-test || true
docker run -t --rm \
--network headscale-test \
-v $$PWD:$$PWD -w $$PWD \
-v /var/run/docker.sock:/var/run/docker.sock golang:1 \
go test -failfast -tags integration_cli,integration -timeout 30m -count=1 ./...

test_integration_derp:
go test -failfast -tags integration_derp,integration -timeout 30m -count=1 ./...
docker network rm $$(docker network ls --filter name=headscale --quiet) || true
docker network create headscale-test || true
docker run -t --rm \
--network headscale-test \
-v $$PWD:$$PWD -w $$PWD \
-v /var/run/docker.sock:/var/run/docker.sock golang:1 \
go test -failfast -tags integration_derp,integration -timeout 30m -count=1 ./...

test_integration_general:
go test -failfast -tags integration_general,integration -timeout 30m -count=1 ./...
docker network rm $$(docker network ls --filter name=headscale --quiet) || true
docker network create headscale-test || true
docker run -t --rm \
--network headscale-test \
-v $$PWD:$$PWD -w $$PWD \
-v /var/run/docker.sock:/var/run/docker.sock golang:1 \
go test -failfast -tags integration_general,integration -timeout 30m -count=1 ./...

test_integration_oidc:
go test -failfast -tags integration_oidc,integration -timeout 30m -count=1 ./...
docker network rm $$(docker network ls --filter name=headscale --quiet) || true
docker network create headscale-test || true
docker run -t --rm \
--network headscale-test \
-v $$PWD:$$PWD -w $$PWD \
-v /var/run/docker.sock:/var/run/docker.sock golang:1 \
go test -failfast -tags integration_oidc,integration -timeout 30m -count=1 ./...

coverprofile_func:
go tool cover -func=coverage.out
Expand Down
6 changes: 5 additions & 1 deletion cmd/headscale/cli/mockoidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func mockOIDC() error {
if clientSecret == "" {
return errMockOidcClientSecretNotDefined
}
addrStr := os.Getenv("MOCKOIDC_ADDR")
if addrStr == "" {
return errMockOidcPortNotDefined
}
portStr := os.Getenv("MOCKOIDC_PORT")
if portStr == "" {
return errMockOidcPortNotDefined
Expand All @@ -61,7 +65,7 @@ func mockOIDC() error {
return err
}

listener, err := net.Listen("tcp", fmt.Sprintf("mockoidc:%d", port))
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addrStr, port))
if err != nil {
return err
}
Expand Down
8 changes: 7 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,13 @@
};
in rec {
# `nix develop`
devShell = pkgs.mkShell {buildInputs = devDeps;};
devShell = pkgs.mkShell {
buildInputs = devDeps;

shellHook = ''
export GOFLAGS=-tags="integration,integration_general,integration_oidc,integration_cli,integration_derp"
'';
};

# `nix build`
packages = with pkgs; {
Expand Down
21 changes: 14 additions & 7 deletions integration_cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
Expand Down Expand Up @@ -42,11 +43,11 @@ func (s *IntegrationCLITestSuite) SetupTest() {
s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
}

if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil {
s.network = *pnetwork
} else {
s.FailNow(fmt.Sprintf("Could not create network: %s", err), "")
network, err := GetFirstOrCreateNetwork(&s.pool, headscaleNetwork)
if err != nil {
s.FailNow(fmt.Sprintf("Failed to create or get network: %s", err), "")
}
s.network = network

headscaleBuildOptions := &dockertest.BuildOptions{
Dockerfile: "Dockerfile",
Expand All @@ -63,8 +64,12 @@ func (s *IntegrationCLITestSuite) SetupTest() {
Mounts: []string{
fmt.Sprintf("%s/integration_test/etc:/etc/headscale", currentPath),
},
Networks: []*dockertest.Network{&s.network},
Cmd: []string{"headscale", "serve"},
Cmd: []string{"headscale", "serve"},
Networks: []*dockertest.Network{&s.network},
ExposedPorts: []string{"8080/tcp"},
PortBindings: map[docker.Port][]docker.PortBinding{
"8080/tcp": {{HostPort: "8080"}},
},
}

err = s.pool.RemoveContainerByName(headscaleHostname)
Expand All @@ -87,7 +92,9 @@ func (s *IntegrationCLITestSuite) SetupTest() {
fmt.Println("Created headscale container for CLI tests")

fmt.Println("Waiting for headscale to be ready for CLI tests")
hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp"))
hostEndpoint := fmt.Sprintf("%s:%s",
s.headscale.GetIPInNetwork(&s.network),
s.headscale.GetPort("8080/tcp"))

if err := s.pool.Retry(func() error {
url := fmt.Sprintf("http://%s/health", hostEndpoint)
Expand Down
34 changes: 30 additions & 4 deletions integration_common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import (
)

const (
headscaleHostname = "headscale-derp"
headscaleNetwork = "headscale-test"
headscaleHostname = "headscale"
DOCKER_EXECUTE_TIMEOUT = 10 * time.Second
)

Expand All @@ -32,7 +33,7 @@ var (
tailscaleVersions = []string{
// "head",
// "unstable",
"1.30.0",
"1.30.2",
"1.28.0",
"1.26.2",
"1.24.2",
Expand Down Expand Up @@ -115,13 +116,19 @@ func ExecuteCommand(
fmt.Println("stdout: ", stdout.String())
fmt.Println("stderr: ", stderr.String())

return stdout.String(), stderr.String(), fmt.Errorf("command failed with: %s", stderr.String())
return stdout.String(), stderr.String(), fmt.Errorf(
"command failed with: %s",
stderr.String(),
)
}

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

return stdout.String(), stderr.String(), fmt.Errorf("command timed out after %s", execConfig.timeout)
return stdout.String(), stderr.String(), fmt.Errorf(
"command timed out after %s",
execConfig.timeout,
)
}
}

Expand Down Expand Up @@ -316,3 +323,22 @@ func GetEnvBool(key string) (bool, error) {

return v, nil
}

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 dockertest.Network{}, err
}

return networks[0], nil
}
}

return networks[0], nil
}
44 changes: 29 additions & 15 deletions integration_embedded_derp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,20 @@ import (
)

const (
namespaceName = "derpnamespace"
totalContainers = 3
headscaleDerpHostname = "headscale-derp"
namespaceName = "derpnamespace"
totalContainers = 3
)

type IntegrationDERPTestSuite struct {
suite.Suite
stats *suite.SuiteInformation

pool dockertest.Pool
networks map[int]dockertest.Network // so we keep the containers isolated
headscale dockertest.Resource
saveLogs bool
pool dockertest.Pool
network dockertest.Network
containerNetworks map[int]dockertest.Network // so we keep the containers isolated
headscale dockertest.Resource
saveLogs bool

tailscales map[string]dockertest.Resource
joinWaitGroup sync.WaitGroup
Expand All @@ -53,7 +55,7 @@ func TestDERPIntegrationTestSuite(t *testing.T) {
s := new(IntegrationDERPTestSuite)

s.tailscales = make(map[string]dockertest.Resource)
s.networks = make(map[int]dockertest.Network)
s.containerNetworks = make(map[int]dockertest.Network)
s.saveLogs = saveLogs

suite.Run(t, s)
Expand All @@ -78,7 +80,7 @@ func TestDERPIntegrationTestSuite(t *testing.T) {
log.Printf("Could not purge resource: %s\n", err)
}

for _, network := range s.networks {
for _, network := range s.containerNetworks {
if err := network.Close(); err != nil {
log.Printf("Could not close network: %s\n", err)
}
Expand All @@ -93,9 +95,15 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
}

network, err := GetFirstOrCreateNetwork(&s.pool, headscaleNetwork)
if err != nil {
s.FailNow(fmt.Sprintf("Failed to create or get network: %s", err), "")
}
s.network = network

for i := 0; i < totalContainers; i++ {
if pnetwork, err := s.pool.CreateNetwork(fmt.Sprintf("headscale-derp-%d", i)); err == nil {
s.networks[i] = *pnetwork
s.containerNetworks[i] = *pnetwork
} else {
s.FailNow(fmt.Sprintf("Could not create network: %s", err), "")
}
Expand All @@ -112,22 +120,24 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
}

headscaleOptions := &dockertest.RunOptions{
Name: headscaleHostname,

Name: headscaleDerpHostname,
Mounts: []string{
fmt.Sprintf(
"%s/integration_test/etc_embedded_derp:/etc/headscale",
currentPath,
),
},
Cmd: []string{"headscale", "serve"},
Networks: []*dockertest.Network{&s.network},
ExposedPorts: []string{"8443/tcp", "3478/udp"},
PortBindings: map[docker.Port][]docker.PortBinding{
"8443/tcp": {{HostPort: "8443"}},
"3478/udp": {{HostPort: "3478"}},
},
}

err = s.pool.RemoveContainerByName(headscaleHostname)
err = s.pool.RemoveContainerByName(headscaleDerpHostname)
if err != nil {
s.FailNow(
fmt.Sprintf(
Expand All @@ -153,13 +163,15 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
hostname, container := s.tailscaleContainer(
fmt.Sprint(i),
version,
s.networks[i],
s.containerNetworks[i],
)
s.tailscales[hostname] = *container
}

log.Println("Waiting for headscale to be ready for embedded DERP tests")
hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8443/tcp"))
hostEndpoint := fmt.Sprintf("%s:%s",
s.headscale.GetIPInNetwork(&s.network),
s.headscale.GetPort("8443/tcp"))

if err := s.pool.Retry(func() error {
url := fmt.Sprintf("https://%s/health", hostEndpoint)
Expand Down Expand Up @@ -320,7 +332,7 @@ func (s *IntegrationDERPTestSuite) TearDownSuite() {
log.Printf("Could not purge resource: %s\n", err)
}

for _, network := range s.networks {
for _, network := range s.containerNetworks {
if err := network.Close(); err != nil {
log.Printf("Could not close network: %s\n", err)
}
Expand Down Expand Up @@ -428,7 +440,9 @@ func (s *IntegrationDERPTestSuite) TestPingAllPeersByHostname() {
}

func (s *IntegrationDERPTestSuite) TestDERPSTUN() {
headscaleSTUNAddr := fmt.Sprintf("localhost:%s", s.headscale.GetPort("3478/udp"))
headscaleSTUNAddr := fmt.Sprintf("%s:%s",
s.headscale.GetIPInNetwork(&s.network),
s.headscale.GetPort("3478/udp"))
client := stun.NewClient()
client.SetVerbose(true)
client.SetVVerbose(true)
Expand Down
29 changes: 23 additions & 6 deletions integration_general_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,17 @@ func (s *IntegrationTestSuite) tailscaleContainer(
},
}

err := s.pool.RemoveContainerByName(hostname)
if err != nil {
s.FailNow(
fmt.Sprintf(
"Could not remove existing container before building test: %s",
err,
),
"",
)
}

pts, err := s.pool.BuildAndRunWithBuildOptions(
tailscaleBuildOptions,
tailscaleOptions,
Expand Down Expand Up @@ -219,11 +230,11 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.FailNow(fmt.Sprintf("Could not connect to docker: %s", err), "")
}

if pnetwork, err := s.pool.CreateNetwork("headscale-test"); err == nil {
s.network = *pnetwork
} else {
s.FailNow(fmt.Sprintf("Could not create network: %s", err), "")
network, err := GetFirstOrCreateNetwork(&s.pool, headscaleNetwork)
if err != nil {
s.FailNow(fmt.Sprintf("Failed to create or get network: %s", err), "")
}
s.network = network

headscaleBuildOptions := &dockertest.BuildOptions{
Dockerfile: "Dockerfile",
Expand All @@ -236,10 +247,14 @@ func (s *IntegrationTestSuite) SetupSuite() {
}

headscaleOptions := &dockertest.RunOptions{
Name: "headscale",
Name: headscaleHostname,
Mounts: []string{
fmt.Sprintf("%s/integration_test/etc:/etc/headscale", currentPath),
},
ExposedPorts: []string{"8080/tcp"},
PortBindings: map[docker.Port][]docker.PortBinding{
"8080/tcp": {{HostPort: "8080"}},
},
Networks: []*dockertest.Network{&s.network},
Cmd: []string{"headscale", "serve"},
}
Expand Down Expand Up @@ -278,7 +293,9 @@ func (s *IntegrationTestSuite) SetupSuite() {
}

log.Println("Waiting for headscale to be ready for core integration tests")
hostEndpoint := fmt.Sprintf("localhost:%s", s.headscale.GetPort("8080/tcp"))
hostEndpoint := fmt.Sprintf("%s:%s",
s.headscale.GetIPInNetwork(&s.network),
s.headscale.GetPort("8080/tcp"))

if err := s.pool.Retry(func() error {
url := fmt.Sprintf("http://%s/health", hostEndpoint)
Expand Down
Loading

0 comments on commit f18e222

Please sign in to comment.