[Tutorial] A tiny hooks charm

A tiny hooks charm

Tutorial: Creating charms with only hooks

Difficulty: Beginner
Author: Erik Lönroth

What you will learn

This tutorial is a good start to learn about the concept of “juju hooks” by modify an existing charm.

You will:

  • Learn how to retrieve a charm from charmstore.
  • Learn how to use juju hooks in charms.
  • Get an overview about the juju event-loop / state-machine.
  • Learn how to use the juju command: ‘juju run’ to execute remote commands.
  • Learn how to use ‘juju debug-log’ for debuging your charms.

After completing this tutorial, you will be able to put together and deploy a charm in minutes for very basic DevOps purposes.

Lets get started!


  1. You should have basic understanding of what juju, charms and models are.
  2. You should have got through the Getting started chapter of official juju documentation.
  3. You need an internet connection.
  4. You need to install juju client.
  5. You need a juju controller to deploy charms through. Here is how you can get that running on your local machine: juju-lxd
  6. You need a basic charm developer environment. If you have completed part 1 “Setup up a basic workbench”, then your’e all set.

The “tiny-python” charm

I have prepared a charm already for you. Its called “tiny-python”. We will modify tiny-python during this tutorial to learn about juju “hooks”.

Your first task is to download/pull the tiny-python charm. Go ahead and run:

charm pull cs:~erik-lonroth/tiny-python

This command downloads the latest version of tiny-python from the charmstore to your local filesystem. This can be done with any charm.

Lets look at what we got.

$ tree tiny-python
├── config.yaml
├── copyright
├── hooks
│   ├── config-changed
│   ├── install
│   ├── leader-elected
│   ├── leader-settings-changed
│   ├── setup.py
│   ├── start
│   ├── stop
│   ├── update-status
│   └── upgrade-charm
├── icon.svg
├── metadata.yaml
├── README.md
├── revision
└── version

Thats all there is! The whole charm is downloaded and the directory: tiny-python/hooks contains all the scripts for the hooks that this charm implements.

Congratulations - you have learned how to retrieve a charm directly from charmstore!

Now, lets try deploy it to your cloud! You can decide if you want to deploy from charmstore or from your local version. For juju, its all the same.

juju deploy, status and debug-log

Lets deploy the charmstore version of the charm and follow it through:

juju deploy cs:~erik-lonroth/tiny-python 
juju status
juju debug-log

juju status gives you the overview of what goes on in your model.

juju debug-log lets us see from a debug point of view what happens inside the model. Read about debuging charms here although we will not go deep on that topic now.

Soon, the application will enter the ‘active’ status and workload. Once you are happy watching logs and status we can continue.

Remove the application

Lets clean up our test deploy, so we don’t run into problems later.

juju remove-application tiny-python

Now, your juju model should be empty. Make sure it is with ‘juju status’.

The juju state machine

Before we dive deeper into “hooks”, you need to understand when hooks are triggered by the juju-state-machine. There are many hooks, but a few are core to the operation of all charms. You could take a quick look at docs here to learn more: Juju event cycle

The core hooks are the following:

  • install
  • config-changed
  • start
  • leader-elected
  • leader-settings-changed
  • stop
  • update-status
  • upgrade-charm

The tiny-python charm implements all “core hooks” (which is good practice) but a charm doesn’t strictly need to implement any of them to be able to deploy.

Now that you have an initial understanding about the core hooks, lets explore the “install” hook to get our hands dirty.

Open the file tiny-python/hooks/install with your favorite editor.

Modifying the ‘install’ hook

Lets decide that our changes to the tiny-python charm will install a snap package “hello” from snapcraft.io and set the application version from the yaml output of ‘snap info hello’.

Note: Installing snaps are pretty much like installing a “deb” or “rpm” package so don’t worry if you don’t know what a snap is. It might be cool to know that snaps works on any linux distro, which is something I like.

So, now edit the ‘install’ hook as below.

import setup

from charmhelpers.core import hookenv
import subprocess
import yaml

