Plugin Conventions¶
Configuration¶
Validation¶
It is up to the plugin writer to determine what configuration values are necessary for the plugin to function.
Pulp performs no validation on the configuration for a plugin. The validate_config
method in each plugin subclass is used to verify the user-entered values for a repository.
This is called when the plugin is first added to the repository and on all subsequent
configuration changes. The configuration is sent to the Pulp server as a JSON document through its
REST APIs and will be deserialized before being passed to the plugin.
This call must ensure the configuration to be used when running the plugin will be valid for the repository. If this call indicates an invalid configuration, the plugin will not be added to the repository (for the add call) or the configuration changes will not be saved to the database (for the update configuration call).
The docstring for the method describes the format of the returned value.
Format¶
Each call into a plugin passes the configuration the call should use for its execution.
The configuration is contained in a pulp.plugins.config.PluginCallConfiguration
instance.
This object is a wrapper on top of the three different locations a configuration value
can come from:
Overrides - Most calls allow the user to specify configuration values as a parameter when they are invoked. These values are made available to the plugin for the operation’s execution, however they are not saved in the server.
Repository-level - When an importer or distributor is attached to a repository, the Pulp server saves the configuration for that plugin with the repository. These values are only used for operations on that repository. For example, if an importer is configured to synchronize from an external feed, the URL of that feed would be stored on a per repository basis.
Plugin-level - Each importer and distributor may be paired with a static configuration file on disk. These are JSON files that are loaded by the Pulp server when the plugins are initialized. Configuration values in this location are available to all instances of the importer/distributor.
The PluginCallConfiguration
defines a method called get(str)
that will retrieve
the value for the given key. This call will check the three configuration locations in the
order listed above. The first value found for the key is returned, removing the need for
the plugin writer to apply this prioritization on their own.
Life Cycle Methods¶
Both types of plugins define a number of methods related to the lifecycle of the plugin on
a particular repository. These methods are called when the importer/distributor is added to
or removed from a repository. Examples include importer_added(repo, config
and
distributor_removed(repo, config)
.
In many cases, these methods can be ignored. The default implementation will not raise an error. Their usage is typically to perform any initialization in the plugin’s working directory that is necessary before the first plugin operation is invoked.
Metadata Method¶
Both types of plugins require a metadata method to be overridden from the base class. The
metadata()
method is responsible for providing Pulp with information on how the
plugin works. The following information must be returned from the metadata call. The docstring
for the method describes the format of the returned value.
ID - Unique ID that is used to refer to this type of plugin. This must be unique for all plugins installed in the Pulp server.
Display Name - User-friendly description of what the plugin does.
Supported Types - List of IDs for all content types that may be handled by the plugin. If there is no type definition found for any of the IDs referenced here, the server will fail to start.
Conduits¶
A conduit is an object passed to a plugin when an method is executed. The conduit is used to access functionality in the Pulp server itself. Each method is given a custom conduit type depending on the needs of the method being invoked. Consult the docstrings for each method in the plugin base class for more information on the conduit class that will be used.
Warning
Plugins should not retain any state between calls. Conduits are typically scoped to the repository being used; reusing old conduit instances can lead to data corruption.
Scratchpads¶
A scratchpad is used to store information across multiple operations run by the plugin. Each importer and distributor on a repository is given its own scratchpad. A plugin may only edit its own scratchpad for the repository being acted on.
The scratchpad is retrieved through the conduit’s get_scratchpad()
method and
updated with set_scratchpad(object)
. The scratchpad is stored in the database,
therefore its value must be able to be pickled. It is recommended to use either a single
string or a dictionary of string pairs.
Additionally, there exists a scratchpad at the repository level, accessible to all importers
and distributors on the repository. This can be used to share information between different
plugins. It is highly recommended to avoid using this wherever possible so as to not tightly
couple plugins together. The repository scratchpad can be accessed using get_repo_scratchpad()
and set_repo_scratchpad(object)
and carries the same pickle restriction as described above.
Working Directories¶
Each plugin on a repository is given a unique location on disk. This directory should be used
for storing any temporary files that need to be created when the plugin is used. These directories
are automatically deleted when the repository is deleted. The location of the working directory
can be found in the repository instance (pulp.plugins.model.Repository
) passed into each
plugin call.
Installation¶
There are two ways to install a plugin.
Entry Points¶
The plugin may define a method that will serve as its entry point. The method must accept zero arguments and return a tuple of the following:
Class of the plugin itself. This must be a subclass of either
pulp.plugins.importer.Importer
orpulp.plugins.distributor.Distributor
.Plugin-level configuration to use for that plugin. See Configuration for more information on the scope of these configuration values.
A sample is as follows:
def entry_point():
return DemoImporter, {}
class DemoImporter(Importer):
...
Python entry points are advertised within the package’s setup.py
file. Multiple entry points
may be advertised by the same setup file. A sample from the Puppet plugins is below:
from setuptools import setup, find_packages
setup(
name='pulp_puppet_plugins',
version='2.0.0',
license='GPLv2+',
packages=find_packages(exclude=['test', 'test.*']),
author='Pulp Team',
author_email='pulp-list@redhat.com',
entry_points = {
'pulp.distributors': [
'distributor = pulp_puppet.plugins.distributors.distributor:entry_point',
],
'pulp.importers': [
'importer = pulp_puppet.plugins.importers.importer:entry_point',
],
}
)
Directory Loading¶
For one-off testing purposes, the code for a plugin can be placed directly in a specific directory without the need to install to site-packages. The entry point method described above is the preferred way to integrate new plugins:
Create directory in
/usr/lib/pulp/plugins/
under the appropriate plugin type.Add
__init__.py
to created directory.Add
importer.py
ordistributor.py
as appropriate.In the above module, add the classes that subclass
Importer
orDistributor
as appropriate.
Additionally, for directory loaded plugins, Pulp will automatically load any configuration files
found in the plugin’s directory. The configuration within will be made available to each
call as described in Configuration. The only restriction on the name of the configuration
file is that it end with .conf
and be placed in the directory created in the first step
above.