Change "unique" to "distinctHosts"

This commit is contained in:
Alex Dadgar
2015-10-22 17:40:41 -07:00
parent a52bdd914d
commit db90849fb1
6 changed files with 47 additions and 48 deletions

View File

@@ -257,7 +257,7 @@ func parseConstraints(result *[]*structs.Constraint, obj *hclobj.Object) error {
m["RTarget"] = constraint
}
if value, ok := m["unique"]; ok {
if value, ok := m["distinctHosts"]; ok {
enabled, err := strconv.ParseBool(value.(string))
if err != nil {
return err
@@ -268,7 +268,7 @@ func parseConstraints(result *[]*structs.Constraint, obj *hclobj.Object) error {
continue
}
m["Operand"] = "unique"
m["Operand"] = "distinctHosts"
}
// Build the constraint

View File

@@ -193,7 +193,7 @@ func TestParse(t *testing.T) {
},
{
"unique-constraint.hcl",
"distinctHosts-constraint.hcl",
&structs.Job{
ID: "foo",
Name: "foo",
@@ -203,7 +203,7 @@ func TestParse(t *testing.T) {
Constraints: []*structs.Constraint{
&structs.Constraint{
Hard: true,
Operand: "unique",
Operand: "distinctHosts",
},
},
},

View File

@@ -1,5 +0,0 @@
job "foo" {
constraint {
unique = "true"
}
}

View File

@@ -152,7 +152,7 @@ func (iter *DriverIterator) hasDrivers(option *structs.Node) bool {
// DynamicConstraintIterator is a FeasibleIterator which returns nodes that
// match constraints that are not static such as Node attributes but are
// effected by alloc placements. Examples are unique and tenancy constraints.
// effected by alloc placements. Examples are distinctHosts and tenancy constraints.
// This is used to filter on job and task group constraints.
type DynamicConstraintIterator struct {
ctx Context
@@ -160,10 +160,10 @@ type DynamicConstraintIterator struct {
tg *structs.TaskGroup
job *structs.Job
// Store whether the Job or TaskGroup has unique constraints so they don't
// have to be calculated every time Next() is called.
tgUnique bool
jobUnique bool
// Store whether the Job or TaskGroup has a distinctHosts constraints so
// they don't have to be calculated every time Next() is called.
tgDistinctHosts bool
jobDistinctHosts bool
}
// NewDynamicConstraintIterator creates a DynamicConstraintIterator from a
@@ -178,21 +178,21 @@ func NewDynamicConstraintIterator(ctx Context, source FeasibleIterator) *Dynamic
func (iter *DynamicConstraintIterator) SetTaskGroup(tg *structs.TaskGroup) {
iter.tg = tg
iter.tgUnique = iter.hasUniqueConstraint(tg.Constraints)
iter.tgDistinctHosts = iter.hasDistinctHostsConstraint(tg.Constraints)
}
func (iter *DynamicConstraintIterator) SetJob(job *structs.Job) {
iter.job = job
iter.jobUnique = iter.hasUniqueConstraint(job.Constraints)
iter.jobDistinctHosts = iter.hasDistinctHostsConstraint(job.Constraints)
}
func (iter *DynamicConstraintIterator) hasUniqueConstraint(constraints []*structs.Constraint) bool {
func (iter *DynamicConstraintIterator) hasDistinctHostsConstraint(constraints []*structs.Constraint) bool {
if constraints == nil {
return false
}
for _, con := range constraints {
if con.Operand == "unique" {
if con.Operand == "distinctHosts" {
return true
}
}
@@ -214,13 +214,13 @@ func (iter *DynamicConstraintIterator) Next() *structs.Node {
// Get the next option from the source
option := iter.source.Next()
// Hot-path if the option is nil or there are no unique constraints.
if option == nil || (!iter.jobUnique && !iter.tgUnique) {
// Hot-path if the option is nil or there are no distinctHosts constraints.
if option == nil || (!iter.jobDistinctHosts && !iter.tgDistinctHosts) {
return option
}
if !iter.satisfiesUnique(option, iter.jobUnique) {
iter.ctx.Metrics().FilterNode(option, "unique")
if !iter.satisfiesDistinctHosts(option, iter.jobDistinctHosts) {
iter.ctx.Metrics().FilterNode(option, "distinctHosts")
continue
}
@@ -228,9 +228,9 @@ func (iter *DynamicConstraintIterator) Next() *structs.Node {
}
}
// satisfiesUnique checks if the node satisfies a unique constraint either
// specified at the job level or the TaskGroup level.
func (iter *DynamicConstraintIterator) satisfiesUnique(option *structs.Node, job bool) bool {
// satisfiesDistinctHosts checks if the node satisfies a distinctHosts
// constraint either specified at the job level or the TaskGroup level.
func (iter *DynamicConstraintIterator) satisfiesDistinctHosts(option *structs.Node, job bool) bool {
// Get the proposed allocations
proposed, err := iter.ctx.ProposedAllocs(option.ID)
if err != nil {
@@ -244,9 +244,9 @@ func (iter *DynamicConstraintIterator) satisfiesUnique(option *structs.Node, job
jobCollision := alloc.JobID == iter.job.ID
taskCollision := alloc.TaskGroup == iter.tg.Name
// If the job has a unique constraint we only need an alloc collision on
// the JobID but if the constraint is on the TaskGroup then we need both
// a job and TaskGroup collision.
// If the job has a distinctHosts constraint we only need an alloc
// collision on the JobID but if the constraint is on the TaskGroup then
// we need both a job and TaskGroup collision.
jobInvalid := job && jobCollision
tgInvalid := !job && jobCollision && taskCollision
if jobInvalid || tgInvalid {
@@ -370,7 +370,7 @@ func resolveConstraintTarget(target string, node *structs.Node) (interface{}, bo
func checkConstraint(ctx Context, operand string, lVal, rVal interface{}) bool {
// Check for constraints not handled by this iterator.
switch operand {
case "unique":
case "distinctHosts":
return true
default:
break

View File

@@ -382,7 +382,7 @@ func TestCheckRegexpConstraint(t *testing.T) {
}
}
func TestDynamicConstraint_JobUnique_Feasible(t *testing.T) {
func TestDynamicConstraint_JobDistinctHosts(t *testing.T) {
_, ctx := testContext(t)
nodes := []*structs.Node{
mock.Node(),
@@ -392,13 +392,13 @@ func TestDynamicConstraint_JobUnique_Feasible(t *testing.T) {
}
static := NewStaticIterator(ctx, nodes)
// Create a job with a unique constraint and two task groups.
// Create a job with a distinctHosts constraint and two task groups.
tg1 := &structs.TaskGroup{Name: "bar"}
tg2 := &structs.TaskGroup{Name: "baz"}
job := &structs.Job{
ID: "foo",
Constraints: []*structs.Constraint{{Operand: "unique"}},
Constraints: []*structs.Constraint{{Operand: "distinctHosts"}},
TaskGroups: []*structs.TaskGroup{tg1, tg2},
}
@@ -420,7 +420,7 @@ func TestDynamicConstraint_JobUnique_Feasible(t *testing.T) {
}
}
func TestDynamicConstraint_JobUnique_Infeasible(t *testing.T) {
func TestDynamicConstraint_JobDistinctHosts_Infeasible(t *testing.T) {
_, ctx := testContext(t)
nodes := []*structs.Node{
mock.Node(),
@@ -428,13 +428,13 @@ func TestDynamicConstraint_JobUnique_Infeasible(t *testing.T) {
}
static := NewStaticIterator(ctx, nodes)
// Create a job with a unique constraint and two task groups.
// Create a job with a distinctHosts constraint and two task groups.
tg1 := &structs.TaskGroup{Name: "bar"}
tg2 := &structs.TaskGroup{Name: "baz"}
job := &structs.Job{
ID: "foo",
Constraints: []*structs.Constraint{{Operand: "unique"}},
Constraints: []*structs.Constraint{{Operand: "distinctHosts"}},
TaskGroups: []*structs.TaskGroup{tg1, tg2},
}
@@ -476,7 +476,7 @@ func TestDynamicConstraint_JobUnique_Infeasible(t *testing.T) {
}
}
func TestDynamicConstraint_JobUnique_InfeasibleCount(t *testing.T) {
func TestDynamicConstraint_JobDistinctHosts_InfeasibleCount(t *testing.T) {
_, ctx := testContext(t)
nodes := []*structs.Node{
mock.Node(),
@@ -484,14 +484,14 @@ func TestDynamicConstraint_JobUnique_InfeasibleCount(t *testing.T) {
}
static := NewStaticIterator(ctx, nodes)
// Create a job with a unique constraint and three task groups.
// Create a job with a distinctHosts constraint and three task groups.
tg1 := &structs.TaskGroup{Name: "bar"}
tg2 := &structs.TaskGroup{Name: "baz"}
tg3 := &structs.TaskGroup{Name: "bam"}
job := &structs.Job{
ID: "foo",
Constraints: []*structs.Constraint{{Operand: "unique"}},
Constraints: []*structs.Constraint{{Operand: "distinctHosts"}},
TaskGroups: []*structs.TaskGroup{tg1, tg2, tg3},
}
@@ -506,7 +506,7 @@ func TestDynamicConstraint_JobUnique_InfeasibleCount(t *testing.T) {
}
}
func TestDynamicConstraint_TaskGroupUnique(t *testing.T) {
func TestDynamicConstraint_TaskGroupDistinctHosts(t *testing.T) {
_, ctx := testContext(t)
nodes := []*structs.Node{
mock.Node(),
@@ -514,11 +514,11 @@ func TestDynamicConstraint_TaskGroupUnique(t *testing.T) {
}
static := NewStaticIterator(ctx, nodes)
// Create a task group with a unique constraint.
// Create a task group with a distinctHosts constraint.
taskGroup := &structs.TaskGroup{
Name: "example",
Constraints: []*structs.Constraint{
{Operand: "unique"},
{Operand: "distinctHosts"},
},
}

View File

@@ -237,13 +237,17 @@ The `constraint` object supports the following keys:
the attribute. This sets the operator to "regexp" and the `value`
to the regular expression.
* `unique` - Unique accepts a boolean value and can be used to mark a Job or
a Task Group as requiring placement on unique nodes. If the `unique`
constraint is placed on a Job, all of it's Task Groups must be placed on
unique nodes. If the `unique` constraint is placed on a Task Group, then
multiple instances of that Task Group must be placed on unique nodes. This
sets the operator to "unique" if `unique` is set to "true". If set to "false",
the constraint is ignored as this is the default behavior.
* `distinctHosts` - `distinctHosts` accepts a boolean `true`. The default is
`false`.
When `distinctHosts is `true` at the Job level, each instance of all Task
Groups specified in the job is placed on a separate host.
When `distinctHosts` is `true` at the Task Group level with count > 1, each
instance of a Task Group is placed on a separate host. Different task groups in
the same job _may_ be co-scheduled.
Tasks within a task group are always co-scheduled.
Below is a table documenting the variables that can be interpreted: