Tutorial: Charms with layer-snap
Difficulty: Intermediate
Author: Erik Lönroth
All code for the charm can be found here: https://github.com/erik78se/layer-pyapp-snapped
Preparations
- You should have completed the first beginner tutorial Part 1, Part 2, Part 3 before taking this tutorial.
- Use juju 2.4 or greater.
- charm-tools 2.4.4 or above or builds will fail.
Its good if you know how to create your own snap, but its not needed. You can learn snap:ing here.
What you will learn
- Understand why snaps are useful in a Juju context.
- Create a simple juju charm with “layer-snap”
Why juju charms + snap ?
Snap packages are universal to Linux. This means that we can deploy our snapped applications without having to re-package for every linux distribution our users prefer, like ubuntu, debian, raspbian, NI Linux Real-Time distribution or centos. Great news for every DevOps team!
A specifically interesting use-case of snaps are for “IoT applications”. The reasons for that are tied to the properties of snaps:
- snap do atomic upgrades.
- snap are Linux distribution agnostic.
- snap has a very secure confinement.
- snap can be made very independent of its execution environment.
Using Juju+snap for development and deployment (CI/CD) will get your applications ready in no-time!
The “pyapp” snap
We will use a training snap I have created for us: pyapp. The pyapp application is a python3 application that writes a simple message to stdout and loggs a few messages to syslog.
The code for pyapp is available here: snap-pyapp
We can easily install and test pyapp directly from the http://snapstore.io:
sudo snap install pyapp --devmode
pyapp.run
Create a new charm & include “layer-snap”.
I called my charm “pyapp-snapped” Feel free to use a cooler name for yours if you like.
snapcaft create layer-pyapp-snapped
Hint! Recall from earlier tutorials that the “layer-” prefix in the charm create is to indicate this is a reactive charm?
Create your layer.yaml and modify the repo value as needed.
Pay some attention to that we are in fact installing two snaps here. “core” and “snap”. The reason for pulling in the “core” snap is that there seems to be a bug that occasionally don’t install the needed “core” snap. I think this bug might go away in the future. For now, see it as a bonus.
includes:
- 'layer:basic'
- 'layer:snap'
options:
snap:
core:
channel: stable
devmode: false
jailmode: false
dangerous: false
classic: false
revision: null
pyapp:
channel: edge
devmode: true
jailmode: false
dangerous: false
classic: false
revision: null
repo: 'https://github.com/erik78se/layer-pyapp-snapped'
Create your metadata.yaml
name: pyapp-snapped
display-name: pyapp-snapped
summary: Simple charm for the pyapp snap
maintainer: Erik Lonroth <erik.lonroth@gmail.com>
description: |
This charm deploys the pyapp application, either from snapstore or if you supply it as a juju resource.
tags:
- example
- snap
- pyapp
series:
- bionic
resources:
pyapp-snap:
type: file
filename: pyapp.snap
description: A pyapp snap
Notice the included charm-resource “pyapp-snap”. This allow us to attach a snap package along with our deployment. If we do so, it will be uploaded to the juju controller and distributed to pyapp.
We won’t attach a local snap in this tutorial, but feel free to try it on your own (Read: layer-snap). The resource is just a placeholder and since we leave it out, layer-snap will automatically download our snap from snapstore.io when we deploy the charm.
Lets continue and see how easy the charm code becomes.
Create the reactive/layer_pyapp_snapped.py
from charmhelpers.core.hookenv import (
open_port,
status_set,
)
from charmhelpers.core.hookenv import application_version_set
from charms.reactive import (
when,when_not,
set_flag,clear_flag
)
from charms.layer import snap
@when('snap.installed.pyapp')
def set_pyapp_snapped_available():
"""
When snap is installed, just keep on updating.
"""
version = snap.get_installed_version('pyapp')
channel = snap.get_installed_channel('pyapp')
application_version_set(version)
status_set('active', "Ready pyapp-{} ({})".format(version,channel))
set_flag('my.pyapp.application.available')
@when_not('snap.installed.pyapp')
def pyapp_not_installed():
"""
Whenever the snap is not installed, clear statuses and flags.
"""
application_version_set("")
clear_flag('my.pyapp.application.available')
status_set('waiting', "Pyapp snap not installed.")
Proof, Build and Deploy
charm proof
charm build
Deploy from our local build.
juju deploy /home/erik/charms/builds/pyapp-snapped
Deploying charm "local:bionic/pyapp-snapped-0".
Test it
Lets see if the snap was installed and works.
juju ssh pyapp-snapped/0 /snap/bin/pyapp.run
Great! You have just created a juju charm that deploys a snap!
Next lesson
Next we will learn to add in a juju-action and relate our charm to a logging and search facility - the ELK (Elasticsearch, Logstash, Kibana) stack. NOT YET WRITTEN.
Contributors
@jamesbeedy - For teaching me about juju
@stub - Author of the layer-snap
@timClicks - for peer-reviewing the text