in core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/docker/DockerContainer.scala [61:158]
def create(transid: TransactionId,
image: Either[ImageName, ImageName],
registryConfig: Option[RuntimesRegistryConfig] = None,
memory: ByteSize = 256.MB,
cpuShares: Int = 0,
cpuLimit: Option[Double] = None,
environment: Map[String, String] = Map.empty,
network: String = "bridge",
dnsServers: Seq[String] = Seq.empty,
dnsSearch: Seq[String] = Seq.empty,
dnsOptions: Seq[String] = Seq.empty,
name: Option[String] = None,
useRunc: Boolean = true,
dockerRunParameters: Map[String, Set[String]])(implicit docker: DockerApiWithFileAccess,
runc: RuncApi,
as: ActorSystem,
ec: ExecutionContext,
log: Logging): Future[DockerContainer] = {
implicit val tid: TransactionId = transid
val environmentArgs = environment.flatMap {
case (key, value) => Seq("-e", s"$key=$value")
}
val params = dockerRunParameters.flatMap {
case (key, valueList) => valueList.toList.flatMap(Seq(key, _))
}
// NOTE: --dns-option on modern versions of docker, but is --dns-opt on docker 1.12
val dnsOptString = if (docker.clientVersion.startsWith("1.12")) { "--dns-opt" } else { "--dns-option" }
val args = Seq(
"--cpu-shares",
cpuShares.toString,
"--memory",
s"${memory.toMB}m",
"--memory-swap",
s"${memory.toMB}m",
"--network",
network) ++
environmentArgs ++
dnsServers.flatMap(d => Seq("--dns", d)) ++
dnsSearch.flatMap(d => Seq("--dns-search", d)) ++
dnsOptions.flatMap(d => Seq(dnsOptString, d)) ++
name.map(n => Seq("--name", n)).getOrElse(Seq.empty) ++
cpuLimit.map(c => Seq("--cpus", c.toString)).getOrElse(Seq.empty) ++
params
val registryConfigUrl = registryConfig.map(_.url).getOrElse("")
val imageToUse = image.merge.resolveImageName(Some(registryConfigUrl))
val pulled = image match {
case Left(userProvided) if userProvided.tag.map(_ == "latest").getOrElse(true) =>
// Iff the image tag is "latest" explicitly (or implicitly because no tag is given at all), failing to pull will
// fail the whole container bringup process, because it is expected to pick up the very latest "untagged"
// version every time.
docker.pull(imageToUse).map(_ => true).recoverWith {
case _ => Future.failed(BlackboxStartupError(Messages.imagePullError(imageToUse)))
}
case Left(_) =>
// Iff the image tag is something else than latest, we tolerate an outdated image if one is available locally.
// A `docker run` will be tried nonetheless to try to start a container (which will succeed if the image is
// already available locally)
docker.pull(imageToUse).map(_ => true).recover { case _ => false }
case Right(_) =>
// Iff we're not pulling at all (OpenWhisk provided image) we act as if the pull was successful.
Future.successful(true)
}
for {
pullSuccessful <- pulled
id <- docker.run(imageToUse, args).recoverWith {
case BrokenDockerContainer(brokenId, _, exitStatus) if exitStatus.isEmpty || exitStatus.contains(125) =>
// Remove the broken container - but don't wait or check for the result.
// If the removal fails, there is nothing we could do to recover from the recovery.
docker.rm(brokenId)
Future.failed(WhiskContainerStartupError(Messages.resourceProvisionError))
case BrokenDockerContainer(brokenId, _, exitStatus) if exitStatus.contains(127) =>
docker.rm(brokenId)
Future.failed(BlackboxStartupError(s"${Messages.commandNotFoundError} in image ${imageToUse}"))
case _ =>
// Iff the pull was successful, we assume that the error is not due to an image pull error, otherwise
// the docker run was a backup measure to try and start the container anyway. If it fails again, we assume
// the image could still not be pulled and wasn't available locally.
if (pullSuccessful) {
Future.failed(WhiskContainerStartupError(Messages.resourceProvisionError))
} else {
Future.failed(BlackboxStartupError(Messages.imagePullError(imageToUse)))
}
}
ip <- docker.inspectIPAddress(id, network).recoverWith {
// remove the container immediately if inspect failed as
// we cannot recover that case automatically
case _ =>
docker.rm(id)
Future.failed(WhiskContainerStartupError(Messages.resourceProvisionError))
}
} yield new DockerContainer(id, ip, useRunc)
}