User Guide¶
The pushsource library provides a common interface over various sources of content, yielding metadata on each piece of content found. These pieces of metadata are referred to as “push items”, as the main use-case for the library is in the creation of content publishing tools.
Here are some examples of push items:
RPMs obtained from a koji server.
RPMs and generic files obtained from a local directory.
Advisories and RPMs obtained from Errata Tool.
Typical usage of the library involves obtaining a Source
instance and iterating over PushItem
objects discovered
by the source.
Obtaining a source by URL¶
In most cases, the recommended method of obtaining a Source
is to provide a URL to the get()
method.
URLs are used to select a backend and provide parameters.
URLs use one of the following syntaxes:
backend:remote_url?arg1=val1&arg2=val2&...
backend:arg1=val1&arg2=val2&...
backend:
The first syntax with a remote_url may be used in the common case where the desired backend itself needs the URL of a remote service in order to function.
The second syntax omits the remote_url in cases where the backend does not accept a URL.
The final syntax is used in cases where the backend does not require any arguments in order to function.
When using URLs to obtain a source, arguments may consist of only strings and lists of strings. By convention, most backends accept lists both by repeating the argument name multiple times, and also by comma-separated values.
backend:list_arg=val1&list_arg=val2
# Is equivalent to:
backend:list_arg=val1,val2
Here are some example URLs:
# Load some RPMs from the Fedora Project's koji
koji:https://koji.fedoraproject.org/kojihub?rpm=python3-3.7.5-2.fc31.x86_64.rpm,python3-3.7.5-2.fc31.src.rpm
# Load an advisory from Errata Tool
errata:https://errata.example.com/?errata=RHBA-2020:1234
# A hypothetical backend which accepts no arguments
mock:
Available backends¶
The pushsource library includes the following backends. For detailed information, see the API reference of the associated class.
Name |
Examples |
Class |
Description |
---|---|---|---|
koji |
|
Obtain RPMs, container images and other content from a koji server |
|
staged |
|
Obtain RPMs, files, AMIs and other content from locally mounted filesystem |
|
registry |
|
Obtain images from a container image registry |
|
errata |
|
Obtain RPMs, container images and advisory metadata from Errata Tool |
|
file |
|
n/a |
Obtain a single file-backed item of various types. Each backend of this type accepts any arguments
of string type documented on the corresponding
|
cgw |
|
||
dir |
|
||
rpm |
|
||
comps |
|
||
modulemd |
|
||
modulemd-src |
|
||
productid |
|
||
pub |
|
Obtain AMIs from Pub task metadata |
Processing push items¶
Once a Source
instance has been obtained, it can be iterated over to obtain
instances of PushItem
.
Each object yielded by the source may be an instance of any PushItem
subclass,
depending on the type of content loaded by the source. For example, RPMs will be
yielded as instances of RpmPushItem
and errata will be yielded
as instances of ErratumPushItem
.
Commonly, different kinds of processing will be needed for different kinds of push items.
In this case, it’s recommended to use isinstance()
checks to dispatch each push
item appropriately.
Consider tolerating push items of an unknown type (perhaps with a warning). This will ensure your code is forwards-compatible with later versions of this library, which may add new types of push items to existing sources.
Example:
with Source.get(...) as source:
for item in source:
if isinstance(item, RpmPushItem):
publish_rpm(item)
elif isinstance(item, ErratumPushItem):
publish_erratum(item)
elif isinstance(item, FilePushItem):
publish_file(item)
else:
LOG.warning("Unexpected push item type: %s", item)
Implementing a backend¶
New backends can be registered with the library, making them accessible via URL. To implement a backend, follow these steps:
Create a class inheriting from
Source
.In your constructor, add any arguments you’d like to be usable in URLs for your backend, while following these conventions:
Remember that all arguments from URLs will be provided as strings.
Accept a url argument if and only if you want your backend URL to accept a URL immediately after the backend scheme (as in example
backend:url?arg=val&arg=val&...
).If your backend uses a customizable number of threads, use an argument named threads for configuring the number of threads.
If your backend has a customizable timeout, use an argument named timeout accepting a number of seconds.
- Implement the
__iter__
method, while following conventions: Lazy loading of data is recommended where practical; i.e. prefer to implement a generator which yields each piece of data as it is ready, rather than eagerly loading all data into a list.
- Implement the
If your class allocates any resources which should be cleaned up when no longer needed, implement
__enter__
and__exit__
methods to manage them.Call the
register_backend
method providing your backend’s name and class as arguments.
After following the above steps, instances of your source can be obtained by
get()
, in the same manner as backends built-in to the library.
If you’re unsure how to arrange for your call to register_backend
to occur prior to the usage of get()
, you may declare entry points
in the pushsource
group; the library will enforce that all modules in this group are
imported when get()
is called. This functionality requires
pushsource 2.17.0 or later.
For example, in setup.py, one may declare:
entry_points={
"pushsource": [
# Left-hand side can be anything.
# Right-hand side is the name of your module which, on import time,
# should perform a call to "Source.register_backend".
"mybackend = mylib.my_pushsource_backend",
]
},
Binding partially-configured backends¶
For developers integrating this library into an environment where certain parameters are known ahead of time, it’s possible and recommended to preconfigure backends, making them less cumbersome to use and hiding configuration details. This can be done by registering a new backend which acts as an alias along with a set of arguments to an existing backend.
For example: this library ships a koji backend. If we are developing a tool which frequently is used with Fedora Koji, it would be cumbersome to require the user to pass the Fedora Koji URL every time the source is used. This can be fixed by creating a fedkoji alias, which delegates to the koji backend with some arguments pre-filled.
# make a 'fedkoji' backend which is simply the koji backend
# pointed at a particular URL
fedkoji_backend = Source.get_partial('koji:https://koji.fedoraproject.org/kojihub')
Source.register_backend('fedkoji', fedkoji_backend)
# fedora koji now accessible without specifying URL
Source.get('fedkoji:rpm=python3-3.7.5-2.fc31.x86_64.rpm,...')