You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by du...@apache.org on 2018/02/06 15:42:17 UTC
[incubator-openwhisk] branch master updated: reduce rule activation
records (#3187)
This is an automated email from the ASF dual-hosted git repository.
dubeejw 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 9ededdf reduce rule activation records (#3187)
9ededdf is described below
commit 9ededdf82210682726f8800b2c02e68e3234efdf
Author: Mark Deuser <md...@us.ibm.com>
AuthorDate: Tue Feb 6 10:42:12 2018 -0500
reduce rule activation records (#3187)
* reduce rule activation records
* include action invocation async response in trigger activation logs
* adjust rule tests to have active rules
* trigger activation log format is array of stringified JSON objects
* Add trigger tests to validate trigger activation logs
---
.../scala/whisk/core/controller/Triggers.scala | 241 +++++++++++++++------
.../test/scala/system/basic/WskBasicTests.scala | 165 ++++++++++++--
.../src/test/scala/system/basic/WskRuleTests.scala | 36 ++-
.../scala/whisk/core/cli/test/Swift311Tests.scala | 11 +
.../core/controller/test/TriggersApiTests.scala | 13 +-
5 files changed, 350 insertions(+), 116 deletions(-)
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 044d5a2..f50fdb1 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
@@ -19,17 +19,22 @@ package whisk.core.controller
import java.time.{Clock, Instant}
+import scala.collection.immutable.Map
+import scala.concurrent.Future
+import scala.util.{Failure, Success}
+
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.model.HttpMethods.POST
-import akka.http.scaladsl.model.StatusCodes._
-import akka.http.scaladsl.model.{HttpEntity, HttpRequest, MediaTypes, Uri}
+import akka.http.scaladsl.model.StatusCodes.{Accepted, BadRequest, InternalServerError, OK}
import akka.http.scaladsl.model.Uri.Path
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
+import akka.http.scaladsl.model._
import akka.http.scaladsl.server.{RequestContext, RouteResult}
import akka.http.scaladsl.unmarshalling.{Unmarshal, Unmarshaller}
import akka.stream.ActorMaterializer
+import spray.json.DefaultJsonProtocol._
import spray.json._
import whisk.common.TransactionId
import whisk.core.controller.RestApiCommons.ListLimit
@@ -37,8 +42,7 @@ import whisk.core.database.CacheChangeNotification
import whisk.core.entitlement.Collection
import whisk.core.entity._
import whisk.core.entity.types.{ActivationStore, EntityStore}
-
-import scala.concurrent.Future
+import whisk.http.ErrorResponse
/** A trait implementing the triggers API. */
trait WhiskTriggersApi extends WhiskCollectionAPI {
@@ -59,10 +63,9 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
protected val activationStore: ActivationStore
/** JSON response formatter. */
- import RestApiCommons.jsonDefaultResponsePrinter
-
/** Path to Triggers REST API. */
protected val triggersPath = "triggers"
+ protected val url = Uri(s"http://localhost:${whiskConfig.servicePort}")
protected implicit val materializer: ActorMaterializer
@@ -107,10 +110,8 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
entity(as[Option[JsObject]]) { payload =>
getEntity(WhiskTrigger, entityStore, entityName.toDocId, Some {
trigger: WhiskTrigger =>
- val args = trigger.parameters.merge(payload)
val triggerActivationId = activationIdFactory.make()
logging.info(this, s"[POST] trigger activation id: ${triggerActivationId}")
-
val triggerActivation = WhiskActivation(
namespace = user.namespace.toPath, // all activations should end up in the one space regardless trigger.namespace,
entityName.name,
@@ -122,70 +123,42 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
version = trigger.version,
duration = None)
- logging.debug(this, s"[POST] trigger activated, writing activation record to datastore: $triggerActivationId")
- WhiskActivation.put(activationStore, triggerActivation) recover {
- case t =>
- logging.error(this, s"[POST] storing trigger activation $triggerActivationId failed: ${t.getMessage}")
- }
-
- val url = Uri(s"http://localhost:${whiskConfig.servicePort}")
-
- trigger.rules.map {
- _.filter {
- case (ruleName, rule) => rule.status == Status.ACTIVE
- } foreach {
- case (ruleName, rule) =>
- val ruleActivationId = activationIdFactory.make()
- val ruleActivation = WhiskActivation(
- namespace = user.namespace.toPath, // all activations should end up in the one space regardless trigger.namespace,
- ruleName.name,
- user.subject,
- ruleActivationId,
- Instant.now(Clock.systemUTC()),
- Instant.EPOCH,
- cause = Some(triggerActivationId),
- response = ActivationResponse.success(),
- version = trigger.version,
- duration = None)
- WhiskActivation.put(activationStore, ruleActivation) recover {
- case t =>
- logging.error(this, s"[POST] storing rule activation $ruleActivationId failed: ${t.getMessage}")
- }
-
- val actionNamespace = rule.action.path.root.asString
- val actionPath = {
- rule.action.path.relativePath.map { pkg =>
- (Path.SingleSlash + pkg.namespace) / rule.action.name.asString
- } getOrElse {
- Path.SingleSlash + rule.action.name.asString
+ // List of active rules associated with the trigger
+ val activeRules: Map[FullyQualifiedEntityName, ReducedRule] =
+ trigger.rules.map(_.filter(_._2.status == Status.ACTIVE)).getOrElse(Map.empty)
+
+ if (activeRules.nonEmpty) {
+ val args: JsObject = trigger.parameters.merge(payload).getOrElse(JsObject())
+ val actionLogList: Iterable[Future[JsObject]] = activateRules(user, args, activeRules)
+
+ // For each of the action activation results, generate a log message to attach to the trigger activation
+ Future
+ .sequence(actionLogList)
+ .map(_.map(_.compactPrint))
+ .onComplete {
+ case Success(triggerLogs) =>
+ val triggerActivationDoc = triggerActivation.withLogs(ActivationLogs(triggerLogs.toVector))
+ logging
+ .debug(
+ this,
+ s"[POST] trigger activated, writing activation record to datastore: $triggerActivationId")
+ WhiskActivation.put(activationStore, triggerActivationDoc) recover {
+ case t =>
+ logging
+ .error(this, s"[POST] storing trigger activation $triggerActivationId failed: ${t.getMessage}")
}
- }.toString
-
- val actionUrl = Path("/api/v1") / "namespaces" / actionNamespace / "actions"
- val request = HttpRequest(
- method = POST,
- uri = url.withPath(actionUrl + actionPath),
- headers =
- List(Authorization(BasicHttpCredentials(user.authkey.uuid.asString, user.authkey.key.asString))),
- entity = HttpEntity(MediaTypes.`application/json`, args.getOrElse(JsObject()).compactPrint))
-
- Http().singleRequest(request).map {
- response =>
- response.status match {
- case OK | Accepted =>
- Unmarshal(response.entity).to[JsObject].map { a =>
- logging.info(this, s"${rule.action} activated ${a.fields("activationId")}")
- }
- case NotFound =>
- response.discardEntityBytes()
- logging.debug(this, s"${rule.action} failed, action not found")
- case _ =>
- Unmarshal(response.entity).to[String].map { error =>
- logging.warn(this, s"${rule.action} failed due to $error")
- }
- }
- }
- }
+ case Failure(e) =>
+ logging.error(this, s"Failed to write action activation results to trigger activation: $e")
+ logging
+ .info(
+ this,
+ s"[POST] trigger activated, writing activation record to datastore: $triggerActivationId")
+ WhiskActivation.put(activationStore, triggerActivation) recover {
+ case t =>
+ logging
+ .error(this, s"[POST] storing trigger activation $triggerActivationId failed: ${t.getMessage}")
+ }
+ }
}
complete(Accepted, triggerActivationId.toJsObject)
@@ -326,6 +299,132 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
complete(OK, trigger.withoutRules)
}
+ /**
+ * Iterates through each active rule and invoke each mapped action.
+ */
+ private def activateRules(user: Identity,
+ args: JsObject,
+ rulesToActivate: Map[FullyQualifiedEntityName, ReducedRule])(
+ implicit transid: TransactionId): Iterable[Future[JsObject]] = {
+ rulesToActivate.map {
+ case (ruleName, rule) =>
+ // Invoke the action. Retain action results for inclusion in the trigger activation record
+ val actionActivationResult: Future[JsObject] = postActivation(user, rule, args)
+ .flatMap { response =>
+ response.status match {
+ case OK | Accepted =>
+ Unmarshal(response.entity).to[JsObject].map { activationResponse =>
+ val activationId: JsValue = activationResponse.fields("activationId")
+ logging.debug(this, s"trigger-fired action '${rule.action}' invoked with activation $activationId")
+ ruleResult(ActivationResponse.Success, ruleName, rule.action, Some(activationId))
+ }
+
+ // all proper controller responses are JSON objects that deserialize to an ErrorResponse instance
+ case code if (response.entity.contentType == ContentTypes.`application/json`) =>
+ Unmarshal(response.entity).to[ErrorResponse].map { e =>
+ val statusCode =
+ if (code != InternalServerError) {
+ logging
+ .debug(
+ this,
+ s"trigger-fired action '${rule.action}' failed to invoke with ${e.error}, ${e.code}")
+ ActivationResponse.ApplicationError
+ } else {
+ logging
+ .error(
+ this,
+ s"trigger-fired action '${rule.action}' failed to invoke with ${e.error}, ${e.code}")
+ ActivationResponse.WhiskError
+ }
+ ruleResult(statusCode, ruleName, rule.action, errorMsg = Some(e.error))
+ }
+
+ case code =>
+ logging.error(this, s"trigger-fired action '${rule.action}' failed to invoke with status code $code")
+ Unmarshal(response.entity).to[String].map { error =>
+ ruleResult(ActivationResponse.WhiskError, ruleName, rule.action, errorMsg = Some(error))
+ }
+ }
+ }
+ .recover {
+ case t =>
+ logging.error(this, s"trigger-fired action '${rule.action}' failed to invoke with $t")
+ ruleResult(
+ ActivationResponse.WhiskError,
+ ruleName,
+ rule.action,
+ errorMsg = Some(InternalServerError.defaultMessage))
+ }
+
+ actionActivationResult
+ }
+ }
+
+ /**
+ * Posts an action activation. Currently done by posting internally to the controller.
+ * TODO: use a poper path that does not route through HTTP.
+ *
+ * @param rule the name of the rule that is activated
+ * @param args the arguments to post to the action
+ * @return a future with the HTTP response from the action activation
+ */
+ private def postActivation(user: Identity, rule: ReducedRule, args: JsObject): Future[HttpResponse] = {
+ // Build the url to invoke an action mapped to the rule
+ val actionUrl = baseControllerPath / rule.action.path.root.asString / "actions"
+
+ val actionPath = {
+ rule.action.path.relativePath.map { pkg =>
+ (Path.SingleSlash + pkg.namespace) / rule.action.name.asString
+ } getOrElse {
+ Path.SingleSlash + rule.action.name.asString
+ }
+ }.toString
+
+ val request = HttpRequest(
+ method = POST,
+ uri = url.withPath(actionUrl + actionPath),
+ headers = List(Authorization(BasicHttpCredentials(user.authkey.uuid.asString, user.authkey.key.asString))),
+ entity = HttpEntity(MediaTypes.`application/json`, args.compactPrint))
+
+ Http().singleRequest(request)
+ }
+
+ /**
+ * Create JSON object containing the pertinent rule activation details.
+ * {
+ * "rule": "my-rule",
+ * "action": "my-action",
+ * "statusCode": 0,
+ * "status": "success",
+ * "activationId": "...", // either this field, ...
+ * "error": "The requested resource does not exist." // ... or this field will be present
+ * }
+ *
+ * @param statusCode one of ActivationResponse values
+ * @param ruleName the name of the rule that was activated
+ * @param actionName the name of the action activated by the rule
+ * @param actionActivationId the activation id, if there is one
+ * @param errorMsg the rror messages otherwise
+ * @return JsObject as formatted above
+ */
+ private def ruleResult(statusCode: Int,
+ ruleName: FullyQualifiedEntityName,
+ actionName: FullyQualifiedEntityName,
+ actionActivationId: Option[JsValue] = None,
+ errorMsg: Option[String] = None): JsObject = {
+ JsObject(
+ Map(
+ "rule" -> JsString(ruleName.asString),
+ "action" -> JsString(actionName.asString),
+ "statusCode" -> JsNumber(statusCode),
+ "success" -> JsBoolean(statusCode == ActivationResponse.Success)) ++
+ actionActivationId.map("activationId" -> _.toJson) ++
+ errorMsg.map("error" -> JsString(_)))
+ }
+
+ /** Common base bath for the controller, used by internal action activation mechanism. */
+ private val baseControllerPath = Path("/api/v1/namespaces")
+
/** Custom unmarshaller for query parameters "limit" for "list" operations. */
private implicit val stringToListLimit: Unmarshaller[String, ListLimit] = RestApiCommons.stringToListLimit(collection)
}
diff --git a/tests/src/test/scala/system/basic/WskBasicTests.scala b/tests/src/test/scala/system/basic/WskBasicTests.scala
index 1696fee..8e68d29 100644
--- a/tests/src/test/scala/system/basic/WskBasicTests.scala
+++ b/tests/src/test/scala/system/basic/WskBasicTests.scala
@@ -47,6 +47,11 @@ class WskBasicTests extends TestHelpers with WskTestHelpers {
val wsk: common.rest.WskRest = new WskRest
val defaultAction: Some[String] = Some(TestUtils.getTestActionFilename("hello.js"))
+ /**
+ * Append the current timestamp in ms
+ */
+ def withTimestamp(text: String) = s"${text}-${System.currentTimeMillis}"
+
behavior of "Wsk REST"
it should "reject creating duplicate entity" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
@@ -484,26 +489,53 @@ class WskBasicTests extends TestHelpers with WskTestHelpers {
behavior of "Wsk Trigger REST"
it should "create, update, get, fire and list trigger" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
- val name = "listTriggers"
+ val ruleName = withTimestamp("r1toa1")
+ val triggerName = withTimestamp("t1tor1")
+ val actionName = withTimestamp("a1")
val params = Map("a" -> "A".toJson)
- assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
- trigger.create(name, parameters = params)
- trigger.create(name, update = true)
+ val ns = wsk.namespace.whois()
+
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, _) =>
+ trigger.create(triggerName, parameters = params)
+ trigger.create(triggerName, update = true)
+ }
+
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
}
- val trigger = wsk.trigger.get(name)
+
+ val trigger = wsk.trigger.get(triggerName)
trigger.getFieldJsValue("parameters") shouldBe JsArray(JsObject("key" -> JsString("a"), "value" -> JsString("A")))
trigger.getFieldJsValue("publish") shouldBe JsBoolean(false)
trigger.getField("version") shouldBe "0.0.2"
val dynamicParams = Map("t" -> "T".toJson)
- val run = wsk.trigger.fire(name, dynamicParams)
+ val run = wsk.trigger.fire(triggerName, dynamicParams)
withActivation(wsk.activation, run) { activation =>
activation.response.result shouldBe Some(dynamicParams.toJson)
activation.duration shouldBe 0L // shouldn't exist but CLI generates it
activation.end shouldBe Instant.EPOCH // shouldn't exist but CLI generates it
+ activation.logs shouldBe defined
+ activation.logs.get.size shouldBe 1
+
+ val logEntry = activation.logs.get(0).parseJson.asJsObject
+ val logs = JsArray(logEntry)
+ val ruleActivationId: String = logEntry.getFields("activationId")(0).convertTo[String]
+ val expectedLogs = JsArray(
+ JsObject(
+ "statusCode" -> JsNumber(0),
+ "activationId" -> JsString(ruleActivationId),
+ "success" -> JsBoolean(true),
+ "rule" -> JsString(ns + "/" + ruleName),
+ "action" -> JsString(ns + "/" + actionName)))
+ logs shouldBe expectedLogs
}
- val runWithNoParams = wsk.trigger.fire(name, Map())
+ val runWithNoParams = wsk.trigger.fire(triggerName, Map())
withActivation(wsk.activation, runWithNoParams) { activation =>
activation.response.result shouldBe Some(JsObject())
activation.duration shouldBe 0L // shouldn't exist but CLI generates it
@@ -512,7 +544,7 @@ class WskBasicTests extends TestHelpers with WskTestHelpers {
val triggerList = wsk.trigger.list()
val triggers = triggerList.getBodyListJsObject()
- triggers.exists(trigger => RestResult.getField(trigger, "name") == name) shouldBe true
+ triggers.exists(trigger => RestResult.getField(trigger, "name") == triggerName) shouldBe true
}
it should "create, and get a trigger summary" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
@@ -554,17 +586,26 @@ class WskBasicTests extends TestHelpers with WskTestHelpers {
}
it should "create, and fire a trigger using a parameter file" in withAssetCleaner(wskprops) {
- val name = "paramFileTrigger"
- val file = Some(TestUtils.getTestActionFilename("argCheck.js"))
+ val ruleName = withTimestamp("r1toa1")
+ val triggerName = withTimestamp("paramFileTrigger")
+ val actionName = withTimestamp("a1")
val argInput = Some(TestUtils.getTestActionFilename("validInput2.json"))
(wp, assetHelper) =>
- assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
- trigger.create(name)
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, _) =>
+ trigger.create(triggerName)
+ }
+
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
}
val expectedOutput = JsObject("payload" -> JsString("test"))
- val run = wsk.trigger.fire(name, parameterFile = argInput)
+ val run = wsk.trigger.fire(triggerName, parameterFile = argInput)
withActivation(wsk.activation, run) { activation =>
activation.response.result shouldBe Some(expectedOutput)
}
@@ -596,12 +637,23 @@ class WskBasicTests extends TestHelpers with WskTestHelpers {
}
it should "create, and fire a trigger to ensure result is empty" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
- val name = "emptyResultTrigger"
- assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
- trigger.create(name)
+ val ruleName = withTimestamp("r1toa1")
+ val triggerName = withTimestamp("emptyResultTrigger")
+ val actionName = withTimestamp("a1")
+
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, _) =>
+ trigger.create(triggerName)
+ }
+
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
}
- val run = wsk.trigger.fire(name)
+ val run = wsk.trigger.fire(triggerName)
withActivation(wsk.activation, run) { activation =>
activation.response.result shouldBe Some(JsObject())
}
@@ -637,6 +689,65 @@ class WskBasicTests extends TestHelpers with WskTestHelpers {
stderr should include regex ("""The requested resource does not exist.""")
}
+ it should "create and fire a trigger with a rule whose action has been deleted" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val ruleName1 = withTimestamp("r1toa1")
+ val ruleName2 = withTimestamp("r2toa2")
+ val triggerName = withTimestamp("t1tor1r2")
+ val actionName1 = withTimestamp("a1")
+ val actionName2 = withTimestamp("a2")
+ val ns = wsk.namespace.whois()
+
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, _) =>
+ trigger.create(triggerName)
+ trigger.create(triggerName, update = true)
+ }
+
+ assetHelper.withCleaner(wsk.action, actionName1) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ wsk.action.create(actionName2, defaultAction) // Delete this after the rule is created
+
+ assetHelper.withCleaner(wsk.rule, ruleName1) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName1)
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName2) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName2)
+ }
+ wsk.action.delete(actionName2)
+
+ val run = wsk.trigger.fire(triggerName)
+ withActivation(wsk.activation, run) { activation =>
+ activation.duration shouldBe 0L // shouldn't exist but CLI generates it
+ activation.end shouldBe Instant.EPOCH // shouldn't exist but CLI generates it
+ activation.logs shouldBe defined
+ activation.logs.get.size shouldBe 2
+
+ val logEntry1 = activation.logs.get(0).parseJson.asJsObject
+ val logEntry2 = activation.logs.get(1).parseJson.asJsObject
+ val logs = JsArray(logEntry1, logEntry2)
+ val ruleActivationId: String = if (logEntry1.getFields("activationId").size == 1) {
+ logEntry1.getFields("activationId")(0).convertTo[String]
+ } else {
+ logEntry2.getFields("activationId")(0).convertTo[String]
+ }
+ val expectedLogs = JsArray(
+ JsObject(
+ "statusCode" -> JsNumber(0),
+ "activationId" -> JsString(ruleActivationId),
+ "success" -> JsBoolean(true),
+ "rule" -> JsString(ns + "/" + ruleName1),
+ "action" -> JsString(ns + "/" + actionName1)),
+ JsObject(
+ "statusCode" -> JsNumber(1),
+ "success" -> JsBoolean(false),
+ "error" -> JsString("The requested resource does not exist."),
+ "rule" -> JsString(ns + "/" + ruleName2),
+ "action" -> JsString(ns + "/" + actionName2)))
+ logs shouldBe expectedLogs
+ }
+ }
+
behavior of "Wsk Rule REST"
it should "create rule, get rule, update rule and list rule" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
@@ -808,18 +919,28 @@ class WskBasicTests extends TestHelpers with WskTestHelpers {
it should "create a trigger, and fire a trigger to get its individual fields from an activation" in withAssetCleaner(
wskprops) { (wp, assetHelper) =>
- val name = "activationFields"
+ val ruleName = withTimestamp("r1toa1")
+ val triggerName = withTimestamp("activationFields")
+ val actionName = withTimestamp("a1")
- assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
- trigger.create(name)
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, _) =>
+ trigger.create(triggerName)
+ }
+
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
}
val ns = wsk.namespace.whois()
- val run = wsk.trigger.fire(name)
+ val run = wsk.trigger.fire(triggerName)
withActivation(wsk.activation, run) { activation =>
var result = wsk.activation.get(Some(activation.activationId))
result.getField("namespace") shouldBe ns
- result.getField("name") shouldBe name
+ result.getField("name") shouldBe triggerName
result.getField("version") shouldBe "0.0.1"
result.getFieldJsValue("publish") shouldBe JsBoolean(false)
result.getField("subject") shouldBe ns
diff --git a/tests/src/test/scala/system/basic/WskRuleTests.scala b/tests/src/test/scala/system/basic/WskRuleTests.scala
index 2baac6d..bcb9dea 100644
--- a/tests/src/test/scala/system/basic/WskRuleTests.scala
+++ b/tests/src/test/scala/system/basic/WskRuleTests.scala
@@ -100,13 +100,11 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
withActivation(wsk.activation, run) { triggerActivation =>
triggerActivation.cause shouldBe None
-
- withActivationsFromEntity(
- wsk.activation,
- ruleName,
- since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.cause shouldBe Some(triggerActivation.activationId)
- }
+ triggerActivation.logs.get.size shouldBe (1)
+ val logs = triggerActivation.logs.get.mkString(" ")
+ logs should include(""""statusCode":0""")
+ logs should include(""""activationId":""")
+ logs should include(""""success":true""")
withActivationsFromEntity(
wsk.activation,
@@ -137,13 +135,11 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
withActivation(wsk.activation, run) { triggerActivation =>
triggerActivation.cause shouldBe None
-
- withActivationsFromEntity(
- wsk.activation,
- ruleName,
- since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.cause shouldBe Some(triggerActivation.activationId)
- }
+ triggerActivation.logs.get.size shouldBe (1)
+ val logs = triggerActivation.logs.get.mkString(" ")
+ logs should include(""""statusCode":0""")
+ logs should include(""""activationId":""")
+ logs should include(""""success":true""")
withActivationsFromEntity(
wsk.activation,
@@ -177,13 +173,11 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
withActivation(wsk.activation, run) { triggerActivation =>
triggerActivation.cause shouldBe None
-
- withActivationsFromEntity(
- wsk.activation,
- ruleName,
- since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.cause shouldBe Some(triggerActivation.activationId)
- }
+ triggerActivation.logs.get.size shouldBe (1)
+ val logs = triggerActivation.logs.get.mkString(" ")
+ logs should include(""""statusCode":0""")
+ logs should include(""""activationId":""")
+ logs should include(""""success":true""")
withActivationsFromEntity(
wsk.activation,
diff --git a/tests/src/test/scala/whisk/core/cli/test/Swift311Tests.scala b/tests/src/test/scala/whisk/core/cli/test/Swift311Tests.scala
index a00b1ea..1dc19f0 100644
--- a/tests/src/test/scala/whisk/core/cli/test/Swift311Tests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/Swift311Tests.scala
@@ -37,6 +37,7 @@ class Swift311Tests extends TestHelpers with WskTestHelpers with Matchers {
val wsk = new WskRest
val expectedDuration = 45 seconds
val activationPollDuration = 60 seconds
+ val defaultJsAction = Some(TestUtils.getTestActionFilename("hello.js"))
lazy val runtimeContainer = "swift:3.1.1"
@@ -116,10 +117,20 @@ class Swift311Tests extends TestHelpers with WskTestHelpers with Matchers {
it should "allow Swift actions to trigger events" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
// create a trigger
val triggerName = s"TestTrigger ${System.currentTimeMillis()}"
+ val ruleName = s"TestTriggerRule ${System.currentTimeMillis()}"
+ val ruleActionName = s"TestTriggerAction ${System.currentTimeMillis()}"
assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, _) =>
trigger.create(triggerName)
}
+ assetHelper.withCleaner(wsk.action, ruleActionName) { (action, name) =>
+ action.create(name, defaultJsAction)
+ }
+
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = ruleActionName)
+ }
+
// create an action that fires the trigger
val file = TestUtils.getTestActionFilename("trigger.swift")
val actionName = "ActionThatTriggers"
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 200086f..3a8d3ce 100644
--- a/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
@@ -64,6 +64,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
val namespace = EntityPath(creds.subject.asString)
val collectionPath = s"/${EntityPath.DEFAULT}/${collection.path}"
def aname() = MakeName.next("triggers_tests")
+ def afullname(namespace: EntityPath, name: String) = FullyQualifiedEntityName(namespace, EntityName(name))
val parametersLimit = Parameters.sizeLimit
//// GET /triggers
@@ -319,9 +320,13 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
//// POST /triggers/name
it should "fire a trigger" in {
implicit val tid = transid()
- val trigger = WhiskTrigger(namespace, aname(), Parameters("x", "b"))
+ val rule = WhiskRule(namespace, aname(), afullname(namespace, aname().name), afullname(namespace, "bogus action"))
+ val trigger = WhiskTrigger(namespace, rule.trigger.name, rules = Some {
+ Map(rule.fullyQualifiedName(false) -> ReducedRule(rule.action, Status.ACTIVE))
+ })
val content = JsObject("xxx" -> "yyy".toJson)
put(entityStore, trigger)
+ put(entityStore, rule)
Post(s"$collectionPath/${trigger.name}", content) ~> Route.seal(routes(creds)) ~> check {
status should be(Accepted)
val response = responseAs[JsObject]
@@ -342,8 +347,12 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
it should "fire a trigger without args" in {
implicit val tid = transid()
- val trigger = WhiskTrigger(namespace, aname(), Parameters("x", "b"))
+ val rule = WhiskRule(namespace, aname(), afullname(namespace, aname().name), afullname(namespace, "bogus action"))
+ val trigger = WhiskTrigger(namespace, rule.trigger.name, Parameters("x", "b"), rules = Some {
+ Map(rule.fullyQualifiedName(false) -> ReducedRule(rule.action, Status.ACTIVE))
+ })
put(entityStore, trigger)
+ put(entityStore, rule)
Post(s"$collectionPath/${trigger.name}") ~> Route.seal(routes(creds)) ~> check {
val response = responseAs[JsObject]
val JsString(id) = response.fields("activationId")
--
To stop receiving notification emails like this one, please contact
dubeejw@apache.org.