A tiny hooks charm
Update 2021-November: The new charmcraft tool does need an additional charmcraft.yaml added to the code as described in this post. How to pack your hooks-based charm with Charmcraft
Update 2022-March: A new improved juju-state-machine is now available! Much wanted from @ppasotti
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!
Preparations
- You should have basic understanding of what juju, charms and models are.
- You should have got through the Getting started chapter of official juju documentation.
- You need an internet connection.
- You need to install juju client.
- You need a juju controller to deploy charms through. Here is how you can get that running on your local machine: juju-lxd
- 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
tiny-python
├── config.yaml
├── copyright
├── hooks
│ ├── config-changed
│ ├── install
│ ├── leader-elected
│ ├── leader-settings-changed
│ ├── setup.py
│ ├── start
│ ├── stop
│ ├── update-status
│ └── upgrade-charm
├── icon.svg
├── LICENSE
├── 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
setup.pre_install()
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__":
install()
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!
Contributors
@cmatsuoka for providing updates for the latest charmcraft tool. @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.