Skip to content

Commit

Permalink
Add network stanza to group
Browse files Browse the repository at this point in the history
Adds a network stanza and additional options to the task group level
in prep for allowing shared networking between tasks of an alloc.
  • Loading branch information
nickethier committed Apr 29, 2019
1 parent 7e2f78a commit 6697776
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 55 deletions.
2 changes: 2 additions & 0 deletions api/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,13 @@ func (r *Resources) Merge(other *Resources) {
type Port struct {
Label string
Value int `mapstructure:"static"`
To int `mapstructure:"to"`
}

// NetworkResource is used to describe required network
// resources of a given task.
type NetworkResource struct {
Mode string
Device string
CIDR string
IP string
Expand Down
4 changes: 4 additions & 0 deletions api/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ type TaskGroup struct {
EphemeralDisk *EphemeralDisk
Update *UpdateStrategy
Migrate *MigrateStrategy
Networks []*NetworkResource
Meta map[string]string
}

Expand Down Expand Up @@ -603,6 +604,9 @@ func (g *TaskGroup) Canonicalize(job *Job) {
for _, a := range g.Affinities {
a.Canonicalize()
}
for _, n := range g.Networks {
n.Canonicalize()
}
}

// Constrain is used to add a constraint to a task group.
Expand Down
52 changes: 31 additions & 21 deletions command/agent/job_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,6 @@ func (s *HTTPServer) jobUpdate(resp http.ResponseWriter, req *http.Request,
if jobName != "" && *args.Job.ID != jobName {
return nil, CodedError(400, "Job ID does not match name")
}

sJob := ApiJobToStructJob(args.Job)

regReq := structs.JobRegisterRequest{
Expand Down Expand Up @@ -667,6 +666,7 @@ func ApiTgToStructsTG(taskGroup *api.TaskGroup, tg *structs.TaskGroup) {
tg.Meta = taskGroup.Meta
tg.Constraints = ApiConstraintsToStructs(taskGroup.Constraints)
tg.Affinities = ApiAffinitiesToStructs(taskGroup.Affinities)
tg.Networks = ApiNetworkResourceToStructs(taskGroup.Networks)

tg.RestartPolicy = &structs.RestartPolicy{
Attempts: *taskGroup.RestartPolicy.Attempts,
Expand Down Expand Up @@ -860,29 +860,51 @@ func ApiResourcesToStructs(in *api.Resources) *structs.Resources {
out.IOPS = *in.IOPS
}

if l := len(in.Networks); l != 0 {
out.Networks = make([]*structs.NetworkResource, l)
for i, nw := range in.Networks {
out.Networks[i] = &structs.NetworkResource{
if len(in.Networks) != 0 {
out.Networks = ApiNetworkResourceToStructs(in.Networks)
}

if l := len(in.Devices); l != 0 {
out.Devices = make([]*structs.RequestedDevice, l)
for i, d := range in.Devices {
out.Devices[i] = &structs.RequestedDevice{
Name: d.Name,
Count: *d.Count,
Constraints: ApiConstraintsToStructs(d.Constraints),
Affinities: ApiAffinitiesToStructs(d.Affinities),
}
}
}

return out
}

func ApiNetworkResourceToStructs(in []*api.NetworkResource) []*structs.NetworkResource {
var out []*structs.NetworkResource
if l := len(in); l != 0 {
out = make([]*structs.NetworkResource, l)
for i, nw := range in {
out[i] = &structs.NetworkResource{
Mode: nw.Mode,
CIDR: nw.CIDR,
IP: nw.IP,
MBits: *nw.MBits,
}

if l := len(nw.DynamicPorts); l != 0 {
out.Networks[i].DynamicPorts = make([]structs.Port, l)
out[i].DynamicPorts = make([]structs.Port, l)
for j, dp := range nw.DynamicPorts {
out.Networks[i].DynamicPorts[j] = structs.Port{
out[i].DynamicPorts[j] = structs.Port{
Label: dp.Label,
Value: dp.Value,
}
}
}

if l := len(nw.ReservedPorts); l != 0 {
out.Networks[i].ReservedPorts = make([]structs.Port, l)
out[i].ReservedPorts = make([]structs.Port, l)
for j, rp := range nw.ReservedPorts {
out.Networks[i].ReservedPorts[j] = structs.Port{
out[i].ReservedPorts[j] = structs.Port{
Label: rp.Label,
Value: rp.Value,
}
Expand All @@ -891,18 +913,6 @@ func ApiResourcesToStructs(in *api.Resources) *structs.Resources {
}
}

if l := len(in.Devices); l != 0 {
out.Devices = make([]*structs.RequestedDevice, l)
for i, d := range in.Devices {
out.Devices[i] = &structs.RequestedDevice{
Name: d.Name,
Count: *d.Count,
Constraints: ApiConstraintsToStructs(d.Constraints),
Affinities: ApiAffinitiesToStructs(d.Affinities),
}
}
}

return out
}

Expand Down
85 changes: 53 additions & 32 deletions jobspec/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ func parseGroups(result *api.Job, list *ast.ObjectList) error {
"vault",
"migrate",
"spread",
"network",
}
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
return multierror.Prefix(err, fmt.Sprintf("'%s' ->", n))
Expand All @@ -334,6 +335,7 @@ func parseGroups(result *api.Job, list *ast.ObjectList) error {
delete(m, "vault")
delete(m, "migrate")
delete(m, "spread")
delete(m, "network")

// Build the group with the basic decode
var g api.TaskGroup
Expand Down Expand Up @@ -370,6 +372,15 @@ func parseGroups(result *api.Job, list *ast.ObjectList) error {
}
}

// Parse network
if o := listVal.Filter("network"); len(o.Items) > 0 {
networks, err := parseNetwork(o)
if err != nil {
return err
}
g.Networks = []*api.NetworkResource{networks}
}

// Parse reschedule policy
if o := listVal.Filter("reschedule"); len(o.Items) > 0 {
if err := parseReschedulePolicy(&g.ReschedulePolicy, o); err != nil {
Expand Down Expand Up @@ -1434,39 +1445,11 @@ func parseResources(result *api.Resources, list *ast.ObjectList) error {

// Parse the network resources
if o := listVal.Filter("network"); len(o.Items) > 0 {
if len(o.Items) > 1 {
return fmt.Errorf("only one 'network' resource allowed")
}

// Check for invalid keys
valid := []string{
"mbits",
"port",
}
if err := helper.CheckHCLKeys(o.Items[0].Val, valid); err != nil {
return multierror.Prefix(err, "resources, network ->")
}

var r api.NetworkResource
var m map[string]interface{}
if err := hcl.DecodeObject(&m, o.Items[0].Val); err != nil {
return err
}
if err := mapstructure.WeakDecode(m, &r); err != nil {
return err
}

var networkObj *ast.ObjectList
if ot, ok := o.Items[0].Val.(*ast.ObjectType); ok {
networkObj = ot.List
} else {
return fmt.Errorf("resource: should be an object")
}
if err := parsePorts(networkObj, &r); err != nil {
return multierror.Prefix(err, "resources, network, ports ->")
r, err := parseNetwork(o)
if err != nil {
return fmt.Errorf("resource, %v", err)
}

result.Networks = []*api.NetworkResource{&r}
result.Networks = []*api.NetworkResource{r}
}

// Parse the device resources
Expand Down Expand Up @@ -1536,11 +1519,49 @@ func parseResources(result *api.Resources, list *ast.ObjectList) error {
return nil
}

func parseNetwork(o *ast.ObjectList) (*api.NetworkResource, error) {
if len(o.Items) > 1 {
return nil, fmt.Errorf("only one 'network' resource allowed")
}

// Check for invalid keys
valid := []string{
"mode",
"mbits",
"port",
}
if err := helper.CheckHCLKeys(o.Items[0].Val, valid); err != nil {
return nil, multierror.Prefix(err, "network ->")
}

var r api.NetworkResource
var m map[string]interface{}
if err := hcl.DecodeObject(&m, o.Items[0].Val); err != nil {
return nil, err
}
if err := mapstructure.WeakDecode(m, &r); err != nil {
return nil, err
}

var networkObj *ast.ObjectList
if ot, ok := o.Items[0].Val.(*ast.ObjectType); ok {
networkObj = ot.List
} else {
return nil, fmt.Errorf("should be an object")
}
if err := parsePorts(networkObj, &r); err != nil {
return nil, multierror.Prefix(err, "network, ports ->")
}

return &r, nil
}

func parsePorts(networkObj *ast.ObjectList, nw *api.NetworkResource) error {
// Check for invalid keys
valid := []string{
"mbits",
"port",
"mode",
}
if err := helper.CheckHCLKeys(networkObj, valid); err != nil {
return err
Expand Down
44 changes: 44 additions & 0 deletions jobspec/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,50 @@ func TestParse(t *testing.T) {
},
false,
},
{
"tg-network.hcl",
&api.Job{
ID: helper.StringToPtr("foo"),
Name: helper.StringToPtr("foo"),
Datacenters: []string{"dc1"},
TaskGroups: []*api.TaskGroup{
{
Name: helper.StringToPtr("bar"),
Count: helper.IntToPtr(3),
Networks: []*api.NetworkResource{
{
Mode: "bridge",
ReservedPorts: []api.Port{
{
Label: "http",
Value: 80,
To: 8080,
},
},
},
},
Tasks: []*api.Task{
{
Name: "bar",
Driver: "raw_exec",
Config: map[string]interface{}{
"command": "bash",
"args": []interface{}{"-c", "echo hi"},
},
Resources: &api.Resources{
Networks: []*api.NetworkResource{
{
MBits: helper.IntToPtr(10),
},
},
},
},
},
},
},
},
false,
},
}

for _, tc := range cases {
Expand Down
25 changes: 25 additions & 0 deletions jobspec/test-fixtures/tg-network.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
job "foo" {
datacenters = ["dc1"]
group "bar" {
count = 3
network {
mode = "bridge"
port "http" {
static = 80
to = 8080
}
}
task "bar" {
driver = "raw_exec"
config {
command = "bash"
args = ["-c", "echo hi"]
}
resources {
network {
mbits = 10
}
}
}
}
}
Loading

0 comments on commit 6697776

Please sign in to comment.