A Juju Charm is a structured bundle of YAML configuration files and scripts for a software component that enables Juju to deploy and manage the software component as a service.
A Juju charm contains all the instructions necessary for deploying and configuring application units.
Charms make it easy to reliably and repeatedly deploy applications, then scale up (and down) as desired with minimal effort
Create, configure and deploy a wordpress environment consisting of two charms: wordpress charm and mysql charm.
Wordpress and mysql charms are choosed as an example.
However, any application charm can be created and deployed by following the steps reported in this documentation, althought the specific charm configuration will vary from application to application.
The Wordpress charm will be created and configured using the reactive paradigm and, specifically, using the reactive.charms python library.
In order to make writing Juju Charms easier, first install Juju Charm Tools:
sudo snap install charm --classic
Using the charm tools plugin, we create the directory structure we need for our charm quickly and easily:
charm create wordpress
This not only creates the directory structure, it also populates it with template files for you to edit.
Find the wordpress directory just created and edit the metadata.yaml file located in it:
name: wordpress summary: The wordpress CMS maintainer: Claudio Pisa <claudio.pisa@garr.it> description: | The wordpress cms tags: # Replace "misc" with one or more whitelisted tags from this list: # https://jujucharms.com/docs/stable/authors-charm-metadata - misc subordinate: false series: [trusty] provides: website: interface: http requires: database: interface: mysql
The metadata.yaml is the file that Juju reads to find out what a charm is, what it does and what it needs to do it.
The summary, maintainer and description fields can be edited with arbitrary descriptions.
The provides and requires fields define which relations are actually provided or required by the wordpress application.
Since the Wordpress charm is a web-based application, it provides a simple HTTP interface.
Also, Wordpress charm needs a connection to a database to function properly, so in the metadata.yaml we require a mysql interface.
Edit the layer.yaml file in the wordpress directory and include the basic and apt layer:
includes: ['layer:basic', 'layer:apt']
Depending on the charm you're building, you can add other layers but tipically the basic and apt layers are required by every charm.
These two layers provide charm.reactive ,charm-helpers, charm.atp libraries and all of their dependencies for use by the charm.
Edit the wordpress.py file in the wordpress/reactive directory:
from charms.reactive import when, when_not, set_flag, hook, when_all from charmhelpers.core.hookenv import status_set, relation_get, log from charmhelpers.core.host import write_file, service_restart from charmhelpers.core.templating import render import charms.apt import subprocess @when_not('apt.installed.wordpress') def install_wordpress_apt(): charms.apt.queue_install(['wordpress']) #sets the 'apt.installed.wordpress' flag when done @when('apt.installed.wordpress') @when_not('wordpress.ready') def install_wordpress(): # Do your setup here. # # If your charm has other dependencies before it can install, # add those as @when() clauses above., or as additional @when() # decorated handlers below # # See the following for information about reactive charms: # # * https://jujucharms.com/docs/devel/developer-getting-started # * https://github.com/juju-solutions/layer-basic#overview # status_set('blocked', "wordpress installed, waiting for database") set_flag('wordpress.ready') @when_not('wordpress.ready') @when_not('apt.installed.wordpress') def waiting_for_wordpress(): status_set('maintenance', "waiting for apt wordpress installation")
Be sure to be logged into a juju controller.
Position yourself in the wordpress directory and from the command line execute these commands:
charm build juju deploy /tmp/charm-builds/wordpress watch -c juju status --color
As you can see, juju will do the following:
Interact with your cloud/openstack/localhost to request the provisioning of a machine on which deploy the charm
Deploy the wordpress charm previously created
The wordpress application and unit will halt on the status: 'blocked' because no other python function was specified to be executed in the wordpress.py file after the install_wordpress function.
Also, the application status 'blocked' is exactly what we expect to see since we wrote the following instruction in wordpress.py:
status_set('blocked', "wordpress installed, waiting for database")
At this point, a charm that will install wordpress via apt and do nothing else it has been created and deployed.
Continue the charm configuration, deploy mysql and add a relation between wordpress and mysql:
Add a new function in the wordpress.py file:
@hook('database-relation-joined') def database_is_ready(): status_set('blocked', 'Database is ready (joined) but not configured') set_flag('wordpress.database_is_ready')
Position yourself in the wordpress directory and from the command line execute these commands:
charm build juju upgrade-charm wordpress --path /tmp/charm-builds/wordpress juju deploy mysql --series xenial juju add-relation wordpress mysql watch -c juju status --color
As a result of these commands, the mysql charm will be deployed.
A relation between mysql and wordpress will be added.
The wordpress application and unit will halt again on the status: 'blocked' as we expect since we wrote the status_set instruction in the database_is_ready function.
The database_is_ready function is executed when both wordpress and mysql units are deployed.
Also, the function execution is triggered when the relation between wordpress and mysql is added as a result of the juju add-relation command previously issued.
Configure Wordpress application according to https://help.ubuntu.com/lts/serverguide/wordpress.html
1 Edit the install_wordpress function in the wordpress.py file:
def install_wordpress(): # Do your setup here. # # If your charm has other dependencies before it can install, # add those as @when() clauses above., or as additional @when() # decorated handlers below # # See the following for information about reactive charms: # # * https://jujucharms.com/docs/devel/developer-getting-started # * https://github.com/juju-solutions/layer-basic#overview # status_set('maintenance', "configuring wordpress") # see https://help.ubuntu.com/lts/serverguide/wordpress.html wpconf = """ Alias /blog /usr/share/wordpress <Directory /usr/share/wordpress> Options FollowSymLinks AllowOverride Limit Options FileInfo DirectoryIndex index.php Order allow,deny Allow from all </Directory> <Directory /usr/share/wordpress/wp-content> Options FollowSymLinks Order allow,deny Allow from all </Directory> """ write_file('/etc/apache2/sites-available/wordpress.conf', wpconf) subprocess.call(['a2ensite', 'wordpress']) service_restart('apache2') status_set('blocked', "wordpress installed, waiting for database") set_flag('wordpress.installed')
The modified function does the following:
writes into etc/apache2/sites-available/wordpress.conf
executes the sudo a2ensite wordpress command
restarts apache service
Create a directory named templates into the wordpress directory
In templates create and edit a file named config-localhost-php.tmpl:
<?php define('DB_NAME', '{{ my_database.database }}'); define('DB_USER', '{{ my_database.user }}'); define('DB_PASSWORD', '{{ my_database.password }}'); define('DB_HOST', '{{ my_database.host }}'); define('WP_CONTENT_DIR', '/usr/share/wordpress/wp-content'); ?>
Write the following function in wordpress.py:
@when_all('wordpress.installed', 'wordpress.database_is_ready') def config_php(): status_set('maintenance', 'configuring wordpress') mysql = relation_get() log(str(mysql),'INFO') render( source='config-localhost-php.tmpl', target='/etc/wordpress/config-default.php', context={ 'my_database': mysql }) status_set('active', 'Ready')
This function retrieves informations from the mysql unit regarding the account and database that was created when the juju add-relation mysql wordpress command was executed.
Then, writes these informations into the /etc/wordpress/config-default.php file.
Build and deploy the wordpress charm with the last modifications:
charm build juju upgrade-charm wordpress --path /tmp/charm-builds/wordpress watch -c juju status --color
The wordpress application and unit status will become Ready.
The wordpress charm is correctly configured and ready to be used.
From the command line execute this command:
juju expose wordpress
From the command line execute the juju status command:
You can notice that the wordpress application is publicy exposed.
Take the public address of the wordpress unit, open a browser and enter the address into the address bar.
Enjoy Wordpress!