artifact: fix sandbox behavior when destination is shared alloc directory (#15712)
This PR fixes the artifact sandbox (new in Nomad 1.5) to allow downloading artifacts into the shared 'alloc' directory made available to each task in a common allocation. Previously we assumed the 'alloc' dir would be mounted under the 'task' dir, but this is only the case in fs isolation: chroot; in other modes the alloc dir is elsewhere.
This commit is contained in:
parent
84cb5fb03d
commit
2a7c7d85a5
|
@ -36,8 +36,9 @@ type parameters struct {
|
|||
Destination string `json:"artifact_destination"`
|
||||
Headers map[string][]string `json:"artifact_headers"`
|
||||
|
||||
// Task Environment
|
||||
TaskDir string `json:"task_dir"`
|
||||
// Task Filesystem
|
||||
AllocDir string `json:"alloc_dir"`
|
||||
TaskDir string `json:"task_dir"`
|
||||
}
|
||||
|
||||
func (p *parameters) reader() io.Reader {
|
||||
|
|
|
@ -27,7 +27,8 @@ const paramsAsJSON = `
|
|||
"artifact_headers": {
|
||||
"X-Nomad-Artifact": ["hi"]
|
||||
},
|
||||
"task_dir": "/path/to/task"
|
||||
"alloc_dir": "/path/to/alloc",
|
||||
"task_dir": "/path/to/alloc/task"
|
||||
}`
|
||||
|
||||
var paramsAsStruct = ¶meters{
|
||||
|
@ -42,7 +43,8 @@ var paramsAsStruct = ¶meters{
|
|||
Mode: getter.ClientModeFile,
|
||||
Source: "https://example.com/file.txt",
|
||||
Destination: "local/out.txt",
|
||||
TaskDir: "/path/to/task",
|
||||
AllocDir: "/path/to/alloc",
|
||||
TaskDir: "/path/to/alloc/task",
|
||||
Headers: map[string][]string{
|
||||
"X-Nomad-Artifact": {"hi"},
|
||||
},
|
||||
|
|
|
@ -36,7 +36,7 @@ func (s *Sandbox) Get(env interfaces.EnvReplacer, artifact *structs.TaskArtifact
|
|||
|
||||
mode := getMode(artifact)
|
||||
headers := getHeaders(env, artifact)
|
||||
dir := getTaskDir(env)
|
||||
allocDir, taskDir := getWritableDirs(env)
|
||||
|
||||
params := ¶meters{
|
||||
// downloader configuration
|
||||
|
@ -55,8 +55,9 @@ func (s *Sandbox) Get(env interfaces.EnvReplacer, artifact *structs.TaskArtifact
|
|||
Destination: destination,
|
||||
Headers: headers,
|
||||
|
||||
// task environment
|
||||
TaskDir: dir,
|
||||
// task filesystem
|
||||
AllocDir: allocDir,
|
||||
TaskDir: taskDir,
|
||||
}
|
||||
|
||||
if err = s.runCmd(params); err != nil {
|
||||
|
|
|
@ -93,9 +93,13 @@ func getHeaders(env interfaces.EnvReplacer, artifact *structs.TaskArtifact) map[
|
|||
return headers
|
||||
}
|
||||
|
||||
func getTaskDir(env interfaces.EnvReplacer) string {
|
||||
p, _ := env.ClientPath("stub", false)
|
||||
return filepath.Dir(p)
|
||||
// getWritableDirs returns host paths to the task's allocation and task specific
|
||||
// directories - the locations into which a Task is allowed to download an artifact.
|
||||
func getWritableDirs(env interfaces.EnvReplacer) (string, string) {
|
||||
stub, _ := env.ClientPath("stub", false)
|
||||
taskDir := filepath.Dir(stub)
|
||||
allocDir := filepath.Dir(taskDir)
|
||||
return allocDir, taskDir
|
||||
}
|
||||
|
||||
// environment merges the default minimal environment per-OS with the set of
|
||||
|
|
|
@ -37,6 +37,6 @@ func defaultEnvironment(taskDir string) map[string]string {
|
|||
}
|
||||
|
||||
// lockdown applies only to Linux
|
||||
func lockdown(string) error {
|
||||
func lockdown(string, string) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ func defaultEnvironment(taskDir string) map[string]string {
|
|||
// dir - the task directory
|
||||
//
|
||||
// Only applies to Linux, when available.
|
||||
func lockdown(dir string) error {
|
||||
func lockdown(allocDir, taskDir string) error {
|
||||
// landlock not present in the kernel, do not sandbox
|
||||
if !landlock.Available() {
|
||||
return nil
|
||||
|
@ -74,7 +74,8 @@ func lockdown(dir string) error {
|
|||
landlock.Dir("/bin", "rx"),
|
||||
landlock.Dir("/usr/bin", "rx"),
|
||||
landlock.Dir("/usr/local/bin", "rx"),
|
||||
landlock.Dir(dir, "rwc"),
|
||||
landlock.Dir(allocDir, "rwc"),
|
||||
landlock.Dir(taskDir, "rwc"),
|
||||
}
|
||||
locker := landlock.New(paths...)
|
||||
return locker.Lock(landlock.Mandatory)
|
||||
|
|
|
@ -134,9 +134,10 @@ func TestUtil_getHeaders(t *testing.T) {
|
|||
func TestUtil_getTaskDir(t *testing.T) {
|
||||
ci.Parallel(t)
|
||||
|
||||
env := noopTaskEnv("/path/to/task")
|
||||
result := getTaskDir(env)
|
||||
must.Eq(t, "/path/to/task", result)
|
||||
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) {
|
||||
|
|
|
@ -19,7 +19,7 @@ func credentials() (uint32, uint32) {
|
|||
}
|
||||
|
||||
// lockdown has no effect on windows
|
||||
func lockdown(string) error {
|
||||
func lockdown(string, string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ func init() {
|
|||
|
||||
// sandbox the host filesystem for this process
|
||||
if !env.DisableFilesystemIsolation {
|
||||
if err := lockdown(env.TaskDir); err != nil {
|
||||
if err := lockdown(env.AllocDir, env.TaskDir); err != nil {
|
||||
subproc.Print("failed to sandbox %s process: %v", SubCommand, err)
|
||||
return subproc.ExitFailure
|
||||
}
|
||||
|
|
|
@ -93,18 +93,22 @@ func testLinux(t *testing.T) {
|
|||
|
||||
check("rawexec", "rawexec_file_default")
|
||||
check("rawexec", "rawexec_file_custom")
|
||||
check("rawexec", "rawexec_file_alloc_dots")
|
||||
check("rawexec", "rawexec_file_alloc_env")
|
||||
check("rawexec", "rawexec_zip_default")
|
||||
check("rawexec", "rawexec_zip_custom")
|
||||
check("rawexec", "rawexec_git_custom")
|
||||
|
||||
check("exec", "exec_file_default")
|
||||
check("exec", "exec_file_custom")
|
||||
check("exec", "exec_file_alloc")
|
||||
check("exec", "exec_zip_default")
|
||||
check("exec", "exec_zip_custom")
|
||||
check("exec", "exec_git_custom")
|
||||
|
||||
check("docker", "docker_file_default")
|
||||
check("docker", "docker_file_custom")
|
||||
check("docker", "docker_file_alloc")
|
||||
check("docker", "docker_zip_default")
|
||||
check("docker", "docker_zip_custom")
|
||||
check("docker", "docker_git_custom")
|
||||
|
|
|
@ -42,6 +42,42 @@ job "linux" {
|
|||
}
|
||||
}
|
||||
|
||||
task "rawexec_file_alloc_dots" {
|
||||
artifact {
|
||||
source = "https://raw.githubusercontent.com/hashicorp/go-set/main/go.mod"
|
||||
destination = "../alloc/go.mod"
|
||||
mode = "file"
|
||||
}
|
||||
driver = "raw_exec"
|
||||
config {
|
||||
command = "cat"
|
||||
args = ["../alloc/go.mod"]
|
||||
}
|
||||
resources {
|
||||
cpu = 16
|
||||
memory = 32
|
||||
disk = 64
|
||||
}
|
||||
}
|
||||
|
||||
task "rawexec_file_alloc_env" {
|
||||
artifact {
|
||||
source = "https://raw.githubusercontent.com/hashicorp/go-set/main/go.mod"
|
||||
destination = "${NOMAD_ALLOC_DIR}/go.mod"
|
||||
mode = "file"
|
||||
}
|
||||
driver = "raw_exec"
|
||||
config {
|
||||
command = "cat"
|
||||
args = ["${NOMAD_ALLOC_DIR}/go.mod"]
|
||||
}
|
||||
resources {
|
||||
cpu = 16
|
||||
memory = 32
|
||||
disk = 64
|
||||
}
|
||||
}
|
||||
|
||||
task "rawexec_zip_default" {
|
||||
artifact {
|
||||
source = "https://github.com/hashicorp/go-set/archive/refs/heads/main.zip"
|
||||
|
@ -127,6 +163,24 @@ job "linux" {
|
|||
}
|
||||
}
|
||||
|
||||
task "exec_file_alloc" {
|
||||
artifact {
|
||||
source = "https://raw.githubusercontent.com/hashicorp/go-set/main/go.mod"
|
||||
destination = "${NOMAD_ALLOC_DIR}/go.mod"
|
||||
mode = "file"
|
||||
}
|
||||
driver = "exec"
|
||||
config {
|
||||
command = "cat"
|
||||
args = ["${NOMAD_ALLOC_DIR}/go.mod"]
|
||||
}
|
||||
resources {
|
||||
cpu = 16
|
||||
memory = 32
|
||||
disk = 64
|
||||
}
|
||||
}
|
||||
|
||||
task "exec_zip_default" {
|
||||
artifact {
|
||||
source = "https://github.com/hashicorp/go-set/archive/refs/heads/main.zip"
|
||||
|
@ -212,6 +266,24 @@ job "linux" {
|
|||
}
|
||||
}
|
||||
|
||||
task "docker_file_alloc" {
|
||||
artifact {
|
||||
source = "https://raw.githubusercontent.com/hashicorp/go-set/main/go.mod"
|
||||
destination = "${NOMAD_ALLOC_DIR}/go.mod"
|
||||
mode = "file"
|
||||
}
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "bash:5"
|
||||
args = ["cat", "${NOMAD_ALLOC_DIR}/go.mod"]
|
||||
}
|
||||
resources {
|
||||
cpu = 16
|
||||
memory = 32
|
||||
disk = 64
|
||||
}
|
||||
}
|
||||
|
||||
task "docker_zip_default" {
|
||||
artifact {
|
||||
source = "https://github.com/hashicorp/go-set/archive/refs/heads/main.zip"
|
||||
|
|
Loading…
Reference in New Issue