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/08/06 15:46:41 UTC

[GitHub] chetanmeh closed pull request #3286: Treat action code as attachments for created/updated actions

chetanmeh closed pull request #3286: Treat action code as attachments for created/updated actions
URL: https://github.com/apache/incubator-openwhisk/pull/3286
 
 
   

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 1866b69195..44cef222b3 100644
--- a/ansible/files/runtimes.json
+++ b/ansible/files/runtimes.json
@@ -6,7 +6,11 @@
                 "image": {
                     "name": "nodejsaction"
                 },
-                "deprecated": true
+                "deprecated": true,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                }
             },
             {
                 "kind": "nodejs:6",
@@ -14,7 +18,11 @@
                 "image": {
                     "name": "nodejs6action"
                 },
-                "deprecated": false
+                "deprecated": false,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                }
             },
             {
                 "kind": "nodejs:8",
@@ -22,7 +30,11 @@
                 "image": {
                     "name": "action-nodejs-v8"
                 },
-                "deprecated": false
+                "deprecated": false,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                }
             }
         ],
         "python": [
@@ -31,7 +43,11 @@
                 "image": {
                     "name": "python2action"
                 },
-                "deprecated": false
+                "deprecated": false,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                }
             },
             {
                 "kind": "python:2",
@@ -39,14 +55,22 @@
                 "image": {
                     "name": "python2action"
                 },
-                "deprecated": false
+                "deprecated": false,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                }
             },
             {
                 "kind": "python:3",
                 "image": {
                     "name": "python3action"
                 },
-                "deprecated": false
+                "deprecated": false,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                }
             }
         ],
         "swift": [
@@ -55,21 +79,33 @@
                 "image": {
                     "name": "swiftaction"
                 },
-                "deprecated": true
+                "deprecated": true,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                }
             },
             {
                 "kind": "swift:3",
                 "image": {
                     "name": "swift3action"
                 },
-                "deprecated": true
+                "deprecated": true,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                }
             },
             {
                 "kind": "swift:3.1.1",
                 "image": {
                     "name": "action-swift-v3.1.1"
                 },
-                "deprecated": false
+                "deprecated": false,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                }
             },
             {
                 "kind": "swift:4.1",
@@ -77,7 +113,11 @@
                 "image": {
                     "name": "action-swift-v4.1"
                 },
-                "deprecated": false
+                "deprecated": false,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                }
             }
         ],
         "java": [
@@ -89,8 +129,8 @@
                 },
                 "deprecated": false,
                 "attached": {
-                    "attachmentName": "jarfile",
-                    "attachmentType": "application/java-archive"
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
                 },
                 "sentinelledLogs": false,
                 "requireMain": true
@@ -101,6 +141,10 @@
                 "kind": "php:7.1",
                 "default": true,
                 "deprecated": false,
+                "attached": {
+                    "attachmentName": "codefile",
+                    "attachmentType": "text/plain"
+                },
                 "image": {
                     "name": "action-php-v7.1"
                 }
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 8f318129d0..77bc8cefe0 100644
--- a/common/scala/src/main/scala/whisk/core/entity/Exec.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/Exec.scala
@@ -30,6 +30,8 @@ import whisk.core.entity.size.SizeInt
 import whisk.core.entity.size.SizeOptionString
 import whisk.core.entity.size.SizeString
 
