Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(k6):Add remote test scripts #2350

Merged
merged 10 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion docs/modules/k6.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,26 @@ k6.RunContainer(ctx, WithCmdOptions("--vus=10", "--duration=30s"), k6.WithTestSc

#### WithTestScript

Use the `WithTestScript` option to specify the test script to run. The path to the script must be an absolute path. This option copies the script file to the container and pass it to k6's `run` command. At least one `WithTestScript` option must be specified.
Use the `WithTestScript` option to specify the test script to run. The path to the script must be an absolute path. This option copies the script file to the container and pass it to k6's `run` command. At least one `WithTestScript` or `WithRemoteTestScript` option must be specified.

```golang
k6.RunContainer(ctx, k6.WithTestScript("/tests/test.js"))
```

#### WithRemoteTestScript

Use the `WithRemoteTestScript` option to specify the remote test script to run. The path to the remote script must be a http or https url. Basic authentication is supported. This option performs a HTTP `GET` to copy the remote file locally then copies the script file to the container and pass it to k6's `run` command. The default timeout for the `GET` is 60 seconds. Only javascript, or more specifically `Content-Type:text/javascript` is supported. At least one `WithTestScript` or `WithRemoteTestScript` option must be specified.

```golang
scriptUrl:="https://raw.githubusercontent.com/testcontainers/testcontainers-go/main/modules/k6/scripts/pass.js"

uri, _ := url.Parse(scriptUrl)
desc := k6.DownloadableFile{Uri: *uri , DownloadDir: t.TempDir()}
options := k6.WithRemoteTestScript(desc)

k6.RunContainer(ctx, k6.WithCache(), options)
```

### Container Methods

The K6 container does not expose any method.
81 changes: 77 additions & 4 deletions modules/k6/k6.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ package k6
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"time"

"github.com/docker/docker/api/types/mount"

Expand All @@ -17,17 +22,73 @@ type K6Container struct {
testcontainers.Container
}

type DownloadableFile struct {
Uri url.URL
DownloadDir string
User string
Password string
}

func (d *DownloadableFile) getDownloadPath() string {
baseName := path.Base(d.Uri.Path)
return path.Join(d.DownloadDir, baseName)

bearrito marked this conversation as resolved.
Show resolved Hide resolved
}

func downloadFileFromDescription(d DownloadableFile) error {

bearrito marked this conversation as resolved.
Show resolved Hide resolved
client := http.Client{Timeout: time.Second * 60}
req, err := http.NewRequest(http.MethodGet, d.Uri.String(), nil)
if err != nil {
return err
}

req.Header.Set("Content-Type", "text/javascript")
// Set up HTTPS request with basic authorization.
if d.User != "" && d.Password != "" {
req.SetBasicAuth(d.User, d.Password)
}

resp, err := client.Do(req)
if err != nil {
return err
}

downloadedFile, err := os.Create(d.getDownloadPath())
if err != nil {
return err
}
defer downloadedFile.Close()

_, err = io.Copy(downloadedFile, resp.Body)
return err

}

// WithTestScript mounts the given script into the ./test directory in the container
// and passes it to k6 as the test to run.
// The path to the script must be an absolute path
func WithTestScript(scriptPath string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
script := filepath.Base(scriptPath)
target := "/home/k6x/" + script

scriptBaseName := filepath.Base(scriptPath)
f, err := os.Open(scriptPath)
if err != nil {
panic("Cannot create reader for test file ")
}
return WithTestScriptReader(f, scriptBaseName)

}

// WithTestScriptReader copies files into the Container using the Reader API
// The script base name is not a path, neither absolute or relative and should
// be just the file name of the script
func WithTestScriptReader(reader io.Reader, scriptBaseName string) testcontainers.CustomizeRequestOption {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We must include these options in the docs (root-dir/docs/modules/k6.md). Let's include that it will only accept Javascript files and the configuration of the http client (timeout=60s, context type, etc) https://github.com/testcontainers/testcontainers-go/pull/2350/files#diff-19a4c472d6cc0154e74ab208f0a048d900eb0e6006b4365b853d56218d73cd5fR40-R50

The users of the module would like to know this option exists and how to use it, from our docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

opt := func(req *testcontainers.GenericContainerRequest) {
target := "/home/k6x/" + scriptBaseName
req.Files = append(
req.Files,
testcontainers.ContainerFile{
HostFilePath: scriptPath,
Reader: reader,
ContainerFilePath: target,
FileMode: 0o644,
},
Expand All @@ -36,6 +97,18 @@ func WithTestScript(scriptPath string) testcontainers.CustomizeRequestOption {
// add script to the k6 run command
req.Cmd = append(req.Cmd, target)
}
return opt
}

// WithRemoteTestScript takes a RemoteTestFileDescription and copies to container
func WithRemoteTestScript(d DownloadableFile) testcontainers.CustomizeRequestOption {

err := downloadFileFromDescription(d)
if err != nil {
panic("Not able to download required test script")
}

return WithTestScript(d.getDownloadPath())
}

// WithCmdOptions pass the given options to the k6 run command
Expand Down
34 changes: 30 additions & 4 deletions modules/k6/k6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package k6_test

import (
"context"
"net/url"
"path/filepath"
"strings"
"testing"

"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/k6"
)

Expand All @@ -24,19 +27,42 @@ func TestK6(t *testing.T) {
script: "fail.js",
expect: 108,
},
{
title: "Passing remote test",
script: "https://raw.githubusercontent.com/testcontainers/testcontainers-go/main/modules/k6/scripts/pass.js",
expect: 0,
},
{
title: "Failing remote test",
script: "https://raw.githubusercontent.com/testcontainers/testcontainers-go/main/modules/k6/scripts/fail.js",
expect: 108,
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.title, func(t *testing.T) {
ctx := context.Background()

absPath, err := filepath.Abs(filepath.Join("scripts", tc.script))
if err != nil {
t.Fatal(err)
var options testcontainers.CustomizeRequestOption
if !strings.HasPrefix(tc.script, "http") {
absPath, err := filepath.Abs(filepath.Join("scripts", tc.script))
if err != nil {
t.Fatal(err)
}
options = k6.WithTestScript(absPath)
} else {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

uri, err := url.Parse(tc.script)
mdelapenya marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
t.Fatal(err)
}

desc := k6.DownloadableFile{Uri: *uri, DownloadDir: t.TempDir()}
options = k6.WithRemoteTestScript(desc)
}

container, err := k6.RunContainer(ctx, k6.WithCache(), k6.WithTestScript(absPath))
container, err := k6.RunContainer(ctx, k6.WithCache(), options)
if err != nil {
t.Fatal(err)
}
Expand Down
Loading