api: restructure client

This commit is contained in:
Ryan Uber
2015-09-09 13:02:39 -07:00
parent aa08c1c01b
commit 5d1e515112
7 changed files with 255 additions and 221 deletions

88
api/compose_test.go Normal file
View File

@@ -0,0 +1,88 @@
package api
import (
"reflect"
"testing"
)
func TestCompose(t *testing.T) {
// Compose a task
task := NewTask("mytask", "docker").
SetConfig("foo", "bar").
SetConfig("baz", "zip")
// Require some amount of resources
task.Require(&Resources{
CPU: 1.25,
MemoryMB: 1024,
DiskMB: 2048,
IOPS: 1024,
Networks: []*NetworkResource{
&NetworkResource{
CIDR: "0.0.0.0/0",
MBits: 100,
ReservedPorts: []int{80, 443},
},
},
})
// Compose a task group
grp := NewTaskGroup("mygroup", 2).
Constrain(HardConstraint("kernel.name", "=", "linux")).
Constrain(SoftConstraint("memory.totalbytes", ">=", "128000000", 1)).
SetMeta("foo", "bar").
SetMeta("baz", "zip").
AddTask(task)
// Check that the composed result looks correct
expect := &TaskGroup{
Name: "mygroup",
Count: 2,
Constraints: []*Constraint{
&Constraint{
Hard: true,
LTarget: "kernel.name",
RTarget: "linux",
Operand: "=",
Weight: 0,
},
&Constraint{
Hard: false,
LTarget: "memory.totalbytes",
RTarget: "128000000",
Operand: ">=",
Weight: 1,
},
},
Tasks: []*Task{
&Task{
Name: "mytask",
Driver: "docker",
Resources: &Resources{
CPU: 1.25,
MemoryMB: 1024,
DiskMB: 2048,
IOPS: 1024,
Networks: []*NetworkResource{
&NetworkResource{
CIDR: "0.0.0.0/0",
MBits: 100,
ReservedPorts: []int{80, 443},
},
},
},
Config: map[string]string{
"foo": "bar",
"baz": "zip",
},
},
},
Meta: map[string]string{
"foo": "bar",
"baz": "zip",
},
}
if !reflect.DeepEqual(grp, expect) {
t.Fatalf("expect: %#v, got: %#v", expect, grp)
}
}

33
api/constraint.go Normal file
View File

@@ -0,0 +1,33 @@
package api
// Constraint is used to serialize a job placement constraint.
type Constraint struct {
Hard bool
LTarget string
RTarget string
Operand string
Weight int
}
// HardConstraint is used to create a new hard constraint.
func HardConstraint(left, operand, right string) *Constraint {
return constraint(left, operand, right, true, 0)
}
// SoftConstraint is used to create a new soft constraint. It
// takes an additional weight parameter to allow balancing
// multiple soft constraints amongst eachother.
func SoftConstraint(left, operand, right string, weight int) *Constraint {
return constraint(left, operand, right, false, weight)
}
// constraint generates a new job placement constraint.
func constraint(left, operand, right string, hard bool, weight int) *Constraint {
return &Constraint{
Hard: hard,
LTarget: left,
RTarget: right,
Operand: operand,
Weight: weight,
}
}

32
api/constraint_test.go Normal file
View File

@@ -0,0 +1,32 @@
package api
import (
"reflect"
"testing"
)
func TestCompose_Constraints(t *testing.T) {
c := HardConstraint("kernel.name", "=", "darwin")
expect := &Constraint{
Hard: true,
LTarget: "kernel.name",
RTarget: "darwin",
Operand: "=",
Weight: 0,
}
if !reflect.DeepEqual(c, expect) {
t.Fatalf("expect: %#v, got: %#v", expect, c)
}
c = SoftConstraint("memory.totalbytes", ">=", "250000000", 5)
expect = &Constraint{
Hard: false,
LTarget: "memory.totalbytes",
RTarget: "250000000",
Operand: ">=",
Weight: 5,
}
if !reflect.DeepEqual(c, expect) {
t.Fatalf("expect: %#v, got: %#v", expect, c)
}
}

