add destination to artifact block and validate it

This commit is contained in:
Alex Dadgar 2016-03-18 12:01:46 -07:00
parent c1b4c9bcc9
commit 823261c974
6 changed files with 131 additions and 2 deletions

View file

@ -99,6 +99,7 @@ type Task struct {
type TaskArtifact struct {
GetterSource string
GetterOptions map[string]string
RelativeDest string
}
// NewTask creates and initializes a new Task.

View file

@ -617,6 +617,7 @@ func parseArtifacts(result *[]*structs.TaskArtifact, list *ast.ObjectList) error
valid := []string{
"source",
"options",
"destination",
}
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
@ -629,6 +630,11 @@ func parseArtifacts(result *[]*structs.TaskArtifact, list *ast.ObjectList) error
delete(m, "options")
// Default to downloading to the local directory.
if _, ok := m["destination"]; !ok {
m["destination"] = "local/"
}
var ta structs.TaskArtifact
if err := mapstructure.WeakDecode(m, &ta); err != nil {
return err

View file

@ -131,12 +131,14 @@ func TestParse(t *testing.T) {
Artifacts: []*structs.TaskArtifact{
{
GetterSource: "http://foo.com/artifact",
RelativeDest: "local/",
GetterOptions: map[string]string{
"checksum": "md5:b8a4f3f72ecab0510a6a31e997461c5f",
},
},
{
GetterSource: "http://bar.com/artifact",
RelativeDest: "local/",
GetterOptions: map[string]string{
"checksum": "md5:ff1cc0d3432dad54d607c1505fb7245c",
},
@ -320,6 +322,58 @@ func TestParse(t *testing.T) {
nil,
true,
},
{
"artifacts.hcl",
&structs.Job{
ID: "binstore-storagelocker",
Name: "binstore-storagelocker",
Type: "service",
Priority: 50,
Region: "global",
TaskGroups: []*structs.TaskGroup{
&structs.TaskGroup{
Name: "binsl",
Count: 1,
Tasks: []*structs.Task{
&structs.Task{
Name: "binstore",
Driver: "docker",
Resources: &structs.Resources{
CPU: 100,
MemoryMB: 10,
DiskMB: 300,
IOPS: 0,
},
LogConfig: &structs.LogConfig{
MaxFiles: 10,
MaxFileSizeMB: 10,
},
Artifacts: []*structs.TaskArtifact{
{
GetterSource: "http://foo.com/bar",
GetterOptions: map[string]string{},
RelativeDest: "",
},
{
GetterSource: "http://foo.com/baz",
GetterOptions: map[string]string{},
RelativeDest: "local/",
},
{
GetterSource: "http://foo.com/bam",
GetterOptions: map[string]string{},
RelativeDest: "var/foo",
},
},
},
},
},
},
},
false,
},
}
for _, tc := range cases {

View file

@ -0,0 +1,21 @@
job "binstore-storagelocker" {
group "binsl" {
task "binstore" {
driver = "docker"
artifact {
source = "http://foo.com/bar"
destination = ""
}
artifact {
source = "http://foo.com/baz"
}
artifact {
source = "http://foo.com/bam"
destination = "var/foo"
}
resources {}
}
}
}

View file

@ -11,6 +11,7 @@ import (
"fmt"
"io"
"net/url"
"path/filepath"
"reflect"
"regexp"
"strconv"
@ -1913,6 +1914,10 @@ type TaskArtifact struct {
// GetterOptions are options to use when downloading the artifact using
// go-getter.
GetterOptions map[string]string `mapstructure:"options"`
// RelativeDest is the download destination given relative to the task's
// directory.
RelativeDest string `mapstructure:"destination"`
}
func (ta *TaskArtifact) Copy() *TaskArtifact {
@ -1925,16 +1930,36 @@ func (ta *TaskArtifact) Copy() *TaskArtifact {
return nta
}
func (ta *TaskArtifact) GoString() string {
return fmt.Sprintf("%+v", ta)
}
func (ta *TaskArtifact) Validate() error {
// Verify the source
var mErr multierror.Error
if ta.GetterSource == "" {
mErr.Errors = append(mErr.Errors, fmt.Errorf("source must be specified"))
} else {
_, err := url.Parse(ta.GetterSource)
if err != nil {
mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid source URL %q: %v", ta.GetterSource, err))
}
}
_, err := url.Parse(ta.GetterSource)
// Verify the destination doesn't escape the tasks directory
alloc := "/foo/bar/"
abs, err := filepath.Abs(filepath.Join(alloc, ta.RelativeDest))
if err != nil {
mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid source URL %q: %v", ta.GetterSource, err))
mErr.Errors = append(mErr.Errors, err)
return mErr.ErrorOrNil()
}
rel, err := filepath.Rel(alloc, abs)
if err != nil {
mErr.Errors = append(mErr.Errors, err)
return mErr.ErrorOrNil()
}
if strings.HasPrefix(rel, "..") {
mErr.Errors = append(mErr.Errors, fmt.Errorf("destination escapes task's directory"))
}
// Verify the checksum

View file

@ -777,6 +777,28 @@ func TestTaskArtifact_Validate_Source(t *testing.T) {
}
}
func TestTaskArtifact_Validate_Dest(t *testing.T) {
valid := &TaskArtifact{GetterSource: "google.com"}
if err := valid.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
valid.RelativeDest = "local/"
if err := valid.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
valid.RelativeDest = "local/.."
if err := valid.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
valid.RelativeDest = "local/../.."
if err := valid.Validate(); err == nil {
t.Fatalf("expected error: %v", err)
}
}
func TestTaskArtifact_Validate_Checksum(t *testing.T) {
cases := []struct {
Input *TaskArtifact