On Fri, Jul 10, 2015 at 5:33 PM, Mathieu Bridon <bochecha(a)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