5b1970468e
* landlock: git needs more files for private repositories This PR fixes artifact downloading so that git may work when cloning from private repositories. It needs - file read on /etc/passwd - dir read on /root/.ssh - file write on /root/.ssh/known_hosts Add these rules to the landlock rules for the artifact sandbox. * cr: use nonexistent instead of devnull Co-authored-by: Michael Schurter <mschurter@hashicorp.com> * cr: use go-homdir for looking up home directory * pr: pull go-homedir into explicit require * cr: fixup homedir tests in homeless root cases * cl: fix root test for real --------- Co-authored-by: Michael Schurter <mschurter@hashicorp.com>
240 lines
5.6 KiB
Go
240 lines
5.6 KiB
Go
package getter
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"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/mitchellh/go-homedir"
|
|
"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("replacements", 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)
|
|
|
|
homedir.DisableCache = true
|
|
t.Cleanup(func() {
|
|
homedir.DisableCache = false
|
|
})
|
|
|
|
t.Run("default", func(t *testing.T) {
|
|
t.Setenv("HOME", "/test")
|
|
result := environment("/a/b/c", "")
|
|
must.Eq(t, []string{
|
|
"HOME=/test",
|
|
"PATH=/usr/local/bin:/usr/bin:/bin",
|
|
"TMPDIR=/a/b/c/tmp",
|
|
}, result)
|
|
})
|
|
|
|
t.Run("append", func(t *testing.T) {
|
|
t.Setenv("HOME", "/test")
|
|
t.Setenv("ONE", "1")
|
|
t.Setenv("TWO", "2")
|
|
result := environment("/a/b/c", "ONE,TWO")
|
|
must.Eq(t, []string{
|
|
"HOME=/test",
|
|
"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("HOME", "/test")
|
|
t.Setenv("PATH", "/opt/bin")
|
|
t.Setenv("TMPDIR", "/scratch")
|
|
result := environment("/a/b/c", "PATH,TMPDIR")
|
|
must.Eq(t, []string{
|
|
"HOME=/test",
|
|
"PATH=/opt/bin",
|
|
"TMPDIR=/scratch",
|
|
}, result)
|
|
})
|
|
|
|
t.Run("missing", func(t *testing.T) {
|
|
t.Setenv("HOME", "/test")
|
|
result := environment("/a/b/c", "DOES_NOT_EXIST")
|
|
must.Eq(t, []string{
|
|
"DOES_NOT_EXIST=",
|
|
"HOME=/test",
|
|
"PATH=/usr/local/bin:/usr/bin:/bin",
|
|
"TMPDIR=/a/b/c/tmp",
|
|
}, result)
|
|
})
|
|
|
|
t.Run("homeless non-root", func(t *testing.T) {
|
|
testutil.RequireNonRoot(t)
|
|
|
|
// assert we fallback via go-homdir ...
|
|
userHome, err := homedir.Dir()
|
|
must.NoError(t, err)
|
|
|
|
// ... when HOME env var is not set, as is the case in some systemd setups
|
|
t.Setenv("HOME", "")
|
|
|
|
result := environment("/a/b/c", "")
|
|
must.Eq(t, []string{
|
|
fmt.Sprintf("HOME=%s", userHome),
|
|
"PATH=/usr/local/bin:/usr/bin:/bin",
|
|
"TMPDIR=/a/b/c/tmp",
|
|
}, result)
|
|
})
|
|
|
|
t.Run("homeless root", func(t *testing.T) {
|
|
testutil.RequireRoot(t)
|
|
|
|
t.Setenv("HOME", "/root") // fake running as full root
|
|
|
|
// assert we fallback via go-homdir ...
|
|
userHome, err := homedir.Dir()
|
|
must.NoError(t, err)
|
|
|
|
// ... when HOME env var is not set, as is the case in some systemd setups
|
|
t.Setenv("HOME", "")
|
|
|
|
result := environment("/a/b/c", "")
|
|
must.Eq(t, []string{
|
|
fmt.Sprintf("HOME=%s", userHome),
|
|
"PATH=/usr/local/bin:/usr/bin:/bin",
|
|
"TMPDIR=/a/b/c/tmp",
|
|
}, result)
|
|
})
|
|
}
|