Skip to content

Commit

Permalink
wip: nettest: add framework for nesting network disruptions in protocols
Browse files Browse the repository at this point in the history
  • Loading branch information
roobre committed Sep 27, 2023
1 parent bde2dcb commit 56c650f
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 0 deletions.
4 changes: 4 additions & 0 deletions pkg/agent/protocol/nettest/containers/iptables/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM alpine:3.18

RUN apk update && apk add iptables

9 changes: 9 additions & 0 deletions pkg/agent/protocol/nettest/containers/redis-go/Dockerfile
Original file line number Diff line number Diff line change
@@ -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" ]
13 changes: 13 additions & 0 deletions pkg/agent/protocol/nettest/containers/redis-go/go.mod
Original file line number Diff line number Diff line change
@@ -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
)
17 changes: 17 additions & 0 deletions pkg/agent/protocol/nettest/containers/redis-go/go.sum
Original file line number Diff line number Diff line change
@@ -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=
41 changes: 41 additions & 0 deletions pkg/agent/protocol/nettest/containers/redis-go/main.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
112 changes: 112 additions & 0 deletions pkg/agent/protocol/nettest/redis/iptables_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
18 changes: 18 additions & 0 deletions pkg/agent/protocol/nettest/tlog/tlog.go
Original file line number Diff line number Diff line change
@@ -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)
}

0 comments on commit 56c650f

Please sign in to comment.