@@ -4,55 +4,23 @@ import (
44 "bytes"
55 "errors"
66 "fmt"
7- "io/fs"
8- "maps"
97 "os"
108 "os/exec"
11- "path"
12- "path/filepath"
13- "slices"
149 "strings"
1510 "syscall"
1611 "unicode"
1712 "unicode/utf8"
1813
1914 "github.com/Masterminds/semver/v3"
20- "github.com/grafana/k6deps"
2115 "github.com/grafana/k6provider"
22- "github.com/spf13/cobra"
2316
2417 "go.k6.io/k6/cloudapi"
2518 "go.k6.io/k6/cmd/state"
2619 "go.k6.io/k6/errext"
2720 "go.k6.io/k6/errext/exitcodes"
2821 "go.k6.io/k6/ext"
29- "go.k6.io/k6/internal/build"
30- "go.k6.io/k6/lib/fsext"
3122)
3223
33- // ioFSBridge allows an afero.Fs to implement the Go standard library io/fs.FS.
34- type ioFSBridge struct {
35- pwd string
36- fsext fsext.Fs
37- }
38-
39- // newIofsBridge returns an IOFSBridge from a Fs
40- func newIOFSBridge (fs fsext.Fs , pwd string ) fs.FS {
41- return & ioFSBridge {
42- fsext : fs ,
43- pwd : pwd ,
44- }
45- }
46-
47- // Open implements fs.Fs Open
48- func (b * ioFSBridge ) Open (name string ) (fs.File , error ) {
49- f , err := b .fsext .Open (path .Join (b .pwd , name ))
50- if err != nil {
51- return nil , fmt .Errorf ("opening file via launcher's bridge: %w" , err )
52- }
53- return f , nil
54- }
55-
5624// commandExecutor executes the requested k6 command line command.
5725// It abstract the execution path from the concrete binary.
5826type commandExecutor interface {
@@ -64,87 +32,6 @@ type provisioner interface {
6432 provision (map [string ]string ) (commandExecutor , error )
6533}
6634
67- // launcher is a k6 launcher. It analyses the requirements of a k6 execution,
68- // then if required, it provisions a binary executor to satisfy the requirements.
69- type launcher struct {
70- gs * state.GlobalState
71- provisioner provisioner
72- commandExecutor commandExecutor
73- }
74-
75- // newLauncher creates a new Launcher from a GlobalState using the default provision function
76- func newLauncher (gs * state.GlobalState ) * launcher {
77- return & launcher {
78- gs : gs ,
79- provisioner : newK6BuildProvisioner (gs ),
80- }
81- }
82-
83- // launch analyzies the command to be executed and its input (e.g. script) to identify its dependencies.
84- // If it has dependencies that cannot be satisfied by the current binary, it obtains a custom commandExecutor
85- // usign the provision function and delegates the execution of the command to this commandExecutor.
86- // On the contrary, continues with the execution of the command in the current binary.
87- func (l * launcher ) launch (cmd * cobra.Command , args []string ) error {
88- if ! isAnalysisRequired (cmd ) {
89- l .gs .Logger .
90- WithField ("command" , cmd .Name ()).
91- Debug ("command does not require dependency analysis" )
92- return nil
93- }
94-
95- deps , err := analyze (l .gs , args )
96- if err != nil {
97- l .gs .Logger .
98- WithError (err ).
99- Error ("Automatic extension resolution is enabled but it failed to analyze the dependencies." +
100- " Please, make sure to report this issue by opening a bug report." )
101- return err
102- }
103-
104- if ! isCustomBuildRequired (deps , build .Version , ext .GetAll ()) {
105- l .gs .Logger .
106- Debug ("The current k6 binary already satisfies all the required dependencies," +
107- " it isn't required to provision a new binary." )
108- return nil
109- }
110-
111- constraintsMap := constraintsMapToProvisionDependency (deps )
112- l .gs .Logger .
113- WithField ("deps" , constraintsMapToStringForLogs (constraintsMap )).
114- Info ("Automatic extension resolution is enabled. The current k6 binary doesn't satisfy all dependencies," +
115- " it's required to provision a custom binary." )
116-
117- customBinary , err := l .provisioner .provision (constraintsMap )
118- if err != nil {
119- l .gs .Logger .
120- WithError (err ).
121- Error ("Failed to provision a k6 binary with required dependencies." +
122- " Please, make sure to report this issue by opening a bug report." )
123- return err
124- }
125-
126- l .commandExecutor = customBinary
127-
128- // override command's RunE method to be processed by the command executor
129- cmd .RunE = l .runE
130-
131- return nil
132- }
133-
134- func constraintsMapToStringForLogs (deps k6provider.Dependencies ) string {
135- var buf bytes.Buffer
136-
137- for idx , depName := range slices .Sorted (maps .Keys (deps )) {
138- if idx > 0 {
139- _ = buf .WriteByte (';' )
140- }
141-
142- buf .WriteString (depName )
143- buf .WriteString (deps [depName ])
144- }
145- return buf .String ()
146- }
147-
14835func constraintsMapToProvisionDependency (deps map [string ]* semver.Constraints ) k6provider.Dependencies {
14936 result := make (k6provider.Dependencies )
15037 for name , constraint := range deps {
@@ -160,11 +47,6 @@ func constraintsMapToProvisionDependency(deps map[string]*semver.Constraints) k6
16047 return result
16148}
16249
163- // runE executes the k6 command using a command executor
164- func (l * launcher ) runE (_ * cobra.Command , _ []string ) error {
165- return l .commandExecutor .run (l .gs )
166- }
167-
16850// customBinary runs the requested commands on a different binary on a subprocess passing the
16951// original arguments
17052type customBinary struct {
@@ -335,75 +217,6 @@ func formatDependencies(deps map[string]string) string {
335217 return strings .Trim (buffer .String (), " " )
336218}
337219
338- // analyze returns the dependencies for the command to be executed.
339- // Presently, only the k6 input script or archive (if any) is passed to k6deps for scanning.
340- // TODO: if k6 receives the input from stdin, it is not used for scanning because we don't know
341- // if it is a script or an archive
342- func analyze (gs * state.GlobalState , args []string ) (dependencies , error ) {
343- dopts := & k6deps.Options {
344- LookupEnv : func (key string ) (string , bool ) { v , ok := gs .Env [key ]; return v , ok },
345- Manifest : k6deps.Source {Ignore : true },
346- }
347-
348- sourceRootPath := args [0 ]
349- gs .Logger .WithField ("source" , "sourceRootPath" ).
350- Debug ("Launcher is resolving and reading the test's script" )
351- src , _ , pwd , err := readSource (gs , sourceRootPath )
352- dopts .RootDir = pwd
353- if err != nil {
354- return nil , fmt .Errorf ("reading source for analysis %w" , err )
355- }
356-
357- // if sourceRooPath is stdin ('-') we need to preserve the content
358- if sourceRootPath == "-" {
359- gs .Stdin = bytes .NewBuffer (src .Data )
360- }
361-
362- if strings .HasSuffix (sourceRootPath , ".tar" ) {
363- dopts .Archive .Contents = src .Data
364- } else {
365- if ! filepath .IsAbs (sourceRootPath ) {
366- sourceRootPath = filepath .Join (pwd , sourceRootPath )
367- }
368- dopts .Script .Name = sourceRootPath
369- dopts .Script .Contents = src .Data
370- dopts .Fs = newIOFSBridge (gs .FS , pwd )
371- }
372-
373- deps , err := k6deps .Analyze (dopts )
374- if err != nil {
375- return nil , err
376- }
377- result := make (dependencies , len (deps ))
378- for n , dep := range deps {
379- result [n ] = dep .Constraints
380- }
381- return result , nil
382- }
383-
384- // isAnalysisRequired returns a boolean indicating if dependency analysis is required for the command
385- func isAnalysisRequired (cmd * cobra.Command ) bool {
386- switch stringifyCommand (cmd ) {
387- case "k6 run" ,
388- "k6 cloud" ,
389- "k6 cloud run" ,
390- "k6 cloud upload" ,
391- "k6 upload" ,
392- "k6 archive" ,
393- "k6 inspect" :
394- return true
395- }
396-
397- return false
398- }
399-
400- func stringifyCommand (cmd * cobra.Command ) string {
401- if cmd .Parent () == nil {
402- return "k6"
403- }
404- return stringifyCommand (cmd .Parent ()) + " " + cmd .Name ()
405- }
406-
407220// extractToken gets the cloud token required to access the build service
408221// from the environment or from the config file
409222func extractToken (gs * state.GlobalState ) (string , error ) {
0 commit comments