Really nice to see this documented.

+1 ack


On Fri, Jul 10, 2015 at 5:36 PM, Adam Miller <maxamillion@fedoraproject.org> wrote:
On Fri, Jul 10, 2015 at 5:33 PM, Mathieu Bridon <bochecha@daitauha.fr> wrote:
> ---
>
> Koji supports different types of plugins.
>
> This commit documents the 3 types that I know of, but I have absolutely
> no idea if there are others.
>
>  docs/Writing_a_plugin.md | 153 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 153 insertions(+)
>  create mode 100644 docs/Writing_a_plugin.md
>
> diff --git a/docs/Writing_a_plugin.md b/docs/Writing_a_plugin.md
> new file mode 100644
> index 0000000..36cddd9
> --- /dev/null
> +++ b/docs/Writing_a_plugin.md
> @@ -0,0 +1,153 @@
> +# Writing Koji plugins
> +
> +Depending on what you are trying to do, there are different ways to write a
> +Koji plugin.
> +
> +Each is described in this file, by use case.
> +
> +## Adding new task types
> +
> +Koji can do several things, for example build RPMs, or live CDs. Those are
> +types of tasks which Koji knows about.
> +
> +If you need to do something which Koji does not know yet how to do, you could
> +create a Koji Builder plugin.
> +
> +Such a plugin would minimally look like this:
> +
> +    from koji.tasks import BaseTaskHandler
> +
> +    class MyTask(BaseTaskHandler):
> +        Methods = ['mytask']
> +        _taskWeight = 2.0
> +
> +        def handler(self, arg1, arg2, kwarg1=None):
> +            self.logger.debug("Running my task...")
> +
> +            # Here is where you actually do something
> +
> +A few explanations on what goes on here:
> +
> +* Your task needs to inherit from `koji.tasks.BaseTaskHandler`
> +* Your task must have a `Methods` attribute, which is a list of the method
> +  names your task can handle.
> +* You can specify the weight of your task with the `_taskWeight` attribute.
> +  The more intensive (CPU, IO, ...) your task is, the higher this number
> +  should be.
> +* The task object has a `logger` attribute, which is a Python logger with the
> +  usual `debug`, `info`, `warning` and `error` methods. The messages you send
> +  with it will end up in the Koji Builder logs (`kojid.log`)
> +* Your task must have a `handler()` method. That is the method Koji will call
> +  to run your task. It is the method that should actually do what you need. It
> +  can have as many positional and named arguments as you want.
> +
> +Save your plugin as e.g `mytask.py`, then install it in the Koji Builder
> +plugins folder: `/usr/lib/koji-builder-plugins/`
> +
> +Finally, edit the Koji Builder config file, `/etc/kojid/kojid.conf`:
> +
> +    # A space-separated list of plugins to enable
> +    plugins = mytask
> +
> +Restart the Koji Builder service, and your plugin will be enabled.
> +
> +You can try running a task from your new task type with the command-line:
> +
> +    $ koji make-task mytask arg1 arg2 kwarg1
> +
> +## Exporting new API methods over XMLRPC
> +
> +Koji clients talk to the Koji Hub via an XMLRPC API.
> +
> +It is sometimes desirable to add to that API, so that clients can request
> +things Koji does not expose right now.
> +
> +Such a plugin would minimally look like this:
> +
> +    def mymethod(arg1, arg2, kwarg1=None):
> +        # Here is where you actually do something
> +
> +    mymethod.exported = True
> +
> +A few explanations on what goes on here:
> +
> +* Your plugin is just a method, with whatever positional and/or named
> +  arguments you need.
> +* You must export your method by setting its `exported` attribute to `True`
> +* The `context.session.assertPerm()` is how you ensure that the
> +
> +Save your plugin as e.g `mymethod.py`, then install it in the Koji Hub plugins
> +folder: `/usr/lib/koji-hub-plugins/`
> +
> +Finally, edit the Koji Hub config file, `/etc/koji-hub/hub.conf`:
> +
> +    # A space-separated list of plugins to enable
> +    Plugins = mymethod
> +
> +Restart the Koji Hub service, and your plugin will be enabled.
> +
> +You can try calling the new XMLRPC API with the Python client library:
> +
> +    >>> import koji
> +    >>> session = koji.ClientSession("http://koji/example.org/kojihub")
> +    >>> session.mymethod(arg1, arg2, kwarg1='some value')
> +
> +### Ensuring the user has the required permissions
> +
> +If you want your new XMLRPC API to require specific permissions from the user,
> +all you need to do is add the following to your method:
> +
> +    from koji.context import context
> +
> +    def mymethod(arg1, arg2, kwarg1=None):
> +        context.session.assertPerm("admin")
> +
> +        # Here is where you actually do something
> +
> +    mymethod.exported = True
> +
> +In the example above, Koji will ensure that the user is an administrator. You
> +could of course create your own permission, and check for that.
> +
> +## Running code automatically triggered on events
> +
> +You might want to run something automatically when something else happens in
> +Koji.
> +
> +A typical example is to automatically sign a package right after a build
> +finished. Another would be to send a notification to a message bus after any
> +kind of event.
> +
> +This can be achieved with a plugin, which would look minimally as follows:
> +
> +    from koji.plugin import callback
> +
> +    @callback('preTag', 'postTag')
> +    def mycallback(cbtype, tag, build, user, force=False):
> +        # Here is where you actually do something
> +
> +A few explanations on what goes on here:
> +
> +* The `@callback` decorator allows you to declare which events should trigger
> +  your function. You can pass as many as you want. For a list of supported
> +  events, see `koji/plugins.py`.
> +* The arguments of the function depend on the event you subscribed to. As a
> +  result, you need to know how it will be called by Koji. You probably should
> +  use `*kwargs` to be safe. You can see how callbacks are called in the
> +  `hub/kojihub.py` file, search for calls of the `run_callbacks` function.
> +
> +Save your plugin as e.g `mycallback.py`, then install it in the Koji Hub
> +plugins folder: `/usr/lib/koji-hub-plugins`
> +
> +Finally, edit the Koji Hub config file, `/etc/koji-hub/hub.conf`:
> +
> +    # A space-separated list of plugins to enable
> +    Plugins = mycallback
> +
> +Restart the Koji Hub service, and your plugin will be enabled.
> +
> +You can try triggering your callback plugin with the command-line. For
> +example, if you registered a callback for the `postTag` event, try tagging a
> +build:
> +
> +    $ koji tag-build mytag mypkg-1.0-1
> --
> 2.4.3
>

Can I +1 more than once? ;)

But no, seriously this is amazing. Thank you so much for writing this.

For whatever it's worth: +1

-AdamM



--

-Jon Disnard