[Tutorial] How to build a charm using modern tools


Duration 0:10

This tutorial aims to show you the steps needed to prepare a charm, starting from just the bare needed Python code and ending in the proper Juju deploy, using both Operator Framework and Charmcraft projects.

Create the charm

Duration 05:00

To start a real project, we can rely on charmcraft init to setup the base structures that we’ll need to construct a complete charm: a basic code we can extend (with its correspondent tests!), proper requirements files for our Python dependencies, the required metadata.yaml, other configuration files we may need (like actions.yaml) and other project files (like a README, LICENSE, etc).

But to understand all that info can be overwhelming, so let’s start with a more basic approach: as the bare minimum things we need to start are our charm code and the metadata file, let’s go with that.

For the purposes of this tutorial, we’ll go with a very simple Python code that doesn’t do anything special, just logs that it was installed properly, so we can see that the whole process is done.

Let’s see it, will explain the code below…

#!/usr/bin/env python3

import logging

from ops.charm import CharmBase
from ops.main import main

logger = logging.getLogger(__name__)

class MyCharm(CharmBase):
    def __init__(self, *args):
        self.framework.observe(self.on.install, self.on_install)

    def on_install(self, event):
        logger.info("Congratulations, the charm was properly installed!")

if __name__ == "__main__":

The overall structure is very simple: we have a couple imports (logging is from Python’s stdlib, and ops is the Operator Framework, we’ll see later how to make it available), then the logger at module level, our charm’s class, and the init at the end. Note that the class inherits from CharmBase and then at the end we pass it to the framework’s main: this is all we need to start using the Operator Framework.

In the charm class, after calling parent’s __init__ (very important for the charm initialization), all we do is tell the framework to call our on_install method when the install event is triggered. The hooked method only logs that all succeeded.

Of course Juju also needs a metadata.yaml file with some information about our project:

name: tutocharm
summary: A charm for the tutorial.
description: A simple and basic charm to show the Operator Framework and Charmcraft in a tutorial.

So, I put that content in the respective files (and made the charm.py file executable):

$ tree .
├── metadata.yaml
├── requirements.txt
└── src
    └── charm.py

1 directory, 3 files

You may have spotted the requirements.txt file in that tree. It’s where we can put all the Python dependencies we need. For this tutorial, we just included the Operator Framework, so the content of our requirements file is:


Build the charm

Duration 02:00

We’re ready to build our charm. For this we need the Charmcraft tool. Soon it will be ready as a snap, but for now we can use it from a virtual environment:

$ virtualenv --python=python3 env
$ source env/bin/activate
(env) $ pip install charmcraft
Successfully installed charmcraft-0.0.1

Ready to use charmcraft, then. Let’s build our charm:

(env) $ charmcraft build
Done, charm left in 'tutocharm.charm'

That’s all we need.

Deploy the charm

Duration 02:00

The final step is deploy it, but before, in a different terminal, execute the following to see the logging we specified in the charm:

juju debug-log

Back to the original terminal, let’s deploy our charm:

juju deploy ./tutocharm.charm

Wait some moments to let Juju do its magic, and at some point, in the juju debug-log terminal, a line very similar to the following one will appear:

unit-tutocharm-1: 13:26:10 INFO unit.tutocharm/1.juju-log Congratulations, the charm was properly installed!

That’s all!


Hi @facundo,

This is really cleaner way of starting a new charm indeed thanks to the new “ops” module and the simplified requirements.txt.

I’m not sure where is the central place these days to store the template of the new operator framework charms, but would you mind keeping the charm-tools updated so that everyone who tries charm create -t operator-python can get benefit of the newer and the cleaner method?
(at this moment, it puts https://github.com/canonical/operator.git as a submodule instead of requirements.txt)


Hello @nobuto, glad you like this!

We will have very soon a charmcraft init command that will create directories and files that will be the base structures for a new charm in this new charmcraft/operator world.

Probably people should wait for that command (in the meanwhile creating the dirs by hand as described in this tutorial) and not use that operator_python template from charm-tools, because it’s not doing the right things…


Cool, good to know.

Would have been nicer if the template was managed by the engineering team, but in the meantime, I’ve opened a pull request to deprecate it in the charm-tools.