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/17 04:43:09 UTC
[GitHub] style95 closed pull request #3671: Activation id in header
style95 closed pull request #3671: Activation id in header
URL: https://github.com/apache/incubator-openwhisk/pull/3671
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/core/controller/src/main/scala/whisk/core/controller/Actions.scala b/core/controller/src/main/scala/whisk/core/controller/Actions.scala
index 12c8ec6ff5..bd92c581eb 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Actions.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Actions.scala
@@ -256,24 +256,28 @@ trait WhiskActionsApi extends WhiskCollectionAPI with PostActionActivation with
onComplete(invokeAction(user, actionWithMergedParams, payload, waitForResponse, cause = None)) {
case Success(Left(activationId)) =>
// non-blocking invoke or blocking invoke which got queued instead
- complete(Accepted, activationId.toJsObject)
+ respondWithActivationIdHeader(activationId) {
+ complete(Accepted, activationId.toJsObject)
+ }
case Success(Right(activation)) =>
val response = if (result) activation.resultAsJson else activation.toExtendedJson
- if (activation.response.isSuccess) {
- complete(OK, response)
- } else if (activation.response.isApplicationError) {
- // actions that result is ApplicationError status are considered a 'success'
- // and will have an 'error' property in the result - the HTTP status is OK
- // and clients must check the response status if it exists
- // NOTE: response status will not exist in the JSON object if ?result == true
- // and instead clients must check if 'error' is in the JSON
- // PRESERVING OLD BEHAVIOR and will address defect in separate change
- complete(BadGateway, response)
- } else if (activation.response.isContainerError) {
- complete(BadGateway, response)
- } else {
- complete(InternalServerError, response)
+ respondWithActivationIdHeader(activation.activationId) {
+ if (activation.response.isSuccess) {
+ complete(OK, response)
+ } else if (activation.response.isApplicationError) {
+ // actions that result is ApplicationError status are considered a 'success'
+ // and will have an 'error' property in the result - the HTTP status is OK
+ // and clients must check the response status if it exists
+ // NOTE: response status will not exist in the JSON object if ?result == true
+ // and instead clients must check if 'error' is in the JSON
+ // PRESERVING OLD BEHAVIOR and will address defect in separate change
+ complete(BadGateway, response)
+ } else if (activation.response.isContainerError) {
+ complete(BadGateway, response)
+ } else {
+ complete(InternalServerError, response)
+ }
}
case Failure(t: RecordTooLargeException) =>
logging.debug(this, s"[POST] action payload was too large")
diff --git a/core/controller/src/main/scala/whisk/core/controller/Entities.scala b/core/controller/src/main/scala/whisk/core/controller/Entities.scala
index cd9a72a08d..b1f34b3dc0 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Entities.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Entities.scala
@@ -20,9 +20,9 @@ package whisk.core.controller
import scala.concurrent.Future
import scala.language.postfixOps
import scala.util.Try
-
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.model.StatusCodes.RequestEntityTooLarge
+import akka.http.scaladsl.model.headers.RawHeader
import akka.http.scaladsl.server.Directive0
import akka.http.scaladsl.server.Directives
import akka.http.scaladsl.server.RequestContext
@@ -65,6 +65,15 @@ protected[controller] trait ValidateRequestSize extends Directives {
protected val fieldDescriptionForSizeError = "Request"
}
+protected trait CustomHeaders extends Directives {
+ val ActivationIdHeader = "x-openwhisk-activation-id"
+
+ /** Add activation ID in headers */
+ protected def respondWithActivationIdHeader(activationId: ActivationId): Directive0 = {
+ respondWithHeader(RawHeader(ActivationIdHeader, activationId.asString))
+ }
+}
+
/** A trait implementing the basic operations on WhiskEntities in support of the various APIs. */
trait WhiskCollectionAPI
extends Directives
@@ -72,8 +81,8 @@ trait WhiskCollectionAPI
with AuthorizedRouteProvider
with ValidateRequestSize
with ReadOps
- with WriteOps {
-
+ with WriteOps
+ with CustomHeaders {
/** The core collections require backend services to be injected in this trait. */
services: WhiskServices =>
diff --git a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
index d293aba05e..a800ea3676 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
@@ -165,7 +165,9 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
.map { activation =>
activationStore.store(activation)
}
- complete(Accepted, triggerActivationId.toJsObject)
+ respondWithActivationIdHeader(triggerActivationId) {
+ complete(Accepted, triggerActivationId.toJsObject)
+ }
} else {
logging
.debug(
diff --git a/core/controller/src/main/scala/whisk/core/controller/WebActions.scala b/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
index 6015525511..4693350c6d 100644
--- a/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
@@ -348,7 +348,7 @@ protected[core] object WhiskWebActionsApi extends Directives {
headers.filter(_.lowercaseName != `Content-Type`.lowercaseName)
}
-trait WhiskWebActionsApi extends Directives with ValidateRequestSize with PostActionActivation {
+trait WhiskWebActionsApi extends Directives with ValidateRequestSize with PostActionActivation with CustomHeaders {
services: WhiskServices =>
/** API path invocation path for posting activations directly through the host. */
@@ -638,37 +638,41 @@ trait WhiskWebActionsApi extends Directives with ValidateRequestSize with PostAc
responseType: MediaExtension)(implicit transid: TransactionId) = {
onComplete(queuedActivation) {
case Success(Right(activation)) =>
- val result = activation.resultAsJson
+ respondWithActivationIdHeader(activation.activationId) {
+ val result = activation.resultAsJson
+
+ if (activation.response.isSuccess || activation.response.isApplicationError) {
+ val resultPath = if (activation.response.isSuccess) {
+ projectResultField
+ } else {
+ // the activation produced an error response: therefore ignore
+ // the requested projection and unwrap the error instead
+ // and attempt to handle it per the desired response type (extension)
+ List(ActivationResponse.ERROR_FIELD)
+ }
- if (activation.response.isSuccess || activation.response.isApplicationError) {
- val resultPath = if (activation.response.isSuccess) {
- projectResultField
+ val result = getFieldPath(activation.resultAsJson, resultPath)
+ result match {
+ case Some(projection) =>
+ val marshaler = Future(responseType.transcoder(projection, transid, webApiDirectives))
+ onComplete(marshaler) {
+ case Success(done) => done // all transcoders terminate the connection
+ case Failure(t) => terminate(InternalServerError)
+ }
+ case _ => terminate(NotFound, Messages.propertyNotFound)
+ }
} else {
- // the activation produced an error response: therefore ignore
- // the requested projection and unwrap the error instead
- // and attempt to handle it per the desired response type (extension)
- List(ActivationResponse.ERROR_FIELD)
- }
-
- val result = getFieldPath(activation.resultAsJson, resultPath)
- result match {
- case Some(projection) =>
- val marshaler = Future(responseType.transcoder(projection, transid, webApiDirectives))
- onComplete(marshaler) {
- case Success(done) => done // all transcoders terminate the connection
- case Failure(t) => terminate(InternalServerError)
- }
- case _ => terminate(NotFound, Messages.propertyNotFound)
+ terminate(BadRequest, Messages.errorProcessingRequest)
}
- } else {
- terminate(BadRequest, Messages.errorProcessingRequest)
}
case Success(Left(activationId)) =>
// blocking invoke which got queued instead
// this should not happen, instead it should be a blocking invoke timeout
logging.debug(this, "activation waiting period expired")
- terminate(Accepted, Messages.responseNotReady)
+ respondWithActivationIdHeader(activationId) {
+ terminate(Accepted, Messages.responseNotReady)
+ }
case Failure(t: RejectRequest) => terminate(t.code, t.message)
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 9b54b0a7bb..0490d49b7d 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
@@ -38,6 +38,7 @@ import whisk.http.Messages
import java.io.ByteArrayInputStream
import java.util.Base64
+import akka.http.scaladsl.model.headers.RawHeader
import akka.stream.scaladsl._
/**
@@ -1132,6 +1133,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
status should be(Accepted)
val response = responseAs[JsObject]
response.fields("activationId") should not be None
+ headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))
}
// it should "ignore &result when invoking nonblocking action"
@@ -1139,6 +1141,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
status should be(Accepted)
val response = responseAs[JsObject]
response.fields("activationId") should not be None
+ headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))
}
}
@@ -1150,6 +1153,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
status should be(Accepted)
val response = responseAs[JsObject]
response.fields("activationId") should not be None
+ headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))
}
}
@@ -1184,6 +1188,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
status should be(Accepted)
val response = responseAs[JsObject]
response.fields("activationId") should not be None
+ headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))
}
}
@@ -1215,6 +1220,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
status should be(OK)
val response = responseAs[JsObject]
response should be(activation.resultAsJson)
+ headers should contain(RawHeader(ActivationIdHeader, activation.activationId.asString))
}
} finally {
deleteActivation(ActivationId(activation.docid.asString))
@@ -1284,7 +1290,10 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
// will not wait long enough should get accepted status
Post(s"$collectionPath/${action.name}?blocking=true&timeout=100") ~> Route.seal(routes(creds)) ~> check {
+ val response = responseAs[JsObject]
status shouldBe Accepted
+ headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))
+
}
// repeat this time wait longer than active ack delay
@@ -1292,6 +1301,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
status shouldBe OK
val response = responseAs[JsObject]
response shouldBe activation.withoutLogs.toExtendedJson
+ headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))
}
} finally {
loadBalancer.whiskActivationStub = None
@@ -1319,6 +1329,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
status should be(InternalServerError)
val response = responseAs[JsObject]
response should be(activation.withoutLogs.toExtendedJson)
+ headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))
}
} finally {
deleteActivation(ActivationId(activation.docid.asString))
@@ -1333,6 +1344,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
status should be(Accepted)
val response = responseAs[JsObject]
response.fields("activationId") should not be None
+ headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))
}
stream.toString should include(s"[WhiskActionMetaData] [GET] serving from datastore: ${CacheKey(action)}")
stream.reset()
diff --git a/tests/src/test/scala/whisk/core/controller/test/ControllerTestCommon.scala b/tests/src/test/scala/whisk/core/controller/test/ControllerTestCommon.scala
index b09d1b1fe0..391da14298 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ControllerTestCommon.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ControllerTestCommon.scala
@@ -33,8 +33,7 @@ import whisk.common.TransactionId
import whisk.core.WhiskConfig
import whisk.core.connector.ActivationMessage
import whisk.core.containerpool.logging.LogStoreProvider
-import whisk.core.controller.RestApiCommons
-import whisk.core.controller.WhiskServices
+import whisk.core.controller.{CustomHeaders, RestApiCommons, WhiskServices}
import whisk.core.database.{ActivationStoreProvider, CacheChangeNotification, DocumentFactory}
import whisk.core.database.test.DbUtils
import whisk.core.entitlement._
@@ -52,7 +51,8 @@ protected trait ControllerTestCommon
with DbUtils
with ExecHelpers
with WhiskServices
- with StreamLogging {
+ with StreamLogging
+ with CustomHeaders {
val activeAckTopicIndex = ControllerInstanceId("0")
diff --git a/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
index cfdda7ebea..b0551607e7 100644
--- a/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
@@ -21,18 +21,15 @@ import java.time.Instant
import scala.concurrent.duration.DurationInt
import scala.language.postfixOps
-
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 akka.http.scaladsl.model.StatusCodes._
-
+import akka.http.scaladsl.model.headers.RawHeader
import spray.json._
import spray.json.DefaultJsonProtocol._
-
import whisk.core.controller.WhiskTriggersApi
import whisk.core.entitlement.Collection
import whisk.core.entity._
@@ -368,6 +365,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
val JsString(id) = response.fields("activationId")
val activationId = ActivationId.parse(id).get
response.fields("activationId") should not be None
+ headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))
val activationDoc = DocId(WhiskEntity.qualifiedName(namespace, activationId))
whisk.utils.retry({
@@ -398,6 +396,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
println(s"trying to delete async activation doc: '${activationDoc}'")
deleteActivation(ActivationId(activationDoc.asString))
response.fields("activationId") should not be None
+ headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))
}, 30, Some(1.second))
}
}
diff --git a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
index d1ddb8b6d8..00049310a2 100644
--- a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
@@ -793,7 +793,8 @@ trait WebActionsApiBaseTests extends ControllerTestCommon with BeforeAndAfterEac
status should be(NoContent)
response.entity shouldBe HttpEntity.Empty
withClue(headers) {
- headers.length shouldBe 0
+ headers.length shouldBe 1
+ headers.exists(_.is(ActivationIdHeader)) should be(true)
}
}
@@ -922,7 +923,8 @@ trait WebActionsApiBaseTests extends ControllerTestCommon with BeforeAndAfterEac
m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
status should be(statusCode)
- headers shouldBe empty
+ headers.size shouldBe 1
+ headers.exists(_.is(ActivationIdHeader)) should be(true)
response.entity shouldBe HttpEntity.Empty
}
}
@@ -939,7 +941,8 @@ trait WebActionsApiBaseTests extends ControllerTestCommon with BeforeAndAfterEac
m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
status should be(statusCode)
- headers shouldBe List(RawHeader("Set-Cookie", "a=b"))
+ headers should contain(RawHeader("Set-Cookie", "a=b"))
+ headers.exists(_.is(ActivationIdHeader)) should be(true)
response.entity shouldBe HttpEntity.Empty
}
}
@@ -959,7 +962,8 @@ trait WebActionsApiBaseTests extends ControllerTestCommon with BeforeAndAfterEac
m(s"$testRoutePath/$path") ~> addHeader("Accept", "application/json") ~> Route.seal(routes(creds)) ~> check {
status should be(statusCode)
- headers shouldBe empty
+ headers.size shouldBe 1
+ headers.exists(_.is(ActivationIdHeader)) should be(true)
response.entity shouldBe HttpEntity.Empty
}
}
@@ -976,7 +980,8 @@ trait WebActionsApiBaseTests extends ControllerTestCommon with BeforeAndAfterEac
m(s"$testRoutePath/$path") ~> addHeader("Accept", "application/json") ~> Route.seal(routes(creds)) ~> check {
status should be(statusCode)
- headers shouldBe List(RawHeader("Set-Cookie", "a=b"))
+ headers should contain(RawHeader("Set-Cookie", "a=b"))
+ headers.exists(_.is(ActivationIdHeader)) should be(true)
response.entity shouldBe HttpEntity.Empty
}
}
@@ -1143,7 +1148,8 @@ trait WebActionsApiBaseTests extends ControllerTestCommon with BeforeAndAfterEac
m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
status should be(OK)
mediaType shouldBe MediaTypes.`application/json`
- headers shouldBe empty
+ headers.size shouldBe 1
+ headers.exists(_.is(ActivationIdHeader)) should be(true)
responseAs[String] shouldBe "hello world"
}
}
----------------------------------------------------------------
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