k6 v0.29.0 introduced xk6 and k6 extensions to the k6 community. 🎁🎉💪
You can now extend the functionality of k6 using Go-based k6 extensions and import them as JS modules in your k6 script.
This feature opens the gates for anyone to use existing k6 extensions and write custom Go extensions for special requirements.
- lack of JS support for system APIs.
- performance penalties due to the JS runtime.
k6 extensions overcome these limitations. They benefit from having excellent performance and the flexibility of using any Go libraries.
xk6 and k6 extensions provide the foundation for unlocking a lot of functionality for the k6 ecosystem.
Traditionally, if you wanted to extend k6, you had to fork the k6 repository and submit a PR with your changes. Merging a change that could potentially affect thousands of other k6 users or introduce maintenance overheads is always a difficult proposition that k6 maintainers are very cautious about, which in some cases can cause delays in introducing new features.
Extensions allow the community to innovate and enable faster development for:
- Building new k6 integrations.
- Adding further protocol support and clients.
- Creating new APIs for high-performant or custom functions.
Extensions will accelerate the innovation of the k6 ecosystem providing a framework to validate new ideas and technologies. Finally, the k6 core team will consider extensions with strong community interest and a solid foundation for "graduation" and becoming part of k6.
The core k6 project is licensed under the GNU AGPLv3. This means that while extensions may use a GPL-compatible license such as MIT/X11, when the extension is built with xk6 the product will be licensed as AGPLv3.
To avoid any legal concerns we suggest extension authors use AGPLv3 or a highly-compatible license such as the Apache 2.0 license.
How xk6 works
xk6 is an experimental framework for extending k6 that allows you to build a custom k6 binary by adding any combination of extensions easily, without any need to know how to code. For example, building a k6 v0.29.0 binary that allows you to use the SQL and Kafka extensions in your test scripts is as simple as:
This will build a k6 binary you can then use to run custom scripts that use the additional functionality.
A couple of things to note here:
- As binaries produced by xk6 can have any number of extensions and functionality, they're not supported in the k6 Cloud. You can however run any k6 version locally, bundled with any extensions you want by xk6, and have it output its metrics to k6 Cloud!
Tutorial - Creating a Redis extension
This short tutorial will walk you through the steps to build a k6 extension for Redis and use it in your k6 test.
Creating the k6 extension
A good starting point is always seeing how existing extensions work, but in this section we're going to guide you through the steps for creating a new extension from scratch.
xk6, like k6 and Go itself, is cross-platform, so while these examples are written with Linux in mind, they should work equally as well on macOS or Windows.
To start, you'll need to setup some prerequisites:
- The latest version of Go for your system. Consider using a version manager like g.
- Git is needed to pull all dependencies.
- Your favorite text editor, of course!
Since k6 extensions are plain Go modules in standard Git repositories, let's first initialize both:
We suggest extension authors follow the naming convention of xk6-<short name> to keep it consistent with current extensions, but this is optional and not validated in any way.
Next let's create our main Go module. Save the following as redis.go:
A few things to note here:
- We register the extension in the init() function, which will be called once when this package is first imported. k6 extensions are required to have a k6/x/ prefix in their import path in order to distinguish them from other built-in modules, and the full path must be unique in each build of k6.
- The XClient method uses a feature of the Go-JS bridge in k6 that exposes a more idiomatic constructor API to JS if a method begins with an X and uses the js/common functions to bind an object to the JS runtime. This is not required, but it's preferable so that scripts can use a more natural new redis.Client() instead of e.g. redis.NewClient().
- When a client is created we store the reference to the underlying redis.Client instance. We could choose to return this instance as is, but exposing the Go API directly to JS would make some methods difficult to work with (because of the use of context.Context and other Go APIs), so we chose to wrap it instead.
- We expose only the Get and Set commands here, but other commands can be easily added.
- The error returned from the Get method will be handled by the Go-JS bridge in k6 and only a single value (string in this case) will be returned in JS. If err is not nil, it will be thrown with the appropriate message in the JS script.
This is pretty much it for the extension implementation! The only thing left is to run go mod vendor to vendor all dependencies, commit all your changes to the Git repo, and optionally publish it to GitHub or elsewhere. Publishing is not necessary for building the k6 binary, as xk6 can work with a local directory as well, but the k6 community would appreciate it if you did. :)
If you do publish it on GitHub, make sure to add the xk6 topic to the repo so that other users can easily find it.
Building the extension with xk6
First install xk6 with:
Make sure to run this outside of the k6 or xk6-redis directories to prevent Go from adding it as a dependency of your project.
Next, let's build the k6 binary. To use the published version of the extension run:
Or if you're working with a local directory run the following, replacing the path as needed:
The first argument to xk6 build is the k6 version to use, so instead of v0.29.0 you can use any branch name or commit SHA here as well, as long as it's newer than v0.29.0 since this was the first version to support xk6.
Running the above should output something like:
And produce a k6 binary in the current directory.
Using the extension
Now we can write our k6 test script that uses the Redis extension.
Create a test.js file with the following contents:
Notice that we create the Redis client in the init context, passing it the server address and some options for demostration purposes. Then in the default function we set a key and log the retrieved value.
Speaking of the server, before you run this script ensure that Redis is actually running of course. You can do this easily with Docker and the official Redis image by running:
Now we can finally run our test script with:
Which should output something like:
We can also confirm the key was written with redis-cli:
Existing k6 extensions
The k6 extension ecosystem is still in its infancy, but we believe the tooling is approachable enough for it to grow beyond our expectations. For now, here are a few extensions written by the k6 team and passionate users, and you can always find an up-to-date list by checking out the xk6 topic on GitHub.
- xk6-datadog: an extension for querying Datadog metrics.
- xk6-kafka: produce and consume Kafka messages in Avro format.
- xk6-notification: a Slack and Teams notification library.
- xk6-redis: an alternative Redis client similar to the one we created in this tutorial.
- xk6-sql: a SQL extension to test against PostgreSQL, MySQL and SQLite.
- xk6-url: an extension for parsing and normalizing URLs.
- xk6-zmq: a ZeroMQ client.
- xk6-ssh: a client for performing commands over SSh.
- xk6-docker: a client for interacting with Docker.
Help and feedback
While we strived to make this system as easy to use for extension writers and k6 users, it's still a project in the experimental phase, and we'd love to hear your feedback about it! Likewise if you need help troubleshooting an extension or xk6 itself feel free to jump on Slack or the community forum. Have fun!