def install():
    subprocess.call(['snap', 'install', 'hello', '--stable'])
    output = subprocess.check_output(['snap', 'info', 'hello'])
    yamldata = yaml.load(output)
    hookenv.application_version_set( yamldata['installed'].split(" ")[0] )
    hookenv.status_set('maintenance', 'Installed, waiting for start')
    hookenv.log('Did some installing', 'INFO')

if __name__ == "__main__":

At this point, you could end up in your charm getting an error with the install hook. To resolve that, fix your code and resolve the issues with:

juju upgrade-charm tiny-python --path=./tiny-python
juju resolved tiny-python/0

If you get really stuck here, you can restart all of this with a brute force nuke of your problems with removing the whole machine X with:

juju remove-machine X --force

… where X is the number of your machine running tiny-python from juju status.

Deploy our local version

With our changes saved, lets deploy our modified local charm.

juju deploy ./tiny-python
watch -c juju status --color
juju debug-log

See from ‘juju status’ that the Store for our App is set to “local”? This tells us that we are using a local version of the charm, instead the one from charmstore.

Amazing! Soon you should also see what version of “hello” you have deployed!

The update-status hook & juju run

Lets now turn our eyes a bit towards the update-status hook. This hook is triggered periodically as you can see in the juju-state-machine. Its a great place to gather information or evaluate the status of your application. The interval at which this update-status occurs is however several minutes. You dont have that time when you are debugging. We need a means to trigger it to run when you want it to.

This is where juju run comes in.

Lets use juju run to execute the update-status script:

juju run --unit tiny-python/1 'hooks/update-status'

As you can see from the output from juju status, we have forced the update-status script to run.

We can play with any other unix commands aswell, lets find out the hostname of machine 1:

juju run --machine 1 hostname

The ‘juju run’ command as a fantastic tool in situations where you don’t have the ability to use ssh! Go ahead and run some other unix commands of you choise.

Your excercise is now to modify the ‘update-status’ hook to update the application version. Its really just to copy the code from the install hook and
I will leave this task to you before we continue and learn how to upgrade your juju charms in your models.

The upgrade-charm hook

Once you have made changes to your charm, you want to upgrade it in your existing model. You can either remove your charm altogether (juju remove-application) and start new, but you then need to wait for a new machine to come alive. This is not what you want always. Also, the “install” hook might take a long time to run.

With your changes in the “update-status” hook, lets use “juju upgrade-charm” to upgrade the charm.

juju upgrade-charm tiny-python --path=./tiny-python

Even though we have not changed the upgrade-charm hook itself, you can see that its ran from the output of ‘juju status’

If you have done everything right, you will also see a new ‘Rev’ number in the model. If it fails, you can probably find out your problem with:

juju debug-log

If you wait for a while, you will now also see the results from your changes to “update-status” as the juju state machine triggers the periodic hook.

What a glorious achievement! Its now up to you to explore what you can do with all the other hooks available to you! Only your imagination is the limit!

Extra material : tiny-bash

The tiny-python charm has a sister charm: tiny-bash. This charm deploys very fast, since its not pulling in any python modules etc. It also implements all the hooks that tiny-python does. It also works on centos7, which you might want to try out. tiny-bash-centos

You can try it if you like and look at the code if you have time. It takes about 5 minutes and a great spot to test out stuff with hooks.

charm pull cs:~erik-lonroth/tiny-bash

Another example of a hooks-only-charm is the telegraf-prometheus charm for centos7 which is simple to study.

Happy charming!


@xinyuem - Added valuable information about typos and also tested the early versions.
@rick_h - Gave valuable information about the “ubuntu lite” charm which is model for the charms in this tutorial.
@jamesbeedy - For great help on juju in general
@Dmitrii - For very wise thoughts around content and great state machine images.


The links for both tiny-python and tiny-bash are broken.

1 Like

I’ll fix that now, thanx! I was thinking to update the tutorials one day - but I’m working on a followup which takes up my time presently.