This proposal contains one major error I realized last night. Pre-hooks run *before* verbs are invoked. That means that a pre-hook *can't* be declared in the config file: config files are read by the verb implementation, if the command takes one as a parameter. (It's possible to latch another phase of hook execution into the context, so that when a config gets loaded, it's checked for hooks, but I think that that is really ugly.)
I'm currently implementing a version of command hooks that only includes hard-wired global hooks. Later, when I get to implementing a .aurora file with defaults, we can add hooks there, which will allow per-user/per-project hooks. -Mark On Tue, Mar 25, 2014 at 3:52 PM, Mark Chu-Carroll <mchucarr...@apache.org>wrote: > Since I'm still stuck waiting on reviews, I figured I'd move on to one of > the next things on my agenda - Aurora-270, hooks for client commands. > Here's what Id like to implement - please beat me up with any criticisms > you have. > > We've got hooks in the client that surround API calls. These are pretty > awkward, > because they don't correlate with user actions. For example, suppose we > wanted a policy that said users weren't allowed to kill all instances of a > production job at once. > > Right now, all that we could hook would be the "killJob" api call. But > kill (at least in newer versions of the client) normally runs in > batches. If a user called killall, what we would see on the API level > is a series of "killJob" calls, each of which specified a batch of > instances. We woudn't be able to distinguish between really killing all > instances of > a job (which is forbidden under this policy), and carefully killing in > batches (which is permitted.) In each case, the hook would just see a > series of API calls, and couldn't find out what the actual command being > executed was! > > For most policy enforcement, what we really want to be able to do is look > at > and vet the commands that a user is performing, not the API calls that the > client uses to implement those commands. > > So I propose that we add a new kind of hooks, which surround noun/verb > commands. A hook will register itself to handle a collection of (noun, > verb) pairs. Whenever any of those noun/verb commands are invoked, the > hooks methods will be called around the execution of the verb. A pre-hook > will have the ability to reject a command, preventing the verb from being > executed. > > These hooks will be registered two ways: > * By the global variable `COMMAND_HOOKS` in a configuration file; > * By a python method call. Hooks registered this way are, effectively, > hardwired into the client executable. > > Commands registered by the python call are called _global_ hooks, because > they > will run for all configurations, whether or not they specify any hooks in > the > configuration file. > > The order of execution of hooks is unspecified: they may be called in > any order. There is no way to guarantee that one hook will execute > before some other hook. > > Users can disable hooks by adding the option > `--disable-all-command-hooks`. The fact that they disabled the hooks will > be logged. > > > The API: > > class Hook(object) > def get_nouns(self): > """Return the nouns that have verbs that should invoke this > hook.""" > > def get_verbs(self, noun): > """Return the verbs for a particular noun that should invoke his > hook.""" > > @abstractmethod > def pre_command(self, noun, verb, context, commandline): > """Execute a hook before invoking a verb. > * noun: the noun being invoked. > * verb: the verb being invoked. > * context: the context object that will be used to invoke the verb. > The options object will be initialized before calling the hook > * commandline: the original argv collection used to invoke the > client. > Returns: True if the command should be allowed to proceed; False if the > command > should be rejected. > """ > > def post_command(self, noun, verb, context, commandline, result): > """Execute a hook after invoking a verb. > * noun: the noun being invoked. > * verb: the verb being invoked. > * context: the context object that will be used to invoke the verb. > The options object will be initialized before calling the hook > * commandline: the original argv collection used to invoke the > client. > * result: the result code returned by the verb. > Returns: nothing > """ > >