This is an automated email from the ASF dual-hosted git repository.

markusthoemmes 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 8fd78b7  Fix attachment name compatibility issue. (#3719)
8fd78b7 is described below

commit 8fd78b788cdfe5f92bc898a8f99611a05704b3d5
Author: Chetan Mehrotra <chet...@apache.org>
AuthorDate: Fri Jun 1 18:07:17 2018 +0530

    Fix attachment name compatibility issue. (#3719)
---
 .../whisk/core/database/CouchDbRestStore.scala     |  29 +++--
 .../main/scala/whisk/core/entity/WhiskAction.scala |   4 +-
 .../test/AttachmentCompatibilityTests.scala        | 134 +++++++++++++++++++++
 3 files changed, 156 insertions(+), 11 deletions(-)

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 ffa2aaf..8c401bd 100644
--- a/common/scala/src/main/scala/whisk/core/database/CouchDbRestStore.scala
+++ b/common/scala/src/main/scala/whisk/core/database/CouchDbRestStore.scala
@@ -17,9 +17,6 @@
 
 package whisk.core.database
 
-import scala.concurrent.Await
-import scala.concurrent.Future
-import scala.concurrent.duration._
 import akka.actor.ActorSystem
 import akka.event.Logging.ErrorLevel
 import akka.http.scaladsl.model._
@@ -28,14 +25,15 @@ import akka.stream.scaladsl._
 import akka.util.ByteString
 import spray.json._
 import whisk.common.{Logging, LoggingMarkers, MetricEmitter, TransactionId}
-import whisk.core.entity.Attachments.Attached
 import whisk.core.database.StoreUtils._
-import whisk.core.entity.BulkEntityResult
-import whisk.core.entity.DocInfo
-import whisk.core.entity.DocumentReader
-import whisk.core.entity.UUID
+import whisk.core.entity.Attachments.Attached
+import whisk.core.entity.{BulkEntityResult, DocInfo, DocumentReader, UUID}
 import whisk.http.Messages
 
+import scala.concurrent.{Await, Future}
+import scala.concurrent.duration._
+import scala.util.Try
+
 /**
  * Basic client to put and delete artifacts in a data store.
  *
@@ -474,12 +472,13 @@ class CouchDbRestStore[DocumentAbstraction <: 
DocumentSerializer](dbProtocol: St
           val (name, value) = fields.head
           value.asJsObject.getFields("content_type", "digest", "length") match 
{
             case Seq(JsString(contentTypeValue), JsString(digest), 
JsNumber(length)) =>
-              val attachmentName = Uri.from(scheme = attachmentScheme, path = 
name).toString()
               val contentType = ContentType.parse(contentTypeValue) match {
                 case Right(ct) => ct
                 case Left(_)   => ContentTypes.NoContentType //Should not 
happen
               }
-              attachmentHandler(doc, Attached(attachmentName, contentType, 
Some(length.intValue()), Some(digest)))
+              attachmentHandler(
+                doc,
+                Attached(getAttachmentName(name), contentType, 
Some(length.intValue()), Some(digest)))
             case x =>
               throw DeserializationException("Attachment json does not have 
required fields" + x)
 
@@ -489,6 +488,16 @@ class CouchDbRestStore[DocumentAbstraction <: 
DocumentSerializer](dbProtocol: St
       .getOrElse(doc)
   }
 
+  /**
+   * Determines if the attachment scheme confirms to new UUID based scheme or 
not
+   * and generates the name based on that
+   */
+  private def getAttachmentName(name: String): String = {
+    Try(java.util.UUID.fromString(name))
+      .map(_ => Uri.from(scheme = attachmentScheme, path = name).toString)
+      .getOrElse(name)
+  }
+
   private def reportFailure[T, U](f: Future[T], onFailure: Throwable => U): 
Future[T] = {
     f.onFailure({
       case _: ArtifactStoreException => // These failures are intentional and 
shouldn't trigger the catcher.
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 2ecbd3e..a0011ff 100644
--- a/common/scala/src/main/scala/whisk/core/entity/WhiskAction.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/WhiskAction.scala
@@ -391,7 +391,9 @@ object WhiskAction extends DocumentFactory[WhiskAction] 
with WhiskEntityQueries[
   def attachmentHandler(action: WhiskAction, attached: Attached): WhiskAction 
= {
     val eu = action.exec match {
       case exec @ CodeExecAsAttachment(_, Attached(attachmentName, _, _, _), 
_) =>
-        require(attachmentName == attached.attachmentName)
+        require(
+          attachmentName == attached.attachmentName,
+          s"Attachment name '${attached.attachmentName}' does not match the 
expected name '$attachmentName'")
         exec.attach(attached)
       case exec => exec
     }
diff --git 
a/tests/src/test/scala/whisk/core/database/test/AttachmentCompatibilityTests.scala
 
b/tests/src/test/scala/whisk/core/database/test/AttachmentCompatibilityTests.scala
new file mode 100644
index 0000000..6550cc0
--- /dev/null
+++ 
b/tests/src/test/scala/whisk/core/database/test/AttachmentCompatibilityTests.scala
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package whisk.core.database.test
+
+import java.io.ByteArrayInputStream
+import java.util.Base64
+
+import akka.http.scaladsl.model.{ContentType, StatusCodes}
+import akka.stream.ActorMaterializer
+import akka.stream.scaladsl.{Source, StreamConverters}
+import akka.util.ByteString
+import common.{StreamLogging, WskActorSystem}
+import org.junit.runner.RunWith
+import org.scalatest.concurrent.ScalaFutures
+import org.scalatest.junit.JUnitRunner
+import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, FlatSpec, 
Matchers}
+import pureconfig.loadConfigOrThrow
+import spray.json.DefaultJsonProtocol
+import whisk.common.TransactionId
+import whisk.core.ConfigKeys
+import whisk.core.database.{CouchDbConfig, CouchDbRestClient, 
NoDocumentException}
+import whisk.core.entity.Attachments.Inline
+import whisk.core.entity.test.ExecHelpers
+import whisk.core.entity.{
+  CodeExecAsAttachment,
+  DocInfo,
+  EntityName,
+  EntityPath,
+  WhiskAction,
+  WhiskEntity,
+  WhiskEntityStore
+}
+
+import scala.concurrent.Future
+
+@RunWith(classOf[JUnitRunner])
+class AttachmentCompatibilityTests
+    extends FlatSpec
+    with Matchers
+    with ScalaFutures
+    with BeforeAndAfterEach
+    with BeforeAndAfterAll
+    with WskActorSystem
+    with ExecHelpers
+    with DbUtils
+    with DefaultJsonProtocol
+    with StreamLogging {
+
+  //Bring in sync the timeout used by ScalaFutures and DBUtils
+  implicit override val patienceConfig: PatienceConfig = 
PatienceConfig(timeout = dbOpTimeout)
+  implicit val materializer = ActorMaterializer()
+  val config = loadConfigOrThrow[CouchDbConfig](ConfigKeys.couchdb)
+  val entityStore = WhiskEntityStore.datastore()
+  val client =
+    new CouchDbRestClient(
+      config.protocol,
+      config.host,
+      config.port,
+      config.username,
+      config.password,
+      config.databaseFor[WhiskEntity])
+
+  override def afterEach(): Unit = {
+    cleanup()
+  }
+
+  override protected def withFixture(test: NoArgTest) = {
+    assume(isCouchStore(entityStore))
+    super.withFixture(test)
+  }
+
+  behavior of "Attachments"
+
+  it should "read attachments created using old scheme" in {
+    implicit val tid: TransactionId = transid()
+    val namespace = EntityPath("attachment-compat-test1")
+    val exec = javaDefault("ZHViZWU=", Some("hello"))
+    val doc =
+      WhiskAction(namespace, EntityName("attachment_unique"), exec)
+
+    doc.exec match {
+      case exec @ CodeExecAsAttachment(_, Inline(code), _) =>
+        val attached = exec.manifest.attached.get
+
+        val newDoc = doc.copy(exec = exec.copy(code = attached))
+        newDoc.revision(doc.rev)
+
+        val codeBytes = Base64.getDecoder().decode(code)
+        val stream = new ByteArrayInputStream(codeBytes)
+        val src = StreamConverters.fromInputStream(() => stream)
+        val info = entityStore.put(newDoc).futureValue
+        val info2 = attach(info, attached.attachmentName, 
attached.attachmentType, src).futureValue
+        docsToDelete += ((entityStore, info2))
+      case _ =>
+        fail("Exec must be code attachment")
+    }
+
+    val doc2 = WhiskAction.get(entityStore, doc.docid).futureValue
+    doc2.exec shouldBe exec
+  }
+
+  private def attach(doc: DocInfo,
+                     name: String,
+                     contentType: ContentType,
+                     docStream: Source[ByteString, _]): Future[DocInfo] = {
+    client.putAttachment(doc.id.id, doc.rev.rev, name, contentType, 
docStream).map {
+      case Right(response) =>
+        val id = response.fields("id").convertTo[String]
+        val rev = response.fields("rev").convertTo[String]
+        DocInfo ! (id, rev)
+
+      case Left(StatusCodes.NotFound) =>
+        throw NoDocumentException("Not found on 'readAttachment'.")
+
+      case Left(code) =>
+        throw new Exception("Unexpected http response code: " + code)
+    }
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
markusthoem...@apache.org.

Reply via email to