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 <[email protected]>
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
[email protected].