Skip to content
This repository has been archived by the owner on Oct 23, 2024. It is now read-only.

Commit

Permalink
generate external vol protos using the unified container protobufs fo…
Browse files Browse the repository at this point in the history
…r apps using docker engine (#4132)

cherry-pick #4131 into dcos 1.7x stream
  • Loading branch information
jdef authored and Tim Harper committed Feb 3, 2017
1 parent 6ccc5ac commit 4ff86ba
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,6 @@ private[impl] case object DVDIProvider extends ExternalVolumeProvider {
override def validations: ExternalVolumeValidations = DVDIProviderValidations

object Builders {
/**
* see [[mesosphere.marathon.api.serialization.ContainerSerializer]].
*/
def toDockerizedMesosVolume(volume: ExternalVolume): MesosVolume =
MesosVolume.newBuilder
.setContainerPath(volume.containerPath)
.setHostPath(volume.external.name)
.setMode(volume.mode)
.build

def dockerVolumeParameters(volume: ExternalVolume): Seq[Parameter] = {
import OptionLabelPatterns._
val prefix: String = name + OptionNamespaceSeparator
Expand Down Expand Up @@ -68,12 +58,16 @@ private[impl] case object DVDIProvider extends ExternalVolumeProvider {
}
}

def toUnifiedMesosVolume(volume: ExternalVolume): MesosVolume = {
def toUnifiedContainerVolume(volume: ExternalVolume): MesosVolume = {
val driverName = volume.external.options(driverOption)
val volBuilder = MesosVolume.Source.DockerVolume.newBuilder
.setDriver(driverName)
.setName(volume.external.name)

// these parameters are only really used for the mesos containerizer, not the docker
// containerizer. the docker containerizer simply ignores them.
applyOptions(volBuilder, dockerVolumeParameters(volume))

MesosVolume.newBuilder
.setContainerPath(volume.containerPath)
.setMode(volume.mode)
Expand All @@ -84,19 +78,8 @@ private[impl] case object DVDIProvider extends ExternalVolumeProvider {
}
} // Builders

override def build(builder: ContainerInfo.Builder, ev: ExternalVolume): Unit = {
// special behavior for docker vs. mesos containers
// - docker containerizer: serialize volumes into mesos proto
// - docker containerizer: specify "volumeDriver" for the container
if (builder.getType == ContainerInfo.Type.DOCKER && builder.hasDocker) {
val driverName = ev.external.options(driverOption)
builder.setDocker(builder.getDocker.toBuilder.setVolumeDriver(driverName).build)
builder.addVolumes(Builders.toDockerizedMesosVolume(ev))
}
else if (builder.getType == ContainerInfo.Type.MESOS) {
builder.addVolumes(Builders.toUnifiedMesosVolume(ev))
}
}
override def build(builder: ContainerInfo.Builder, ev: ExternalVolume): Unit =
builder.addVolumes(Builders.toUnifiedContainerVolume(ev))

val driverOption = "dvdi/driver"
val quotedDriverOption = '"' + driverOption + '"'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class DVDIProviderVolumeToUnifiedMesosVolumeTest extends MarathonSpec with Match
for ((testParams, idx) <- testParameters.zipWithIndex) {
test(s"toUnifiedMesosVolume $idx") {
assertResult(testParams.wantsVol, "generated volume doesn't match expectations") {
DVDIProvider.Builders.toUnifiedMesosVolume(testParams.externalVolume)
DVDIProvider.Builders.toUnifiedContainerVolume(testParams.externalVolume)
}
}
}
Expand Down
127 changes: 94 additions & 33 deletions src/test/scala/mesosphere/mesos/TaskBuilderTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ class TaskBuilderTest extends MarathonSpec with Matchers {
assert(portsResource.getRole == "marathon")
}

test("build creates task for DOCKER container using named, external [DockerVolume] volumes") {
test("build creates task for DOCKER container using relative hostPath") {
val offer = MarathonTestHelper.makeBasicOffer(
cpus = 2.0, mem = 128.0, disk = 2000.0, beginPort = 31000, endPort = 32000
).build
Expand All @@ -325,7 +325,7 @@ class TaskBuilderTest extends MarathonSpec with Matchers {
docker = Some(Docker()), // must have this to force docker container serialization
`type` = MesosProtos.ContainerInfo.Type.DOCKER,
volumes = Seq[Volume](
DockerVolume("/container/path", "namedFoo", MesosProtos.Volume.Mode.RW)
DockerVolume("/container/path", "relativeDirName", MesosProtos.Volume.Mode.RW)
)
))
)
Expand All @@ -336,22 +336,22 @@ class TaskBuilderTest extends MarathonSpec with Matchers {
assert(resource("cpus") == ScalarResource("cpus", 1)) // sanity, we DID match the offer, right?

// check protobuf construction, should be a ContainerInfo w/ volumes
def vol(name: String): Option[MesosProtos.Volume] = {
def vol(path: String): Option[MesosProtos.Volume] = {
if (taskInfo.hasContainer) {
taskInfo.getContainer.getVolumesList.asScala.find(_.getHostPath == name)
taskInfo.getContainer.getVolumesList.asScala.find(_.getHostPath == path)
}
else None
}

assert(taskInfo.getContainer.getVolumesList.size > 0, "check that container has volumes declared")
assert(!taskInfo.getContainer.getDocker.hasVolumeDriver, "docker spec should not define a volume driver")
assert(vol("namedFoo").isDefined,
s"missing expected volume namedFoo, got instead: ${taskInfo.getContainer.getVolumesList}")
assert(
vol("relativeDirName").isDefined,
s"missing expected volume relativeDirName, got instead: ${taskInfo.getContainer.getVolumesList}")
}

// TODO(jdef) test both dockerhostvol and persistent extvol in the same docker container

test("build creates task for DOCKER container using external [DockerVolume] volumes") {
test("build creates task for DOCKER container using host-local and external [DockerVolume] volumes") {
import mesosphere.marathon.core.externalvolume.impl.providers.DVDIProviderVolumeToUnifiedMesosVolumeTest._
val offer = MarathonTestHelper.makeBasicOffer(
cpus = 2.0, mem = 128.0, disk = 2000.0, beginPort = 31000, endPort = 32000
).build
Expand All @@ -374,11 +374,7 @@ class TaskBuilderTest extends MarathonSpec with Matchers {
provider = "dvdi",
options = Map[String, String]("dvdi/driver" -> "bar")
), MesosProtos.Volume.Mode.RW),
ExternalVolume("/container/path2", ExternalVolumeInfo(
name = "namedEdc",
provider = "dvdi",
options = Map[String, String]("dvdi/driver" -> "ert")
), MesosProtos.Volume.Mode.RO)
DockerVolume("/container/path", "relativeDirName", MesosProtos.Volume.Mode.RW)
)
))
)
Expand All @@ -396,13 +392,90 @@ class TaskBuilderTest extends MarathonSpec with Matchers {
else None
}

assert(taskInfo.getContainer.getVolumesList.size > 0, "check that container has volumes declared")
assert(taskInfo.getContainer.getDocker.hasVolumeDriver, "docker spec should define a volume driver")
assert(taskInfo.getContainer.getDocker.getVolumeDriver == "ert", "docker spec should choose ert driver")
assert(vol("namedFoo").isDefined,
s"missing expected volume namedFoo, got instead: ${taskInfo.getContainer.getVolumesList}")
assert(vol("namedEdc").isDefined,
s"missing expected volume namedFoo, got instead: ${taskInfo.getContainer.getVolumesList}")
assert(taskInfo.getContainer.getVolumesList.size == 2, "check that container has volumes declared")
assert(!taskInfo.getContainer.getDocker.hasVolumeDriver, "docker spec should not define a volume driver")

assert(
taskInfo.getContainer.getVolumesList.size == 2,
s"check that container has 2 volumes declared, got instead ${taskInfo.getExecutor.getContainer.getVolumesList}")

val vol1 = volumeWith(
containerPath("/container/path"),
mode(MesosProtos.Volume.Mode.RW),
volumeRef("bar", "namedFoo")
)

val got1 = taskInfo.getContainer.getVolumes(0)
assert(vol1.equals(got1), s"expected volume $vol1, got instead: $got1")

assert(
vol("relativeDirName").isDefined,
s"missing expected volume relativeDirName, got instead: ${taskInfo.getContainer.getVolumesList}")
}

test("build creates task for DOCKER container using external [DockerVolume] volumes") {
import mesosphere.marathon.core.externalvolume.impl.providers.DVDIProviderVolumeToUnifiedMesosVolumeTest._
val offer = MarathonTestHelper.makeBasicOffer(
cpus = 2.0, mem = 128.0, disk = 2000.0, beginPort = 31000, endPort = 32000
).build

val task: Option[(MesosProtos.TaskInfo, _)] = buildIfMatches(
offer,
AppDefinition(
id = "/product/frontend".toPath,
cmd = Some("foo"),
cpus = 1.0,
mem = 32.0,
executor = "//cmd",
portDefinitions = Nil,
container = Some(Container(
`type` = MesosProtos.ContainerInfo.Type.DOCKER,
docker = Some(Docker()), // must have this to force docker container serialization
volumes = Seq[Volume](
ExternalVolume("/container/path", ExternalVolumeInfo(
name = "namedFoo",
provider = "dvdi",
options = Map[String, String]("dvdi/driver" -> "bar")
), MesosProtos.Volume.Mode.RW),
ExternalVolume("/container/path2", ExternalVolumeInfo(
name = "namedEdc",
provider = "dvdi",
options = Map[String, String]("dvdi/driver" -> "ert", "dvdi/boo" -> "baa")
), MesosProtos.Volume.Mode.RO)
)
))
)
)

val Some((taskInfo, _)) = task
def resource(name: String): Resource = taskInfo.getResourcesList.asScala.find(_.getName == name).get
assert(resource("cpus") == ScalarResource("cpus", 1)) // sanity, we DID match the offer, right?

assert(taskInfo.getContainer.getVolumesList.size == 2, "check that container has volumes declared")
assert(!taskInfo.getContainer.getDocker.hasVolumeDriver, "docker spec should not define a volume driver")

// check protobuf construction, should be a ContainerInfo w/ no volumes, w/ envvar
assert(
taskInfo.getContainer.getVolumesList.size == 2,
s"check that container has 2 volumes declared, got instead ${taskInfo.getExecutor.getContainer.getVolumesList}")

val vol1 = volumeWith(
containerPath("/container/path"),
mode(MesosProtos.Volume.Mode.RW),
volumeRef("bar", "namedFoo")
)

val got1 = taskInfo.getContainer.getVolumes(0)
assert(vol1.equals(got1), s"expected volume $vol1, got instead: $got1")

val vol2 = volumeWith(
containerPath("/container/path2"),
mode(MesosProtos.Volume.Mode.RO),
volumeRef("ert", "namedEdc"),
options(Map("boo" -> "baa"))
)
val got2 = taskInfo.getContainer.getVolumes(1)
assert(vol2.equals(got2), s"expected volume $vol2, got instead: $got2")
}

test("build creates task for MESOS container using named, external [ExternalVolume] volumes") {
Expand Down Expand Up @@ -443,18 +516,6 @@ class TaskBuilderTest extends MarathonSpec with Matchers {
def resource(name: String): Resource = taskInfo.getResourcesList.asScala.find(_.getName == name).get
assert(resource("cpus") == ScalarResource("cpus", 1)) // sanity, we DID match the offer, right?

def hasEnv(name: String, value: String): Boolean =
taskInfo.getExecutor.getCommand.hasEnvironment &&
taskInfo.getExecutor.getCommand.getEnvironment.getVariablesList.asScala.find{ ev =>
ev.getName == name && ev.getValue == value
}.isDefined

def missingEnv(name: String): Boolean =
taskInfo.getExecutor.getCommand.hasEnvironment &&
taskInfo.getExecutor.getCommand.getEnvironment.getVariablesList.asScala.find{ ev =>
ev.getName == name
}.isEmpty

taskInfo.hasContainer should be (false)
taskInfo.hasCommand should be (false)
taskInfo.getExecutor.hasContainer should be (true)
Expand Down

0 comments on commit 4ff86ba

Please sign in to comment.