-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Plugin support #1353
Comments
Thank you for creating this issue! As I said in Slack, we've discussed plugins a few times internally, but the lack of cross-platform support has always stopped us. Docker is an acceptable workaround for Windows though, and if a public plugin ever becomes very popular, we can always work on upstreaming it or having similar functionality in core k6, so I think this should be fine. A few comments about the proposal itself. Even though this is a v1, and we likely won't be making any API stability guarantees, we probably should think a bit about supporting other things besides new JS modules. Not actually implement anything else, just figure out a mechanism by which a plugin can declare its capabilities. The only capability currently available and supported by k6 would be extra modules exported to the JS runtime, but in the near future we'll probably want to include outputs in the mix. The reason for this is that I'd like the CLI plugin flag to one, So, I'd like k6 to be able to dynamically determine what a plugin adds. Maybe type assertions with a bunch of pre-defined type K6Plugin interface {
Name() string
Preflight() error // though maybe Setup() or Init() would be a bit better?
Postflight() error // Teardown()?
}
type JavaScriptPlugin interface{
K6Plugin
GetModules() map[string]interface{}
} We'll discuss this internally again tomorrow, and if we think of any other potential issues, we'll mention them here. |
Hey, @andremedeiros, we discussed this internally again and we didn't see any obstacles, so k6 plugins are a go! 🎊 So, if you're interested in working on this, don't hesitate to ask any questions. As a practical matter, the actual configuration handling of Also, we'd like the initial PR to k6 that adds the JS plugins to also feature a very simple demo plugin, and a small integration test in CI that the plugin actually works. The plugin can be dead simple though, a function that reverses a string or a |
Sounds great to me! I'll get to work on it! |
Friends, I've pushed a preliminary PR (#1396) that loads plugins, exposes them, and does the setup/teardown lifecycle. I don't have tests yet, and I'm not 100% happy with how clunky writing plugins feels right now, but it's doing what it's supposed to. I'd love to have a more involved chat with the team re: types and how things "feel." Basically, with the way that Go's We have two options:
Curious to hear your thoughts on the approach. |
🥳 I've submitted some initial feedback. While this looks great so far, I think it will be easier to reason about once a runnable proof of concept (the previously mentioned demo) has been added to it. Regarding the options: |
Sorry, I'm probably not going to be able to review the PR until next week - I'm swamped by other things today, and after that I'm off until next Tuesday. |
Had another pass today, and I'm pretty happy with the way it turned out. Addressed some of the concerns, and decided to push a couple of things for future me to handle. With regards to what I mentioned above, and after digesting @simskij's feedback, I decided to go with option 1. It's negligible in the broader context of running a load test, so At this point, baring an issue in the tests where the plugin is being built in the exact same container as the tests are being run, but somehow golang thinks it's built with a different runtime, I think we have a solid v1 implementation of plugins. It's useful (at least for the use case I originally had) and it sort of builds... which leads me to... Can one of you look at the tests? I'm not quite sure why they're failing right now, and it seems to be pretty happy On My Machine (tm). Also, as a way to have a plugin that can be loaded in tests, I pushed a repo to andremedeiros/leftpad that contains a very minimal plugin example. This gets loaded in tests and we run assertions on the output too. Part of me feels that this should be done inside the test code, in some dynamically created test directory, but I can't for the life of me find an acceptable pattern for this use case in golang. I'm willing to hear what other people think and adapt. |
My evaluation of the plug-in system for k6The plug-in system will soon be the quintessential part of the k6 tooling ecosystem. The benefits of such a system are not hidden to anyone, with the most significant being that load testing various protocols become easy, in addition to various useful plugins that can help with current issues with JavaScript external modules (NodeJavaScript APIs, browser APIs and ...). These are my observations: The GoodI was able to quickly create three plugins:
It took me a long time to create the first plugin because I was learning Golang and dealing with various parts of the language and the k6 APIs. But the second plugin was created in an afternoon. Before creating the third plugin, I started to think that a pattern is emerging, so I created a template repository on GitHub and started forking it for the third plugin. The template is not perfect and it also includes the hacky parts for getting state and pushing metrics. Thus, once the plugin system is merged, the template repo should be updated. The BadIt was really difficult to understand how k6 and the plugin system works behind the scenes, mostly because of the lack of documentation and examples. This was significantly more difficult in the first plugin, but became easier over time. The hardest part was figuring out that there's a magic The UglyUsing a hacky solution to get (current) state from the |
In my opinion, using Go plugins is not the way to go. Facts:
As the 5th top contributor to the go repo put it:
I think the k6 team has to consider a cross-platform solution, built on a more solid foundation. One possibility is to use yaegi, a Go interpreter implemented in Go and designed to add plugins to Go applications. |
@mardukbp, I hear you. We're not blind about the issues with Go plugins, though I have to admit I hadn't considered the potential problems of vendored dependencies. Another issue you don't mention, which we recently found out about, is the requirement to use Regarding the vendor issue - I don't think it'd be a huge problem in the case of k6. The likely use case of a k6 JS plugin would be to expose some new functionality to k6 scripts (DB queries, DNS, raw TCP, queue messages, some SDK for something, etc.) and maybe measure the things it does and emit metrics. So, in the unlikely event it shares a dependency with k6, it's not going to be an issue if it has its own copy of it. The architecture of how VUs are executed and how they emit metrics is reasonably decoupled and shouldn't require any specific dependency beyond k6. Also, k6 is meant to be a batteries-included sort of tool. If a particular well-written plugin becomes very popular, it's very likely we'll try to bring it into the k6 core. And, your original suggestion (#1595) is never going to not be an option for anyone who knows Go. Compiling a k6 version with a plugin bundled in is probably going to be very easy, possibly also automate-able. @mstoykov actually suggested that instead of plugins, we should have a "k6 bundler" which builds a unique k6 binary, for any platform, allowing you to just select which things out of a list of features/plugins you want. Sort of like these JS library builders - for example, babel. I don't think that would offer better UX, but it's not completely out of the question that we pivot in that direction, if the current version of plugins proves to be too problematic for us. We'd be very explicit that we don't offer any guarantees about backwards compatibility yet, when it comes to plugins. Lack of Windows support for Go plugins isn't ideal either, but having Docker as a workaround is mostly OK, in my mind. The k6 docker image sits at ~10MB, which seems reasonable to me. And, I'm not certain, but the Linux k6 binary should work on the the Windows Subsystem for Linux (WSL), which means you should be able to use k6 plugins that way also... yaegi deserves some investigation - we've previously only considered it as a way to allow non-JS scripts to be executed by k6. As I explained in #751, there are some issues with that use case, mostly refactoring that needs to happen in k6 before we can try it. We haven't considered how suitable it would be for plugins though, and my biggest worry is performance. It's not out of the question for a single Go dependency to be tens of thousands of lines of Go code. For example, the https://github.com/Shopify/sarama/ dependency, which is currently used only for the Kafka output in k6. Our vendored copy of it sits at 10k lines of Go code, and that's not including any of the dependencies it itself has. Judging by the #617 PR that added the Kafka output, it might be closer to 25k. So, a kafka plugin is going to pull in anywhere from 10k to 25k lines of Go code, and there are probably much larger things (e.g. AWS SDK or some crypto library?). It seems to me like we'd want them to be compiled instead of interpreted, especially in a load testing tool. We already have issues with huge JS scripts. goja, the JS runtime we use, is reasonably performant with most scripts, but we're hitting performance issues when dealing with huge ones. I don't want to also have to deal with performance issues because we're interpreting huge Go files... |
Hi @mardukbp, Regarding your comment about yaegi and support for running Go code in k6, I've made a plugin that does exactly that, although I haven't publicly published it yet. But there are some issues with it:
Regarding the half-baked plugin system and no support for Windows, we talked about it a lot. We also considered using the go-plugin from Hashi Corp. instead, but the current implementation is going to be THE version 1 of the plugin system, which may change in the future, possibly breaking backward compatibility. The overall goal of the plugin system is to let users write JS plugins specifically, so no Go for now and no Windows support until golang/go#19282 is resolved. |
I firmly believe that k6 should be extensible both at the Go and JS levels. None is more important than the other. They just serve different purposes. Having said that, I think that a k6 bundler is the way to go. That is how the plugin system of the Caddy webserver works. This offers a cross-platform solution leveraging the Go build system, which is one of the platform's greatest strengths. In any case, I would suggest adding a section to the Documentation explaining how to extend k6 with Go. It is simple, useful and works now and in all platforms. |
hmm I didn't know about https://github.com/caddyserver/xcaddy, this has a lot of potential... |
Hmm I'm not sure what implications the AGPL-3.0 k6 license is going to have on a bundler - would all k6 plugins in such a system have to be open source? |
I think that part of the clauses only apply if you are distributing the binary or are providing a service that directly uses it ... I think :D |
@mstoykov, maybe, but that wasn't my impression 🤷♂️ if we go that route, we might need to offer some guidance what's kosher and not in the docs... @mostafa, in as much as it has versioned things, xcaddy looks like every version/package/environment manager... 😉 But it's very different compared to pretty much every other plugin system I've seen, considering something like that is going to build a bespoke k6 binary with the desired plugins built-in... Moreover, and mind you, this is someting that I very much like, it doesn't seem to require any infrastructure from our side. It seems to basically operate with git repos, which is a nice benefit. Another plus is that it seems to be built on top of Go modules, which from @mstoykov's experiments caused a ton of issues with the Go plugins from #1396... @mardukbp, thank you very much for bringing all of these issues and potential solutions to our attention! |
Sorry for writing so late, but I have been running tests and trying to get a plugin to work with the now go modules.
As a whole, I think this is not a great solution given all of the above problems and the fact that it doesn't support windows and with all of those and the requirement for CGO_ENABLED=1 leading to even more k6 builds I think this puts the last nail in the ⚰️ for this approach until there is some movement on the golang side... While the xcaddy approach has bigger technical expectations from users, I don't think it will be impossible to (in the future) make it more encapsulated, while it does make "plugin" development much better, especially in the sense that you will get a lot of the above problems fixed. Notably:
Thanks a lot to @andremedeiros for getting the ball rolling(and the implementation), no matter what the final decision/solution will be. |
So before I go and rebase the PR, what's the plan? |
@andremedeiros, we're re-evaluating the way we want to go forward with k6 plugins. We still want plugins, or at least a way to allow users to more easily extend the k6 functionality, but unfortunately Go plugins don't seem to be the best way to go about solving this issue... 😞 There are just too many technical hurdles and limitations - for plugin authors, for plugin users, and for k6 developers. So, I'd say don't rebase the PR... 😞 We're not going to close it until we've fully committed ourselves to some other approach, but there's no need for you to spend any more time on it while things are in limbo. We plan to investigate the xcaddy approach more thoroughly in the coming weeks. The current plan for k6 v0.28.0 (slated for release mid-September) is not to commit to any plugin/extension architecture yet. At most, we might refactor the k6 module registration slightly, to make it easier to extend k6, both in an xcaddy-like manner and as even a fork. This will allow us to work on an PoC for a xcaddy-like tool ( Thank you for your huge contributions with the plugins and sorry again things are so complicated! Whatever we settle on in the end, you'd get credit in the release notes at the very least, since if it weren't for your suggestions and PR, we probably wouldn't even be talking about this now, much less exploring potential solutions. |
I suggest calling the k6 builder 'pack6`. |
I know there is an issue about k6-Telegraf integration, but I think it is relevant to bring up Telegraf in this issue since it is mostly plugin-driven. Maybe there are things to learn and/or reuse from their implementation. |
@mardukbp, a valid point, though as far as I know, all of their "plugins" are baked into the main repo. They just have some very clean interfaces for the different types of plugins (see https://godoc.org/github.com/influxdata/telegraf) and then a whole bunch of in-tree implementations of these plugins (see https://godoc.org/github.com/influxdata/telegraf#pkg-subdirectories, all that start with |
This adds plugin support to k6 inspired by xcaddy[1]. See the xk6 repo[2]. Closes #1353 [1]: https://github.com/caddyserver/xcaddy [2]: https://github.com/k6io/xk6
This adds plugin support to k6 inspired by xcaddy[1]. See the xk6 repo[2]. Closes #1353 [1]: https://github.com/caddyserver/xcaddy [2]: https://github.com/k6io/xk6
This adds plugin support to k6 inspired by xcaddy[1]. See the xk6 repo[2]. Closes #1353 [1]: https://github.com/caddyserver/xcaddy [2]: https://github.com/k6io/xk6
This adds plugin support to k6 inspired by xcaddy[1]. See the xk6 repo[2]. Closes grafana#1353 [1]: https://github.com/caddyserver/xcaddy [2]: https://github.com/k6io/xk6
As a user, I would like to be able to implement custom functionality and protocols and expose it in the JavaScript VM so that I can write the heavy lifting work in Golang but write tests in JS.
Feature Description
As a protocol developer, I'd like to be able to develop a client that talks to my protocol via the JavaScript VM that k6 runs. To do this, I would prefer to be able to write a plugin that doesn't have to live in the main codebase and isn't the responsibility of the k6 team.
Effectively, a plugin should expose:
map[string]interface{}
that gets added to the module mapping so that the plugin's functions can be imported from JavaScript.Suggested Solution (optional)
My MVP proposal would be to use Golang's
plugin
package. Despite not supporting Windows in its current state, Windows users could use Docker to run plugins. Moreso, it would not introduce any new dependencies to k6.Plugins would be loaded by passing a
-plugin
argument when launching k6, which should receive a.so
file path, and would be initialized (preflight) once, on test start, and cleaned up (postflight) once, on test finish.A plugin struct could look something like:
For each plugin file, k9 would run something to the lines of:
Tagging @na--, @imiric, and @mstoykov as indicated in Slack.
The text was updated successfully, but these errors were encountered: