Skip to content

Commit

Permalink
feat: added support for running archive
Browse files Browse the repository at this point in the history
  • Loading branch information
szkiba committed Aug 29, 2024
1 parent a34efde commit f32f656
Show file tree
Hide file tree
Showing 11 changed files with 259 additions and 11 deletions.
140 changes: 140 additions & 0 deletions cmd/archive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package cmd

import (
"archive/tar"
"encoding/json"
"errors"
"io"
"os"
"path/filepath"
"strings"

"github.com/grafana/k6deps"
"github.com/grafana/k6pack"
)

//nolint:forbidigo
func analyzeArchive(filename string) (k6deps.Dependencies, error) {
dir, err := os.MkdirTemp("", "k6-archive-*")
if err != nil {
return nil, err
}

defer os.RemoveAll(dir) //nolint:errcheck

err = extractArchive(dir, filename)
if err != nil {
return nil, err
}

opts, err := loadMetadata(dir)
if err != nil {
return nil, err
}

return k6deps.Analyze(opts)
}

//nolint:forbidigo
func loadMetadata(dir string) (*k6deps.Options, error) {
var meta archiveMetadata

data, err := os.ReadFile(filepath.Join(filepath.Clean(dir), "metadata.json"))
if err != nil {
return nil, err
}

if err = json.Unmarshal(data, &meta); err != nil {
return nil, err
}

opts := new(k6deps.Options)

opts.Manifest.Ignore = true // no manifest (yet) in archive

opts.Script.Name = filepath.Join(
dir,
"file",
filepath.FromSlash(strings.TrimPrefix(meta.Filename, "file:///")),
)

if value, found := meta.Env[k6deps.EnvDependencies]; found {
opts.Env.Name = k6deps.EnvDependencies
opts.Env.Contents = []byte(value)
} else {
opts.Env.Ignore = true
}

contents, err := os.ReadFile(filepath.Join(filepath.Clean(dir), "data"))
if err != nil {
return nil, err
}

script, _, err := k6pack.Pack(string(contents), &k6pack.Options{Filename: opts.Script.Name})
if err != nil {
return nil, err
}

opts.Script.Contents = script

return opts, nil
}

type archiveMetadata struct {
Filename string `json:"filename"`
Env map[string]string `json:"env"`
}

//nolint:forbidigo
func extractArchive(dir string, filename string) error {
input, err := os.Open(filepath.Clean(filename))
if err != nil {
return err
}

defer input.Close() //nolint:errcheck

reader := tar.NewReader(input)

const maxFileSize = 1024 * 1024 * 10 // 10M

for {
header, err := reader.Next()

switch {
case err == io.EOF:
return nil
case err != nil:
return err
case header == nil:
continue
}

target := filepath.Join(dir, filepath.Clean(filepath.FromSlash(header.Name)))

switch header.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(target, 0o750); err != nil {
return err
}

case tar.TypeReg:
if ext := filepath.Ext(target); ext == ".csv" || (ext == ".json" && filepath.Base(target) != "metadata.json") {
continue
}

file, err := os.OpenFile(filepath.Clean(target), os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
if err != nil {
return err
}

if _, err := io.CopyN(file, reader, maxFileSize); err != nil && !errors.Is(err, io.EOF) {
return err
}

if err = file.Close(); err != nil {
return err
}
}
}
}
29 changes: 29 additions & 0 deletions cmd/archive_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cmd

import (
"path/filepath"
"testing"

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

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

actual, err := analyzeArchive(filepath.Join("testdata", "archive.tar"))

require.NoError(t, err)

opts := &k6deps.Options{
Script: k6deps.Source{Name: filepath.Join("testdata", "combined.js")},
Manifest: k6deps.Source{Ignore: true},
Env: k6deps.Source{Ignore: true},
}

expected, err := k6deps.Analyze(opts)

require.NoError(t, err)

require.Equal(t, expected, actual)
}
32 changes: 22 additions & 10 deletions cmd/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/url"
"os"
"os/exec"
"strings"

"github.com/grafana/k6deps"
"github.com/grafana/k6exec"
Expand Down Expand Up @@ -68,18 +69,29 @@ func (s *state) persistentPreRunE(_ *cobra.Command, _ []string) error {
return nil
}

