You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by cs...@apache.org on 2017/06/30 19:00:18 UTC
[incubator-openwhisk] branch master updated: Normalize rate checks
to the identity namespace (uuid). (#2433)
This is an automated email from the ASF dual-hosted git repository.
csantanapr 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 544d065 Normalize rate checks to the identity namespace (uuid). (#2433)
544d065 is described below
commit 544d0657db2a3c3bac11e143f4fecf1b888e5079
Author: rodric rabbah <ro...@gmail.com>
AuthorDate: Fri Jun 30 15:00:16 2017 -0400
Normalize rate checks to the identity namespace (uuid). (#2433)
---
.../main/scala/whisk/core/entity/Identity.scala | 4 ++-
.../core/entitlement/ActivationThrottler.scala | 15 +++++-----
.../scala/whisk/core/entitlement/Entitlement.scala | 32 +++++++++++-----------
.../whisk/core/entitlement/RateThrottler.scala | 20 ++++++++------
.../core/loadBalancer/LoadBalancerService.scala | 31 +++++++++++----------
docs/reference.md | 2 +-
.../whisk/core/cli/test/WskWebActionsTests.scala | 18 +++++-------
.../controller/test/ControllerTestCommon.scala | 2 +-
.../core/controller/test/RateThrottleTests.scala | 3 +-
.../core/controller/test/WebActionsApiTests.scala | 2 +-
10 files changed, 66 insertions(+), 63 deletions(-)
diff --git a/common/scala/src/main/scala/whisk/core/entity/Identity.scala b/common/scala/src/main/scala/whisk/core/entity/Identity.scala
index e150653..9fb0ef2 100644
--- a/common/scala/src/main/scala/whisk/core/entity/Identity.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/Identity.scala
@@ -28,7 +28,9 @@ import whisk.core.database.NoDocumentException
import whisk.core.entitlement.Privilege
import whisk.core.entitlement.Privilege.Privilege
-protected[core] case class Identity(subject: Subject, namespace: EntityName, authkey: AuthKey, rights: Set[Privilege])
+protected[core] case class Identity(subject: Subject, namespace: EntityName, authkey: AuthKey, rights: Set[Privilege]) {
+ def uuid = authkey.uuid
+}
object Identity extends MultipleReadersSingleWriterCache[Identity, DocInfo] with DefaultJsonProtocol {
diff --git a/core/controller/src/main/scala/whisk/core/entitlement/ActivationThrottler.scala b/core/controller/src/main/scala/whisk/core/entitlement/ActivationThrottler.scala
index 4ba44e9..a778607 100644
--- a/core/controller/src/main/scala/whisk/core/entitlement/ActivationThrottler.scala
+++ b/core/controller/src/main/scala/whisk/core/entitlement/ActivationThrottler.scala
@@ -24,7 +24,8 @@ import akka.actor.ActorSystem
import whisk.common.Logging
import whisk.common.Scheduler
import whisk.common.TransactionId
-import whisk.core.entity.Subject
+import whisk.core.entity.Identity
+import whisk.core.entity.UUID
import whisk.core.loadBalancer.LoadBalancer
/**
@@ -48,16 +49,16 @@ class ActivationThrottler(consulServer: String, loadBalancer: LoadBalancer, conc
* the number of concurrent invocations it has in the system
*/
@volatile
- private var userActivationCounter = Map.empty[String, Int]
+ private var namespaceActivationCounter = Map.empty[UUID, Int]
private val healthCheckInterval = 5.seconds
/**
* Checks whether the operation should be allowed to proceed.
*/
- def check(subject: Subject)(implicit tid: TransactionId): Boolean = {
- val concurrentActivations = userActivationCounter.getOrElse(subject.asString, 0)
- logging.info(this, s"subject = ${subject.toString}, concurrent activations = $concurrentActivations, below limit = $concurrencyLimit")
+ def check(user: Identity)(implicit tid: TransactionId): Boolean = {
+ val concurrentActivations = namespaceActivationCounter.getOrElse(user.uuid, 0)
+ logging.debug(this, s"namespace = ${user.uuid.asString}, concurrent activations = $concurrentActivations, below limit = $concurrencyLimit")
concurrentActivations < concurrencyLimit
}
@@ -65,13 +66,13 @@ class ActivationThrottler(consulServer: String, loadBalancer: LoadBalancer, conc
* Checks whether the system is in a generally overloaded state.
*/
def isOverloaded()(implicit tid: TransactionId): Boolean = {
- val concurrentActivations = userActivationCounter.values.sum
+ val concurrentActivations = namespaceActivationCounter.values.sum
logging.info(this, s"concurrent activations in system = $concurrentActivations, below limit = $systemOverloadLimit")
concurrentActivations > systemOverloadLimit
}
Scheduler.scheduleWaitAtLeast(healthCheckInterval) { () =>
- userActivationCounter = loadBalancer.getActiveUserActivationCounts
+ namespaceActivationCounter = loadBalancer.getActiveNamespaceActivationCounts
Future.successful(Unit)
}
}
diff --git a/core/controller/src/main/scala/whisk/core/entitlement/Entitlement.scala b/core/controller/src/main/scala/whisk/core/entitlement/Entitlement.scala
index 6c205b5..ad4fd8f 100644
--- a/core/controller/src/main/scala/whisk/core/entitlement/Entitlement.scala
+++ b/core/controller/src/main/scala/whisk/core/entitlement/Entitlement.scala
@@ -117,20 +117,20 @@ protected[core] abstract class EntitlementProvider(config: WhiskConfig, loadBala
protected def entitled(subject: Subject, right: Privilege, resource: Resource)(implicit transid: TransactionId): Future[Boolean]
/**
- * Checks action activation rate throttles for subject.
+ * Checks action activation rate throttles for an identity.
*
- * @param user the subject to check rate throttles for
- * @return a promise that completes with success iff the subject is within their activation quota
+ * @param user the identity to check rate throttles for
+ * @return a promise that completes with success iff the user is within their activation quota
*/
protected[core] def checkThrottles(user: Identity)(
implicit transid: TransactionId): Future[Unit] = {
- val subject = user.subject
- logging.info(this, s"checking user '$subject' has not exceeded activation quota")
+
+ logging.info(this, s"checking user '${user.subject}' has not exceeded activation quota")
checkSystemOverload(ACTIVATE) orElse {
- checkThrottleOverload(!invokeRateThrottler.check(subject), tooManyRequests)
+ checkThrottleOverload(!invokeRateThrottler.check(user), tooManyRequests)
} orElse {
- checkThrottleOverload(!concurrentInvokeThrottler.check(subject), tooManyConcurrentRequests)
+ checkThrottleOverload(!concurrentInvokeThrottler.check(user), tooManyConcurrentRequests)
} map {
Future.failed(_)
} getOrElse Future.successful({})
@@ -161,7 +161,7 @@ protected[core] abstract class EntitlementProvider(config: WhiskConfig, loadBala
* resource for example, or explicit. The implicit check is computed here. The explicit check
* is delegated to the service implementing this interface.
*
- * @param user the subject to check rights for
+ * @param user the subject identity to check rights for
* @param right the privilege the subject is requesting (applies to the entire set of resources)
* @param resources the set of resources the subject requests access to
* @return a promise that completes with success iff the subject is permitted to access all of the requested resources
@@ -174,9 +174,9 @@ protected[core] abstract class EntitlementProvider(config: WhiskConfig, loadBala
if (resources.nonEmpty) {
logging.info(this, s"checking user '$subject' has privilege '$right' for '${resources.mkString(",")}'")
checkSystemOverload(right) orElse {
- checkUserThrottle(subject, right, resources)
+ checkUserThrottle(user, right, resources)
} orElse {
- checkConcurrentUserThrottle(subject, right, resources)
+ checkConcurrentUserThrottle(user, right, resources)
} map {
Future.failed(_)
} getOrElse checkPrivilege(user, right, resources)
@@ -245,17 +245,17 @@ protected[core] abstract class EntitlementProvider(config: WhiskConfig, loadBala
* While it is possible for the set of resources to contain more than one action or trigger, the plurality is ignored and treated
* as one activation since these should originate from a single macro resources (e.g., a sequence).
*
- * @param subject the subject to check quota for
+ * @param user the subject identity to check rights for
* @param right the privilege, if ACTIVATE then check quota else return None
* @param resource the set of resources must contain at least one resource that can be activated else return None
* @return None if subject is not throttled else a rejection
*/
- private def checkUserThrottle(subject: Subject, right: Privilege, resources: Set[Resource])(
+ private def checkUserThrottle(user: Identity, right: Privilege, resources: Set[Resource])(
implicit transid: TransactionId): Option[RejectRequest] = {
def userThrottled = {
val isInvocation = resources.exists(_.collection.path == Collection.ACTIONS)
val isTrigger = resources.exists(_.collection.path == Collection.TRIGGERS)
- (isInvocation && !invokeRateThrottler.check(subject)) || (isTrigger && !triggerRateThrottler.check(subject))
+ (isInvocation && !invokeRateThrottler.check(user)) || (isTrigger && !triggerRateThrottler.check(user))
}
checkThrottleOverload(right == ACTIVATE && userThrottled, tooManyRequests)
@@ -267,16 +267,16 @@ protected[core] abstract class EntitlementProvider(config: WhiskConfig, loadBala
* While it is possible for the set of resources to contain more than one action, the plurality is ignored and treated
* as one activation since these should originate from a single macro resources (e.g., a sequence).
*
- * @param subject the subject to check quota for
+ * @param user the subject identity to check rights for
* @param right the privilege, if ACTIVATE then check quota else return None
* @param resource the set of resources must contain at least one resource that can be activated else return None
* @return None if subject is not throttled else a rejection
*/
- private def checkConcurrentUserThrottle(subject: Subject, right: Privilege, resources: Set[Resource])(
+ private def checkConcurrentUserThrottle(user: Identity, right: Privilege, resources: Set[Resource])(
implicit transid: TransactionId): Option[RejectRequest] = {
def userThrottled = {
val isInvocation = resources.exists(_.collection.path == Collection.ACTIONS)
- (isInvocation && !concurrentInvokeThrottler.check(subject))
+ (isInvocation && !concurrentInvokeThrottler.check(user))
}
checkThrottleOverload(right == ACTIVATE && userThrottled, tooManyConcurrentRequests)
diff --git a/core/controller/src/main/scala/whisk/core/entitlement/RateThrottler.scala b/core/controller/src/main/scala/whisk/core/entitlement/RateThrottler.scala
index e5b22e2..ee32604 100644
--- a/core/controller/src/main/scala/whisk/core/entitlement/RateThrottler.scala
+++ b/core/controller/src/main/scala/whisk/core/entitlement/RateThrottler.scala
@@ -21,7 +21,8 @@ import scala.collection.concurrent.TrieMap
import whisk.common.Logging
import whisk.common.TransactionId
-import whisk.core.entity.Subject
+import whisk.core.entity.Identity
+import whisk.core.entity.UUID
/**
* A class tracking the rate of invocation (or any operation) by subject (any key really).
@@ -33,21 +34,22 @@ class RateThrottler(description: String, maxPerMinute: Int)(implicit logging: Lo
logging.info(this, s"$description: maxPerMinute = $maxPerMinute")(TransactionId.controller)
/**
- * Maintains map of subject to operations rates.
+ * Maintains map of subject namespace to operations rates.
*/
- private val rateMap = new TrieMap[Subject, RateInfo]
+ private val rateMap = new TrieMap[UUID, RateInfo]
/**
* Checks whether the operation should be allowed to proceed.
- * Every `check` operation charges the subject for one operation.
+ * Every `check` operation charges the subject namespace for one operation.
*
- * @param subject the subject to check
- * @return true iff subject is below allowed limit
+ * @param user the identity to check
+ * @return true iff subject namespace is below allowed limit
*/
- def check(subject: Subject)(implicit transid: TransactionId): Boolean = {
- val rate = rateMap.getOrElseUpdate(subject, new RateInfo(maxPerMinute))
+ def check(user: Identity)(implicit transid: TransactionId): Boolean = {
+ val uuid = user.uuid // this is namespace identifier
+ val rate = rateMap.getOrElseUpdate(uuid, new RateInfo(maxPerMinute))
val belowLimit = rate.check()
- logging.info(this, s"subject = ${subject.toString}, rate = ${rate.count()}, below limit = $belowLimit")
+ logging.debug(this, s"namespace = ${uuid.asString} rate = ${rate.count()}, below limit = $belowLimit")
belowLimit
}
}
diff --git a/core/controller/src/main/scala/whisk/core/loadBalancer/LoadBalancerService.scala b/core/controller/src/main/scala/whisk/core/loadBalancer/LoadBalancerService.scala
index d1d5208..6534307 100644
--- a/core/controller/src/main/scala/whisk/core/loadBalancer/LoadBalancerService.scala
+++ b/core/controller/src/main/scala/whisk/core/loadBalancer/LoadBalancerService.scala
@@ -49,6 +49,8 @@ import whisk.core.database.NoDocumentException
import whisk.core.entity.{ ActivationId, WhiskAction, WhiskActivation }
import whisk.core.entity.InstanceId
import whisk.core.entity.ExecutableWhiskAction
+import whisk.core.entity.UUID
+import whisk.core.entity.WhiskAction
import whisk.core.entity.types.EntityStore
import scala.annotation.tailrec
@@ -57,11 +59,11 @@ trait LoadBalancer {
val activeAckTimeoutGrace = 1.minute
/**
- * Retrieves a per subject map of counts representing in-flight activations as seen by the load balancer
+ * Retrieves a per namespace id map of counts representing in-flight activations as seen by the load balancer
*
- * @return a map where the key is the subject and the long is total issued activations by that user
+ * @return a map where the key is the namespace id and the long is total issued activations by that namespace
*/
- def getActiveUserActivationCounts: Map[String, Int]
+ def getActiveNamespaceActivationCounts: Map[UUID, Int]
/**
* Publishes activation message on internal bus for an invoker to pick up.
@@ -88,13 +90,12 @@ class LoadBalancerService(config: WhiskConfig, instance: InstanceId, entityStore
private val blackboxFraction: Double = Math.max(0.0, Math.min(1.0, config.controllerBlackboxFraction))
logging.info(this, s"blackboxFraction = $blackboxFraction")
- override def getActiveUserActivationCounts: Map[String, Int] = activationBySubject.toMap mapValues { _.size }
+ override def getActiveNamespaceActivationCounts: Map[UUID, Int] = activationByNamespaceId.toMap mapValues { _.size }
override def publish(action: ExecutableWhiskAction, msg: ActivationMessage)(
implicit transid: TransactionId): Future[Future[Either[ActivationId, WhiskActivation]]] = {
chooseInvoker(action, msg).flatMap { invokerName =>
- val subject = msg.user.subject.asString
- val entry = setupActivation(action, msg.activationId, subject, invokerName, transid)
+ val entry = setupActivation(action, msg.activationId, msg.user.uuid, invokerName, transid)
sendActivationToInvoker(messageProducer, msg, invokerName).map { _ =>
entry.promise.future
}
@@ -107,11 +108,11 @@ class LoadBalancerService(config: WhiskConfig, instance: InstanceId, entityStore
* A map storing current activations based on ActivationId.
* The promise value represents the obligation of writing the answer back.
*/
- case class ActivationEntry(id: ActivationId, subject: String, invokerName: String, created: Instant, promise: Promise[Either[ActivationId, WhiskActivation]])
+ case class ActivationEntry(id: ActivationId, namespaceId: UUID, invokerName: String, created: Instant, promise: Promise[Either[ActivationId, WhiskActivation]])
type TrieSet[T] = TrieMap[T, Unit]
private val activationById = new TrieMap[ActivationId, ActivationEntry]
private val activationByInvoker = new TrieMap[String, TrieSet[ActivationEntry]]
- private val activationBySubject = new TrieMap[String, TrieSet[ActivationEntry]]
+ private val activationByNamespaceId = new TrieMap[UUID, TrieSet[ActivationEntry]]
/**
* Tries to fill in the result slot (i.e., complete the promise) when a completion message arrives.
@@ -122,10 +123,10 @@ class LoadBalancerService(config: WhiskConfig, instance: InstanceId, entityStore
private def processCompletion(response: Either[ActivationId, WhiskActivation], tid: TransactionId, forced: Boolean): Unit = {
val aid = response.fold(l => l, r => r.activationId)
activationById.remove(aid) match {
- case Some(entry @ ActivationEntry(_, subject, invokerIndex, _, p)) =>
+ case Some(entry @ ActivationEntry(_, namespaceId, invokerIndex, _, p)) =>
logging.info(this, s"${if (!forced) "received" else "forced"} active ack for '$aid'")(tid)
activationByInvoker.getOrElseUpdate(invokerIndex, new TrieSet[ActivationEntry]).remove(entry)
- activationBySubject.getOrElseUpdate(subject, new TrieSet[ActivationEntry]).remove(entry)
+ activationByNamespaceId.getOrElseUpdate(namespaceId, new TrieSet[ActivationEntry]).remove(entry)
if (!forced) {
p.trySuccess(response)
} else {
@@ -141,7 +142,7 @@ class LoadBalancerService(config: WhiskConfig, instance: InstanceId, entityStore
/**
* Creates an activation entry and insert into various maps.
*/
- private def setupActivation(action: ExecutableWhiskAction, activationId: ActivationId, subject: String, invokerName: String, transid: TransactionId): ActivationEntry = {
+ private def setupActivation(action: ExecutableWhiskAction, activationId: ActivationId, namespaceId: UUID, invokerName: String, transid: TransactionId): ActivationEntry = {
// either create a new promise or reuse a previous one for this activation if it exists
val timeout = action.limits.timeout.duration + activeAckTimeoutGrace
val entry = activationById.getOrElseUpdate(activationId, {
@@ -157,12 +158,12 @@ class LoadBalancerService(config: WhiskConfig, instance: InstanceId, entityStore
processCompletion(Left(activationId), transid, forced = true)
}
- ActivationEntry(activationId, subject, invokerName, Instant.now(Clock.systemUTC()), promiseRef.get)
+ ActivationEntry(activationId, namespaceId, invokerName, Instant.now(Clock.systemUTC()), promiseRef.get)
})
// add the entry to our maps, for bookkeeping
activationByInvoker.getOrElseUpdate(invokerName, new TrieSet[ActivationEntry]).put(entry, {})
- activationBySubject.getOrElseUpdate(subject, new TrieSet[ActivationEntry]).put(entry, {})
+ activationByNamespaceId.getOrElseUpdate(namespaceId, new TrieSet[ActivationEntry]).put(entry, {})
entry
}
@@ -172,10 +173,10 @@ class LoadBalancerService(config: WhiskConfig, instance: InstanceId, entityStore
private def clearInvokerState(invokerName: String) = {
val actSet = activationByInvoker.getOrElseUpdate(invokerName, new TrieSet[ActivationEntry])
actSet.keySet map {
- case actEntry @ ActivationEntry(activationId, subject, invokerIndex, _, promise) =>
+ case actEntry @ ActivationEntry(activationId, namespaceId, invokerIndex, _, promise) =>
promise.tryFailure(new LoadBalancerException(s"Invoker $invokerIndex restarted"))
activationById.remove(activationId)
- activationBySubject.get(subject) map { _.remove(actEntry) }
+ activationByNamespaceId.get(namespaceId) map { _.remove(actEntry) }
}
actSet.clear()
}
diff --git a/docs/reference.md b/docs/reference.md
index 686cc2f..c6d826e 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -400,7 +400,7 @@ The following table lists the default limits for actions.
| memory | a container is not allowed to allocate more than N MB of memory | per action | MB | 256 |
| logs | a container is not allowed to write more than N MB to stdout | per action | MB | 10 |
| concurrent | no more than N activations may be submitted per namespace either executing or queued for execution | per namespace | number | 100 |
-| minuteRate | no more than N activations may be submitted per namespace per minute | per user | number | 120 |
+| minuteRate | no more than N activations may be submitted per namespace per minute | per namespace | number | 120 |
| codeSize | the maximum size of the actioncode | not configurable, limit per action | MB | 48 |
| parameters | the maximum size of the parameters that can be attached | not configurable, limit per action/package/trigger | MB | 1 |
diff --git a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
index 5983a99..5a1a653 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
@@ -89,8 +89,7 @@ class WskWebActionsTestsV2 extends WskWebActionsTests with BeforeAndAfterAll {
val file = Some(TestUtils.getTestActionFilename("echo.js"))
assetHelper.withCleaner(wsk.action, actionName) {
- (action, _) =>
- action.create(actionName, file, web = Some(true.toString))(wp)
+ (action, _) => action.create(actionName, file, web = Some(true.toString))(wp)
}
val url = getServiceApiHost(vanitySubdomain, true) + s"/default/$actionName.text/a?a=A"
@@ -148,8 +147,7 @@ trait WskWebActionsTests
val file = Some(TestUtils.getTestActionFilename("echo.js"))
assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, web = Some("true"))
+ (action, _) => action.create(name, file, web = Some("true"))
}
val host = getServiceURL()
@@ -241,11 +239,11 @@ trait WskWebActionsTests
val url = host + s"$testRoutePath/$namespace/default/webaction"
assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, web = Some("true"))
+ (action, _) => action.create(name, file, web = Some("true"))
}
- val responses = Seq(RestAssured.given().config(sslconfig).options(s"$url.http"),
+ val responses = Seq(
+ RestAssured.given().config(sslconfig).options(s"$url.http"),
RestAssured.given().config(sslconfig).get(s"$url.json"))
responses.foreach { response =>
@@ -264,8 +262,7 @@ trait WskWebActionsTests
val bodyContent = "This is the body"
assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, web = Some("true"))
+ (action, _) => action.create(name, file, web = Some("true"))
}
val host = getServiceURL()
@@ -290,8 +287,7 @@ trait WskWebActionsTests
val file = Some(TestUtils.getTestActionFilename("textBody.js"))
assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, web = Some("true"))
+ (action, _) => action.create(name, file, web = Some("true"))
}
val host = getServiceURL()
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 f2e6656..0110050 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ControllerTestCommon.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ControllerTestCommon.scala
@@ -199,7 +199,7 @@ class DegenerateLoadBalancerService(config: WhiskConfig)(implicit ec: ExecutionC
// unit tests that need an activation via active ack/fast path should set this to value expected
var whiskActivationStub: Option[(FiniteDuration, WhiskActivation)] = None
- override def getActiveUserActivationCounts: Map[String, Int] = Map()
+ override def getActiveNamespaceActivationCounts: Map[UUID, Int] = Map.empty
override def publish(action: ExecutableWhiskAction, msg: ActivationMessage)(implicit transid: TransactionId): Future[Future[Either[ActivationId, WhiskActivation]]] =
Future.successful {
diff --git a/tests/src/test/scala/whisk/core/controller/test/RateThrottleTests.scala b/tests/src/test/scala/whisk/core/controller/test/RateThrottleTests.scala
index e404be8..62e0537 100644
--- a/tests/src/test/scala/whisk/core/controller/test/RateThrottleTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/RateThrottleTests.scala
@@ -28,6 +28,7 @@ import common.StreamLogging
import whisk.common.TransactionId
import whisk.core.entitlement._
import whisk.core.entity.Subject
+import whisk.core.entity.AuthKey
/**
* Tests rate throttle.
@@ -42,7 +43,7 @@ class RateThrottleTests
with StreamLogging {
implicit val transid = TransactionId.testing
- val subject = Subject()
+ val subject = Subject().toIdentity(AuthKey())
behavior of "Rate Throttle"
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 307a31c..af57577 100644
--- a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
@@ -1131,7 +1131,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
implicit val tid = transid()
customOptions = false
- Seq(s"$systemId/proxy/export_c.http", s"$systemId/proxy/export_c.json?a=b&c=d").
+ Seq(s"$systemId/proxy/export_c.http", s"$systemId/proxy/export_c.json").
foreach { path =>
allowedMethods.foreach { m =>
invocationsAllowed += 1
--
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <co...@openwhisk.apache.org>'].