Skip to content

Commit

Permalink
[constraints] by hostname
Browse files Browse the repository at this point in the history
  • Loading branch information
Tony L. Kerz authored and Tony L. Kerz committed Oct 9, 2015
1 parent 4d4e37a commit 17ad52c
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 13 deletions.
5 changes: 3 additions & 2 deletions docs/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,9 @@ When specifying the `command` field in your job hash, use `url-runner.bash` (mak

## Constraints

These constraints will currently only work against attributes that are specifically set on the Mesos slaves [as described in the Mesos documentation](http://mesos.apache.org/documentation/latest/configuration).
(i.e. the `hostname` attribute is not currently automatically available for constraints [as it is in Marathon](https://mesosphere.github.io/marathon/docs/constraints))
These constraints will work against attributes that are specifically set on the Mesos slaves [as described in the Mesos documentation](http://mesos.apache.org/documentation/latest/configuration).

If a `hostname` attribute is not explicitly specified, one will automatically be created and made available for constraints [as it is in Marathon](https://mesosphere.github.io/marathon/docs/constraints). It should be noted that calling out specific hostnames is not resilient to slave failure and should be avoided if possible.

### EQUALS constraint

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.apache.mesos.chronos.scheduler.mesos

import org.apache.mesos.Protos
import org.apache.mesos.chronos.scheduler.jobs.constraints.Constraint
import java.util.logging.Logger
import scala.collection.JavaConverters._

/**
* Helper for checking resource offer against job constraints
*
* @author tony kerz (anthony.kerz@gmail.com)
*/
class ConstraintChecker {
private[this] val log = Logger.getLogger(getClass.getName)
val Hostname = "hostname"

def checkConstraints(offer: Protos.Offer, constraints: Seq[Constraint]): Boolean = {
var attributes = offer.getAttributesList.asScala

if (!attributes.exists(attr => attr.getName == Hostname)) {
log.fine(s"adding hostname-attribute=${offer.getHostname} to offer=${offer}")
val hostnameText = Protos.Value.Text.newBuilder().setValue(offer.getHostname).build()
val hostnameAttribute = Protos.Attribute.newBuilder().setName(Hostname).setText(hostnameText).setType(Protos.Value.Type.TEXT).build()
attributes = offer.getAttributesList.asScala :+ hostnameAttribute
}

constraints.foreach { c =>
if (!c.matches(attributes)) {
return false
}
}
true
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ class MesosJobFramework @Inject()(
val config: SchedulerConfiguration,
val frameworkIdUtil: FrameworkIdUtil,
val taskBuilder: MesosTaskBuilder,
val mesosOfferReviver: MesosOfferReviver)
val mesosOfferReviver: MesosOfferReviver,
val constraintChecker: ConstraintChecker = new ConstraintChecker())
extends Scheduler {

val frameworkName = "chronos"
Expand Down Expand Up @@ -116,15 +117,6 @@ class MesosJobFramework @Inject()(
def generateLaunchableTasks(offerResources: mutable.HashMap[Offer, Resources]): mutable.Buffer[(String, BaseJob, Offer)] = {
val tasks = mutable.Buffer[(String, BaseJob, Offer)]()

def checkConstraints(attributes: Seq[Protos.Attribute], constraints: Seq[Constraint]): Boolean = {
constraints.foreach { c =>
if (!c.matches(attributes)) {
return false
}
}
true
}

@tailrec
def generate() {
taskManager.getTask match {
Expand All @@ -143,7 +135,7 @@ class MesosJobFramework @Inject()(
case None =>
val neededResources = new Resources(job)
offerResources.toIterator.find { ors =>
ors._2.canSatisfy(neededResources) && checkConstraints(ors._1.getAttributesList.asScala, job.constraints)
ors._2.canSatisfy(neededResources) && constraintChecker.checkConstraints(ors._1, job.constraints)
} match {
case Some((offer, resources)) =>
// Subtract this job's resource requirements from the remaining available resources in this offer.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.apache.mesos.chronos.scheduler.mesos

import mesosphere.mesos.protos._
import org.apache.mesos.chronos.scheduler.jobs.constraints.ConstraintSpecHelper
import org.apache.mesos.Protos
import java.util.logging.Logger
import org.specs2.mock.Mockito
import org.specs2.mutable.SpecificationWithJUnit
import org.apache.mesos.chronos.scheduler.jobs.constraints.LikeConstraint
import org.apache.mesos.chronos.scheduler.jobs.constraints.EqualsConstraint
import mesosphere.mesos.protos.Implicits._
import org.specs2.specification.BeforeEach

/**
* spec for testing ConstraintChecker
*
* @author tony kerz (anthony.kerz@gmail.com)
*/
class ConstraintCheckerSpec extends SpecificationWithJUnit
with Mockito
with ConstraintSpecHelper
{
isolated

val offer = Protos.Offer.newBuilder()
.setId(OfferID("1"))
.setFrameworkId(FrameworkID("chronos"))
.setSlaveId(SlaveID("slave-1"))
.setHostname("slave.one.com")
.addAttributes(createTextAttribute("rack", "rack-1"))
.build()

val offerWithHostname = Protos.Offer.newBuilder()
.setId(OfferID("1"))
.setFrameworkId(FrameworkID("chronos"))
.setSlaveId(SlaveID("slave-1"))
.setHostname("slave.one.com")
.addAttributes(createTextAttribute("hostname", "slave.explicit.com"))
.build()

val constraintChecker = new ConstraintChecker()

"check constraints" should {

"be true when equal" in {
val constraints = Seq(EqualsConstraint("rack", "rack-1"))
constraintChecker.checkConstraints(offer, constraints) must beTrue
}

"be false when not equal" in {
val constraints = Seq(EqualsConstraint("rack", "rack-2"))
constraintChecker.checkConstraints(offer, constraints) must beFalse
}

"be true when like" in {
val constraints = Seq(LikeConstraint("rack", "rack-[1-3]"))
constraintChecker.checkConstraints(offer, constraints) must beTrue
}

"be false when not like" in {
val constraints = Seq(LikeConstraint("rack", "rack-[2-3]"))
constraintChecker.checkConstraints(offer, constraints) must beFalse
}

"be true when hostname equal" in {
val constraints = Seq(EqualsConstraint("hostname", "slave.one.com"))
constraintChecker.checkConstraints(offer, constraints) must beTrue
}

"be false when hostname not equal" in {
val constraints = Seq(EqualsConstraint("hostname", "slave.two.com"))
constraintChecker.checkConstraints(offer, constraints) must beFalse
}

"be false when hostname explicitly set to something else and not equal" in {
val constraints = Seq(EqualsConstraint("hostname", "slave.one.com"))
constraintChecker.checkConstraints(offerWithHostname, constraints) must beFalse
}

"be true when hostname explicitly set to something else and equal" in {
val constraints = Seq(EqualsConstraint("hostname", "slave.explicit.com"))
constraintChecker.checkConstraints(offerWithHostname, constraints) must beTrue
}
}
}

0 comments on commit 17ad52c

Please sign in to comment.