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>'].

Reply via email to