Conary:Dynamic Tags
From rPath Wiki
|
See the Dynamic Tags section of Repository-Based System Management Using Conary for an introduction to the purpose of tags.
With the exception of certain file types (such as shared libraries), files that have tags attached to them will be processed by tag handlers. A tag name should describe the file, not the action to take based on the file; for example, <file> is-a <tag>
Tagging Files in Recipes
For most tags, recipes that create files that would be tagged already have the trove that provides the tag description file in their buildRequires list. For those tags, and for the special tags handled inside Conary (see below), you do not have to touch recipes.
However, if your recipe does not have a build requirement for the trove that would cause your file to be tagged, you should add a TagSpec line. One common example is initscripts. Very few packages that provide initscripts have a real build dependency on chkconfig, the package that provides the initscript tagdescription. Rather than adding 'chkconfig:runtime' to buildRequires, you should explicitly tag your initscripts with:
r.TagSpec('initscript', '%(initdir)s/')
Needing to provide the TagSpec line violates the general rule of avoiding boilerplate in recipes, but it helps avoid two other evils:
- Putting distribution-defined bits in Conary proper
- Build dependency loops
Prerequisites
Prerequisites for package installation are merely runtime dependencies for the tag handler. So use lines like:
r.Requires('%(bindir)s/foo', '%(taghandlerdir)s/')
r.Requires('bar:runtime', '%(taghandlerdir)s/thistaghandler')
The first one says that every taghandler that the recipe installs should depend on the file /usr/bin/foo (be careful -- you can only require files that are provided. In modern versions of conary, all files in bin/ directories are provided, but it's good to double check that some package has that file listed in its provides section.) The second form is specific to one taghandler and has a trove requirement for the 'bar:runtime' component. Most recipes provide only one taghandler, so you can use the simple filterexp of '%(taghandlerdir)s/' as a shorthand.
Tag Catalog
Normal Tags
These are the tags defined in the @rpl namespace:
a2psfontmetrics | A font-metrics file that is part of a2ps |
cacheable-font | A font that should be added to the fontconfig font cache |
desktop-file | A GNOME application description file, containing MIME information as of GNOME 2.8. |
docbook-catalog | A docbook catalog file |
docbook-dtd-catalog | [Deprecated] A docbook-dtd catalog file |
docbook-dsssl-catalog | [Deprecated] A docbook-dsssl catalog file |
docbook-other-catalog | [Deprecated] Supplemental catalogs for docbook (dsssl, openjade) |
docbook-xmlent | Docbook XML entity files |
docbook-xsl-stylesheet | A docbook-xsl stylesheet file |
fmtutilconfig | [Deprecated] A TeTeX fmtutil config file |
firefox-chrome | A chrome file that needs to be registered with Firefox |
firefox-extension | An extension that needs to be registered with Firefox |
firefox-xpt-component | A component file that needs to be registered with Firefox |
gconf2schema | A GConf version 2 schema file |
gdk-pixbuf-loader | A GDK pixbuf loader module that needs to be registered in a catalog |
gnome-panel-settings | Gnome panel settings file that needs to be loaded into the GConf database |
group-info | A Conary groupinfo file, located in /etc/conary/groupinfo |
gstreamerplugin | A Gstreamer plugin module that needs to be registered in a catalog |
gtk-input-method | A GTK input method modules that needs to be registered in a catalog |
gtk-update-icon-cache | Icon theme caches which need to be updated |
info-file | A master info file ( foo.info{,.gz} not foo.info-1{,.gz} )
|
initscript | An init script that needs proper installation, such as setting up the right symlinks |
kernel | A kernel image or module (such as something that might require a new initrd, changes to bootloader config, etc.) |
mime-database | Shared MIME-info database entry |
pangomodule | A pango shaper module that needs to be registered in a catalog |
scrollkeeper-record | A OMF (Open source Metadata Framework) record for the scrollkeeper document cataloger |
shell | A program that should be added to or removed from the /etc/shells file. This tag must be added manually.
|
texdata | Files located in /usr/share/texmf -- tex keeps a database of these files |
texpackagesource | .ini/.xmt files in /usr/share/texmf, needed to update configuration files for fmtutil
|
user-info | A Conary userinfo file, located in /etc/conary/userinfo |
x-font | A font intended to be used with the X Window System. |
xml-catalog | XML catalog file located in /etc/xml; regenerated based on the contents of the tag description files provided by packages for catalogs located /etc/xml. |
Special Tags
Special tags are handled within Conary, and are the same regardless of namespace:
shlib | A shared library; Conary checks that it is an ELF object, which filterexp's cannot represent |
tagdescription | Conary needs internal knowledge of tag descriptions to implement tag handling, including the {handler} protocol. |
taghandler | Required to handle the {handler} protocol. |
buildlog | Used purely internally to tag build logs in :debuginfo components.
|
Creating Tags
There are two files per handler, both of which have to be implemented in the same component:
- Tag Description: A text file describing what files the tag should be applied to. This file should be placed in the
/etc/conary/tagssubdirectory (macro equivalent is%(tagdescriptiondir)s); examine the contents of this subdirectory for examples. - Tag Handler: A script or executable that processes the tag actions. This file should be placed in the
/usr/libexec/conary/tagssubdirectory (macro equivalent is%(taghandlerdir)s); examine the contents of this subdirectory for examples.
Writing Tag Descriptions
A tag description resembles the following:
file %(taghandlerdir)s/<name-of-tag-handler> name <short text to use in log.debug message> description <Arbitrary text> implements <calling convention> datasource args|stdin exclude <filterexp> include <filterexp>
-
filespecifies the filename of the script or executable, located in/usr/libexec/conary/tags -
nameis optional, and defaults to the name of the tag description file. This is typically omitted. - The calling convention argument to
implementsis one offiles update,files preupdate(in Conary 1.0.26 and later),files preremove,files remove,handler update, orhandler preremove; other conventions result in errors. -
datasourceis optional, and defaults toargs, which means that the filenames are passed to the tag handler as command-line arguments.stdinmeans that they are passed via standard input, one filename per line, which is useful for (rare) cases where a tag might be used for thousands of files.multitagmeans that they are passed via standard input, in pairs of lines, where the first line of the pair contains one or more tags, and the second line contains the filename that the tag or tags apply to:
tag1 tag2 ... /path/to/file tag2 tag 3 ... /path/to/other/file
Each tag (here, tag1, tag2, and tag3) must have its own tag description file which lists the tag handler and specifies the multitag datasource. The multitag datasource protocol is useful mainly because Conary enforces no ordering between tag handlers, and so whenever there is an ordering constraint on handling multiple tags, they must be handled by the same tag handler.
-
excludeandinclude: You can have arbitrarily many include and exclude lines. They are processed in order, and the first match wins. No match implies exclusion -- the files act as if the last line is "exclude .*" A packagepolicy will read the descriptions and apply them to each file packaged. You'll be able to override the results of applying tag handler exclude/include lines in a recipe -- though it would generally be preferable to fix the tag handler.
A <filterexp> is a regular expression as extended by filter.Filter. That is, if it starts with a / character, a ^ character is prepended to the expression so that it only matches from the beginning of a path, and if it does not end with a / or $ character, a $ character is appended to the expression so that the match stops there; "/lib/foo" matches neither /usr/lib/foo nor /lib/foo/bar. In other words, a <filterexp> is a regular expression that does what you obviously intend for matching paths.
A tag handler can call conary to query the conary database for any specific information it needs in order to do its work, though it cannot perform any operations that might change the database.
The following calling convention is used:
-
$0 files update <filelist>This announces that the files in<filelist>have been either installed or changed, and lets the handler do whatever it needs to. Remember that this is called only for files that have actually changed name or content, not at every random package upgrade (unlike RPM). It is also not called ifhandler updateis called, since the action ofhandler updateis a superset offiles update: it provides all the tagged files, not just changed tagged files. It is also called the first time a taghandler is installed, if that taghandler providesfiles updatebut nothandler update. -
$0 files preupdate <filelist>This will be called before files are updated, only if the tag handler is installed on the system before that update job chunk is started. Note thatfiles updatewill also be called. In general, this should be used only for circumstances that cannot be handled withfiles update, and if there is any chance that the taghandler will be installed concurrently with the tagged files, this tag handler may not be executed on initial install, but the tag handler must be written so that it will not break if it is executed on initial install. (Introduced in Conary 1.0.26.) -
$0 files preremove <filelist>This will be called before files are removed. Use only when necessary, such as with "service foo stop", where the file needs to still exist, or if the taghandler is in the same component as the tagged file. Do everything possible in the remove action instead. By the same logic ashandler update,files preremoveis not called ifhandler preremoveis called. -
$0 files remove <filelist>This announces that the files in<filelist>no longer exist on the system, and requests an update. This will also be called for the old name if a file changes name. By the same logic ashandler update,files removeis not called ifhandler preremoveis called. Note that if the tagged file and the tag handler are in the same package,files removewill not be called if the package is erased, because the tag handler will have been erased; usefiles preremovewhen tag handlers and tagged files are in the same component and a removal action is required. -
$0 handler update <filelist>This announces that either the tagdescription file or the taghandler itself has been either installed or changed.<filelist>is a list of all files currently on the system carrying this tag.handler updatecan be particularly useful when a new version of a taghandlers has to clean up after bugs. Again, called on real change, not every time a package is installed; and ifhandler updateis called, thenfiles updateis not called. -
$0 handler preremove <filelist>This announces that the tag description and tag handler files are about to be removed. (Obviously, we can't call the taghandler after removing it!)<filelist>is a list of all files currently on the system carrying this tag, facilitating cleanup. Note that ifhandler preremoveis called, thenfiles handler/removeis not called.
Each item of the calling convention which a tag handler supports will be
listed in the tag description file with animplements line, for example:implements files update implements files remove
Writing Tag Handlers
A tag handler in Conary is an executable which performs specific operations on a given file or list of files each time those files are changed. Appliance builders will occasionally use a tag handler to automate tasks based on file updates.
Tag handlers are only called for files that have actually changed from one version to another. The tag handler will be run after any tagged files have been changed on the system by applying Conary changesets (or updates). Tag handlers will not be called on an update if no tagged files have been changed; even if a package or group is updated, a tagged file itself must have changed for the tag handler to run.
| Keep tag handler use to a minimum! Throughout this page, note the potential issues. Only use tag handlers when alternatives present a higher cost or a higher risk to system health. |
Tag Handler API with Conary
A tag handler is passed a type such as files or handler as the first argument and an action such as update or preremove as the second argument. These types and actions are registered in the tag description. This makes error handling straightforward using case statements. See the two-level case statements in the tags in /usr/libexec/conary/tags/; rPath recommends using the structure of existing tag handlers as a guide to creating new ones.
The succeeding arguments passed to the tag handler are the paths of the tagged files that have changed. Recall that even if a package which contains tagged files is updated, the tag handler will not run unless at least one of the actual tagged files have changed.
File List as Arguments
In some cases, the list of files is not important to the tag handler; knowing that one of the tagged files was changed is sufficient to run the tag handler. This is common for database entries and cache entries in which the action is always a rescan of the supported paths. As an alternative, an update and remove could possibly be used to perform the same task.
In other cases, the list of files is important to the tag handler, and the list can be passed as the remaining arguments to the tag handler. In such cases, assume that more than one file can be passed because more than one tagged file may be changed in any given operation.
Multiple Packages and Package Versions
If a tag handler is expected to manage multiple versions of a package or multiple packages, it may not be called on one particular set of files or one version of a package at a time. The tag handler is run at the end of change operations, meaning it is possible that multiple items are updated at the same time.
Ordering
Conary does not provide ordering between tag handlers, and the order in which they are run is not guaranteed. A tag handler cannot expect that any other tag handler has run before it processes. This means that the runtime requirements for a given tag handler may have yet to run their own tag handlers. To handle things in a certain order, you will need to use multitag protocol handlers, in which one handler acts on more than one type of tag.
Tag Handler Updates
If a tag handler is updated, it is re-run against the tagged files on the system. This provides a means to correct errors in a tag handler, but it can expose new errors if you do not expect this behavior. For example, you may not want a tag handler to just append paths to a config file, but to also check to see if the file exists first because it is possible that the tag handler has been called on the same files before.
Shell for Tag Handlers
Though there is not specific requirement that a tag handler be written in shell, it is a best practice to ensure that dependencies are kept to a minimum.
Shell script runtime requirements are not automatically discovered, and when these dependencies are not present, the results are unpredictable. External dependencies required by the tag handler itself must be discovered and added as runtime requirements for the trove which contains the tag handler, and this is not done automatically for shell scripts. The runtime requirements must be worked out manually and assigned in the recipe. As a result, the more complex the tag handler, the greater the chance for missing runtime requirements.
Consider writing tag handler that are explicitly bash scripts and use bash conventions. For example, use ${path/*\//} instead of $(basename $path). Use the Parameter Expansion page as a reference.
Sample Tag Handler
The following example is the initscript tag handler with some additional comments to explain what it will do. Also, create a corresponding tag description file to accompany the tag handler.
#!/bin/bash # Use at least enough arguments to ensure a valid type and action, or error out. if [ $# -lt 2 ]; then echo "not enough arguments: $0 $*" >&2 exit 1 fi # Initialize type and action to leave the remaining args as the filelist. type="$1" shift action="$1" shift # Use case type to ensure that the taghandler is passed only types it can # handle. In this case, use only handle files and error out on anything # else case "$type" in files) # For file type operations, case the possible actions and error # for anything unsupported. case "$action" in update) # Iterate the file list because it is not known in advance # how many files we will be passed in a given operation. for file in $@; do /sbin/install_initd $file done ;; # Use preremove to cleanly stop a service # before removing the init file which controls it. preremove) for file in $@; do $file stop /sbin/remove_initd $file done ;; *) echo "ERROR: taghandler $0 invoked for an action ($action) that is not handled" 1>&2 exit 1 ;; esac ;; *) echo "ERROR: taghandler $0 invoked for a type ($type) that is not handled" 1>&2 exit 1 ;; esac exit 0
