See also:
A charm library is a Python file (or a Python module) in your charm project that allows charm developers to easily share and reuse auxiliary logic related to charms – for example, logic related to the relations between charms. They are a great way to help other authors to write charms that can relate to yours.
Authors associate libraries with a specific charm, and publish them to Charmhub with a reference to the origin charm. This does not prevent re-use, modification, and sharing.
The publishing tools around libraries are deliberately kept simple. Libraries are however versioned and uniquely identified.
A library must comprise a single Python file. If you write a library that feels too “big” for a single file, it is likely that the library should be split up, or that you are actually writing a full on charm.
Contents:
Location
Charm libraries are located in a directory inside the charm with the following structure:
$CHARMDIR/lib/charms/<charm>/v<API>/<libname>.py
where $CHARMDIR
is the project’s root (contains src/
, hooks/
, etc.), and the <charm>
placeholder represents the charm responsible for the library named as <libname>.py
with API version <API>
.
For example, inside a charm mysql
, the library db
with major version 3 will be in a directory with the structure below:
$CHARMDIR/lib/charms/mysql/v3/db.py
When you pack your charm, Charmcraft copies the top lib
directory into the root directory of the charm. Thus, to import this library in Python use the full path minus the top lib
directory, as below:
import charms.mysql.v3.db
Structure
A charm library is a Python file with the following structure:
Docstring
Your charm file begins with a long docstring. This docstring describes your library. Charmcraft publishes it as your library’s documentation on Charmhub. This documentation is updated whenever a new version of the library is published.
The docstring supports Markdown, specifically, CommonMark.
Metadata
After the docstring, there are a few metadata keys, as below.
LIBID
The LIBID
key controls the unique identifier for a library across the entire universe of charms. The value is assigned by Charmhub to this particular library automatically at library creation time. This key enables Charmhub and charmcraft
to track the library uniquely even if the charm or the library are renamed, which allows updates to warn and guide users through the process.
LIBAPI
LIBAPI
is set to an initial state of 0
. In general, LIBAPI
must match the major version in the import path.
LIBPATCH
LIBPATCH
is set to an initial state of 1
. In general, it must match the current patch version (needs to be updated when changing).
PYDEPS
The PYDEPS
key is an optional key that allows you to declare external Python dependencies. Charmcraft will make sure to add them to the dependencies required for your charm.
The key maps to a list of strings. Each string is a regular “pip installable” Python dependency that will be retrieved from PyPI in the usual way (subject to the user’s system configuration) and which supports all dependency formats (just the package name, a link to a Github project, etc.).
Some examples of possible PYDEPS declarations are as below:
PYDEPS=["jinja2"]
PYDEPS = ["pyyaml", "httpcore<0.15.0,>=0.14.5"]
PYDEPS = [
"git+https://github.com/canonical/operator/#egg=ops",
"httpcore<0.15.0,>=0.14.5",
"requests",
]
Of course, only one PYDEPS
declaration is allowed.
Charmcraft will collect the dependencies from all libraries included in the charm and install them from source in the virtual environment inside the .charm
file, together with all the other Python dependencies declared by the charm itself.
Note that, when called to install all the dependencies from the charm and all the used libraries, pip
may detect conflicts between the requested packages and their versions. This is a feature, because it’s always better to detect incompatibilities between dependencies at this moment than when the charm is being deployed or run after deployment.
Code
After the docstring and the metadata, there’s the library code. For some great examples of libraries, confirming to best practices, see:
If you’d like to highlight other libraries that you have found useful, or share a library that you’re working on, drop a comment using the feedback link below.
grafana_dashboard
The grafana_dashboard library is a great example of the core use case for charm libraries: a charm wants to tell other charms how to interact with it. This library demonstrates how to integrate the grafana dashboard with a charmed service.
redis
The redis
library implements a requires/provides pattern for redis. It’s useful both for interacting with redis, and as a reference for implementing this commonly used pattern yourself!
kubernetes_service_patch
The kubernetes_service_patch library is maintained by the Observability team, but is useful for most Kubernetes charms. It implements a workaround for an issue with service naming in Juju Core, and is an excellent example of how libraries can be used to share known good workarounds with the community, and extend or modify the core Juju feature set.
ingress
Another fantastic and re-usable library for Kubernetes deploys, the ingress
library implements the Requires/Provides pattern for relations with the nginx-ingress-integrator
charm.
systemd
Stepping over to the world of “legacy estate” (bare metal machines and vms), the systemd
library wraps calls to the systemctl command on the host. It provides quick access to a core set of commands for enabling, disabling, restarting, and reconfiguring services on bare metal and VMs. The library also integrates the output from systemctl with the charm logs, formalizes error checking, and turns some common multi step tasks, like masking and stopping a service, into a single call to the lib.
(If you’ve used charmhelpers in your reactive charms, you may recognize some of the code here, and in the other libraries that live at that link – we’re working on pulling selected bits of charmhelpers into standalone libraries, so that charms built with the Operator Framework can take advantage of the community knowledge built up in charmhelpers, without needing to take on the weight of the entire charmhelpers lib.)