This is an automated email from the ASF dual-hosted git repository. rabbah 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 6d17625 Display proper error when sequence invocation fails due to missing component (#3799) 6d17625 is described below commit 6d17625dd9de8ae693fae26508de2b97437d7e97 Author: James Dubee <jwdu...@us.ibm.com> AuthorDate: Mon Jul 30 19:16:00 2018 -0400 Display proper error when sequence invocation fails due to missing component (#3799) --- .../core/controller/actions/SequenceActions.scala | 22 +++++++++---- .../core/controller/test/SequenceApiTests.scala | 38 ++++++++++++++++++---- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/core/controller/src/main/scala/whisk/core/controller/actions/SequenceActions.scala b/core/controller/src/main/scala/whisk/core/controller/actions/SequenceActions.scala index dd3e331..cd6c1ea 100644 --- a/core/controller/src/main/scala/whisk/core/controller/actions/SequenceActions.scala +++ b/core/controller/src/main/scala/whisk/core/controller/actions/SequenceActions.scala @@ -34,6 +34,7 @@ import whisk.common.Logging import whisk.common.TransactionId import whisk.core.controller.WhiskServices import whisk.core.database.ActivationStore +import whisk.core.database.NoDocumentException import whisk.core.entity._ import whisk.core.entity.size.SizeInt import whisk.core.entity.types._ @@ -259,14 +260,21 @@ protected[actions] trait SequenceActions { .foldLeft(initialAccounting) { (accountingFuture, futureAction) => accountingFuture.flatMap { accounting => if (accounting.atomicActionCnt < actionSequenceLimit) { - invokeNextAction(user, futureAction, accounting, cause).flatMap { accounting => - if (!accounting.shortcircuit) { - Future.successful(accounting) - } else { - // this is to short circuit the fold - Future.failed(FailedSequenceActivation(accounting)) // terminates the fold + invokeNextAction(user, futureAction, accounting, cause) + .flatMap { accounting => + if (!accounting.shortcircuit) { + Future.successful(accounting) + } else { + // this is to short circuit the fold + Future.failed(FailedSequenceActivation(accounting)) // terminates the fold + } + } + .recoverWith { + case _: NoDocumentException => + val updatedAccount = + accounting.fail(ActivationResponse.applicationError(sequenceComponentNotFound), None) + Future.failed(FailedSequenceActivation(updatedAccount)) // terminates the fold } - } } else { val updatedAccount = accounting.fail(ActivationResponse.applicationError(sequenceIsTooLong), None) Future.failed(FailedSequenceActivation(updatedAccount)) // terminates the fold diff --git a/tests/src/test/scala/whisk/core/controller/test/SequenceApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/SequenceApiTests.scala index 9784278..5bc1996 100644 --- a/tests/src/test/scala/whisk/core/controller/test/SequenceApiTests.scala +++ b/tests/src/test/scala/whisk/core/controller/test/SequenceApiTests.scala @@ -19,22 +19,19 @@ package whisk.core.controller.test import scala.concurrent.duration.DurationInt import scala.language.postfixOps - +import java.time.Instant import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner - import akka.http.scaladsl.model.StatusCodes._ import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._ import akka.http.scaladsl.server.Route - import spray.json._ import spray.json.DefaultJsonProtocol._ - import whisk.common.TransactionId import whisk.core.controller.WhiskActionsApi import whisk.core.entity._ -import whisk.http.ErrorResponse -import whisk.http.Messages +import whisk.http.{ErrorResponse, Messages} +import whisk.http.Messages.sequenceComponentNotFound /** * Tests Sequence API - stand-alone tests that require only the controller to be up @@ -53,6 +50,35 @@ class SequenceApiTests extends ControllerTestCommon with WhiskActionsApi { val allowedActionDuration = 120 seconds + it should "partially invoke a sequence with missing component and produce component missing error" in { + implicit val tid = transid() + val seqName = s"${aname()}_seq" + val compName1 = s"${aname()}_comp1" + val compName2 = s"${aname()}_comp2" + val comp1Activation = WhiskActivation( + namespace, + EntityName(compName1), + creds.subject, + activationIdFactory.make(), + start = Instant.now, + end = Instant.now) + + putSimpleSequenceInDB(seqName, namespace, Vector(compName1, compName2)) + deleteAction(DocId(s"$namespace/$compName2")) + loadBalancer.whiskActivationStub = Some((1.milliseconds, comp1Activation)) + + Post(s"$collectionPath/$seqName?blocking=true") ~> Route.seal(routes(creds)) ~> check { + deleteAction(DocId(s"$namespace/$seqName")) + deleteAction(DocId(s"$namespace/$compName1")) + status should be(BadGateway) + val response = responseAs[JsObject] + response.fields("response") shouldBe ActivationResponse.applicationError(sequenceComponentNotFound).toExtendedJson + val logs = response.fields("logs").convertTo[JsArray] + logs.elements.size shouldBe 1 + logs.elements.head shouldBe comp1Activation.activationId.toJson + } + } + it should "reject creation of sequence with more actions than allowed limit" in { implicit val tid = transid() val seqName = EntityName(s"${aname()}_toomanyactions")