-
Notifications
You must be signed in to change notification settings - Fork 2
Conversation
Readme.md
Outdated
/ __________ \ |__| \__\ \_____/ .io | ||
|
||
execution: local | ||
script: ../xk6-docker/test.js |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: The script name it's not the same (test.js->script.js).
docker.go
Outdated
func (d *Docker) Ps() []string { | ||
cli, err := client.NewClientWithOpts(client.FromEnv) | ||
if err != nil { | ||
panic(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to return the error. But probably @simskij knows better :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree that we should return the error here rather than panicking. The docker client erroring should probably not be considered fatal for the whole VU goroutine. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
docker.go
Outdated
result := make([]string, 0, len(containers)) | ||
|
||
for _, container := range containers { | ||
result = append(result, fmt.Sprintf("%s %s %s", container.ID[:10], container.Image, container.Command)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it's better to return a struct, instead of a string with the concatenated values.
From my POV it's easier to consume from JS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @dgzlopes on this one. If the struct itself is too big/contains a lot of properties we don't care about, I'd suggest remapping it to a bespoke struct with reasonable properties and proper json tags.
Consider a scenario where I want to list my containers and get the command used to launch one named whatever-nginx
. I'd then. have to regex parse the string from the JS runtime, which likely won't be that fast. If I got a json object on the other hand, it wouldn't be a big deal if I had to console.log
the object myself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@simskij please take a look at rework commit. I tried to keep the initial interface that came from docker go lib. Example js file shows how to get normal docker ps
output now. IMO the benefits of this approach are transparent support of https://github.com/moby/moby/tree/master/client functionality. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely agree!
If anything, I think it could end up being too much. I don't want to assume too much docker knowledge from the user. But let's start from here. If this extension takes off and people seem to be confused with how to use the props for some reason, we could always limit it in a future version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm excited about this extension :)
Left some comments (for @simme primarily). Also, I think you should vendor your deps!
I'm guessing you meant to mention @simskij :) |
Oh, sorry @simme. I can't trust GH auto-complete 😄 |
docker.go
Outdated
func (d *Docker) Ps() []string { | ||
cli, err := client.NewClientWithOpts(client.FromEnv) | ||
if err != nil { | ||
panic(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree that we should return the error here rather than panicking. The docker client erroring should probably not be considered fatal for the whole VU goroutine. 🤔
Great work! 👏🏼 Added a few comments. |
@simskij @dgzlopes Ok I'm removing WIP label as my PR becomes huge and I would like to wrap it up and continue work in tiny PRs. I reworked it since your last review and added the list of docker commands (listed in PR description).
|
Yup! If we've smaller PRs, it's easier to review them. To vendor the dependencies, usually, I run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks superb! That was really fast of you! 🎉 👍🏼
Left some minor suggestions.
func (d *Containers) SetupClient() { | ||
cli, err := client.NewClientWithOpts(client.FromEnv) | ||
if err != nil { | ||
panic(err) | ||
} | ||
d.Client = cli | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should allow the user to configure this by supplying a config object as well and not strictly enforce config through env vars?
I'm thinking that if an object is supplied as the argument to the function, we should treat that as a client config object, if not - default to FromEnv
. If we decide to go for this, it could be good if we also cover it with a unit test for each of the paths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So you mean the usage will look like this?
import containers from 'k6/x/docker/containers';
export default function () {
const containerId = 'YOUR_CONTAINER_ID';
const env = {
....
}
containers.setup(env)
containers.startContainer(containerId, {attach: true});
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, exactly! 👍🏼
Readme.md
Outdated
|
||
Result output: | ||
|
||
```bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
plain might be preferable here given the weird syntax highlighting of the output?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
containers.go
Outdated
func (d *Containers) ListContainers(options types.ContainerListOptions) ([]types.Container, error) { | ||
containers, err := d.Client.ContainerList(context.Background(), options) | ||
return containers, err | ||
} | ||
|
||
// StartContainer works as Docker start command | ||
func (d *Containers) StartContainer(containerID string, options types.ContainerStartOptions) error { | ||
return d.Client.ContainerStart(context.Background(), containerID, options) | ||
} | ||
|
||
// StopContainer works as Docker stop command | ||
func (d *Containers) StopContainer(containerID string) error { | ||
// TODO: Add timeout option support | ||
timeout := 0 * time.Second | ||
|
||
return d.Client.ContainerStop(context.Background(), containerID, &timeout) | ||
} | ||
|
||
// PauseContainer works as Docker pause command | ||
func (d *Containers) PauseContainer(containerID string) error { | ||
return d.Client.ContainerPause(context.Background(), containerID) | ||
} | ||
|
||
// UnpauseContainer works as Docker unpause command | ||
func (d *Containers) UnpauseContainer(containerID string) error { | ||
return d.Client.ContainerUnpause(context.Background(), containerID) | ||
} | ||
|
||
// Logs works as Docker logs command | ||
func (d *Containers) Logs(containerID string, options types.ContainerLogsOptions) (string, error) { | ||
response, err := d.Client.ContainerLogs(context.Background(), containerID, options) | ||
buf := new(bytes.Buffer) | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
buf.ReadFrom(response) | ||
return buf.String(), nil | ||
} | ||
|
||
// InspectContainer works as Docker inspect command | ||
func (d *Containers) InspectContainer(containerID string) (types.ContainerJSON, error) { | ||
return d.Client.ContainerInspect(context.Background(), containerID) | ||
} | ||
|
||
// ExecContainer works as Docker exec command | ||
func (d *Containers) ExecContainer(containerID string, config types.ExecConfig) error { | ||
response, err := d.Client.ContainerExecCreate(context.Background(), containerID, config) | ||
if err != nil { | ||
return err | ||
} | ||
return d.Client.ContainerExecStart(context.Background(), response.ID, types.ExecStartCheck{}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I think about it, and see it in the context of the other commands, maybe we should omit Container
from the method names?
containers.listContainers
and containers.unpauseContainer
looks a bit excessive in comparison to containers.list
and containers.unpause
, don't you think? Since they are all available from the containers
object, it should be quite obvious what they refer to anyhow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
tail: 20, | ||
// defaults: true | ||
} | ||
const result = containers.logs(containerId, options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This goes for all of the functions that take an options object: Should we provide some "sane" defaults in this case if a user omits the options object from the call? Or is the docker client already taking care of that?
examples/docker-pause.js
Outdated
|
||
export default function () { | ||
const containerId = 'YOUR_CONTAINER_ID'; | ||
containers.pauseContainer(containerId, {attach: true}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
containers.pauseContainer(containerId, {attach: true}); | |
containers.pauseContainer(containerId); |
implementation does not take any args
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
examples/docker-start.js
Outdated
|
||
export default function () { | ||
const containerId = 'YOUR_CONTAINER_ID'; | ||
containers.startContainer(containerId, {attach: true}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would attaching even mean in this context? That we block until the container exits?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface is wrong, I just remove it and keep containerId only
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅
As discussed on slack, I'd also suggest having a look at whether we can generate typescript types for the extension using something like https://github.com/OneOfOne/struct2ts or https://github.com/tkrajina/typescriptify-golang-structs. This would definitely improve the developer experience while writing scripts, regardless if the user is using typescript or not. |
Yeah, I'll go ahead and merge this and then we can continue in smaller PR:s. 👍🏼 |
The update includes the next docker methods: