Skip to content

Commit

Permalink
refactoring - e2e network (#1401)
Browse files Browse the repository at this point in the history
* change to testcontainers-go/network
  • Loading branch information
kwitsch authored Mar 18, 2024
1 parent 7eef4bf commit c3a319f
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 174 deletions.
26 changes: 16 additions & 10 deletions e2e/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,24 @@ import (
)

var _ = Describe("Basic functional tests", func() {
var blocky testcontainers.Container
var err error
var (
e2eNet *testcontainers.DockerNetwork
blocky testcontainers.Container
err error
)

BeforeEach(func(ctx context.Context) {
e2eNet = getRandomNetwork(ctx)
})

Describe("Container start", func() {
BeforeEach(func(ctx context.Context) {
_, err = createDNSMokkaContainer(ctx, "moka1", `A google/NOERROR("A 1.2.3.4 123")`)

_, err = createDNSMokkaContainer(ctx, "moka1", e2eNet, `A google/NOERROR("A 1.2.3.4 123")`)
Expect(err).Should(Succeed())
})
When("wrong port configuration is provided", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx,
blocky, err = createBlockyContainer(ctx, e2eNet,
"upstreams:",
" groups:",
" default:",
Expand All @@ -50,7 +56,7 @@ var _ = Describe("Basic functional tests", func() {
})
When("Minimal configuration is provided", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx,
blocky, err = createBlockyContainer(ctx, e2eNet,
"upstreams:",
" groups:",
" default:",
Expand Down Expand Up @@ -81,7 +87,7 @@ var _ = Describe("Basic functional tests", func() {
Context("http port configuration", func() {
When("'httpPort' is not defined", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx,
blocky, err = createBlockyContainer(ctx, e2eNet,
"upstreams:",
" groups:",
" default:",
Expand All @@ -101,7 +107,7 @@ var _ = Describe("Basic functional tests", func() {
})
When("'httpPort' is defined", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx,
blocky, err = createBlockyContainer(ctx, e2eNet,
"upstreams:",
" groups:",
" default:",
Expand Down Expand Up @@ -137,12 +143,12 @@ var _ = Describe("Basic functional tests", func() {

Describe("Logging", func() {
BeforeEach(func(ctx context.Context) {
_, err = createDNSMokkaContainer(ctx, "moka1", `A google/NOERROR("A 1.2.3.4 123")`)
_, err = createDNSMokkaContainer(ctx, "moka1", e2eNet, `A google/NOERROR("A 1.2.3.4 123")`)
Expect(err).Should(Succeed())
})
When("log privacy is enabled", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx,
blocky, err = createBlockyContainer(ctx, e2eNet,
"upstreams:",
" groups:",
" default:",
Expand Down
20 changes: 13 additions & 7 deletions e2e/blocking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@ import (
)

var _ = Describe("External lists and query blocking", func() {
var blocky testcontainers.Container
var err error
var (
e2eNet *testcontainers.DockerNetwork
blocky testcontainers.Container
err error
)

BeforeEach(func(ctx context.Context) {
_, err = createDNSMokkaContainer(ctx, "moka", `A google/NOERROR("A 1.2.3.4 123")`)
e2eNet = getRandomNetwork(ctx)

_, err = createDNSMokkaContainer(ctx, "moka", e2eNet, `A google/NOERROR("A 1.2.3.4 123")`)
Expect(err).Should(Succeed())
})
Describe("List download on startup", func() {
When("external blacklist ist not available", func() {
Context("loading.strategy = blocking", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx,
blocky, err = createBlockyContainer(ctx, e2eNet,
"log:",
" level: warn",
"upstreams:",
Expand Down Expand Up @@ -56,7 +62,7 @@ var _ = Describe("External lists and query blocking", func() {
})
Context("loading.strategy = failOnError", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx,
blocky, err = createBlockyContainer(ctx, e2eNet,
"log:",
" level: warn",
"upstreams:",
Expand Down Expand Up @@ -93,10 +99,10 @@ var _ = Describe("External lists and query blocking", func() {
Describe("Query blocking against external blacklists", func() {
When("external blacklists are defined and available", func() {
BeforeEach(func(ctx context.Context) {
_, err = createHTTPServerContainer(ctx, "httpserver", "list.txt", "blockeddomain.com")
_, err = createHTTPServerContainer(ctx, "httpserver", e2eNet, "list.txt", "blockeddomain.com")
Expect(err).Should(Succeed())

blocky, err = createBlockyContainer(ctx,
blocky, err = createBlockyContainer(ctx, e2eNet,
"log:",
" level: warn",
"upstreams:",
Expand Down
33 changes: 19 additions & 14 deletions e2e/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ const (
// createDNSMokkaContainer creates a DNS mokka container with the given rules attached to the test network
// under the given alias.
// It is automatically terminated when the test is finished.
func createDNSMokkaContainer(ctx context.Context, alias string, rules ...string) (testcontainers.Container, error) {
func createDNSMokkaContainer(ctx context.Context, alias string, e2eNet *testcontainers.DockerNetwork,
rules ...string,
) (testcontainers.Container, error) {
mokaRules := make(map[string]string)

for i, rule := range rules {
Expand All @@ -60,13 +62,14 @@ func createDNSMokkaContainer(ctx context.Context, alias string, rules ...string)
Env: mokaRules,
}

return startContainerWithNetwork(ctx, req, alias)
return startContainerWithNetwork(ctx, req, alias, e2eNet)
}

// createHTTPServerContainer creates a static HTTP server container that serves one file with the given lines
// and is attached to the test network under the given alias.
// It is automatically terminated when the test is finished.
func createHTTPServerContainer(ctx context.Context, alias, filename string, lines ...string,
func createHTTPServerContainer(ctx context.Context, alias string, e2eNet *testcontainers.DockerNetwork,
filename string, lines ...string,
) (testcontainers.Container, error) {
file := createTempFile(lines...)

Expand All @@ -84,23 +87,25 @@ func createHTTPServerContainer(ctx context.Context, alias, filename string, line
},
}

return startContainerWithNetwork(ctx, req, alias)
return startContainerWithNetwork(ctx, req, alias, e2eNet)
}

// createRedisContainer creates a redis container attached to the test network under the alias 'redis'.
// It is automatically terminated when the test is finished.
func createRedisContainer(ctx context.Context) (*redis.RedisContainer, error) {
func createRedisContainer(ctx context.Context, e2eNet *testcontainers.DockerNetwork,
) (*redis.RedisContainer, error) {
return deferTerminate(redis.RunContainer(ctx,
testcontainers.WithImage(redisImage),
redis.WithLogLevel(redis.LogLevelVerbose),
WithNetwork(ctx, "redis"),
withNetwork("redis", e2eNet),
))
}

// createPostgresContainer creates a postgres container attached to the test network under the alias 'postgres'.
// It creates a database 'user' with user 'user' and password 'user'.
// It is automatically terminated when the test is finished.
func createPostgresContainer(ctx context.Context) (*postgres.PostgresContainer, error) {
func createPostgresContainer(ctx context.Context, e2eNet *testcontainers.DockerNetwork,
) (*postgres.PostgresContainer, error) {
const waitLogOccurrence = 2

return deferTerminate(postgres.RunContainer(ctx,
Expand All @@ -113,27 +118,28 @@ func createPostgresContainer(ctx context.Context) (*postgres.PostgresContainer,
wait.ForLog("database system is ready to accept connections").
WithOccurrence(waitLogOccurrence).
WithStartupTimeout(startupTimeout)),
WithNetwork(ctx, "postgres"),
withNetwork("postgres", e2eNet),
))
}

// createMariaDBContainer creates a mariadb container attached to the test network under the alias 'mariaDB'.
// It creates a database 'user' with user 'user' and password 'user'.
// It is automatically terminated when the test is finished.
func createMariaDBContainer(ctx context.Context) (*mariadb.MariaDBContainer, error) {
func createMariaDBContainer(ctx context.Context, e2eNet *testcontainers.DockerNetwork,
) (*mariadb.MariaDBContainer, error) {
return deferTerminate(mariadb.RunContainer(ctx,
testcontainers.WithImage(mariaDBImage),
mariadb.WithDatabase("user"),
mariadb.WithUsername("user"),
mariadb.WithPassword("user"),
WithNetwork(ctx, "mariaDB"),
withNetwork("mariaDB", e2eNet),
))
}

// createBlockyContainer creates a blocky container with a config provided by the given lines.
// It is attached to the test network under the alias 'blocky'.
// It is automatically terminated when the test is finished.
func createBlockyContainer(ctx context.Context,
func createBlockyContainer(ctx context.Context, e2eNet *testcontainers.DockerNetwork,
lines ...string,
) (testcontainers.Container, error) {
confFile := createTempFile(lines...)
Expand Down Expand Up @@ -163,7 +169,7 @@ func createBlockyContainer(ctx context.Context,
WaitingFor: wait.ForHealthCheck().WithStartupTimeout(startupTimeout),
}

container, err := startContainerWithNetwork(ctx, req, "blocky")
container, err := startContainerWithNetwork(ctx, req, "blocky", e2eNet)
if err != nil {
// attach container log if error occurs
if r, err := container.Logs(ctx); err == nil {
Expand Down Expand Up @@ -202,14 +208,14 @@ func checkBlockyReadiness(ctx context.Context, cfg *config.Config, container tes
retry.Attempts(retryAttempts),
retry.DelayType(retry.BackOffDelay),
retry.Delay(time.Second))

if err != nil {
return fmt.Errorf("can't perform the DNS healthcheck request: %w", err)
}

for _, httpPort := range cfg.Ports.HTTP {
parts := strings.Split(httpPort, ":")
port := parts[len(parts)-1]

err = retry.Do(
func() error {
return doHTTPRequest(ctx, container, port)
Expand All @@ -220,7 +226,6 @@ func checkBlockyReadiness(ctx context.Context, cfg *config.Config, container tes
retry.Attempts(retryAttempts),
retry.DelayType(retry.BackOffDelay),
retry.Delay(time.Second))

if err != nil {
return fmt.Errorf("can't perform the HTTP request: %w", err)
}
Expand Down
75 changes: 19 additions & 56 deletions e2e/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,74 +3,32 @@ package e2e
import (
"bufio"
"context"
"fmt"
"net"
"strings"
"time"

"github.com/docker/go-connections/nat"
"github.com/miekg/dns"
"github.com/onsi/ginkgo/v2"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/testcontainers/testcontainers-go"
testNet "github.com/testcontainers/testcontainers-go/network"
)

//nolint:gochecknoglobals
var (
// currentNetwork is the global test network instance.
currentNetwork = testNetwork{}
)

// WithNetwork attaches the container with the given alias to the test network
//
//nolint:staticcheck
func WithNetwork(ctx context.Context, alias string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
networkName := currentNetwork.Name()
network, err := testcontainers.GenericNetwork(ctx, testcontainers.GenericNetworkRequest{
NetworkRequest: testcontainers.NetworkRequest{
Name: networkName,
CheckDuplicate: true, // force the Docker provider to reuse an existing network
Attachable: true,
},
})

if err != nil && !strings.Contains(err.Error(), "already exists") {
ginkgo.Fail(fmt.Sprintf("Failed to create network '%s'. Container won't be attached to this network: %v",
networkName, err))

return
}

// decrement the network counter when the test is finished and remove the network if it is not used anymore.
ginkgo.DeferCleanup(func(ctx context.Context) error {
if currentNetwork.Detach() {
if err := network.Remove(ctx); err != nil &&
!strings.Contains(err.Error(), "removing") &&
!strings.Contains(err.Error(), "not found") {
return err
}
}

return nil
})

// increment the network counter when the container is created.
currentNetwork.Attach()

// attaching to the network because it was created with success or it already existed.
req.Networks = append(req.Networks, networkName)

if req.NetworkAliases == nil {
req.NetworkAliases = make(map[string][]string)
}
// getRandomNetwork returns a new test network which is used for the tests and removed afterwards.
func getRandomNetwork(ctx context.Context) *testcontainers.DockerNetwork {
e2eNet, err := testNet.New(ctx)
Expect(err).Should(Succeed())
DeferCleanup(func(ctx context.Context) {
Expect(e2eNet.Remove(ctx)).Should(Succeed())
})

req.NetworkAliases[networkName] = []string{alias}
}
return e2eNet
}

// deferTerminate is a helper function to terminate the container when the test is finished.
func deferTerminate[T testcontainers.Container](container T, err error) (T, error) {
ginkgo.DeferCleanup(func(ctx context.Context) error {
DeferCleanup(func(ctx context.Context) error {
if container.IsRunning() {
return container.Terminate(ctx)
}
Expand All @@ -84,12 +42,13 @@ func deferTerminate[T testcontainers.Container](container T, err error) (T, erro
// startContainerWithNetwork starts the container with the given alias and attaches it to the test network.
// The container is wrapped with deferTerminate to terminate the container when the test is finished.
func startContainerWithNetwork(ctx context.Context, req testcontainers.ContainerRequest, alias string,
e2eNet *testcontainers.DockerNetwork,
) (testcontainers.Container, error) {
greq := testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
}
WithNetwork(ctx, alias).Customize(&greq)
withNetwork(alias, e2eNet).Customize(&greq)

return deferTerminate(testcontainers.GenericContainer(ctx, greq))
}
Expand Down Expand Up @@ -121,7 +80,6 @@ func getContainerHostPort(ctx context.Context, c testcontainers.Container, p nat
}

host, err = c.Host(ctx)

if err != nil {
return "", "", err
}
Expand Down Expand Up @@ -149,3 +107,8 @@ func getContainerLogs(ctx context.Context, c testcontainers.Container) (lines []

return nil, err
}

// withNetwork returns a CustomizeRequestOption which attaches the container to the given network with the given alias.
func withNetwork(alias string, e2eNet *testcontainers.DockerNetwork) testcontainers.CustomizeRequestOption {
return testNet.WithNetwork([]string{alias}, e2eNet)
}
Loading

0 comments on commit c3a319f

Please sign in to comment.