This is an automated email from the ASF dual-hosted git repository. csantanapr pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git
The following commit(s) were added to refs/heads/master by this push: new 489a2bc Allow docker pull to be skipped for local docker actions. (#3052) 489a2bc is described below commit 489a2bcdd9f549db738491eec842d6ae84611168 Author: rodric rabbah <rod...@gmail.com> AuthorDate: Wed Dec 13 11:03:43 2017 -0500 Allow docker pull to be skipped for local docker actions. (#3052) This commit adds a deployment flag which allows a docker action to be treated as a native image. A native image may eschew a docker pull. It is defined as one that has a prefix matching the docker prefix for managed images. Also added some missing tids. --- ansible/environments/docker-machine/group_vars/all | 1 + ansible/environments/local/group_vars/all | 1 + ansible/group_vars/all | 3 ++ .../main/scala/whisk/common/TransactionId.scala | 1 + .../whisk/core/database/CouchDbRestStore.scala | 2 +- .../src/main/scala/whisk/core/entity/Exec.scala | 5 +-- .../scala/whisk/core/entity/ExecManifest.scala | 49 ++++++++++++++-------- .../whisk/core/containerpool/ContainerPool.scala | 3 +- .../main/scala/whisk/core/invoker/Invoker.scala | 2 +- .../whisk/core/entity/test/ExecManifestTests.scala | 36 +++++++++------- 10 files changed, 63 insertions(+), 40 deletions(-) diff --git a/ansible/environments/docker-machine/group_vars/all b/ansible/environments/docker-machine/group_vars/all index 9e8a93c..391fa1e 100644 --- a/ansible/environments/docker-machine/group_vars/all +++ b/ansible/environments/docker-machine/group_vars/all @@ -3,6 +3,7 @@ config_root_dir: /Users/Shared/wskconf whisk_logs_dir: /Users/Shared/wsklogs docker_registry: "" docker_dns: "" +bypass_pull_for_local_images: true # The whisk_api_localhost_name is used to configure nginx to permit vanity URLs for web actions. # It is also used for the SSL certificate generation. For a local deployment, this is typically diff --git a/ansible/environments/local/group_vars/all b/ansible/environments/local/group_vars/all index 0056e96..7b838b7 100755 --- a/ansible/environments/local/group_vars/all +++ b/ansible/environments/local/group_vars/all @@ -3,6 +3,7 @@ config_root_dir: /tmp/wskconf whisk_logs_dir: /tmp/wsklogs docker_registry: "" docker_dns: "" +bypass_pull_for_local_images: true db_prefix: whisk_local_ diff --git a/ansible/group_vars/all b/ansible/group_vars/all index 78366dd..5cde947 100644 --- a/ansible/group_vars/all +++ b/ansible/group_vars/all @@ -29,10 +29,13 @@ whisk: # defaultImageTag: the default image tag # runtimes: set of language runtime families grouped by language (e.g., nodejs, python) # blackboxes: list of pre-populated docker action images as "name" with optional "prefix" and "tag" +# bypassPullForLocalImages: optional, if true, allow images with a prefix that matches {{ docker.image.prefix }} +# to skip docker pull in invoker even if the image is not part of the blackboxe set # runtimesManifest: "{{ runtimes_manifest | default(runtimesManifestDefault) }}" runtimesManifestDefault: + bypassPullForLocalImages: "{{ bypass_pull_for_local_images | default(false) }}" defaultImagePrefix: "openwhisk" defaultImageTag: "latest" runtimes: diff --git a/common/scala/src/main/scala/whisk/common/TransactionId.scala b/common/scala/src/main/scala/whisk/common/TransactionId.scala index 0a43b79..95e6eef 100644 --- a/common/scala/src/main/scala/whisk/common/TransactionId.scala +++ b/common/scala/src/main/scala/whisk/common/TransactionId.scala @@ -215,6 +215,7 @@ object TransactionId { val loadbalancer = TransactionId(-120) // Loadbalancer thread val invokerHealth = TransactionId(-121) // Invoker supervision val controller = TransactionId(-130) // Controller startup + val dbBatcher = TransactionId(-140) // Database batcher def apply(tid: BigDecimal): TransactionId = { Try { diff --git a/common/scala/src/main/scala/whisk/core/database/CouchDbRestStore.scala b/common/scala/src/main/scala/whisk/core/database/CouchDbRestStore.scala index 2127f86..7625e22 100644 --- a/common/scala/src/main/scala/whisk/core/database/CouchDbRestStore.scala +++ b/common/scala/src/main/scala/whisk/core/database/CouchDbRestStore.scala @@ -74,7 +74,7 @@ class CouchDbRestStore[DocumentAbstraction <: DocumentSerializer](dbProtocol: St private val maxOpenDbRequests = system.settings.config.getInt("akka.http.host-connection-pool.max-connections") / 2 private val batcher: Batcher[JsObject, Either[ArtifactStoreException, DocInfo]] = - new Batcher(500, maxOpenDbRequests)(put(_)(TransactionId.unknown)) + new Batcher(500, maxOpenDbRequests)(put(_)(TransactionId.dbBatcher)) override protected[database] def put(d: DocumentAbstraction)(implicit transid: TransactionId): Future[DocInfo] = { val asJson = d.toDocumentRecord diff --git a/common/scala/src/main/scala/whisk/core/entity/Exec.scala b/common/scala/src/main/scala/whisk/core/entity/Exec.scala index 268fdbe..bf066f6 100644 --- a/common/scala/src/main/scala/whisk/core/entity/Exec.scala +++ b/common/scala/src/main/scala/whisk/core/entity/Exec.scala @@ -266,7 +266,7 @@ protected[core] object Exec extends ArgNormalizer[Exec] with DefaultJsonProtocol s"if defined, 'code' must a string defined in 'exec' for '${Exec.BLACKBOX}' actions") case None => None } - val native = execManifests.blackboxImages.contains(image) + val native = execManifests.skipDockerPull(image) BlackBoxExec(image, code, optMainField, native) case _ => @@ -384,8 +384,7 @@ protected[core] object ExecMetaDataBase extends ArgNormalizer[ExecMetaDataBase] throw new DeserializationException( s"'image' must be a string defined in 'exec' for '${Exec.BLACKBOX}' actions") } - - val native = execManifests.blackboxImages.contains(image) + val native = execManifests.skipDockerPull(image) BlackBoxExecMetaData(native) case _ => diff --git a/common/scala/src/main/scala/whisk/core/entity/ExecManifest.scala b/common/scala/src/main/scala/whisk/core/entity/ExecManifest.scala index 479f23d..dc44691 100644 --- a/common/scala/src/main/scala/whisk/core/entity/ExecManifest.scala +++ b/common/scala/src/main/scala/whisk/core/entity/ExecManifest.scala @@ -17,7 +17,7 @@ package whisk.core.entity -import scala.util.{Failure, Success, Try} +import scala.util.{Failure, Try} import spray.json._ import spray.json.DefaultJsonProtocol._ import whisk.core.WhiskConfig @@ -41,15 +41,13 @@ protected[core] object ExecManifest { * singleton Runtime instance. * * @param config a valid configuration - * @param reinit re-initialize singleton iff true - * @return the manifest if initialized successfully, or if previously initialized + * @param localDockerImagePrefix optional local docker prefix, permitting images matching prefix to bypass docker pull + * @return the manifest if initialized successfully, or an failure */ - protected[core] def initialize(config: WhiskConfig, reinit: Boolean = false): Try[Runtimes] = { - if (manifest.isEmpty || reinit) { - val mf = Try(config.runtimesManifest.parseJson.asJsObject).flatMap(runtimes(_)) - mf.foreach(m => manifest = Some(m)) - mf - } else Success(manifest.get) + protected[core] def initialize(config: WhiskConfig, localDockerImagePrefix: Option[String] = None): Try[Runtimes] = { + val mf = Try(config.runtimesManifest.parseJson.asJsObject).flatMap(runtimes(_, localDockerImagePrefix)) + mf.foreach(m => manifest = Some(m)) + mf } /** @@ -71,26 +69,34 @@ protected[core] object ExecManifest { * @param config a configuration object as JSON * @return Runtimes instance */ - protected[entity] def runtimes(config: JsObject): Try[Runtimes] = Try { + protected[entity] def runtimes(config: JsObject, localDockerImagePrefix: Option[String] = None): Try[Runtimes] = Try { val prefix = config.fields.get("defaultImagePrefix").map(_.convertTo[String]) val tag = config.fields.get("defaultImageTag").map(_.convertTo[String]) - val runtimes = config - .fields("runtimes") - .convertTo[Map[String, Set[RuntimeManifest]]] - .map { + + val runtimes = config.fields + .get("runtimes") + .map(_.convertTo[Map[String, Set[RuntimeManifest]]].map { case (name, versions) => RuntimeFamily(name, versions.map { mf => val img = ImageName(mf.image.name, mf.image.prefix.orElse(prefix), mf.image.tag.orElse(tag)) mf.copy(image = img) }) - } - .toSet + }.toSet) + val blackbox = config.fields .get("blackboxes") .map(_.convertTo[Set[ImageName]].map { image => ImageName(image.name, image.prefix.orElse(prefix), image.tag.orElse(tag)) }) - Runtimes(runtimes, blackbox.getOrElse(Set.empty)) + + val bypassPullForLocalImages = config.fields + .get("bypassPullForLocalImages") + .map(_.convertTo[Boolean]) + .filter(identity) + .flatMap(_ => localDockerImagePrefix) + .map(_.trim) + + Runtimes(runtimes.getOrElse(Set.empty), blackbox.getOrElse(Set.empty), bypassPullForLocalImages) } /** @@ -215,10 +221,17 @@ protected[core] object ExecManifest { * * @param set of supported runtime families */ - protected[core] case class Runtimes(runtimes: Set[RuntimeFamily], blackboxImages: Set[ImageName]) { + protected[core] case class Runtimes(runtimes: Set[RuntimeFamily], + blackboxImages: Set[ImageName], + bypassPullForLocalImages: Option[String]) { val knownContainerRuntimes: Set[String] = runtimes.flatMap(_.versions.map(_.kind)) + def skipDockerPull(image: ImageName): Boolean = { + blackboxImages.contains(image) || + image.prefix.flatMap(p => bypassPullForLocalImages.map(_ == p)).getOrElse(false) + } + def toJson: JsObject = { runtimes .map { family => diff --git a/core/invoker/src/main/scala/whisk/core/containerpool/ContainerPool.scala b/core/invoker/src/main/scala/whisk/core/containerpool/ContainerPool.scala index b02f528..5177026 100644 --- a/core/invoker/src/main/scala/whisk/core/containerpool/ContainerPool.scala +++ b/core/invoker/src/main/scala/whisk/core/containerpool/ContainerPool.scala @@ -25,6 +25,7 @@ import akka.actor.ActorRefFactory import akka.actor.Props import whisk.common.AkkaLogging +import whisk.common.TransactionId import whisk.core.entity.ByteSize import whisk.core.entity.CodeExec import whisk.core.entity.EntityName @@ -72,7 +73,7 @@ class ContainerPool(childFactory: ActorRefFactory => ActorRef, var prewarmedPool = immutable.Map.empty[ActorRef, ContainerData] prewarmConfig.foreach { config => - logging.info(this, s"pre-warming ${config.count} ${config.exec.kind} containers") + logging.info(this, s"pre-warming ${config.count} ${config.exec.kind} containers")(TransactionId.invokerWarmup) (1 to config.count).foreach { _ => prewarmContainer(config.exec, config.memoryLimit) } diff --git a/core/invoker/src/main/scala/whisk/core/invoker/Invoker.scala b/core/invoker/src/main/scala/whisk/core/invoker/Invoker.scala index 77e3da8..648e61e 100644 --- a/core/invoker/src/main/scala/whisk/core/invoker/Invoker.scala +++ b/core/invoker/src/main/scala/whisk/core/invoker/Invoker.scala @@ -101,7 +101,7 @@ object Invoker { abort("Bad configuration, cannot start.") } - val execManifest = ExecManifest.initialize(config) + val execManifest = ExecManifest.initialize(config, localDockerImagePrefix = Some(config.dockerImagePrefix)) if (execManifest.isFailure) { logger.error(this, s"Invalid runtimes manifest: ${execManifest.failed.get}") abort("Bad configuration, cannot start.") diff --git a/tests/src/test/scala/whisk/core/entity/test/ExecManifestTests.scala b/tests/src/test/scala/whisk/core/entity/test/ExecManifestTests.scala index f15682f..d232081 100644 --- a/tests/src/test/scala/whisk/core/entity/test/ExecManifestTests.scala +++ b/tests/src/test/scala/whisk/core/entity/test/ExecManifestTests.scala @@ -18,20 +18,18 @@ package whisk.core.entity.test import java.io.{BufferedWriter, File, FileWriter} -import java.util.NoSuchElementException -import scala.util.{Success} +import common.{StreamLogging, WskActorSystem} import org.junit.runner.RunWith -import org.scalatest.FlatSpec -import org.scalatest.Matchers +import org.scalatest.{FlatSpec, Matchers} import org.scalatest.junit.JUnitRunner -import spray.json._ import spray.json.DefaultJsonProtocol._ +import spray.json._ import whisk.core.WhiskConfig import whisk.core.entity.ExecManifest import whisk.core.entity.ExecManifest._ -import common.StreamLogging -import common.WskActorSystem + +import scala.util.Success @RunWith(classOf[JUnitRunner]) class ExecManifestTests extends FlatSpec with WskActorSystem with StreamLogging with Matchers { @@ -112,7 +110,10 @@ class ExecManifestTests extends FlatSpec with WskActorSystem with StreamLogging val mf = JsObject("runtimes" -> JsObject(), "blackboxes" -> imgs.toJson) val runtimes = ExecManifest.runtimes(mf).get + runtimes.blackboxImages shouldBe imgs + imgs.foreach(img => runtimes.skipDockerPull(img) shouldBe true) + runtimes.skipDockerPull(ImageName("???", Some("bbb"))) shouldBe false } it should "read a valid configuration with blackbox images, default prefix and tag" in { @@ -137,6 +138,8 @@ class ExecManifestTests extends FlatSpec with WskActorSystem with StreamLogging ImageName("???", Some("pre"), Some("ttt"))) } + runtimes.skipDockerPull(ImageName("???", Some("pre"), Some("test"))) shouldBe true + runtimes.skipDockerPull(ImageName("???", Some("bbb"), Some("test"))) shouldBe false } it should "reject runtimes with multiple defaults" in { @@ -175,21 +178,22 @@ class ExecManifestTests extends FlatSpec with WskActorSystem with StreamLogging } } - it should "throw an error when configured manifest is a valid JSON, but with a missing key" in { - val config_manifest = """{"nodejs":[{"kind":"nodejs:6","default":true,"image":{"name":"nodejs6action"}}]}""" + it should "indicate image is local if it matches deployment docker prefix" in { + val config_manifest = """{"bypassPullForLocalImages":true}""" val file = File.createTempFile("cxt", ".txt") file.deleteOnExit() val bw = new BufferedWriter(new FileWriter(file)) - bw.write("runtimes.manifest=" + config_manifest + "\n") + bw.write(WhiskConfig.runtimesManifest + s"=$config_manifest\n") bw.close() - val result = ExecManifest.initialize(new WhiskConfig(Map("runtimes.manifest" -> null), Set(), file), true) - - result should be a 'failure + val props = Map(WhiskConfig.runtimesManifest -> null) + val manifest = + ExecManifest.initialize(new WhiskConfig(props, Set(), file), localDockerImagePrefix = Some("localpre")) + manifest should be a 'success - the[NoSuchElementException] thrownBy { - result.get - } should have message ("key not found: runtimes") + manifest.get.skipDockerPull(ImageName(prefix = Some("x"), name = "y")) shouldBe false + manifest.get.skipDockerPull(ImageName(prefix = Some("localpre"), name = "y")) shouldBe true } + } -- To stop receiving notification emails like this one, please contact ['"commits@openwhisk.apache.org" <commits@openwhisk.apache.org>'].