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) =>

Reply via email to