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 fb0bab6 Add an annotations to inject the API key into the action
context. (#4284)
fb0bab6 is described below
commit fb0bab64de6634725b409a63ffbb90fee295e8c1
Author: rodric rabbah <[email protected]>
AuthorDate: Thu Mar 7 20:04:21 2019 -0500
Add an annotations to inject the API key into the action context. (#4284)
* Factor out some annotation names to WhiskAction singleton.
* Omit API key unless requested explicitly.
* Add an annotation provide-api-key which causes an API key to be passed to
the action context.
* Modify Parameters.isTruthy to allow for caller to specify how it wants to
treat a missing key.
Default is missing key -> false (preserving behavior).
Add test for isTruthy.
* Treat a missing provide-key-annotation as truthy annotation.
Actions that already exist without the annotation will receive the key as
they might already expect.
* Add container proxy test.
* Update tests for new system annotation.
* Allow pre-existing actions to be exempt from new system annotations.
* Update docs.
---
.../apache/openwhisk/core/entity/Parameter.scala | 13 +-
.../apache/openwhisk/core/entity/WhiskAction.scala | 18 +-
.../apache/openwhisk/core/controller/Actions.scala | 47 +++--
.../openwhisk/core/controller/WebActions.scala | 8 +-
.../core/containerpool/ContainerProxy.scala | 8 +-
docs/actions.md | 12 +-
docs/annotations.md | 9 +
.../core/cli/test/WskRestBasicUsageTests.scala | 69 ++++++--
.../containerpool/test/ContainerProxyTests.scala | 54 +++++-
.../core/controller/test/ActionsApiTests.scala | 196 +++++++++++++++------
.../controller/test/ControllerTestCommon.scala | 9 +
.../controller/test/PackageActionsApiTests.scala | 2 +-
.../core/controller/test/WebActionsApiTests.scala | 2 +-
.../openwhisk/core/entity/test/SchemaTests.scala | 13 ++
.../test/scala/system/basic/WskActionTests.scala | 9 +-
.../scala/system/basic/WskRestBasicTests.scala | 5 +-
16 files changed, 360 insertions(+), 114 deletions(-)
diff --git
a/common/scala/src/main/scala/org/apache/openwhisk/core/entity/Parameter.scala
b/common/scala/src/main/scala/org/apache/openwhisk/core/entity/Parameter.scala
index 2fe63d2..bb82fb9 100644
---
a/common/scala/src/main/scala/org/apache/openwhisk/core/entity/Parameter.scala
+++
b/common/scala/src/main/scala/org/apache/openwhisk/core/entity/Parameter.scala
@@ -65,7 +65,7 @@ protected[core] class Parameters protected[entity] (private
val params: Map[Para
}
/** Remove parameter by name. */
- protected[core] def -(p: String) = {
+ protected[core] def -(p: String): Parameters = {
// wrap with try since parameter name may throw an exception for illegal p
Try(new Parameters(params - new ParameterName(p))) getOrElse this
}
@@ -103,15 +103,20 @@ protected[core] class Parameters protected[entity]
(private val params: Map[Para
.fold[Try[JsValue]](Failure(new IllegalStateException(s"key '$p' does
not exist")))(Success.apply)
.flatMap(js => Try(js.convertTo[T]))
- /** Retrieves parameter by name if it exist. Returns true if parameter
exists and has truthy value. */
- protected[core] def isTruthy(p: String): Boolean = {
+ /**
+ * Retrieves parameter by name if it exist.
+ * @param p the parameter to check for a truthy value
+ * @param valueForNonExistent the value to return for a missing parameter
(default false)
+ * @return true if parameter exists and has truthy value, otherwise returns
the specified value for non-existent keys
+ */
+ protected[core] def isTruthy(p: String, valueForNonExistent: Boolean =
false): Boolean = {
get(p) map {
case JsBoolean(b) => b
case JsNumber(n) => n != 0
case JsString(s) => s.nonEmpty
case JsNull => false
case _ => true
- } getOrElse false
+ } getOrElse valueForNonExistent
}
}
diff --git
a/common/scala/src/main/scala/org/apache/openwhisk/core/entity/WhiskAction.scala
b/common/scala/src/main/scala/org/apache/openwhisk/core/entity/WhiskAction.scala
index 38d1a2f..eaa58c3 100644
---
a/common/scala/src/main/scala/org/apache/openwhisk/core/entity/WhiskAction.scala
+++
b/common/scala/src/main/scala/org/apache/openwhisk/core/entity/WhiskAction.scala
@@ -143,7 +143,7 @@ case class WhiskAction(namespace: EntityPath,
* Merges parameters (usually from package) with existing action parameters.
* Existing parameters supersede those in p.
*/
- def inherit(p: Parameters) = copy(parameters = p ++
parameters).revision[WhiskAction](rev)
+ def inherit(p: Parameters): WhiskAction = copy(parameters = p ++
parameters).revision[WhiskAction](rev)
/**
* Resolves sequence components if they contain default namespace.
@@ -166,7 +166,7 @@ case class WhiskAction(namespace: EntityPath,
}
}
- def toExecutableWhiskAction = exec match {
+ def toExecutableWhiskAction: Option[ExecutableWhiskAction] = exec match {
case codeExec: CodeExec[_] =>
Some(
ExecutableWhiskAction(namespace, name, codeExec, parameters, limits,
version, publish, annotations)
@@ -178,7 +178,7 @@ case class WhiskAction(namespace: EntityPath,
* This the action summary as computed by the database view.
* Strictly used in view testing to enforce alignment.
*/
- override def summaryAsJson = {
+ override def summaryAsJson: JsObject = {
val binary = exec match {
case c: CodeExec[_] => c.binary
case _ => false
@@ -309,6 +309,12 @@ object WhiskAction extends DocumentFactory[WhiskAction]
with WhiskEntityQueries[
val execFieldName = "exec"
val finalParamsAnnotationName = "final"
+ val webActionAnnotationName = "web-export"
+ val webCustomOptionsAnnotationName = "web-custom-options"
+ val rawHttpAnnotationName = "raw-http"
+ val requireWhiskAuthAnnotation = "require-whisk-auth"
+ val requireWhiskAuthHeader = "x-require-whisk-auth"
+ val provideApiKeyAnnotationName = "provide-api-key"
override val collectionName = "actions"
@@ -325,9 +331,6 @@ object WhiskAction extends DocumentFactory[WhiskAction]
with WhiskEntityQueries[
override val cacheEnabled = true
- val requireWhiskAuthAnnotation = "require-whisk-auth"
- val requireWhiskAuthHeader = "x-require-whisk-auth"
-
// overriden to store attached code
override def put[A >: WhiskAction](db: ArtifactStore[A], doc: WhiskAction,
old: Option[WhiskAction])(
implicit transid: TransactionId,
@@ -512,9 +515,6 @@ object WhiskActionMetaData
with WhiskEntityQueries[WhiskActionMetaData]
with DefaultJsonProtocol {
- val execFieldName = "exec"
- val finalParamsAnnotationName = "final"
-
override val collectionName = "actions"
override implicit val serdes = jsonFormat(
diff --git
a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Actions.scala
b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Actions.scala
index d535fd1..d3e79c3 100644
---
a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Actions.scala
+++
b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/Actions.scala
@@ -59,6 +59,38 @@ object WhiskActionsApi {
* This is the default timeout on a POST request.
*/
protected[core] val maxWaitForBlockingActivation = 60 seconds
+
+ /**
+ * Amends annotations on an action create/update with system defined values.
+ * This method currently adds the following annotations:
+ * 1. [[WhiskAction.provideApiKeyAnnotationName]] with the value false iff
the annotation is not already defined in the action annotations
+ * 2. An [[execAnnotation]] consistent with the action kind; this annotation
is always added and overrides a pre-existing value
+ */
+ protected[core] def amendAnnotations(annotations: Parameters, exec: Exec,
create: Boolean = true): Parameters = {
+ val newAnnotations = if (create) {
+ // these annotations are only added on newly created actions
+ // since they can break existing actions created before the
+ // annotation was created
+ annotations
+ .get(WhiskAction.provideApiKeyAnnotationName)
+ .map(_ => annotations)
+ .getOrElse {
+ annotations ++ Parameters(WhiskAction.provideApiKeyAnnotationName,
JsBoolean(false))
+ }
+ } else annotations
+ newAnnotations ++ execAnnotation(exec)
+ }
+
+ /**
+ * Constructs an "exec" annotation. This is redundant with the exec kind
+ * information available in WhiskAction but necessary for some clients which
+ * fetch action lists but cannot determine action kinds without fetching
them.
+ * An alternative is to include the exec in the action list "view" but this
+ * will require an API change. So using an annotation instead.
+ */
+ private def execAnnotation(exec: Exec): Parameters = {
+ Parameters(WhiskAction.execFieldName, exec.kind)
+ }
}
/** A trait implementing the actions API. */
@@ -416,7 +448,7 @@ trait WhiskActionsApi extends WhiskCollectionAPI with
PostActionActivation with
limits,
content.version getOrElse SemVer(),
content.publish getOrElse false,
- (content.annotations getOrElse Parameters()) ++ execAnnotation(exec))
+ WhiskActionsApi.amendAnnotations(content.annotations getOrElse
Parameters(), exec))
}
/** For a sequence action, gather referenced entities and authorize access.
*/
@@ -531,7 +563,7 @@ trait WhiskActionsApi extends WhiskCollectionAPI with
PostActionActivation with
limits,
content.version getOrElse action.version.upPatch,
content.publish getOrElse action.publish,
- (content.annotations getOrElse action.annotations) ++
execAnnotation(exec))
+ WhiskActionsApi.amendAnnotations(content.annotations getOrElse
action.annotations, exec, create = false))
.revision[WhiskAction](action.docinfo.rev)
}
@@ -685,17 +717,6 @@ trait WhiskActionsApi extends WhiskCollectionAPI with
PostActionActivation with
}
}
- /**
- * Constructs an "exec" annotation. This is redundant with the exec kind
- * information available in WhiskAction but necessary for some clients which
- * fetch action lists but cannot determine action kinds without fetching
them.
- * An alternative is to include the exec in the action list "view" but this
- * will require an API change. So using an annotation instead.
- */
- private def execAnnotation(exec: Exec): Parameters = {
- Parameters(WhiskAction.execFieldName, exec.kind)
- }
-
/** Max atomic action count allowed for sequences. */
private lazy val actionSequenceLimit = whiskConfig.actionSequenceLimit.toInt
diff --git
a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala
b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala
index b7f0bce..d698cb4 100644
---
a/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala
+++
b/core/controller/src/main/scala/org/apache/openwhisk/core/controller/WebActions.scala
@@ -490,7 +490,9 @@ trait WhiskWebActionsApi
this,
"web action with require-whisk-auth was invoked
without a matching x-require-whisk-auth header value")
terminate(Unauthorized)
- } else if
(!action.annotations.getAs[Boolean]("web-custom-options").getOrElse(false)) {
+ } else if (!action.annotations
+
.getAs[Boolean](WhiskAction.webCustomOptionsAnnotationName)
+ .getOrElse(false)) {
respondWithHeaders(defaultCorsResponse(context.headers))
{
if (context.method == OPTIONS) {
complete(OK, HttpEntity.Empty)
@@ -554,7 +556,7 @@ trait WhiskWebActionsApi
processRequest(actionOwnerIdentity, action, extension, onBehalfOf,
context.withBody(body), isRawHttpAction)
}
- provide(action.annotations.getAs[Boolean]("raw-http").getOrElse(false)) {
isRawHttpAction =>
+
provide(action.annotations.getAs[Boolean](WhiskAction.rawHttpAnnotationName).getOrElse(false))
{ isRawHttpAction =>
httpEntity match {
case Empty =>
process(None, isRawHttpAction)
@@ -705,7 +707,7 @@ trait WhiskWebActionsApi
actionLookup flatMap { action =>
val requiresAuthenticatedUser =
action.annotations.getAs[Boolean](WhiskAction.requireWhiskAuthAnnotation).getOrElse(false)
- val isExported =
action.annotations.getAs[Boolean]("web-export").getOrElse(false)
+ val isExported =
action.annotations.getAs[Boolean](WhiskAction.webActionAnnotationName).getOrElse(false)
if ((isExported && requiresAuthenticatedUser && authenticated) ||
(isExported && !requiresAuthenticatedUser)) {
diff --git
a/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/ContainerProxy.scala
b/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/ContainerProxy.scala
index 7419bf3..a69e992 100644
---
a/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/ContainerProxy.scala
+++
b/core/invoker/src/main/scala/org/apache/openwhisk/core/containerpool/ContainerProxy.scala
@@ -560,7 +560,13 @@ class ContainerProxy(
}
val parameters = job.msg.content getOrElse JsObject.empty
- val authEnvironment = job.msg.user.authkey.toEnvironment
+ // if the action requests the api key to be injected into the action
context, add it here;
+ // treat a missing annotation as requesting the api key for backward
compatibility
+ val authEnvironment = {
+ if
(job.action.annotations.isTruthy(WhiskAction.provideApiKeyAnnotationName,
valueForNonExistent = true)) {
+ job.msg.user.authkey.toEnvironment
+ } else JsObject.empty
+ }
val environment = JsObject(
"namespace" -> job.msg.user.namespace.name.toJson,
diff --git a/docs/actions.md b/docs/actions.md
index f77426b..4fc1f78 100644
--- a/docs/actions.md
+++ b/docs/actions.md
@@ -616,9 +616,9 @@ or set an internal alarm when the action is about to use up
its allotted time bu
The properties are accessible via the system environment for all supported
runtimes:
Node.js, Python, Swift, Java and Docker actions when using the OpenWhisk
Docker skeleton.
-* `__OW_API_HOST` the API host for the OpenWhisk deployment running this action
-* `__OW_API_KEY` the API key for the subject invoking the action, this key may
be a restricted API key
-* `__OW_NAMESPACE` the namespace for the _activation_ (this may not be the
same as the namespace for the action)
-* `__OW_ACTION_NAME` the fully qualified name of the running action
-* `__OW_ACTIVATION_ID` the activation id for this running action instance
-* `__OW_DEADLINE` the approximate time when this action will have consumed its
entire duration quota (measured in epoch milliseconds)
+* `__OW_API_HOST` the API host for the OpenWhisk deployment running this
action.
+* `__OW_API_KEY` the API key for the subject invoking the action, this key may
be a restricted API key. This property is absent unless explicitly
[requested](./annotations.md#annotations-for-all-actions).
+* `__OW_NAMESPACE` the namespace for the _activation_ (this may not be the
same as the namespace for the action).
+* `__OW_ACTION_NAME` the fully qualified name of the running action.
+* `__OW_ACTIVATION_ID` the activation id for this running action instance.
+* `__OW_DEADLINE` the approximate time when this action will have consumed its
entire duration quota (measured in epoch milliseconds).
diff --git a/docs/annotations.md b/docs/annotations.md
index 5012eb3..58a00d0 100644
--- a/docs/annotations.md
+++ b/docs/annotations.md
@@ -57,6 +57,15 @@ The annotations we have used for describing parameters
include:
The annotations are _not_ checked. So while it is conceivable to use the
annotations to infer if a composition of two actions into a sequence is legal,
for example, the system does not yet do that.
+# Annotations for all actions
+
+The following annotations on an action are available.
+
+* `provide-api-key`: This annotation may be attached to actions which require
an API key, for example to make REST API calls to the OpenWhisk host.
+The absence of this annotation, or its presence with a value that is not
_falsy_ (i.e., a value that is different from zero, null, false, and the empty
string)
+will cause an API key to be present in the [action execution
context](./actions.md#accessing-action-metadata-within-the-action-body). This
annotation is added
+to newly created actions, if not already specified, with a default false value.
+
# Annotations specific to web actions
Web actions are enabled with explicit annotations which decorate individual
actions. The annotations only apply to the [web actions](webactions.md) API,
diff --git
a/tests/src/test/scala/org/apache/openwhisk/core/cli/test/WskRestBasicUsageTests.scala
b/tests/src/test/scala/org/apache/openwhisk/core/cli/test/WskRestBasicUsageTests.scala
index bc6d896..65dc1e3 100644
---
a/tests/src/test/scala/org/apache/openwhisk/core/cli/test/WskRestBasicUsageTests.scala
+++
b/tests/src/test/scala/org/apache/openwhisk/core/cli/test/WskRestBasicUsageTests.scala
@@ -300,7 +300,10 @@ class WskRestBasicUsageTests extends TestHelpers with
WskTestHelpers with WskAct
it should "invoke an action using npm openwhisk" in
withAssetCleaner(wskprops) { (wp, assetHelper) =>
val name = "hello npm openwhisk"
assetHelper.withCleaner(wsk.action, name, confirmDelete = false) {
(action, _) =>
- action.create(name,
Some(TestUtils.getTestActionFilename("helloOpenwhiskPackage.js")))
+ action.create(
+ name,
+ Some(TestUtils.getTestActionFilename("helloOpenwhiskPackage.js")),
+ annotations = Map(WhiskAction.provideApiKeyAnnotationName ->
JsBoolean(true)))
}
val run = wsk.action.invoke(name, Map("ignore_certs" -> true.toJson,
"name" -> name.toJson))
@@ -313,25 +316,51 @@ class WskRestBasicUsageTests extends TestHelpers with
WskTestHelpers with WskAct
wsk.action.delete(name, expectedExitCode = NotFound.intValue)
}
- it should "invoke an action receiving context properties" in
withAssetCleaner(wskprops) { (wp, assetHelper) =>
- val namespace = wsk.namespace.whois()
- val name = "context"
- assetHelper.withCleaner(wsk.action, name) { (action, _) =>
- action.create(name,
Some(TestUtils.getTestActionFilename("helloContext.js")))
- }
+ it should "invoke an action receiving context properties excluding api key"
in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val namespace = wsk.namespace.whois()
+ val name = "context"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name,
Some(TestUtils.getTestActionFilename("helloContext.js")))
+ }
- val start = Instant.now(Clock.systemUTC()).toEpochMilli
- val run = wsk.action.invoke(name)
- withActivation(wsk.activation, run) { activation =>
- activation.response.status shouldBe "success"
- val fields = activation.response.result.get.convertTo[Map[String,
String]]
- fields("api_host") shouldBe WhiskProperties.getApiHostForAction
- fields("api_key") shouldBe wskprops.authKey
- fields("namespace") shouldBe namespace
- fields("action_name") shouldBe s"/$namespace/$name"
- fields("activation_id") shouldBe activation.activationId
- fields("deadline").toLong should be >= start
- }
+ val start = Instant.now(Clock.systemUTC()).toEpochMilli
+ val run = wsk.action.invoke(name)
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.status shouldBe "success"
+ val fields = activation.response.result.get.convertTo[Map[String,
String]]
+ fields("api_host") shouldBe WhiskProperties.getApiHostForAction
+ fields.get("api_key") shouldBe empty
+ fields("namespace") shouldBe namespace
+ fields("action_name") shouldBe s"/$namespace/$name"
+ fields("activation_id") shouldBe activation.activationId
+ fields("deadline").toLong should be >= start
+ }
+ }
+
+ it should "invoke an action receiving context properties including api key"
in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val namespace = wsk.namespace.whois()
+ val name = "context"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(
+ name,
+ Some(TestUtils.getTestActionFilename("helloContext.js")),
+ annotations = Map(WhiskAction.provideApiKeyAnnotationName ->
JsBoolean(true)))
+ }
+
+ val start = Instant.now(Clock.systemUTC()).toEpochMilli
+ val run = wsk.action.invoke(name)
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.status shouldBe "success"
+ val fields = activation.response.result.get.convertTo[Map[String,
String]]
+ fields("api_host") shouldBe WhiskProperties.getApiHostForAction
+ fields("api_key") shouldBe wskprops.authKey
+ fields("namespace") shouldBe namespace
+ fields("action_name") shouldBe s"/$namespace/$name"
+ fields("activation_id") shouldBe activation.activationId
+ fields("deadline").toLong should be >= start
+ }
}
it should "invoke an action successfully with options --blocking and
--result" in withAssetCleaner(wskprops) {
@@ -404,6 +433,7 @@ class WskRestBasicUsageTests extends TestHelpers with
WskTestHelpers with WskAct
val action = wsk.action.get(name)
action.getFieldJsValue("annotations").convertTo[Set[JsObject]] shouldBe
Set(
JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")),
+ JsObject("key" -> WhiskAction.provideApiKeyAnnotationName.toJson,
"value" -> JsBoolean(false)),
JsObject("key" -> JsString("web-export"), "value" ->
JsBoolean(webEnabled || rawEnabled)),
JsObject("key" -> JsString("raw-http"), "value" ->
JsBoolean(rawEnabled)),
JsObject("key" -> JsString("final"), "value" -> JsBoolean(webEnabled
|| rawEnabled)))
@@ -424,6 +454,7 @@ class WskRestBasicUsageTests extends TestHelpers with
WskTestHelpers with WskAct
JsObject("key" -> JsString("web-export"), "value" -> JsBoolean(true)),
JsObject("key" -> JsString("raw-http"), "value" -> JsBoolean(false)),
JsObject("key" -> JsString("final"), "value" -> JsBoolean(true)),
+ JsObject("key" -> WhiskAction.provideApiKeyAnnotationName.toJson,
"value" -> JsBoolean(false)),
JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")))
}
diff --git
a/tests/src/test/scala/org/apache/openwhisk/core/containerpool/test/ContainerProxyTests.scala
b/tests/src/test/scala/org/apache/openwhisk/core/containerpool/test/ContainerProxyTests.scala
index 866ee68..cf93736 100644
---
a/tests/src/test/scala/org/apache/openwhisk/core/containerpool/test/ContainerProxyTests.scala
+++
b/tests/src/test/scala/org/apache/openwhisk/core/containerpool/test/ContainerProxyTests.scala
@@ -41,6 +41,7 @@ import org.apache.openwhisk.core.entity._
import org.apache.openwhisk.core.entity.size._
import org.apache.openwhisk.http.Messages
import org.apache.openwhisk.core.database.UserContext
+import org.apache.openwhisk.core.entity.WhiskAction.provideApiKeyAnnotationName
import scala.concurrent.Await
import scala.concurrent.duration._
@@ -71,6 +72,7 @@ class ContainerProxyTests
val invocationNamespace = EntityName("invocationSpace")
val action = ExecutableWhiskAction(EntityPath("actionSpace"),
EntityName("actionName"), exec)
+
val concurrencyEnabled =
Option(WhiskProperties.getProperty("whisk.action.concurrency")).exists(_.toBoolean)
val testConcurrencyLimit = if (concurrencyEnabled) ConcurrencyLimit(2) else
ConcurrencyLimit(1)
val concurrentAction = ExecutableWhiskAction(
@@ -1067,11 +1069,54 @@ class ContainerProxyTests
}
}
+ // This tests ensures the user api key is not present in the action context
if not requested
+ it should "omit api key from action run context" in within(timeout) {
+ val container = new TestContainer(apiKeyMustBePresent = false)
+ val factory = createFactory(Future.successful(container))
+ val acker = createAcker()
+ val store = createStore
+ val collector = createCollector()
+
+ val machine =
+ childActorOf(
+ ContainerProxy
+ .props(
+ factory,
+ acker,
+ store,
+ collector,
+ InvokerInstanceId(0, userMemory = defaultUserMemory),
+ poolConfig,
+ pauseGrace = pauseGrace))
+ registerCallback(machine)
+
+ preWarm(machine)
+
+ val keyFalsyAnnotation = Parameters(provideApiKeyAnnotationName,
JsBoolean(false))
+ val actionWithFalsyKeyAnnotation =
+ ExecutableWhiskAction(EntityPath("actionSpace"),
EntityName("actionName"), exec, annotations = keyFalsyAnnotation)
+
+ machine ! Run(actionWithFalsyKeyAnnotation, message)
+ expectMsg(Transition(machine, Started, Running))
+ expectWarmed(invocationNamespace.name, actionWithFalsyKeyAnnotation)
+ expectMsg(Transition(machine, Running, Ready))
+
+ awaitAssert {
+ factory.calls should have size 1
+ container.initializeCount shouldBe 1
+ container.runCount shouldBe 1
+ collector.calls should have size 1
+ acker.calls should have size 1
+ store.calls should have size 1
+ }
+ }
+
/**
* Implements all the good cases of a perfect run to facilitate error case
overriding.
*/
class TestContainer(initPromise: Option[Promise[Interval]] = None,
- runPromises: Seq[Promise[(Interval,
ActivationResponse)]] = Seq.empty)
+ runPromises: Seq[Promise[(Interval,
ActivationResponse)]] = Seq.empty,
+ apiKeyMustBePresent: Boolean = true)
extends Container {
protected[core] val id = ContainerId("testcontainer")
protected val addr = ContainerAddress("0.0.0.0")
@@ -1121,7 +1166,12 @@ class ContainerProxyTests
environment.fields("action_name") shouldBe
message.action.qualifiedNameWithLeadingSlash.toJson
environment.fields("activation_id") shouldBe message.activationId.toJson
val authEnvironment =
environment.fields.filterKeys(message.user.authkey.toEnvironment.fields.contains)
- message.user.authkey.toEnvironment shouldBe
authEnvironment.toJson.asJsObject
+ if (apiKeyMustBePresent) {
+ message.user.authkey.toEnvironment shouldBe
authEnvironment.toJson.asJsObject
+ } else {
+ authEnvironment shouldBe empty
+ }
+
val deadline =
Instant.ofEpochMilli(environment.fields("deadline").convertTo[String].toLong)
val maxDeadline = Instant.now.plusMillis(timeout.toMillis)
diff --git
a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/ActionsApiTests.scala
b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/ActionsApiTests.scala
index 32eedde..7851181 100644
---
a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/ActionsApiTests.scala
+++
b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/ActionsApiTests.scala
@@ -39,6 +39,8 @@ import org.apache.openwhisk.core.database.UserContext
import akka.http.scaladsl.model.headers.RawHeader
import org.apache.commons.lang3.StringUtils
import org.apache.openwhisk.core.entity.Attachments.Inline
+import org.apache.openwhisk.core.entity.test.ExecHelpers
+import org.scalatest.{FlatSpec, Matchers}
/**
* Tests Actions API.
@@ -62,7 +64,9 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
val context = UserContext(creds)
val namespace = EntityPath(creds.subject.asString)
val collectionPath = s"/${EntityPath.DEFAULT}/${collection.path}"
+
def aname() = MakeName.next("action_tests")
+
val actionLimit = Exec.sizeLimit
val parametersLimit = Parameters.sizeLimit
@@ -80,7 +84,9 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
val actions = (1 to 2).map { i =>
WhiskAction(namespace, aname(), jsDefault("??"), Parameters("x", "b"))
}.toList
- actions foreach { put(entityStore, _) }
+ actions foreach {
+ put(entityStore, _)
+ }
waitOnView(entityStore, WhiskAction, namespace, 2)
Get(collectionPath) ~> Route.seal(routes(creds)) ~> check {
status should be(OK)
@@ -140,7 +146,9 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
val actions = (1 to 2).map { i =>
WhiskAction(namespace, aname(), jsDefault("??"), Parameters("x", "b"))
}.toList
- actions foreach { put(entityStore, _) }
+ actions foreach {
+ put(entityStore, _)
+ }
waitOnView(entityStore, WhiskAction, namespace, 2)
Get(s"$collectionPath?docs=true") ~> Route.seal(routes(creds)) ~> check {
status should be(OK)
@@ -155,7 +163,9 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
val actions = (1 to 2).map { i =>
WhiskAction(namespace, aname(), jsDefault("??"), Parameters("x", "b"))
}.toList
- actions foreach { put(entityStore, _) }
+ actions foreach {
+ put(entityStore, _)
+ }
waitOnView(entityStore, WhiskAction, namespace, 2)
Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(creds)) ~>
check {
status should be(OK)
@@ -298,7 +308,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
action.exec.kind))
+ action.annotations ++ systemAnnotations(action.exec.kind))
val expectedWhiskActionMetaData = WhiskActionMetaData(
action.namespace,
@@ -308,7 +318,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskActionMetaData.execFieldName,
action.exec.kind))
+ action.annotations ++ systemAnnotations(action.exec.kind))
Put(s"$collectionPath/${action.name}", content) ~>
Route.seal(routes(creds)) ~> check {
status should be(OK)
@@ -428,9 +438,10 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
it should "put should reject request with malformed property exec" in {
implicit val tid = transid()
- val content = """|{"name":"name",
- |"publish":true,
- |"exec":""}""".stripMargin.parseJson.asJsObject
+ val content =
+ """|{"name":"name",
+ |"publish":true,
+ |"exec":""}""".stripMargin.parseJson.asJsObject
Put(s"$collectionPath/xxx", content) ~> Route.seal(routes(creds)) ~> check
{
val response = responseAs[String]
status should be(BadRequest)
@@ -542,7 +553,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
NODEJS10)))
+ action.annotations ++ systemAnnotations(NODEJS10)))
}
}
@@ -563,7 +574,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
Exec.BLACKBOX)))
+ action.annotations ++ systemAnnotations(BLACKBOX)))
response.exec shouldBe an[BlackBoxExec]
response.exec.asInstanceOf[BlackBoxExec].code shouldBe empty
}
@@ -586,7 +597,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
Exec.BLACKBOX)))
+ action.annotations ++ systemAnnotations(BLACKBOX)))
response.exec shouldBe an[BlackBoxExec]
val bb = response.exec.asInstanceOf[BlackBoxExec]
bb.code shouldBe Some(Inline("cc"))
@@ -594,7 +605,50 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
}
}
+ // this test is to ensure pre-existing actions can continue to to opt-out of
new system annotations
+ it should "preserve annotations on pre-existing actions" in {
+ implicit val tid = transid()
+ val action = WhiskAction(namespace, aname(), jsDefault(""))
+ put(entityStore, action, false) // install the action into the database
directly
+
+ var content = JsObject("exec" -> JsObject("code" -> "".toJson, "kind" ->
action.exec.kind.toJson))
+
+ Put(s"$collectionPath/${action.name}?overwrite=true", content) ~>
Route.seal(routes(creds)) ~> check {
+ status should be(OK)
+ val response = responseAs[WhiskAction]
+ response should be(
+ WhiskAction(
+ action.namespace,
+ action.name,
+ action.exec,
+ action.parameters,
+ action.limits,
+ action.version.upPatch,
+ action.publish,
+ action.annotations ++ Parameters(WhiskAction.execFieldName,
action.exec.kind)))
+ }
+
+ content =
"""{"annotations":[{"key":"a","value":"B"}]}""".parseJson.asJsObject
+
+ Put(s"$collectionPath/${action.name}?overwrite=true", content) ~>
Route.seal(routes(creds)) ~> check {
+ deleteAction(action.docid)
+ status should be(OK)
+ val response = responseAs[WhiskAction]
+ response should be(
+ WhiskAction(
+ action.namespace,
+ action.name,
+ action.exec,
+ action.parameters,
+ action.limits,
+ action.version.upPatch.upPatch,
+ action.publish,
+ action.annotations ++ Parameters("a", "B") ++
Parameters(WhiskAction.execFieldName, action.exec.kind)))
+ }
+ }
+
private implicit val fqnSerdes = FullyQualifiedEntityName.serdes
+
private def seqParameters(seq: Vector[FullyQualifiedEntityName]) =
Parameters("_actions", seq.map("/" + _.asString).toJson)
@@ -660,7 +714,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
NODEJS10)))
+ action.annotations ++ systemAnnotations(NODEJS10)))
}
}
@@ -700,7 +754,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
NODEJS10)))
+ action.annotations ++ systemAnnotations(NODEJS10)))
}
}
@@ -735,7 +789,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
kind)))
+ action.annotations ++ systemAnnotations(kind)))
}
stream.toString should include(s"caching ${CacheKey(action)}")
stream.toString should not include (s"invalidating ${CacheKey(action)}
on delete")
@@ -754,7 +808,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
kind)))
+ action.annotations ++ systemAnnotations(kind)))
}
stream.toString should include(s"serving from cache:
${CacheKey(action)}")
stream.reset()
@@ -772,7 +826,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version.upPatch,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
kind))
+ action.annotations ++ systemAnnotations(kind))
}
}
stream.toString should include(s"entity exists, will try to update
'$action'")
@@ -793,7 +847,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version.upPatch,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
kind)))
+ action.annotations ++ systemAnnotations(kind)))
}
stream.toString should include(s"invalidating ${CacheKey(action)}")
stream.reset()
@@ -847,7 +901,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
kind)))
+ action.annotations ++ systemAnnotations(kind)))
}
stream.toString should not include (s"invalidating ${CacheKey(action)}
on delete")
@@ -867,7 +921,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
kind)))
+ action.annotations ++ systemAnnotations(kind)))
}
stream.toString should include(s"serving from cache:
${CacheKey(action)}")
stream.toString should not include regex(notExpectedGetLog)
@@ -886,7 +940,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
kind)))
+ action.annotations ++ systemAnnotations(kind)))
}
stream.toString should include(s"invalidating ${CacheKey(action)}")
@@ -930,7 +984,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
JAVA_DEFAULT)))
+ action.annotations ++ systemAnnotations(JAVA_DEFAULT)))
}
stream.toString should not include (s"invalidating ${CacheKey(action)} on
delete")
@@ -950,7 +1004,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
JAVA_DEFAULT)))
+ action.annotations ++ systemAnnotations(JAVA_DEFAULT)))
}
stream.toString should include(s"serving from cache: ${CacheKey(action)}")
@@ -970,7 +1024,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
JAVA_DEFAULT)))
+ action.annotations ++ systemAnnotations(JAVA_DEFAULT)))
}
stream.toString should include(s"invalidating ${CacheKey(action)}")
stream.reset()
@@ -1019,7 +1073,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
kind)))
+ action.annotations ++ systemAnnotations(kind)))
}
stream.toString should include regex (expectedGetLog)
@@ -1063,7 +1117,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName, kind))
+ action.annotations ++ systemAnnotations(kind))
(0 until 5).par.map { i =>
Get(s"$collectionPath/$name") ~> Route.seal(routes(creds)(transid())) ~>
check {
@@ -1126,7 +1180,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version.upPatch,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
kind)))
+ action.annotations ++ systemAnnotations(kind)))
}
stream.toString should include regex (expectedPutLog)
stream.reset()
@@ -1144,7 +1198,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.limits,
action.version.upPatch,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
kind)))
+ action.annotations ++ systemAnnotations(kind)))
}
stream.toString should include(s"invalidating ${CacheKey(action)}")
stream.reset()
@@ -1191,7 +1245,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
actionOldSchema.limits,
actionOldSchema.version.upPatch,
actionOldSchema.publish,
- actionOldSchema.annotations ++ Parameters(WhiskAction.execFieldName,
NODEJS10)))
+ actionOldSchema.annotations ++ systemAnnotations(NODEJS10, create =
false)))
}
stream.toString should include regex (expectedPutLog)
@@ -1215,7 +1269,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
actionOldSchema.limits,
actionOldSchema.version.upPatch,
actionOldSchema.publish,
- actionOldSchema.annotations ++ Parameters(WhiskAction.execFieldName,
NODEJS10)))
+ actionOldSchema.annotations ++ systemAnnotations(NODEJS10, create =
false)))
}
}
@@ -1258,7 +1312,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
content.limits.get.logs.get,
content.limits.get.concurrency.get),
version = action.version.upPatch,
- annotations = action.annotations ++
Parameters(WhiskAction.execFieldName, NODEJS10))
+ annotations = action.annotations ++ systemAnnotations(NODEJS10,
create = false))
}
}
}
@@ -1279,7 +1333,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
action.exec,
content.parameters.get,
version = action.version.upPatch,
- annotations = action.annotations ++
Parameters(WhiskAction.execFieldName, NODEJS10))
+ annotations = action.annotations ++ systemAnnotations(NODEJS10,
false))
}
}
}
@@ -1327,7 +1381,7 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
it should "not invoke an action when final parameters are redefined" in {
implicit val tid = transid()
- val annotations =
Parameters(WhiskActionMetaData.finalParamsAnnotationName, JsBoolean(true))
+ val annotations = Parameters(WhiskAction.finalParamsAnnotationName,
JsBoolean(true))
val parameters = Parameters("a", "A") ++ Parameters("empty", JsNull)
val action = WhiskAction(namespace, aname(), jsDefault("??"), parameters =
parameters, annotations = annotations)
put(entityStore, action)
@@ -1568,26 +1622,26 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
val deprecatedKind = "test:2"
val customManifest = Some(s"""
- |{ "runtimes": {
- | "test": [
- | {
- | "kind": "$okKind",
- | "deprecated": false,
- | "default": true,
- | "image": {
- | "name": "xyz"
- | }
- | }, {
- | "kind": "$deprecatedKind",
- | "deprecated": true,
- | "image": {
- | "name": "xyz"
- | }
- | }
- | ]
- | }
- |}
- |""".stripMargin)
+ |{ "runtimes": {
+ | "test": [
+ | {
+ | "kind": "$okKind",
+ | "deprecated": false,
+ | "default": true,
+ | "image": {
+ | "name": "xyz"
+ | }
+ | }, {
+ | "kind": "$deprecatedKind",
+ | "deprecated": true,
+ | "image": {
+ | "name": "xyz"
+ | }
+ | }
+ | ]
+ | }
+ |}
+ |""".stripMargin)
ExecManifest.initialize(whiskConfig, customManifest)
val deprecatedManifest =
ExecManifest.runtimesManifest.resolveDefaultRuntime(deprecatedKind).get
@@ -1649,3 +1703,41 @@ class ActionsApiTests extends ControllerTestCommon with
WhiskActionsApi {
}
}
}
+
+@RunWith(classOf[JUnitRunner])
+class WhiskActionsApiTests extends FlatSpec with Matchers with ExecHelpers {
+ import WhiskActionsApi.amendAnnotations
+ import WhiskAction.provideApiKeyAnnotationName
+ import WhiskAction.execFieldName
+
+ val baseParams = Parameters("a", JsString("A")) ++ Parameters("b",
JsString("B"))
+ val keyTruthyAnnotation = Parameters(provideApiKeyAnnotationName,
JsBoolean(true))
+ val keyFalsyAnnotation = Parameters(provideApiKeyAnnotationName,
JsString("")) // falsy other than JsBoolean(false)
+ val execAnnotation = Parameters(execFieldName, JsString("foo"))
+ val exec: Exec = jsDefault("??")
+
+ it should "add key annotation if it is not present already" in {
+ Seq(Parameters(), baseParams).foreach { p =>
+ amendAnnotations(p, exec) shouldBe {
+ p ++ Parameters(provideApiKeyAnnotationName, JsBoolean(false)) ++
+ Parameters(WhiskAction.execFieldName, exec.kind)
+ }
+ }
+ }
+
+ it should "not add key annotation if already present regardless of value" in
{
+ Seq(keyTruthyAnnotation, keyFalsyAnnotation).foreach { p =>
+ amendAnnotations(p, exec) shouldBe {
+ p ++ Parameters(WhiskAction.execFieldName, exec.kind)
+ }
+ }
+ }
+
+ it should "override system annotation as necessary" in {
+ amendAnnotations(baseParams ++ execAnnotation, exec) shouldBe {
+ baseParams ++ Parameters(provideApiKeyAnnotationName, JsBoolean(false))
++ Parameters(
+ WhiskAction.execFieldName,
+ exec.kind)
+ }
+ }
+}
diff --git
a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/ControllerTestCommon.scala
b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/ControllerTestCommon.scala
index 9e58639..56df5e2 100644
---
a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/ControllerTestCommon.scala
+++
b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/ControllerTestCommon.scala
@@ -85,6 +85,15 @@ protected trait ControllerTestCommon
}
}
+ def systemAnnotations(kind: String, create: Boolean = true): Parameters = {
+ val base = if (create) {
+ Parameters(WhiskAction.provideApiKeyAnnotationName, JsBoolean(false))
+ } else {
+ Parameters()
+ }
+ base ++ Parameters(WhiskAction.execFieldName, kind)
+ }
+
val entityStore = WhiskEntityStore.datastore()
val authStore = WhiskAuthStore.datastore()
val logStore = SpiLoader.get[LogStoreProvider].instance(actorSystem)
diff --git
a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/PackageActionsApiTests.scala
b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/PackageActionsApiTests.scala
index 59bc153..d1033b0 100644
---
a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/PackageActionsApiTests.scala
+++
b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/PackageActionsApiTests.scala
@@ -162,7 +162,7 @@ class PackageActionsApiTests extends ControllerTestCommon
with WhiskActionsApi {
action.limits,
action.version,
action.publish,
- action.annotations ++ Parameters(WhiskAction.execFieldName,
NODEJS10)))
+ action.annotations ++ systemAnnotations(NODEJS10)))
}
}
diff --git
a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/WebActionsApiTests.scala
b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/WebActionsApiTests.scala
index 81e91db..594e9a2 100644
---
a/tests/src/test/scala/org/apache/openwhisk/core/controller/test/WebActionsApiTests.scala
+++
b/tests/src/test/scala/org/apache/openwhisk/core/controller/test/WebActionsApiTests.scala
@@ -210,7 +210,7 @@ trait WebActionsApiBaseTests extends ControllerTestCommon
with BeforeAndAfterEac
requireAuthentication: Boolean = false,
requireAuthenticationAsBoolean: Boolean = true) = {
- val annotations =
Parameters(WhiskActionMetaData.finalParamsAnnotationName, JsBoolean(true))
+ val annotations = Parameters(WhiskAction.finalParamsAnnotationName,
JsBoolean(true))
WhiskAction(
namespace,
name,
diff --git
a/tests/src/test/scala/org/apache/openwhisk/core/entity/test/SchemaTests.scala
b/tests/src/test/scala/org/apache/openwhisk/core/entity/test/SchemaTests.scala
index 802c0a2..c2fbea5 100644
---
a/tests/src/test/scala/org/apache/openwhisk/core/entity/test/SchemaTests.scala
+++
b/tests/src/test/scala/org/apache/openwhisk/core/entity/test/SchemaTests.scala
@@ -620,6 +620,7 @@ class SchemaTests extends FlatSpec with BeforeAndAfter with
ExecHelpers with Mat
}
behavior of "Parameter"
+
it should "properly deserialize and reserialize JSON" in {
val json = Seq[JsValue](
JsArray(JsObject("key" -> "k".toJson, "value" -> "v".toJson)),
@@ -668,7 +669,19 @@ class SchemaTests extends FlatSpec with BeforeAndAfter
with ExecHelpers with Mat
an[IllegalArgumentException] should be thrownBy Parameters(null, "")
an[IllegalArgumentException] should be thrownBy Parameters(null, " ")
an[IllegalArgumentException] should be thrownBy Parameters(null)
+ }
+
+ it should "recognize truthy values" in {
+ Seq(JsBoolean(true), JsNumber(1), JsString("x")).foreach { v =>
+ Parameters("x", v).isTruthy("x") shouldBe true
+ }
+
+ Seq(JsBoolean(false), JsNumber(0), JsString(""), JsNull).foreach { v =>
+ Parameters("x", v).isTruthy("x") shouldBe false
+ }
+ Parameters("x", JsBoolean(true)).isTruthy("y") shouldBe false
+ Parameters("x", JsBoolean(true)).isTruthy("y", valueForNonExistent = true)
shouldBe true
}
it should "serialize to json" in {
diff --git a/tests/src/test/scala/system/basic/WskActionTests.scala
b/tests/src/test/scala/system/basic/WskActionTests.scala
index 693ebb9..8e6bf3d 100644
--- a/tests/src/test/scala/system/basic/WskActionTests.scala
+++ b/tests/src/test/scala/system/basic/WskActionTests.scala
@@ -24,6 +24,7 @@ import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import common._
import common.rest.WskRestOperations
+import org.apache.openwhisk.core.entity.WhiskAction
import org.apache.commons.io.FileUtils
import spray.json._
import spray.json.DefaultJsonProtocol._
@@ -187,7 +188,8 @@ class WskActionTests extends TestHelpers with
WskTestHelpers with JsHelpers with
JsObject("key" -> JsString("copiedAnnot2"), "value" ->
JsBoolean(false)),
JsObject("key" -> JsString("copiedAnnot1"), "value" ->
JsString("copiedAnnotValue1")),
JsObject("key" -> JsString("origAnnot2"), "value" -> JsBoolean(true)),
- JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")))
+ JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")),
+ JsObject("key" -> WhiskAction.provideApiKeyAnnotationName.toJson,
"value" -> JsBoolean(false)))
assetHelper.withCleaner(wsk.action, origName) {
val file = Some(TestUtils.getTestActionFilename("echo.js"))
@@ -248,7 +250,10 @@ class WskActionTests extends TestHelpers with
WskTestHelpers with JsHelpers with
val child = "wc"
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
- action.create(name, Some(TestUtils.getTestActionFilename("wcbin.js")))
+ action.create(
+ name,
+ Some(TestUtils.getTestActionFilename("wcbin.js")),
+ annotations = Map(WhiskAction.provideApiKeyAnnotationName ->
JsBoolean(true)))
}
assetHelper.withCleaner(wsk.action, child) { (action, _) =>
action.create(child, Some(TestUtils.getTestActionFilename("wc.js")))
diff --git a/tests/src/test/scala/system/basic/WskRestBasicTests.scala
b/tests/src/test/scala/system/basic/WskRestBasicTests.scala
index b279a29..ca6796e 100644
--- a/tests/src/test/scala/system/basic/WskRestBasicTests.scala
+++ b/tests/src/test/scala/system/basic/WskRestBasicTests.scala
@@ -33,6 +33,7 @@ import common.rest.RestResult
import spray.json._
import spray.json.DefaultJsonProtocol._
import org.apache.openwhisk.core.containerpool.Container
+import org.apache.openwhisk.core.entity.WhiskAction
import org.apache.openwhisk.http.Messages
@RunWith(classOf[JUnitRunner])
@@ -144,6 +145,7 @@ class WskRestBasicTests extends TestHelpers with
WskTestHelpers with WskActorSys
"value" -> JsArray(
JsObject("name" -> JsString("paramName1"), "description" ->
JsString("Parameter description 1")),
JsObject("name" -> JsString("paramName2"), "description" ->
JsString("Parameter description 2")))),
+ JsObject("key" -> WhiskAction.provideApiKeyAnnotationName.toJson,
"value" -> JsBoolean(false)),
JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")))
}
@@ -343,6 +345,7 @@ class WskRestBasicTests extends TestHelpers with
WskTestHelpers with WskActorSys
result.getFieldJsValue("parameters") shouldBe JsArray(
JsObject("key" -> JsString("payload"), "value" -> JsString("test")))
result.getFieldJsValue("annotations") shouldBe JsArray(
+ JsObject("key" -> WhiskAction.provideApiKeyAnnotationName.toJson,
"value" -> JsBoolean(false)),
JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")))
result.getFieldJsValue("limits") shouldBe JsObject(
"timeout" -> JsNumber(60000),
@@ -449,8 +452,8 @@ class WskRestBasicTests extends TestHelpers with
WskTestHelpers with WskActorSys
"value" -> JsArray(
JsObject("name" -> JsString("paramName1"), "description" ->
JsString("Parameter description 1")),
JsObject("name" -> JsString("paramName2"), "description" ->
JsString("Parameter description 2")))),
+ JsObject("key" -> WhiskAction.provideApiKeyAnnotationName.toJson,
"value" -> JsBoolean(false)),
JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")))
-
}
it should "create an action with a name that contains spaces" in
withAssetCleaner(wskprops) { (wp, assetHelper) =>