Skip to content
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

Introduce a new flag to the k6 archive sub-command #2605

Merged
merged 2 commits into from Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 16 additions & 1 deletion cmd/archive.go
Expand Up @@ -9,7 +9,8 @@ import (
type cmdArchive struct {
gs *globalState

archiveOut string
archiveOut string
excludeEnvVars bool
}

func (c *cmdArchive) run(cmd *cobra.Command, args []string) error {
Expand All @@ -35,6 +36,12 @@ func (c *cmdArchive) run(cmd *cobra.Command, args []string) error {
return err
}

if c.excludeEnvVars {
c.gs.logger.Debug("environment variables will be excluded from the archive")

arc.Env = nil
}

err = arc.Write(f)
if cerr := f.Close(); err == nil && cerr != nil {
err = cerr
Expand All @@ -48,6 +55,14 @@ func (c *cmdArchive) flagSet() *pflag.FlagSet {
flags.AddFlagSet(optionFlagSet())
flags.AddFlagSet(runtimeOptionFlagSet(false))
flags.StringVarP(&c.archiveOut, "archive-out", "O", c.archiveOut, "archive output filename")
flags.BoolVarP(
&c.excludeEnvVars,
"exclude-env-vars",
"",
false,
"do not embed any environment variables (either from --env or the actual environment) in the archive metadata",
)

return flags
}

Expand Down
119 changes: 119 additions & 0 deletions cmd/archive_test.go
@@ -1,7 +1,13 @@
package cmd

import (
"archive/tar"
"bytes"
"encoding/json"
"errors"
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"

Expand Down Expand Up @@ -86,3 +92,116 @@ func TestArchiveThresholds(t *testing.T) {
})
}
}

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

// given some script that will be archived
fileName := "script.js"
testScript := []byte(`export default function () {}`)
testState := newGlobalTestState(t)
require.NoError(t, afero.WriteFile(testState.fs, filepath.Join(testState.cwd, fileName), testScript, 0o644))

// when we do archiving and passing the `--env` flags
testState.args = []string{"k6", "--env", "ENV1=lorem", "--env", "ENV2=ipsum", "archive", fileName}

newRootCommand(testState.globalState).execute()
require.NoError(t, untar(t, testState.fs, "archive.tar", "tmp/"))

data, err := afero.ReadFile(testState.fs, "tmp/metadata.json")
require.NoError(t, err)

metadata := struct {
Env map[string]string
}{}

// then unpacked metadata should contain the environment variables with the proper values
require.NoError(t, json.Unmarshal(data, &metadata))
require.Len(t, metadata.Env, 2)

require.Contains(t, metadata.Env, "ENV1")
require.Contains(t, metadata.Env, "ENV2")

require.Equal(t, "lorem", metadata.Env["ENV1"])
require.Equal(t, "ipsum", metadata.Env["ENV2"])
}

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

// given some script that will be archived
fileName := "script.js"
testScript := []byte(`export default function () {}`)
testState := newGlobalTestState(t)
require.NoError(t, afero.WriteFile(testState.fs, filepath.Join(testState.cwd, fileName), testScript, 0o644))

// when we do archiving and passing the `--env` flags altogether with `--exclude-env-vars` flag
testState.args = []string{"k6", "--env", "ENV1=lorem", "--env", "ENV2=ipsum", "archive", "--exclude-env-vars", fileName}

newRootCommand(testState.globalState).execute()
require.NoError(t, untar(t, testState.fs, "archive.tar", "tmp/"))

data, err := afero.ReadFile(testState.fs, "tmp/metadata.json")
require.NoError(t, err)

metadata := struct {
Env map[string]string
}{}

// then unpacked metadata should not contain any environment variables passed at the moment of archive creation
require.NoError(t, json.Unmarshal(data, &metadata))
require.Len(t, metadata.Env, 0)
}

// untar untars a `fileName` file to a `destination` path
func untar(t *testing.T, fs afero.Fs, fileName string, destination string) error {
t.Helper()

archiveFile, err := afero.ReadFile(fs, fileName)
if err != nil {
return err
}

reader := bytes.NewBuffer(archiveFile)

tr := tar.NewReader(reader)

for {
header, err := tr.Next()
switch {
case errors.Is(err, io.EOF):
return nil
case err != nil:
return err
case header == nil:
continue
}

// as long as this code in a test helper, we can safely
// omit G305: File traversal when extracting zip/tar archive
target := filepath.Join(destination, header.Name) //nolint:gosec

switch header.Typeflag {
case tar.TypeDir:
if _, err := fs.Stat(target); err != nil && !os.IsNotExist(err) {
return err
}

if err := fs.MkdirAll(target, 0o755); err != nil {
return err
}
case tar.TypeReg:
f, err := fs.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
if err != nil {
return err
}
defer func() { _ = f.Close() }()

// as long as this code in a test helper, we can safely
// omit G110: Potential DoS vulnerability via decompression bomb
if _, err := io.Copy(f, tr); err != nil { //nolint:gosec
return err
}
}
}
}