From 56c650fccfa0295323dfe68aa9baedfbf80f84f2 Mon Sep 17 00:00:00 2001 From: Roberto Santalla Date: Wed, 20 Sep 2023 21:00:26 +0200 Subject: [PATCH 1/6] wip: nettest: add framework for nesting network disruptions in protocols --- .../nettest/containers/iptables/Dockerfile | 4 + .../nettest/containers/redis-go/Dockerfile | 9 ++ .../nettest/containers/redis-go/go.mod | 13 ++ .../nettest/containers/redis-go/go.sum | 17 +++ .../nettest/containers/redis-go/main.go | 41 +++++++ .../protocol/nettest/redis/iptables_test.go | 112 ++++++++++++++++++ pkg/agent/protocol/nettest/tlog/tlog.go | 18 +++ 7 files changed, 214 insertions(+) create mode 100644 pkg/agent/protocol/nettest/containers/iptables/Dockerfile create mode 100644 pkg/agent/protocol/nettest/containers/redis-go/Dockerfile create mode 100644 pkg/agent/protocol/nettest/containers/redis-go/go.mod create mode 100644 pkg/agent/protocol/nettest/containers/redis-go/go.sum create mode 100644 pkg/agent/protocol/nettest/containers/redis-go/main.go create mode 100644 pkg/agent/protocol/nettest/redis/iptables_test.go create mode 100644 pkg/agent/protocol/nettest/tlog/tlog.go diff --git a/pkg/agent/protocol/nettest/containers/iptables/Dockerfile b/pkg/agent/protocol/nettest/containers/iptables/Dockerfile new file mode 100644 index 00000000..d4dace98 --- /dev/null +++ b/pkg/agent/protocol/nettest/containers/iptables/Dockerfile @@ -0,0 +1,4 @@ +FROM alpine:3.18 + +RUN apk update && apk add iptables + diff --git a/pkg/agent/protocol/nettest/containers/redis-go/Dockerfile b/pkg/agent/protocol/nettest/containers/redis-go/Dockerfile new file mode 100644 index 00000000..b0ffe7a4 --- /dev/null +++ b/pkg/agent/protocol/nettest/containers/redis-go/Dockerfile @@ -0,0 +1,9 @@ +FROM golang:1.21-bookworm as build + +WORKDIR /app +COPY . . +RUN go build -o redis-go + +FROM debian:bookworm +COPY --from=build /app/redis-go /bin +ENTRYPOINT [ "/bin/redis-go" ] diff --git a/pkg/agent/protocol/nettest/containers/redis-go/go.mod b/pkg/agent/protocol/nettest/containers/redis-go/go.mod new file mode 100644 index 00000000..961db484 --- /dev/null +++ b/pkg/agent/protocol/nettest/containers/redis-go/go.mod @@ -0,0 +1,13 @@ +module redisgo + +go 1.19 + +require ( + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect +) + +require ( + github.com/go-redis/redis/v8 v8.11.5 + github.com/onsi/gomega v1.27.10 // indirect +) diff --git a/pkg/agent/protocol/nettest/containers/redis-go/go.sum b/pkg/agent/protocol/nettest/containers/redis-go/go.sum new file mode 100644 index 00000000..da4a8515 --- /dev/null +++ b/pkg/agent/protocol/nettest/containers/redis-go/go.sum @@ -0,0 +1,17 @@ +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/pkg/agent/protocol/nettest/containers/redis-go/main.go b/pkg/agent/protocol/nettest/containers/redis-go/main.go new file mode 100644 index 00000000..db9834b8 --- /dev/null +++ b/pkg/agent/protocol/nettest/containers/redis-go/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "context" + "log" + "os" + "time" + + "github.com/go-redis/redis/v8" +) + +func main() { + rdb := redis.NewClient(&redis.Options{ + Addr: os.Args[1], + Password: "", // no password set + DB: 0, // use default DB + }) + + ctx := context.Background() + + err := rdb.Set(ctx, "counter", 0.0, 0).Err() + if err != nil { + log.Fatalf("creating redis key: %v", err) + } + + for { + err = rdb.Incr(ctx, "counter").Err() + if err != nil { + log.Fatalf("incrementing counter: %v", err) + } + + cmd := rdb.Get(ctx, "counter") + if err := cmd.Err(); err != nil { + log.Fatalf("getting current value: %v", err) + } + + current, _ := cmd.Float64() + log.Printf("Current value: %f", current) + time.Sleep(time.Second) + } +} diff --git a/pkg/agent/protocol/nettest/redis/iptables_test.go b/pkg/agent/protocol/nettest/redis/iptables_test.go new file mode 100644 index 00000000..aac29cf3 --- /dev/null +++ b/pkg/agent/protocol/nettest/redis/iptables_test.go @@ -0,0 +1,112 @@ +package redis_test + +import ( + "context" + "os" + "path/filepath" + "testing" + "time" + + "github.com/docker/docker/api/types/container" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" +) + +const iptablesRule = "INPUT -p tcp --dport 6379 -j REJECT --reject-with tcp-reset" + +func Test_Redis(t *testing.T) { + t.Parallel() + + if os.Getenv("NETTEST") == "" { + t.Skip("Skipping network protocol test as NETTEST is not set") + } + + ctx := context.TODO() + + redis, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ProviderType: testcontainers.ProviderDocker, + ContainerRequest: testcontainers.ContainerRequest{ + Networks: []string{}, + Image: "redis", + ExposedPorts: []string{"6379/tcp"}, + WaitingFor: wait.ForExposedPort(), + }, + Started: true, + }) + if err != nil { + t.Fatalf("failed to create redis container %v", err) + } + + t.Cleanup(func() { + _ = redis.Terminate(ctx) + }) + + iptables, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ProviderType: testcontainers.ProviderDocker, + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ + Dockerfile: "Dockerfile", + Context: filepath.Join("..", "containers", "iptables"), + }, + NetworkMode: container.NetworkMode("container:" + redis.GetContainerID()), + Cmd: []string{"/bin/sh", "-c", "echo ready && sleep infinity"}, + Privileged: true, + WaitingFor: wait.ForLog("ready"), + }, + Started: true, + }) + if err != nil { + t.Fatalf("failed to create agent container %v", err) + } + + t.Cleanup(func() { + _ = iptables.Terminate(ctx) + }) + + redisGo, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ProviderType: testcontainers.ProviderDocker, + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ + Dockerfile: "Dockerfile", + Context: filepath.Join("..", "containers", "redis-go"), + }, + Cmd: []string{"localhost:6379"}, + NetworkMode: container.NetworkMode("container:" + redis.GetContainerID()), + }, + Started: true, + }) + if err != nil { + t.Fatalf("failed to create agent container %v", err) + } + + t.Cleanup(func() { + _ = redisGo.Terminate(ctx) + }) + + // TODO:Follow container under test logs. Currently doing so hangs out the test forever. + + redisGoStatus, err := redisGo.State(ctx) + if err != nil { + t.Fatal(err) + } + if !redisGoStatus.Running { + t.Fatalf("Redis client container failed") + } + + //nolint:errcheck,gosec // Error checking elided for brevity. TODO: Wrap this in a helper function. + iptables.Exec(context.TODO(), []string{"/bin/sh", "-c", "iptables -I " + iptablesRule}) + + time.Sleep(2 * time.Second) + + //nolint:errcheck,gosec // Error checking elided for brevity. TODO: Wrap this in a helper function. + iptables.Exec(context.TODO(), []string{"/bin/sh", "-c", "iptables -D " + iptablesRule}) + + redisGoStatus, err = redisGo.State(ctx) + if err != nil { + t.Fatal(err) + } + + if !redisGoStatus.Running { + t.Fatalf("Redis client container failed") + } +} diff --git a/pkg/agent/protocol/nettest/tlog/tlog.go b/pkg/agent/protocol/nettest/tlog/tlog.go new file mode 100644 index 00000000..ff9d9aae --- /dev/null +++ b/pkg/agent/protocol/nettest/tlog/tlog.go @@ -0,0 +1,18 @@ +// Package tlog implements a testcontainers log handler that mirrors logs to a test logger. +package tlog + +import ( + "testing" + + "github.com/testcontainers/testcontainers-go" +) + +// Mirror is a testcontainers log adapter that mirrors container output to testing.T.Log. +type Mirror struct { + T *testing.T +} + +// Accept implements the testcontainers adapter interface by writing received output to the test logger. +func (m Mirror) Accept(log testcontainers.Log) { + m.T.Logf("%s: %s", log.LogType, log.Content) +} From 2a630937043388d2d8b0070c073d0a40c2d5f561 Mon Sep 17 00:00:00 2001 From: Roberto Santalla Date: Fri, 22 Sep 2023 13:11:19 +0200 Subject: [PATCH 2/6] nettest: small changes and cleanup --- .../protocol/nettest/redis/iptables_test.go | 21 ++++++++++++++----- pkg/agent/protocol/nettest/tlog/tlog.go | 11 ++++++++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/pkg/agent/protocol/nettest/redis/iptables_test.go b/pkg/agent/protocol/nettest/redis/iptables_test.go index aac29cf3..8cb68623 100644 --- a/pkg/agent/protocol/nettest/redis/iptables_test.go +++ b/pkg/agent/protocol/nettest/redis/iptables_test.go @@ -10,6 +10,8 @@ import ( "github.com/docker/docker/api/types/container" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" + + "github.com/grafana/xk6-disruptor/pkg/agent/protocol/nettest/tlog" ) const iptablesRule = "INPUT -p tcp --dport 6379 -j REJECT --reject-with tcp-reset" @@ -26,7 +28,6 @@ func Test_Redis(t *testing.T) { redis, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ProviderType: testcontainers.ProviderDocker, ContainerRequest: testcontainers.ContainerRequest{ - Networks: []string{}, Image: "redis", ExposedPorts: []string{"6379/tcp"}, WaitingFor: wait.ForExposedPort(), @@ -79,11 +80,21 @@ func Test_Redis(t *testing.T) { t.Fatalf("failed to create agent container %v", err) } - t.Cleanup(func() { - _ = redisGo.Terminate(ctx) - }) + // TODO: Calling terminate with a log attached makes the test hang. + // See: https://github.com/testcontainers/testcontainers-go/issues/1669 + // t.Cleanup(func() { + // _ = redisGo.Terminate(ctx) + // }) - // TODO:Follow container under test logs. Currently doing so hangs out the test forever. + redisGo.FollowOutput(tlog.Mirror{T: t, Name: "redis-go"}) + err = redisGo.StartLogProducer(ctx) + if err != nil { + t.Fatal(err) + } + // TODO: See above. + // t.Cleanup(func() { + // redisGo.StopLogProducer() + // }) redisGoStatus, err := redisGo.State(ctx) if err != nil { diff --git a/pkg/agent/protocol/nettest/tlog/tlog.go b/pkg/agent/protocol/nettest/tlog/tlog.go index ff9d9aae..7ec06e0c 100644 --- a/pkg/agent/protocol/nettest/tlog/tlog.go +++ b/pkg/agent/protocol/nettest/tlog/tlog.go @@ -9,10 +9,17 @@ import ( // Mirror is a testcontainers log adapter that mirrors container output to testing.T.Log. type Mirror struct { - T *testing.T + T *testing.T + Name string } // Accept implements the testcontainers adapter interface by writing received output to the test logger. func (m Mirror) Accept(log testcontainers.Log) { - m.T.Logf("%s: %s", log.LogType, log.Content) + prefix := "" + if m.Name != "" { + prefix += m.Name + "/" + } + prefix += log.LogType + + m.T.Logf("%s: %s", prefix, log.Content) } From bb2bca2ce8b4b877b5926a76c080230fcf7c68c6 Mon Sep 17 00:00:00 2001 From: Roberto Santalla Date: Mon, 2 Oct 2023 12:35:39 +0200 Subject: [PATCH 3/6] nettest: rename and widen tlog package to util --- .../protocol/nettest/redis/iptables_test.go | 8 +-- pkg/agent/protocol/nettest/tlog/tlog.go | 25 -------- pkg/agent/protocol/nettest/util/util.go | 58 +++++++++++++++++++ 3 files changed, 62 insertions(+), 29 deletions(-) delete mode 100644 pkg/agent/protocol/nettest/tlog/tlog.go create mode 100644 pkg/agent/protocol/nettest/util/util.go diff --git a/pkg/agent/protocol/nettest/redis/iptables_test.go b/pkg/agent/protocol/nettest/redis/iptables_test.go index 8cb68623..d40e8f08 100644 --- a/pkg/agent/protocol/nettest/redis/iptables_test.go +++ b/pkg/agent/protocol/nettest/redis/iptables_test.go @@ -11,12 +11,12 @@ import ( "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" - "github.com/grafana/xk6-disruptor/pkg/agent/protocol/nettest/tlog" + "github.com/grafana/xk6-disruptor/pkg/agent/protocol/nettest/util" ) const iptablesRule = "INPUT -p tcp --dport 6379 -j REJECT --reject-with tcp-reset" -func Test_Redis(t *testing.T) { +func Test_Redis_Iptables(t *testing.T) { t.Parallel() if os.Getenv("NETTEST") == "" { @@ -83,10 +83,10 @@ func Test_Redis(t *testing.T) { // TODO: Calling terminate with a log attached makes the test hang. // See: https://github.com/testcontainers/testcontainers-go/issues/1669 // t.Cleanup(func() { - // _ = redisGo.Terminate(ctx) + // _ = redisGo.Terminate(ctx) // }) - redisGo.FollowOutput(tlog.Mirror{T: t, Name: "redis-go"}) + redisGo.FollowOutput(util.Mirror{T: t, Name: "redis-go"}) err = redisGo.StartLogProducer(ctx) if err != nil { t.Fatal(err) diff --git a/pkg/agent/protocol/nettest/tlog/tlog.go b/pkg/agent/protocol/nettest/tlog/tlog.go deleted file mode 100644 index 7ec06e0c..00000000 --- a/pkg/agent/protocol/nettest/tlog/tlog.go +++ /dev/null @@ -1,25 +0,0 @@ -// Package tlog implements a testcontainers log handler that mirrors logs to a test logger. -package tlog - -import ( - "testing" - - "github.com/testcontainers/testcontainers-go" -) - -// Mirror is a testcontainers log adapter that mirrors container output to testing.T.Log. -type Mirror struct { - T *testing.T - Name string -} - -// Accept implements the testcontainers adapter interface by writing received output to the test logger. -func (m Mirror) Accept(log testcontainers.Log) { - prefix := "" - if m.Name != "" { - prefix += m.Name + "/" - } - prefix += log.LogType - - m.T.Logf("%s: %s", prefix, log.Content) -} diff --git a/pkg/agent/protocol/nettest/util/util.go b/pkg/agent/protocol/nettest/util/util.go new file mode 100644 index 00000000..da0cc706 --- /dev/null +++ b/pkg/agent/protocol/nettest/util/util.go @@ -0,0 +1,58 @@ +// Package util implements misc utilities for network tests +package util + +import ( + "bufio" + "context" + "strings" + "testing" + + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/exec" +) + +// Mirror is a testcontainers log adapter that mirrors container output to testing.T.Log. +type Mirror struct { + T *testing.T + Name string +} + +// Accept implements the testcontainers adapter interface by writing received output to the test logger. +func (m Mirror) Accept(log testcontainers.Log) { + prefix := "" + if m.Name != "" { + prefix += m.Name + "/" + } + prefix += log.LogType + + m.T.Logf("%s: %s", prefix, log.Content) +} + +// TCExec runs a command on a container in a shell, echoing the output and failing the test if it cannot be run. +func TCExec(t *testing.T, c testcontainers.Container, shellcmd string) { + t.Helper() + + cmd := []string{"/bin/sh", "-c", shellcmd} + + t.Logf("%s: running %q", c.GetContainerID(), shellcmd) + rc, out, err := c.Exec(context.TODO(), cmd, exec.Multiplexed()) + if err != nil { + t.Fatalf("running command on %s: %v", c.GetContainerID(), err) + } + + if rc != 0 { + t.Errorf("%s:%s exited with %d", c.GetContainerID(), cmd, rc) + } + + go func() { + buf := bufio.NewReader(out) + for { + line, err := buf.ReadString('\n') + if err != nil { + return + } + + t.Logf("%s:%s: %s", c.GetContainerID(), cmd, strings.TrimSpace(line)) + } + }() +} From 27ee731435ccb42cf7f953f467135fdf22666d23 Mon Sep 17 00:00:00 2001 From: Roberto Santalla Date: Mon, 2 Oct 2023 12:35:54 +0200 Subject: [PATCH 4/6] nettest: add tcpkill container and test for redis --- .../nettest/containers/tcpkill/Dockerfile | 3 + .../protocol/nettest/redis/tcpkill_test.go | 126 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 pkg/agent/protocol/nettest/containers/tcpkill/Dockerfile create mode 100644 pkg/agent/protocol/nettest/redis/tcpkill_test.go diff --git a/pkg/agent/protocol/nettest/containers/tcpkill/Dockerfile b/pkg/agent/protocol/nettest/containers/tcpkill/Dockerfile new file mode 100644 index 00000000..e5c6503e --- /dev/null +++ b/pkg/agent/protocol/nettest/containers/tcpkill/Dockerfile @@ -0,0 +1,3 @@ +FROM debian:bookworm + +RUN apt update && apt install -y dsniff diff --git a/pkg/agent/protocol/nettest/redis/tcpkill_test.go b/pkg/agent/protocol/nettest/redis/tcpkill_test.go new file mode 100644 index 00000000..c896b409 --- /dev/null +++ b/pkg/agent/protocol/nettest/redis/tcpkill_test.go @@ -0,0 +1,126 @@ +package redis_test + +import ( + "context" + "path/filepath" + "testing" + "time" + + "github.com/docker/docker/api/types/container" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" + + "github.com/grafana/xk6-disruptor/pkg/agent/protocol/nettest/util" +) + +func Test_Redis_TCPKill(t *testing.T) { + t.Parallel() + + // if os.Getenv("NETTEST") == "" { + // t.Skip("Skipping network protocol test as NETTEST is not set") + // } + + ctx := context.TODO() + + redis, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ProviderType: testcontainers.ProviderDocker, + ContainerRequest: testcontainers.ContainerRequest{ + Image: "redis", + ExposedPorts: []string{"6379/tcp"}, + WaitingFor: wait.ForExposedPort(), + }, + Started: true, + }) + if err != nil { + t.Fatalf("failed to create redis container %v", err) + } + + t.Cleanup(func() { + _ = redis.Terminate(ctx) + }) + + redisGo, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ProviderType: testcontainers.ProviderDocker, + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ + Dockerfile: "Dockerfile", + Context: filepath.Join("..", "containers", "redis-go"), + }, + Cmd: []string{"localhost:6379"}, + NetworkMode: container.NetworkMode("container:" + redis.GetContainerID()), + }, + Started: true, + }) + if err != nil { + t.Fatalf("failed to create agent container %v", err) + } + + // TODO: Calling terminate with a log attached makes the test hang. + // See: https://github.com/testcontainers/testcontainers-go/issues/1669 + // t.Cleanup(func() { + // _ = redisGo.Terminate(ctx) + // }) + + redisGo.FollowOutput(util.Mirror{T: t, Name: "redis-go"}) + err = redisGo.StartLogProducer(ctx) + if err != nil { + t.Fatal(err) + } + // TODO: See above. + // t.Cleanup(func() { + // redisGo.StopLogProducer() + // }) + + redisGoStatus, err := redisGo.State(ctx) + if err != nil { + t.Fatal(err) + } + if !redisGoStatus.Running { + t.Fatalf("Redis client container failed") + } + + time.Sleep(3 * time.Second) + + tcpkill, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ProviderType: testcontainers.ProviderDocker, + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ + Dockerfile: "Dockerfile", + Context: filepath.Join("..", "containers", "tcpkill"), + }, + NetworkMode: container.NetworkMode("container:" + redis.GetContainerID()), + Cmd: []string{"/bin/sh", "-c", "tcpkill -i any -5 port 6379"}, + Privileged: true, + WaitingFor: wait.ForLog("tcpkill: listening"), + }, + Started: true, + }) + if err != nil { + t.Fatalf("failed to create tcpkill container %v", err) + } + + t.Cleanup(func() { + _ = tcpkill.Terminate(ctx) + }) + + tcpkill.FollowOutput(util.Mirror{T: t, Name: "tcpkill"}) + err = tcpkill.StartLogProducer(ctx) + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { + tcpkill.StopLogProducer() + }) + + time.Sleep(7 * time.Second) + + redisGoStatus, err = redisGo.State(ctx) + if err != nil { + t.Fatal(err) + } + + if !redisGoStatus.Running { + t.Fatalf("Redis client container failed") + } +} From 25cbc7ad7f4c8785c4ce5faa5419a5b597e86e6d Mon Sep 17 00:00:00 2001 From: Roberto Santalla Date: Tue, 3 Oct 2023 12:07:56 +0200 Subject: [PATCH 5/6] nettest: add test for cutter (currently broken) --- .../nettest/containers/cutter/Dockerfile | 9 ++ .../protocol/nettest/redis/cutter_test.go | 126 ++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 pkg/agent/protocol/nettest/containers/cutter/Dockerfile create mode 100644 pkg/agent/protocol/nettest/redis/cutter_test.go diff --git a/pkg/agent/protocol/nettest/containers/cutter/Dockerfile b/pkg/agent/protocol/nettest/containers/cutter/Dockerfile new file mode 100644 index 00000000..0f769482 --- /dev/null +++ b/pkg/agent/protocol/nettest/containers/cutter/Dockerfile @@ -0,0 +1,9 @@ +FROM debian:bookworm + +RUN apt update && apt install -y curl zstd + +# Upstream for cutter website is unavailable at the time of writing, the arch linux package is the most stable +# source I was able to find. +RUN curl -vL https://archive.archlinux.org/packages/c/cutter/cutter-1.04-3-x86_64.pkg.tar.zst \ + | tar --zstd -xvC / + diff --git a/pkg/agent/protocol/nettest/redis/cutter_test.go b/pkg/agent/protocol/nettest/redis/cutter_test.go new file mode 100644 index 00000000..750aa293 --- /dev/null +++ b/pkg/agent/protocol/nettest/redis/cutter_test.go @@ -0,0 +1,126 @@ +package redis_test + +import ( + "context" + "os" + "path/filepath" + "testing" + "time" + + "github.com/docker/docker/api/types/container" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" + + "github.com/grafana/xk6-disruptor/pkg/agent/protocol/nettest/util" +) + +func Test_Redis_Cutter(t *testing.T) { + t.Parallel() + + if os.Getenv("NETTEST") == "" { + t.Skip("Skipping network protocol test as NETTEST is not set") + } + + ctx := context.TODO() + + redis, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ProviderType: testcontainers.ProviderDocker, + ContainerRequest: testcontainers.ContainerRequest{ + Image: "redis", + ExposedPorts: []string{"6379/tcp"}, + WaitingFor: wait.ForExposedPort(), + }, + Started: true, + }) + if err != nil { + t.Fatalf("failed to create redis container %v", err) + } + + t.Cleanup(func() { + _ = redis.Terminate(ctx) + }) + + redisGo, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ProviderType: testcontainers.ProviderDocker, + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ + Dockerfile: "Dockerfile", + Context: filepath.Join("..", "containers", "redis-go"), + }, + Cmd: []string{"localhost:6379"}, + NetworkMode: container.NetworkMode("container:" + redis.GetContainerID()), + }, + Started: true, + }) + if err != nil { + t.Fatalf("failed to create agent container %v", err) + } + + // TODO: Calling terminate with a log attached makes the test hang. + // See: https://github.com/testcontainers/testcontainers-go/issues/1669 + // t.Cleanup(func() { + // _ = redisGo.Terminate(ctx) + // }) + + redisGo.FollowOutput(util.Mirror{T: t, Name: "redis-go"}) + err = redisGo.StartLogProducer(ctx) + if err != nil { + t.Fatal(err) + } + // TODO: See above. + // t.Cleanup(func() { + // redisGo.StopLogProducer() + // }) + + redisGoStatus, err := redisGo.State(ctx) + if err != nil { + t.Fatal(err) + } + if !redisGoStatus.Running { + t.Fatalf("Redis client container failed") + } + + time.Sleep(3 * time.Second) + + cutter, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ProviderType: testcontainers.ProviderDocker, + ContainerRequest: testcontainers.ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ + Dockerfile: "Dockerfile", + Context: filepath.Join("..", "containers", "cutter"), + }, + NetworkMode: container.NetworkMode("container:" + redis.GetContainerID()), + Cmd: []string{"/bin/sh", "-c", "tcp-cutter 127.0.0.1 6379"}, + Privileged: true, + }, + Started: true, + }) + if err != nil { + t.Fatalf("failed to create cutter container %v", err) + } + + // t.Cleanup(func() { + // _ = cutter.Terminate(ctx) + // }) + + cutter.FollowOutput(util.Mirror{T: t, Name: "cutter"}) + err = cutter.StartLogProducer(ctx) + if err != nil { + t.Fatal(err) + } + + // t.Cleanup(func() { + // cutter.StopLogProducer() + // }) + + time.Sleep(2 * time.Second) + + redisGoStatus, err = redisGo.State(ctx) + if err != nil { + t.Fatal(err) + } + + if !redisGoStatus.Running { + t.Fatalf("Redis client container failed") + } +} From 1bb054e7ce766595d9a63ed322d3a40590ecf651 Mon Sep 17 00:00:00 2001 From: Roberto Santalla Date: Tue, 3 Oct 2023 12:08:17 +0200 Subject: [PATCH 6/6] nettest: add tcpdump container for debugging --- pkg/agent/protocol/nettest/containers/tcpdump/Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 pkg/agent/protocol/nettest/containers/tcpdump/Dockerfile diff --git a/pkg/agent/protocol/nettest/containers/tcpdump/Dockerfile b/pkg/agent/protocol/nettest/containers/tcpdump/Dockerfile new file mode 100644 index 00000000..44d128fd --- /dev/null +++ b/pkg/agent/protocol/nettest/containers/tcpdump/Dockerfile @@ -0,0 +1,5 @@ +FROM debian:bookworm + +RUN apt update && apt install -y tcpdump + +ENTRYPOINT ["/bin/tcpdump"]