+import java.util.Base64
+
 /**
  * Exec encodes the executable details of an action. For black
  * box container, an image name is required. For Javascript and Python
@@ -140,19 +142,24 @@ protected[core] case class CodeExecMetaDataAsString(manifest: RuntimeManifest,
 
 protected[core] case class CodeExecAsAttachment(manifest: RuntimeManifest,
                                                 override val code: Attachment[String],
-                                                override val entryPoint: Option[String])
+                                                override val entryPoint: Option[String],
+                                                override val binary: Boolean = false)
     extends CodeExec[Attachment[String]] {
   override val kind = manifest.kind
   override val image = manifest.image
   override val sentinelledLogs = manifest.sentinelledLogs.getOrElse(true)
   override val deprecated = manifest.deprecated.getOrElse(false)
   override val pull = false
-  override lazy val binary = true
   override def codeAsJson = code.toJson
 
   def inline(bytes: Array[Byte]): CodeExecAsAttachment = {
-    val encoded = new String(bytes, StandardCharsets.UTF_8)
-    copy(code = Inline(encoded))
+    val code = new String(bytes, StandardCharsets.UTF_8)
+
+    if (kind == "java" && !Exec.isBinaryCode(code)) {
+      val encoded = Base64.getEncoder.encodeToString(bytes)
+      copy(code = Inline(encoded))
+    } else
+      copy(code = Inline(code))
   }
 
   def attach: CodeExecAsAttachment = {
@@ -304,21 +311,29 @@ protected[core] object Exec extends ArgNormalizer[Exec] with DefaultJsonProtocol
 
           manifest.attached
             .map { a =>
-              val jar: Attachment[String] = {
-                // java actions once stored the attachment in "jar" instead of "code"
-                obj.fields.get("code").orElse(obj.fields.get("jar"))
+              val code = obj.fields.get("code")
+              val binary: Boolean = code match {
+                case Some(JsString(c)) => isBinaryCode(c)
+                case _ =>
+                  obj.fields.get("binary") match {
+                    case Some(JsBoolean(b)) => b
+                    case _                  => false
+                  }
+              }
+              val attachment: Attachment[String] = {
+                code
               } map {
                 attFmt[String].read(_)
               } getOrElse {
-                throw new DeserializationException(
-                  s"'code' must be a valid base64 string in 'exec' for '$kind' actions")
+                throw new DeserializationException(s"'code' must be a string defined in 'exec' for '$kind' actions")
               }
               val main = optMainField.orElse {
                 if (manifest.requireMain.exists(identity)) {
                   throw new DeserializationException(s"'main' must be a string defined in 'exec' for '$kind' actions")
                 } else None
               }
-              CodeExecAsAttachment(manifest, jar, main)
+
+              CodeExecAsAttachment(manifest, attachment, main, binary)
             }
             .getOrElse {
               val code: String = obj.fields.get("code") match {
diff --git a/common/scala/src/main/scala/whisk/core/entity/WhiskAction.scala b/common/scala/src/main/scala/whisk/core/entity/WhiskAction.scala
index 1e380c2e88..ac1ec385d8 100644
--- a/common/scala/src/main/scala/whisk/core/entity/WhiskAction.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/WhiskAction.scala
@@ -19,7 +19,6 @@ package whisk.core.entity
 
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayOutputStream
-import java.util.Base64
 
 import scala.concurrent.ExecutionContext
 import scala.concurrent.Future
@@ -326,14 +325,14 @@ object WhiskAction extends DocumentFactory[WhiskAction] with WhiskEntityQueries[
       require(doc != null, "doc undefined")
     } map { _ =>
       doc.exec match {
-        case exec @ CodeExecAsAttachment(_, Inline(code), _) =>
+        case exec @ CodeExecAsAttachment(_, Inline(code), _, _) =>
           implicit val logger = db.logging
           implicit val ec = db.executionContext
 
           val newDoc = doc.copy(exec = exec.attach)
           newDoc.revision(doc.rev)
 
-          val stream = new ByteArrayInputStream(Base64.getDecoder().decode(code))
+          val stream = new ByteArrayInputStream(code.getBytes("UTF-8"))
           val manifest = exec.manifest.attached.get
 
           for (i1 <- super.put(db, newDoc);
@@ -369,16 +368,14 @@ object WhiskAction extends DocumentFactory[WhiskAction] with WhiskEntityQueries[
 
     fa.flatMap { action =>
       action.exec match {
-        case exec @ CodeExecAsAttachment(_, Attached(attachmentName, _), _) =>
+        case exec @ CodeExecAsAttachment(_, Attached(attachmentName, _), _, _) =>
           val boas = new ByteArrayOutputStream()
-          val b64s = Base64.getEncoder().wrap(boas)
 
-          getAttachment[A](db, action, attachmentName, b64s, Some { a: WhiskAction =>
-            b64s.close()
-            val newAction = a.copy(exec = exec.inline(boas.toByteArray))
-            newAction.revision(a.rev)
+          getAttachment[A](db, action, attachmentName, boas).map { _ =>
+            val newAction = action.copy(exec = exec.inline(boas.toByteArray))
+            newAction.revision(action.rev)
             newAction
-          })
+          }
 
         case _ =>
           Future.successful(action)
diff --git a/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
index 609e773534..d060699fbd 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
@@ -18,6 +18,7 @@
 package whisk.core.controller.test
 
 import java.time.Instant
+import java.io.ByteArrayInputStream
 
 import scala.concurrent.duration.DurationInt
 import scala.language.postfixOps
@@ -29,6 +30,7 @@ import akka.http.scaladsl.model.StatusCodes._
 import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonMarshaller
 import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonUnmarshaller
 import akka.http.scaladsl.server.Route
+import akka.stream.scaladsl.StreamConverters
 
 import spray.json._
 import spray.json.DefaultJsonProtocol._
@@ -40,10 +42,6 @@ import whisk.core.entitlement.Collection
 import whisk.http.ErrorResponse
 import whisk.http.Messages
 
-import java.io.ByteArrayInputStream
-import java.util.Base64
-import akka.stream.scaladsl._
-
 /**
  * Tests Actions API.
  *
@@ -69,6 +67,274 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
   val actionLimit = Exec.sizeLimit
   val parametersLimit = Parameters.sizeLimit
 
+  it should "put and then get an action with attachment from cache" in {
+    val javaAction =
+      WhiskAction(namespace, aname(), javaDefault("ZHViZWU=", Some("hello")), annotations = Parameters("exec", "java"))
+    val nodeAction = WhiskAction(namespace, aname(), jsDefault("??"), Parameters("x", "b"))
+    val swiftAction = WhiskAction(namespace, aname(), swift3("??"), Parameters("x", "b"))
+    val actions = Seq((javaAction, JAVA_DEFAULT), (nodeAction, NODEJS6), (swiftAction, SWIFT3))
+
+    actions.foreach {
+      case (action, kind) =>
+        val content = WhiskActionPut(
+          Some(action.exec),
+          Some(action.parameters),
+          Some(ActionLimitsOption(Some(action.limits.timeout), Some(action.limits.memory), Some(action.limits.logs))))
+        val cacheKey = s"${CacheKey(action)}".replace("(", "\\(").replace(")", "\\)")
+
+        val expectedPutLog = Seq(
+          s"caching $cacheKey",
+          s"uploading attachment 'codefile' of document 'id: ${action.namespace}/${action.name}",
+          s"caching $cacheKey").mkString("(?s).*")
+        val notExpectedGetLog = Seq(
+          s"finding document: 'id: ${action.namespace}/${action.name}",
+          s"finding attachment 'codefile' of document 'id: ${action.namespace}/${action.name}").mkString("(?s).*")
+
+        // first request invalidates any previous entries and caches new result
+        Put(s"$collectionPath/${action.name}", content) ~> Route.seal(routes(creds)(transid())) ~> check {
+          status should be(OK)
+          val response = responseAs[WhiskAction]
+          response should be(
+            WhiskAction(
+              action.namespace,
+              action.name,
+              action.exec,
+              action.parameters,
+              action.limits,
+              action.version,
+              action.publish,
+              action.annotations ++ Parameters(WhiskAction.execFieldName, kind)))
+        }
+
+        stream.toString should not include (s"invalidating ${CacheKey(action)} on delete")
+        stream.toString should include regex (expectedPutLog)
+        stream.reset()
+
+        // second request should fetch from cache
+        Get(s"$collectionPath/${action.name}") ~> Route.seal(routes(creds)(transid())) ~> check {
+          status should be(OK)
+          val response = responseAs[WhiskAction]
+          response should be(
+            WhiskAction(
+              action.namespace,
+              action.name,
+              action.exec,
+              action.parameters,
+              action.limits,
+              action.version,
+              action.publish,
+              action.annotations ++ Parameters(WhiskAction.execFieldName, kind)))
+        }
+        stream.toString should include(s"serving from cache: ${CacheKey(action)}")
+        stream.toString should not include regex(notExpectedGetLog)
+        stream.reset()
+
+        // delete should invalidate cache
+        Delete(s"$collectionPath/${action.name}") ~> Route.seal(routes(creds)(transid())) ~> check {
+          status should be(OK)
+          val response = responseAs[WhiskAction]
+          response should be(
+            WhiskAction(
+              action.namespace,
+              action.name,
+              action.exec,
+              action.parameters,
+              action.limits,
+              action.version,
+              action.publish,
+              action.annotations ++ Parameters(WhiskAction.execFieldName, kind)))
+        }
+
+        stream.toString should include(s"invalidating ${CacheKey(action)}")
+        stream.reset()
+    }
+  }
+
+  it should "get an action with attachment that is not cached" in {
+    implicit val tid = transid()
+    val code = "ZHViZWU="
+    val action =
+      WhiskAction(namespace, aname(), javaDefault(code, Some("hello")), annotations = Parameters("exec", "java"))
+    val content = WhiskActionPut(
+      Some(action.exec),
+      Some(action.parameters),
+      Some(ActionLimitsOption(Some(action.limits.timeout), Some(action.limits.memory), Some(action.limits.logs))))
+    val name = action.name
+    val cacheKey = s"${CacheKey(action)}".replace("(", "\\(").replace(")", "\\)")
+    val expectedGetLog = Seq(
+      s"finding document: 'id: ${action.namespace}/${action.name}",
+      s"finding attachment 'codefile' of document 'id: ${action.namespace}/${action.name}").mkString("(?s).*")
+
+    action.exec match {
+      case exec @ CodeExecAsAttachment(_, _, _, _) =>
+        val newAction = action.copy(exec = exec.attach)
+        newAction.revision(action.rev)
+
+        val doc1 = put(entityStore, newAction, false)
+
+        val stream = new ByteArrayInputStream(code.getBytes)
+        val manifest = exec.manifest.attached.get
+        val src = StreamConverters.fromInputStream(() => stream)
+
+        attach(entityStore, doc1, manifest.attachmentName, manifest.attachmentType, src)
+
+      case _ =>
+    }
+
+    // second request should fetch from cache
+    Get(s"$collectionPath/$name") ~> Route.seal(routes(creds)(transid())) ~> check {
+      status should be(OK)
+      val response = responseAs[WhiskAction]
+      response should be(
+        WhiskAction(
+          action.namespace,
+          action.name,
+          action.exec,
+          action.parameters,
+          action.limits,
+          action.version,
+          action.publish,
+          action.annotations ++ Parameters(WhiskAction.execFieldName, JAVA_DEFAULT)))
+    }
+
+    stream.toString should include regex (expectedGetLog)
+    stream.reset()
+  }
+
+  it should "update an existing action with attachment that is not cached" in {
+    implicit val tid = transid()
+    val code = "ZHViZWU="
+    val action =
+      WhiskAction(namespace, aname(), javaDefault(code, Some("hello")), annotations = Parameters("exec", "java"))
+    val content = WhiskActionPut(
+      Some(action.exec),
+      Some(action.parameters),
+      Some(ActionLimitsOption(Some(action.limits.timeout), Some(action.limits.memory), Some(action.limits.logs))))
+    val name = action.name
+    val cacheKey = s"${CacheKey(action)}".replace("(", "\\(").replace(")", "\\)")
+    val expectedPutLog = Seq(
+      s"caching $cacheKey",
+      s"uploading attachment 'codefile' of document 'id: ${action.namespace}/${action.name}",
+      s"caching $cacheKey").mkString("(?s).*")
+
+    action.exec match {
+      case exec @ CodeExecAsAttachment(_, _, _, _) =>
+        val newAction = action.copy(exec = exec.attach)
+        newAction.revision(action.rev)
+
+        val doc = put(entityStore, newAction)
+
+        val stream = new ByteArrayInputStream(code.getBytes)
+        val manifest = exec.manifest.attached.get
+        val src = StreamConverters.fromInputStream(() => stream)
+
+        attach(entityStore, doc, manifest.attachmentName, manifest.attachmentType, src)
+
+      case _ =>
+    }
+
+    Put(s"$collectionPath/$name?overwrite=true", content) ~> Route.seal(routes(creds)(transid())) ~> check {
+      status should be(OK)
+      val response = responseAs[WhiskAction]
+      response should be(
+        WhiskAction(
+          action.namespace,
+          action.name,
+          action.exec,
+          action.parameters,
+          action.limits,
+          action.version.upPatch,
+          action.publish,
+          action.annotations ++ Parameters(WhiskAction.execFieldName, JAVA_DEFAULT)))
+    }
+    stream.toString should include regex (expectedPutLog)
+    stream.reset()
+
+    // delete should invalidate cache
+    Delete(s"$collectionPath/$name") ~> Route.seal(routes(creds)(transid())) ~> check {
+      status should be(OK)
+      val response = responseAs[WhiskAction]
+      response should be(
+        WhiskAction(
+          action.namespace,
+          action.name,
+          action.exec,
+          action.parameters,
+          action.limits,
+          action.version.upPatch,
+          action.publish,
+          action.annotations ++ Parameters(WhiskAction.execFieldName, JAVA_DEFAULT)))
+    }
+    stream.toString should include(s"invalidating ${CacheKey(action)}")
+    stream.reset()
+  }
+
+  it should "ensure old and new action schemas are supported" in {
+    implicit val tid = transid()
+    val actionOldSchema = WhiskAction(namespace, aname(), js6Old("??"))
+    val actionNewSchema = WhiskAction(namespace, aname(), jsDefault("??"))
+    val content = WhiskActionPut(
+      Some(actionOldSchema.exec),
+      Some(actionOldSchema.parameters),
+      Some(
+        ActionLimitsOption(
+          Some(actionOldSchema.limits.timeout),
+          Some(actionOldSchema.limits.memory),
+          Some(actionOldSchema.limits.logs))))
+    val expectedPutLog =
+      Seq(s"uploading attachment 'codefile' of document 'id: ${actionOldSchema.namespace}/${actionOldSchema.name}")
+        .mkString("(?s).*")
+
+    put(entityStore, actionOldSchema)
+
+    stream.toString should not include regex(expectedPutLog)
+    stream.reset()
+
+    Post(s"$collectionPath/${actionOldSchema.name}") ~> Route.seal(routes(creds)) ~> check {
+      status should be(Accepted)
+      val response = responseAs[JsObject]
+      response.fields("activationId") should not be None
+    }
+
+    Put(s"$collectionPath/${actionOldSchema.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
+      val response = responseAs[WhiskAction]
+      response should be(
+        WhiskAction(
+          actionOldSchema.namespace,
+          actionOldSchema.name,
+          actionNewSchema.exec,
+          actionOldSchema.parameters,
+          actionOldSchema.limits,
+          actionOldSchema.version.upPatch,
+          actionOldSchema.publish,
+          actionOldSchema.annotations ++ Parameters(WhiskAction.execFieldName, NODEJS6)))
+    }
+
+    stream.toString should include regex (expectedPutLog)
+    stream.reset()
+
+    Post(s"$collectionPath/${actionOldSchema.name}") ~> Route.seal(routes(creds)) ~> check {
+      status should be(Accepted)
+      val response = responseAs[JsObject]
+      response.fields("activationId") should not be None
+    }
+
+    Delete(s"$collectionPath/${actionOldSchema.name}") ~> Route.seal(routes(creds)) ~> check {
+      status should be(OK)
+      val response = responseAs[WhiskAction]
+      response should be(
+        WhiskAction(
+          actionOldSchema.namespace,
+          actionOldSchema.name,
+          actionNewSchema.exec,
+          actionOldSchema.parameters,
+          actionOldSchema.limits,
+          actionOldSchema.version.upPatch,
+          actionOldSchema.publish,
+          actionOldSchema.annotations ++ Parameters(WhiskAction.execFieldName, NODEJS6)))
+    }
+  }
+
   //// GET /actions
   it should "return empty list when no actions exist" in {
     implicit val tid = transid()
@@ -779,201 +1045,6 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
     }
   }
 
-  it should "put and then get an action with attachment from cache" in {
-    val action =
-      WhiskAction(namespace, aname(), javaDefault("ZHViZWU=", Some("hello")), annotations = Parameters("exec", "java"))
-    val content = WhiskActionPut(
-      Some(action.exec),
-      Some(action.parameters),
-      Some(ActionLimitsOption(Some(action.limits.timeout), Some(action.limits.memory), Some(action.limits.logs))))
-    val name = action.name
-    val cacheKey = s"${CacheKey(action)}".replace("(", "\\(").replace(")", "\\)")
-    val expectedPutLog = Seq(
-      s"caching $cacheKey",
-      s"uploading attachment 'jarfile' of document 'id: ${action.namespace}/${action.name}",
-      s"caching $cacheKey").mkString("(?s).*")
-    val notExpectedGetLog = Seq(
-      s"finding document: 'id: ${action.namespace}/${action.name}",
-      s"finding attachment 'jarfile' of document 'id: ${action.namespace}/${action.name}").mkString("(?s).*")
-
-    // first request invalidates any previous entries and caches new result
-    Put(s"$collectionPath/$name", content) ~> Route.seal(routes(creds)(transid())) ~> check {
-      status should be(OK)
-      val response = responseAs[WhiskAction]
-      response should be(
-        WhiskAction(
-          action.namespace,
-          action.name,
-          action.exec,
-          action.parameters,
-          action.limits,
-          action.version,
-          action.publish,
-          action.annotations ++ Parameters(WhiskAction.execFieldName, JAVA_DEFAULT)))
-    }
-
-    stream.toString should not include (s"invalidating ${CacheKey(action)} on delete")
-    stream.toString should include regex (expectedPutLog)
-    stream.reset()
-
-    // second request should fetch from cache
-    Get(s"$collectionPath/$name") ~> Route.seal(routes(creds)(transid())) ~> check {
-      status should be(OK)
-      val response = responseAs[WhiskAction]
-      response should be(
-        WhiskAction(
-          action.namespace,
-          action.name,
-          action.exec,
-          action.parameters,
-          action.limits,
-          action.version,
-          action.publish,
-          action.annotations ++ Parameters(WhiskAction.execFieldName, JAVA_DEFAULT)))
-    }
-
-    stream.toString should include(s"serving from cache: ${CacheKey(action)}")
-    stream.toString should not include regex(notExpectedGetLog)
-    stream.reset()
-
-    // delete should invalidate cache
-    Delete(s"$collectionPath/$name") ~> Route.seal(routes(creds)(transid())) ~> check {
-      status should be(OK)
-      val response = responseAs[WhiskAction]
-      response should be(
-        WhiskAction(
-          action.namespace,
-          action.name,
-          action.exec,
-          action.parameters,
-          action.limits,
-          action.version,
-          action.publish,
-          action.annotations ++ Parameters(WhiskAction.execFieldName, JAVA_DEFAULT)))
-    }
-    stream.toString should include(s"invalidating ${CacheKey(action)}")
-    stream.reset()
-  }
-
-  it should "get an action with attachment that is not cached" in {
-    implicit val tid = transid()
-    val code = "ZHViZWU="
-    val action =
-      WhiskAction(namespace, aname(), javaDefault(code, Some("hello")), annotations = Parameters("exec", "java"))
-    val content = WhiskActionPut(
-      Some(action.exec),
-      Some(action.parameters),
-      Some(ActionLimitsOption(Some(action.limits.timeout), Some(action.limits.memory), Some(action.limits.logs))))
-    val name = action.name
-    val cacheKey = s"${CacheKey(action)}".replace("(", "\\(").replace(")", "\\)")
-    val expectedGetLog = Seq(
-      s"finding document: 'id: ${action.namespace}/${action.name}",
-      s"finding attachment 'jarfile' of document 'id: ${action.namespace}/${action.name}").mkString("(?s).*")
-
-    action.exec match {
-      case exec @ CodeExecAsAttachment(_, _, _) =>
-        val newAction = action.copy(exec = exec.attach)
-        newAction.revision(action.rev)
-
-        val doc1 = put(entityStore, newAction, false)
-
-        val stream = new ByteArrayInputStream(Base64.getDecoder().decode(code))
-        val manifest = exec.manifest.attached.get
-        val src = StreamConverters.fromInputStream(() => stream)
-
-        attach(entityStore, doc1, manifest.attachmentName, manifest.attachmentType, src)
-
-      case _ =>
-    }
-
-    // second request should fetch from cache
-    Get(s"$collectionPath/$name") ~> Route.seal(routes(creds)(transid())) ~> check {
-      status should be(OK)
-      val response = responseAs[WhiskAction]
-      response should be(
-        WhiskAction(
-          action.namespace,
-          action.name,
-          action.exec,
-          action.parameters,
-          action.limits,
-          action.version,
-          action.publish,
-          action.annotations ++ Parameters(WhiskAction.execFieldName, JAVA_DEFAULT)))
-    }
-
-    stream.toString should include regex (expectedGetLog)
-    stream.reset()
-  }
-
-  it should "update an existing action with attachment that is not cached" in {
-    implicit val tid = transid()
-    val code = "ZHViZWU="
-    val action =
-      WhiskAction(namespace, aname(), javaDefault(code, Some("hello")), annotations = Parameters("exec", "java"))
-    val content = WhiskActionPut(
-      Some(action.exec),
-      Some(action.parameters),
-      Some(ActionLimitsOption(Some(action.limits.timeout), Some(action.limits.memory), Some(action.limits.logs))))
-    val name = action.name
-    val cacheKey = s"${CacheKey(action)}".replace("(", "\\(").replace(")", "\\)")
-    val expectedPutLog = Seq(
-      s"caching $cacheKey",
-      s"uploading attachment 'jarfile' of document 'id: ${action.namespace}/${action.name}",
-      s"caching $cacheKey").mkString("(?s).*")
-
-    action.exec match {
-      case exec @ CodeExecAsAttachment(_, _, _) =>
-        val newAction = action.copy(exec = exec.attach)
-        newAction.revision(action.rev)
-
-        val doc = put(entityStore, newAction)
-
-        val stream = new ByteArrayInputStream(Base64.getDecoder().decode(code))
-        val manifest = exec.manifest.attached.get
-        val src = StreamConverters.fromInputStream(() => stream)
-
-        attach(entityStore, doc, manifest.attachmentName, manifest.attachmentType, src)
-
-      case _ =>
-    }
-
-    Put(s"$collectionPath/$name?overwrite=true", content) ~> Route.seal(routes(creds)(transid())) ~> check {
-      status should be(OK)
-      val response = responseAs[WhiskAction]
-      response should be(
-        WhiskAction(
-          action.namespace,
-          action.name,
-          action.exec,
-          action.parameters,
-          action.limits,
-          action.version.upPatch,
-          action.publish,
-          action.annotations ++ Parameters(WhiskAction.execFieldName, JAVA_DEFAULT)))
-    }
-    stream.toString should include regex (expectedPutLog)
-    stream.reset()
-
-    // delete should invalidate cache
-    Delete(s"$collectionPath/$name") ~> Route.seal(routes(creds)(transid())) ~> check {
-      status should be(OK)
-      val response = responseAs[WhiskAction]
-      response should be(
-        WhiskAction(
-          action.namespace,
-          action.name,
-          action.exec,
-          action.parameters,
-          action.limits,
-          action.version.upPatch,
-          action.publish,
-          action.annotations ++ Parameters(WhiskAction.execFieldName, JAVA_DEFAULT)))
-    }
-    stream.toString should include(s"invalidating ${CacheKey(action)}")
-    stream.reset()
-  }
-
   it should "reject put with conflict for pre-existing action" in {
     implicit val tid = transid()
     val action = WhiskAction(namespace, aname(), jsDefault("??"), Parameters("x", "b"))
diff --git a/tests/src/test/scala/whisk/core/database/test/CacheConcurrencyTests.scala b/tests/src/test/scala/whisk/core/database/test/CacheConcurrencyTests.scala
index d2d61bb83d..4e68b37cb6 100644
--- a/tests/src/test/scala/whisk/core/database/test/CacheConcurrencyTests.scala
+++ b/tests/src/test/scala/whisk/core/database/test/CacheConcurrencyTests.scala
@@ -126,7 +126,11 @@ class CacheConcurrencyTests extends FlatSpec with WskTestHelpers with BeforeAndA
         para.tasksupport = new ForkJoinTaskSupport(new ForkJoinPool(nThreads))
         para.map { i =>
           if (i != 16) {
-            wsk.action.get(name)
+            val rr = wsk.action.get(name, expectedExitCode = DONTCARE_EXIT)
+            withClue(s"expecting get to either succeed or fail with not found: $rr") {
+              // some will succeed and some should fail with not found
+              rr.exitCode should (be(SUCCESS_EXIT) or be(NOT_FOUND))
+            }
           } else {
             wsk.action.create(name, None, parameters = Map("color" -> JsString("blue")), update = true)
           }
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 688fcd5b7e..3bee1dc8d7 100644
--- a/tests/src/test/scala/whisk/core/entity/test/ExecHelpers.scala
+++ b/tests/src/test/scala/whisk/core/entity/test/ExecHelpers.scala
@@ -53,33 +53,52 @@ trait ExecHelpers extends Matchers with WskActorSystem with StreamLogging {
     ExecManifest.ImageName(image, Some("openwhisk"), Some("latest"))
   }
 
-  protected def js(code: String, main: Option[String] = None) = {
+  protected def jsOld(code: String, main: Option[String] = None) = {
     CodeExecAsString(RuntimeManifest(NODEJS, imagename(NODEJS), deprecated = Some(true)), trim(code), main.map(_.trim))
   }
 
-  protected def js6(code: String, main: Option[String] = None) = {
+  protected def js(code: String, main: Option[String] = None) = {
+    val attachment = attFmt[String].read(code.trim.toJson)
+    val manifest = ExecManifest.runtimesManifest.resolveDefaultRuntime(NODEJS).get
+
+    CodeExecAsAttachment(manifest, attachment, main.map(_.trim), Exec.isBinaryCode(code))
+  }
+
+  protected def js6Old(code: String, main: Option[String] = None) = {
     CodeExecAsString(
       RuntimeManifest(NODEJS6, imagename(NODEJS6), default = Some(true), deprecated = Some(false)),
       trim(code),
       main.map(_.trim))
   }
+  protected def js6(code: String, main: Option[String] = None) = {
+    val attachment = attFmt[String].read(code.trim.toJson)
+    val manifest = ExecManifest.runtimesManifest.resolveDefaultRuntime(NODEJS6).get
+
+    CodeExecAsAttachment(manifest, attachment, main.map(_.trim), Exec.isBinaryCode(code))
+  }
 
   protected def jsDefault(code: String, main: Option[String] = None) = {
     js6(code, main)
   }
 
-  protected def js6MetaData(main: Option[String] = None, binary: Boolean) = {
+  protected def js6MetaDataOld(main: Option[String] = None, binary: Boolean) = {
     CodeExecMetaDataAsString(
       RuntimeManifest(NODEJS6, imagename(NODEJS6), default = Some(true), deprecated = Some(false)),
       binary,
       main.map(_.trim))
   }
 
+  protected def js6MetaData(main: Option[String] = None, binary: Boolean) = {
+    val manifest = ExecManifest.runtimesManifest.resolveDefaultRuntime(NODEJS6).get
+
+    CodeExecMetaDataAsAttachment(manifest, binary, main.map(_.trim))
+  }
+
   protected def javaDefault(code: String, main: Option[String] = None) = {
     val attachment = attFmt[String].read(code.trim.toJson)
     val manifest = ExecManifest.runtimesManifest.resolveDefaultRuntime(JAVA_DEFAULT).get
 
-    CodeExecAsAttachment(manifest, attachment, main.map(_.trim))
+    CodeExecAsAttachment(manifest, attachment, main.map(_.trim), Exec.isBinaryCode(code))
   }
 
   protected def javaMetaData(main: Option[String] = None, binary: Boolean) = {
@@ -88,16 +107,22 @@ trait ExecHelpers extends Matchers with WskActorSystem with StreamLogging {
     CodeExecMetaDataAsAttachment(manifest, binary, main.map(_.trim))
   }
 
-  protected def swift(code: String, main: Option[String] = None) = {
+  protected def swiftOld(code: String, main: Option[String] = None) = {
     CodeExecAsString(RuntimeManifest(SWIFT, imagename(SWIFT), deprecated = Some(true)), trim(code), main.map(_.trim))
   }
 
+  protected def swift(code: String, main: Option[String] = None) = {
+    val attachment = attFmt[String].read(code.trim.toJson)
+    val manifest = ExecManifest.runtimesManifest.resolveDefaultRuntime(SWIFT).get
+
+    CodeExecAsAttachment(manifest, attachment, main.map(_.trim), Exec.isBinaryCode(code))
+  }
+
   protected def swift3(code: String, main: Option[String] = None) = {
-    val default = ExecManifest.runtimesManifest.resolveDefaultRuntime(SWIFT3).flatMap(_.default)
-    CodeExecAsString(
-      RuntimeManifest(SWIFT3, imagename(SWIFT3), default = default, deprecated = Some(false)),
-      trim(code),
-      main.map(_.trim))
+    val attachment = attFmt[String].read(code.trim.toJson)
+    val manifest = ExecManifest.runtimesManifest.resolveDefaultRuntime(SWIFT3).get
+
+    CodeExecAsAttachment(manifest, attachment, main.map(_.trim), Exec.isBinaryCode(code))
   }
 
   protected def sequence(components: Vector[FullyQualifiedEntityName]) = SequenceExec(components)


 

----------------------------------------------------------------
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