View File

@@ -97,145 +97,3 @@ type registerJobRequest struct {
type registerJobResponse struct {
EvalID string
}
// Constraint is used to serialize a job placement constraint.
type Constraint struct {
Hard bool
LTarget string
RTarget string
Operand string
Weight int
}
// HardConstraint is used to create a new hard constraint.
func HardConstraint(left, operand, right string) *Constraint {
return constraint(left, operand, right, true, 0)
}
// SoftConstraint is used to create a new soft constraint. It
// takes an additional weight parameter to allow balancing
// multiple soft constraints amongst eachother.
func SoftConstraint(left, operand, right string, weight int) *Constraint {
return constraint(left, operand, right, false, weight)
}
// constraint generates a new job placement constraint.
func constraint(left, operand, right string, hard bool, weight int) *Constraint {
return &Constraint{
Hard: hard,
LTarget: left,
RTarget: right,
Operand: operand,
Weight: weight,
}
}
// TaskGroup is the unit of scheduling.
type TaskGroup struct {
Name string
Count int
Constraints []*Constraint
Tasks []*Task
Meta map[string]string
}
// NewTaskGroup creates a new TaskGroup.
func NewTaskGroup(name string, count int) *TaskGroup {
return &TaskGroup{
Name: name,
Count: count,
}
}
// Constrain is used to add a constraint to a task group.
func (g *TaskGroup) Constrain(c *Constraint) *TaskGroup {
g.Constraints = append(g.Constraints, c)
return g
}
// AddMeta is used to add a meta k/v pair to a task group
func (g *TaskGroup) AddMeta(key, val string) *TaskGroup {
if g.Meta == nil {
g.Meta = make(map[string]string)
}
g.Meta[key] = val
return g
}
// AddTask is used to add a new task to a task group.
func (g *TaskGroup) AddTask(t *Task) *TaskGroup {
g.Tasks = append(g.Tasks, t)
return g
}
// Task is a single process in a task group.
type Task struct {
Name string
Driver string
Config map[string]string
Constraints []*Constraint
Resources *Resources
Meta map[string]string
}
// NewTask creates and initializes a new Task.
func NewTask(name, driver string) *Task {
return &Task{
Name: name,
Driver: driver,
}
}
// Configure is used to configure a single k/v pair on
// the task.
func (t *Task) Configure(key, val string) *Task {
if t.Config == nil {
t.Config = make(map[string]string)
}
t.Config[key] = val
return t
}
// AddMeta is used to add metadata k/v pairs to the task.
func (t *Task) AddMeta(key, val string) *Task {
if t.Meta == nil {
t.Meta = make(map[string]string)
}
t.Meta[key] = val
return t
}
// Require is used to add resource requirements to a task.
// It creates and initializes the task resources.
func (t *Task) Require(r *Resources) *Task {
if t.Resources == nil {
t.Resources = &Resources{}
}
if r == nil {
return t
}
t.Resources.CPU += r.CPU
t.Resources.MemoryMB += r.MemoryMB
t.Resources.DiskMB += r.DiskMB
t.Resources.IOPS += r.IOPS
return t
}
// Resources encapsulates the required resources of
// a given task or task group.
type Resources struct {
CPU float64
MemoryMB int
DiskMB int
IOPS int
Networks []*NetworkResource
}
// NetworkResource is used to describe required network
// resources of a given task.
type NetworkResource struct {
Public bool
CIDR string
ReservedPorts []int
MBits int
}

View File

@@ -178,82 +178,3 @@ func TestJobs_Delete(t *testing.T) {
t.Fatalf("expected 0 jobs, got: %d", n)
}
}
func TestJobs_Constraints(t *testing.T) {
{
c := HardConstraint("kernel.name", "=", "darwin")
expect := &Constraint{
Hard: true,
LTarget: "kernel.name",
RTarget: "darwin",
Operand: "=",
Weight: 0,
}
if !reflect.DeepEqual(c, expect) {
t.Fatalf("expect: %#v, got: %#v", expect, c)
}
}
{
c := SoftConstraint("memory.totalbytes", ">=", "250000000", 5)
expect := &Constraint{
Hard: false,
LTarget: "memory.totalbytes",
RTarget: "250000000",
Operand: ">=",
Weight: 5,
}
if !reflect.DeepEqual(c, expect) {
t.Fatalf("expect: %#v, got: %#v", expect, c)
}
}
}
func TestJobs_Compose(t *testing.T) {
// Compose a task
task := NewTask("mytask", "docker").
Require(&Resources{CPU: 1.25, MemoryMB: 1024}).
Require(&Resources{DiskMB: 2048}).
Configure("foo", "bar").
Configure("baz", "zip")
// Compose a task group
grp := NewTaskGroup("mytask", 2).
Constrain(HardConstraint("kernel.name", "=", "linux")).
Constrain(SoftConstraint("memory.totalbytes", ">=", "128000000", 1)).
AddMeta("foo", "bar").
AddMeta("baz", "zip").
AddTask(task)
// Check that the composed result looks correct
expect := &TaskGroup{
Name: "mytask",
Count: 2,
Constraints: []*Constraint{
HardConstraint("kernel.name", "=", "linux"),
SoftConstraint("memory.totalbytes", ">=", "128000000", 1),
},
Tasks: []*Task{
&Task{
Name: "mytask",
Driver: "docker",
Resources: &Resources{
CPU: 1.25,
MemoryMB: 1024,
DiskMB: 2048,
},
Config: map[string]string{
"foo": "bar",
"baz": "zip",
},
},
},
Meta: map[string]string{
"foo": "bar",
"baz": "zip",
},
}
if !reflect.DeepEqual(grp, expect) {
t.Fatalf("expect: %#v, got: %#v", expect, grp)
}
}

