Skip to content

Commit

Permalink
Merge pull request #322 from grafana/agent-integration-tests
Browse files Browse the repository at this point in the history
POC: agent integration tests using test containers
  • Loading branch information
pablochacin authored Aug 29, 2023
2 parents 6b5e14b + fb6d1f5 commit 09fcf9e
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 12 deletions.
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ agent_image ?= ghcr.io/grafana/xk6-disruptor-agent:latest

all: build

agent-image: build-agent test
agent-image: build-agent
docker build --build-arg TARGETARCH=${arch} -t $(agent_image) images/agent

disruptor-image:
Expand All @@ -21,6 +21,7 @@ build-e2e:
go build -tags e2e -o build/e2e-cluster ./cmd/e2e-cluster/main.go

build-agent:
go test ./pkg/agent/...
GOOS=linux CGO_ENABLED=0 go build -o images/agent/build/xk6-disruptor-agent-linux-${arch} ./cmd/agent

clean:
Expand All @@ -44,6 +45,11 @@ e2e-setup: build-e2e
format:
go fmt ./...

integration-agent: agent-image
go test -tags integration ./pkg/agent/...

integration: integration-agent

# Running with -buildvcs=false works around the issue of `go list all` failing when git, which runs as root inside
# the container, refuses to operate on the disruptor source tree as it is not owned by the same user (root).
lint:
Expand Down
19 changes: 10 additions & 9 deletions docs/01-development/01-contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ $ git clone https://github.com/grafana/xk6-disruptor.git
$ cd xk6-disruptor
```

### Makefile

Most of the development tasks can be executed using `make` targets:
* `agent-image`: builds the `xk6-disruptor-agent` image locally
* `build`: builds k6 with the `xk6-disruptor` extension
* `clean`: removes local build and other work directories
* `e2e`: executes the end-to-end tests. These tests can take several minutes.
* `test`: executes unit tests
* `lint`: runs the linter
## Integration tests

Integration tests are implemented in the same packages than the component they test. Execution is conditioned using the `integration` build tag.

In order to run integration tests (if any) for a package, use the following command:

```sh
go test -tags integration ./path/to/package/...
```


### Extension/agent image versions dependencies

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ module github.com/grafana/xk6-disruptor
go 1.19

require (
github.com/docker/docker v24.0.5+incompatible
github.com/dop251/goja v0.0.0-20230621100801-7749907a8a20
github.com/google/go-cmp v0.5.9
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.5.0
github.com/testcontainers/testcontainers-go v0.23.0
go.k6.io/k6 v0.46.0
k8s.io/api v0.27.4
k8s.io/apimachinery v0.27.4
Expand All @@ -23,7 +25,6 @@ require (
github.com/containerd/containerd v1.7.3 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v24.0.5+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
Expand All @@ -38,7 +39,6 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
github.com/opencontainers/runc v1.1.5 // indirect
github.com/testcontainers/testcontainers-go v0.23.0 // indirect
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/tools v0.7.0 // indirect
Expand Down
152 changes: 152 additions & 0 deletions pkg/agent/integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//go:build integration
// +build integration

package agent

import (
"context"
"fmt"
"net/http"
"testing"

"github.com/docker/docker/api/types/container"
testcontainers "github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
)

func injectHTTPError(rate float64, code int, upstream string) []string {
return []string{
"xk6-disruptor-agent",
"http",
"--duration",
"300s",
"--rate",
fmt.Sprintf("%.2f", rate),
"--error",
fmt.Sprintf("%d", code),
"--port",
"8080",
"--target",
"80",
"--upstream-host",
upstream,
}
}

func Test_HTTPFaultInjection(t *testing.T) {
t.Parallel()

testCases := []struct {
test string
rate float64
code int
request int
expect int
}{
{
test: "inject 418 error",
rate: 1.0,
code: 418,
request: 200,
expect: 418,
},
{
test: "inject no error",
rate: 0.0,
code: 0,
request: 200,
expect: 200,
},
{
test: "handle upstream error",
rate: 0.0,
code: 0,
request: 500,
expect: 500,
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.test, func(t *testing.T) {
t.Parallel()

ctx := context.Background()

gcr := testcontainers.GenericContainerRequest{
ProviderType: testcontainers.ProviderDocker,
ContainerRequest: testcontainers.ContainerRequest{
Image: "kennethreitz/httpbin",
ExposedPorts: []string{
"80",
},
WaitingFor: wait.ForExposedPort(),
},
Started: true,
}
httpbin, err := testcontainers.GenericContainer(ctx, gcr)
if err != nil {
t.Fatalf("failed to create httpbin container %v", err)
}

t.Cleanup(func() {
_ = httpbin.Terminate(ctx)
})

httpbinIP, err := httpbin.ContainerIP(ctx)
if err != nil {
t.Fatalf("failed to get httpbin IP:\n%v", err)
}

// make the agent run using the same stack than the httpbin container
httpbinNetwork := container.NetworkMode("container:" + httpbin.GetContainerID())
gcr = testcontainers.GenericContainerRequest{
ProviderType: testcontainers.ProviderDocker,
ContainerRequest: testcontainers.ContainerRequest{
Image: "ghcr.io/grafana/xk6-disruptor-agent",
NetworkMode: httpbinNetwork,
Cmd: injectHTTPError(tc.rate, tc.code, httpbinIP),
Privileged: true,
// TODO: find a better way for checking the agent is ready
WaitingFor: wait.ForExec([]string{"pgrep", "xk6-disruptor-agent"}),
},
Started: true,
}

agent, err := testcontainers.GenericContainer(ctx, gcr)
if err != nil {
t.Fatalf("failed to create agent container %v", err)
}

t.Cleanup(func() {
_ = agent.Terminate(ctx)
})

httpPort, err := httpbin.MappedPort(ctx, "80")
if err != nil {
t.Fatalf("failed to get httpbin port:\n%v", err)
}

// access httpbin
url := fmt.Sprintf("http://localhost:%s/status/%d", httpPort.Port(), tc.request)

request, err := http.NewRequest("GET", url, nil)
if err != nil {
t.Fatalf("failed to create request %v", err)
}

resp, err := http.DefaultClient.Do(request)
if err != nil {
t.Fatalf("failed request to %q: %v", url, err)
}

defer func() {
_ = resp.Body.Close()
}()

if resp.StatusCode != tc.expect {
t.Fatalf("expected status code %d but %d received", tc.expect, resp.StatusCode)
}
})
}
}

0 comments on commit 09fcf9e

Please sign in to comment.