-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Integrate binary provisioning into k6 root command #4801
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
base: master
Are you sure you want to change the base?
Conversation
0e9b32c
to
69c540b
Compare
fd5f482
to
cd6b16a
Compare
Signed-off-by: Pablo Chacin <pablochacin@gmail.com>
Signed-off-by: Pablo Chacin <pablochacin@gmail.com>
e6edf2e
to
b1ba5b7
Compare
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.
The global architecture LGTM. There are a few minor adjustments required.
internal/cmd/root.go
Outdated
return newRootWithLauncher(gs, newLauncher(gs)) | ||
} | ||
|
||
// creates a root command with a launcher. Used to facilitate testing |
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.
// creates a root command with a launcher. Used to facilitate testing | |
// newRootWithLauncher creates a root command with a launcher. Used to facilitate testing |
internal/cmd/root.go
Outdated
} | ||
|
||
// creates a root command with a default launcher |
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.
// creates a root command with a default launcher | |
// newRootCommand creates a root command with a default launcher |
func isAnalysisRequired(cmd *cobra.Command) bool { | ||
switch cmd.Name() { | ||
case "run": | ||
if cmd.Parent() != nil && cmd.Parent().Name() == "cloud" { |
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.
if cmd.Parent() != nil && cmd.Parent().Name() == "cloud" { | |
// exclude `k6 cloud run` command | |
if cmd.Parent() != nil && cmd.Parent().Name() == "cloud" { |
internal/cmd/launcher.go
Outdated
// launch analyzies the command to be executed and its input (e.g. script) | ||
// to identify if its dependencies. It it has dependencies tha cannot be satisfied by the | ||
// current binary, it obtains a custom binary usign the provision function and delegates | ||
// the execution of the command to this binary. if not, continues with the execution of the | ||
// command in the current binary. |
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 comment needs some adjustment.
internal/cmd/launcher.go
Outdated
// with their constrains, and it returns an executor that satisfies them. | ||
provision func(*state.GlobalState, k6deps.Dependencies) (commandExecutor, error) | ||
// provider defines the interface for provisioning a custom k6 binary for a set of dependencies | ||
type provider interface { |
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.
type provider interface { | |
type provisioner interface { |
It sounds to me more idiomatic. If we want provider
then I expect a provide
function.
internal/cmd/launcher_test.go
Outdated
|
||
script := scriptNameFromArgs(tc.args) | ||
// TODO: check error |
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.
// TODO: check error | |
// TODO: check error |
Can we address this TODO, please?
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.
it is a leftover. execute does not return any error
} | ||
|
||
// customBuildRequired checks if the build is required | ||
// isCustomBuildRequired checks if the build is required | ||
// it's required if there is one or more dependencies other than k6 itself | ||
// or if the required k6 version is not satisfied by the current binary's version | ||
// TODO: get the version of any built-in extension and check if they satisfy the dependencies |
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 have the feeling we have an issue for this TODO. Please, mention here directly the issue so they are bidirectionally linked.
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.
Added. This isssue is addressed in this PR #4812
Signed-off-by: Pablo Chacin <pablochacin@gmail.com>
Signed-off-by: Pablo Chacin <pablochacin@gmail.com>
Signed-off-by: Pablo Chacin <pablochacin@gmail.com>
internal/cmd/launcher.go
Outdated
// This can be avoided by checking if the current binary has already extensins that | ||
// satisfies the dependencies. See comment in isCustomBuildRequired function. |
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 can be avoided by checking if the current binary has already extensins that | |
// satisfies the dependencies. See comment in isCustomBuildRequired function. | |
// This can be avoided by checking if the current binary has already extensions that | |
// satisfies the dependencies. See comment in isCustomBuildRequired function. |
Signed-off-by: Pablo Chacin <pablochacin@gmail.com>
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 find the whole way the binary provisioning trying to be integrated ... strange.
In the sense that it isn't being integrated, it does practically the same thing as if it is a separate program, but now internally.
Looking at the problem it is trying to solve and given my lack of work with the previous work, I would not try to be wrapping the whole process and then try to parse stuff the same way as k6. Instead, I will let k6 parse everything it can and use that it does it the way it does to get the exact argument it has with all its other configurations to figure out if I need binary provisioning.
In this case IMO this means going to https://github.com/grafana/k6/blob/master/internal/cmd/test_load.go#L46 or even
Lines 177 to 186 in 34c2dc9
func readSource(gs *state.GlobalState, filename string) (*loader.SourceData, map[string]fsext.Fs, string, error) { | |
pwd, err := gs.Getwd() | |
if err != nil { | |
return nil, nil, "", err | |
} | |
filesystems := loader.CreateFilesystems(gs.FS) | |
src, err := loader.ReadSource(gs.Logger, filename, pwd, filesystems, gs.Stdin) | |
return src, filesystems, pwd, err | |
} |
After this depending on the use case I guess you can error out with a specific error of "We need this modules" and then capturing it potentially in a similar way in the root command.
This IMO not only will reduce the code here, but will also be way more future prove. As you won't need to try to work around you not having parsed some configuration or us having anew command that reads the source code that needs to be wrapped.
I looked at some of the other changes in the other PRs, but it doesn't seem like any of them stop trying to parse the configuration as k6, just to do it again.
Is there a reason this was not considered or are there any known problems with something like this that I do not know about ?
I think part of this impression may come from the fact that I split the integration into two PRs (this and #4811)) to reduce the scope of review. The difference of this approach from having an external program (which is basically how we did the initial "integration" as a wrapper of
Basically, to have a clear execution path: either with binary provisioning or without it. See my comment below about why I considered this an advantage.
I'm not sure about this option. It depends on the command not having side-effects, and therefore stopping its execution and then retrying with binary provisioning will not find these side-effects. I find it harder to reason about this execution logic that the one existing now. Also, I'm not sure how k6 handles importing files. From what I saw in the
Not sure what duplicated parsing you refer to? There's no additional parsing, unless you mean parsing the test code itself searching dependencies. Can you give an example of this duplicated parsing? (again, consider this in the context of #4811. In general, I'm not against a deeper integration, but maybe only if we ensure this |
What?
integrates the logic of binary provisioning into the k6 root command.
Why?
Presently, the logic of binary provisioning happens before the k6 root command is executed. This brings some additional complexity and some problems because the arguments passed to k6 have not yet been parsed .
Checklist
make check
) and all pass.Checklist: Documentation (only for k6 maintainers and if relevant)
Please do not merge this PR until the following items are filled out.
Related PR(s)/Issue(s)
Closes #4737
Closes #4758