Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add binding annotation to record an action path not resolved #4211

Merged
merged 9 commits into from
May 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ import org.apache.openwhisk.core.entity.size.SizeString
* - EntityPath: the namespace and package where the entity is located
* - EntityName: the name of the entity
* - Version: the semantic version of the resource
* - Binding : the entity path of the package binding, it can be used by entities that support binding
*/
protected[core] case class FullyQualifiedEntityName(path: EntityPath, name: EntityName, version: Option[SemVer] = None)
protected[core] case class FullyQualifiedEntityName(path: EntityPath,
name: EntityName,
version: Option[SemVer] = None,
binding: Option[EntityPath] = None)
extends ByteSizeable {
private val qualifiedName: String = path + EntityPath.PATHSEP + name

Expand Down Expand Up @@ -60,7 +64,7 @@ protected[core] case class FullyQualifiedEntityName(path: EntityPath, name: Enti

protected[core] object FullyQualifiedEntityName extends DefaultJsonProtocol {
// must use jsonFormat with explicit field names and order because class extends a trait
private val caseClassSerdes = jsonFormat(FullyQualifiedEntityName.apply _, "path", "name", "version")
private val caseClassSerdes = jsonFormat(FullyQualifiedEntityName.apply _, "path", "name", "version", "binding")

protected[core] val serdes = new RootJsonFormat[FullyQualifiedEntityName] {
def write(n: FullyQualifiedEntityName) = caseClassSerdes.write(n)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ case class WhiskActionMetaData(namespace: EntityPath,
limits: ActionLimits = ActionLimits(),
version: SemVer = SemVer(),
publish: Boolean = false,
annotations: Parameters = Parameters())
annotations: Parameters = Parameters(),
binding: Option[EntityPath] = None)
extends WhiskActionLikeMetaData(name) {

require(exec != null, "exec undefined")
Expand All @@ -209,7 +210,8 @@ case class WhiskActionMetaData(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[WhiskActionMetaData](rev)
def inherit(p: Parameters, binding: Option[EntityPath] = None) =
copy(parameters = p ++ parameters, binding = binding).revision[WhiskActionMetaData](rev)

/**
* Resolves sequence components if they contain default namespace.
Expand All @@ -228,12 +230,20 @@ case class WhiskActionMetaData(namespace: EntityPath,
def toExecutableWhiskAction = exec match {
case execMetaData: ExecMetaData =>
Some(
ExecutableWhiskActionMetaData(namespace, name, execMetaData, parameters, limits, version, publish, annotations)
ExecutableWhiskActionMetaData(
namespace,
name,
execMetaData,
parameters,
limits,
version,
publish,
annotations,
binding)
.revision[ExecutableWhiskActionMetaData](rev))
case _ =>
None
}

}

/**
Expand All @@ -255,6 +265,7 @@ case class WhiskActionMetaData(namespace: EntityPath,
* @param version the semantic version
* @param publish true to share the action or false otherwise
* @param annotation the set of annotations to attribute to the action
* @param binding the path of the package binding if any
* @throws IllegalArgumentException if any argument is undefined
*/
@throws[IllegalArgumentException]
Expand All @@ -265,7 +276,8 @@ case class ExecutableWhiskAction(namespace: EntityPath,
limits: ActionLimits = ActionLimits(),
version: SemVer = SemVer(),
publish: Boolean = false,
annotations: Parameters = Parameters())
annotations: Parameters = Parameters(),
binding: Option[EntityPath] = None)
extends WhiskActionLike(name) {

require(exec != null, "exec undefined")
Expand All @@ -283,7 +295,8 @@ case class ExecutableWhiskAction(namespace: EntityPath,
}

def toWhiskAction =
WhiskAction(namespace, name, exec, parameters, limits, version, publish, annotations).revision[WhiskAction](rev)
WhiskAction(namespace, name, exec, parameters, limits, version, publish, annotations)
.revision[WhiskAction](rev)
}

@throws[IllegalArgumentException]
Expand All @@ -294,7 +307,8 @@ case class ExecutableWhiskActionMetaData(namespace: EntityPath,
limits: ActionLimits = ActionLimits(),
version: SemVer = SemVer(),
publish: Boolean = false,
annotations: Parameters = Parameters())
annotations: Parameters = Parameters(),
binding: Option[EntityPath] = None)
extends WhiskActionLikeMetaData(name) {

require(exec != null, "exec undefined")
Expand All @@ -303,6 +317,13 @@ case class ExecutableWhiskActionMetaData(namespace: EntityPath,
def toWhiskAction =
WhiskActionMetaData(namespace, name, exec, parameters, limits, version, publish, annotations)
.revision[WhiskActionMetaData](rev)

/**
* Some fully qualified name only if there's a binding, else None.
*/
def bindingFullyQualifiedName: Option[FullyQualifiedEntityName] =
binding.map(ns => FullyQualifiedEntityName(ns, name, None))

}

object WhiskAction extends DocumentFactory[WhiskAction] with WhiskEntityQueries[WhiskAction] with DefaultJsonProtocol {
Expand Down Expand Up @@ -504,7 +525,9 @@ object WhiskAction extends DocumentFactory[WhiskAction] with WhiskEntityQueries[
// fully resolved name for the action
val fqnAction = resolvedPkg.fullyQualifiedName(withVersion = false).add(actionName)
// get the whisk action associate with it and inherit the parameters from the package/binding
WhiskAction.get(entityStore, fqnAction.toDocId) map { _.inherit(resolvedPkg.parameters) }
WhiskAction.get(entityStore, fqnAction.toDocId) map {
_.inherit(resolvedPkg.parameters)
}
}
}
}
Expand All @@ -526,7 +549,8 @@ object WhiskActionMetaData
"limits",
"version",
"publish",
"annotations")
"annotations",
"binding")

override val cacheEnabled = true

Expand Down Expand Up @@ -579,7 +603,10 @@ object WhiskActionMetaData
val fqnAction = resolvedPkg.fullyQualifiedName(withVersion = false).add(actionName)
// get the whisk action associate with it and inherit the parameters from the package/binding
WhiskActionMetaData.get(entityStore, fqnAction.toDocId) map {
_.inherit(resolvedPkg.parameters)
_.inherit(
resolvedPkg.parameters,
if (fullyQualifiedName.path.equals(resolvedPkg.fullPath)) None
else Some(fullyQualifiedName.path))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ object WhiskActivation

/** Some field names for annotations */
val pathAnnotation = "path"
val bindingAnnotation = "binding"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will check down below but this raises the opportunity to have the name in the activation be that of the binding, and the annotation is that of the package.

Copy link
Member Author

@upgle upgle Mar 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, I don't understand this.

In the example below, I think the name triple in the activation can not be the name of the binding whisk.system/p2

  • there is an action whisk.system/pkg1/triple
  • and the whisk.system/p2 is binding the whisk.system/pkg1
  • and i invoke an action whisk.system/p2/triple
{
    "namespace": "whisk.system",
    "name": "triple",
    "version": "0.0.1",
    "subject": "whisk.system",
    "activationId": "b86606b16b8a4371a606b16b8a437182",
    "start": 1552900408086,
    "end": 1552900408090,
    "duration": 4,
    "annotations": [
        {
            "key": "binding",
            "value": "whisk.system/p2"
        },
        {
            "key": "path",
            "value": "whisk.system/pkg1/triple"
        },
...

val kindAnnotation = "kind"
val limitsAnnotation = "limits"
val topmostAnnotation = "topmost"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,10 @@ import scala.language.postfixOps
import scala.util.{Failure, Success, Try}
import org.apache.kafka.common.errors.RecordTooLargeException
import akka.actor.ActorSystem
import akka.http.scaladsl.model.HttpMethod
import akka.http.scaladsl.model.StatusCodes._
import akka.http.scaladsl.server.RequestContext
import akka.http.scaladsl.server.RouteResult
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonMarshaller
import akka.http.scaladsl.unmarshalling._
import spray.json._
import spray.json.DefaultJsonProtocol._
Expand Down Expand Up @@ -173,14 +171,10 @@ trait WhiskActionsApi extends WhiskCollectionAPI with PostActionActivation with
onComplete(entitlementProvider.check(user, right, packageResource)) {
case Success(_) =>
getEntity(WhiskPackage.get(entityStore, packageDocId), Some {
if (right == Privilege.READ || right == Privilege.ACTIVATE) {
// need to merge package with action, hence authorize subject for package
// access (if binding, then subject must be authorized for both the binding
// and the referenced package)
//
// NOTE: it is an error if either the package or the action does not exist,
// the former manifests as unauthorized and the latter as not found
mergeActionWithPackageAndDispatch(m, user, EntityName(innername)) _
if (right == Privilege.READ || right == Privilege.ACTIVATE) { wp: WhiskPackage =>
val actionResource = Resource(wp.fullPath, collection, Some(innername))
dispatchOp(user, right, actionResource)

} else {
// these packaged action operations do not need merging with the package,
// but may not be permitted if this is a binding, or if the subject does
Expand Down Expand Up @@ -253,7 +247,7 @@ trait WhiskActionsApi extends WhiskCollectionAPI with PostActionActivation with
'result ? false,
'timeout.as[FiniteDuration] ? WhiskActionsApi.maxWaitForBlockingActivation) { (blocking, result, waitOverride) =>
entity(as[Option[JsObject]]) { payload =>
getEntity(WhiskActionMetaData.get(entityStore, entityName.toDocId), Some {
getEntity(WhiskActionMetaData.resolveActionAndMergeParameters(entityStore, entityName), Some {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@style95 the action is resolved here after it's dispatched.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, this is the similar way which is taken for web action.

act: WhiskActionMetaData =>
// resolve the action --- special case for sequences that may contain components with '_' as default package
val action = act.resolve(user.namespace)
Expand Down Expand Up @@ -355,18 +349,19 @@ trait WhiskActionsApi extends WhiskCollectionAPI with PostActionActivation with
parameter('code ? true) { code =>
code match {
case true =>
getEntity(WhiskAction.get(entityStore, entityName.toDocId), Some { action: WhiskAction =>
getEntity(WhiskAction.resolveActionAndMergeParameters(entityStore, entityName), Some { action: WhiskAction =>
val mergedAction = env map {
action inherit _
} getOrElse action
complete(OK, mergedAction)
})
case false =>
getEntity(WhiskActionMetaData.get(entityStore, entityName.toDocId), Some { action: WhiskActionMetaData =>
val mergedAction = env map {
action inherit _
} getOrElse action
complete(OK, mergedAction)
getEntity(WhiskActionMetaData.resolveActionAndMergeParameters(entityStore, entityName), Some {
action: WhiskActionMetaData =>
val mergedAction = env map {
action inherit _
} getOrElse action
complete(OK, mergedAction)
})
}
}
Expand Down Expand Up @@ -595,38 +590,6 @@ trait WhiskActionsApi extends WhiskCollectionAPI with PostActionActivation with
})
}

/**
* Constructs a WhiskPackage that is a merger of a package with its packing binding (if any).
* This resolves a reference versus an actual package and merge parameters as needed.
* Once the package is resolved, the operation is dispatched to the action in the package
* namespace.
*/
private def mergeActionWithPackageAndDispatch(method: HttpMethod,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@upgle Could you elaborate the reason to remove this method a little bit?
It would help people who are not familiar with this change like me to get the intention more easily.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an action is resolved before it's dispatched, there is no way to know its original name in the activate method because it's already resolved.
So, I modified it to resolve inside the activate method after it has been dispatched.

override def activate(user: Identity, entityName: FullyQualifiedEntityName, env: Option[Parameters])(
    implicit transid: TransactionId) = {	    implicit transid: TransactionId)

user: Identity,
action: EntityName,
ref: Option[WhiskPackage] = None)(wp: WhiskPackage)(
implicit transid: TransactionId): RequestContext => Future[RouteResult] = {
wp.binding map {
case b: Binding =>
val docid = b.fullyQualifiedName.toDocId
logging.debug(this, s"fetching package '$docid' for reference")
// already checked that subject is authorized for package and binding;
// this fetch is redundant but should hit the cache to ameliorate cost
getEntity(WhiskPackage.get(entityStore, docid), Some {
mergeActionWithPackageAndDispatch(method, user, action, Some { wp }) _
})
} getOrElse {
// a subject has implied rights to all resources in a package, so dispatch
// operation without further entitlement checks
val params = { ref map { _ inherit wp.parameters } getOrElse wp } parameters
val ns = wp.namespace.addPath(wp.name) // the package namespace
val resource = Resource(ns, collection, Some { action.asString }, Some { params })
val right = collection.determineRight(method, resource.entity)
logging.debug(this, s"merged package parameters and rebased action to '$ns")
dispatchOp(user, right, resource)
}
}

/**
* Checks that the sequence is not cyclic and that the number of atomic actions in the "inlined" sequence is lower than max allowed.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ protected[actions] trait PrimitiveActions {

val message = ActivationMessage(
transid,
FullyQualifiedEntityName(action.namespace, action.name, Some(action.version)),
FullyQualifiedEntityName(action.namespace, action.name, Some(action.version), action.binding),
action.rev,
user,
activationId, // activation id created here
Expand Down Expand Up @@ -539,6 +539,10 @@ protected[actions] trait PrimitiveActions {
Parameters(WhiskActivation.causedByAnnotation, JsString(Exec.SEQUENCE))
}

// set binding if invoked action is in a package binding
val binding =
session.action.binding.map(f => Parameters(WhiskActivation.bindingAnnotation, JsString(f.asString)))

val end = Instant.now(Clock.systemUTC())

// create the whisk activation
Expand All @@ -558,7 +562,7 @@ protected[actions] trait PrimitiveActions {
Parameters(WhiskActivation.pathAnnotation, JsString(session.action.fullyQualifiedName(false).asString)) ++
Parameters(WhiskActivation.kindAnnotation, JsString(Exec.SEQUENCE)) ++
Parameters(WhiskActivation.conductorAnnotation, JsTrue) ++
causedBy ++
causedBy ++ binding ++
sequenceLimits,
duration = Some(session.duration))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ protected[actions] trait SequenceActions {
Some(Parameters(WhiskActivation.causedByAnnotation, JsString(Exec.SEQUENCE)))
} else None

// set binding if an invoked action is in a package binding
val binding = action.binding map { path =>
Parameters(WhiskActivation.bindingAnnotation, JsString(path.asString))
}

// create the whisk activation
WhiskActivation(
namespace = user.namespace.name.toPath,
Expand All @@ -223,7 +228,7 @@ protected[actions] trait SequenceActions {
annotations = Parameters(WhiskActivation.topmostAnnotation, JsBoolean(topmost)) ++
Parameters(WhiskActivation.pathAnnotation, JsString(action.fullyQualifiedName(false).asString)) ++
Parameters(WhiskActivation.kindAnnotation, JsString(Exec.SEQUENCE)) ++
causedBy ++
causedBy ++ binding ++
sequenceLimits,
duration = Some(accounting.duration))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,9 @@ object ContainerProxy {
initInterval.map(initTime => Parameters(WhiskActivation.initTimeAnnotation, initTime.duration.toMillis.toJson))
}

val binding =
job.msg.action.binding.map(f => Parameters(WhiskActivation.bindingAnnotation, JsString(f.asString)))

WhiskActivation(
activationId = job.msg.activationId,
namespace = job.msg.user.namespace.name.toPath,
Expand All @@ -764,7 +767,7 @@ object ContainerProxy {
Parameters(WhiskActivation.pathAnnotation, JsString(job.action.fullyQualifiedName(false).asString)) ++
Parameters(WhiskActivation.kindAnnotation, JsString(job.action.exec.kind)) ++
Parameters(WhiskActivation.timeoutAnnotation, JsBoolean(isTimeout)) ++
causedBy ++ initTime
causedBy ++ initTime ++ binding
})
}
}
Expand Down
1 change: 1 addition & 0 deletions docs/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ and must be present and explicitly set to `true` to have an affect. The annotati
The system decorates activation records with annotations as well. They are:

* `path`: the fully qualified path name of the action that generated the activation. Note that if this activation was the result of an action in a package binding, the path refers to the parent package.
* `binding`: the entity path of the package binding. Note that this is only present for actions in a package binding.
* `kind`: the kind of action executed, and one of the support OpenWhisk runtime kinds.
* `limits`: the time, memory and log limits that this activation were subject to.

Expand Down
Loading