20
api/resources.go Normal file
View File

@@ -0,0 +1,20 @@
package api
// Resources encapsulates the required resources of
// a given task or task group.
type Resources struct {
CPU float64
MemoryMB int
DiskMB int
IOPS int
Networks []*NetworkResource
}
// NetworkResource is used to describe required network
// resources of a given task.
type NetworkResource struct {
Public bool
CIDR string
ReservedPorts []int
MBits int
}

82
api/tasks.go Normal file
View File

@@ -0,0 +1,82 @@
package api
// TaskGroup is the unit of scheduling.
type TaskGroup struct {
Name string
Count int
Constraints []*Constraint
Tasks []*Task
Meta map[string]string
}
// NewTaskGroup creates a new TaskGroup.
func NewTaskGroup(name string, count int) *TaskGroup {
return &TaskGroup{
Name: name,
Count: count,
}
}
// Constrain is used to add a constraint to a task group.
func (g *TaskGroup) Constrain(c *Constraint) *TaskGroup {
g.Constraints = append(g.Constraints, c)
return g
}
// AddMeta is used to add a meta k/v pair to a task group
func (g *TaskGroup) SetMeta(key, val string) *TaskGroup {
if g.Meta == nil {
g.Meta = make(map[string]string)
}
g.Meta[key] = val
return g
}
// AddTask is used to add a new task to a task group.
func (g *TaskGroup) AddTask(t *Task) *TaskGroup {
g.Tasks = append(g.Tasks, t)
return g
}
// Task is a single process in a task group.
type Task struct {
Name string
Driver string
Config map[string]string
Constraints []*Constraint
Resources *Resources
Meta map[string]string
}
// NewTask creates and initializes a new Task.
func NewTask(name, driver string) *Task {
return &Task{
Name: name,
Driver: driver,
}
}
// Configure is used to configure a single k/v pair on
// the task.
func (t *Task) SetConfig(key, val string) *Task {
if t.Config == nil {
t.Config = make(map[string]string)
}
t.Config[key] = val
return t
}
// SetMeta is used to add metadata k/v pairs to the task.
func (t *Task) SetMeta(key, val string) *Task {
if t.Meta == nil {
t.Meta = make(map[string]string)
}
t.Meta[key] = val
return t
}
// Require is used to add resource requirements to a task.
func (t *Task) Require(r *Resources) *Task {
t.Resources = r
return t
}