Skip to content

Commit

Permalink
[constraints] by hostname (#563)
Browse files Browse the repository at this point in the history
  • Loading branch information
tony-kerz authored and gkleiman committed Apr 26, 2017
1 parent 002138d commit b2d7ec1
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 12 deletions.
5 changes: 3 additions & 2 deletions docs/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,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. 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,28 @@
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
*/
object 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.forall(_.matches(attributes))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,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 +134,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,76 @@
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

class ConstraintCheckerSpec extends SpecificationWithJUnit
with Mockito
with ConstraintSpecHelper {

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()

"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 b2d7ec1

Please sign in to comment.