jobspec: Add Volume and VolumeMount declarations

This commit is contained in:
Danielle Lancashire 2019-07-25 16:42:11 +02:00
parent 1871c1edbc
commit b45bd36230
No known key found for this signature in database
GPG key ID: 8D65584EF3DDF91B
4 changed files with 137 additions and 0 deletions

View file

@ -53,6 +53,7 @@ func parseGroups(result *api.Job, list *ast.ObjectList) error {
"spread", "spread",
"network", "network",
"service", "service",
"volume",
} }
if err := helper.CheckHCLKeys(listVal, valid); err != nil { if err := helper.CheckHCLKeys(listVal, valid); err != nil {
return multierror.Prefix(err, fmt.Sprintf("'%s' ->", n)) return multierror.Prefix(err, fmt.Sprintf("'%s' ->", n))
@ -74,6 +75,7 @@ func parseGroups(result *api.Job, list *ast.ObjectList) error {
delete(m, "spread") delete(m, "spread")
delete(m, "network") delete(m, "network")
delete(m, "service") delete(m, "service")
delete(m, "volume")
// Build the group with the basic decode // Build the group with the basic decode
var g api.TaskGroup var g api.TaskGroup
@ -161,6 +163,13 @@ func parseGroups(result *api.Job, list *ast.ObjectList) error {
} }
} }
// Parse any volume declarations
if o := listVal.Filter("volume"); len(o.Items) > 0 {
if err := parseVolumes(&g.Volumes, o); err != nil {
return multierror.Prefix(err, "volume ->")
}
}
// Parse tasks // Parse tasks
if o := listVal.Filter("task"); len(o.Items) > 0 { if o := listVal.Filter("task"); len(o.Items) > 0 {
if err := parseTasks(&g.Tasks, o); err != nil { if err := parseTasks(&g.Tasks, o); err != nil {
@ -274,3 +283,61 @@ func parseRestartPolicy(final **api.RestartPolicy, list *ast.ObjectList) error {
*final = &result *final = &result
return nil return nil
} }
func parseVolumes(out *map[string]*api.Volume, list *ast.ObjectList) error {
volumes := make(map[string]*api.Volume, len(list.Items))
for _, item := range list.Items {
n := item.Keys[0].Token.Value().(string)
valid := []string{
"type",
"read_only",
"hidden",
"config",
}
if err := helper.CheckHCLKeys(item.Val, valid); err != nil {
return err
}
var m map[string]interface{}
if err := hcl.DecodeObject(&m, item.Val); err != nil {
return err
}
// TODO(dani): FIXME: this is gross but we don't have ObjectList.Filter here
var cfg map[string]interface{}
if cfgI, ok := m["config"]; ok {
cfgL, ok := cfgI.([]map[string]interface{})
if !ok {
return fmt.Errorf("Incorrect `config` type, expected map")
}
if len(cfgL) != 1 {
return fmt.Errorf("Expected single `config`, found %d", len(cfgL))
}
cfg = cfgL[0]
}
delete(m, "config")
var result api.Volume
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
WeaklyTypedInput: true,
Result: &result,
})
if err != nil {
return err
}
if err := dec.Decode(m); err != nil {
return err
}
result.Name = n
result.Config = cfg
volumes[n] = &result
}
*out = volumes
return nil
}

View file

@ -72,6 +72,7 @@ func parseTask(item *ast.ObjectItem) (*api.Task, error) {
"vault", "vault",
"kill_signal", "kill_signal",
"kind", "kind",
"volume_mount",
} }
if err := helper.CheckHCLKeys(listVal, valid); err != nil { if err := helper.CheckHCLKeys(listVal, valid); err != nil {
return nil, err return nil, err
@ -93,6 +94,7 @@ func parseTask(item *ast.ObjectItem) (*api.Task, error) {
delete(m, "service") delete(m, "service")
delete(m, "template") delete(m, "template")
delete(m, "vault") delete(m, "vault")
delete(m, "volume_mount")
// Build the task // Build the task
var t api.Task var t api.Task
@ -173,6 +175,14 @@ func parseTask(item *ast.ObjectItem) (*api.Task, error) {
} }
} }
// Parse volume mounts
if o := listVal.Filter("volume_mount"); len(o.Items) > 0 {
if err := parseVolumeMounts(&t.VolumeMounts, o); err != nil {
return multierror.Prefix(err, fmt.Sprintf(
"'%s', volume_mount ->", n))
}
}
// If we have resources, then parse that // If we have resources, then parse that
if o := listVal.Filter("resources"); len(o.Items) > 0 { if o := listVal.Filter("resources"); len(o.Items) > 0 {
var r api.Resources var r api.Resources
@ -504,3 +514,40 @@ func parseResources(result *api.Resources, list *ast.ObjectList) error {
return nil return nil
} }
func parseVolumeMounts(out *[]*api.VolumeMount, list *ast.ObjectList) error {
mounts := make([]*api.VolumeMount, len(list.Items))
for i, item := range list.Items {
valid := []string{
"volume",
"read_only",
"destination",
}
if err := helper.CheckHCLKeys(item.Val, valid); err != nil {
return err
}
var m map[string]interface{}
if err := hcl.DecodeObject(&m, item.Val); err != nil {
return err
}
var result api.VolumeMount
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
WeaklyTypedInput: true,
Result: &result,
})
if err != nil {
return err
}
if err := dec.Decode(m); err != nil {
return err
}
mounts[i] = &result
}
*out = mounts
return nil
}

View file

@ -86,6 +86,7 @@ func TestParse(t *testing.T) {
TaskGroups: []*api.TaskGroup{ TaskGroups: []*api.TaskGroup{
{ {
Name: helper.StringToPtr("outside"), Name: helper.StringToPtr("outside"),
Tasks: []*api.Task{ Tasks: []*api.Task{
{ {
Name: "outside", Name: "outside",
@ -110,6 +111,13 @@ func TestParse(t *testing.T) {
Operand: "=", Operand: "=",
}, },
}, },
Volumes: map[string]*api.Volume{
"foo": {
Name: "foo",
Type: "host",
},
},
Affinities: []*api.Affinity{ Affinities: []*api.Affinity{
{ {
LTarget: "${node.datacenter}", LTarget: "${node.datacenter}",
@ -187,6 +195,12 @@ func TestParse(t *testing.T) {
}, },
}, },
}, },
VolumeMounts: []*api.VolumeMount{
{
Volume: "foo",
Destination: "/mnt/foo",
},
},
Affinities: []*api.Affinity{ Affinities: []*api.Affinity{
{ {
LTarget: "${meta.foo}", LTarget: "${meta.foo}",

View file

@ -63,6 +63,10 @@ job "binstore-storagelocker" {
group "binsl" { group "binsl" {
count = 5 count = 5
volume "foo" {
type = "host"
}
restart { restart {
attempts = 5 attempts = 5
interval = "10m" interval = "10m"
@ -143,6 +147,11 @@ job "binstore-storagelocker" {
} }
} }
volume_mount {
volume = "foo"
destination = "/mnt/foo"
}
logs { logs {
max_files = 14 max_files = 14
max_file_size = 101 max_file_size = 101