open-nomad/client/allocrunner/taskrunner/getter/util_test.go
Seth Hoenig caba88128e
artifact: enable reading system git/mercurial configuration (#15903)
This PR adjusts the artifact sandbox on Linux to enable reading from known
system-wide git or mercurial configuration, if they exist.

Folks doing something odd like specifying custom paths for global config will
need to use the standard locations, or disable artifact filesystem isolation.
2023-01-26 13:07:40 -06:00

217 lines
5.1 KiB
Go

package getter
import (
"errors"
"os"
"path/filepath"
"testing"
"github.com/hashicorp/go-getter"
"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/client/testutil"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/shoenig/go-landlock"
"github.com/shoenig/test/must"
)
func TestUtil_getURL(t *testing.T) {
ci.Parallel(t)
cases := []struct {
name string
artifact *structs.TaskArtifact
expURL string
expErr *Error
}{{
name: "basic http",
artifact: &structs.TaskArtifact{GetterSource: "example.com"},
expURL: "example.com",
expErr: nil,
}, {
name: "bad url",
artifact: &structs.TaskArtifact{GetterSource: "::example.com"},
expURL: "",
expErr: &Error{
URL: "::example.com",
Err: errors.New(`failed to parse source URL "::example.com": parse "::example.com": missing protocol scheme`),
Recoverable: false,
},
}, {
name: "option",
artifact: &structs.TaskArtifact{
GetterSource: "git::github.com/hashicorp/nomad",
GetterOptions: map[string]string{"sshkey": "abc123"},
},
expURL: "git::github.com/hashicorp/nomad?sshkey=abc123",
expErr: nil,
}, {
name: "github case",
artifact: &structs.TaskArtifact{
GetterSource: "git@github.com:hashicorp/nomad.git",
GetterOptions: map[string]string{"sshkey": "abc123"},
},
expURL: "git@github.com:hashicorp/nomad.git?sshkey=abc123",
expErr: nil,
}}
env := noopTaskEnv("/path/to/task")
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
result, err := getURL(env, tc.artifact)
err2, _ := err.(*Error)
must.Equal(t, tc.expErr, err2)
must.Eq(t, tc.expURL, result)
})
}
}
func TestUtil_getDestination(t *testing.T) {
ci.Parallel(t)
env := noopTaskEnv("/path/to/task")
t.Run("ok", func(t *testing.T) {
result, err := getDestination(env, &structs.TaskArtifact{
RelativeDest: "local/downloads",
})
must.NoError(t, err)
must.Eq(t, "/path/to/task/local/downloads", result)
})
t.Run("escapes", func(t *testing.T) {
result, err := getDestination(env, &structs.TaskArtifact{
RelativeDest: "../../../../../../../etc",
})
must.EqError(t, err, "artifact destination path escapes alloc directory")
must.Eq(t, "", result)
})
}
func TestUtil_getMode(t *testing.T) {
ci.Parallel(t)
cases := []struct {
mode string
exp getter.ClientMode
}{
{mode: structs.GetterModeFile, exp: getter.ClientModeFile},
{mode: structs.GetterModeDir, exp: getter.ClientModeDir},
{mode: structs.GetterModeAny, exp: getter.ClientModeAny},
}
for _, tc := range cases {
t.Run(tc.mode, func(t *testing.T) {
artifact := &structs.TaskArtifact{GetterMode: tc.mode}
result := getMode(artifact)
must.Eq(t, tc.exp, result)
})
}
}
func TestUtil_getHeaders(t *testing.T) {
ci.Parallel(t)
env := upTaskEnv("/path/to/task")
t.Run("empty", func(t *testing.T) {
result := getHeaders(env, &structs.TaskArtifact{
GetterHeaders: nil,
})
must.Nil(t, result)
})
t.Run("replacments", func(t *testing.T) {
result := getHeaders(env, &structs.TaskArtifact{
GetterHeaders: map[string]string{
"color": "red",
"number": "six",
},
})
must.MapEq(t, map[string][]string{
"Color": {"RED"},
"Number": {"SIX"},
}, result)
})
}
func TestUtil_getTaskDir(t *testing.T) {
ci.Parallel(t)
env := noopTaskEnv("/path/to/alloc/task")
allocDir, taskDir := getWritableDirs(env)
must.Eq(t, "/path/to/alloc", allocDir)
must.Eq(t, "/path/to/alloc/task", taskDir)
}
func TestUtil_environment(t *testing.T) {
// not parallel
testutil.RequireLinux(t)
t.Run("default", func(t *testing.T) {
result := environment("/a/b/c", "")
must.Eq(t, []string{
"PATH=/usr/local/bin:/usr/bin:/bin",
"TMPDIR=/a/b/c/tmp",
}, result)
})
t.Run("append", func(t *testing.T) {
t.Setenv("ONE", "1")
t.Setenv("TWO", "2")
result := environment("/a/b/c", "ONE,TWO")
must.Eq(t, []string{
"ONE=1",
"PATH=/usr/local/bin:/usr/bin:/bin",
"TMPDIR=/a/b/c/tmp",
"TWO=2",
}, result)
})
t.Run("override", func(t *testing.T) {
t.Setenv("PATH", "/opt/bin")
t.Setenv("TMPDIR", "/scratch")
result := environment("/a/b/c", "PATH,TMPDIR")
must.Eq(t, []string{
"PATH=/opt/bin",
"TMPDIR=/scratch",
}, result)
})
t.Run("missing", func(t *testing.T) {
result := environment("/a/b/c", "DOES_NOT_EXIST")
must.Eq(t, []string{
"DOES_NOT_EXIST=",
"PATH=/usr/local/bin:/usr/bin:/bin",
"TMPDIR=/a/b/c/tmp",
}, result)
})
}
func TestUtil_loadVersionControlGlobalConfigs(t *testing.T) {
const filePerm = 0o644
const dirPerm = 0o755
fakeEtc := t.TempDir()
var (
gitFile = filepath.Join(fakeEtc, "gitconfig")
hgFile = filepath.Join(fakeEtc, "hgrc")
hgDir = filepath.Join(fakeEtc, "hgrc.d")
)
err := os.WriteFile(gitFile, []byte("git"), filePerm)
must.NoError(t, err)
err = os.WriteFile(hgFile, []byte("hg"), filePerm)
must.NoError(t, err)
err = os.Mkdir(hgDir, dirPerm)
must.NoError(t, err)
paths := loadVersionControlGlobalConfigs(gitFile, hgFile, hgDir)
must.SliceEqual(t, []*landlock.Path{
landlock.File(gitFile, "r"),
landlock.File(hgFile, "r"),
landlock.Dir(hgDir, "r"),
}, paths)
}