Skip to content

Commit

Permalink
WIP: use k6provider
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo Chacin <[email protected]>
  • Loading branch information
pablochacin committed Jan 27, 2025
1 parent c8d2c26 commit d9f1126
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 145 deletions.
8 changes: 0 additions & 8 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,6 @@ func New(levelVar *slog.LevelVar) *cobra.Command {

flags := root.PersistentFlags()

flags.StringVar(
&state.extensionCatalogURL,
"extension-catalog-url",
state.extensionCatalogURL,
"URL of the k6 extension catalog to be used",
)
flags.StringVar(
&state.buildServiceURL,
"build-service-url",
Expand All @@ -72,8 +66,6 @@ func New(levelVar *slog.LevelVar) *cobra.Command {
root.Flags().Lookup("help").Usage = "help for k6"
root.Flags().BoolVar(&state.version, "version", false, "version for k6")

root.MarkFlagsMutuallyExclusive("extension-catalog-url", "build-service-url")

return root
}

Expand Down
6 changes: 0 additions & 6 deletions cmd/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"io"
"log/slog"
"os"
"strings"
"testing"

"github.com/grafana/k6exec/cmd"
Expand All @@ -25,15 +24,10 @@ func TestNew(t *testing.T) { //nolint:paralleltest

flags := c.PersistentFlags()

require.NotNil(t, flags.Lookup("extension-catalog-url"))
require.NotNil(t, flags.Lookup("build-service-url"))
require.NotNil(t, flags.Lookup("verbose"))
require.NotNil(t, flags.Lookup("quiet"))
require.NotNil(t, flags.Lookup("no-color"))

out := captureStdout(t, func() { require.NoError(t, c.Execute()) })

require.True(t, strings.Contains(out, " k6"))
}

//nolint:forbidigo
Expand Down
6 changes: 0 additions & 6 deletions cmd/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@ The build service URL can be specified in the `K6_BUILD_SERVICE_URL` environment

There is no default URL for the build service, otherwise k6exec will automatically provide k6 with the native builder.

#### Native Builder

To use the native builder, you only need to install the [Go language toolkit](https://go.dev/doc/install).

The native builder uses a k6 extension catalog to resolve extension URLs and versions. The extension catalog URL has a default value. A different extension catalog URL can be specified in the `K6_EXTENSION_CATALOG_URL` environment variable or by using the `--extension-catalog-url` flag.

### Dependencies

Dependencies can come from three sources: k6 test script, manifest file, `K6_DEPENDENCIES` environment variable. Instead of these three sources, a k6 archive can also be specified, which can contain all three sources.
Expand Down
4 changes: 2 additions & 2 deletions cmd/k6exec/main_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ func Test_newCmd(t *testing.T) { //nolint:paralleltest

lvar := new(slog.LevelVar)

// TODO: add more assertions and more test cases
cmd := newCmd([]string{"run", abs}, lvar)

require.NoError(t, cmd.Execute())
require.Equal(t, "k6exec", cmd.Name())
}

func Test_initLogging(t *testing.T) { //nolint:paralleltest
Expand Down
41 changes: 11 additions & 30 deletions cmd/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"context"
"log/slog"
"net/url"
"os"
"os/exec"

Expand All @@ -13,17 +12,14 @@ import (

type state struct {
k6exec.Options
buildServiceURL string
extensionCatalogURL string
verbose bool
quiet bool
nocolor bool
version bool
usage bool
levelVar *slog.LevelVar

buildServiceURL string // TODO: this field seems redundant with
verbose bool
quiet bool
nocolor bool
version bool
usage bool
levelVar *slog.LevelVar
cmd *exec.Cmd

cleanup func() error
}

Expand All @@ -33,34 +29,18 @@ func newState(levelVar *slog.LevelVar) *state {

s.levelVar = levelVar

// FIXME: Remove this as the k6provider library does it
if value, found := os.LookupEnv("K6_BUILD_SERVICE_URL"); found {
s.buildServiceURL = value
}

if value, found := os.LookupEnv("K6_EXTENSION_CATALOG_URL"); found {
s.extensionCatalogURL = value
}

return s
}

func (s *state) persistentPreRunE(_ *cobra.Command, _ []string) error {
// TODO: is this check necessary?
if len(s.buildServiceURL) > 0 {
val, err := url.Parse(s.buildServiceURL)
if err != nil {
return err
}

s.Options.BuildServiceURL = val
}

if len(s.extensionCatalogURL) > 0 {
val, err := url.Parse(s.extensionCatalogURL)
if err != nil {
return err
}

s.Options.ExtensionCatalogURL = val
s.Options.BuildServiceURL = s.buildServiceURL
}

if s.verbose && s.levelVar != nil {
Expand Down Expand Up @@ -122,6 +102,7 @@ func (s *state) preRunE(sub *cobra.Command, args []string) error {
func (s *state) runE(_ *cobra.Command, _ []string) error {
var err error

// FIXME: I think this code is not setting the error to the cleanup function (pablochacin)
defer func() {
e := s.cleanup()
if err == nil {
Expand Down
29 changes: 11 additions & 18 deletions cmd/state_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"path/filepath"
"testing"

"github.com/grafana/k6build/pkg/testutils"
"github.com/grafana/k6exec"
"github.com/stretchr/testify/require"
)
Expand All @@ -15,21 +16,17 @@ func Test_newState(t *testing.T) {
lvar := new(slog.LevelVar)

t.Setenv("K6_BUILD_SERVICE_URL", "")
t.Setenv("K6_EXTENSION_CATALOG_URL", "")

st := newState(lvar)

require.Same(t, lvar, st.levelVar)
require.Empty(t, st.buildServiceURL)
require.Empty(t, st.extensionCatalogURL)

t.Setenv("K6_BUILD_SERVICE_URL", "foo")
t.Setenv("K6_EXTENSION_CATALOG_URL", "bar")

st = newState(lvar)

require.Equal(t, "foo", st.buildServiceURL)
require.Equal(t, "bar", st.extensionCatalogURL)
}

func Test_persistentPreRunE(t *testing.T) {
Expand All @@ -38,26 +35,15 @@ func Test_persistentPreRunE(t *testing.T) {
st := &state{levelVar: new(slog.LevelVar)}

require.NoError(t, st.persistentPreRunE(nil, nil))
require.Nil(t, st.BuildServiceURL)
require.Nil(t, st.ExtensionCatalogURL)
require.Empty(t, st.BuildServiceURL)
require.Equal(t, slog.LevelInfo, st.levelVar.Level())

st.buildServiceURL = "http://example.com"
st.extensionCatalogURL = "http://example.net"

require.NoError(t, st.persistentPreRunE(nil, nil))
require.Equal(t, "http://example.com", st.BuildServiceURL.String())
require.Equal(t, "http://example.net", st.ExtensionCatalogURL.String())

st.buildServiceURL = "http://example.com/%"
require.Error(t, st.persistentPreRunE(nil, nil))

st.buildServiceURL = "http://example.com"
st.extensionCatalogURL = "http://example.net/%"
require.Error(t, st.persistentPreRunE(nil, nil))
require.Equal(t, "http://example.com", st.BuildServiceURL)

st.buildServiceURL = "http://example.com"
st.extensionCatalogURL = "http://example.net"
st.verbose = true

require.NoError(t, st.persistentPreRunE(nil, nil))
Expand All @@ -70,9 +56,16 @@ func Test_persistentPreRunE(t *testing.T) {
func Test_preRunE(t *testing.T) {
t.Parallel()

env, err := testutils.NewTestEnv(testutils.TestEnvConfig{
WorkDir: t.TempDir(),
})
require.NoError(t, err)

t.Cleanup(env.Cleanup)

st := &state{
levelVar: new(slog.LevelVar),
Options: k6exec.Options{CacheDir: t.TempDir()},
Options: k6exec.Options{CacheDir: t.TempDir(), BuildServiceURL: env.BuildServiceURL()},
}

sub := newSubcommand("version", st)
Expand Down
16 changes: 3 additions & 13 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,29 @@ package k6exec

import (
"context"
"os"
"os/exec"
"path/filepath"

"github.com/grafana/k6provision"
)

// Command returns the exec.Cmd struct to execute k6 with the given arguments.
// If the given subcommand has a script argument, it analyzes the dependencies
// in the script and provisions a k6 executable based on them.
// In Options, you can also specify environment variable and manifest file as dependency sources.
// If no errors occur, the provisioned k6 executable will be placed in a temporary directory.
// The second return value is a cleanup function that is used to delete this temporary directory.
// TODO: as the cache is now handled by the k6provider library, consider removing the cleanup function
func Command(ctx context.Context, args []string, opts *Options) (*exec.Cmd, func() error, error) {
deps, err := analyze(args, opts)
if err != nil {
return nil, nil, err
}

dir, err := os.MkdirTemp("", "k6exec-*") //nolint:forbidigo
exe, err := provision(ctx, deps, opts)
if err != nil {
return nil, nil, err
}

exe := filepath.Join(dir, k6provision.ExeName)

if err := provision(ctx, deps, exe, opts); err != nil {
return nil, nil, err
}

cmd := exec.CommandContext(ctx, exe, args...) //nolint:gosec

return cmd, func() error { return os.RemoveAll(dir) }, nil //nolint:forbidigo
return cmd, func() error { return nil }, nil
}

/*
Expand Down
59 changes: 26 additions & 33 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,35 @@ package k6exec_test

import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"

"github.com/grafana/k6build/pkg/testutils"
"github.com/grafana/k6deps"
"github.com/grafana/k6exec"
"github.com/grafana/k6provision"
"github.com/grafana/k6provider"

"github.com/stretchr/testify/require"
)

func TestCommand(t *testing.T) {
t.Parallel()

srv := testWebServer(t)
defer srv.Close()

u, err := url.Parse(srv.URL + "/minimal-catalog.json")
env, err := testutils.NewTestEnv(testutils.TestEnvConfig{
WorkDir: t.TempDir(),
CatalogURL: "testdata/minimal-catalog.json",
})
require.NoError(t, err)

ctx := context.Background()
t.Cleanup(env.Cleanup)

opts := &k6exec.Options{ExtensionCatalogURL: u, Env: k6deps.Source{Ignore: true}, Manifest: k6deps.Source{Ignore: true}}
opts := &k6exec.Options{
Env: k6deps.Source{Ignore: true},
Manifest: k6deps.Source{Ignore: true},
BuildServiceURL: env.BuildServiceURL(),
}

cmd, cleanup, err := k6exec.Command(ctx, []string{"version"}, opts)
cmd, cleanup, err := k6exec.Command(context.TODO(), []string{"version"}, opts)
defer func() { require.NoError(t, cleanup()) }()

require.NoError(t, err)
Expand All @@ -42,31 +45,21 @@ func TestCommand(t *testing.T) {
func TestCommand_errors(t *testing.T) {
t.Parallel()

srv := testWebServer(t)
defer srv.Close()

u, err := url.Parse(srv.URL + "/missing-catalog.json")
env, err := testutils.NewTestEnv(testutils.TestEnvConfig{
WorkDir: t.TempDir(),
CatalogURL: "testdata/empty-catalog.json",
})
require.NoError(t, err)

ctx := context.Background()
t.Cleanup(env.Cleanup)

_, _, err = k6exec.Command(ctx, nil, &k6exec.Options{AppName: invalidAppName(t)})
require.Error(t, err)
require.ErrorIs(t, err, k6provision.ErrCache)
opts := &k6exec.Options{
Env: k6deps.Source{Ignore: true},
Manifest: k6deps.Source{Ignore: true},
BuildServiceURL: env.BuildServiceURL(),
}

_, _, err = k6exec.Command(ctx, nil, &k6exec.Options{ExtensionCatalogURL: u})
_, _, err = k6exec.Command(context.TODO(), nil, opts)
require.Error(t, err)
require.ErrorIs(t, err, k6provision.ErrBuild)
}

func testWebServer(t *testing.T) *httptest.Server {
t.Helper()

return httptest.NewServer(http.FileServer(http.Dir("testdata")))
}

func invalidAppName(t *testing.T) string {
t.Helper()

return strings.Repeat("too long", 2048)
require.ErrorIs(t, err, k6provider.ErrInvalidParameters)
}
Loading

0 comments on commit d9f1126

Please sign in to comment.