You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@openwhisk.apache.org by GitBox <gi...@apache.org> on 2018/05/31 12:22:59 UTC

[GitHub] markusthoemmes closed pull request #3714: Revert "Make stemcells configurable by deployment (#3669)"

markusthoemmes closed pull request #3714: Revert "Make stemcells configurable by deployment  (#3669)"
URL: https://github.com/apache/incubator-openwhisk/pull/3714
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/ansible/files/runtimes.json b/ansible/files/runtimes.json
index 44cb30f2c1..1866b69195 100644
--- a/ansible/files/runtimes.json
+++ b/ansible/files/runtimes.json
@@ -14,11 +14,7 @@
                 "image": {
                     "name": "nodejs6action"
                 },
-                "deprecated": false,
-                "stemCells": [{
-                    "count": 2,
-                    "memory": "256 MB"
-                }]
+                "deprecated": false
             },
             {
                 "kind": "nodejs:8",
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 7436d167f0..668ef609f6 100644
--- a/common/scala/src/main/scala/whisk/core/entity/ExecManifest.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/ExecManifest.scala
@@ -122,7 +122,6 @@ protected[core] object ExecManifest {
    * @param requireMain true iff main entry point is not optional
    * @param sentinelledLogs true iff the runtime generates stdout/stderr log sentinels after an activation
    * @param image optional image name, otherwise inferred via fixed mapping (remove colons and append 'action')
-   * @param stemCells optional list of stemCells to be initialized by invoker per kind
    */
   protected[core] case class RuntimeManifest(kind: String,
                                              image: ImageName,
@@ -130,17 +129,17 @@ protected[core] object ExecManifest {
                                              default: Option[Boolean] = None,
                                              attached: Option[Attached] = None,
                                              requireMain: Option[Boolean] = None,
-                                             sentinelledLogs: Option[Boolean] = None,
-                                             stemCells: Option[List[StemCell]] = None)
-
-  /**
-   * A stemcell configuration read from the manifest for a container image to be initialized by the container pool.
-   *
-   * @param count the number of stemcell containers to create
-   * @param memory the max memory this stemcell will allocate
-   */
-  protected[entity] case class StemCell(count: Int, memory: ByteSize) {
-    require(count > 0, "count must be positive")
+                                             sentinelledLogs: Option[Boolean] = None) {
+
+    protected[entity] def toJsonSummary = {
+      JsObject(
+        "kind" -> kind.toJson,
+        "image" -> image.publicImageName.toJson,
+        "deprecated" -> deprecated.getOrElse(false).toJson,
+        "default" -> default.getOrElse(false).toJson,
+        "attached" -> attached.isDefined.toJson,
+        "requireMain" -> requireMain.getOrElse(false).toJson)
+    }
   }
 
   /**
@@ -241,14 +240,6 @@ protected[core] object ExecManifest {
 
     val knownContainerRuntimes: Set[String] = runtimes.flatMap(_.versions.map(_.kind))
 
-    val manifests: Map[String, RuntimeManifest] = {
-      runtimes.flatMap {
-        _.versions.map { m =>
-          m.kind -> m
-        }
-      }.toMap
-    }
-
     def skipDockerPull(image: ImageName): Boolean = {
       blackboxImages.contains(image) ||
       image.prefix.flatMap(p => bypassPullForLocalImages.map(_ == p)).getOrElse(false)
@@ -257,16 +248,7 @@ protected[core] object ExecManifest {
     def toJson: JsObject = {
       runtimes
         .map { family =>
-          family.name -> family.versions.map {
-            case rt =>
-              JsObject(
-                "kind" -> rt.kind.toJson,
-                "image" -> rt.image.publicImageName.toJson,
-                "deprecated" -> rt.deprecated.getOrElse(false).toJson,
-                "default" -> rt.default.getOrElse(false).toJson,
-                "attached" -> rt.attached.isDefined.toJson,
-                "requireMain" -> rt.requireMain.getOrElse(false).toJson)
-          }
+          family.name -> family.versions.map(_.toJsonSummary)
         }
         .toMap
         .toJson
@@ -280,17 +262,12 @@ protected[core] object ExecManifest {
       }
     }
 
-    /**
-     * Collects all runtimes for which there is a stemcell configuration defined
-     *
-     * @return list of runtime manifests with stemcell configurations
-     */
-    def stemcells: Map[RuntimeManifest, List[StemCell]] = {
-      manifests
-        .flatMap {
-          case (_, m) => m.stemCells.map(m -> _)
+    val manifests: Map[String, RuntimeManifest] = {
+      runtimes.flatMap {
+        _.versions.map { m =>
+          m.kind -> m
         }
-        .filter(_._2.nonEmpty)
+      }.toMap
     }
 
     private val defaultRuntimes: Map[String, String] = {
@@ -309,11 +286,5 @@ protected[core] object ExecManifest {
   }
 
   protected[entity] implicit val imageNameSerdes = jsonFormat3(ImageName.apply)
-
-  protected[entity] implicit val stemCellSerdes = {
-    import whisk.core.entity.size.serdes
-    jsonFormat2(StemCell.apply)
-  }
-
-  protected[entity] implicit val runtimeManifestSerdes = jsonFormat8(RuntimeManifest)
+  protected[entity] implicit val runtimeManifestSerdes = jsonFormat7(RuntimeManifest)
 }
diff --git a/common/scala/src/main/scala/whisk/core/entity/Size.scala b/common/scala/src/main/scala/whisk/core/entity/Size.scala
index 2d5b7d9a20..61e27b4faf 100644
--- a/common/scala/src/main/scala/whisk/core/entity/Size.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/Size.scala
@@ -21,8 +21,6 @@ import java.nio.charset.StandardCharsets
 
 import com.typesafe.config.ConfigValue
 import pureconfig._
-import spray.json._
-import whisk.core.entity.ByteSize.formatError
 
 object SizeUnits extends Enumeration {
 
@@ -126,15 +124,6 @@ object size {
   // Creation of an intermediary Config object is necessary here, since "getBytes" is only part of that interface.
   implicit val pureconfigReader =
     ConfigReader[ConfigValue].map(v => ByteSize(v.atKey("key").getBytes("key"), SizeUnits.BYTE))
-
-  protected[entity] implicit val serdes = new RootJsonFormat[ByteSize] {
-    def write(b: ByteSize) = JsString(b.toString)
-
-    def read(value: JsValue): ByteSize = value match {
-      case JsString(s) => ByteSize.fromString(s)
-      case _           => deserializationError(formatError)
-    }
-  }
 }
 
 trait SizeConversion {
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 d26ebdc2f9..183556942e 100644
--- a/core/invoker/src/main/scala/whisk/core/containerpool/ContainerPool.scala
+++ b/core/invoker/src/main/scala/whisk/core/containerpool/ContainerPool.scala
@@ -18,9 +18,15 @@
 package whisk.core.containerpool
 
 import scala.collection.immutable
+
 import whisk.common.{AkkaLogging, LoggingMarkers, TransactionId}
+
 import akka.actor.{Actor, ActorRef, ActorRefFactory, Props}
-import whisk.core.entity._
+
+import whisk.core.entity.ByteSize
+import whisk.core.entity.CodeExec
+import whisk.core.entity.EntityName
+import whisk.core.entity.ExecutableWhiskAction
 import whisk.core.entity.size._
 import whisk.core.connector.MessageFeed
 
@@ -54,7 +60,7 @@ case class WorkerData(data: ContainerData, state: WorkerState)
  */
 class ContainerPool(childFactory: ActorRefFactory => ActorRef,
                     feed: ActorRef,
-                    prewarmConfig: List[PrewarmingConfig] = List.empty,
+                    prewarmConfig: Option[PrewarmingConfig] = None,
                     poolConfig: ContainerPoolConfig)
     extends Actor {
   implicit val logging = new AkkaLogging(context.system.log)
@@ -65,8 +71,7 @@ class ContainerPool(childFactory: ActorRefFactory => ActorRef,
   val logMessageInterval = 10.seconds
 
   prewarmConfig.foreach { config =>
-    logging.info(this, s"pre-warming ${config.count} ${config.exec.kind} ${config.memoryLimit.toString}")(
-      TransactionId.invokerWarmup)
+    logging.info(this, s"pre-warming ${config.count} ${config.exec.kind} containers")(TransactionId.invokerWarmup)
     (1 to config.count).foreach { _ =>
       prewarmContainer(config.exec, config.memoryLimit)
     }
@@ -199,25 +204,26 @@ class ContainerPool(childFactory: ActorRefFactory => ActorRef,
    * @param kind the kind you want to invoke
    * @return the container iff found
    */
-  def takePrewarmContainer(action: ExecutableWhiskAction): Option[(ActorRef, ContainerData)] = {
-    val kind = action.exec.kind
-    val memory = action.limits.memory.megabytes.MB
-    prewarmedPool
-      .find {
-        case (_, PreWarmedData(_, `kind`, `memory`)) => true
-        case _                                       => false
-      }
-      .map {
-        case (ref, data) =>
-          // Move the container to the usual pool
-          freePool = freePool + (ref -> data)
-          prewarmedPool = prewarmedPool - ref
-          // Create a new prewarm container
-          // NOTE: prewarming ignores the action code in exec, but this is dangerous as the field is accessible to the factory
-          prewarmContainer(action.exec, memory)
-          (ref, data)
-      }
-  }
+  def takePrewarmContainer(action: ExecutableWhiskAction): Option[(ActorRef, ContainerData)] =
+    prewarmConfig.flatMap { config =>
+      val kind = action.exec.kind
+      val memory = action.limits.memory.megabytes.MB
+      prewarmedPool
+        .find {
+          case (_, PreWarmedData(_, `kind`, `memory`)) => true
+          case _                                       => false
+        }
+        .map {
+          case (ref, data) =>
+            // Move the container to the usual pool
+            freePool = freePool + (ref -> data)
+            prewarmedPool = prewarmedPool - ref
+            // Create a new prewarm container
+            prewarmContainer(config.exec, config.memoryLimit)
+
+            (ref, data)
+        }
+    }
 
   /** Removes a container and updates state accordingly. */
   def removeContainer(toDelete: ActorRef) = {
@@ -276,9 +282,9 @@ object ContainerPool {
   def props(factory: ActorRefFactory => ActorRef,
             poolConfig: ContainerPoolConfig,
             feed: ActorRef,
-            prewarmConfig: List[PrewarmingConfig] = List.empty) =
+            prewarmConfig: Option[PrewarmingConfig] = None) =
     Props(new ContainerPool(factory, feed, prewarmConfig, poolConfig))
 }
 
-/** Contains settings needed to perform container prewarming. */
+/** Contains settings needed to perform container prewarming */
 case class PrewarmingConfig(count: Int, exec: CodeExec[_], memoryLimit: ByteSize)
diff --git a/core/invoker/src/main/scala/whisk/core/invoker/InvokerReactive.scala b/core/invoker/src/main/scala/whisk/core/invoker/InvokerReactive.scala
index 20cbbd46cd..b132dd815f 100644
--- a/core/invoker/src/main/scala/whisk/core/invoker/InvokerReactive.scala
+++ b/core/invoker/src/main/scala/whisk/core/invoker/InvokerReactive.scala
@@ -33,6 +33,7 @@ import whisk.core.containerpool._
 import whisk.core.containerpool.logging.LogStoreProvider
 import whisk.core.database._
 import whisk.core.entity._
+import whisk.core.entity.size._
 import whisk.http.Messages
 import whisk.spi.SpiLoader
 
@@ -172,17 +173,14 @@ class InvokerReactive(
       ContainerProxy
         .props(containerFactory.createContainer, ack, store, logsProvider.collectLogs, instance, poolConfig))
 
-  val prewarmingConfigs: List[PrewarmingConfig] = {
-    ExecManifest.runtimesManifest.stemcells.flatMap {
-      case (mf, cells) =>
-        cells.map { cell =>
-          PrewarmingConfig(cell.count, new CodeExecAsString(mf, "", None), cell.memory)
-        }
-    }.toList
-  }
+  private val prewarmKind = "nodejs:6"
+  private val prewarmExec = ExecManifest.runtimesManifest
+    .resolveDefaultRuntime(prewarmKind)
+    .map(manifest => CodeExecAsString(manifest, "", None))
+    .get
 
-  private val pool =
-    actorSystem.actorOf(ContainerPool.props(childFactory, poolConfig, activationFeed, prewarmingConfigs))
+  private val pool = actorSystem.actorOf(
+    ContainerPool.props(childFactory, poolConfig, activationFeed, Some(PrewarmingConfig(2, prewarmExec, 256.MB))))
 
   /** Is called when an ActivationMessage is read from Kafka */
   def processActivationMessage(bytes: Array[Byte]): Future[Unit] = {
diff --git a/tests/src/test/scala/whisk/core/containerpool/test/ContainerPoolTests.scala b/tests/src/test/scala/whisk/core/containerpool/test/ContainerPoolTests.scala
index 3fe12538fe..b61e6f8db0 100644
--- a/tests/src/test/scala/whisk/core/containerpool/test/ContainerPoolTests.scala
+++ b/tests/src/test/scala/whisk/core/containerpool/test/ContainerPoolTests.scala
@@ -240,7 +240,7 @@ class ContainerPoolTests
 
     val pool =
       system.actorOf(
-        ContainerPool.props(factory, ContainerPoolConfig(0, 0), feed.ref, List(PrewarmingConfig(1, exec, memoryLimit))))
+        ContainerPool.props(factory, ContainerPoolConfig(0, 0), feed.ref, Some(PrewarmingConfig(1, exec, memoryLimit))))
     containers(0).expectMsg(Start(exec, memoryLimit))
   }
 
@@ -250,7 +250,7 @@ class ContainerPoolTests
 
     val pool =
       system.actorOf(
-        ContainerPool.props(factory, ContainerPoolConfig(1, 1), feed.ref, List(PrewarmingConfig(1, exec, memoryLimit))))
+        ContainerPool.props(factory, ContainerPoolConfig(1, 1), feed.ref, Some(PrewarmingConfig(1, exec, memoryLimit))))
     containers(0).expectMsg(Start(exec, memoryLimit))
     containers(0).send(pool, NeedWork(preWarmedData(exec.kind)))
     pool ! runMessage
@@ -265,7 +265,7 @@ class ContainerPoolTests
 
     val pool = system.actorOf(
       ContainerPool
-        .props(factory, ContainerPoolConfig(1, 1), feed.ref, List(PrewarmingConfig(1, alternativeExec, memoryLimit))))
+        .props(factory, ContainerPoolConfig(1, 1), feed.ref, Some(PrewarmingConfig(1, alternativeExec, memoryLimit))))
     containers(0).expectMsg(Start(alternativeExec, memoryLimit)) // container0 was prewarmed
     containers(0).send(pool, NeedWork(preWarmedData(alternativeExec.kind)))
     pool ! runMessage
@@ -281,7 +281,7 @@ class ContainerPoolTests
     val pool =
       system.actorOf(
         ContainerPool
-          .props(factory, ContainerPoolConfig(1, 1), feed.ref, List(PrewarmingConfig(1, exec, alternativeLimit))))
+          .props(factory, ContainerPoolConfig(1, 1), feed.ref, Some(PrewarmingConfig(1, exec, alternativeLimit))))
     containers(0).expectMsg(Start(exec, alternativeLimit)) // container0 was prewarmed
     containers(0).send(pool, NeedWork(preWarmedData(exec.kind, alternativeLimit)))
     pool ! runMessage
diff --git a/tests/src/test/scala/whisk/core/entity/test/ExecHelpers.scala b/tests/src/test/scala/whisk/core/entity/test/ExecHelpers.scala
index 7ff281078e..688fcd5b7e 100644
--- a/tests/src/test/scala/whisk/core/entity/test/ExecHelpers.scala
+++ b/tests/src/test/scala/whisk/core/entity/test/ExecHelpers.scala
@@ -26,7 +26,6 @@ import whisk.core.WhiskConfig
 import whisk.core.entity._
 import whisk.core.entity.ArgNormalizer.trim
 import whisk.core.entity.ExecManifest._
-import whisk.core.entity.size._
 
 import spray.json._
 import spray.json.DefaultJsonProtocol._
@@ -60,12 +59,7 @@ trait ExecHelpers extends Matchers with WskActorSystem with StreamLogging {
 
   protected def js6(code: String, main: Option[String] = None) = {
     CodeExecAsString(
-      RuntimeManifest(
-        NODEJS6,
-        imagename(NODEJS6),
-        default = Some(true),
-        deprecated = Some(false),
-        stemCells = Some(List(StemCell(2, 256.MB)))),
+      RuntimeManifest(NODEJS6, imagename(NODEJS6), default = Some(true), deprecated = Some(false)),
       trim(code),
       main.map(_.trim))
   }
@@ -76,12 +70,7 @@ trait ExecHelpers extends Matchers with WskActorSystem with StreamLogging {
 
   protected def js6MetaData(main: Option[String] = None, binary: Boolean) = {
     CodeExecMetaDataAsString(
-      RuntimeManifest(
-        NODEJS6,
-        imagename(NODEJS6),
-        default = Some(true),
-        deprecated = Some(false),
-        stemCells = Some(List(StemCell(2, 256.MB)))),
+      RuntimeManifest(NODEJS6, imagename(NODEJS6), default = Some(true), deprecated = Some(false)),
       binary,
       main.map(_.trim))
   }
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 690e449051..71f02379e9 100644
--- a/tests/src/test/scala/whisk/core/entity/test/ExecManifestTests.scala
+++ b/tests/src/test/scala/whisk/core/entity/test/ExecManifestTests.scala
@@ -19,14 +19,12 @@ package whisk.core.entity.test
 
 import common.{StreamLogging, WskActorSystem}
 import org.junit.runner.RunWith
-import org.scalatest.junit.JUnitRunner
 import org.scalatest.{FlatSpec, Matchers}
-import spray.json._
+import org.scalatest.junit.JUnitRunner
 import spray.json.DefaultJsonProtocol._
+import spray.json._
 import whisk.core.entity.ExecManifest
 import whisk.core.entity.ExecManifest._
-import whisk.core.entity.size._
-import whisk.core.entity.ByteSize
 
 import scala.util.Success
 
@@ -65,11 +63,10 @@ class ExecManifestTests extends FlatSpec with WskActorSystem with StreamLogging
     val k1 = RuntimeManifest("k1", ImageName("???"))
     val k2 = RuntimeManifest("k2", ImageName("???"), default = Some(true))
     val p1 = RuntimeManifest("p1", ImageName("???"))
-    val s1 = RuntimeManifest("s1", ImageName("???"), stemCells = Some(List(StemCell(2, 256.MB))))
-    val mf = manifestFactory(JsObject("ks" -> Set(k1, k2).toJson, "p1" -> Set(p1).toJson, "s1" -> Set(s1).toJson))
+    val mf = manifestFactory(JsObject("ks" -> Set(k1, k2).toJson, "p1" -> Set(p1).toJson))
     val runtimes = ExecManifest.runtimes(mf, RuntimeManifestConfig()).get
 
-    Seq("k1", "k2", "p1", "s1").foreach {
+    Seq("k1", "k2", "p1").foreach {
       runtimes.knownContainerRuntimes.contains(_) shouldBe true
     }
 
@@ -78,11 +75,9 @@ class ExecManifestTests extends FlatSpec with WskActorSystem with StreamLogging
     runtimes.resolveDefaultRuntime("k1") shouldBe Some(k1)
     runtimes.resolveDefaultRuntime("k2") shouldBe Some(k2)
     runtimes.resolveDefaultRuntime("p1") shouldBe Some(p1)
-    runtimes.resolveDefaultRuntime("s1") shouldBe Some(s1)
 
     runtimes.resolveDefaultRuntime("ks:default") shouldBe Some(k2)
     runtimes.resolveDefaultRuntime("p1:default") shouldBe Some(p1)
-    runtimes.resolveDefaultRuntime("s1:default") shouldBe Some(s1)
   }
 
   it should "read a valid configuration without default prefix, default tag" in {
@@ -90,15 +85,9 @@ class ExecManifestTests extends FlatSpec with WskActorSystem with StreamLogging
     val i2 = RuntimeManifest("i2", ImageName("???", Some("ppp")), default = Some(true))
     val j1 = RuntimeManifest("j1", ImageName("???", Some("ppp"), Some("ttt")))
     val k1 = RuntimeManifest("k1", ImageName("???", None, Some("ttt")))
-    val s1 = RuntimeManifest("s1", ImageName("???"), stemCells = Some(List(StemCell(2, 256.MB))))
 
     val mf =
-      JsObject(
-        "runtimes" -> JsObject(
-          "is" -> Set(i1, i2).toJson,
-          "js" -> Set(j1).toJson,
-          "ks" -> Set(k1).toJson,
-          "ss" -> Set(s1).toJson))
+      JsObject("runtimes" -> JsObject("is" -> Set(i1, i2).toJson, "js" -> Set(j1).toJson, "ks" -> Set(k1).toJson))
     val rmc = RuntimeManifestConfig(defaultImagePrefix = Some("pre"), defaultImageTag = Some("test"))
     val runtimes = ExecManifest.runtimes(mf, rmc).get
 
@@ -106,9 +95,6 @@ class ExecManifestTests extends FlatSpec with WskActorSystem with StreamLogging
     runtimes.resolveDefaultRuntime("i2").get.image.publicImageName shouldBe "ppp/???:test"
     runtimes.resolveDefaultRuntime("j1").get.image.publicImageName shouldBe "ppp/???:ttt"
     runtimes.resolveDefaultRuntime("k1").get.image.publicImageName shouldBe "pre/???:ttt"
-    runtimes.resolveDefaultRuntime("s1").get.image.publicImageName shouldBe "pre/???:test"
-    runtimes.resolveDefaultRuntime("s1").get.stemCells.get(0).count shouldBe 2
-    runtimes.resolveDefaultRuntime("s1").get.stemCells.get(0).memory shouldBe 256.MB
   }
 
   it should "read a valid configuration with blackbox images but without default prefix or tag" in {
@@ -157,7 +143,7 @@ class ExecManifestTests extends FlatSpec with WskActorSystem with StreamLogging
     an[IllegalArgumentException] should be thrownBy ExecManifest.runtimes(mf, RuntimeManifestConfig()).get
   }
 
-  it should "reject finding a default when none specified for multiple versions in the same family" in {
+  it should "reject finding a default when none is specified for multiple versions" in {
     val k1 = RuntimeManifest("k1", ImageName("???"))
     val k2 = RuntimeManifest("k2", ImageName("???"))
     val mf = manifestFactory(JsObject("ks" -> Set(k1, k2).toJson))
@@ -194,123 +180,4 @@ class ExecManifestTests extends FlatSpec with WskActorSystem with StreamLogging
     manifest.get.skipDockerPull(ImageName(prefix = Some("localpre"), name = "y")) shouldBe true
   }
 
-  it should "de/serialize stem cell configuration" in {
-    val cell = StemCell(3, 128.MB)
-    val cellAsJson = JsObject("count" -> JsNumber(3), "memory" -> JsString("128 MB"))
-    stemCellSerdes.write(cell) shouldBe cellAsJson
-    stemCellSerdes.read(cellAsJson) shouldBe cell
-
-    an[IllegalArgumentException] shouldBe thrownBy {
-      StemCell(-1, 128.MB)
-    }
-
-    an[IllegalArgumentException] shouldBe thrownBy {
-      StemCell(0, 128.MB)
-    }
-
-    an[IllegalArgumentException] shouldBe thrownBy {
-      val cellAsJson = JsObject("count" -> JsNumber(0), "memory" -> JsString("128 MB"))
-      stemCellSerdes.read(cellAsJson)
-    }
-
-    the[IllegalArgumentException] thrownBy {
-      val cellAsJson = JsObject("count" -> JsNumber(1), "memory" -> JsString("128"))
-      stemCellSerdes.read(cellAsJson)
-    } should have message {
-      ByteSize.formatError
-    }
-  }
-
-  it should "parse manifest from JSON string" in {
-    val json = """
-                 |{ "runtimes": {
-                 |    "nodef": [
-                 |      {
-                 |        "kind": "nodejs:6",
-                 |        "image": {
-                 |          "name": "nodejsaction"
-                 |        },
-                 |        "stemCells": [{
-                 |          "count": 1,
-                 |          "memory": "128 MB"
-                 |        }]
-                 |      }, {
-                 |        "kind": "nodejs:8",
-                 |        "default": true,
-                 |        "image": {
-                 |          "name": "nodejsaction"
-                 |        },
-                 |        "stemCells": [{
-                 |          "count": 1,
-                 |          "memory": "128 MB"
-                 |        }, {
-                 |          "count": 1,
-                 |          "memory": "256 MB"
-                 |        }]
-                 |      }
-                 |    ],
-                 |    "pythonf": [{
-                 |      "kind": "python",
-                 |      "image": {
-                 |        "name": "pythonaction"
-                 |      },
-                 |      "stemCells": [{
-                 |        "count": 2,
-                 |        "memory": "256 MB"
-                 |      }]
-                 |    }],
-                 |    "swiftf": [{
-                 |      "kind": "swift",
-                 |      "image": {
-                 |        "name": "swiftaction"
-                 |      },
-                 |      "stemCells": []
-                 |    }],
-                 |    "phpf": [{
-                 |      "kind": "php",
-                 |      "image": {
-                 |        "name": "phpaction"
-                 |      }
-                 |    }]
-                 |  }
-                 |}
-                 |""".stripMargin.parseJson.asJsObject
-
-    val js6 = RuntimeManifest("nodejs:6", ImageName("nodejsaction"), stemCells = Some(List(StemCell(1, 128.MB))))
-    val js8 = RuntimeManifest(
-      "nodejs:8",
-      ImageName("nodejsaction"),
-      default = Some(true),
-      stemCells = Some(List(StemCell(1, 128.MB), StemCell(1, 256.MB))))
-    val py = RuntimeManifest("python", ImageName("pythonaction"), stemCells = Some(List(StemCell(2, 256.MB))))
-    val sw = RuntimeManifest("swift", ImageName("swiftaction"), stemCells = Some(List.empty))
-    val ph = RuntimeManifest("php", ImageName("phpaction"))
-    val mf = ExecManifest.runtimes(json, RuntimeManifestConfig()).get
-
-    mf shouldBe {
-      Runtimes(
-        Set(
-          RuntimeFamily("nodef", Set(js6, js8)),
-          RuntimeFamily("pythonf", Set(py)),
-          RuntimeFamily("swiftf", Set(sw)),
-          RuntimeFamily("phpf", Set(ph))),
-        Set.empty,
-        None)
-    }
-
-    def stemCellFactory(m: RuntimeManifest, cells: List[StemCell]) = cells.map { c =>
-      (m.kind, m.image, c.count, c.memory)
-    }
-
-    mf.stemcells.flatMap {
-      case (m, cells) =>
-        cells.map { c =>
-          (m.kind, m.image, c.count, c.memory)
-        }
-    }.toList should contain theSameElementsAs List(
-      (js6.kind, js6.image, 1, 128.MB),
-      (js8.kind, js8.image, 1, 128.MB),
-      (js8.kind, js8.image, 1, 256.MB),
-      (py.kind, py.image, 2, 256.MB))
-  }
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services