Files
nomad/client/allocrunner/taskrunner/getter/params_test.go
Chris Roberts fd1e40537c [artifact] add artifact inspection after download (#26608)
This adds artifact inspection after download to detect any issues
with the content fetched. Currently this means checking for any
symlinks within the artifact that resolve outside the task or
allocation directories. On platforms where lockdown is available
(some Linux) this inspection is not performed.

The inspection can be disabled with the DisableArtifactInspection
option. A dedicated option for disabling this behavior allows
the DisableFilesystemIsolation option to be enabled but still
have artifacts inspected after download.
2025-08-27 10:37:34 -07:00

165 lines
4.1 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package getter
import (
"context"
"io"
"strings"
"testing"
"time"
"github.com/hashicorp/go-getter"
"github.com/shoenig/test/must"
)
const paramsAsJSON = `
{
"http_read_timeout": 1000000000,
"http_max_bytes": 2000,
"gcs_timeout": 2000000000,
"git_timeout": 3000000000,
"hg_timeout": 4000000000,
"s3_timeout": 5000000000,
"decompression_limit_file_count": 3,
"decompression_limit_size": 98765,
"disable_artifact_inspection": false,
"disable_filesystem_isolation": true,
"filesystem_isolation_extra_paths": [
"f:r:/dev/urandom",
"d:rx:/opt/bin",
"d:r:/tmp/stash"
],
"set_environment_variables": "",
"artifact_mode": 2,
"artifact_insecure": false,
"artifact_source": "https://example.com/file.txt",
"artifact_destination": "local/out.txt",
"artifact_headers": {
"X-Nomad-Artifact": ["hi"]
},
"alloc_dir": "/path/to/alloc",
"task_dir": "/path/to/alloc/task",
"chown": true,
"user":"nobody"
}`
var paramsAsStruct = &parameters{
HTTPReadTimeout: 1 * time.Second,
HTTPMaxBytes: 2000,
GCSTimeout: 2 * time.Second,
GitTimeout: 3 * time.Second,
HgTimeout: 4 * time.Second,
S3Timeout: 5 * time.Second,
DecompressionLimitFileCount: 3,
DecompressionLimitSize: 98765,
DisableFilesystemIsolation: true,
FilesystemIsolationExtraPaths: []string{
"f:r:/dev/urandom",
"d:rx:/opt/bin",
"d:r:/tmp/stash",
},
Mode: getter.ClientModeFile,
Source: "https://example.com/file.txt",
Destination: "local/out.txt",
AllocDir: "/path/to/alloc",
TaskDir: "/path/to/alloc/task",
Headers: map[string][]string{
"X-Nomad-Artifact": {"hi"},
},
User: "nobody",
Chown: true,
}
func TestParameters_reader(t *testing.T) {
p := paramsAsStruct
reader := p.reader()
b, err := io.ReadAll(reader)
must.NoError(t, err)
must.EqJSON(t, paramsAsJSON, string(b))
}
func TestParameters_read(t *testing.T) {
reader := strings.NewReader(paramsAsJSON)
p := new(parameters)
err := p.read(reader)
must.NoError(t, err)
must.Equal(t, paramsAsStruct, p)
}
func TestParameters_deadline(t *testing.T) {
t.Run("typical", func(t *testing.T) {
dur := paramsAsStruct.deadline()
must.Eq(t, 31*time.Minute, dur)
})
t.Run("long", func(t *testing.T) {
params := &parameters{
HTTPReadTimeout: 1 * time.Hour,
GCSTimeout: 2 * time.Hour,
GitTimeout: 3 * time.Hour,
HgTimeout: 4 * time.Hour,
S3Timeout: 5 * time.Hour,
}
dur := params.deadline()
must.Eq(t, 5*time.Hour+1*time.Minute, dur)
})
}
func TestParameters_client(t *testing.T) {
ctx := context.Background()
c := paramsAsStruct.client(ctx)
must.NotNil(t, c)
// security options
must.False(t, c.Insecure)
must.True(t, c.DisableSymlinks)
must.Eq(t, umask, c.Umask)
// artifact options
must.Eq(t, "https://example.com/file.txt", c.Src)
must.Eq(t, "local/out.txt", c.Dst)
// decompression limits
const fileCountLimit = 3
const fileSizeLimit = 98765
must.Eq(t, fileSizeLimit, c.Decompressors["zip"].(*getter.ZipDecompressor).FileSizeLimit)
must.Eq(t, fileCountLimit, c.Decompressors["zip"].(*getter.ZipDecompressor).FilesLimit)
must.Eq(t, fileSizeLimit, c.Decompressors["tar.gz"].(*getter.TarGzipDecompressor).FileSizeLimit)
must.Eq(t, fileCountLimit, c.Decompressors["tar.gz"].(*getter.TarGzipDecompressor).FilesLimit)
must.Eq(t, fileSizeLimit, c.Decompressors["xz"].(*getter.XzDecompressor).FileSizeLimit)
// xz does not support files count limit
}
func TestParameters_Equal_headers(t *testing.T) {
p1 := &parameters{
Headers: map[string][]string{
"East": []string{"New York", "Florida"},
"West": []string{"California"},
},
}
p2 := &parameters{
Headers: map[string][]string{
"East": []string{"New York", "Florida"},
"West": []string{"California"},
},
}
// equal
must.Equal(t, p1, p2)
// equal
must.Equal(t, p1, &parameters{
Headers: map[string][]string{
"West": {"California"},
"East": {"Florida", "New York"},
},
})
// not equal
p2.Headers["East"] = []string{"New York"}
must.NotEqual(t, p1, p2)
}