func (s *state) preRunE(sub *cobra.Command, args []string) error {
var (
deps k6deps.Dependencies
err error
dopts k6deps.Options
)

if scriptname, hasScript := scriptArg(sub, args); hasScript {
dopts.Script.Name = scriptname
func analyze(sub *cobra.Command, args []string) (k6deps.Dependencies, error) {
scriptname, hasScript := scriptArg(sub, args)
if !hasScript {
return k6deps.Analyze(&k6deps.Options{})
}

if strings.HasSuffix(scriptname, ".tar") {
return analyzeArchive(scriptname)
}

deps, err = k6deps.Analyze(&dopts)
return analyzeScript(scriptname)
}

func analyzeScript(filename string) (k6deps.Dependencies, error) {
var opts k6deps.Options

opts.Script.Name = filename

return k6deps.Analyze(&opts)
}

func (s *state) preRunE(sub *cobra.Command, args []string) error {
deps, err := analyze(sub, args)
if err != nil {
return err
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/state_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ func Test_preRunE(t *testing.T) {
arg := filepath.Join("testdata", "script.js")
require.NoError(t, st.preRunE(sub, []string{arg}))

arg = filepath.Join("testdata", "archive.tar")
require.NoError(t, st.preRunE(sub, []string{arg}))

arg = filepath.Join("testdata", "invalid_constraint.js")
require.Error(t, st.preRunE(sub, []string{arg}))

Expand Down
Binary file added cmd/testdata/archive.tar
Binary file not shown.
13 changes: 13 additions & 0 deletions cmd/testdata/combined.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"use k6 = 0.52";
"use k6 with k6/x/faker >= 0.3.0";
"use k6 with k6/x/sql >= 0.4.0";

import faker from "./faker.js";
import sqlite from "./sqlite.js";

export { setup, teardown } from "./sqlite.js";

export default () => {
faker();
sqlite();
};
11 changes: 11 additions & 0 deletions cmd/testdata/faker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// source: https://github.com/szkiba/xk6-faker/blob/v0.3.0/examples/custom-faker.js
"use k6 = 0.52";
import { Faker } from "k6/x/faker";

const faker = new Faker(11);

export default function () {
console.log(faker.person.firstName());
}

// output: Josiah
24 changes: 24 additions & 0 deletions cmd/testdata/sqlite.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// source: https://github.com/grafana/xk6-sql/blob/v0.4.0/examples/sqlite3_test.js
import sql from "k6/x/sql";

const db = sql.open("sqlite3", "./test.db");

export function setup() {
db.exec(`CREATE TABLE IF NOT EXISTS keyvalues (
id integer PRIMARY KEY AUTOINCREMENT,
key varchar NOT NULL,
value varchar);`);
}

export function teardown() {
db.close();
}

export default function () {
db.exec("INSERT INTO keyvalues (key, value) VALUES('plugin-name', 'k6-plugin-sql');");

let results = sql.query(db, "SELECT * FROM keyvalues WHERE key = $1;", "plugin-name");
for (const row of results) {
console.log(`key: ${row.key}, value: ${row.value}`);
}
}
1 change: 1 addition & 0 deletions examples/faker.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// source: https://github.com/szkiba/xk6-faker/blob/v0.3.0/examples/custom-faker.js
"use k6 = 0.52";
import { Faker } from "k6/x/faker";

const faker = new Faker(11);
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/grafana/clireadme v0.1.0
github.com/grafana/k6build v0.3.0
github.com/grafana/k6deps v0.1.4
github.com/grafana/k6pack v0.2.2
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
github.com/samber/slog-logrus/v2 v2.5.0
github.com/sirupsen/logrus v1.9.3
Expand All @@ -23,7 +24,6 @@ require (
github.com/google/btree v1.1.2 // indirect
github.com/grafana/k6catalog v0.1.0 // indirect
github.com/grafana/k6foundry v0.2.0 // indirect
github.com/grafana/k6pack v0.2.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand Down
15 changes: 15 additions & 0 deletions releases/v0.1.6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
k6exec `v0.1.6` is here 🎉!

This is an internal maintenance release.

**New features**:

- The archive file created with the `archive` command can now be run using the `run` command.
```bash
k6exec run archive.tar
```

**Dependency upgrades**:

- k6build v0.3.0

0 comments on commit f32f656

Please sign in to comment.